diff --git a/game/engines/default/engine/interface/ActorTalents.lua b/game/engines/default/engine/interface/ActorTalents.lua index a055f4dda57ba799dc27a371aaa1b2bc9501e2a4..9230becb030ee251f35828860d4eca0f4e5208ad 100644 --- a/game/engines/default/engine/interface/ActorTalents.lua +++ b/game/engines/default/engine/interface/ActorTalents.lua @@ -197,6 +197,19 @@ function _M:useTalent(id, who, force_level, ignore_cd, force_target, silent, no_ if not self:postUseTalent(ab, ret) then return end self.sustain_talents[id] = ret + + if ab.sustain_lists then + local lists = ab.sustain_lists + if 'table' ~= type(lists) then lists = {lists} end + for _, list in ipairs(lists) do + if 'table' == type(list) then + list = table.getTable(self, unpack(list)) + else + list = table.getTable(self, list) + end + table.insert(list, id) + end + end else local old_level if force_level then old_level = who.talents[id]; who.talents[id] = force_level end @@ -214,6 +227,19 @@ function _M:useTalent(id, who, force_level, ignore_cd, force_target, silent, no_ -- Everything went ok? then start cooldown if any if not ignore_cd then self:startTalentCooldown(ab) end self.sustain_talents[id] = nil + + if ab.sustain_lists then + local lists = ab.sustain_lists + if 'table' ~= type(lists) then lists = {lists} end + for _, list in ipairs(lists) do + if 'table' == type(list) then + list = table.getTable(self, unpack(list)) + else + list = table.getTable(self, list) + end + table.removeFromList(list, id) + end + end end end) local success, err @@ -353,6 +379,19 @@ function _M:learnTalent(t_id, force, nb) end end end + + if t.learn_lists then + local lists = t.learn_lists + if 'table' ~= type(lists) then lists = {lists} end + for _, list in ipairs(lists) do + if 'table' == type(list) then + list = table.getTable(self, unpack(list)) + else + list = table.getTable(self, list) + end + table.insert(list, t.id) + end + end end for i = 1, (nb or 1) do @@ -443,6 +482,19 @@ function _M:unlearnTalent(t_id, nb) end end + if t.learn_lists and not self:knowTalent(t_id) then + local lists = t.learn_lists + if 'table' ~= type(lists) then lists = {lists} end + for _, list in ipairs(lists) do + if 'table' == type(list) then + list = table.getTable(self, unpack(list)) + else + list = table.getTable(self, list) + end + table.removeFromList(list, t.id) + end + end + if self.talents[t_id] == nil then self.talents_auto[t_id] = nil end self.changed = true diff --git a/game/engines/default/engine/interface/ActorTemporaryEffects.lua b/game/engines/default/engine/interface/ActorTemporaryEffects.lua index 581a62c35b4834c73de76e8edd4cf150e356f60e..1dcbbaf2c89c88de25dc12210d245e41607c73b8 100644 --- a/game/engines/default/engine/interface/ActorTemporaryEffects.lua +++ b/game/engines/default/engine/interface/ActorTemporaryEffects.lua @@ -141,6 +141,20 @@ function _M:setEffect(eff_id, dur, p, silent) end end if ed.activate then ed.activate(self, p, ed) end + + if ed.lists then + local lists = ed.lists + if 'table' ~= type(lists) then lists = {lists} end + for _, list in ipairs(lists) do + if 'table' == type(list) then + list = table.getTable(self, unpack(list)) + else + list = table.getTable(self, list) + end + end + table.insert(list, eff_id) + end + self.changed = true self:check("on_temporary_effect_added", eff_id, ed, p) end @@ -177,6 +191,19 @@ function _M:removeEffect(eff, silent, force) end end if _M.tempeffect_def[eff].deactivate then _M.tempeffect_def[eff].deactivate(self, p, _M.tempeffect_def[eff]) end + if ed.lists then + local lists = ed.lists + if 'table' ~= type(lists) then lists = {lists} end + for _, list in ipairs(lists) do + if 'table' == type(list) then + list = table.getTable(self, unpack(list)) + else + list = table.getTable(self, list) + end + end + table.removeFromList(list, eff_id) + end + self:check("on_temporary_effect_removed", eff, _M.tempeffect_def[eff], p) end diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua index f4a6dcfaef4baf3f7a852bd6ca15496a0f02baf8..15b04b91e65edfe605385e091d7fdd9ed3fd147b 100644 --- a/game/engines/default/engine/utils.lua +++ b/game/engines/default/engine/utils.lua @@ -394,6 +394,26 @@ function table.set(table, ...) table[args[#args - 1]] = args[#args] end +--[=[ + Decends recursively through a table by the given list of keys, + returning the table at the end. Missing keys will have tables + created for them. If a non-table value is encountered, return nil. +]=] +function table.getTable(table, ...) + if 'table' ~= type(table) then return end + local args = {...} + for i = 1, #args do + local key = args[i] + local subtable = table[key] + if not subtable then + subtable = {} + table[key] = subtable + end + if 'table' ~= type(subtable) then return end + table = subtable + end + return table +end -- Taken from http://lua-users.org/wiki/SortedIteration and modified local function cmp_multitype(op1, op2) diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 1c1fb340325f4ab39b5afa89cb4a6a308de94a5e..45f72560cba49068c98dfe2f5ed7e4120446f982 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -2184,7 +2184,7 @@ function _M:onTakeHit(value, src, death_note) self:removeEffect(self.EFF_PSI_DAMAGE_SHIELD) end end - + if value > 0 and self:attr("shadow_empathy") then -- Absorb damage into a random shadow local shadow = self:callTalent(self.T_SHADOW_EMPATHY, "getRandomShadow") @@ -5082,7 +5082,7 @@ function _M:postUseTalent(ab, ret, silent) end -- Cancel stealth! - if ab.id ~= self.T_STEALTH and ab.id ~= self.T_HIDE_IN_PLAIN_SIGHT and not util.getval(ab.no_break_stealth, self, ab) then self:breakStealth() end + if not util.getval(ab.no_break_stealth, self, ab) then self:breakStealth() end if ab.id ~= self.T_LIGHTNING_SPEED then self:breakLightningSpeed() end if ab.id ~= self.T_GATHER_THE_THREADS and ab.is_spell then self:breakChronoSpells() end if not ab.no_reload_break then self:breakReloading() end @@ -5166,6 +5166,22 @@ function _M:forceUseTalent(t, def) return unpack(ret) end +-- Remove an effect or sustain. +function _M:removeModifier(id) + if 'T_' == id:sub(1, 2) then + self:forceUseTalent(id, {ignore_energy=true}) + elseif 'EFF_' == id:sub(1, 4) then + self:removeEffect(id) + end +end + +-- Remove a list of effects or sustains. +function _M:removeModifierList(list) + for _, id in ipairs(list) do + self:removeModifier(id) + end +end + function _M:breakReloading() if self:hasEffect(self.EFF_RELOADING) then self:removeEffect(self.EFF_RELOADING) @@ -5174,7 +5190,8 @@ end --- Breaks stealth if active function _M:breakStealth() - if self:isTalentActive(self.T_STEALTH) then + local breaks = self.break_with_stealth + if breaks and #breaks > 0 then local chance = 0 if self:knowTalent(self.T_UNSEEN_ACTIONS) then chance = self:callTalent(self.T_UNSEEN_ACTIONS,"getChance") + (self:getLck() - 50) * 0.2 @@ -5182,28 +5199,15 @@ function _M:breakStealth() -- Do not break stealth if rng.percent(chance) then return end - self:forceUseTalent(self.T_STEALTH, {ignore_energy=true}) + self:removeModifierList(breaks) self.changed = true end end --- Breaks step up if active function _M:breakStepUp() - if self:hasEffect(self.EFF_STEP_UP) then - self:removeEffect(self.EFF_STEP_UP) - end - if self:hasEffect(self.EFF_WILD_SPEED) then - self:removeEffect(self.EFF_WILD_SPEED) - end - if self:hasEffect(self.EFF_HUNTER_SPEED) then - self:removeEffect(self.EFF_HUNTER_SPEED) - end - if self:hasEffect(self.EFF_REFLEXIVE_DODGING) then - self:removeEffect(self.EFF_REFLEXIVE_DODGING) - end - if self:hasEffect(self.EFF_SKIRMISHER_DIRECTED_SPEED) then - self:removeEffect(self.EFF_SKIRMISHER_DIRECTED_SPEED) - end + local breaks = self.break_with_step_up + if breaks and #breaks > 0 then self:removeModifierList(breaks) end end --- Breaks lightning speed if active diff --git a/game/modules/tome/data/talents/cunning/stealth.lua b/game/modules/tome/data/talents/cunning/stealth.lua index b605d362015d935736e773d1a4aa86a6d3038fb1..ffb001310f62962f2a47426e65bb674cc53680d0 100644 --- a/game/modules/tome/data/talents/cunning/stealth.lua +++ b/game/modules/tome/data/talents/cunning/stealth.lua @@ -45,6 +45,7 @@ newTalent{ allow_autocast = true, no_energy = true, tactical = { BUFF = 3 }, + no_break_stealth = true, getStealthPower = function(self, t) return 10 + self:combatScale(math.max(1,self:getCun(10, true) * self:getTalentLevel(t)), 5, 1, 54, 50) end, --TL 5, cun 100 = 54 getRadius = function(self, t) return math.ceil(self:combatTalentLimit(t, 0, 8.9, 4.6)) end, -- Limit to range >= 1 on_pre_use = function(self, t, silent) @@ -58,13 +59,14 @@ newTalent{ -- Check nearby actors detection ability if not self.x or not self.y or not game.level then return end if not rng.percent(self.hide_chance or 0) then - if stealthDetection(self, t.getRadius(self, t)) > 0 then + if stealthDetection(self, t.getRadius(self, t)) > 0 then if not silent then game.logPlayer(self, "You are being observed too closely to enter Stealth!") end return nil end end return true end, + sustain_lists = "break_with_stealth", activate = function(self, t) local res = { stealth = self:addTemporaryValue("stealth", t.getStealthPower(self, t)), @@ -123,6 +125,7 @@ newTalent{ -- 90% (~= 47% chance against 1 opponent (range 1) at talent level 1, 270% (~= 75% chance against 1 opponent (range 1) and 3 opponents (range 6) at talent level 5 -- vs flat 47% at 1, 75% @ 5 previous stealthMult = function(self, t) return self:combatTalentScale(t, 0.9, 2.7) end, + no_break_stealth = true, getChance = function(self, t, fake) local netstealth = t.stealthMult(self, t) * (self:callTalent(self.T_STEALTH, "getStealthPower") + (self:attr("inc_stealth") or 0)) if fake then return netstealth end diff --git a/game/modules/tome/data/timed_effects/physical.lua b/game/modules/tome/data/timed_effects/physical.lua index 21efc23075a46055315518cbc0d4afbff222bdb2..d9299aa3fc5e30974e98d37e7c92816f519b1215 100644 --- a/game/modules/tome/data/timed_effects/physical.lua +++ b/game/modules/tome/data/timed_effects/physical.lua @@ -1096,6 +1096,7 @@ newEffect{ local d = game.turn - eff.start_turn return util.bound(360 - d / eff.possible_end_turns * 360, 0, 360) end, + lists = 'break_with_step_up', activate = function(self, eff) eff.start_turn = game.turn eff.possible_end_turns = 10 * (eff.dur+1) @@ -1124,6 +1125,7 @@ newEffect{ local d = game.turn - eff.start_turn return util.bound(360 - d / eff.possible_end_turns * 360, 0, 360) end, + lists = 'break_with_step_up', activate = function(self, eff) eff.start_turn = game.turn eff.possible_end_turns = 10 * (eff.dur+1) @@ -1152,6 +1154,7 @@ newEffect{ local d = game.turn - eff.start_turn return util.bound(360 - d / eff.possible_end_turns * 360, 0, 360) end, + lists = 'break_with_step_up', activate = function(self, eff) eff.start_turn = game.turn eff.possible_end_turns = 10 * (eff.dur+1) @@ -1281,14 +1284,14 @@ newEffect{ callbackOnHit = function(self, eff, cb, src) if not src then return cb.value end local share = cb.value * eff.sharePct - + -- deal the redirected damage as physical because I don't know how to preserve the damage type in a callback if not self.__grapling_feedback_damage then self.__grapling_feedback_damage = true DamageType:get(DamageType.PHYSICAL).projector(self or eff.src, eff.trgt.x, eff.trgt.y, DamageType.PHYSICAL, share) self.__grapling_feedback_damage = nil end - + return cb.value - share end, } @@ -1502,6 +1505,7 @@ newEffect{ parameters = { power=0.1 }, on_gain = function(self, err) return "#Target# speeds up.", "+Reflexive Dodging" end, on_lose = function(self, err) return "#Target# slows down.", "-Reflexive Dodging" end, + lists = 'break_with_step_up', activate = function(self, eff) eff.tmpid = self:addTemporaryValue("global_speed_add", eff.power) end, @@ -2653,6 +2657,7 @@ newEffect { }, status = "beneficial", on_lose = function(self, eff) return "#Target# loses speed.", "-Directed Speed" end, + lists = 'break_with_step_up', callbackOnMove = function(self, eff, moved, force, ox, oy) local angle_start = normalize_direction(math.atan2(self.y - eff.start_y, self.x - eff.start_x)) local angle_last = normalize_direction(math.atan2(self.y - eff.last_y, self.x - eff.last_x))