diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 8744a4c9260764d67919c64a99cbc6d6ab630af6..98632f3689d0d8ae28d4e368eb21aa639d071e50 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -1082,23 +1082,6 @@ function _M:onTakeHit(value, src) end end - if self:attr("damage_shunt") then - -- Absorb damage into the shield - if value <= self.damage_shunt_absorb then - self.damage_shunt_absorb = self.damage_shunt_absorb - value - value = 0 - else - value = value - self.damage_shunt_absorb - self.damage_shunt_absorb = 0 - end - - -- If we are at the end of the capacity, remove the effect - if self.damage_shunt_absorb <= 0 then - game.logPlayer(self, "Your damage shunt spell has done all it can!") - self:removeEffect(self.EFF_DAMAGE_SHUNT) - end - end - if self:hasEffect(self.EFF_BONE_SHIELD) then local e = self.tempeffect_def[self.EFF_BONE_SHIELD] e.absorb(self, self.tmp[self.EFF_BONE_SHIELD]) @@ -1504,6 +1487,23 @@ function _M:die(src) p.src:project({type="ball", radius=4, x=self.x, y=self.y}, self.x, self.y, DamageType.MATTER, p.explosion, {type="flame"}) end end + + if self:hasEffect(self.EFF_CEASE_TO_EXIST) then + local kill = true + game:onTickEnd(function() + if game._chronoworlds == nil then + game.logPlayer(game.player, "#LIGHT_RED#The cease to exist spell fizzles and cancels, leaving the timeline intact.") + kill = false + return + end + game:chronoRestore("cease_to_exist", true) + -- check that the kill condition still applies + if kill == true then + local t = game.player:getTalentFromId(game.player.T_CEASE_TO_EXIST) + t.do_instakill(game.player, t) + end + end) + end -- Increase vim if src and src.knowTalent and src:knowTalent(src.T_VIM_POOL) then src:incVim(1 + src:getWil() / 10) end @@ -2394,10 +2394,9 @@ function _M:postUseTalent(ab, ret) if ab.id ~= self.T_GATHER_THE_THREADS then self:breakGatherTheThreads() end self:breakStepUp() - --and ab.type[1] == "chronomancy" and ab.mode == "activated" and self:getTalentLevel(ab.id) >= self:getTalentLevel(self.T_REDUX) - if ab.id ~= self.T_REDUX and self:hasEffect(self.EFF_REDUX) and ab.type[1]:find("^chronomancy/") and ab.mode == "activated" and self:getTalentLevel(self.T_REDUX) >= self:getTalentLevel(ab.id) then self:removeEffect(self.EFF_REDUX) + -- this still consumes energy but works as the talent suggests it does self:forceUseTalent(ab.id, {ignore_energy=true, ignore_cd = true}) end @@ -2847,6 +2846,10 @@ function _M:on_set_temporary_effect(eff_id, e, p) if e.status == "detrimental" and self:knowTalent(self.T_RESILIENT_BONES) then p.dur = math.ceil(p.dur * (1 - (self:getTalentLevel(self.T_RESILIENT_BONES) / 12))) end + if e.status == "detrimental" and self:hasEffect(self.EFF_FADE_FROM_TIME) then + local fft = self:hasEffect(self.EFF_FADE_FROM_TIME) + p.dur = math.ceil(p.dur * (1 - (fft.power/100))) + end if e.status == "detrimental" and self:attr("negative_status_effect_immune") then p.dur = 0 end diff --git a/game/modules/tome/class/PlayerDisplay.lua b/game/modules/tome/class/PlayerDisplay.lua index 744722bb6115b8b8d28f261dabb8abeba9cf2de8..3a3f7dcc5db050f51a3eeb12e881a7a9ab01b620 100644 --- a/game/modules/tome/class/PlayerDisplay.lua +++ b/game/modules/tome/class/PlayerDisplay.lua @@ -234,7 +234,6 @@ function _M:display() if player:attr("time_shield") then shield = shield + player.time_shield_absorb max_shield = max_shield + player.time_shield_absorb_max end if player:attr("damage_shield") then shield = shield + player.damage_shield_absorb max_shield = max_shield + player.damage_shield_absorb_max end if player:attr("displacement_shield") then shield = shield + player.displacement_shield max_shield = max_shield + player.displacement_shield_max end - if player:attr("damage_shunt") then shield = shield + player.damage_shunt_absorb max_shield = max_shield + player.damage_shunt_absorb_max end if max_shield > 0 then self:mouseTooltip(self.TOOLTIP_DAMAGE_SHIELD, self:makeTextureBar("#WHITE#Shield:", nil, shield, max_shield, nil, x, h, 255, 255, 255, {r=colors.GREY.r / 3, g=colors.GREY.g / 3, b=colors.GREY.b / 3}, {r=colors.GREY.r / 6, g=colors.GREY.g / 6, b=colors.GREY.b / 6})) h = h + self.font_h end diff --git a/game/modules/tome/data/quests/paradoxology.lua b/game/modules/tome/data/quests/paradoxology.lua index f9880abe5208e3e8fa292ea0c24817a5e10f6ad3..092e7d9b9ec4c814666ba69bf86dd218690e2e78 100644 --- a/game/modules/tome/data/quests/paradoxology.lua +++ b/game/modules/tome/data/quests/paradoxology.lua @@ -71,8 +71,8 @@ generate = function(self, player, x, y) a:learnTalent(a.T_STATIC_HISTORY, true, 4) a:learnTalent(a.T_RETHREAD, true, 3) a:learnTalent(a.T_GATHER_THE_THREADS, true, 3) - a:learnTalent(a.T_DAMAGE_SHUNT, true, 3) - a:learnTalent(a.T_FLAWED_DESIGN, true, 4) + a:learnTalent(a.T_FADE_FROM_TIME, true, 3) + a:learnTalent(a.T_ASHES_TO_ASHES, true, 4) a:learnTalent(a.T_QUANTUM_SPIKE, true, 3) a:learnTalent(a.T_SEVER_LIFELINE, true, 5) diff --git a/game/modules/tome/data/talents/chronomancy/chronomancer.lua b/game/modules/tome/data/talents/chronomancy/chronomancer.lua index 2358bde0043015781541a8480e6e5da06f1d9519..1872e81d9728e2f18d8f68c960de3d7f587da3ab 100644 --- a/game/modules/tome/data/talents/chronomancy/chronomancer.lua +++ b/game/modules/tome/data/talents/chronomancy/chronomancer.lua @@ -113,11 +113,11 @@ end -- Make sure we don't run concurrent chronoworlds; to prevent lag and possible game breaking bugs or exploits checkTimeline = function(self) - if self:isTalentActive(self.T_DOOR_TO_THE_PAST) or self:hasEffect(self.EFF_SEE_THREADS) or self:hasEffect(self.EFF_PRECOGNITION) or self:hasEffect(self.EFF_PARADOX_CLONE) or self:hasEffect(self.EFF_IMMINENT_PARADOX_CLONE) then + if game._chronoworlds == nil then + return false + else game.logPlayer(self, "The timeline is too fractured right now to use this ability.") return true - else - return false end end diff --git a/game/modules/tome/data/talents/chronomancy/energy.lua b/game/modules/tome/data/talents/chronomancy/energy.lua index 6060c84f479af16a8a89e81b49c28df9f016b9d7..e17113e196b475ba6d434058678c44c72689b78d 100644 --- a/game/modules/tome/data/talents/chronomancy/energy.lua +++ b/game/modules/tome/data/talents/chronomancy/energy.lua @@ -30,8 +30,8 @@ newTalent{ on_damage = function(self, t, damtype, dam) if not DamageType:get(damtype).antimagic_resolve then return dam end local absorb = t.getAbsorption(self, t) - -- works like armor with 50% hardiness for projected energy effects - dam = math.max(dam * 0.5 - absorb, 0) + (dam * 0.5) + -- works like armor with 30% hardiness for projected energy effects + dam = math.max(dam * 0.3 - absorb, 0) + (dam * 0.7) print("[PROJECTOR] after static reduction dam", dam) return dam end, @@ -47,7 +47,7 @@ newTalent{ end, info = function(self, t) local absorption = t.getAbsorption(self, t) - return ([[Reduces all incoming energy damage by 50%% up to a maximum of %d. + return ([[Reduces all incoming energy damage by 30%% up to a maximum of %d. The maximum damage reduction will scale with your Spellpower.]]):format(absorption) end, } @@ -164,6 +164,7 @@ newTalent{ paradox = 20, cooldown = 12, tactical = { BUFF = 2 }, + no_energy = true, getMaxLevel = function(self, t) return self:getTalentLevel(t) end, action = function(self, t) -- effect is handled in actor postUse @@ -172,7 +173,9 @@ newTalent{ end, info = function(self, t) local maxlevel = t.getMaxLevel(self, t) - return ([[You'll recast the next activated chronomancy spell (up to talent level %0.1f) that you cast within the next 5 turns one turn later without spending an action.]]): + return ([[You may recast the next activated chronomancy spell (up to talent level %0.1f) that you cast within the next 5 turns on the turn following its initial casting. + The Paradox cost of the initial spell will be paid each time it is cast and the second casting will still consume a turn. + This spell takes no time to cast.]]): format(maxlevel) end, } diff --git a/game/modules/tome/data/talents/chronomancy/paradox.lua b/game/modules/tome/data/talents/chronomancy/paradox.lua index fac62f72b01f434164aa15b91dec36473965cae6..6ffe4eeac4a31d1c4769f1641c1a00ec7f7d4755 100644 --- a/game/modules/tome/data/talents/chronomancy/paradox.lua +++ b/game/modules/tome/data/talents/chronomancy/paradox.lua @@ -38,62 +38,101 @@ newTalent{ } newTalent{ - name = "Damage Shunt", + name = "Cease to Exist", type = {"chronomancy/paradox", 2}, require = chrono_req_high2, points = 5, - paradox = 10, - cooldown = 15, - tactical = { DEFEND = 2 }, - no_energy = true, - getAbsorb = function(self, t) return self:combatTalentSpellDamage(t, 30, 470) * getParadoxModifier(self, pm) end, + cooldown = 24, + paradox = 20, + range = 10, + tactical = { ATTACK = 2 }, + requires_target = true, + direct_hit = true, + no_npc_use = true, + getDuration = function(self, t) return 4 + math.floor(self:getTalentLevel(t) * getParadoxModifier(self, pm)) end, + getPower = function(self, t) return self:combatTalentSpellDamage(t, 10, 50) * getParadoxModifier(self, pm) end, + do_instakill = function(self, t) + -- search for target because it's ID will change when the chrono restore takes place + local tg = false + local grids = core.fov.circle_grids(self.x, self.y, 10, true) + for x, yy in pairs(grids) do for y, _ in pairs(grids[x]) do + local a = game.level.map(x, y, Map.ACTOR) + if a and a:hasEffect(a.EFF_CEASE_TO_EXIST) then + tg = a + end + end end + + if tg then + game:onTickEnd(function() + tg:removeEffect(tg.EFF_CEASE_TO_EXIST) + game.logSeen(tg, "#LIGHT_BLUE#%s never existed, this never happened!", tg.name:capitalize()) + tg:die(self) + end) + end + end, action = function(self, t) - self:setEffect(self.EFF_DAMAGE_SHUNT, 10, {power=t.getAbsorb(self, t)}) - game:playSoundNear(self, "talents/heal") + -- check for other chrono worlds + if checkTimeline(self) == true then + return + end + + -- get our target + local tg = {type="hit", range=self:getTalentRange(t)} + local tx, ty = self:getTarget(tg) + if not tx or not ty then return nil end + local _ _, tx, ty = self:canProject(tg, tx, ty) + + local target = game.level.map(tx, ty, Map.ACTOR) + if not target then return end + + -- does the spell hit? if not nothing happens + if not self:checkHit(self:combatSpellpower(), target:combatSpellResist()) then + game.logSeen(target, "%s resists!", target.name:capitalize()) + return true + end + + -- Manualy start cooldown and spend paradox before the chronoworld is made + game.player:startTalentCooldown(t) + game.player:incParadox(t.paradox * (1 + (game.player.paradox / 300))) + + -- set up chronoworld next, we'll load it when the target dies in class\actor + game:onTickEnd(function() + game:chronoClone("cease_to_exist") + end) + + target:setEffect(target.EFF_CEASE_TO_EXIST, t.getDuration(self,t), {power=t.getPower(self, t)}) + return true end, info = function(self, t) - local absorb = t.getAbsorb(self, t) - return ([[Divides up to %0.2f damage evenly along every second of your past and future, effectively negating it. Damage Shunt lasts 10 turns or until the maximum damage limit has been reached. - Casting Damage Shunt costs no time and the effect will scale with your Paradox and Spellpower.]]): - format(absorb) + local duration = t.getDuration(self, t) + local power = t.getPower(self, t) + return ([[Over the next %d turns you attempt to remove the target from the timeline. It's resistances will be reduced by %d%% and if you manage to kill it while the spell is in effect you'll be returned to the point in time you cast this spell and the target will be slain. + The duration will scale with your Paradox and the resistance penalty will scale with your paradox and spellpower.]]) + :format(duration, power) end, } newTalent{ - name = "Flawed Design", - type = {"chronomancy/paradox",3}, + name = "Fade From Time", + type = {"chronomancy/paradox", 3}, require = chrono_req_high3, points = 5, - cooldown = 20, - paradox = 20, - range = 10, - tactical = { DISABLE = 2 }, - requires_target = true, - direct_hit = true, - getDuration = function(self, t) return 2 + math.floor(self:getTalentLevel(t) * getParadoxModifier(self, pm)) end, - getReduction = function(self, t) return 10 + (self:combatTalentSpellDamage(t, 10, 40) * getParadoxModifier(self, pm)) end, + paradox = 10, + cooldown = 24, + tactical = { DEFEND = 2 }, + getResist = function(self, t) return self:combatTalentSpellDamage(t, 10, 70) * getParadoxModifier(self, pm) end, 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 - x, y = checkBackfire(self, x, y) - self:project(tg, x, y, function(tx, ty) - local target = game.level.map(tx, ty, Map.ACTOR) - if not target then return end - if target:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 15) then - target:setEffect(target.EFF_FLAWED_DESIGN, t.getDuration(self,t), {power=t.getReduction(self, t)}) - end - end) - game:playSoundNear(self, "talents/generic2") + self:setEffect(self.EFF_FADE_FROM_TIME, 10, {power=t.getResist(self, t)}) + game:playSoundNear(self, "talents/heal") return true end, info = function(self, t) - local duration = t.getDuration(self, t) - local reduction = t.getReduction(self, t) - return ([[By altering the target's past you change its present, reducing all of its resistances by %d%% for %d turns. - The duration and reduction will scale with your Paradox. The reduction will increase with your Spellpower.]]): - format(reduction, duration) + local resist = t.getResist(self, t) + return ([[You partially remove yourself from the timeline for 10 turns, increasing your resistance to all damage by %d%%, reducing the duration of all detrimental effects by %d%%, and reducing all damage you deal by 10%%. + The resistance bonus, detrimental effect reduction, and damage penalty will gradually lose power over the course of the spell. + The effect will scale with your Paradox and Spellpower.]]): + format(resist, resist, resist/10) end, } diff --git a/game/modules/tome/data/talents/chronomancy/timetravel.lua b/game/modules/tome/data/talents/chronomancy/timetravel.lua index 5bf36620a6865947103ee43a49ef339fdc44867b..7a4d50e52fc2fd5707a7803428baaa6cce5ea94f 100644 --- a/game/modules/tome/data/talents/chronomancy/timetravel.lua +++ b/game/modules/tome/data/talents/chronomancy/timetravel.lua @@ -189,7 +189,9 @@ newTalent{ game:playSoundNear(self, "talents/arcane") return { - game:chronoClone("revision"), + game:onTickEnd(function() + game:chronoClone("revision") + end), particle = self:addParticles(Particles.new("temporal_aura", 1)), } end, @@ -235,9 +237,10 @@ newTalent{ -- Manualy start the cooldown of the "old player" game.player:startTalentCooldown(t) game.player:incParadox(t.paradox * (1 + (game.player.paradox / 300))) - + game.player:forceUseTalent(game.player.T_DOOR_TO_THE_PAST, {ignore_energy=true}) -- remove anomaly count if self.dttp_anomaly_count then self.dttp_anomaly_count = nil end + if game._chronoworlds then game._chronoworlds = nil end end) return true diff --git a/game/modules/tome/data/timed_effects.lua b/game/modules/tome/data/timed_effects.lua index 3f9590525b994a5636e5b6d9a6e82710bee443ae..06539649d31aa92f0d7198528d7a81c0196dcc0e 100644 --- a/game/modules/tome/data/timed_effects.lua +++ b/game/modules/tome/data/timed_effects.lua @@ -3431,13 +3431,13 @@ newEffect{ end, deactivate = function(self, eff) game:onTickEnd(function() - if game._chronoworlds == nil then - game.logSeen(self, "#LIGHT_RED#The precognition spell fizzles and cancels, leaving you in this timeline.") + if not game:chronoRestore("precognition", true) then + game.logSeen(self, "#LIGHT_RED#The spell fizzles.") return end - game:chronoRestore("precognition", true) - game.logPlayer(game.player, "#LIGHT_BLUE#You unfold the space time continuum to a previous state!") + game.logPlayer(game.player, "#LIGHT_BLUE#You unfold the spacetime continuum to a previous state!") game.player.tmp[self.EFF_PRECOGNITION] = nil + if game._chronoworlds then game._chronoworlds = nil end end) end, } @@ -3496,30 +3496,6 @@ newEffect{ end, } -newEffect{ - name = "DAMAGE_SHUNT", - desc = "Damage Shunt", - long_desc = function(self, eff) return ("The target is splitting all damage it receives along the timeline, negating up to %d/%d damage."):format(self.damage_shunt_absorb, eff.power) end, - type = "magical", - status = "beneficial", - parameters = { power=100 }, - on_gain = function(self, err) return "#Target# is splitting damage along the timeline.", "+Damage Shunt" end, - on_lose = function(self, err) return "The spell protecting #target# has ended.", "-Damage Shunt" end, - activate = function(self, eff) - eff.tmpid = self:addTemporaryValue("damage_shunt", eff.power) - --- Warning there can be only one time shield active at once for an actor - self.damage_shunt_absorb = eff.power - self.damage_shunt_absorb_max = eff.power - eff.particle = self:addParticles(Particles.new("damage_shield", 1)) - end, - deactivate = function(self, eff) - self:removeParticles(eff.particle) - self:removeTemporaryValue("damage_shunt", eff.tmpid) - self.damage_shunt_absorb = nil - self.damage_shunt_absorb_max = nil - end, -} - newEffect{ name = "IMMINENT_PARADOX_CLONE", desc = "Imminent Paradox Clone", @@ -4239,3 +4215,57 @@ newEffect{ self:removeTemporaryValue("combat_spellspeed", eff.spdid) end, } + +newEffect{ + name = "CEASE_TO_EXIST", + desc = "Cease to Exist", + long_desc = function(self, eff) return ("The target is being removed from the timeline and is suffering %d temporal damage per turn."):format(eff.dam) end, + type = "magical", + status = "detrimental", + parameters = { power = 1 }, + on_gain = function(self, err) return "#Target# is being removed from the timeline.", "+Cease to Exist" end, + activate = function(self, eff) + eff.resists = self:addTemporaryValue("resists", { all = -eff.power}) + end, + deactivate = function(self, eff) + if game._chronoworlds then + game._chronoworlds = nil + end + self:removeTemporaryValue("resists", eff.resists) + end, +} + +newEffect{ + name = "FADE_FROM_TIME", + desc = "Fade From Time", + long_desc = function(self, eff) return ("The target is partially removed from the timeline, reducing all damage dealt by %d%%, all damage recieved by %d%%, and the duration of all detrimental effects by %d%%."): + format(eff.dur + 1, eff.cur_power or eff.power, eff.cur_power or eff.power) end, + type = "time", -- so no timeless shinanigans or removal by body reversion + status = "beneficial", + parameters = { power=10 }, + on_gain = function(self, err) return "#Target# has partially removed itself from the timeline.", "+Fade From Time" end, + on_lose = function(self, err) return "#Target# has returned fully to the timeline.", "-Fade From Time" end, + on_merge = function(self, old_eff, new_eff) + self:removeTemporaryValue("inc_damage", old_eff.dmgid) + self:removeTemporaryValue("resists", old_eff.rstid) + old_eff.cur_power = (new_eff.power) + old_eff.dmgid = self:addTemporaryValue("inc_damage", {all = - old_eff.dur}) + old_eff.rstid = self:addTemporaryValue("resists", {all = old_eff.cur_power}) + + old_eff.dur = old_eff.dur + return old_eff + end, + on_timeout = function(self, eff) + local current = eff.power * eff.dur/10 + self:setEffect(self.EFF_FADE_FROM_TIME, 1, {power = current}) + end, + activate = function(self, eff) + eff.cur_power = eff.power + eff.rstid = self:addTemporaryValue("resists", { all = eff.power}) + eff.dmgid = self:addTemporaryValue("inc_damage", {all= -10}) + end, + deactivate = function(self, eff) + self:removeTemporaryValue("resists", eff.rstid) + self:removeTemporaryValue("inc_damage", eff.dmgid) + end, +} \ No newline at end of file diff --git a/game/modules/tome/data/zones/temporal-rift/npcs.lua b/game/modules/tome/data/zones/temporal-rift/npcs.lua index e2825a372dec80529c3e28cbd81645cd9c2ed78e..0d61b59f1bd2127ca9cf941187e846984b4b173c 100644 --- a/game/modules/tome/data/zones/temporal-rift/npcs.lua +++ b/game/modules/tome/data/zones/temporal-rift/npcs.lua @@ -171,7 +171,7 @@ newEntity{ base="BASE_NPC_HORROR", define_as = "CHRONOLITH_TWIN", }, autolevel = "warriormage", - ai = "tactical", ai_state = { talent_in=2, ai_move="move_astar", }, + ai = "tactical", ai_state = { talent_in=1, ai_move="move_astar", }, ai_tactic = resolvers.tactic "ranged", resolvers.inscriptions(1, {"shielding rune"}), @@ -220,7 +220,7 @@ newEntity{ base="BASE_NPC_HORROR", define_as = "CHRONOLITH_CLONE", }, autolevel = "warriormage", - ai = "tactical", ai_state = { talent_in=2, ai_move="move_astar", }, + ai = "tactical", ai_state = { talent_in=1, ai_move="move_astar", }, ai_tactic = resolvers.tactic "ranged", resolvers.inscriptions(1, {"invisibility rune"}),