Commit efc20fbe9f3a3f2be90a21c82c59e50f15412eaa

Authored by DarkGod
2 parents d79e49ed 4c7ab713

Merge branch 'DefilerRevision' into 'master'

Defiler revision

Isn't Vim a text editor too?  I feel like this resource is some sort of political statement

See merge request !529
... ... @@ -1966,6 +1966,34 @@ function util.adjacentCoords(x, y, no_diagonals, no_cardinals)
1966 1966 return coords
1967 1967 end
1968 1968
  1969 +--- Return the closest adjacent coordinate to the source coordinate from the target coordinate (use for gap closer positioning, etc)
  1970 +-- @param x x-coordinate of the source tile.
  1971 +-- @param y y-coordinate of the source tile.
  1972 +-- @param tx x-coordinate of the target tile.
  1973 +-- @param ty y-coordinate of the target tile.
  1974 +-- @param check_block Boolean for whether to check for block_move
  1975 +-- @param extra_check(x,y) Function to run on each grid and return true if that grid is invalid
  1976 +-- @return Table containing the x,y coordinate of the closest grid.
  1977 +function util.closestAdjacentCoord(x, y, tx, ty, check_block, extra_check)
  1978 + local check_block = check_block or true
  1979 + local coords = util.adjacentCoords(x, y)
  1980 + local valid = {}
  1981 + for _, coord in pairs(coords) do
  1982 + if not (check_block and game.level.map:checkEntity(coord[1], coord[2], engine.Map.TERRAIN, "block_move")) and not (extra_check and extra_check(coord[1], coord[2])) then
  1983 + valid[#valid+1] = coord
  1984 + end
  1985 + end
  1986 +
  1987 + if #valid == 0 then return end
  1988 + local closest = valid[1]
  1989 + for _, coord in pairs(valid) do
  1990 + if core.fov.distance(closest[1], closest[2], tx, ty, true) > core.fov.distance(coord[1], coord[2], tx, ty, true) then
  1991 + closest = coord
  1992 + end
  1993 + end
  1994 +
  1995 + return closest
  1996 +end
1969 1997 function util.coordAddDir(x, y, dir)
1970 1998 local dx, dy = util.dirToCoord(dir, x, y)
1971 1999 return x + dx, y + dy
... ...
... ... @@ -109,3 +109,48 @@ newAI("charred_scar_target", function(self)
109 109 self:setTarget(game.player)
110 110 return true
111 111 end)
  112 +
  113 +-- Always retarget to closest
  114 +-- This is most useful on immobile melee prone to just wasting their turns if their current target isn't adjacent
  115 +newAI("target_closest", function(self)
  116 + if not self.x then return end
  117 + local log_detail = config.settings.log_detail_ai or 0
  118 + if log_detail > 0 then print("[ActorAI] Invoking target_closest AI for", self.uid, self.name, self.x, self.y) end
  119 + local aitarget = self.ai_target.actor
  120 + if aitarget then
  121 + if log_detail >= 2 then print("[target_closest AI] current target:", aitarget.uid, aitarget.name) end
  122 + if aitarget.summoner and (aitarget.dead or not game.level:hasEntity(aitarget)) then
  123 + if log_detail > 0 then print("[target_closest AI] targeting summoner of dead summon:", aitarget.uid, aitarget.name) end
  124 + self:setTarget(aitarget.summoner)
  125 + aitarget = self.ai_target.actor
  126 + end
  127 + end
  128 +
  129 + -- Find closest enemy and target it or set no target
  130 + -- Get list of actors ordered by distance
  131 + local arr = self.fov.actors_dist
  132 + local act
  133 + local sqsense = math.max(self.lite or 0, self.infravision or 0, self.heightened_senses or 0)
  134 + if log_detail >= 2 then print("[target_closest AI]", self.uid, self.name, "at", self.x, self.y, "searching for new targets in range", sqsense) end
  135 + sqsense = sqsense * sqsense
  136 + for i = 1, #arr do
  137 + act = self.fov.actors_dist[i]
  138 + if log_detail > 2 then print("\t checking target", act.x, act.y, act.uid, act.name) end
  139 + if act and act.x and not act.dead and self:reactionToward(act) < 0 and game.level.map:isBound(act.x, act.y) and
  140 + (((act.lite or 0) > 0) -- If it has lite we can always see it
  141 + or -- Otherwise check if we can see it with our "senses"
  142 + (self:canSee(act) and (self.fov.actors[act].sqdist <= sqsense) or game.level.map.lites(act.x, act.y))
  143 + ) and not act:attr("invulnerable") then
  144 +
  145 + print("[target_closest AI]", self.uid, self.name, "selecting NEW TARGET", act.x, act.y, act.uid, act.name)
  146 + self:setTarget(act)
  147 + return act
  148 + end
  149 + end
  150 + if aitarget then -- clear old target if a new one wasn't found or kept
  151 + if log_detail > 0 then print("[target_closest AI] clearing old target (no replacement):", aitarget.uid, aitarget.name) end
  152 + if log_detail > 1.4 and config.settings.cheat then game.log("#RED# [%s]%s #ORANGE#CLEARING OLD TARGET#LAST#: [%s]%s", self.uid, self.name, aitarget.uid, aitarget.name) end -- debugging
  153 + self:setTarget()
  154 + end
  155 + return self.ai_target.actor
  156 +end)
\ No newline at end of file
... ...
... ... @@ -1928,6 +1928,10 @@ function _M:tooltip(x, y, seen_by)
1928 1928 local eff = self:hasEffect(self.EFF_FROZEN)
1929 1929 ts:add({"color", 0, 255, 128}, ("Iceblock: %d"):format(eff.hp), {"color", "WHITE"}, true)
1930 1930 end
  1931 + if game.player:knowTalent(self.T_VIM_POOL) then
  1932 + ts:add({"color", 0, 255, 128}, ("%sVim Value: %d#LAST#"):format(self.resources_def.vim.color, (game.player:getWil() * 0.3 + 1) * self.rank), {"color", "WHITE"}, true)
  1933 + end
  1934 +
1931 1935 --ts:add(("Stats: %d / %d / %d / %d / %d / %d"):format(self:getStr(), self:getDex(), self:getCon(), self:getMag(), self:getWil(), self:getCun()), true)
1932 1936 --if #resists > 0 then ts:add("Resists: ", table.concat(resists, ','), true) end
1933 1937
... ... @@ -2611,11 +2615,6 @@ function _M:onTakeHit(value, src, death_note)
2611 2615 end
2612 2616 end
2613 2617
2614   - -- Bloodlust!
2615   - if value > 0 and src and not (src == self) and src.knowTalent and src:knowTalent(src.T_BLOODLUST) then
2616   - src:setEffect(src.EFF_BLOODLUST, 1, {})
2617   - end
2618   -
2619 2618 if value > 0 and self:knowTalent(self.T_RAMPAGE) then
2620 2619 local t = self:getTalentFromId(self.T_RAMPAGE)
2621 2620 t:onTakeHit(self, value / self.max_life)
... ... @@ -3260,15 +3259,13 @@ function _M:die(src, death_note)
3260 3259 end
3261 3260
3262 3261 -- Increase vim
3263   - if src and src.knowTalent and src:knowTalent(src.T_VIM_POOL) then src:incVim(1 + src:getWil() / 10) end
3264   - if src and src.attr and src:attr("vim_on_death") and not self:attr("undead") then src:incVim(src:attr("vim_on_death")) end
3265   - if src and death_note and death_note.source_talent and death_note.source_talent.vim and src.last_vim_turn ~= game.turn then
3266   - src.last_vim_turn = game.turn
3267   - game:onTickEnd(function() -- Do it on tick end to make sure Vim is spent by the talent code before being refunded
3268   - src:incVim(util.getval(death_note.source_talent.vim, self, death_note.source_talent))
  3262 + if src and src.knowTalent and src:knowTalent(src.T_VIM_POOL) and src:reactionToward(self) <= 0 then
  3263 + game:onTickEnd(function() -- Do it on tick end to make sure Vim is spent by the talent code before being gained, otherwise it feels weird when you expect to spend life
  3264 + src:incVim((src:getWil() * 0.3 + 1) * self.rank)
3269 3265 end)
3270 3266 end
3271   -
  3267 + if src and src.attr and src:attr("vim_on_death") and not self:attr("undead") then src:incVim(src:attr("vim_on_death")) end
  3268 +
3272 3269 if src and ((src.resolveSource and src:resolveSource().player) or src.player) then
3273 3270 -- Achievements
3274 3271 local p = game.party:findMember{main=true}
... ... @@ -5330,13 +5327,15 @@ end
5330 5327 -- Overwrite incVim to set up Bloodcasting
5331 5328 local previous_incVim = _M.incVim
5332 5329 function _M:incVim(v)
5333   - if v < 0 and self:attr("bloodcasting") then
5334   - local mult = self:attr("bloodcasting") / 100
  5330 + if v < 0 then
  5331 + local mult = 2
  5332 + if self:attr("bloodcasting") then mult = self:attr("bloodcasting") / 100 end
  5333 +
5335 5334 local cost = math.abs(v)
5336 5335 if self.vim - cost < 0 then
5337 5336 local damage = (cost - (self.vim or 0)) * mult
5338 5337 self:incVim(-self.vim or 0)
5339   - self.life = self.life - damage
  5338 + self.life = self.life - damage -- die_at life can't be used
5340 5339 else
5341 5340 return previous_incVim(self, v)
5342 5341 end
... ... @@ -5348,8 +5347,10 @@ end
5348 5347 -- Overwrite getVim to set up Bloodcasting
5349 5348 local previous_getVim = _M.getVim
5350 5349 function _M:getVim()
5351   - if self:attr("bloodcasting") and self.on_preuse_checking_resources then
5352   - return math.max(self.vim, self.life)
  5350 + if self.on_preuse_checking_resources then
  5351 + local mult = 2
  5352 + if self:attr("bloodcasting") then mult = self:attr("bloodcasting") / 100 end
  5353 + return math.max(self.vim, self.life / mult)
5353 5354 else
5354 5355 return previous_getVim(self)
5355 5356 end
... ...
... ... @@ -738,7 +738,7 @@ function _M:attackTargetHitProcs(target, weapon, dam, apr, armor, damtype, mult,
738 738 -- Ruin
739 739 if hitted and not target.dead and self:knowTalent(self.T_RUIN) and self:isTalentActive(self.T_RUIN) then
740 740 local t = self:getTalentFromId(self.T_RUIN)
741   - local dam = t.getDamage(self, t)
  741 + local dam = {dam=t.getDamage(self, t), healfactor=0.4, source=t}
742 742 DamageType:get(DamageType.DRAINLIFE).projector(self, target.x, target.y, DamageType.DRAINLIFE, dam)
743 743 end
744 744
... ... @@ -1764,7 +1764,7 @@ function _M:combatSpellpower(mod, add)
1764 1764 add = add + self:callTalent(self.T_SHADOW_CUNNING,"getSpellpower") * self:getCun() / 100
1765 1765 end
1766 1766 if self:hasEffect(self.EFF_BLOODLUST) then
1767   - add = add + self:hasEffect(self.EFF_BLOODLUST).power
  1767 + add = add + self:hasEffect(self.EFF_BLOODLUST).spellpower * self:hasEffect(self.EFF_BLOODLUST).stacks
1768 1768 end
1769 1769
1770 1770 local am = 1
... ...
... ... @@ -100,8 +100,8 @@ It slowly decreases and is replenished by using some talents.
100 100 TOOLTIP_VIM = [[#GOLD#Vim#LAST#
101 101 Vim represents the amount of life energy you control. Each corruption talent requires some.
102 102 It does not regenerate naturally; you need to drain it from yourself or your victims.
103   -Each time you kill a creature you drain 10% of your Willpower as Vim.
104   -Also if you used a Corruption spell that cost Vim to kill a creature, that cost is refunded on death.
  103 +Each time you kill a creature you gain 30% of your Willpower + 1 as Vim. This value is multiplied by half the rank of the creature.
  104 +If you can't pay for the Vim cost of a talent you may instead pay with life at a rate of 200% of the Vim cost.
105 105 ]]
106 106
107 107 TOOLTIP_EQUILIBRIUM = [[#GOLD#Equilibrium#LAST#
... ...
... ... @@ -2469,9 +2469,10 @@ newDamageType{
2469 2469 useImplicitCrit(src, state)
2470 2470 if _G.type(dam) == "number" then dam = {dam=dam, healfactor=0.4} end
2471 2471 local target = game.level.map(x, y, Map.ACTOR) -- Get the target first to make sure we heal even on kill
  2472 + dam.source = dam.source or target
2472 2473 local realdam = DamageType:get(DamageType.BLIGHT).projector(src, x, y, DamageType.BLIGHT, dam.dam, state)
2473 2474 if target and realdam > 0 and not src:attr("dead") then
2474   - src:heal(realdam * dam.healfactor, target)
  2475 + src:heal(realdam * dam.healfactor, dam.source)
2475 2476 end
2476 2477 return realdam
2477 2478 end,
... ...
... ... @@ -72,7 +72,7 @@ newTalent{
72 72 getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end,
73 73 getDamage = function(self, t) return self:combatTalentSpellDamage(t, 5, 140) end,
74 74 action = function(self, t)
75   - local tg = {type="bolt", range=self:getTalentRange(t), talent=t}
  75 + local tg = {type="bolt", range=self:getTalentRange(t), friendlyblock=false, talent=t}
76 76 local x, y = self:getTarget(tg)
77 77 if not x or not y then return nil end
78 78
... ... @@ -82,13 +82,23 @@ newTalent{
82 82 if not target then return end
83 83
84 84 if core.fov.distance(self.x, self.y, target.x, target.y) > 1 then
85   - target:pull(self.x, self.y, tg.range)
86 85 DamageType:get(DamageType.PHYSICAL).projector(self, target.x, target.y, DamageType.PHYSICAL, dam)
87 86 if target:canBe("pin") then
88 87 target:setEffect(target.EFF_BONE_GRAB, t.getDuration(self, t), {apply_power=self:combatSpellpower()})
89 88 else
90 89 game.logSeen(target, "%s resists the pin!", target.name:capitalize())
91 90 end
  91 +
  92 + local hit = self:checkHit(self:combatSpellpower(), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0))
  93 + if not target:canBe("teleport") or not hit then
  94 + game.logSeen(target, "%s resists being teleported by Bone Grab!", target.name:capitalize())
  95 + return true
  96 + end
  97 +
  98 + -- Grab the closest adjacent grid that doesn't have block_move or no_teleport
  99 + local grid = util.closestAdjacentCoord(self.x, self.y, target.x, target.y, true, function(x, y) return game.level.map.attrs(x, y, "no_teleport") end)
  100 + if not grid then return true end
  101 + target:teleportRandom(grid[1], grid[2], 0)
92 102 else
93 103 local tg = {type="cone", cone_angle=90, range=0, radius=6, friendlyfire=false}
94 104
... ... @@ -141,6 +151,7 @@ newTalent{
141 151 end,
142 152 callbackOnTalentPost = function(self, t, ab, ret, silent)
143 153 if ab.no_energy then return end
  154 + if ab.mode ~= "active" then return end
144 155 if self.turn_procs.bone_spike then return end
145 156 self.turn_procs.bone_spike = true
146 157
... ... @@ -182,7 +193,7 @@ newTalent{
182 193 direct_hit = true,
183 194 getRegen = function(self, t) return self:combatTalentLimit(t, 3, 20, 3.3) end,
184 195 getNb = function(self, t) return math.floor(self:combatTalentScale(t, 1, 3.5)) end,
185   - getThreshold = function(self, t) return math.floor(self:combatSpellpower()) end,
  196 + getThreshold = function(self, t) return math.floor(self:combatSpellpower() * 0.7) end,
186 197 iconOverlay = function(self, t, p)
187 198 local p = self.sustain_talents[t.id]
188 199 if not p or not p.nb then return "" end
... ...
... ... @@ -293,8 +293,10 @@ newTalent{
293 293 params.__tmpvals = nil
294 294 params.src = self
295 295 params.apply_power = self:combatSpellpower()
  296 + local dur = math.max(6, params.dur)
  297 +
296 298 if target:canBe("disease") then
297   - target:setEffect(disease.id, 6, params)
  299 + target:setEffect(disease.id, dur, params)
298 300 else
299 301 game.logSeen(target, "%s resists the disease!", target.name:capitalize())
300 302 end
... ...
... ... @@ -42,24 +42,24 @@ newTalent{
42 42 end,
43 43 }
44 44
45   --- Fix display
46 45 newTalent{
47 46 name = "Bloodlust",
48 47 type = {"corruption/reaving-combat", 2},
49 48 mode = "passive",
50 49 require = str_corrs_req2,
51 50 points = 5,
52   - -- _M:combatSpellpower references effect in mod\class\interface\Combat.lua
53   - -- Effect is refreshed in function _M:onTakeHit(value, src) in mod\class\Actor.lua
54   - -- getParams called in definition of EFF_BLOODLUST in data\timed_effects\magical.lua
55   - getParams = function(self, t) -- returns maxSP per turn, max duration
56   - return self:combatTalentScale(t, 1, 8, 0.75), math.floor(self:combatTalentScale(t, 2, 6))
  51 + getSpellpower = function(self, t) return self:combatTalentScale(t, 1, 5.5) end, -- 66 at TL5 total
  52 + callbackOnMeleeAttack = function(self, t, target, hitted)
  53 + if not hitted or not (self:reactionToward(target) < 0) then return end
  54 + self:setEffect(self.EFF_BLOODLUST, 3, {spellpower = t.getSpellpower(self, t), max_stacks = 10})
  55 + return true
57 56 end,
58 57 info = function(self, t)
59   - local SPbonus, maxDur = t.getParams(self, t)
60   - return ([[Each time you deal damage to one of your foes, you enter a bloodlust-infused frenzy, increasing your Spellpower by 1 (maximum %d Spellpower per turn, %d Spellpower overall), and extending any current frenzy for an additional turn.
61   - The frenzy lasts up to %d turns, and the bonus decreases by %0.1f%% of its current value each turn you don't deal damage.]]):
62   - format(SPbonus / 5, SPbonus*8, maxDur, 100/maxDur)
  58 + local SPbonus = t.getSpellpower(self, t)
  59 + return ([[Each time you hit an enemy with a melee weapon you enter a bloodlust-infused frenzy, increasing your Spellpower by %0.1f.
  60 + This effect stacks up to 10 times for a total Spellpower gain of %d.
  61 + The frenzy lasts 3 turns.]]):
  62 + format(SPbonus, SPbonus*10)
63 63 end,
64 64 }
65 65
... ...
... ... @@ -66,7 +66,7 @@ carrionworm = function(self, target, duration, x, y)
66 66 desc = [[A worm spawned from a damaged horror. Destroying it may have consequences.]],
67 67 autolevel = "none",
68 68 ai = "summoned", ai_real = "tactical",
69   - ai_state = { ai_move="move_complex", talent_in=1, ally_compassion=10 },
  69 + ai_state = { ai_move="move_complex", talent_in=1, ally_compassion=10, ai_target="target_closest", },
70 70 ai_tactic = resolvers.tactic"melee",
71 71 stats = { str=10, dex=15, mag=3, con=3 },
72 72 level_range = {1, self.level}, exp_worth = 0,
... ...
... ... @@ -22,7 +22,6 @@ newTalent{
22 22 type = {"corruption/sanguisuge", 1},
23 23 require = corrs_req1,
24 24 points = 5,
25   - vim = 0,
26 25 cooldown = 5,
27 26 reflectable = true,
28 27 proj_speed = 15,
... ... @@ -47,7 +46,6 @@ newTalent{
47 46 end,
48 47 }
49 48
50   --- Sustain?
51 49 newTalent{
52 50 name = "Bloodcasting",
53 51 type = {"corruption/sanguisuge", 2},
... ... @@ -55,13 +53,12 @@ newTalent{
55 53 points = 5,
56 54 mode = "passive",
57 55 no_npc_use = true,
58   - getLifeCost = function(self, t) return math.floor(self:combatTalentScale(t, 250, 100)) end,
  56 + getLifeCost = function(self, t) return math.floor(self:combatTalentScale(t, 180, 100)) end,
59 57 passives = function(self, t, p)
60 58 self:talentTemporaryValue(p, "bloodcasting", t.getLifeCost(self, t))
61 59 end,
62 60 info = function(self, t)
63   - return ([[Your corruption spells will consume health instead of vim if their cost is higher than your vim.
64   - The health cost is equal to %d%% of the vim cost.]]):
  61 + return ([[The cost of using life instead of vim for talents is reduced to %d%%.]]):
65 62 format(t.getLifeCost(self,t))
66 63 end,
67 64 }
... ... @@ -108,7 +105,6 @@ newTalent{
108 105 points = 5,
109 106 vim = 40,
110 107 cooldown = 20,
111   - range = 10,
112 108 no_energy = true,
113 109 tactical = { HEAL = 2 },
114 110 getMult = function(self,t) return self:combatTalentSpellDamage(t, 4, 30) end,
... ...
... ... @@ -52,15 +52,24 @@ newTalent{
52 52 local speed2, hit2 = self:attackTargetWith(target, offweapon.combat, nil, self:getOffHandMult(offweapon.combat, t.getDamage(self, t)))
53 53 DamageType:projectingFor(self, nil)
54 54
  55 + local effs = target:effectsFilter(function(e) return e.subtype.disease end, 9999)
55 56 if hit1 then
56   - local effs = target:effectsFilter(function(e) return e.subtype.disease end, 1)
  57 + if #effs > 0 then table.sort(effs, function(a, b)
  58 + local eff = target:hasEffect(a)
  59 + local eff2 = target:hasEffect(b)
  60 + return eff.dur < eff2.dur end)
  61 + end
57 62 local eff2 = target:hasEffect(effs[1])
58 63 if eff2 then
59 64 eff2.dur = eff2.dur + t.getIncrease(self, t)
60 65 end
61 66 end
62 67 if hit2 then
63   - local effs = target:effectsFilter(function(e) return e.subtype.disease end, 1)
  68 + if #effs > 0 then table.sort(effs, function(a, b)
  69 + local eff = target:hasEffect(a)
  70 + local eff2 = target:hasEffect(b)
  71 + return eff.dur < eff2.dur end)
  72 + end
64 73 local eff2 = target:hasEffect(effs[1])
65 74 if eff2 then
66 75 eff2.dur = eff2.dur + t.getIncrease(self, t)
... ... @@ -70,7 +79,7 @@ newTalent{
70 79 return true
71 80 end,
72 81 info = function(self, t)
73   - return ([[Strike the target with both weapons dealing %d%% damage with each hit. Each strike that hits will increase the duration of a random disease effect by %d.]]):
  82 + return ([[Strike the target with both weapons dealing %d%% damage with each hit. Each strike that hits will increase the duration of the lowest duration disease effect by %d.]]):
74 83 format(100 * t.getDamage(self, t), t.getIncrease(self, t))
75 84 end,
76 85 }
... ...
... ... @@ -1003,47 +1003,21 @@ newEffect{
1003 1003 newEffect{
1004 1004 name = "BLOODLUST", image = "talents/bloodlust.png",
1005 1005 desc = "Bloodlust",
1006   - long_desc = function(self, eff) return ("The target is in a magical frenzy, improving spellpower by %d."):format(eff.power) end,
  1006 + long_desc = function(self, eff) return ("The target is in a magical frenzy, improving spellpower by %d."):format(eff.spellpower * eff.stacks) end,
1007 1007 type = "magical",
1008 1008 subtype = { frenzy=true },
1009 1009 status = "beneficial",
1010   - charges = function(self, eff) return math.floor(eff.power) end,
1011   - parameters = { power=1 },
1012   - on_timeout = function(self, eff)
1013   - if eff.refresh_turn + 10 < game.turn then -- Decay only if it's not refreshed
1014   - eff.power = math.max(0, eff.power*(100-eff.decay)/100)
1015   - end
1016   - end,
  1010 + charges = function(self, eff) return math.floor(eff.spellpower * eff.stacks) end,
  1011 + parameters = { spellpower=1, stacks=1, max_stacks=1 },
1017 1012 on_merge = function(self, old_eff, new_eff)
1018   - local dur = new_eff.dur
1019   - local max_turn, maxDur = self:callTalent(self.T_BLOODLUST, "getParams")
1020   - local maxSP = max_turn * 6 -- max total sp
1021   - local power = new_eff.power
1022   -
1023   - if old_eff.last_turn + 10 <= game.turn then -- clear limits every game turn (10 ticks)
1024   - old_eff.used_this_turn = 0
1025   - old_eff.last_turn = game.turn
1026   - end
1027   - if old_eff.used_this_turn >= max_turn then
1028   - dur = 0
1029   - power = 0
1030   - else
1031   - power = math.min(max_turn-old_eff.used_this_turn, power)
1032   - old_eff.power = math.min(old_eff.power + power, maxSP)
1033   - old_eff.used_this_turn = old_eff.used_this_turn + power
1034   - end
1035   -
1036   - old_eff.decay = 100/maxDur
1037   - old_eff.dur = math.min(old_eff.dur + dur, maxDur)
1038   - old_eff.refresh_turn = game.turn
  1013 + old_eff.dur = new_eff.dur
  1014 + old_eff.stacks = old_eff.stacks + 1
  1015 + old_eff.max_stacks = new_eff.max_stacks
  1016 + old_eff.stacks = math.min(old_eff.max_stacks, old_eff.stacks)
1039 1017 return old_eff
1040 1018 end,
1041 1019 activate = function(self, eff)
1042   - eff.last_turn = game.turn
1043   - local SPbonus, maxDur = self:callTalent(self.T_BLOODLUST, "getParams")
1044   - eff.used_this_turn = eff.power
1045   - eff.decay = 100/maxDur
1046   - eff.refresh_turn = game.turn
  1020 + eff.stacks = 1
1047 1021 end,
1048 1022 deactivate = function(self, eff)
1049 1023 end,
... ... @@ -4261,6 +4235,7 @@ newEffect{
4261 4235 long_desc = function(self, eff) return ("The target is hexed. Each time it uses an ability it takes %0.2f fire damage, and talent cooldowns are increased by %s plus 1 turn."):
4262 4236 format(eff.dam, eff.power and ("%d%%"):format((eff.power-1)*100) or "")
4263 4237 end,
  4238 + charges = function(self, eff) return (tostring(math.floor((eff.power-1)*100)).."%") end,
4264 4239 type = "magical",
4265 4240 subtype = { hex=true, fire=true },
4266 4241 status = "detrimental",
... ... @@ -4274,6 +4249,7 @@ newEffect{
4274 4249 name = "EMPATHIC_HEX", image = "talents/empathic_hex.png",
4275 4250 desc = "Empathic Hex",
4276 4251 long_desc = function(self, eff) return ("The target is hexed, creating an empathic bond with its victims. It takes %d%% feedback damage from all damage done."):format(eff.power) end,
  4252 + charges = function(self, eff) return (tostring(math.floor(eff.power)).."%") end,
4277 4253 type = "magical",
4278 4254 subtype = { hex=true, dominate=true },
4279 4255 status = "detrimental",
... ...