diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index ae0b786e577a5af92f0aaf5cc17d2d4a6f2865c1..1bc51b5f778c101b1460377b539a4901283ca016 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -5064,12 +5064,14 @@ function _M:learnTalent(t_id, force, nb, extra) end -- Simulate calling the talent's close method if we were not learnt from the levelup dialog + local lvl = self:getTalentLevel(t_id) + local lvl_raw = self:getTalentLevelRaw(t_id) if t.on_levelup_close and not self.is_dialog_talent_leveling then - local lvl = self:getTalentLevel(t_id) - local lvl_raw = self:getTalentLevelRaw(t_id) t.on_levelup_close(self, t, lvl, old_lvl, lvl_raw, old_lvl_raw, false) end + self:fireTalentCheck("callbackOnTalentChange", t_id, "learn", lvl_raw - old_lvl_raw) + return true end @@ -5187,6 +5189,7 @@ function _M:unlearnTalent(t_id, nb, no_unsustain, extra) nb = math.max(0, oldnb - (self.talents[t_id] or 0)) local t = _M.talents_def[t_id] + local old_lvl_raw = self:getTalentLevelRaw(t_id) if not self:knowTalent(t_id) and (t.mode ~= "sustained" or t.passive_callbacks) then self:unregisterCallbacks(t, t_id) @@ -5242,6 +5245,9 @@ function _M:unlearnTalent(t_id, nb, no_unsustain, extra) end end + local lvl_raw = self:getTalentLevelRaw(t_id) + self:fireTalentCheck("callbackOnTalentChange", t_id, "unlearn", lvl_raw - old_lvl_raw) + return true end @@ -5947,6 +5953,7 @@ local sustainCallbackCheck = { callbackOnTakeoffTinker = "talents_on_takeoff_tinker", callbackOnWear = "talents_on_wear", callbackOnTakeoff = "talents_on_takeoff", + callbackOnTalentChange = "talents_on_talent_change", callbackOnTalentPost = "talents_on_talent_post", callbackOnTemporaryEffect = "talents_on_tmp", callbackOnTemporaryEffectRemove = "talents_on_tmp_remove", diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index 33edc9c9dd657ace7491902379a77c13378e5964..f3d94765641cda2457f026ef76e7e72b97556e95 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -2021,14 +2021,14 @@ function _M:setupCommands() print("===============") end end, [{"_g","ctrl"}] = function() if config.settings.cheat then + game.player:takeHit(100, game.player) +do return end self:changeLevel(game.level.level + 1) do return end local f, err = loadfile("/data/general/events/weird-pedestals.lua") print(f, err) setfenv(f, setmetatable({level=self.level, zone=self.zone}, {__index=_G})) print(pcall(f)) -do return end - game.player:takeHit(100, game.player) do return end package.loaded["mod.dialogs.shimmer.ShimmerDemo"] = nil self:registerDialog(require("mod.dialogs.shimmer.ShimmerDemo").new(game.player, "iron throne couture: ")) diff --git a/game/modules/tome/data/birth/classes/mage.lua b/game/modules/tome/data/birth/classes/mage.lua index e3f40ba78b2b96bcb8c62216dd3b2e1cb1413ae3..c7448cca4528a443942144b0f9dbbdcb6495ddb0 100644 --- a/game/modules/tome/data/birth/classes/mage.lua +++ b/game/modules/tome/data/birth/classes/mage.lua @@ -287,11 +287,11 @@ newBirthDescriptor{ end, }, talents = { - [ActorTalents.T_NECROTIC_AURA] = 1, - [ActorTalents.T_CREATE_MINIONS] = 1, - [ActorTalents.T_ARCANE_EYE] = 1, - [ActorTalents.T_INVOKE_DARKNESS] = 1, - [ActorTalents.T_BLURRED_MORTALITY] = 1, + -- [ActorTalents.T_NECROTIC_AURA] = 1, + -- [ActorTalents.T_CREATE_MINIONS] = 1, + -- [ActorTalents.T_ARCANE_EYE] = 1, + -- [ActorTalents.T_INVOKE_DARKNESS] = 1, + -- [ActorTalents.T_BLURRED_MORTALITY] = 1, }, copy = { soul = 1, diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua index 690f9958cda61fff471678c469e8fd8d6aa542d7..02e662572abd7df341ab5b4352feb4473e9734d8 100644 --- a/game/modules/tome/data/damage_types.lua +++ b/game/modules/tome/data/damage_types.lua @@ -4262,17 +4262,20 @@ newDamageType{ -- Light + Darkness newDamageType{ name = _t"dark light", type = "DARKLIGHT", text_color = "#9D9DC9#", + damdesc_split = { {DamageType.DARKNESS, 0.5}, {DamageType.LIGHT, 0.5} }, projector = function(src, x, y, type, dam, state) state = initState(state) useImplicitCrit(src, state) - DamageType:get(DamageType.DARKNESS).projector(src, x, y, DamageType.DARKNESS, dam / 2, state) - DamageType:get(DamageType.LIGHT).projector(src, x, y, DamageType.LIGHT, dam / 2, state) + local realdam1 = DamageType:get(DamageType.DARKNESS).projector(src, x, y, DamageType.DARKNESS, dam / 2, state) + local realdam2 = DamageType:get(DamageType.LIGHT).projector(src, x, y, DamageType.LIGHT, dam / 2, state) + return (realdam1 or 0) + (realdam2 or 0) end, } -- Fire + Physical newDamageType{ - name = "meteor", type = "METEOR", text_color = "#CRIMSON#", + name = _t"meteor", type = "METEOR", text_color = "#CRIMSON#", + damdesc_split = { {DamageType.PHYSICAL, 0.5}, {DamageType.FIRE, 0.5} }, projector = function(src, x, y, type, dam, state) state = initState(state) useImplicitCrit(src, state) @@ -4285,12 +4288,13 @@ newDamageType{ -- Cold/Darkness damage newDamageType{ - name = "frostdusk", type = "FROSTDUSK", text_color = "#BLUE#", - damdesc_split = { {DamageType.TEMPORAL, 0.5}, {DamageType.DARKNESS, 0.5} }, + name = _t"frostdusk", type = "FROSTDUSK", text_color = "#DARK_BLUE#", + damdesc_split = { {DamageType.COLD, 0.5}, {DamageType.DARKNESS, 0.5} }, projector = function(src, x, y, type, dam, state) state = initState(state) useImplicitCrit(src, state) - DamageType:get(DamageType.TEMPORAL).projector(src, x, y, DamageType.COLD, dam / 2, state) - DamageType:get(DamageType.DARKNESS).projector(src, x, y, DamageType.DARKNESS, dam / 2, state) + local realdam1 = DamageType:get(DamageType.COLD).projector(src, x, y, DamageType.COLD, dam / 2, state) + local realdam2 = DamageType:get(DamageType.DARKNESS).projector(src, x, y, DamageType.DARKNESS, dam / 2, state) + return (realdam1 or 0) + (realdam2 or 0) end, } diff --git a/game/modules/tome/data/general/npcs/horror.lua b/game/modules/tome/data/general/npcs/horror.lua index 3e28c283bc1734aeaf68ebc08b07fb5f0ca3fdb3..0796c1b3601db63101b44248d102203758d7dcc1 100644 --- a/game/modules/tome/data/general/npcs/horror.lua +++ b/game/modules/tome/data/general/npcs/horror.lua @@ -175,7 +175,8 @@ newEntity{ base = "BASE_NPC_HORROR", [Talents.T_WEAKNESS]={base=3, every=12, max=8}, [Talents.T_DISMAY]={base=3, every=12, max=8}, [Talents.T_DOMINATE]={base=3, every=12, max=8}, - [Talents.T_INVOKE_DARKNESS]={base=5, every=8, max=10}, + -- DGDGDGDG + -- [Talents.T_INVOKE_DARKNESS]={base=5, every=8, max=10}, [Talents.T_NIGHTMARE]={base=5, every=8, max=10}, [Talents.T_WAKING_NIGHTMARE]={base=3, every=8, max=10}, [Talents.T_ABYSSAL_SHROUD]={base=3, every=8, max=8}, diff --git a/game/modules/tome/data/general/npcs/orc-rak-shor.lua b/game/modules/tome/data/general/npcs/orc-rak-shor.lua index a9ccf0f3a75b180b67d6894a3c00a5483037f82d..b0141f1e7f2fc9e3cbdafc832858a85b626066e2 100644 --- a/game/modules/tome/data/general/npcs/orc-rak-shor.lua +++ b/game/modules/tome/data/general/npcs/orc-rak-shor.lua @@ -62,7 +62,7 @@ newEntity{ base = "BASE_NPC_ORC_RAK_SHOR", combat_armor = 0, combat_def = 5, soul = resolvers.rngavg(5, 10), - +--[[DGDGDGDG resolvers.talents{ [Talents.T_NECROTIC_AURA] = 1, [Talents.T_AURA_MASTERY] = 5, @@ -79,6 +79,7 @@ newEntity{ base = "BASE_NPC_ORC_RAK_SHOR", [Talents.T_FORGERY_OF_HAZE]={base=3, every=5, max=7}, [Talents.T_FROSTDUSK]={base=3, every=5, max=7}, }, +]] resolvers.sustains_at_birth(), } diff --git a/game/modules/tome/data/general/npcs/vampire.lua b/game/modules/tome/data/general/npcs/vampire.lua index 3397450fbfadc10dfc41b97727c9a7cc2e8b5341..bc3176bd002e384c03c358989823c2daea379501 100644 --- a/game/modules/tome/data/general/npcs/vampire.lua +++ b/game/modules/tome/data/general/npcs/vampire.lua @@ -17,6 +17,9 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org +-- DGDGDGDG +do return end + local Talents = require("engine.interface.ActorTalents") newEntity{ diff --git a/game/modules/tome/data/general/objects/world-artifacts.lua b/game/modules/tome/data/general/objects/world-artifacts.lua index 56bdc66ab39841d3e4c60f8861cb686b3ce1b566..ecd86fddb7400b3350bd81045c996808d581766c 100644 --- a/game/modules/tome/data/general/objects/world-artifacts.lua +++ b/game/modules/tome/data/general/objects/world-artifacts.lua @@ -4508,7 +4508,8 @@ newEntity{ base = "BASE_LITE", --Thanks Grayswandir! resists_pen = {[DamageType.COLD]=10}, talent_cd_reduction = { - [Talents.T_CHILL_OF_THE_TOMB] = 2, + -- DGDGDGDG !!!! + -- [Talents.T_CHILL_OF_THE_TOMB] = 2, }, }, max_power = 20, power_regen = 1, diff --git a/game/modules/tome/data/talents/spells/advanced-necrotic-minions.lua b/game/modules/tome/data/talents/spells/advanced-necrotic-minions.lua deleted file mode 100644 index 3dff2fc273fc23a05c9c346942a8c8d2aac0d2be..0000000000000000000000000000000000000000 --- a/game/modules/tome/data/talents/spells/advanced-necrotic-minions.lua +++ /dev/null @@ -1,371 +0,0 @@ --- ToME - Tales of Maj'Eyal --- Copyright (C) 2009 - 2019 Nicolas Casalini --- --- This program is free software: you can redistribute it and/or modify --- it under the terms of the GNU General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- This program is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU General Public License for more details. --- --- You should have received a copy of the GNU General Public License --- along with this program. If not, see <http://www.gnu.org/licenses/>. --- --- Nicolas Casalini "DarkGod" --- darkgod@te4.org - -local minions_list = { - bone_giant = { - type = "undead", subtype = "giant", - blood_color = colors.GREY, - display = "K", - combat = { dam=resolvers.levelup(resolvers.mbonus(45, 20), 1, 1), atk=15, apr=10, dammod={str=0.8} }, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - infravision = 10, - life_rating = 12, - max_stamina = 90, - rank = 2, - size_category = 4, - movement_speed = 1.5, - autolevel = "warrior", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=2, }, - stats = { str=20, dex=12, mag=16, con=16 }, - resists = { [DamageType.PHYSICAL] = 20, [DamageType.BLIGHT] = 20, [DamageType.COLD] = 50, }, - open_door = 1, - no_breath = 1, - confusion_immune = 1, - poison_immune = 1, - blind_immune = 1, - fear_immune = 1, - stun_immune = 1, - see_invisible = resolvers.mbonus(15, 5), - undead = 1, - name = "bone giant", color=colors.WHITE, - desc=_t[[A towering creature, made from the bones of dozens of dead bodies. It is covered by an unholy aura.]], - resolvers.nice_tile{image="invis.png", add_mos = {{image="npc/undead_giant_bone_giant.png", display_h=2, display_y=-1}}}, - max_life = resolvers.rngavg(100,120), - level_range = {1, nil}, exp_worth = 0, - combat_armor = 20, combat_def = 0, - on_melee_hit = {[DamageType.BLIGHT]=resolvers.mbonus(15, 5)}, - melee_project = {[DamageType.BLIGHT]=resolvers.mbonus(15, 5)}, - resolvers.talents{ T_BONE_ARMOUR={base=3, every=10, max=5}, T_STUN={base=3, every=10, max=5}, }, - }, - h_bone_giant = { - type = "undead", subtype = "giant", - blood_color = colors.GREY, - display = "K", - combat = { dam=resolvers.levelup(resolvers.mbonus(45, 20), 1, 1), atk=15, apr=10, dammod={str=0.8} }, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - infravision = 10, - life_rating = 12, - max_stamina = 90, - rank = 2, - size_category = 4, - movement_speed = 1.5, - autolevel = "warrior", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=2, }, - stats = { str=20, dex=12, mag=16, con=16 }, - resists = { [DamageType.PHYSICAL] = 20, [DamageType.BLIGHT] = 20, [DamageType.COLD] = 50, }, - open_door = 1, - no_breath = 1, - confusion_immune = 1, - poison_immune = 1, - blind_immune = 1, - fear_immune = 1, - stun_immune = 1, - see_invisible = resolvers.mbonus(15, 5), - undead = 1, - name = "heavy bone giant", color=colors.RED, - desc=_t[[A towering creature, made from the bones of hundreds of dead bodies. It is covered by an unholy aura.]], - resolvers.nice_tile{image="invis.png", add_mos = {{image="npc/undead_giant_heavy_bone_giant.png", display_h=2, display_y=-1}}}, - level_range = {1, nil}, exp_worth = 0, - max_life = resolvers.rngavg(100,120), - combat_armor = 20, combat_def = 0, - on_melee_hit = {[DamageType.BLIGHT]=resolvers.mbonus(15, 5)}, - melee_project = {[DamageType.BLIGHT]=resolvers.mbonus(15, 5)}, - resolvers.talents{ T_BONE_ARMOUR={base=3, every=10, max=5}, T_THROW_BONES={base=4, every=10, max=7}, T_STUN={base=3, every=10, max=5}, }, - }, - e_bone_giant = { - type = "undead", subtype = "giant", - blood_color = colors.GREY, - display = "K", - combat = { dam=resolvers.levelup(resolvers.mbonus(45, 20), 1, 1), atk=15, apr=10, dammod={str=0.8} }, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - infravision = 10, - life_rating = 12, - max_stamina = 90, - rank = 2, - size_category = 4, - movement_speed = 1.5, - autolevel = "warrior", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=2, }, - stats = { str=20, dex=12, mag=16, con=16 }, - resists = { [DamageType.PHYSICAL] = 20, [DamageType.BLIGHT] = 20, [DamageType.COLD] = 50, }, - open_door = 1, - no_breath = 1, - confusion_immune = 1, - poison_immune = 1, - blind_immune = 1, - fear_immune = 1, - stun_immune = 1, - see_invisible = resolvers.mbonus(15, 5), - undead = 1, - name = "eternal bone giant", color=colors.GREY, - desc=_t[[A towering creature, made from the bones of hundreds of dead bodies. It is covered by an unholy aura.]], - resolvers.nice_tile{image="invis.png", add_mos = {{image="npc/undead_giant_eternal_bone_giant.png", display_h=2, display_y=-1}}}, - level_range = {1, nil}, exp_worth = 0, - max_life = resolvers.rngavg(100,120), - combat_armor = 40, combat_def = 20, - on_melee_hit = {[DamageType.BLIGHT]=resolvers.mbonus(15, 5)}, - melee_project = {[DamageType.BLIGHT]=resolvers.mbonus(15, 5)}, - autolevel = "warriormage", - resists = {all = 50}, - resolvers.talents{ T_BONE_ARMOUR={base=5, every=10, max=7}, T_STUN={base=3, every=10, max=5}, T_SKELETON_REASSEMBLE=5, }, - }, - r_bone_giant = { - type = "undead", subtype = "giant", - blood_color = colors.GREY, - display = "K", - combat = { dam=resolvers.levelup(resolvers.mbonus(45, 20), 1, 1), atk=15, apr=10, dammod={str=0.8} }, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - infravision = 10, - life_rating = 12, - max_stamina = 90, - rank = 2, - size_category = 4, - movement_speed = 1.5, - autolevel = "warrior", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=2, }, - stats = { str=20, dex=12, mag=16, con=16 }, - resists = { [DamageType.PHYSICAL] = 20, [DamageType.BLIGHT] = 20, [DamageType.COLD] = 50, }, - open_door = 1, - no_breath = 1, - confusion_immune = 1, - poison_immune = 1, - blind_immune = 1, - fear_immune = 1, - stun_immune = 1, - see_invisible = resolvers.mbonus(15, 5), - undead = 1, - name = "runed bone giant", color=colors.RED, - desc=_t[[A towering creature, made from the bones of hundreds of dead bodies, rune-etched and infused with hateful sorceries.]], - resolvers.nice_tile{image="invis.png", add_mos = {{image="npc/undead_giant_runed_bone_giant.png", display_h=2, display_y=-1}}}, - level_range = {1, nil}, exp_worth = 0, - rank = 3, - max_life = resolvers.rngavg(100,120), - combat_armor = 20, combat_def = 40, - melee_project = {[DamageType.BLIGHT]=resolvers.mbonus(15, 15)}, - autolevel = "warriormage", - resists = {all = 30}, - resolvers.talents{ - T_BONE_ARMOUR={base=5, every=10, max=7}, - T_STUN={base=3, every=10, max=5}, - T_SKELETON_REASSEMBLE=5, - T_ARCANE_POWER={base=4, every=5, max = 8}, - T_MANATHRUST={base=4, every=5, max = 10}, - T_MANAFLOW={base=5, every=5, max = 10}, - T_STRIKE={base=4, every=5, max = 12}, - T_INNER_POWER={base=4, every=5, max = 10}, - T_EARTHEN_MISSILES={base=5, every=5, max = 10}, - }, - resolvers.sustains_at_birth(), - }, -} - -newTalent{ - name = "Undead Explosion", - type = {"spell/advanced-necrotic-minions",1}, - require = spells_req_high1, - points = 5, - mana = 30, - cooldown = 10, - tactical = { ATTACKAREA = { BLIGHT = 2 } }, - radius = function(self, t) return math.floor(self:combatTalentScale(t, 1.3, 2.7)) end, - range = 8, - requires_target = true, - no_npc_use = true, - getDamage = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 20, 70), 90, 0, 0, 25, 25) end, -- Limit to 50% life - action = function(self, t) - local tg = {type="hit", range=self:getTalentRange(t), talent=t, first_target="friend"} - local tx, ty, target = self:getTarget(tg) - if not tx or not ty or not target or not target.summoner or not target.summoner == self or not target.necrotic_minion then return nil end - - local tg = {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), talent=t, friendlyfire=not rng.percent(self:getTalentLevelRaw(self.T_DARK_EMPATHY) * 0.2)} - local dam = target.max_life * t.getDamage(self, t) / 100 - target:die() - target:project(tg, target.x, target.y, DamageType.BLIGHT, dam) - game.level.map:particleEmitter(target.x, target.y, tg.radius, "ball_acid", {radius=tg.radius}) - - game:playSoundNear(self, "talents/fireflash") - return true - end, - info = function(self, t) - return ([[Minions are only tools. You may dispose of them... Violently. - Makes the targeted minion explode for %d%% of its maximum life in a radius of %d as blight damage. - Beware! Don't get caught in the blast! (unless you know Dark Empthy: %d%% chance to ignore damage)]]): - tformat(t.getDamage(self, t),t.radius(self,t), self:getTalentLevelRaw(self.T_DARK_EMPATHY) * 20) - end, -} - -newTalent{ - name = "Assemble", - type = {"spell/advanced-necrotic-minions",2}, - require = spells_req_high2, - points = 5, - mana = 90, - cooldown = 25, - tactical = { ATTACK = 10 }, - requires_target = true, - on_pre_use = function(self, t) - local nb = 0 - local nbgolem = 0 - if game.party and game.party:hasMember(self) then - for act, def in pairs(game.party.members) do - if act.summoner and act.summoner == self and act.necrotic_minion then - if act.subtype ~= "giant" then nb = nb + 1 - else nbgolem = nbgolem + 1 end - end - end - else - for uid, act in pairs(game.level.entities) do - if act.summoner and act.summoner == self and act.necrotic_minion then - if act.subtype ~= "giant" then nb = nb + 1 - else nbgolem = nbgolem + 1 end - end - end - end - if nb < 3 then return false end - local maxgolem = 1 - if necroEssenceDead(self, true) then maxgolem = 2 end - if nbgolem >= maxgolem then return false end - return true - end, - getLevel = function(self, t) return math.floor(self:combatScale(self:getTalentLevel(t), -6, 0.9, 2, 5)) end, -- -6 @ 1, +2 @ 5, +5 @ 8 - action = function(self, t) - local nbgolem = 0 - local make_golem = function() - local list = {} - nbgolem = 0 - if game.party and game.party:hasMember(self) then - for act, def in pairs(game.party.members) do - if act.summoner and act.summoner == self and act.necrotic_minion then - if act.subtype ~= "giant" then list[#list+1] = act - else nbgolem = nbgolem + 1 end - end - end - else - for uid, act in pairs(game.level.entities) do - if act.summoner and act.summoner == self and act.necrotic_minion then - if act.subtype ~= "giant" then list[#list+1] = act - else nbgolem = nbgolem + 1 end - end - end - end - if #list < 3 then return end - - rng.tableRemove(list):die(self) - rng.tableRemove(list):die(self) - rng.tableRemove(list):die(self) - - local kind = ({"bone_giant","bone_giant","h_bone_giant","h_bone_giant","e_bone_giant"})[util.bound(math.floor(self:getTalentLevel(t)), 1, 5)] - if self:getTalentLevel(t) >= 6 and rng.percent(20) then kind = "r_bone_giant" end - - local minion = require("mod.class.NPC").new(minions_list[kind]) - local x, y = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true}) - if minion and x and y then - local lev = t.getLevel(self, t) - necroSetupSummon(self, minion, x, y, lev, true) - nbgolem = nbgolem + 1 - end - end - - local empower = necroEssenceDead(self) - for i = 1, empower and 2 or 1 do - make_golem() - if nbgolem >= (empower and 2 or 1) then break end - end - - if nbgolem >= 2 and empower then empower() end - - game:playSoundNear(self, "talents/spell_generic2") - return true - end, - info = function(self, t) - return ([[Combines 3 of your minions into a bone giant. - At level 1, it makes a bone giant. - At level 3, it makes a heavy bone giant. - At level 5, it makes an eternal bone giant. - At level 6, it has a 20%% chance to produce a runed bone giant. - Only %s can be active at any time.]]): - tformat(necroEssenceDead(self, true) and _t"two bone giants" or _t"one bone giant") - end, -} - -newTalent{ - name = "Sacrifice", - type = {"spell/advanced-necrotic-minions",3}, - require = spells_req_high3, - points = 5, - mana = 5, - cooldown = 25, - tactical = { DEFEND = 1 }, - requires_target = true, - on_pre_use = function(self, t) - if game.party and game.party:hasMember(self) then - for act, def in pairs(game.party.members) do - if act.summoner and act.summoner == self and act.necrotic_minion then - if act.subtype == "giant" then return true end - end - end - else - for uid, act in pairs(game.level.entities) do - if act.summoner and act.summoner == self and act.necrotic_minion then - if act.subtype == "giant" then return true end - end - end - end - return false - end, - getTurns = function(self, t) return math.floor(4 + self:combatTalentSpellDamage(t, 8, 20)) end, - getPower = function(self, t) return math.floor(self:combatTalentLimit(t, 10, 22.5, 16.5)) end, -- Limit >10% - action = function(self, t) - local list = {} - if game.party and game.party:hasMember(self) then - for act, def in pairs(game.party.members) do - if act.summoner and act.summoner == self and act.necrotic_minion and act.subtype == "giant" then list[#list+1] = act end - end - else - for uid, act in pairs(game.level.entities) do - if act.summoner and act.summoner == self and act.necrotic_minion and act.subtype == "giant" then list[#list+1] = act end - end - end - if #list < 1 then return end - - rng.tableRemove(list):die(self) - - self:setEffect(self.EFF_BONE_SHIELD, t.getTurns(self, t), {power=t.getPower(self, t)}) - - game:playSoundNear(self, "talents/spell_generic2") - return true - end, - info = function(self, t) - return ([[Sacrifice a bone giant minion. Using its bones, you make a temporary shield around you that prevents any attacks from doing more than %d%% of your total life. - The effect lasts %d turns.]]): - tformat(t.getPower(self, t), t.getTurns(self, t)) - end, -} - -newTalent{ - name = "Minion Mastery", - type = {"spell/advanced-necrotic-minions",4}, - require = spells_req_high4, - points = 5, - mode = "passive", - info = function(self, t) - return ([[Each minion you summon has a chance to be a more advanced form of undead. Your chance for each type of minion is as follows:%s]]): - tformat(self:callTalent(self.T_CREATE_MINIONS,"MinionChancesDesc")) - end, -} diff --git a/game/modules/tome/data/talents/spells/animus.lua b/game/modules/tome/data/talents/spells/animus.lua index 7b3cc71a7a6873b709bb757701858ab2ad884f07..d3934b33203cf687347cbe71ee488b2fcb309e8c 100644 --- a/game/modules/tome/data/talents/spells/animus.lua +++ b/game/modules/tome/data/talents/spells/animus.lua @@ -1,5 +1,5 @@ -- ToME - Tales of Maj'Eyal --- Copyright (C) 2009 - 2019 Nicolas Casalini +-- Copyright (C) 2009 - 2020 Nicolas Casalini -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by @@ -17,254 +17,150 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org -local Object = require "mod.class.Object" - newTalent{ - name = "Consume Soul", + name = "Soul Leech", type = {"spell/animus",1}, require = spells_req1, points = 5, - soul = 1, - cooldown = 10, - tactical = { HEAL = 1, MANA = 1 }, - getHeal = function(self, t) return (40 + self:combatTalentSpellDamage(t, 10, 520)) * (necroEssenceDead(self, true) and 1.5 or 1) end, - is_heal = true, - action = function(self, t) - self:attr("allow_on_heal", 1) - self:heal(self:spellCrit(t.getHeal(self, t)), self) - self:attr("allow_on_heal", -1) - self:incMana(self:spellCrit(t.getHeal(self, t)) / 3, self) - if core.shader.active(4) then - self:addParticles(Particles.new("shader_shield_temp", 1, {toback=true , size_factor=1.5, y=-0.3, img="healdark", life=25}, {type="healing", time_factor=6000, beamsCount=15, noup=2.0, beamColor1={0xcb/255, 0xcb/255, 0xcb/255, 1}, beamColor2={0x35/255, 0x35/255, 0x35/255, 1}})) - self:addParticles(Particles.new("shader_shield_temp", 1, {toback=false, size_factor=1.5, y=-0.3, img="healdark", life=25}, {type="healing", time_factor=6000, beamsCount=15, noup=1.0, beamColor1={0xcb/255, 0xcb/255, 0xcb/255, 1}, beamColor2={0x35/255, 0x35/255, 0x35/255, 1}})) + mode = "passive", + autolearn_talent = "T_SOUL_POOL", + getTurns = function(self, t) return self:combatTalentLimit(t, 1, 10, 3) end, + getTurnsByRank = function(self, t, target) + local base = t.getTurns(self, t) + if target.rank == 3.2 then return math.ceil(base * 3), true + elseif target.rank == 3.5 then return math.ceil(base * 2.2), true + elseif target.rank == 4 then return math.ceil(base * 1.3), true + elseif target.rank == 5 then return math.ceil(base), true + elseif target.rank >= 10 then return math.ceil(base / 2), true + else return 20, false + end + end, + callbackOnDealDamage = function(self, t, val, target, dead, death_note) + if dead then + self:incSoul(1) + else + if target:hasEffect(target.EFF_SOUL_LEECH) then return end -- Dont reset, we want it to exprei to leech + local turns, powerful = t.getTurnsByRank(self, t, target) + target:setEffect(target.EFF_SOUL_LEECH, turns, {src=self, powerful=powerful}) end - game:playSoundNear(self, "talents/heal") - if necroEssenceDead(self, true) then necroEssenceDead(self)() end - return true end, info = function(self, t) - local heal = t.getHeal(self, t) - return ([[Crush and consume one of your captured souls, healing you for %d life and restoring %d mana. - The life and mana healed will increase with your Spellpower.]]): - tformat(heal, heal / 3) + local npc = mod.class.NPC.new{rank=3.2} + local _, c_rare = npc:TextRank() + npc.rank = 3.5 local _, c_unique = npc:TextRank() + npc.rank = 4 local _, c_boss = npc:TextRank() + npc.rank = 5 local _, c_eboss = npc:TextRank() + + return ([[Each time you deal damage to a creature you apply Soul Leech to them. + If a creature dies with this effect active, you steal its soul. + Strong creatures and bosses are so overflowing with soul power that you steal a fragment of their soul every few turns: + %s- rare: at most every %d turns + %s- unique: at most every %d turns + %s- boss: at most every %d turns + %s- elite boss: at most every %d turns + ]]):tformat(c_rare, t.getTurnsByRank(self, t, {rank=3.2}), c_unique, t.getTurnsByRank(self, t, {rank=3.5}), c_boss, t.getTurnsByRank(self, t, {rank=4}), c_eboss, t.getTurnsByRank(self, t, {rank=5})) end, } newTalent{ - name = "Animus Hoarder", - type = {"spell/animus",2}, + name = "Consume Soul", + type = {"spell/animus", 2}, require = spells_req2, - mode = "passive", points = 5, - getMax = function(self, t) return math.floor(self:combatTalentScale(t, 2, 8)) end, - getChance = function(self, t) return self:combatTalentLimit(t, 100, 20, 80) end, - passives = function(self, t, p) - self:talentTemporaryValue(p, "extra_soul_chance", t.getChance(self, t)) - self:talentTemporaryValue(p, "max_soul", t.getMax(self, t)) - end, + soul = 1, + cooldown = 15, + getHeal = function(self, t) return 20 + self:combatTalentSpellDamage(t, 40, 450) end, + getMana = function(self, t) return 10 + self:combatTalentSpellDamage(t, 40, 180) end, + getSpellpower = function(self, t) return self:combatTalentScale(t, 15, 50) end, + tactical = { MANA=1, HEAL=2, BUFF=function(self) return self.life < 1 and 2 or 0 end}, + action = function(self, t, p) + if self.life < 1 then + self:setEffect(self.EFF_CONSUME_SOUL, 10, {power=t.getSpellpower(self, t)}) + end + self:attr("allow_on_heal", 1) + self:heal(self:spellCrit(t.getHeal(self, t)), self) + self:attr("allow_on_heal", -1) + self:incMana(t.getMana(self, t)) + return true + end, info = function(self, t) - local max, chance = t.getMax(self, t), t.getChance(self, t) - return ([[Your hunger for souls grows ever more. When you kill a creature you rip away its animus with great force, granting you a %d%% chance to gain one additional soul. - In addition you are able to store %d more souls.]]): - tformat(chance, max) + return ([[Consume a soul whole to rebuild your body, healing you by %d and generating %d mana. + If used below 1 life the surge increases your spellpower by %d for 10 turns. + The heal and mana increases with your Spellpower.]]): + tformat(t.getHeal(self, t), t.getMana(self, t), t.getSpellpower(self, t)) end, } newTalent{ - name = "Animus Purge", - type = {"spell/animus",3}, + name = "Torture Souls", + type = {"spell/animus", 3}, require = spells_req3, points = 5, - mana = 45, - soul = 2, - cooldown = 15, - range = 6, - proj_speed = 20, - requires_target = true, - no_npc_use = true, - direct_hit = function(self, t) if self:getTalentLevel(t) >= 3 then return true else return false end end, - target = function(self, t) - local tg = {type="hit", range=self:getTalentRange(t), talent=t} - return tg - end, - getMaxLife = function(self, t) return self:combatTalentLimit(t, 50, 10, 25) end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 35, 330) end, + mana = 25, + cooldown = 18, + tactical = { ATTACKAREA = { COLD=1, DARK=1 }, SOUL=2 }, + radius = 10, + range = 0, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 30, 300) end, + getNb = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5)) end, + target = function(self, t) return {type="ball", radius=self:getTalentRadius(t), talent=t} end, action = function(self, t) - local tg = self:getTalentTarget(t) - local x, y = self:getTarget(tg) - if not x or not y then return nil end - self:project(tg, x, y, function(px, py) - local m = game.level.map(px, py, Map.ACTOR) - if not m or not m.max_life or not m.life or m.on_die then return end - - if game.party and game.party:hasMember(self) then - for act, def in pairs(game.party.members) do - if act.summoner and act.summoner == self then - if act.type == "undead" and act.subtype == "husk" then - game.party:removeMember(act) - act:disappear(act) - end - end - end - end - - local dam = self:spellCrit(t.getDamage(self, t)) - local olddie = rawget(m, "die") - m.die = function() end - local oldclone = m.clone_on_hit - m.clone_on_hit = nil - DamageType:get(DamageType.DARKNESS).projector(self, px, py, DamageType.DARKNESS, dam) - m.clone_on_hit = oldclone - m.die = olddie - game.level.map:particleEmitter(px, py, 1, "dark") - if 100 * m.life / m.max_life <= t.getMaxLife(self, t) and self:checkHit(self:combatSpellpower(), m:combatSpellResist()) and m:canBe("instakill") and m.rank <= 3.2 and not m:attr("undead") and not m.summoner and not m.summon_time then - m.type = "undead" - m.subtype = "husk" - m:attr("no_life_regen", 1) - m:attr("no_healing", 1) - m.ai_state.tactic_leash = 100 - m.remove_from_party_on_death = true - m.no_inventory_access = true - m.no_party_reward = true - m.life = m.max_life - m.move_others = true - m.summoner = self - m.summoner_gain_exp = true - m.unused_stats = 0 - m.dead = nil - m.undead = 1 - m.no_breath = 1 - m.unused_talents = 0 - m.unused_generics = 0 - m.unused_prodigies = 0 - m.unused_talents_types = 0 - m.silent_levelup = true - m.clone_on_hit = nil - if m:knowTalent(m.T_BONE_SHIELD) then m:unlearnTalent(m.T_BONE_SHIELD, m:getTalentLevelRaw(m.T_BONE_SHIELD)) end - if m:knowTalent(m.T_MULTIPLY) then m:unlearnTalent(m.T_MULTIPLY, m:getTalentLevelRaw(m.T_MULTIPLY)) end - if m:knowTalent(m.T_SUMMON) then m:unlearnTalent(m.T_SUMMON, m:getTalentLevelRaw(m.T_SUMMON)) end - m:learnTalent(m.T_HUSK_DESTRUCT, 1) - m.no_points_on_levelup = true - m.faction = self.faction - - m.on_act = function(self) - if game.player ~= self then return end - if not self.summoner.dead and not self:hasLOS(self.summoner.x, self.summoner.y) then - if not self:hasEffect(self.EFF_HUSK_OFS) then - self:setEffect(self.EFF_HUSK_OFS, 3, {}) - end - else - if self:hasEffect(self.EFF_HUSK_OFS) then - self:removeEffect(self.EFF_HUSK_OFS) - end - end - end - - m.on_can_control = function(self, vocal) - if not self:hasLOS(self.summoner.x, self.summoner.y) then - if vocal then game.logPlayer(game.player, "Your husk is out of sight; you cannot establish direct control.") end - return false - end - return true - end - - m:removeEffectsFilter({status="detrimental"}, nil, true) - game.level.map:particleEmitter(px, py, 1, "demon_teleport") - - applyDarkEmpathy(self, m) - - game.party:addMember(m, { - control="full", - type="husk", - title=_t"Lifeless Husk", - orders = {leash=true, follow=true}, - on_control = function(self) - self:hotkeyAutoTalents() - end, - }) - game:onTickEnd(function() self:incSoul(2) end) - - self:logCombat(m, "#GREY##Source# rips apart the animus of #target# and creates an undead husk.") + local dam = self:spellCrit(t.getDamage(self, t)) + local nb = 0 + self:projectApply(self:getTalentTarget(t), self.x, self.y, Map.ACTOR, function(target) + if not target:hasEffect(target.EFF_SOUL_LEECH) then return end + if DamageType:get(DamageType.FROSTDUSK).projector(self, target.x, target.y, DamageType.FROSTDUSK, dam) > 0 then + nb = nb + 1 end - end) - - game:playSoundNear(self, "talents/spell_generic") + end, "hostile") + self:incSoul(math.min(nb, t.getNb(self, t))) return true end, info = function(self, t) - local damage = t.getDamage(self, t) - return ([[Try to crush the soul of your foe, doing %0.2f darkness damage (that can never kill the target). - If the target is left with less than %d%% life you try to take control of its body. - Should this succeed the target becomes your permanent minion (unaffected by your aura) and you regain 2 souls. - Husks prossess the same abilities as they had in life (affected by Dark Empathy), are healed to full when created but can never heal or be healed by any means. - Only one husk can be controlled at any time, if this spell is cast again it will dispell the previous husk, even if no new one is created. - Bosses, other undeads and summoned creatures can not be turned into husks. - The damage and chance will increase with your Spellpower.]]): - tformat(damDesc(self, DamageType.DARKNESS, damage), t.getMaxLife(self, t)) + return ([[Unleash dark forces to all foes in sight that are afflicted by Soul Leech, dealing %0.2f frostdusk damage to them and tearing apart their souls. + This returns up to %d souls toyou (based on number of foes hit). + The damage increases with your Spellpower.]]): + tformat(damDesc(self, DamageType.FROSTDUSK, t.getDamage(self, t)), t.getNb(self, t)) end, } newTalent{ - name = "Essence of the Dead", + name = "Reaping", type = {"spell/animus",4}, require = spells_req4, points = 5, - mana = 20, - soul = 2, - cooldown = 20, - tactical = { BUFF = 3 }, - getnb = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5)) end, - action = function(self, t) - self:setEffect(self.EFF_ESSENCE_OF_THE_DEAD, 1, {nb=t.getnb(self, t)}) - return true - end, - info = function(self, t) - local nb = t.getnb(self, t) - return ([[Crush and consume two souls to empower your next %d spells, granting them a special effect. - Affected spells are: - - Undeath Link: in addition to the heal a shield is created for half the heal power - - Create Minions: allows you to summon 2 more minions - - Assemble: allows you to summon a second bone golem - - Invoke Darkness: becomes a cone of darkness - - Shadow Tunnel: teleported minions will also be healed for 30%% of their max life - - Cold Flames: freeze chance increased to 100%% - - Freeze: becomes a ball of radius 2 and makes all targets wet - - Consume Soul: effect increased by 50%%]]): - tformat(nb) + mode = "sustained", + sustain_mana = 30, + getNb = function(self, t) return math.floor(self:combatTalentScale(t, 2, 8)) end, + getMana = function(self, t) return math.floor(self:combatTalentScale(t, 5, 30)) / 10 end, + getSpellpower = function(self, t) return math.floor(self:combatTalentScale(t, 10, 40)) end, + getResists = function(self, t) return math.floor(self:combatTalentLimit(t, 20, 5, 10)) end, + callbackOnActBase = function(self, t) + if not self.__old_reaping_souls then self.__old_reaping_souls = self:getSoul() end + if self.__old_reaping_souls == self:getSoul() then return end + self:updateTalentPassives(t) end, -} - - -newTalent{ - name = "Self-destruction", short_name = "HUSK_DESTRUCT", image = "talents/golem_destruct.png", - type = {"spell/other", 1}, - points = 1, - range = 0, - radius = 4, - no_unlearn_last = true, - target = function(self, t) - return {type="ball", range=self:getTalentRange(t), selffire=false, radius=self:getTalentRadius(t)} + passives = function(self, t, p) + if not self:isTalentActive(t.id) then return end + local s = self:getSoul() + if s >= 2 then self:talentTemporaryValue(p, "mana_regen", t.getMana(self, t)) end + if s >= 5 then self:talentTemporaryValue(p, "combat_spellpower", t.getSpellpower(self, t)) end + if s >= 8 then self:talentTemporaryValue(p, "resists", {all=t.getResists(self, t)}) end + self:talentTemporaryValue(p, "max_soul", t.getNb(self, t)) end, - tactical = { ATTACKAREA = { DARKNESS = 3 } }, - no_npc_use = true, - on_pre_use = function(self, t) - return self.summoner and self.summoner.dead + activate = function(self, t) + game:onTickEnd(function() self:updateTalentPassives(t) end) + return {} end, - action = function(self, t) - local tg = self:getTalentTarget(t) - self:project(tg, self.x, self.y, DamageType.DARKNESS, 50 + 10 * self.level) - if core.shader.active() then - game.level.map:particleEmitter(self.x, self.y, tg.radius, "starfall", {radius=tg.radius}) - else - game.level.map:particleEmitter(self.x, self.y, tg.radius, "shadow_flash", {radius=tg.radius}) - end - game:playSoundNear(self, "talents/fireflash") - self:die(self) + deactivate = function(self, t) return true end, info = function(self, t) - local rad = self:getTalentRadius(t) - return ([[The husk self-destructs, destroying itself and generating a blast of shadows in a radius of %d, doing %0.2f darkness damage. - This spell is only usable when the husk's master is dead.]]):tformat(rad, damDesc(self, DamageType.DARKNESS, 50 + 10 * self.level)) + return ([[You draw constant power from the souls you hold within your grasp. + If you hold at least 2, your mana regeneration is increased by %0.1f per turn. + If you hold at least 5, your spellpower is increased by %d. + If you hold at least 8, all your resistances are increased by %d. + Also increases your maximum souls capacity by %d.]]): + tformat(t.getMana(self, t), t.getSpellpower(self, t), t.getResists(self, t), t.getNb(self, t)) end, } diff --git a/game/modules/tome/data/talents/spells/grave.lua b/game/modules/tome/data/talents/spells/grave.lua deleted file mode 100644 index 4aa7d66ae019f00e828ce1ccb75eebfc0796b6e4..0000000000000000000000000000000000000000 --- a/game/modules/tome/data/talents/spells/grave.lua +++ /dev/null @@ -1,377 +0,0 @@ --- ToME - Tales of Maj'Eyal --- Copyright (C) 2009 - 2019 Nicolas Casalini --- --- This program is free software: you can redistribute it and/or modify --- it under the terms of the GNU General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- This program is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU General Public License for more details. --- --- You should have received a copy of the GNU General Public License --- along with this program. If not, see <http://www.gnu.org/licenses/>. --- --- Nicolas Casalini "DarkGod" --- darkgod@te4.org - -local Object = require "mod.class.Object" - -newTalent{ - name = "Chill of the Tomb", - type = {"spell/grave",1}, - require = spells_req1, - points = 5, - mana = 30, - cooldown = 8, - tactical = { ATTACKAREA = { COLD = 2 } }, - range = 7, - radius = function(self, t) - return math.floor(self:combatTalentScale(t, 2, 6, 0.5, 0, 0, true)) - end, - proj_speed = 4, - direct_hit = true, - requires_target = true, - target = function(self, t) - return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=self:spellFriendlyFire(), talent=t, display={particle="bolt_ice", trail="icetrail"}} - end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 28, 280) end, - action = function(self, t) - local tg = self:getTalentTarget(t) - local x, y = self:getTarget(tg) - if not x or not y then return nil end - self:projectile(tg, x, y, DamageType.COLD, self:spellCrit(t.getDamage(self, t)), function(self, tg, x, y, grids) - game.level.map:particleEmitter(x, y, tg.radius, "iceflash", {radius=tg.radius, tx=x, ty=y}) - end) - game:playSoundNear(self, "talents/ice") - return true - end, - info = function(self, t) - local damage = t.getDamage(self, t) - local radius = self:getTalentRadius(t) - return ([[Conjures up a bolt of cold that moves toward the target and explodes into a chilly circle of death, doing %0.2f cold damage in a radius of %d. - The damage will increase with your Spellpower. - Additionally, when Will o' the Wisp is sustained, minions killed by this spell will spawn Wisps.]]): - tformat(damDesc(self, DamageType.COLD, damage), radius) - end, - callbackOnKill = function(self, t, target, death_note) - if not death_note then return end - if not death_note.source_talent then return end - if not death_note.source_talent == self:getTalentFromId(self.T_CHILL_OF_THE_TOMB) then return end - local talent = self:isTalentActive(self.T_WILL_O__THE_WISP) - if talent then - if target.summoner and (target.summoner == self) and target.necrotic_minion then - local wisp = self:getTalentFromId(self.T_WILL_O__THE_WISP) - -- Handle any % chances or whatever you want involved here - wisp.summon(self, wisp, self:isTalentActive(self.T_WILL_O__THE_WISP).dam, target, self, true) - end - end - end, -} - -newTalent{ - name = "Will o' the Wisp", - type = {"spell/grave",2}, - require = spells_req2, - mode = "sustained", - points = 5, - sustain_mana = 60, - cooldown = 30, - tactical = { BUFF = 3 }, - getParams = function(self, t) return util.bound(30 + self:getTalentLevel(t) * 10, 30, 100), 20 + self:combatTalentSpellDamage(t, 25, 300) end, - summon = function(self, t, dam, src, killer, grave) - if not killer or not killer.faction or (self:reactionToward(killer) >= 0 and not grave) or self.dead then return end - local minion = require("mod.class.NPC").new{ - name = _t"will o' the wisp", - image = "npc/undead_ghost_will_o__the_wisp.png", - type = "undead", subtype = "ghost", - blood_color = colors.GREY, - display = "G", color=colors.WHITE, - combat = { dam=1, atk=1, apr=1 }, - autolevel = "warriormage", - ai = "dumb_talented_simple", ai_state = { talent_in=1, }, - dont_pass_target = true, - movement_speed = 2, - stats = { str=14, dex=18, mag=20, con=12 }, - rank = 2, - size_category = 1, - infravision = 10, - can_pass = {pass_wall=70}, - resists = {all = 35, [DamageType.LIGHT] = -70, [DamageType.COLD] = 65, [DamageType.DARKNESS] = 65}, - no_breath = 1, - stone_immune = 1, - confusion_immune = 1, - fear_immune = 1, - teleport_immune = 0.5, - disease_immune = 1, - poison_immune = 1, - stun_immune = 1, - blind_immune = 1, - cut_immune = 1, - see_invisible = 80, - undead = 1, - will_o_wisp_dam = dam, - talents = {T_WILL_O__THE_WISP_EXPLODE = 1}, - } - local x, y = util.findFreeGrid(src.x or self.x, src.y or self.y, 5, true, {[Map.ACTOR]=true}) - if minion and x and y then - necroSetupSummon(self, minion, x, y, lev, true) - --minion.on_die = nil - minion.on_act = nil - if not grave then minion:setTarget(killer) end - end - return true - end, - activate = function(self, t) - local chance, dam = t.getParams(self, t) - game:playSoundNear(self, "talents/spell_generic2") - local ret = { - chance = chance, - dam = dam, - } - return ret - end, - deactivate = function(self, t, p) - return true - end, - info = function(self, t) - local chance, dam = t.getParams(self, t) - return ([[Surround yourself with undead energies. When one of your minions is destroyed while inside your necrotic aura, it has a %d%% chance to create a will o' the wisp. - The will o' the wisp will take a random target in sight and home in on it. When it reaches the target, it will explode for %0.2f cold damage. - The damage will increase with your Spellpower.]]): - tformat(chance, damDesc(self, DamageType.COLD, dam)) - end, -} - --- Kinda copied from Creeping Darkness -newTalent{ - name = "Cold Flames", - type = {"spell/grave",3}, - require = spells_req3, - points = 5, - mana = 40, - cooldown = 22, - range = 5, - radius = 3, - tactical = { ATTACK = { COLD = 2 }, DISABLE = { stun = 1 } }, - requires_target = true, - -- implementation of creeping darkness..used in various locations, but stored here - canCreep = function(x, y, ignoreCreepingDark) - -- not on map - if not game.level.map:isBound(x, y) then return false end - -- already dark - if not ignoreCreepingDark then - if game.level.map:checkAllEntities(x, y, "coldflames") then return false end - end - -- allow objects and terrain to block, but not actors - if game.level.map:checkAllEntities(x, y, "block_move") and not game.level.map(x, y, Map.ACTOR) then return false end - - return true - end, - doCreep = function(tCreepingDarkness, self, useCreep) - local start = rng.range(0, 8) - for i = start, start + 8 do - local x = self.x + (i % 3) - 1 - local y = self.y + math.floor((i % 9) / 3) - 1 - if not (x == self.x and y == self.y) and tCreepingDarkness.canCreep(x, y) then - -- add new dark - local newCreep - if useCreep then - -- transfer some of our creep to the new dark - newCreep = math.ceil(self.creep / 2) - self.creep = self.creep - newCreep - else - -- just clone our creep - newCreep = self.creep - end - tCreepingDarkness.createDark(self.summoner, x, y, self.damage, self.originalDuration, newCreep, self.creepChance, 0) - return true - end - - -- nowhere to creep - return false - end - end, - createDark = function(summoner, x, y, damage, duration, creep, creepChance, initialCreep) - local e = Object.new{ - name = _t"cold flames", - canAct = false, - canCreep = true, - x = x, y = y, - damage = damage, - originalDuration = duration, - duration = duration, - creep = creep, - creepChance = creepChance, - summoner = summoner, - summoner_gain_exp = true, - act = function(self) - local Map = require "engine.Map" - - self:useEnergy() - - -- apply damage to anything inside the darkness - local actor = game.level.map(self.x, self.y, Map.ACTOR) - if actor and actor ~= self.summoner and (not actor.summoner or actor.summoner ~= self.summoner) then - self.summoner:project(actor, actor.x, actor.y, engine.DamageType.ICE, self.damage) - --DamageType:get(DamageType.DARKNESS).projector(self.summoner, actor.x, actor.y, DamageType.DARKNESS, damage) - end - - if self.duration <= 0 then - -- remove - if self.particles then game.level.map:removeParticleEmitter(self.particles) end - game.level.map:remove(self.x, self.y, Map.TERRAIN+3) - game.level:removeEntity(self) - self.coldflames = nil - --game.level.map:redisplay() - else - self.duration = self.duration - 1 - - local tCreepingDarkness = self.summoner:getTalentFromId(self.summoner.T_COLD_FLAMES) - - if self.canCreep and self.creep > 0 and rng.percent(self.creepChance) then - if not tCreepingDarkness.doCreep(tCreepingDarkness, self, true) then - -- doCreep failed..pass creep on to a neighbor and stop creeping - self.canCreep = false - local start = rng.range(0, 8) - for i = start, start + 8 do - local x = self.x + (i % 3) - 1 - local y = self.y + math.floor((i % 9) / 3) - 1 - if not (x == self.x and y == self.y) and tCreepingDarkness.canCreep(x, y) then - local dark = game.level.map:checkAllEntities(x, y, "coldflames") - if dark and dark.canCreep then - -- transfer creep - dark.creep = dark.creep + self.creep - self.creep = 0 - return - end - end - end - end - end - end - end, - } - e.coldflames = e -- used for checkAllEntities to return the dark Object itself - game.level:addEntity(e) - game.level.map(x, y, Map.TERRAIN+3, e) - - -- add particles - e.particles = Particles.new("coldflames", 1, { }) - e.particles.x = x - e.particles.y = y - game.level.map:addParticleEmitter(e.particles) - - -- do some initial creeping - if initialCreep > 0 then - local tCreepingDarkness = self.summoner:getTalentFromId(summoner.T_COLD_FLAMES) - while initialCreep > 0 do - if not tCreepingDarkness.doCreep(tCreepingDarkness, e, false) then - e.canCreep = false - e.initialCreep = 0 - break - end - initialCreep = initialCreep - 1 - end - end - end, - - getDarkCount = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, - getDamage = function(self, t) - return self:combatTalentSpellDamage(t, 10, 90) - end, - action = function(self, t) - local range = self:getTalentRange(t) - local radius = self:getTalentRadius(t) - local damage = {dam=t.getDamage(self, t), chance=25} - local darkCount = t.getDarkCount(self, t) - - local tg = {type="ball", nolock=true, pass_terrain=false, nowarning=true, friendly_fire=true, default_target=self, range=range, radius=radius, talent=t} - local x, y = self:getTarget(tg) - if not x or not y then return nil end - local _ _, _, _, x, y = self:canProject(tg, x, y) - - -- get locations in line of movement from center - local locations = {} - local grids = core.fov.circle_grids(x, y, radius, true) - for darkX, yy in pairs(grids) do for darkY, _ in pairs(grids[darkX]) do - local l = line.new(x, y, darkX, darkY) - local lx, ly = l() - while lx and ly do - if game.level.map:checkAllEntities(lx, ly, "block_move") then break end - - lx, ly = l() - end - if not lx and not ly then lx, ly = darkX, darkY end - - if lx == darkX and ly == darkY and t.canCreep(darkX, darkY) then - locations[#locations+1] = {darkX, darkY} - end - end end - - darkCount = math.min(darkCount, #locations) - if darkCount == 0 then return false end - - local empower = necroEssenceDead(self) - if empower then - damage.chance = 100 - empower() - end - for i = 1, darkCount do - local location, id = rng.table(locations) - table.remove(locations, id) - t.createDark(self, location[1], location[2], damage, 8, 4, 70, 0) - end - - game:playSoundNear(self, "talents/breath") - return true - end, - info = function(self, t) - local radius = self:getTalentRadius(t) - local damage = t.getDamage(self, t) - local darkCount = t.getDarkCount(self, t) - return ([[Cold Flames slowly spread from %d spots in a radius of %d around the targeted location. The flames deal %0.2f cold damage, and have a chance of freezing. - Damage improves with your Spellpower.]]):tformat(darkCount, radius, damDesc(self, DamageType.COLD, damage)) - end, -} - -newTalent{ - name = "Vampiric Gift", - type = {"spell/grave",4}, - require = spells_req4, - points = 5, - mode = "sustained", - sustain_mana = 250, - cooldown = 30, - tactical = { BUFF = 3 }, - getParams = function(self, t) return self:combatTalentLimit(t, 100, 25, 45), self:combatLimit(self:combatTalentSpellDamage(t, 5, 30), 100, 0, 0, 18.65, 18.65) end, -- Limit chance and life leach to <100% each - activate = function(self, t) - local chance, val = t.getParams(self, t) - game:playSoundNear(self, "talents/spell_generic2") - local particle - if core.shader.active(4) then - local bx, by = self:attachementSpot("back", true) - particle = self:addParticles(Particles.new("shader_wings", 1, {infinite=1, x=bx, y=by, img="darkwings"})) - end - local ret = { - chance = self:addTemporaryValue("life_leech_chance", chance), - val = self:addTemporaryValue("life_leech_value", val), - particle = particle, - } - return ret - end, - deactivate = function(self, t, p) - if p.particle then self:removeParticles(p.particle) end - self:removeTemporaryValue("life_leech_chance", p.chance) - self:removeTemporaryValue("life_leech_value", p.val) - return true - end, - info = function(self, t) - local chance, val = t.getParams(self, t) - return ([[Vampiric energies fill you; each time you deal damage, you have %d%% chance to heal for %d%% of the damage done. - The absorption percent will increase with your Spellpower.]]): - tformat(chance, val) - end, -} diff --git a/game/modules/tome/data/talents/spells/necrosis.lua b/game/modules/tome/data/talents/spells/necrosis.lua index c1859dc8515e1dd744c58de2bfbcdd892af37407..16c1936ffafa6ba7044f117afb0a7d9d956d3d3e 100644 --- a/game/modules/tome/data/talents/spells/necrosis.lua +++ b/game/modules/tome/data/talents/spells/necrosis.lua @@ -21,277 +21,151 @@ newTalent{ name = "Blurred Mortality", type = {"spell/necrosis",1}, require = spells_req1, - mode = "sustained", + mode = "passive", points = 5, - sustain_mana = 30, - cooldown = 30, - tactical = { BUFF = 2 }, - lifeBonus = function(self, t) -- Add fraction of max life - return 50 * self:getTalentLevelRaw(t) + self.max_life * self:combatTalentLimit(t, 1, .01, .05) + getLifeBonus = function(self, t) + return self:combatTalentStatDamage(t, "con", 30, 1000) end, - activate = function(self, t) - if self.player and not self:attr("no_lichform_quest") and not self:hasQuest("lichform") and not self:attr("undead") then - self:grantQuest("lichform") - if not game:isCampaign("Maj'Eyal") then self:setQuestStatus("lichform", engine.Quest.DONE) end - require("engine.ui.Dialog"):simplePopup(_t"Lichform", _t"You have mastered the lesser arts of overcoming death, but your true goal is before you: the true immortality of Lichform!") - end - - local ret = { - die_at = self:addTemporaryValue("die_at", -t.lifeBonus(self, t)), - } -- Add up to 100% max life - return ret + getLifeLostFactor = function(self, t) + return 0.5 end, - deactivate = function(self, t, p) - self:removeTemporaryValue("die_at", p.die_at) - return true + getResists = function(self, t) + return self:combatTalentScale(t, 6, 12) + end, + callbackOnStatChange = function(self, t, stat, v) + if stat == self.STAT_CON then self:updateTalentPassives(t) end + end, + callbackOnActBase = checkLifeThreshold(1, function(self, t) + self:updateTalentPassives(t) + end), + passives = function(self, t, p) + local bonus = t.getLifeBonus(self, t) + self:talentTemporaryValue(p, "die_at", -bonus) + self:talentTemporaryValue(p, "max_life", -math.ceil(bonus * t.getLifeLostFactor(self, t))) + if self.life < 1 then + self:talentTemporaryValue(p, "resists", {all=t.getResists(self, t)}) + end end, info = function(self, t) - return ([[The line between life and death blurs for you; you can only die when you reach -%d life.]]): - tformat(t.lifeBonus(self, t)) + local bonus = t.getLifeBonus(self, t) + return ([[The line between life and death blurs for you. + You can only die when you reach -%d life but your maximum life is reduced by %d. + When you are below 1 life you gain %d%% to all resistances. + The life amount is based on your Constitution attribute.]]): + tformat(bonus, math.ceil(bonus * t.getLifeLostFactor(self, t)), t.getResists(self, t)) end, } newTalent{ - name = "Impending Doom", + name = "Across the Veil", type = {"spell/necrosis",2}, require = spells_req2, + mode = "passive", points = 5, - mana = 60, - cooldown = 25, - tactical = { ATTACK = { ARCANE = 3 }, DISABLE = 2 }, - rnd_boss_restrict = function(self, t, data) - return data.level < 15 - end, - range = 7, - requires_target = true, - getMax = function(self, t) return 200 + self:combatTalentSpellDamage(t, 28, 850) end, - getDamage = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 10, 100), 150, 50, 0, 117, 67) end, -- Limit damage factor to < 150% - action = function(self, t) - local tg = {type="hit", range=self:getTalentRange(t), talent=t} - local x, y = self:getTarget(tg) - if not x or not y then return nil end - self:project(tg, x, y, function(px, py) - local target = game.level.map(px, py, Map.ACTOR) - if not target then return end - local dam = target.life * t.getDamage(self, t) / 100 - dam = math.min(dam, t.getMax(self, t)) - target:setEffect(target.EFF_IMPENDING_DOOM, 10, {apply_power=self:combatSpellpower(), dam=dam/10, src=self}) - end, 1, {type="freeze"}) + radius = function(self, t) return self:combatTalentLimit(t, 10, 2, 5) end, + getCD = function(self, t) return math.ceil(self:combatTalentLimit(t, 12, 2, 8)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 40, 300) end, + callbackOnActBase = checkLifeThreshold(1, function(self, t) + local list = {} + for tid, c in pairs(self.talents_cd) do + local t = self:getTalentFromId(tid) + if t and not t.fixed_cooldown then list[#list+1] = tid end + end + + local cd = t.getCD(self, t) + local dam = self:spellCrit(t.getDamage(self, t)) + game.logSeen(self, "#GREY#%s unleashes a blast of frostdusk as %s crosses the veil!", self:getName():capitalize(), string.he_she(self)) + self:projectApply({type="ball", radius=self:getTalentRadius(t), talent=t}, self.x, self.y, Map.ACTOR, function(target, px, py) + local d = DamageType:get(DamageType.FROSTDUSK).projector(self, target.x, target.y, DamageType.FROSTDUSK, dam) + if d > 0 and #list > 0 then + self:alterTalentCoolingdown(rng.tableRemove(list), -cd) + end + end, "hostile") + game.level.map:particleEmitter(self.x, self.y, self:getTalentRadius(t), "shockwave", {radius=self:getTalentRadius(t), distort_color=colors.simple1(colors.BLACK), allow=core.shader.allow("distort")}) return true - end, + end), info = function(self, t) - return ([[Your target's doom draws near. Its healing factor is reduced by 80%%, and will take %d%% of its remaining life (or %0.2f, whichever is lower) over 10 turns as arcane damage. + return ([[As you learn to tiptoe across the veil of death you learn to master the dark forces. + Each time you cross the 1 life threshold you automatically unleash a blast of %0.2f frostdusk damage in radius %d. + For each creature that takes damage from the blast one of your talent's cooldown is reduced by %d turns. The damage will increase with your Spellpower.]]): - tformat(t.getDamage(self, t), t.getMax(self, t)) + tformat(damDesc(self, DamageType.FROSTDUSK, t.getDamage(self, t)), self:getTalentRadius(t), t.getCD(self, t)) end, } newTalent{ - name = "Undeath Link", + name = "Runeskin", type = {"spell/necrosis",3}, require = spells_req3, points = 5, - random_ego = "attack", - mana = 30, - cooldown = 18, - tactical = { HEAL = 2 }, - is_heal = true, - getHeal = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 10, 70), 100, 20, 0, 66.7, 46.7) end, --Limit to <100% - on_pre_use = function(self, t) - if game.party and game.party:hasMember(self) then - for act, def in pairs(game.party.members) do - if act.summoner and act.summoner == self and act.necrotic_minion then - return true - end - end - else - for uid, act in pairs(game.level.entities) do - if act.summoner and act.summoner == self and act.necrotic_minion then - return true - end + mode = "passive", + no_npc_use = true, -- They mostly wouldnt use it efficiently + getLifeBonus = function(self, t) return self:combatTalentScale(t, 8, 30) end, + getCrit = function(self, t) return self:combatTalentScale(t, 1, 3) end, + countRunes = function(self, t) + local nb = 0 + for tid, lvl in pairs(self.talents) do + local tt = self:getTalentFromId(tid) + if tt.is_inscription then + if tt.is_nature then nb = -1 break end -- FILTHY NATURE USER! WE ARE DONE WITH YOU! + if tt.is_spell then nb = nb + 1 end end end - return false + return nb + end, + callbackOnTalentChange = function(self, t, tid, mode, lvldiff) + if self:getTalentFromId(tid).is_inscription then self:updateTalentPassives(t) end end, - action = function(self, t) - local heal = t.getHeal(self, t) - local maxdrain = 0 --Use biggest drain for healing purposes - local drain = 0 - if game.party and game.party:hasMember(self) then - for act, def in pairs(game.party.members) do - if act.summoner and act.summoner == self and act.necrotic_minion then - drain = math.min(act.max_life * heal / 100, act.life-act.die_at) - act:takeHit(drain, self) - maxdrain = math.max(maxdrain, drain) - end - end - else - for uid, act in pairs(game.level.entities) do - if act.summoner and act.summoner == self and act.necrotic_minion then - drain = math.min(act.max_life * heal / 100, act.life-act.die_at) - act:takeHit(drain, self) - maxdrain = math.max(maxdrain, drain) - end - end - end - self:attr("allow_on_heal", 1) - self:heal(maxdrain) - local empower = necroEssenceDead(self) - if empower then - self:setEffect(self.EFF_DAMAGE_SHIELD, 4, {color={0xcb/255, 0xcb/255, 0xcb/255}, power=maxdrain * 0.3}) - empower() - end - self:attr("allow_on_heal", -1) - if core.shader.active(4) then - self:addParticles(Particles.new("shader_shield_temp", 1, {size_factor=1.5, y=-0.3, img="healdark", life=25}, {type="healing", time_factor=6000, beamsCount=15, noup=2.0, beamColor1={0xcb/255, 0xcb/255, 0xcb/255, 1}, beamColor2={0x35/255, 0x35/255, 0x35/255, 1}})) - self:addParticles(Particles.new("shader_shield_temp", 1, {size_factor=1.5, y=-0.3, img="healdark", life=25}, {type="healing", time_factor=6000, beamsCount=15, noup=1.0, beamColor1={0xcb/255, 0xcb/255, 0xcb/255, 1}, beamColor2={0x35/255, 0x35/255, 0x35/255, 1}})) - end - game:playSoundNear(self, "talents/ice") - return true + passives = function(self, t, p) + local nb = t.countRunes(self, t) + if nb < 1 then return end + self:talentTemporaryValue(p, "die_at", -t.getLifeBonus(self, t) * nb) + self:talentTemporaryValue(p, "combat_spellcrit", t.getCrit(self, t) * nb) end, info = function(self, t) - local heal = t.getHeal(self, t) - return ([[Absorb up to %d%% of the maximum life of each of your necrotic minions (even negative life, possibly destroying them). This will heal you for the greatest amount absorbed. - The healing will increase with your Spellpower.]]): - tformat(heal) + local bonus + local nb = t.countRunes(self, t) + if nb == -1 then bonus = _t"effects disabled because of an infusion" + elseif nb == 0 then bonus = _t"effects disabled because of no rune" + elseif nb > 0 then bonus = ("%d runes active"):tformat(nb) end + + return ([[As you continue to attune your body to undeath you reject nature as a whole. + As long as you have no natural infusion on your skin, each rune on it increases your minimum negative life by -%d and your spells critical chance by %d%%. + + Currently: %s]]): + tformat(t.getLifeBonus(self, t), t.getCrit(self, t), bonus) end, } newTalent{ - name = "Lichform", + name = "Spikes of Decrepitude", type = {"spell/necrosis",4}, - require = { - stat = { mag=function(level) return 40 + (level-1) * 2 end }, - level = function(level) return 20 + (level-1) end, - special = { desc=_t"'From Death, Life' quest completed and not already undead", fct=function(self, t) return not self:attr("undead") and (self:isQuestStatus("lichform", engine.Quest.DONE) or game.state.birth.ignore_prodigies_special_reqs) end}, - }, - mode = "sustained", + require = spells_req4, + mode = "passive", points = 5, - sustain_mana = 150, - cooldown = 30, - no_unlearn_last = true, - no_npc_use = true, - becomeLich = function(self, t) - self.has_used_lichform = true - self.descriptor.race = "Undead" - self.descriptor.subrace = "Lich" - if not self.has_custom_tile then - self.moddable_tile = "skeleton" - self.moddable_tile_nude = 1 - self.moddable_tile_base = "base_lich_01.png" - self.moddable_tile_ornament = nil - self.moddable_tile_hair = nil - self.moddable_tile_facial_features = nil - self.moddable_tile_tatoo = nil - self.moddable_tile_horn = nil - self.attachement_spots = "race_skeleton" - end - self.blood_color = colors.GREY - self:attr("poison_immune", 1) - self:attr("disease_immune", 1) - self:attr("stun_immune", 1) - self:attr("cut_immune", 1) - self:attr("fear_immune", 1) - self:attr("no_breath", 1) - self:attr("undead", 1) - self.resists[DamageType.COLD] = (self.resists[DamageType.COLD] or 0) + 20 - self.resists[DamageType.DARKNESS] = (self.resists[DamageType.DARKNESS] or 0) + 20 - self.inscription_forbids = self.inscription_forbids or {} - self.inscription_forbids["inscriptions/infusions"] = true - - local level = self:getTalentLevel(t) - if level < 2 then - self:incIncStat("mag", -3) self:incIncStat("wil", -3) - self.resists.all = (self.resists.all or 0) - 10 - elseif level < 3 then - -- nothing - elseif level < 4 then - self:incIncStat("mag", 3) self:incIncStat("wil", 3) - self.life_rating = self.life_rating + 1 - elseif level < 5 then - self:incIncStat("mag", 3) self:incIncStat("wil", 3) - self:attr("combat_spellresist", 10) self:attr("combat_mentalresist", 10) - self.life_rating = self.life_rating + 2 - self:learnTalentType("celestial/star-fury", true) - self:setTalentTypeMastery("celestial/star-fury", self:getTalentTypeMastery("celestial/star-fury") - 0.3) - self.negative_regen = self.negative_regen + 0.2 + 0.1 - elseif level < 6 then - self:incIncStat("mag", 5) self:incIncStat("wil", 5) - self:attr("combat_spellresist", 10) self:attr("combat_mentalresist", 10) - self.resists_cap.all = (self.resists_cap.all or 0) + 10 - self.life_rating = self.life_rating + 2 - self:learnTalentType("celestial/star-fury", true) - self:setTalentTypeMastery("celestial/star-fury", self:getTalentTypeMastery("celestial/star-fury") - 0.1) - self.negative_regen = self.negative_regen + 0.2 + 0.5 - elseif level < 7 then - self:incIncStat("mag", 6) self:incIncStat("wil", 6) self:incIncStat("cun", 6) - self:attr("combat_spellresist", 15) self:attr("combat_mentalresist", 15) - self.resists_cap.all = (self.resists_cap.all or 0) + 15 - self.life_rating = self.life_rating + 3 - self:learnTalentType("celestial/star-fury", true) - self:setTalentTypeMastery("celestial/star-fury", self:getTalentTypeMastery("celestial/star-fury") + 0.1) - self.negative_regen = self.negative_regen + 0.2 + 1 - else -- level 7 - self:incIncStat("mag", 12) self:incIncStat("wil", 12) self:incIncStat("cun", 12) - self:attr("combat_spellresist", 35) self:attr("combat_mentalresist", 35) - self.resists_cap.all = (self.resists_cap.all or 0) + 15 - self.life_rating = self.life_rating + 4 - self:attr("ignore_direct_crits", 60) - self:learnTalentType("celestial/star-fury", true) - self:setTalentTypeMastery("celestial/star-fury", self:getTalentTypeMastery("celestial/star-fury") + 0.3) - self.negative_regen = self.negative_regen + 0.2 + 1 - end - - if self:attr("blood_life") then - self.blood_life = nil - game.log("#GREY#As you turn into a powerful undead you feel your body violently rejecting the Blood of Life.") - end - - if not self.has_custom_tile then - self:removeAllMOs() - self:updateModdableTile() - require("engine.ui.Dialog"):yesnoLongPopup(_t"Lichform", _t"#GREY#You feel your life slip away, only to be replaced by pure arcane forces! Your flesh starts to rot on your bones, and your eyes fall apart as you are reborn into a Lich!\n\n#{italic}#You may now choose to customize the appearance of your Lich, this can not be changed afterwards.", 600, function(ret) if ret then - require("mod.dialogs.Birther"):showCosmeticCustomizer(self, "Lich Cosmetic Options") - end end, _t"Customize Appearance", _t"Use Default", true) - else - require("engine.ui.Dialog"):simplePopup(_t"Lichform", _t"#GREY#You feel your life slip away, only to be replaced by pure arcane forces! Your flesh starts to rot on your bones, and your eyes fall apart as you are reborn into a Lich!") + radius = 10, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 5, 70) end, + getReduce = function(self, t) return self:combatTalentLimit(t, 50, 8, 25) end, + callbackOnActBase = function(self, t) + local runes = self:callTalent(self.T_RUNESKIN, "countRunes") + if runes < 1 then return end + local dam = t.getDamage(self, t) -- no crit + local reduce = t.getReduce(self, t) + local targets = table.keys(self:projectCollect({type="ball", radius=self:getTalentRadius(t), talent=t}, self.x, self.y, Map.ACTOR, "hostile")) + while #targets > 0 and runes > 0 do + local target = rng.tableRemove(targets) + runes = runes - 1 + DamageType:get(DamageType.FROSTDUSK).projector(self, target.x, target.y, DamageType.FROSTDUSK, dam) + if self.life < 1 then + target:setEffect(target.EFF_SPIKE_OF_DECREPITUDE, 2, {apply_power=self:combatSpellpower(), power=reduce}) + end end - - game.level.map:particleEmitter(self.x, self.y, 1, "demon_teleport") - end, - on_pre_use = function(self, t) - if self:attr("undead") then return false else return true end - end, - activate = function(self, t) - local ret = { - mana = self:addTemporaryValue("mana_regen", -4), - } - return ret - end, - deactivate = function(self, t, p) - self:removeTemporaryValue("mana_regen", p.mana) - return true end, info = function(self, t) - return ([[This is your true goal and the purpose of all necromancy - to become a powerful and everliving Lich! - If you are killed while this spell is active, the arcane forces you unleash will be able to rebuild your body into the desired Lichform. - All liches gain the following intrinsics: - - Poison, cut, and fear immunity. - - 100%% disease and stun resistance. - - 20%% cold and darkness resistance. - - No need to breathe. - - Infusions do not work. - Also: - At level 1: -3 to all stats, -10%% to all resistances. Such meagre devotion! - At level 2: Nothing. - At level 3: +3 Magic and Willpower, +1 life rating (not retroactive). - At level 4: +3 Magic and Willpower, +2 life rating (not retroactive), +10 spell and mental saves, Celestial/Star Fury category (0.7) and 0.1 negative energies regeneration. - At level 5: +5 Magic and Willpower, +2 life rating (not retroactive), +10 spell and mental saves, all resistance caps raised by 10%%, Celestial/Star Fury category (0.9) and 0.5 negative energy regeneration. - At level 6: +6 Magic, Willpower and Cunning, +3 life rating (not retroactive), +15 spell and mental saves, all resistance caps raised by 15%%, Celestial/Star Fury category (1.1) and 1.0 negative energy regeneration. - At level 7: #CRIMSON##{bold}#Your power becomes overwhelming!#{normal}##LAST# +12 Magic, Willpower and Cunning, 60%% chance to ignore critical hits, +4 life rating (not retroactive), +35 spell and mental saves, all resistance caps raised by 15%%, Celestial/Star Fury category (1.3) and 1.0 negative energy regeneration. - The undead cannot use this talent. - While active, it will drain 4 mana per turn. - Once you die and turn into a Lich you can not invest any more in this talent.]]): - tformat() + return ([[Each turn you unleash dark powers through your runeskin. + For each rune you have a random foe in sight will be hit by a spike of decrepitude, dealing %0.2f frostdusk damage. + A foe can only be hit by one spike per turn. + If your life is below 1, the spikes also reduce all damage done by the targets by %d%%.]]): + tformat(damDesc(self, DamageType.FROSTDUSK, t.getDamage(self, t)), t.getReduce(self, t)) end, } diff --git a/game/modules/tome/data/talents/spells/necrotic-minions.lua b/game/modules/tome/data/talents/spells/necrotic-minions.lua deleted file mode 100644 index 817efb5d4e3c295e42567721300655e436ebf1cd..0000000000000000000000000000000000000000 --- a/game/modules/tome/data/talents/spells/necrotic-minions.lua +++ /dev/null @@ -1,892 +0,0 @@ --- ToME - Tales of Maj'Eyal --- Copyright (C) 2009 - 2019 Nicolas Casalini --- --- This program is free software: you can redistribute it and/or modify --- it under the terms of the GNU General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- This program is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU General Public License for more details. --- --- You should have received a copy of the GNU General Public License --- along with this program. If not, see <http://www.gnu.org/licenses/>. --- --- Nicolas Casalini "DarkGod" --- darkgod@te4.org - -newTalent{ - name = "Necrotic Aura", image = "talents/aura_mastery.png", - type = {"spell/other", 1}, - points = 1, - mode = "sustained", - autolearn_talent = "T_SOUL_POOL", - cooldown = 10, - sustain_mana = 10, - no_unlearn_last = true, - tactical = { BUFF = 2 }, - die_speach = function(self, t) - if rng.percent(90) then return end - self:doEmote(rng.table{ - _t"Noooooo!", - _t"Save me, Master, save meeee---", - _t"Aaaauuuggghhh!", - _t"Did I do good?", - _t"Bwuh? Nwaaah!", - _t"Why, Master, whyyyyy---?", - _t"I thought you loved me! I thought-", - _t"For Master's glory!", - _t"Bye... bye....", - _t"We love you, Master!", - _t"EeeeeeeaaaAAAAAUUUUUGGGGGHHHHH!!!!", - _t"The pain, the PAAAAAIN!", - _t"Please, no, nooo--", - _t"Unlife no more for this dead matter, the time comes for my flesh to splatter.", - _t"You gave back life, you gave back dreams, but now I'm bursting at the seams...", - _t"Remember meeeee!", - _t"My tummy hurts...", - _t"Whu..?", - _t"Ahahahahaha!", - _t"Me go boom, me go BOOM!", - _t"Grave circumstances, Master....", - _t"I see the light.. I see, oh.. just a wisp....", - _t"Master, wait... I thought I saw a....Master? ..", - _t"I'm not.. so sure my spine is supposed to bend this way....", - _t"I told you I could Dash 100 yards and back in time! You owe me 10 gol....", - }, 40) - end, - getDecay = function(self, t) return math.max(3, 10 - self:getTalentLevelRaw(self.T_AURA_MASTERY)) end, - getRadius = function(self, t) return 2 + self:callTalent(self.T_AURA_MASTERY, "getbonusRadius") end, - absorbSoul = function(self, t, victim) - local nb = self:getSoul() - self:incSoul(1) - if self:attr("extra_soul_chance") and rng.percent(self:attr("extra_soul_chance")) then - self:incSoul(1) - game.logPlayer(self, "%s rips more animus from its victim. (+1 more soul)", self:getName():capitalize()) - end - self.changed = true - return self:getSoul() - nb - end, - activate = function(self, t) - local radius = t.getRadius(self, t) - local decay = t.getDecay(self, t) - game:playSoundNear(self, "talents/spell_generic2") - local ret = { - rad = self:addTemporaryValue("necrotic_aura_radius", radius), - decay = self:addTemporaryValue("necrotic_aura_decay", decay), - retch = self:addTemporaryValue("retch_heal", 1), - particle = self:addParticles(Particles.new("necrotic-aura", 1, {radius=radius})), - particle2 = self:addParticles(Particles.new("circle", 1, {oversize=0.7, a=75, appear=8, speed=8, img="necro_aura", radius=radius})), - } - return ret - end, - deactivate = function(self, t, p) - self:removeParticles(p.particle) - self:removeParticles(p.particle2) - self:removeTemporaryValue("retch_heal", p.retch) - self:removeTemporaryValue("necrotic_aura_radius", p.rad) - self:removeTemporaryValue("necrotic_aura_decay", p.decay) - return true - end, - info = function(self, t) - local radius = t.getRadius(self, t) - local decay = t.getDecay(self, t) - return ([[Emits a necrotic aura, sustaining your undead minions in a radius of %d. Minions outside the radius will lose %d%% life per turn. - Any creature you or your minions kill within your aura will be absorbed as a soul that can be used to raise minions. - Retch from your ghouls will also heal you, even if you are not undead.]]): - tformat(radius, decay) - end, -} - - -local minions_list = { - d_skel_warrior = { - type = "undead", subtype = "skeleton", - name = "degenerated skeleton warrior", color=colors.WHITE, image="npc/degenerated_skeleton_warrior.png", - blood_color = colors.GREY, - display = "s", - combat = { dam=1, atk=1, apr=1 }, - level_range = {1, nil}, exp_worth = 0, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, QUIVER=1 }, - infravision = 10, - rank = 2, - size_category = 3, - autolevel = "warrior", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=4, }, - stats = { str=14, dex=12, mag=10, con=12 }, - resolvers.racial(), - resolvers.tmasteries{ ["technique/other"]=0.3, ["technique/2hweapon-offense"]=0.3, ["technique/2hweapon-cripple"]=0.3 }, - open_door = true, - cut_immune = 1, - blind_immune = 1, - poison_immune = 1, - fear_immune = 1, - see_invisible = 2, - undead = 1, - rarity = 1, - - resolvers.equip{ {type="weapon", subtype="greatsword", autoreq=true} }, - max_life = resolvers.rngavg(40,50), - combat_armor = 5, combat_def = 1, - }, - skel_warrior = { - type = "undead", subtype = "skeleton", - name = "skeleton warrior", color=colors.SLATE, image="npc/skeleton_warrior.png", - blood_color = colors.GREY, - display = "s", color=colors.SLATE, - combat = { dam=1, atk=1, apr=1 }, - level_range = {1, nil}, exp_worth = 0, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, QUIVER=1 }, - infravision = 10, - rank = 2, - size_category = 3, - autolevel = "warrior", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=4, }, - stats = { str=14, dex=12, mag=10, con=12 }, - resolvers.racial(), - resolvers.tmasteries{ ["technique/other"]=0.3, ["technique/2hweapon-offense"]=0.3, ["technique/2hweapon-cripple"]=0.3 }, - open_door = true, - cut_immune = 1, - blind_immune = 1, - fear_immune = 1, - see_invisible = 2, - poison_immune = 1, - undead = 1, - rarity = 1, - - max_life = resolvers.rngavg(90,100), - combat_armor = 5, combat_def = 1, - resolvers.equip{ {type="weapon", subtype="greatsword", autoreq=true} }, - resolvers.talents{ T_STUNNING_BLOW={base=1, every=7, max=5}, T_WEAPON_COMBAT={base=1, every=7, max=10}, T_WEAPONS_MASTERY={base=1, every=7, max=10}, }, - ai_state = { talent_in=1, }, - }, - a_skel_warrior = { - type = "undead", subtype = "skeleton", - name = "armoured skeleton warrior", color=colors.STEEL_BLUE, image="npc/armored_skeleton_warrior.png", - blood_color = colors.GREY, - display = "s", color=colors.STEEL_BLUE, - combat = { dam=1, atk=1, apr=1 }, - level_range = {1, nil}, exp_worth = 0, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, QUIVER=1 }, - infravision = 10, - rank = 2, - size_category = 3, - autolevel = "warrior", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=4, }, - stats = { str=14, dex=12, mag=10, con=12 }, - resolvers.racial(), - resolvers.tmasteries{ ["technique/other"]=0.3, ["technique/2hweapon-offense"]=0.3, ["technique/2hweapon-cripple"]=0.3 }, - open_door = true, - cut_immune = 1, - blind_immune = 1, - fear_immune = 1, - poison_immune = 1, - see_invisible = 2, - undead = 1, - rarity = 1, - - resolvers.inscriptions(1, "rune"), - resolvers.talents{ - T_WEAPON_COMBAT={base=1, every=7, max=10}, - T_WEAPONS_MASTERY={base=1, every=7, max=10}, - T_ARMOUR_TRAINING={base=2, every=14, max=4}, - T_SHIELD_PUMMEL={base=1, every=7, max=5}, - T_RIPOSTE={base=3, every=7, max=7}, - T_OVERPOWER={base=1, every=7, max=5}, - T_DISARM={base=3, every=7, max=7}, - }, - resolvers.equip{ {type="weapon", subtype="longsword", autoreq=true}, {type="armor", subtype="shield", autoreq=true}, {type="armor", subtype="heavy", autoreq=true} }, - ai_state = { talent_in=1, }, - }, - skel_archer = { - type = "undead", subtype = "skeleton", - name = "skeleton archer", color=colors.UMBER, image="npc/skeleton_archer.png", - blood_color = colors.GREY, - display = "s", - combat = { dam=1, atk=1, apr=1 }, - level_range = {1, nil}, exp_worth = 0, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, QUIVER=1 }, - infravision = 10, - rank = 2, - size_category = 3, - autolevel = "warrior", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=4, }, - stats = { str=14, dex=12, mag=10, con=12 }, - resolvers.racial(), - resolvers.tmasteries{ ["technique/other"]=0.3, ["technique/2hweapon-offense"]=0.3, ["technique/2hweapon-cripple"]=0.3 }, - open_door = true, - cut_immune = 1, - blind_immune = 1, - fear_immune = 1, - poison_immune = 1, - see_invisible = 2, - undead = 1, - rarity = 1, - max_life = resolvers.rngavg(70,80), - combat_armor = 5, combat_def = 1, - resolvers.talents{ T_BOW_MASTERY={base=1, every=7, max=10}, T_WEAPON_COMBAT={base=1, every=7, max=10}, T_SHOOT=1, }, - ai_state = { talent_in=1, }, - autolevel = "archer", - resolvers.equip{ {type="weapon", subtype="longbow", autoreq=true}, {type="ammo", subtype="arrow", autoreq=true} }, - }, - skel_m_archer = { - type = "undead", subtype = "skeleton", - name = "skeleton master archer", color=colors.LIGHT_UMBER, image="npc/master_skeleton_archer.png", - blood_color = colors.GREY, - display = "s", - combat = { dam=1, atk=1, apr=1 }, - level_range = {1, nil}, exp_worth = 0, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, QUIVER=1 }, - infravision = 10, - rank = 2, - size_category = 3, - autolevel = "warrior", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=4, }, - stats = { str=14, dex=12, mag=10, con=12 }, - resolvers.racial(), - resolvers.tmasteries{ ["technique/other"]=0.3, ["technique/2hweapon-offense"]=0.3, ["technique/2hweapon-cripple"]=0.3 }, - open_door = true, - cut_immune = 1, - blind_immune = 1, - fear_immune = 1, - poison_immune = 1, - see_invisible = 2, - undead = 1, - rarity = 1, - - max_life = resolvers.rngavg(70,80), - combat_armor = 5, combat_def = 1, - resolvers.talents{ T_BOW_MASTERY={base=1, every=7, max=10}, T_WEAPON_COMBAT={base=1, every=7, max=10}, T_SHOOT=1, T_PINNING_SHOT=3, T_CRIPPLING_SHOT=3, }, - ai_state = { talent_in=1, }, - rank = 3, - autolevel = "archer", - resolvers.equip{ {type="weapon", subtype="longbow", autoreq=true}, {type="ammo", subtype="arrow", autoreq=true} }, - }, - skel_mage = { - type = "undead", subtype = "skeleton", - name = "skeleton mage", color=colors.LIGHT_RED, image="npc/skeleton_mage.png", - blood_color = colors.GREY, - display = "s", - combat = { dam=1, atk=1, apr=1 }, - level_range = {1, nil}, exp_worth = 0, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, QUIVER=1 }, - infravision = 10, - rank = 2, - size_category = 3, - autolevel = "warrior", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=4, }, - stats = { str=14, dex=12, mag=10, con=12 }, - resolvers.racial(), - resolvers.tmasteries{ ["technique/other"]=0.3, ["technique/2hweapon-offense"]=0.3, ["technique/2hweapon-cripple"]=0.3 }, - open_door = true, - cut_immune = 1, - blind_immune = 1, - fear_immune = 1, - poison_immune = 1, - see_invisible = 2, - undead = 1, - rarity = 1, - - max_life = resolvers.rngavg(50,60), - max_mana = resolvers.rngavg(70,80), - combat_armor = 3, combat_def = 1, - stats = { str=10, dex=12, cun=14, mag=14, con=10 }, - resolvers.talents{ T_STAFF_MASTERY={base=1, every=10, max=5}, T_FLAME={base=1, every=7, max=5}, T_MANATHRUST={base=2, every=7, max=5} }, - resolvers.equip{ {type="weapon", subtype="staff", autoreq=true} }, - autolevel = "caster", - ai_state = { talent_in=1, }, - }, - ghoul = { - type = "undead", subtype = "ghoul", - display = "z", - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - autolevel = "ghoul", - level_range = {1, nil}, exp_worth = 0, - ai = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_ghoul", }, - stats = { str=14, dex=12, mag=10, con=12 }, - rank = 2, - size_category = 3, - infravision = 10, - resolvers.racial(), - resolvers.tmasteries{ ["technique/other"]=0.3, }, - open_door = true, - blind_immune = 1, - see_invisible = 2, - undead = 1, - name = "ghoul", color=colors.TAN, - max_life = resolvers.rngavg(90,100), - combat_armor = 2, combat_def = 7, - resolvers.talents{ - T_STUN={base=1, every=10, max=5}, - T_BITE_POISON={base=1, every=10, max=5}, - T_ROTTING_DISEASE={base=1, every=10, max=5}, - }, - ai_state = { talent_in=4, }, - combat = { dam=resolvers.levelup(10, 1, 1), atk=resolvers.levelup(5, 1, 1), apr=3, dammod={str=0.6} }, - }, - ghast = { - type = "undead", subtype = "ghoul", - display = "z", - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - level_range = {1, nil}, exp_worth = 0, - autolevel = "ghoul", - ai = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_ghoul", }, - stats = { str=14, dex=12, mag=10, con=12 }, - rank = 2, - size_category = 3, - infravision = 10, - resolvers.racial(), - resolvers.tmasteries{ ["technique/other"]=0.3, }, - open_door = true, - blind_immune = 1, - see_invisible = 2, - undead = 1, - name = "ghast", color=colors.UMBER, - max_life = resolvers.rngavg(90,100), - combat_armor = 2, combat_def = 7, - resolvers.talents{ - T_STUN={base=1, every=10, max=5}, - T_BITE_POISON={base=1, every=10, max=5}, - T_ROTTING_DISEASE={base=1, every=10, max=5}, - }, - ai_state = { talent_in=4, }, - combat = { dam=resolvers.levelup(10, 1, 1), atk=resolvers.levelup(5, 1, 1), apr=3, dammod={str=0.6} }, - }, - ghoulking = { - type = "undead", subtype = "ghoul", - display = "z", - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - level_range = {1, nil}, exp_worth = 0, - autolevel = "ghoul", - ai = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_ghoul", }, - stats = { str=14, dex=12, mag=10, con=12 }, - rank = 2, - size_category = 3, - infravision = 10, - resolvers.racial(), - resolvers.tmasteries{ ["technique/other"]=0.3, }, - open_door = true, - blind_immune = 1, - see_invisible = 2, - undead = 1, - name = "ghoulking", color={0,0,0}, - max_life = resolvers.rngavg(90,100), - combat_armor = 3, combat_def = 10, - ai_state = { talent_in=2, ai_pause=20 }, - rank = 3, - combat = { dam=resolvers.levelup(30, 1, 1.2), atk=resolvers.levelup(8, 1, 1), apr=4, dammod={str=0.6} }, - resolvers.talents{ - T_STUN={base=3, every=9, max=7}, - T_BITE_POISON={base=3, every=9, max=7}, - T_ROTTING_DISEASE={base=4, every=9, max=7}, - T_DECREPITUDE_DISEASE={base=3, every=9, max=7}, - T_WEAKNESS_DISEASE={base=3, every=9, max=7}, - }, - }, - - -- Advanced minions - vampire = { - type = "undead", subtype = "vampire", - display = "V", - combat = { dam=resolvers.levelup(resolvers.mbonus(30, 10), 1, 0.8), atk=10, apr=9, damtype=DamageType.DRAINLIFE, dammod={str=1.9} }, - level_range = {1, nil}, exp_worth = 0, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - autolevel = "warriormage", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=9, }, - stats = { str=12, dex=12, mag=12, con=12 }, - infravision = 10, - life_regen = 3, - size_category = 3, - rank = 2, - open_door = true, - resolvers.inscriptions(1, "rune"), - resolvers.sustains_at_birth(), - resists = { [DamageType.COLD] = 80, [DamageType.NATURE] = 80, [DamageType.LIGHT] = -50, }, - blind_immune = 1, - confusion_immune = 1, - see_invisible = 5, - undead = 1, - name = "vampire", color=colors.SLATE, image = "npc/vampire.png", - desc=_t[[It is a humanoid with an aura of power. You notice a sharp set of front teeth.]], - max_life = resolvers.rngavg(70,80), - combat_armor = 9, combat_def = 6, - resolvers.talents{ T_STUN={base=1, every=7, max=5}, T_BLUR_SIGHT={base=1, every=7, max=5}, T_ROTTING_DISEASE={base=1, every=7, max=5}, }, - }, - m_vampire = { - type = "undead", subtype = "vampire", - display = "V", - combat = { dam=resolvers.levelup(resolvers.mbonus(30, 10), 1, 0.8), atk=10, apr=9, damtype=DamageType.DRAINLIFE, dammod={str=1.9} }, - level_range = {1, nil}, exp_worth = 0, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - autolevel = "warriormage", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=9, }, - stats = { str=12, dex=12, mag=12, con=12 }, - infravision = 10, - life_regen = 3, - size_category = 3, - rank = 2, - open_door = true, - resolvers.inscriptions(1, "rune"), - resolvers.sustains_at_birth(), - resists = { [DamageType.COLD] = 80, [DamageType.NATURE] = 80, [DamageType.LIGHT] = -50, }, - blind_immune = 1, - confusion_immune = 1, - see_invisible = 5, - undead = 1, - name = "master vampire", color=colors.GREEN, image = "npc/master_vampire.png", - resolvers.nice_tile{image="invis.png", add_mos = {{image="npc/master_vampire.png", display_h=2, display_y=-1}}}, - desc=_t[[It is a humanoid form dressed in robes. Power emanates from its chilling frame.]], - max_life = resolvers.rngavg(80,90), - combat_armor = 10, combat_def = 8, - ai = "dumb_talented_simple", ai_state = { talent_in=1, }, - resolvers.talents{ T_STUN={base=1, every=7, max=5}, T_BLUR_SIGHT={base=2, every=7, max=5}, T_PHANTASMAL_SHIELD={base=1, every=7, max=5}, T_ROTTING_DISEASE={base=2, every=7, max=5}, }, - }, - g_wight = { - type = "undead", subtype = "wight", - display = "W", - combat = { dam=resolvers.mbonus(30, 10), atk=10, apr=9, damtype=DamageType.DRAINEXP }, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - level_range = {1, nil}, exp_worth = 0, - autolevel = "caster", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=4, }, - stats = { str=11, dex=11, mag=15, con=12 }, - infravision = 10, - rank = 2, - size_category = 3, - open_door = true, - resolvers.sustains_at_birth(), - resists = { [DamageType.COLD] = 80, [DamageType.FIRE] = 20, [DamageType.LIGHTNING] = 40, [DamageType.PHYSICAL] = 35, [DamageType.LIGHT] = -50, }, - poison_immune = 1, - blind_immune = 1, - see_invisible = 7, - undead = 1, - name = "grave wight", color=colors.SLATE, image="npc/grave_wight.png", - desc=_t[[It is a ghostly form with eyes that haunt you.]], - max_life = resolvers.rngavg(70,80), - combat_armor = 9, combat_def = 6, - resolvers.talents{ T_FLAMESHOCK={base=2, every=5, max=6}, T_LIGHTNING={base=2, every=5, max=6}, T_GLACIAL_VAPOUR={base=2, every=5, max=6}, - T_MIND_DISRUPTION={base=2, every=5, max=6}, - }, - }, - b_wight = { - type = "undead", subtype = "wight", - display = "W", - combat = { dam=resolvers.mbonus(30, 10), atk=10, apr=9, damtype=DamageType.DRAINEXP }, - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - level_range = {1, nil}, exp_worth = 0, - autolevel = "caster", - ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=4, }, - stats = { str=11, dex=11, mag=15, con=12 }, - infravision = 10, - rank = 2, - size_category = 3, - open_door = true, - resolvers.sustains_at_birth(), - resists = { [DamageType.COLD] = 80, [DamageType.FIRE] = 20, [DamageType.LIGHTNING] = 40, [DamageType.PHYSICAL] = 35, [DamageType.LIGHT] = -50, }, - poison_immune = 1, - blind_immune = 1, - see_invisible = 7, - undead = 1, - name = "barrow wight", color=colors.LIGHT_RED, image="npc/barrow_wight.png", - resolvers.nice_tile{image="invis.png", add_mos = {{image="npc/barrow_wight.png", display_h=2, display_y=-1}}}, - desc=_t[[It is a ghostly nightmare of an entity.]], - max_life = resolvers.rngavg(80,90), - combat_armor = 10, combat_def = 8, - resolvers.talents{ T_FLAMESHOCK={base=3, every=5, max=7}, T_LIGHTNING={base=3, every=5, max=7}, T_GLACIAL_VAPOUR={base=3, every=5, max=7}, - T_MIND_DISRUPTION={base=3, every=5, max=7}, - }, - }, - dread = { - type = "undead", subtype = "ghost", - blood_color = colors.GREY, - display = "G", - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - autolevel = "warriormage", - ai = "dumb_talented_simple", ai_state = { talent_in=2, }, - stats = { str=14, dex=18, mag=20, con=12 }, - rank = 2, - size_category = 3, - infravision = 10, - can_pass = {pass_wall=70}, - resists = {all = 35, [DamageType.LIGHT] = -70, [DamageType.DARKNESS] = 65}, - no_breath = 1, - stone_immune = 1, - confusion_immune = 1, - fear_immune = 1, - teleport_immune = 0.5, - disease_immune = 1, - poison_immune = 1, - stun_immune = 1, - blind_immune = 1, - cut_immune = 1, - see_invisible = 80, - undead = 1, - resolvers.sustains_at_birth(), - name = "dread", color=colors.ORANGE, image="npc/dread.png", - desc=_t[[It is a form that screams its presence against the eye. Death incarnate, its hideous black body seems to struggle against reality as the universe itself strives to banish it.]], - level_range = {1, nil}, exp_worth = 0, - max_life = resolvers.rngavg(90,100), - combat_armor = 0, combat_def = resolvers.mbonus(10, 50), - invisibility = resolvers.mbonus(5, 10), - ai_state = { talent_in=4, }, - combat = { dam=resolvers.mbonus(45, 45), atk=resolvers.mbonus(25, 45), apr=100, dammod={str=0.5, mag=0.5} }, - resolvers.talents{ - T_BURNING_HEX={base=3, every=5, max=7}, - T_BLUR_SIGHT={base=4, every=6, max=8}, - }, - }, - lich = { - type = "undead", subtype = "lich", - display = "L", - rank = 3, size = 3, - combat = { dam=resolvers.rngavg(16,27), atk=16, apr=9, damtype=DamageType.DARKSTUN, dammod={mag=0.9} }, - body = { INVEN = 10, MAINHAND = 1, OFFHAND = 1, FINGER = 2, NECK = 1, LITE = 1, BODY = 1, HEAD = 1, CLOAK = 1, HANDS = 1, BELT = 1, FEET = 1}, - equipment = resolvers.equip{ - {type="armor", subtype="cloth", ego_chance=75, autoreq=true}, - {type="armor", subtype="head", ego_chance=75, autoreq=true}, - {type="armor", subtype="feet", ego_chance=75, autoreq=true}, - {type="armor", subtype="cloak", ego_chance=75, autoreq=true}, - {type="jewelry", subtype="amulet", ego_chance=100, autoreq=true}, - {type="jewelry", subtype="ring", ego_chance=100, autoreq=true}, - {type="jewelry", subtype="ring", ego_chance=100, autoreq=true}, - }, - autolevel = "caster", - ai = "tactical", ai_state = { talent_in=1, }, - ai_tactic = resolvers.tactic"ranged", - stats = { str=8, dex=15, mag=20, wil=18, con=10, cun=18 }, - resists = { [DamageType.NATURE] = 90, [DamageType.FIRE] = 20, [DamageType.MIND] = 100, [DamageType.LIGHT] = -60, [DamageType.DARKNESS] = 95, [DamageType.BLIGHT] = 90 }, - resolvers.inscriptions(3, "rune"), - instakill_immune = 1, - stun_immune = 1, - poison_immune = 1, - undead = 1, - blind_immune = 1, - see_invisible = 100, - infravision = 10, - silence_immune = 0.7, - fear_immune = 1, - negative_regen = 0.4, -- make their negative energies slowly increase - mana_regen = 0.3, - hate_regen = 2, - open_door = 1, - combat_spellpower = resolvers.mbonus(20, 10), - combat_spellcrit = resolvers.mbonus(5, 5), - resolvers.sustains_at_birth(), - name = "lich", color=colors.DARK_BLUE, - desc=_t[[Having thought to discover life eternal, these beings have allowed undeath to rob them of the joys of life. Now they seek to destroy it as well.]], - resolvers.nice_tile{image="invis.png", add_mos = {{image="npc/undead_lich_lich.png", display_h=2, display_y=-1}}}, - level_range = {1, nil}, exp_worth = 0, - rarity = 20, - max_life = resolvers.rngavg(70,80), - combat_armor = 10, combat_def = 20, - resolvers.talents{ - T_HYMN_OF_SHADOWS=4, - T_MOONLIGHT_RAY=5, - T_SHADOW_BLAST=5, - T_TWILIGHT_SURGE=3, - T_STARFALL=3, - T_FREEZE=3, - T_MANATHRUST=5, - T_CONGEAL_TIME=5, --- T_CREEPING_DARKNESS=4, - T_DARK_VISION=4, - T_DARK_TORRENT=4, --- T_DARK_TENDRILS=4, - T_BONE_GRAB=4, - T_BONE_SPEAR=4, - -- Utility spells - T_PHASE_DOOR=5, - T_TELEPORT=5, - T_STONE_SKIN=5, - - T_CALL_SHADOWS=3, - T_FOCUS_SHADOWS=3, - T_SHADOW_MAGES=1, - T_SHADOW_WARRIORS=1, - }, - }, -} ---[[ -function getAdvancedMinionChances(self) - local cl = math.floor(self:getTalentLevel(self.T_MINION_MASTERY)) - if cl <= 1 then - return { vampire=4, m_vampire=0, g_wight=0, b_wight=0, dread=0, lich=0 } - elseif cl == 2 then - return { vampire=4, m_vampire=2, g_wight=0, b_wight=0, dread=2, lich=0 } - elseif cl == 3 then - return { vampire=6, m_vampire=2, g_wight=2, b_wight=0, dread=2, lich=0 } - elseif cl == 4 then - return { vampire=6, m_vampire=4, g_wight=2, b_wight=2, dread=4, lich=2 } - elseif cl == 5 then - return { vampire=6, m_vampire=4, g_wight=4, b_wight=2, dread=4, lich=2 } - elseif cl >= 6 then - return { vampire=4, m_vampire=4, g_wight=4, b_wight=4, dread=6, lich=4 } - end -end - -local function getMinionChances(self) - local cl = math.floor(self:getTalentLevel(self.T_CREATE_MINIONS)) - if cl <= 1 then - return { d_skel_warrior=55, skel_warrior=10, a_skel_warrior=0, skel_archer=10, skel_m_archer=0, skel_mage=5, ghoul=20, ghast=0, ghoulking=0 } - elseif cl == 2 then - return { d_skel_warrior=31, skel_warrior=15, a_skel_warrior=2, skel_archer=15, skel_m_archer=2, skel_mage=10, ghoul=20, ghast=5, ghoulking=0 } - elseif cl == 3 then - return { d_skel_warrior=24, skel_warrior=15, a_skel_warrior=5, skel_archer=20, skel_m_archer=4, skel_mage=10, ghoul=15, ghast=5, ghoulking=2 } - elseif cl == 4 then - return { d_skel_warrior=9, skel_warrior=20, a_skel_warrior=10, skel_archer=15, skel_m_archer=6, skel_mage=10, ghoul=15, ghast=10, ghoulking=5 } - elseif cl == 5 then - return { d_skel_warrior=9, skel_warrior=20, a_skel_warrior=10, skel_archer=10, skel_m_archer=8, skel_mage=15, ghoul=10, ghast=10, ghoulking=8 } - elseif cl >= 6 then - return { d_skel_warrior=0, skel_warrior=25, a_skel_warrior=15, skel_archer=10, skel_m_archer=10, skel_mage=15, ghoul=5, ghast=10, ghoulking=10 } - end -end - -local function makeNecroticMinion(self, lev) - if self:knowTalent(self.T_MINION_MASTERY) then - local adv = getAdvancedMinionChances(self) - local tot = 0 - local list = {} - for k, e in pairs(adv) do for i = 1, e do list[#list+1] = k end tot = tot + e end - local sel = list[rng.range(1, 100)] - if sel then return require("mod.class.NPC").new(minions_list[sel]) end - end - - local chances = getMinionChances(self) - local tot = 0 - local list = {} - for k, e in pairs(chances) do for i = 1, e do list[#list+1] = k end tot = tot + e end - - local m = require("mod.class.NPC").new(minions_list[rng.table(list)]) - return m -end ---]] - -local minion_order = {"d_skel_warrior", "skel_warrior", "a_skel_warrior", "skel_archer", "skel_m_archer", "skel_mage", "ghoul", "ghast", "ghoulking","vampire", "m_vampire", "g_wight", "b_wight", "dread", "lich"} -- Sets listing order - --- Parameters are b, n, m, p where weight = b + n*tl + m*tl^p -local MinionWeightParams = { - d_skel_warrior={92.944, 0.000, -37.944, 0.500}, - skel_warrior={ 7.000, 0.000, 3.000, 1.000}, - a_skel_warrior={-3.000, 0.000, 3.000, 1.000}, - skel_archer={ 0.000, 11.667, -1.667, 2.000}, - skel_m_archer={ -2.000, 0.000, 2.000, 1.000}, - skel_mage={ 1.471, 0.000, 3.529, 0.750}, - ghoul={ 23.000, 0.000, -3.000, 1.000}, - ghast={ -3.529, 0.000, 3.529, 0.750}, - ghoulking={ -7.816, 0.000, 4.647, 0.750} -} - -local AdvMinionWeightParams = { - vampire={ 2.690, 3.139, -0.448, 2.000}, - m_vampire={ -1.076, 0.000, 1.076, 1.000}, - g_wight={ -2.690, 0.000, 1.345, 1.000}, - b_wight={ -5.381, 0.000, 1.794, 1.000}, - dread={ -1.614, 0.000, 1.614, 1.000}, - lich={ -5.381, 0.000, 1.794, 1.000} -} - --- tl = talent level, or {createminionsTL, minionmasteryTL} --- wtable = weight table or table of weight tables -local function getMinionWeights(tl,wtable) - local tl = tl - if type(tl) == "number" then tl = {tl} end - local tables = #wtable > 0 and #wtable or 1 - local chances, sum = {}, 0 - - for i = 1, tables do - for utype, params in pairs(tables > 1 and wtable[i] or wtable) do - chances[utype] = math.max(0,params[1] + params[2]*tl[i] + params[3]*tl[i]^params[4]) - sum = sum + chances[utype] - end - end - return chances, sum -end - -local function getMinionChances(self) - local chances, sum - local tlcm, tlmm = self:getTalentLevel(self.T_CREATE_MINIONS),self:getTalentLevel(self.T_MINION_MASTERY) - if tlmm > 0 then - chances, sum = getMinionWeights({math.max(tlcm,tlmm), tlmm},{MinionWeightParams, AdvMinionWeightParams}) -- Balance talent levels to avoid too many powerful minions - else - chances, sum = getMinionWeights(tlcm,MinionWeightParams) - end - for i,k in pairs(chances) do - chances[i] = k*100/sum - end - return chances -end - --- Export for addons to alter -necrotic_minions_list = minions_list - -function makeNecroticMinion(self, lev) - local chances = getMinionChances(self) - local pick = rng.float(0,100) - local tot, m = 0 - for k, e in pairs(chances) do - tot = tot + e - if tot > pick then m = k break end - end - m = require("mod.class.NPC").new(minions_list[m]) - m.necrotic_minion = true - return m -end - -newTalent{ - name = "Create Minions", - type = {"spell/necrotic-minions",1}, - require = spells_req1, - points = 5, - fake_ressource = true, - mana = 5, - soul = function(self, t) return math.max(1, math.min(t.getMax(self, t), self:getSoul())) end, - cooldown = 14, - tactical = { ATTACK = 10 }, - requires_target = true, - range = 0, - autolearn_talent = "T_NECROTIC_AURA", - unlearn_on_clone = true, - radius = function(self, t) - local aura = self:getTalentFromId(self.T_NECROTIC_AURA) - return aura.getRadius(self, aura) - end, - target = function(self, t) - return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} - end, - on_pre_use = function(self, t) - local p = self:isTalentActive(self.T_NECROTIC_AURA) - if not p then return end - return true - end, - getMax = function(self, t) - local max = math.max(1, math.floor(self:combatTalentScale(t, 1, 5, "log"))) - if necroEssenceDead(self, true) then max = max + 2 end - return max - necroGetNbSummon(self) - end, -- talent level 1-5 gives 1-5 - getLevel = function(self, t) return math.floor(self:combatScale(self:getTalentLevel(t), -6, 0.9, 2, 5)) end, -- -6 @ 1, +2 @ 5, +5 @ 8 - MinionChancesDesc = function(self) - local c = getMinionChances(self) - local chancelist = tstring({}) - for i, k in ipairs(minion_order) do - if c[k] then chancelist:add(true,_t(minions_list[k].name):capitalize(),(": %d%%"):tformat(c[k])) end - end - return chancelist:toString() - end, - action = function(self, t) - local p = self:isTalentActive(self.T_NECROTIC_AURA) - local nb = t.getMax(self, t) - nb = math.min(nb, self:getSoul()) - local lev = t.getLevel(self, t) - - -- Summon minions in a cone - local tg = self:getTalentTarget(t) - local x, y = self:getTarget(tg) - if not x or not y then return nil end - local possible_spots = {} - self:project(tg, x, y, function(px, py) - if not game.level.map:checkAllEntities(px, py, "block_move") then - possible_spots[#possible_spots+1] = {x=px, y=py} - end - end) - local use_ressource = not self:attr("zero_resource_cost") and not self:attr("force_talent_ignore_ressources") - for i = 1, nb do - local minion = makeNecroticMinion(self, self:getTalentLevel(t)) - local pos = rng.tableRemove(possible_spots) - if minion and pos then - if use_ressource then self:incSoul(-1) end - necroSetupSummon(self, minion, pos.x, pos.y, lev, true) - end - end - - local empower = necroEssenceDead(self) - if empower then empower() end - - if use_ressource then self:incMana(-util.getval(t.mana, self, t) * (100 + 2 * self:combatFatigue()) / 100) end - game:playSoundNear(self, "talents/spell_generic2") - return true - end, - info = function(self, t) - local nb = t.getMax(self, t) - local lev = t.getLevel(self, t) - local mm = self:knowTalent(self.T_MINION_MASTERY) and _t" (Minion Mastery effects included)" or "" - return ([[Fires powerful undead energies through your necrotic aura. For each recent death that happened inside your aura, you will raise an undead minion (up to %d minions). These minions will be raised within a cone that extends to the edge of your necrotic aura. - The minions level is your level %+d. - Each minion has a chance to be%s:%s]]): - tformat(nb, lev, mm, t.MinionChancesDesc(self, t)) - end, -} - -newTalent{ - name = "Aura Mastery", - type = {"spell/necrotic-minions",2}, - require = spells_req2, - points = 5, - mode = "passive", - getbonusRadius = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5)) end, - on_learn = function(self, t) - self:forceUseTalent(self.T_NECROTIC_AURA, {ignore_energy=true, ignore_cd=true, no_equilibrium_fail=true, no_paradox_fail=true}) - self:forceUseTalent(self.T_NECROTIC_AURA, {ignore_energy=true, ignore_cd=true, no_equilibrium_fail=true, no_paradox_fail=true}) - end, - on_unlearn = function(self, t) - self:forceUseTalent(self.T_NECROTIC_AURA, {ignore_energy=true, ignore_cd=true, no_equilibrium_fail=true, no_paradox_fail=true}) - self:forceUseTalent(self.T_NECROTIC_AURA, {ignore_energy=true, ignore_cd=true, no_equilibrium_fail=true, no_paradox_fail=true}) - end, - info = function(self, t) - return ([[Your dark power radiates further as you grow stronger. Increases the radius of your necrotic aura by %d, and reduces the decay rate of your minions outside the aura by %d%%. - At level 3, necrotic minions inside your aura have a 25%% chance to refund their soul on death. If a minion turns into a will o' the wisp then the wisp will have that chance instead.]]): - tformat(math.floor(t.getbonusRadius(self, t)), math.min(7, self:getTalentLevelRaw(t))) - end, -} - -newTalent{ - name = "Surge of Undeath", - type = {"spell/necrotic-minions",3}, - require = spells_req3, - points = 5, - mana = 45, - cooldown = 20, - tactical = { ATTACKAREA = 2 }, - getPower = function(self, t) return self:combatTalentSpellDamage(t, 10, 60) end, - getCrit = function(self, t) return self:combatTalentSpellDamage(t, 6, 25) end, - getAPR = function(self, t) return self:combatTalentSpellDamage(t, 10, 50) end, - action = function(self, t) - local apply = function(a) - a:setEffect(a.EFF_SURGE_OF_UNDEATH, 6, {power=t.getPower(self, t), apr=t.getAPR(self, t), crit=t.getCrit(self, t)}) - end - - if game.party and game.party:hasMember(self) then - for act, def in pairs(game.party.members) do - if act.summoner and act.summoner == self and act.necrotic_minion then apply(act) end - end - else - for uid, act in pairs(game.level.entities) do - if act.summoner and act.summoner == self and act.necrotic_minion then apply(act) end - end - end - - game:playSoundNear(self, "talents/spell_generic2") - return true - end, - info = function(self, t) - return ([[A surge of power radiates to all your minions, increasing their Physical Power, Spellpower and Accuracy by %d, their Armour penetration by %d and their critical hit chance by %d for 6 turns. - The effects will increase with your Spellpower.]]): - tformat(t.getPower(self, t), t.getAPR(self, t), t.getCrit(self, t)) - end, -} - -newTalent{ - name = "Dark Empathy", - type = {"spell/necrotic-minions",4}, - require = spells_req4, - points = 5, - mode = "passive", - getPerc = function(self, t) return self:combatTalentSpellDamage(t, 15, 80) end, - info = function(self, t) - return ([[You share your powers with your minions, granting them %d%% of your resistances and saves. - In addition all damage done by your minions to you or your other minions is reduced by %d%%. - The effect will increase with your Spellpower.]]): - tformat(t.getPerc(self, t), self:getTalentLevelRaw(t) * 20) - end, -} diff --git a/game/modules/tome/data/talents/spells/nightfall.lua b/game/modules/tome/data/talents/spells/nightfall.lua deleted file mode 100644 index e59c7546cd632237593794e510d232c25d2c0809..0000000000000000000000000000000000000000 --- a/game/modules/tome/data/talents/spells/nightfall.lua +++ /dev/null @@ -1,195 +0,0 @@ --- ToME - Tales of Maj'Eyal --- Copyright (C) 2009 - 2019 Nicolas Casalini --- --- This program is free software: you can redistribute it and/or modify --- it under the terms of the GNU General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- This program is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU General Public License for more details. --- --- You should have received a copy of the GNU General Public License --- along with this program. If not, see <http://www.gnu.org/licenses/>. --- --- Nicolas Casalini "DarkGod" --- darkgod@te4.org - -local isFF = function(self) - if self:getTalentLevel(self.T_INVOKE_DARKNESS) >= 5 then return false - else return true - end -end - -newTalent{ - name = "Invoke Darkness", - type = {"spell/nightfall",1}, - require = spells_req1, - points = 5, - random_ego = "attack", - mana = 10, - cooldown = 3, - tactical = { ATTACK = { DARKNESS = 2 } }, - range = 10, - reflectable = true, - proj_speed = 20, - requires_target = true, - direct_hit = function(self, t) if self:getTalentLevel(t) >= 3 then return true else return false end end, - target = function(self, t) - local tg = {type="bolt", range=self:getTalentRange(t), friendlyfire=isFF(self), talent=t, display={particle="bolt_dark", trail="darktrail"}} - if self:getTalentLevel(t) >= 3 then tg.type = "beam" end - if necroEssenceDead(self, true) then tg.radius, tg.range = tg.range, 0 tg.type = "cone" tg.cone_angle = 25 end - return tg - end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 25, 230) end, - action = function(self, t) - local tg = self:getTalentTarget(t) - local x, y = self:getTarget(tg) - if not x or not y then return nil end - local empower = necroEssenceDead(self) - if empower then - self:project(tg, x, y, DamageType.DARKNESS, self:spellCrit(t.getDamage(self, t))) - game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_shadow", {radius=tg.radius, tx=x-self.x, ty=y-self.y, spread=20}) - empower() - elseif self:getTalentLevel(t) < 3 then - self:projectile(tg, x, y, DamageType.DARKNESS, self:spellCrit(t.getDamage(self, t)), function(self, tg, x, y, grids) - game.level.map:particleEmitter(x, y, 1, "dark") - end) - else - self:project(tg, x, y, DamageType.DARKNESS, self:spellCrit(t.getDamage(self, t))) - local _ _, x, y = self:canProject(tg, x, y) - game.level.map:particleEmitter(self.x, self.y, tg.radius, "shadow_beam", {tx=x-self.x, ty=y-self.y}) - end - - game:playSoundNear(self, "talents/spell_generic") - return true - end, - info = function(self, t) - local damage = t.getDamage(self, t) - return ([[Conjures up a bolt of darkness, doing %0.2f darkness damage. - At level 3, it will create a beam of shadows. - At level 5, none of your Nightfall spells will hurt your minions. - The damage will increase with your Spellpower.]]): - tformat(damDesc(self, DamageType.DARKNESS, damage)) - end, -} - -newTalent{ - name = "Circle of Death", - type = {"spell/nightfall",2}, - require = spells_req2, - points = 5, - mana = 40, - cooldown = 16, - tactical = { ATTACKAREA = { DARKNESS = 2 }, DISABLE = { confusion = 1.5, blind = 1.5 } }, - range = 6, - radius = 3, - direct_hit = true, - requires_target = true, - target = function(self, t) - return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)} - end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 40) end, - getDuration = function(self, t) return 5 end, - getBaneDur = function(self,t) return math.floor(self:combatTalentScale(t, 4.5, 6.5)) end, - action = function(self, t) - local tg = self:getTalentTarget(t) - local x, y = self:getTarget(tg) - if not x or not y then return nil end - local _ _, _, _, x, y = self:canProject(tg, x, y) - -- Add a lasting map effect - game.level.map:addEffect(self, - x, y, t.getDuration(self, t), - DamageType.CIRCLE_DEATH, {dam=self:spellCrit(t.getDamage(self, t)), dur=t.getBaneDur(self,t), ff=isFF(self)}, - self:getTalentRadius(t), - 5, nil, - {type="circle_of_death", overlay_particle={zdepth=6, only_one=true, type="circle", args={oversize=1, a=100, appear=8, speed=-0.05, img="necromantic_circle", radius=self:getTalentRadius(t)}}}, --- {zdepth=6, only_one=true, type="circle", args={oversize=1, a=130, appear=8, speed=-0.03, img="arcane_circle", radius=self:getTalentRadius(t)}}, - nil, false - ) - - game:playSoundNear(self, "talents/fire") - return true - end, - info = function(self, t) - local damage = t.getDamage(self, t) - return ([[Dark fumes erupt from the ground for 5 turns. Any creature entering the circle will receive either a bane of confusion or a bane of blindness. - Only one bane can affect a creature. - Banes last for %d turns, and also deal %0.2f darkness damage. - The damage will increase with your Spellpower.]]): - tformat(t.getBaneDur(self,t), damDesc(self, DamageType.DARKNESS, damage)) - end, -} - -newTalent{ - name = "Fear the Night", - type = {"spell/nightfall",3}, - require = spells_req3, - points = 5, - random_ego = "attack", - mana = 30, - cooldown = 10, - direct_hit = true, - tactical = { ATTACKAREA = { DARKNESS = 2 }, DISABLE = { knockback = 2 }, ESCAPE = { knockback = 1 } }, - range = 0, - radius = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end, - requires_target = true, - target = function(self, t) return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), friendlyfire=isFF(self), talent=t} end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 230) end, - action = function(self, t) - local tg = self:getTalentTarget(t) - local x, y = self:getTarget(tg) - if not x or not y then return nil end - self:project(tg, x, y, DamageType.DARKKNOCKBACK, {dist=4, dam=self:spellCrit(t.getDamage(self, t))}) - game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_dark", {radius=tg.radius, tx=x-self.x, ty=y-self.y}) - game:playSoundNear(self, "talents/fire") - return true - end, - info = function(self, t) - local damage = t.getDamage(self, t) - local radius = self:getTalentRadius(t) - return ([[Invoke a cone dealing %0.2f darkness damage in a radius of %d. Any creatures caught inside must make check against their Mental Save or be knocked back 4 grids away. - The damage will increase with your Spellpower.]]): - tformat(damDesc(self, DamageType.DARKNESS, damage), self:getTalentRadius(t)) - end, -} - -newTalent{ - name = "Rigor Mortis", - type = {"spell/nightfall",4}, - require = spells_req4, - points = 5, - mana = 60, - cooldown = 20, - tactical = { ATTACKAREA = 3 }, - range = 7, - radius = 1, - direct_hit = true, - requires_target = true, - target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), friendlyfire=isFF(self), talent=t, display={particle="bolt_dark", trail="darktrail"}} end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 28, 280) end, - getMinion = function(self, t) return 10 + self:combatTalentSpellDamage(t, 10, 30) end, - getDur = function(self, t) return math.floor(self:combatTalentScale(t, 3.6, 6.3)) end, - getSpeed = function(self, t) return math.min(self:getTalentLevel(t) * 0.065, 0.5) end, - action = function(self, t) - local tg = self:getTalentTarget(t) - local x, y = self:getTarget(tg) - if not x or not y then return nil end - self:projectile(tg, x, y, DamageType.RIGOR_MORTIS, {dam=self:spellCrit(t.getDamage(self, t)), minion=t.getMinion(self, t), speed=t.getSpeed(self, t), dur=t.getDur(self, t)}, {type="dark"}) - game:playSoundNear(self, "talents/fireflash") - return true - end, - info = function(self, t) - local damage = t.getDamage(self, t) - local speed = t.getSpeed(self, t) * 100 - local dur = t.getDur(self, t) - local minion = t.getMinion(self, t) - return ([[Invoke a ball of darkness that deals %0.2f darkness damage in a radius of %d. Every creature hit will start to become closer to death, reducing their global speed by %d%%. - Necrotic minions' damage against those creatures is increased by %d%%. - The effects last for %d turns. - The damage done and the minions' damage increase will increase with your Spellpower.]]): - tformat(damDesc(self, DamageType.DARKNESS, damage), self:getTalentRadius(t), speed, minion, dur) - end, -} diff --git a/game/modules/tome/data/talents/spells/shades.lua b/game/modules/tome/data/talents/spells/shades.lua deleted file mode 100644 index 724d7aef4f0ab4572234478d17834b0d74f5f5c3..0000000000000000000000000000000000000000 --- a/game/modules/tome/data/talents/spells/shades.lua +++ /dev/null @@ -1,278 +0,0 @@ --- ToME - Tales of Maj'Eyal --- Copyright (C) 2009 - 2019 Nicolas Casalini --- --- This program is free software: you can redistribute it and/or modify --- it under the terms of the GNU General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- This program is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU General Public License for more details. --- --- You should have received a copy of the GNU General Public License --- along with this program. If not, see <http://www.gnu.org/licenses/>. --- --- Nicolas Casalini "DarkGod" --- darkgod@te4.org - -newTalent{ - name = "Shadow Tunnel", - type = {"spell/shades",1}, - require = spells_req_high1, - points = 5, - random_ego = "attack", - mana = 25, - cooldown = 20, - range = 10, - tactical = { DEFEND = 2 }, - requires_target = true, - getChance = function(self, t) return 20 + self:combatTalentSpellDamage(t, 15, 60) end, - action = function(self, t) - local list = {} - if game.party and game.party:hasMember(self) then - for act, def in pairs(game.party.members) do - if act.summoner and act.summoner == self and act.necrotic_minion then list[#list+1] = act end - end - else - for uid, act in pairs(game.level.entities) do - if act.summoner and act.summoner == self and act.necrotic_minion then list[#list+1] = act end - end - end - - local empower = necroEssenceDead(self) - for i, m in ipairs(list) do - local x, y = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true}) - if x and y then - m:move(x, y, true) - game.level.map:particleEmitter(x, y, 1, "summon") - end - m:setEffect(m.EFF_EVASION, 5, {chance=t.getChance(self, t)}) - if empower then - m:heal(m.max_life * 0.3) - if core.shader.active(4) then - m:addParticles(Particles.new("shader_shield_temp", 1, {toback=true , size_factor=1.5, y=-0.3, img="healdark", life=25}, {type="healing", time_factor=6000, beamsCount=15, noup=2.0, beamColor1={0xcb/255, 0xcb/255, 0xcb/255, 1}, beamColor2={0x35/255, 0x35/255, 0x35/255, 1}})) - m:addParticles(Particles.new("shader_shield_temp", 1, {toback=false, size_factor=1.5, y=-0.3, img="healdark", life=25}, {type="healing", time_factor=6000, beamsCount=15, noup=1.0, beamColor1={0xcb/255, 0xcb/255, 0xcb/255, 1}, beamColor2={0x35/255, 0x35/255, 0x35/255, 1}})) - end - end - end - if empower then empower() end - - game:playSoundNear(self, "talents/spell_generic") - return true - end, - info = function(self, t) - local chance = t.getChance(self, t) - return ([[Surround your minions in a veil of darkness. The darkness will teleport them to you, and grant them %d%% evasion for 5 turns. - The evasion chance will increase with your Spellpower.]]): - tformat(chance) - end, -} - -newTalent{ - name = "Curse of the Meek", - type = {"spell/shades",2}, - require = spells_req_high2, - points = 5, - mana = 50, - cooldown = 30, - range = 10, - tactical = { DEFEND = 3 }, - action = function(self, t) - local nb = math.ceil(self:getTalentLevel(t)) - for i = 1, nb do - local x, y = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true}) - if x and y then - local NPC = require "mod.class.NPC" - local m = NPC.new{ - type = "humanoid", display = "p", - color=colors.WHITE, - - combat = { dam=resolvers.rngavg(1,2), atk=2, apr=0, dammod={str=0.4} }, - - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, QUIVER=1 }, - lite = 3, - - life_rating = 10, - rank = 2, - size_category = 3, - - autolevel = "warrior", - stats = { str=12, dex=8, mag=6, con=10 }, - ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=2, }, - level_range = {1, 3}, - - max_life = resolvers.rngavg(30,40), - combat_armor = 2, combat_def = 0, - - summoner = self, - summoner_gain_exp=false, - summon_time = 8, - } - - m.level = 1 - local race = 5 -- rng.range(1, 5) - if race == 1 then - m.name = _t"human farmer" - m.subtype = "human" - m.image = "npc/humanoid_human_human_farmer.png" - m.desc=_t[[A weather-worn human farmer, looking at a loss as to what's going on.]] - elseif race == 2 then - m.name = _t"halfling gardener" - m.subtype = "halfling" - m.desc=_t[[A rugged halfling gardener, looking quite confused as to what he's doing here.]] - m.image = "npc/humanoid_halfling_halfling_gardener.png" - elseif race == 3 then - m.name = _t"shalore scribe" - m.subtype = "shalore" - m.desc=_t[[A scrawny elven scribe, looking bewildered at his surroundings.]] - m.image = "npc/humanoid_shalore_shalore_rune_master.png" - elseif race == 4 then - m.name = _t"dwarven lumberjack" - m.subtype = "dwarf" - m.desc=_t[[A brawny dwarven lumberjack, looking a bit upset at his current situation.]] - m.image = "npc/humanoid_dwarf_lumberjack.png" - elseif race == 5 then - m.name = _t"cute bunny" - m.type = "vermin" m.subtype = "rodent" - m.desc=_t[[It is so cute!]] - m.image = "npc/vermin_rodent_cute_little_bunny.png" - end - m.faction = self.faction - m.no_necrotic_soul = true - - m:resolve() m:resolve(nil, true) - m:forceLevelup(self.level) - game.zone:addEntity(game.level, m, "actor", x, y) - game.level.map:particleEmitter(x, y, 1, "summon") - m:setEffect(m.EFF_CURSE_HATE, 100, {src=self}) - m.on_die = function(self, src) - local p = self.summoner:isTalentActive(self.summoner.T_NECROTIC_AURA) - if p and src and src.reactionToward and src:reactionToward(self) < 0 and rng.percent(70) then - self.summoner:incSoul(1) - self.summoner.changed = true - end - end - end - end - game:playSoundNear(self, "talents/spell_generic") - return true - end, - info = function(self, t) - return ([[Reaches through the shadows into quieter places, summoning %d harmless creatures. - Those creatures are then cursed with a Curse of Hate, making all hostile foes try to kill them. - If the summoned creatures are killed by hostile foes, you have 70%% chance to gain a soul.]]): - tformat(math.ceil(self:getTalentLevel(t))) - end, -} - -newTalent{ - name = "Forgery of Haze", - type = {"spell/shades",3}, - require = spells_req_high3, - points = 5, - mana = 70, - cooldown = 30, - range = 10, - tactical = { ATTACK = 2, }, - requires_target = true, - unlearn_on_clone = true, - getDuration = function(self, t) return math.floor(self:combatTalentLimit(t, 30, 4, 8.1)) end, -- Limit <30 - getHealth = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 20, 500), 1.0, 0.2, 0, 0.58, 384) end, -- Limit health < 100% - getDam = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 10, 500), 1.40, 0.4, 0, 0.76, 361) end, -- Limit damage < 140% - action = function(self, t) - -- Find space - local x, y = util.findFreeGrid(self.x, self.y, 1, true, {[Map.ACTOR]=true}) - if not x then - game.logPlayer(self, "Not enough space to summon!") - return - end - local hfct = t.getHealth(self, t) - local m = require("mod.class.NPC").new(self:cloneActor{ - shader = "shadow_simulacrum", - faction = self.faction, exp_worth = 0, - max_life = self.max_life*hfct, die_at = self.die_at*hfct, - life = util.bound(self.life*hfct, self.die_at*hfct, self.max_life*hfct), - max_level = self.level, - summoner = self, summoner_gain_exp=true, summon_time = t.getDuration(self, t), - - ai_target = {actor=table.NIL_MERGE}, - ai = "summoned", ai_real = "tactical", - name = ("Forgery of Haze (%s)"):tformat(self:getName()), - desc = ([[A dark shadowy shape whose form resembles %s.]]):tformat(self:getName()), - }) - - m:removeTimedEffectsOnClone() - m:unlearnTalentsOnClone() - - m.inc_damage.all = ((100 + (m.inc_damage.all or 0)) * t.getDam(self, t)) - 100 - - game.zone:addEntity(game.level, m, "actor", x, y) - game.level.map:particleEmitter(x, y, 1, "shadow") - - if game.party:hasMember(self) then - game.party:addMember(m, { - control="no", - type="minion", - title=_t"Forgery of Haze", - orders = {target=true}, - }) - end - - game:playSoundNear(self, "talents/spell_generic2") - return true - end, - info = function(self, t) - return ([[Through the shadows, you forge a temporary copy of yourself, existing for %d turns. - The copy possesses your exact talents and stats, has %d%% life and deals %d%% damage.]]): - tformat(t.getDuration(self, t), t.getHealth(self, t) * 100, t.getDam(self, t) * 100) - end, -} - -newTalent{ - name = "Frostdusk", - type = {"spell/shades",4}, - require = spells_req_high4, - points = 5, - mode = "sustained", - sustain_mana = 50, - cooldown = 30, - tactical = { BUFF = 2 }, - getDamageIncrease = function(self, t) return self:combatTalentScale(t, 2.5, 10) end, - getResistPenalty = function(self, t) return self:combatTalentLimit(t, 100, 17, 50, true) end, -- Limit to < 100% - getAffinity = function(self, t) return self:combatTalentLimit(t, 100, 10, 50) end, -- Limit < 100% - activate = function(self, t) - game:playSoundNear(self, "talents/spell_generic") - local ret = { - dam = self:addTemporaryValue("inc_damage", {[DamageType.DARKNESS] = t.getDamageIncrease(self, t), [DamageType.COLD] = t.getDamageIncrease(self, t)}), - resist = self:addTemporaryValue("resists_pen", {[DamageType.DARKNESS] = t.getResistPenalty(self, t)}), - affinity = self:addTemporaryValue("damage_affinity", {[DamageType.DARKNESS] = t.getAffinity(self, t)}), - } - local particle - if core.shader.active(4) then - ret.particle1 = self:addParticles(Particles.new("shader_ring_rotating", 1, {rotation=0, radius=1.1, img="spinningwinds_black"}, {type="spinningwinds", ellipsoidalFactor={1,1}, time_factor=6000, noup=2.0, verticalIntensityAdjust=-3.0})) - ret.particle1.toback = true - ret.particle2 = self:addParticles(Particles.new("shader_ring_rotating", 1, {rotation=0, radius=1.1, img="spinningwinds_black"}, {type="spinningwinds", ellipsoidalFactor={1,1}, time_factor=6000, noup=1.0, verticalIntensityAdjust=-3.0})) - else - ret.particle1 = self:addParticles(Particles.new("ultrashield", 1, {rm=0, rM=0, gm=0, gM=0, bm=10, bM=100, am=70, aM=180, radius=0.4, density=60, life=14, instop=20})) - end - return ret - end, - deactivate = function(self, t, p) - if p.particle1 then self:removeParticles(p.particle1) end - if p.particle2 then self:removeParticles(p.particle2) end - self:removeTemporaryValue("inc_damage", p.dam) - self:removeTemporaryValue("resists_pen", p.resist) - self:removeTemporaryValue("damage_affinity", p.affinity) - return true - end, - info = function(self, t) - local damageinc = t.getDamageIncrease(self, t) - local ressistpen = t.getResistPenalty(self, t) - local affinity = t.getAffinity(self, t) - return ([[Surround yourself with Frostdusk, increasing all your darkness and cold damage by %0.1f%%, and ignoring %d%% of the darkness resistance of your targets. - In addition, all darkness damage you take heals you for %d%% of the damage.]]) - :tformat(damageinc, ressistpen, affinity) - end, -} diff --git a/game/modules/tome/data/talents/spells/spectre.lua b/game/modules/tome/data/talents/spells/spectre.lua new file mode 100644 index 0000000000000000000000000000000000000000..44ed7d1b89da0d269f1eff17e29c0f4d7a50ba78 --- /dev/null +++ b/game/modules/tome/data/talents/spells/spectre.lua @@ -0,0 +1,221 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009 - 2020 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +local Object = require "mod.class.Object" + +newTalent{ + name = "Ghost Walk", + type = {"spell/spectre",1}, + require = spells_req1, + points = 5, + mana = 12, + cooldown = 10, + tactical = { CLOSEIN = 2, ESCAPE = 2 }, + range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end, + requires_target = true, + target = function(self, t) + return {type="hit", nolock=true, range=self:getTalentRange(t)} + end, + direct_hit = true, + is_teleport = true, + action = function(self, t) + local tg = self:getTalentTarget(t) + local x, y, target = self:getTarget(tg) + if not x or not y then return nil end + if not self:hasLOS(x, y) or game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then -- To prevent teleporting through walls + game.logPlayer(self, "You do not have line of sight.") + return nil + end + local _ _, x, y = self:canProject(tg, x, y) + + local orig_x = self.x + local orig_y = self.y + + game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") + if not self:teleportRandom(x, y, 0) then + game.logSeen(self, "%s's ghost walk fizzles!", self.name:capitalize()) + else + self:setEffect(self.EFF_GHOST_WALK, 3, {src=self, range=self:getTalentRange(t), x=orig_x, y=orig_y}) + if self:knowTalent(self.T_SPECTRAL_SIGHT) then + self:setEffect(self.EFF_SENSE, self:callTalent(self.T_SPECTRAL_SIGHT, "getDuration"), { + range = self:callTalent(self.T_SPECTRAL_SIGHT, "getVision"), + actor = 1, + }) + end + if self:knowTalent(self.T_INTANGIBILITY) then + self:setEffect(self.EFF_INTANGIBILITY, self:callTalent(self.T_INTANGIBILITY, "getDuration"), { + src = self, + power = self:callTalent(self.T_INTANGIBILITY, "getChance"), + }) + end + game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") + end + + game:playSoundNear(self, "talents/teleport") + return true + end, + info = function(self, t) + local range = self:getTalentRange(t) + return ([[Taking on a spectral form, you teleport to the target tile within range %d. + For 3 turns after using this spell, you gain the ability to teleport back to your original tile as long as you are within range of it.]]):tformat(range) + end, +} + +newTalent{ + name = "Ghost Walk - Return", short_name = "GHOST_WALK_RETURN", + type = {"spell/other",1}, + tactical = { CLOSEIN = 2, ESCAPE = 2 }, + requires_target = true, + direct_hit = true, + is_teleport = true, + no_energy = true, + action = function(self, t) + local eff = self:hasEffect(self.EFF_GHOST_WALK) + if not eff then return end + local x = eff.x + local y = eff.y + if not self:hasLOS(x, y) or game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then -- To prevent teleporting through walls + game.logPlayer(self, "You do not have line of sight.") + return nil + end + + if self:knowTalent(self.T_PATH_TO_BEYOND) then + local ActorTalents = require("engine.interface.ActorTalents") + for i=1, self:callTalent(self.T_PATH_TO_BEYOND, "getNb") do + local tx, ty = util.findFreeGrid(self.x, self.y, 3, true, {[Map.ACTOR]=true}) + if not tx then return end + local NPC = require "mod.class.NPC" + local m = NPC.new{ + name = "wisp", + display = "g", color=colors.LIGHT_BLUE, image="npc/undead_ghost_will_o__the_wisp.png", + blood_color = colors.BLUE, + type = "undead", subtype = "ghost", + rank = 2, + size_category = 2, + body = { INVEN = 10 }, + level_range = {self.level, self.level}, + no_drops = true, + autolevel = "warriorwill", + exp_worth = 0, + ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=2 }, + stats = { str=15, dex=15, wil=15, con=15, cun=15}, + infravision = 10, + silent_levelup = true, + no_breath = 1, + negative_status_effect_immune = 1, + infravision = 10, + max_life = resolvers.rngavg(10, 30), + life_rating = 6, + combat_armor = 1, combat_def = 10, + combat = { dam=1, atk=1, apr=1, damtype=DamageType.COLD }, + + } + m.faction = self.faction + m.summoner = self + m.summoner_gain_exp = true + m.on_act = function(self) + if self.turn_procs.taunt then return end + self.turn_procs.taunt = true + local tg = {type="ball", range=0, radius=3, friendlyfire=false} + self:project(tg, self.x, self.y, function(tx, ty) + local a = game.level.map(tx, ty, Map.ACTOR) + if a and not a.dead and a:reactionToward(self) < 0 then a:setTarget(self) end + end) + end + m.summon_time = self:callTalent(self.T_PATH_TO_BEYOND, "getDuration") + m.remove_from_party_on_death = true + m:resolve() m:resolve(nil, true) + m:forceLevelup(self.level) + ActorTalents.main_env.setupSummon(self, m, tx, ty) + game.level.map:particleEmitter(tx, ty, 1, "generic_teleport", {rm=60, rM=130, gm=20, gM=110, bm=90, bM=130, am=70, aM=180}) + end + end + + game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") + if not self:teleportRandom(x, y, 0) then + game.logSeen(self, "%s's ghost walk fizzles!", self.name:capitalize()) + else + game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") + end + + self:removeEffect(self.EFF_GHOST_WALK) + game:playSoundNear(self, "talents/teleport") + return true + end, + info = function(self, t) + local range = self:getTalentRange(t) + return ([[Return to the location you used Ghost Walk from.]]):tformat() + end, +} + +newTalent{ + name = "Spectral Sight", + type = {"spell/spectre", 2}, + require = spells_req2, + points = 5, + mode = "passive", + getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 4)) end, + getVision = function(self, t) return math.min(10, math.floor(7.5 + self:getTalentLevel(t)/2)) end, + getEsp = function(self, t) return math.floor(10 + self:getTalentLevel(t)) end, + range = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, 1)) end, + passives = function(self, t, p) + self:talentTemporaryValue(p, "esp", {["undead"]=1}) + end, + info = function(self, t) + local dur = t.getDuration(self,t) + local vision = t.getVision(self,t) + local esp = t.getEsp(self,t) + local range = self:getTalentRange(t) + return ([[While in your spectral form from Ghost Walk and for %d turns afterwards, you gain vision of all enemies within radius %d. +In addition, at all times you gain the ability to sense undead within %d tiles, and can perceive any foe within %d tiles of your necrotic minions.]]): + tformat(dur, vision, esp, range) + end, +} + +newTalent{ + name = "Intangibility", + type = {"spell/spectre", 3}, + require = spells_req3, + points = 5, + mode = "passive", + getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 4)) end, + getChance = function(self, t) return math.floor(self:combatTalentLimit(t, 45, 12, 27)) end, + info = function(self, t) + local dur = t.getDuration(self,t) + local chance = t.getChance(self,t) + return ([[While in your spectral form from Ghost Walk and for %d turns afterwards, damage and detrimental effects have a %d%% chance to harmlessly pass through you.]]): + tformat(dur, chance) + end, +} + +newTalent{ + name = "Path to Beyond", + type = {"spell/spectre",4}, + require = spells_req4, + points = 5, + mode = "passive", + getNb = function(self, t) return math.floor(self:combatTalentScale(t, 2, 4.5)) end, + getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9)) end, + info = function(self, t) + local dur = t.getDuration(self,t) + local nb = t.getNb(self,t) + return ([[On using Ghost Walk - Return, you create a portal that summons %d spirits around you for %d turns. The spirits do very little damage, but taunt all enemies in radius 3 into attacking them.]]): + tformat(nb, dur) + end, +} diff --git a/game/modules/tome/data/talents/spells/spells.lua b/game/modules/tome/data/talents/spells/spells.lua index bbec94e3702e27b65c22104e74856b41f00623ce..c594a21b5ac30a762b259270d7338d1393554de0 100644 --- a/game/modules/tome/data/talents/spells/spells.lua +++ b/game/modules/tome/data/talents/spells/spells.lua @@ -20,8 +20,6 @@ -- Archmage spells newTalentType{ allow_random=true, no_silence=true, is_spell=true, mana_regen=true, type="spell/arcane", name = _t"arcane", description = _t"Arcane studies manipulate the raw magic energies to shape them into both offensive and defensive spells." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, mana_regen=true, type="spell/aether", name = _t"aether", description = _t"Tap on the core arcane forces of the aether, unleashing devastating effects on your foes." } - --- Elemental spells newTalentType{ allow_random=true, no_silence=true, is_spell=true, mana_regen=true, type="spell/fire", name = _t"fire", description = _t"Harness the power of fire to burn your foes to ashes." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, mana_regen=true, type="spell/wildfire", name = _t"wildfire", min_lev = 10, description = _t"Harness the power of wildfire to burn your foes to ashes." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, mana_regen=true, type="spell/earth", name = _t"earth", description = _t"Harness the power of the earth to protect and destroy." } @@ -30,8 +28,6 @@ newTalentType{ allow_random=true, no_silence=true, is_spell=true, mana_regen=tru newTalentType{ allow_random=true, no_silence=true, is_spell=true, mana_regen=true, type="spell/ice", name = _t"ice", min_lev = 10, description = _t"Harness the power of ice to freeze and shatter your foes." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, mana_regen=true, type="spell/air", name = _t"air", description = _t"Harness the power of the air to fry your foes." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, mana_regen=true, type="spell/storm", name = _t"storm", min_lev = 10, description = _t"Harness the power of the storm to incinerate your foes." } - --- Various other magic schools newTalentType{ allow_random=true, no_silence=true, is_spell=true, mana_regen=true, type="spell/meta", name = _t"meta", description = _t"Meta spells alter the working of magic itself." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, mana_regen=true, type="spell/temporal", name = _t"temporal", description = _t"The school of time manipulation." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, mana_regen=true, type="spell/phantasm", name = _t"phantasm", description = _t"Control the power of tricks and illusions." } @@ -60,14 +56,22 @@ newTalentType{ type="golem/golem", name = _t"golem", description = _t"Golem basi newTalentType{ type="golem/drolem", name = _t"drolem", description = _t"Drolem basic capacity." } -- Necromancer spells -newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/necrotic-minions", name = _t"necrotic minions", description = _t"Create and empower dumb undead minions." } -newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/advanced-necrotic-minions", name = _t"advanced necrotic minions", min_lev = 10, description = _t"Create and empower powerful undead minions." } +newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/master-of-bones", name = _t"master of bones", description = _t"Become of the master of bones, creating skeletal minions to do your bidding." } +newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/master-of-flesh", name = _t"master of flesh", description = _t"Become of the master of flesh, creating ghoul minions to do your bidding" } +newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/master-necromancer", name = _t"master necromancer", min_lev = 10, description = _t"Full and total control over your undead army." } newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/nightfall", name = _t"nightfall", description = _t"Manipulate darkness itself to slaughter your foes." } -newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/shades", name = _t"shades", min_lev = 10, description = _t"Invoke and control shades." } -newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/necrosis", name = _t"necrosis", description = _t"Gain control over death, eventually turning into a lich." } +newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/dreadmaster", name = _t"dreadmaster", description = _t"Summon an undead minion of pure darkness to harass your foes." } +newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/age-of-dusk", name = _t"age of dusk", min_lev = 10, description = _t"Recall the glorious days of the Age of Dusk when necromancers reigned supreme." } newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/grave", name = _t"grave", description = _t"Use the rotting cold doom of the tomb to fell your foes." } +newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/glacial-waste", name = _t"glacial waste", description = _t"Wither the land into a cold, dead ground to protect yourself." } +newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/rime-wraith", name = _t"rime wraith", min_lev = 10, description = _t"Summon an undead minion of pure cold to harass your foes." } +newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/death", name = _t"death", description = _t"Learn to fasten your foes way into the grave." } newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/animus", name = _t"animus", description = _t"Crush the souls of your foes to improve yourself." } +newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/eradication", name = _t"eradication", min_lev = 10, description = _t"Doom to all your foes. Crush them." } +newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/necrosis", name = _t"necrosis", generic = true, description = _t"Gain control over death, by unnaturally expanding your life." } +newTalentType{ allow_random=true, no_silence=true, is_necromancy=true, is_spell=true, mana_regen=true, type="spell/spectre", name = _t"spectre", generic = true, description = _t"Turn into a spectre to move around the battlefield." } +-- Stone Warden spells newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="spell/eldritch-shield", name = _t"eldritch shield", description = _t"Infuse arcane forces into your shield." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="spell/eldritch-stone", name = _t"eldritch stone", description = _t"Summon stony spikes imbued with various powers." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="spell/deeprock", name = _t"deeprock", description = _t"Harness the power of the world to turn into a Deeprock Form." } @@ -250,6 +254,17 @@ function necroEssenceDead(self, checkonly) if eff.nb <= 0 then self:removeEffect(self.EFF_ESSENCE_OF_THE_DEAD, true) end end end + +function checkLifeThreshold(val, fct) + return function(self, t) + local checkid = "__check_threshold_"..t.id + if not self[checkid] then self[checkid] = self.life end + if (self[checkid] >= val and self.life < val) or (self[checkid] < val and self.life >= val) then + fct(self, t) + end + self[checkid] = self.life + end +end ------------------------------------------- load("/data/talents/spells/arcane.lua") @@ -282,13 +297,9 @@ load("/data/talents/spells/energy-alchemy.lua") load("/data/talents/spells/stone-alchemy.lua") load("/data/talents/spells/golem.lua") -load("/data/talents/spells/necrotic-minions.lua") -load("/data/talents/spells/advanced-necrotic-minions.lua") -load("/data/talents/spells/nightfall.lua") -load("/data/talents/spells/shades.lua") -load("/data/talents/spells/necrosis.lua") -load("/data/talents/spells/grave.lua") load("/data/talents/spells/animus.lua") +load("/data/talents/spells/necrosis.lua") +load("/data/talents/spells/spectre.lua") load("/data/talents/spells/eldritch-shield.lua") load("/data/talents/spells/eldritch-stone.lua") diff --git a/game/modules/tome/data/timed_effects/magical.lua b/game/modules/tome/data/timed_effects/magical.lua index 9abdf76f03c1b2ff06c6e6d830e82a2838d048a8..44f51566cb415535e4d14438df489a9c24f41f84 100644 --- a/game/modules/tome/data/timed_effects/magical.lua +++ b/game/modules/tome/data/timed_effects/magical.lua @@ -4585,7 +4585,7 @@ newEffect{ newEffect{ name = "DAZZLED", - desc = "Dazzled", + desc = _t"Dazzled", long_desc = function(self, eff) return ("All damage decreased by %d%%."):format(eff.power) end, type = "magical", subtype = { stun=true,}, @@ -4600,8 +4600,8 @@ newEffect{ newEffect{ name = "LICH_RESISTED", image = "talents/lichform.png", - desc = "Immune to Frightening Presence", - long_desc = function(self, eff) return ("You resisted a Lich and are immune to its frightening presence.") end, + desc = _t"Immune to Frightening Presence", + long_desc = function(self, eff) return _t"You resisted a Lich and are immune to its frightening presence." end, type = "magical", subtype = { lich=true, fear=true}, status = "beneficial", @@ -4615,8 +4615,8 @@ newEffect{ newEffect{ name = "ELEMENTAL_MIRAGE1", image = "talents/blur_sight.png", - desc = "Elemental Mirage (First Element)", - long_desc = function(self, eff) return ("%s damage increased by %d%% and resistance penetration by %d%%."):format(DamageType:get(eff.dt).name, eff.power, eff.pen or 0) end, + desc = _t"Elemental Mirage (First Element)", + long_desc = function(self, eff) return ("%s damage increased by %d%% and resistance penetration by %d%%."):tformat(DamageType:get(eff.dt).name, eff.power, eff.pen or 0) end, type = "magical", subtype = { phantasm=true,}, status = "beneficial", @@ -4631,8 +4631,8 @@ newEffect{ newEffect{ name = "LICH_FEAR", image = "talents/lichform.png", - desc = "Frightening Presence", - long_desc = function(self, eff) return ("The mere sight of a Lich sent you into a frightened state, reducing all saves by %d, all damage by %d%% and movement speed by %d%%."):format(eff.saves, eff.dam, eff.speed) end, + desc = _t"Frightening Presence", + long_desc = function(self, eff) return ("The mere sight of a Lich sent you into a frightened state, reducing all saves by %d, all damage by %d%% and movement speed by %d%%."):tformat(eff.saves, eff.dam, eff.speed) end, type = "magical", subtype = { lich=true, fear=true}, status = "detrimental", @@ -4650,8 +4650,8 @@ newEffect{ newEffect{ name = "ELEMENTAL_MIRAGE2", image = "talents/alter_mirage.png", - desc = "Elemental Mirage (Second Element)", - long_desc = function(self, eff) return ("%s damage increased by %d%% and resistance penetration by %d%%."):format(DamageType:get(eff.dt).name, eff.power, eff.pen or 0) end, + desc = _t"Elemental Mirage (Second Element)", + long_desc = function(self, eff) return ("%s damage increased by %d%% and resistance penetration by %d%%."):tformat(DamageType:get(eff.dt).name, eff.power, eff.pen or 0) end, type = "magical", subtype = { phantasm=true,}, status = "beneficial", @@ -4666,8 +4666,8 @@ newEffect{ newEffect{ name = "COMMANDER_OF_THE_DEAD", image = "talents/commander_of_the_dead.png", - desc = "Commander of the Dead", - long_desc = function(self, eff) return ("Physical power, spellpower and all saves increased by %d."):format(eff.power) end, + desc = _t"Commander of the Dead", + long_desc = function(self, eff) return ("Physical power, spellpower and all saves increased by %d."):tformat(eff.power) end, type = "magical", subtype = { lich=true, power=true }, status = "beneficial", @@ -4681,3 +4681,49 @@ newEffect{ self:effectTemporaryValue(eff, "combat_generic_power", eff.power) end, } + +newEffect{ + name = "CONSUME_SOUL", image = "talents/consume_soul.png", + desc = _t"Consume Soul", + long_desc = function(self, eff) return ("Spellpower increased by %d."):tformat(eff.power) end, + type = "magical", + subtype = { necrotic=true }, + status = "beneficial", + parameters = {power=10}, + on_gain = function(self, err) return nil, true end, + on_lose = function(self, err) return nil, true end, + activate = function(self, eff) + self:effectTemporaryValue(eff, "combat_spellpower", eff.power) + end, +} + +newEffect{ + name = "SPIKE_OF_DECREPITUDE", image = "talents/spikes_of_decrepitude.png", + desc = _t"Spike of Decrepitude", + long_desc = function(self, eff) return ("Damage reduced by %d%%."):tformat(eff.power) end, + type = "magical", + subtype = { necrotic=true,}, + status = "detrimental", + parameters = {power=10}, + on_gain = function(self, err) return nil, true end, + on_lose = function(self, err) return nil, true end, + activate = function(self, eff) + self:effectTemporaryValue(eff, "inc_damage", {all = -eff.power}) + end, +} + +newEffect{ + name = "SOUL_LEECH", image = "talents/soul_leech.png", + desc = _t"Soul Leech", + long_desc = function(self, eff) return _t"Soul absorbed upon death." end, + type = "magical", + subtype = { necrotic=true,}, + status = "detrimental", + parameters = {}, + callbackOnDeath = function(self, eff) + if eff.src then eff.src:incSoul(1) end + end, + deactivate = function(self, eff) + if eff.src and eff.powerful then eff.src:incSoul(1) end + end, +}