diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 1c0b80f78fba576ece1fedb6e6f4537e5712f3b2..010fc096adf9417137ca8fa6ceaa0a927438d607 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -1648,13 +1648,21 @@ function _M:preUseTalent(ab, silent, fake) -- Paradox is special, it has no max, but the higher it is the higher the chance of something bad happening if (ab.paradox or ab.sustain_paradox) and not fake then local pa = ab.paradox or ab.sustain_paradox - local modifier = self:getWil() * (1 + (self:getTalentLevel(self.T_PARADOX_MASTERY)/10) or 0 ) + + --check for Paradox Mastery + if self:knowTalent(self.T_PARADOX_MASTERY) and self:isTalentActive(self.T_PARADOX_MASTERY) then + modifier = self:getWil() * (1 + (self:getTalentLevel(self.T_PARADOX_MASTERY)/10) or 0 ) + else + modifier = self:getWil() + end + --print("[Paradox] Will modifier: ", modifier, "::", self:getParadox()) local chance = math.pow (((self:getParadox() - modifier)/200), 2)*((100 + self:combatFatigue()) / 100) -- Fail ? lose energy and 1/2 more paradox --print("[Paradox] Fail chance: ", chance, "::", self:getParadox()) if rng.percent(math.pow((self:getParadox()/400), 4)) and not game.zone.no_anomalies and not self:attr("no_paradox_fail") then -- Random anomaly + self:incParadox(pa / 2) local ts = {} for id, t in pairs(self.talents_def) do if t.type[1] == "chronomancy/anomalies" then ts[#ts+1] = id end @@ -1665,7 +1673,7 @@ function _M:preUseTalent(ab, silent, fake) return false elseif rng.percent(chance) and not self:attr("no_paradox_fail") then if not silent then game.logPlayer(self, "You fail to use %s due to your paradox!", ab.name) end - self:incParadox(pa / 2) + self:incParadox(pa / 10) self:useEnergy() return false end diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua index 9e6b5c1f295e4a86c52b0485fbe85fafdead2ee0..c0894f66968b9073e7cb7c65a0cd38885ce28493 100644 --- a/game/modules/tome/class/Player.lua +++ b/game/modules/tome/class/Player.lua @@ -125,13 +125,7 @@ function _M:onEnterLevel(zone, level) end function _M:onEnterLevelEnd(zone, level) - -- Clone level when they are made, for chronomancy - if self:attr("game_cloning") then - -- First remove the old world to make sure we only have one on_level restore in memory at a time - if game._chronoworlds.on_level then game._chronoworlds.on_level = nil end - -- then clone the new level - game:chronoClone("on_level") - end + end function _M:onLeaveLevel(zone, level) diff --git a/game/modules/tome/class/interface/Archery.lua b/game/modules/tome/class/interface/Archery.lua index 7bb1f5682d82dea5a4a67f0ec75108e24da3e495..b405e429c0b4813b448db3a09caf96dffe7da10c 100644 --- a/game/modules/tome/class/interface/Archery.lua +++ b/game/modules/tome/class/interface/Archery.lua @@ -173,6 +173,17 @@ local function archery_projectile(tx, ty, tg, self) -- Regen on being hit if hitted and not target.dead and target:attr("stamina_regen_on_hit") then target:incStamina(target.stamina_regen_on_hit) end if hitted and not target.dead and target:attr("mana_regen_on_hit") then target:incMana(target.mana_regen_on_hit) end + + -- Ablative armor + if hitted and not target.dead and target:attr("carbon_spikes") then + if target.carbon_armor >= 1 then + target.carbon_armor = target.carbon_armor - 1 + else + -- Deactivate without loosing energy + target:forceUseTalent(target.T_CARBON_SPIKES, {ignore_energy=true}) + end + end + end --- Shoot at one target diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua index d338098a2a5f8e7446c1971f05481493b35efb30..c632d30d83aa7292a7921fb5a3f9717dc0995d34 100644 --- a/game/modules/tome/class/interface/Combat.lua +++ b/game/modules/tome/class/interface/Combat.lua @@ -375,7 +375,17 @@ function _M:attackTargetWith(target, weapon, damtype, mult) if hitted and not target.dead and target:attr("stamina_regen_on_hit") then target:incStamina(target.stamina_regen_on_hit) end if hitted and not target.dead and target:attr("mana_regen_on_hit") then target:incMana(target.mana_regen_on_hit) end if hitted and not target.dead and target:attr("equilibrium_regen_on_hit") then target:incEquilibrium(-target.equilibrium_regen_on_hit) end - + + -- Ablative Armor + if hitted and not target.dead and target:attr("carbon_spikes") then + if target.carbon_armor >= 1 then + target.carbon_armor = target.carbon_armor - 1 + else + -- Deactivate without loosing energy + target:forceUseTalent(target.T_CARBON_SPIKES, {ignore_energy=true}) + end + end + -- Riposte! if not hitted and not target.dead and not evaded and not target:attr("stunned") and not target:attr("dazed") and not target:attr("stoned") and target:knowTalent(target.T_RIPOSTE) and rng.percent(target:getTalentLevel(target.T_RIPOSTE) * (5 + target:getDex(5))) then game.logSeen(self, "%s ripostes!", target.name:capitalize()) @@ -421,9 +431,6 @@ function _M:combatDefense() if self:hasDualWeapon() and self:knowTalent(self.T_DUAL_WEAPON_DEFENSE) then add = add + 4 + (self:getTalentLevel(self.T_DUAL_WEAPON_DEFENSE) * self:getDex()) / 12 end - if self:knowTalent(self.T_PROBABILITY_WEAVING) then - add = add + (self:getTalentLevel(self.T_PROBABILITY_WEAVING) * 2) - end return self.combat_def + (self:getDex() - 10) * 0.35 + add + (self:getLck() - 50) * 0.4 end @@ -441,6 +448,9 @@ function _M:combatArmor() if self:hasMassiveArmor() and self:knowTalent(self.T_MASSIVE_ARMOUR_TRAINING) then add = add + self:getTalentLevel(self.T_MASSIVE_ARMOUR_TRAINING) * 1.6 end + if self:knowTalent(self.T_CARBON_SPIKES) and self:isTalentActive(self.T_CARBON_SPIKES) then + add = add + self.carbon_armor + end return self.combat_armor + add end @@ -618,7 +628,7 @@ function _M:physicalCrit(dam, weapon, target) if target:attr("combat_critical") then chance = chance + target:attr("combat_critical") end - if target:knowTalent(target.T_PROBABILITY_WEAVING) then + if target:knowTalent(target.T_PROBABILITY_WEAVING) and target:isTalentActive(T_PROBABILIT_WEAVING) then chance = chance - target:getTalentLevel(target.T_PROBABILITY_WEAVING) end if target:hasHeavyArmor() and target:knowTalent(target.T_HEAVY_ARMOUR_TRAINING) then diff --git a/game/modules/tome/data/birth/classes/chronomancer.lua b/game/modules/tome/data/birth/classes/chronomancer.lua index 961938d6ec5dde04ed809f6e1f33477e1dbfb629..458a3ac2ee9a3c9dd6d2b2414f29b597a64b2e05 100644 --- a/game/modules/tome/data/birth/classes/chronomancer.lua +++ b/game/modules/tome/data/birth/classes/chronomancer.lua @@ -51,7 +51,7 @@ newBirthDescriptor{ stats = { mag=5, wil=3, cun=1, }, talents_types = { ["chronomancy/age-manipulation"]={true, 0.3}, - --["chronomancy/anomalies"]={true, 0}, + -- ["chronomancy/anomalies"]={true, 0}, ["chronomancy/chronomancy"]={true, 0.3}, ["chronomancy/energy"]={true, 0.3}, ["chronomancy/gravity"]={true, 0.3}, @@ -68,7 +68,7 @@ newBirthDescriptor{ [ActorTalents.T_STATIC_HISTORY] = 1, [ActorTalents.T_TURN_BACK_THE_CLOCK] = 1, [ActorTalents.T_DUST_TO_DUST] = 1, - [ActorTalents.T_CELERITY] = 1, + [ActorTalents.T_ECHOES_FROM_THE_PAST] = 1, }, copy = { max_life = 90, diff --git a/game/modules/tome/data/gfx/particles/ball_temporal.lua b/game/modules/tome/data/gfx/particles/ball_temporal.lua index c301fa23343c63f61f8ae0f9b4bf59a73e94e43c..18b4c89eade083c8a8ea686d1b27df25bdf2075c 100644 --- a/game/modules/tome/data/gfx/particles/ball_temporal.lua +++ b/game/modules/tome/data/gfx/particles/ball_temporal.lua @@ -40,9 +40,9 @@ return { generator = function() dir = a, dirv = 0, dira = 0, vel = sradius / 2 / 10, velv = 0, vela = 0, - r = rng.range(70, 176)/255, rv = 0, ra = 0, - g = rng.range(130, 196)/255, gv = 0.005, ga = 0.0005, - b = rng.range(180, 222)/255, bv = 0, ba = 0, + r = rng.range(176, 250)/255, rv = 0, ra = 0, + g = rng.range(196, 250)/255, gv = 0.005, ga = 0.0005, + b = rng.range(222, 0)/255, bv = 0, ba = 0, a = rng.range(25, 220)/255, av = static and -0.034 or 0, aa = 0.005, } end, }, diff --git a/game/modules/tome/data/lore/age-allure.lua b/game/modules/tome/data/lore/age-allure.lua index 333eb0208d22ecbfde112d23e60a7f5a27291ffc..e38b646b7c59461b15ac88742516faf6b8974ad3 100644 --- a/game/modules/tome/data/lore/age-allure.lua +++ b/game/modules/tome/data/lore/age-allure.lua @@ -36,13 +36,13 @@ Current theory: Possibly a portal of some kind? We have only rumours and myths o #{bold}#Hompalan's Log Entry 2#{normal}# #{italic}#Age of Allure Year 4541#{normal}# -Testing of live animals has not gone well. Thankfully I can leave the clean-up to my technicians... Four years and it feels like we have not even begun to understand these ruins. I'm worried our research grant will soon be cut. But I have hope that some of the items we've recently uncovered may provide some sort of key to the portals. Or perhaps they were just used for Sher'Tul sports? We shall see. +Testing of live yeeks has not gone well. Thankfully I can leave the clean-up to my technicians... Four years and it feels like we have not even begun to understand these ruins. I'm worried our research grant will soon be cut. But I have hope that some of the items we've recently uncovered may provide some sort of key to the portals. Or perhaps they were just used for Sher'Tul sports? We shall see. #{bold}#Hompalan's Log Entry 3#{normal}# #{italic}#Age of Allure 4542#{normal}# -Third test confirms it - the orb allows transition between portal gates. This is the greatest discovery of my life. We are the first research team to ever unlock such powers from a Sher'Tul ruin! Just a shame about all the animals we sent through before. I'm really starting to suspect that pronged instrument was some sort of eating device... Oh well, plenty more test animals available. +Third test confirms it - the orb allows transition between portal gates. This is the greatest discovery of my life. We are the first research team to ever unlock such powers from a Sher'Tul ruin! Just a shame about all the yeeks we sent through before. I'm really starting to suspect that pronged instrument was some sort of eating device... Oh well, plenty more dumb yeeks available. ]] } @@ -61,6 +61,7 @@ Just when I was getting somewhere the military decide to barge in and take over. #{italic}#Age of Allure 4545#{normal}# Well, I suppose this whole war thing wasn't just hot wind after all. Apparently there's lots of people dying outside. What a nuisance - I just want to get on with my work without my supplies being cut off. There is one upside though - human test subjects! We're going to get started on them in the coming months. +I must say I'll be glad to get rid of these disgusting yeeks. They disturb me somehow with their oversized heads. Why we ever decided to use these useless creatures as servants is beyond me... At least the human subjects will be able to talk, lacking though they be in true mental capacity. #{bold}#Hompalan's Log Entry 6#{normal}# diff --git a/game/modules/tome/data/lore/last-hope.lua b/game/modules/tome/data/lore/last-hope.lua index 3f089392da8a7b213124db05e8e4a2d61cb97a3f..4df870b198d3af017ccb1b71d88f6d6724d1e948 100644 --- a/game/modules/tome/data/lore/last-hope.lua +++ b/game/modules/tome/data/lore/last-hope.lua @@ -75,7 +75,7 @@ newLore{ lore = [[#{italic}#2nd of Allure, Year of Pyre 711#{normal}# We have solidified the defences at Last Hope and received more troops from all of the kingdoms. Our tactical position near the sea is proving an excellent base for further attacks on the orcs. I hope to expand our facilities here much further. I am drawing up an alliance now with the other leaders, for only together can we hope to beat this terrible threat to all our kingdoms. -Mirvenia has been key to the discussions and the plans. She is a tactical genius, and I am constantly amazed by her wealth of knowledge and skills. There is an odd woman that accompanies her though, with long silver hair and a shadowy robe. One of my men swears he saw her riding into battle in a blaze of fire, burning all the orcs before her. The idea of a spellweaver in our midst is unsettling, but Mirvenia says I should trust her. And if Mirvenia says so then I must believe her. +Mirvenia has been key to the discussions and the plans. She is a tactical genius, and I am constantly amazed by her wealth of knowledge and skills. There is an odd woman that accompanies her though, with long red hair and a shadowy robe - Linaniil is her name. . One of my men swears he saw her riding into battle in a blaze of fire, burning all the orcs before her. The idea of a spellweaver in our midst is unsettling, but Mirvenia says I should trust her. And if Mirvenia says so then I must believe her. She is a remarkable force for good, Mirvenia, popular with all the troops. I feel my normal iron rule smoothed by her presence. Too long have I led the cold life of a soldier... alone... diff --git a/game/modules/tome/data/talents/chronomancy/age-manipulation.lua b/game/modules/tome/data/talents/chronomancy/age-manipulation.lua index 20591080e7f31a4263bc4a6a93c989fd850021b7..ee6b4327b6994d419bd60c468a3b7bf2ea781e77 100644 --- a/game/modules/tome/data/talents/chronomancy/age-manipulation.lua +++ b/game/modules/tome/data/talents/chronomancy/age-manipulation.lua @@ -137,11 +137,12 @@ newTalent{ tactical = { ATTACKAREA = 2 }, getDamage = function(self, t) return self:combatTalentSpellDamage(t, 8, 135)*getParadoxModifier(self, pm) end, getDuration = function(self, t) return 5 + math.ceil(self:getTalentLevel(t)) end, + getRadius = function(self, t) return 3 + math.floor(self:getTalentLevel(t)/4)end, action = function(self, t) game.level.map:addEffect(self, self.x, self.y, t.getDuration(self, t), DamageType.WASTING, t.getDamage(self, t), - 3, + t.getRadius(self, t), 5, nil, engine.Entity.new{alpha=100, display='', color_br=176, color_bg=196, color_bb=222}, function(e) @@ -157,7 +158,8 @@ newTalent{ info = function(self, t) local damage = t.getDamage(self, t) local duration = t.getDuration(self, t) - return ([[You surround yourself with a radius 3 aura of time distortion for %d turns that deals %0.2f stacking temporal damage over 3 turns to all other creatures. - The damage will scale with your Paradox and Magic stat]]):format(duration, damDesc(self, DamageType.TEMPORAL, damage)) + local radius = t.getRadius(self, t) + return ([[You surround yourself with a radius %d aura of time distortion for %d turns that deals %0.2f stacking temporal damage over 3 turns to all other creatures. + The damage will scale with your Paradox and Magic stat]]):format(radius, duration, damDesc(self, DamageType.TEMPORAL, damage)) end, } \ No newline at end of file diff --git a/game/modules/tome/data/talents/chronomancy/anomalies.lua b/game/modules/tome/data/talents/chronomancy/anomalies.lua index df2ac08e506482a4fcba95a6fdb2e435a493b603..5273a249f4e99b15195a82c23892cec2c204d595 100644 --- a/game/modules/tome/data/talents/chronomancy/anomalies.lua +++ b/game/modules/tome/data/talents/chronomancy/anomalies.lua @@ -17,23 +17,21 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org --- Ideas, sex change, gold loss/increase, draining/charging charged items. Stat rearrangement. Vertigo (reassign hotkeys so up is down left is right) - local Object = require "engine.Object" newTalent{ name = "Anomaly Teleport", type = {"chronomancy/anomalies", 1}, points = 1, - range = 6, + range = 10, direct_hit = true, type_no_req = true, getTargetCount = function(self, t) return math.floor(self:getParadox()/200) end, - getRange = function(self, t) return (self:getParadox()/10) end, + getRange = function(self, t) return math.ceil(self:getParadox()/10) end, message = "Reality has shifted.", action = function(self, t) local tgts = {} - local grids = core.fov.circle_grids(self.x, self.y, 6, true) + 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:canBe("teleport") then @@ -65,15 +63,15 @@ newTalent{ name = "Anomaly Rearrange", type = {"chronomancy/anomalies", 1}, points = 1, - range = 6, + range = 10, direct_hit = true, type_no_req = true, getTargetCount = function(self, t) return math.floor(self:getParadox()/50) end, - getRange = function(self, t) return (self:getParadox()/100) end, + getRange = function(self, t) return math.ceil(self:getParadox()/100) end, message = "@Source@ has caused a hiccup in the fabric of spacetime.", action = function(self, t) local tgts = {} - local grids = core.fov.circle_grids(self.x, self.y, 6, true) + 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:canBe("teleport") then @@ -105,16 +103,16 @@ newTalent{ name = "Anomaly Stop", type = {"chronomancy/anomalies", 1}, points = 1, - range = 6, + range = 10, direct_hit = true, type_no_req = true, getTargetCount = function(self, t) return 1 end, getRadius = function(self, t) return math.floor(self:getParadox()/200) end, - getStop = function(self, t) return (self:getParadox()/100) end, + getStop = function(self, t) return math.ceil(self:getParadox()/100) end, message = "@Source@ has created a bubble of nul time.", action = function(self, t) local tgts = {} - local grids = core.fov.circle_grids(self.x, self.y, 6, true) + 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 then @@ -153,7 +151,7 @@ newTalent{ message = "@Source@ has created a bubble of slow time.", action = function(self, t) local tgts = {} - local grids = core.fov.circle_grids(self.x, self.y, 6, true) + 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 then @@ -191,7 +189,7 @@ newTalent{ message = "@Source@ has sped up several threads of time.", action = function(self, t) local tgts = {} - local grids = core.fov.circle_grids(self.x, self.y, 6, true) + 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 then @@ -253,7 +251,7 @@ newTalent{ points = 1, type_no_req = true, getDamage = function(self, t) return self:getParadox()/25 end, - getDuration = function(self, t) return self:getParadox()/50 end, + getDuration = function(self, t) return math.ceil (self:getParadox()/50) end, message = "A temporal storm rages around @Source@.", action = function(self, t) -- Add a lasting map effect @@ -279,7 +277,7 @@ newTalent{ name = "Anomaly Summon Time Elemental", type = {"chronomancy/anomalies", 1}, points = 1, - range = 6, + range = 10, direct_hit = true, type_no_req = true, getTargetCount = function(self, t) return math.floor(self:getParadox()/200) end, @@ -287,10 +285,10 @@ newTalent{ message = "Some Time Elementals have been attracted by @Source@'s meddling.", action = function(self, t) local tgts = {} - local grids = core.fov.circle_grids(self.x, self.y, 6, true) + 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:canBe("teleport") then + if a then tgts[#tgts+1] = a end end end @@ -367,17 +365,17 @@ newTalent{ name = "Anomaly Temporal Bubble", type = {"chronomancy/anomalies", 1}, points = 1, - range = 6, + range = 10, type_no_req = true, getTargetCount = function(self, t) return math.floor(self:getParadox()/200) end, - getDuration = function(self, t) return (self:getParadox()/100) end, + getDuration = function(self, t) return math.ceil(self:getParadox()/100) end, message = "@Source@ has paused a temporal thread.", action = function(self, t) local tgts = {} - local grids = core.fov.circle_grids(self.x, self.y, 6, true) + 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:canBe("teleport") then + if a then tgts[#tgts+1] = a end end end @@ -401,81 +399,456 @@ newTalent{ end, } ---[[newTalent{ - name = "Anomaly Terrain Change", +newTalent{ + name = "Anomaly Dig", type = {"chronomancy/anomalies", 1}, points = 1, type_no_req = true, + getRadius = function(self, t) return math.ceil(self:getParadox()/100) end, + message = "Matter turns to dust around @Source@.", action = function(self, t) + local tg = {type="ball", range=0, radius=t.getRadius(self,t), friendlyfire=false, talent=t} + self:project(tg, self.x, self.y, DamageType.DIG, 1) + game.level.map:particleEmitter(self.x, self.y, tg.radius, "ball_earth", {radius=tg.radius}) + game:playSoundNear(self, "talents/breath") return true end, info = function(self, t) - return (Random Terrain in a ball.) + return ([[Digs out all terrain in a ball centered on the caster..]]) end, } newTalent{ - name = "Anomaly Dig", + name = "Anomaly Swap", type = {"chronomancy/anomalies", 1}, points = 1, + direct_hit = true, type_no_req = true, + getRange = function(self, t) return math.ceil(self:getParadox()/10) end, + message = "Reality has shifted.", action = function(self, t) + local tgts = {} + local grids = core.fov.circle_grids(self.x, self.y, t.getRange(self, t)) + 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:canBe("teleport") and a ~= self then + tgts[#tgts+1] = a + end + end end + + -- Randomly take targets + local tg = {type="hit", range=self:getTalentRange(t), talent=t} + for i = 1, 1 do + if #tgts <= 0 then break end + local a, id = rng.table(tgts) + table.remove(tgts, id) + + local px, py = self.x, self.y + local gx, gy = a.x, a.y + + self:move(gx, gy, true) + a:move(px, py, true) + self:move(gx, gy, true) + a:move(px, py, true) + game.level.map:particleEmitter(px, py, 1, "teleport") + game.level.map:particleEmitter(gx, gy, 1, "teleport") + + end return true end, info = function(self, t) - return (Random Terrain in a ball.) + return ([[Caster swaps places with a random target.]]) end, } newTalent{ - name = "Anomaly Stat Reorder", + name = "Anomaly Gravity Spike", type = {"chronomancy/anomalies", 1}, points = 1, + range = 6, + direct_hit = true, type_no_req = true, + getTargetCount = function(self, t) return 1 end, + getRadius = function(self, t) return math.floor(self:getParadox()/100) end, + getDamage = function(self, t) return self:getParadox()/10 end, + message = "@Source@ has caused a Gravity Spike.", action = function(self, t) + local tgts = {} + local grids = core.fov.circle_grids(self.x, self.y, 6, 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 then + tgts[#tgts+1] = a + end + end end + + -- Randomly take targets + local tg = {type="ball", range=self:getTalentRange(t), radius=t.getRadius(self, t), friendlyfire=self:spellFriendlyFire(), talent=t} + for i = 1, t.getTargetCount(self, t) do + if #tgts <= 0 then break end + local a, id = rng.table(tgts) + table.remove(tgts, id) + + self:project(tg, a.x, a.y, function(px, py) + local target = game.level.map(px, py, Map.ACTOR) + if not target then return end + local tx, ty = util.findFreeGrid(a.x, a.y, 5, true, {[Map.ACTOR]=true}) + if tx and ty and target:canBe("knockback") then + target:move(tx, ty, true) + game.logSeen(target, "%s is drawn in by the gravity spike!", target.name:capitalize()) + end + end) + + self:project (tg, a.x, a.y, DamageType.PHYSICAL, self:spellCrit(t.getDamage(self, t))) + game.level.map:particleEmitter(a.x, a.y, tg.radius, "gravity_spike", {radius=tg.radius, grids=grids, tx=a.x, ty=a.y}) + game:playSoundNear(self, "talents/earth") + + end return true end, info = function(self, t) - return (Target loses stats.) + return ([[Creates a Gravity Spike.]]) end, } newTalent{ - name = "Anomaly Heal", + name = "Anomaly Entropy", type = {"chronomancy/anomalies", 1}, points = 1, + range = 6, + direct_hit = true, type_no_req = true, + getTargetCount = function(self, t) return math.floor(self:getParadox()/100) end, + getPower = function(self, t) return math.ceil(self:getParadox()/50) end, + getTalentCount = function(self, t) return math.floor(self:getParadox()/200) end, + message = "@Source@ has increased local entropy.", action = function(self, t) + local tgts = {} + 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 then + tgts[#tgts+1] = a + end + end end + + -- Randomly take targets + local tg = {type="hit", range=self:getTalentRange(t), talent=t} + for i = 1, t.getTargetCount(self, t) do + if #tgts <= 0 then break end + local a, id = rng.table(tgts) + table.remove(tgts, id) + + self:project(tg, a.x, a.y, function(px, py) + local target = game.level.map(px, py, engine.Map.ACTOR) + if not target then return end + + local tids = {} + for tid, lev in pairs(target.talents) do + local t = target:getTalentFromId(tid) + if not target.talents_cd[tid] and t.mode == "activated" then tids[#tids+1] = t end + end + for i = 1, t.getTalentCount(self, t) do + local power = t.getPower(self, t) + local t = rng.tableRemove(tids) + if not t then break end + target.talents_cd[t.id] = rng.range(2, power) + game.logSeen(target, "%s's %s is disrupted!", target.name:capitalize(), t.name) + end + target.changed = true + end, nil) + end return true end, info = function(self, t) - return (Target is healed to full life.) + return ([[Places target's talents on cooldown.]]) end, } newTalent{ - name = "Anomaly Double", + name = "Anomaly Summon Townsfolk", type = {"chronomancy/anomalies", 1}, points = 1, + range = 10, + direct_hit = true, type_no_req = true, + getTargetCount = function(self, t) return math.floor(self:getParadox()/200) end, + getSummonTime = function(self, t) return math.floor(self:getParadox()/20) end, + message = "Some innocent bystanders have been pulled out of their timeline.", action = function(self, t) + local tgts = {} + 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 then + tgts[#tgts+1] = a + end + end end + + -- Randomly take targets + local tg = {type="hit", range=self:getTalentRange(t), talent=t} + for i = 1, t.getTargetCount(self, t) do + if #tgts <= 0 then break end + local a, id = rng.table(tgts) + table.remove(tgts, id) + -- Randomly pick a race + local race = rng.range(1, 4) + + -- Find space + for i = 1, 4 do + local x, y = util.findFreeGrid(a.x, a.y, 5, true, {[Map.ACTOR]=true}) + if not x then + game.logPlayer(self, "Not enough space to summon!") + return + end + + 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, + + open_door = true, + + autolevel = "warrior", + energy = { mod=1 }, + 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 = t.getSummonTime(self, t), + } + + m.level = 1 + + if race == 1 then + m.name = "human farmer" + m.subtype = "human" + m.desc = [[A weather-worn human farmer, looking at a loss as to what's going on.]] + m.faction = "allied-kingdoms" + m.resolvers.inscriptions(1, "infusion") + elseif race == 2 then + m.name = "halfling gardner" + m.subtype = "halfling" + m.desc = [[A rugged halfling gardner, looking quite confused as to what he's doing here.]] + m.faction = "allied-kingdoms" + m.resolvers.inscriptions(1, "infusion") + elseif race == 3 then + m.name = "shalore scribe" + m.subtype = "elf" + m.desc = [[A scrawny elven scribe, looking bewildered at his surroundings.]] + m.faction = "shalore" + m.resolvers.inscriptions(1, "rune") + elseif race == 4 then + m.name = "dwarven smith" + m.subtype = "dwarf" + m.desc = [[A brawny dwarven smith, looking a bit upset at his current situation.]] + m.faction = "iron-throne" + m.resolvers.inscriptions(1, "rune") + end + + 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") + end + + game:playSoundNear(self, "talents/spell_generic") + end return true end, info = function(self, t) - return (Clones a random non-elite creature. Clone may or may not be hostile to the caster.) + return ([[Pulls innocent people into the fight.]]) end, } newTalent{ - name = "Anomaly Swap", + name = "Anomaly Call", type = {"chronomancy/anomalies", 1}, points = 1, + direct_hit = true, type_no_req = true, + getRange = function(self, t) return math.ceil(self:getParadox()/10) end, + getTargetCount = function(self, t) return math.floor(self:getParadox()/200) end, + message = "Poof!!", action = function(self, t) + local tgts = {} + local grids = core.fov.circle_grids(self.x, self.y, t.getRange(self, t)) + 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:canBe("teleport") and a ~= self then + tgts[#tgts+1] = a + end + end end + + -- Randomly take targets + local tg = {type="hit", range=self:getTalentRange(t), talent=t} + for i = 1, t.getTargetCount(self, t) do + if #tgts <= 0 then break end + local a, id = rng.table(tgts) + table.remove(tgts, id) + + local x, y = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true}) + if not x then + game.logPlayer(self, "Not enough space to summon!") + return + end + + a:move(x, y, true) + game.level.map:particleEmitter(x, y, 1, "teleport") + + end + return true + end, + info = function(self, t) + return ([[Poofs in some enemies from the level to the caster's location.]]) + end, +} + +newTalent{ + name = "Anomaly Flawed Design", + type = {"chronomancy/anomalies", 1}, + points = 1, + range = 6, + direct_hit = true, + type_no_req = true, + getTargetCount = function(self, t) return math.floor(self:getParadox()/150) end, + getPower = function(self, t) return (self:getParadox()/30) end, + message = "@Source@ has inadvertently weakened several creatures.", + action = function(self, t) + local tgts = {} + 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 then + tgts[#tgts+1] = a + end + end end + + -- Randomly take targets + local tg = {type="hit", range=self:getTalentRange(t), talent=t} + for i = 1, t.getTargetCount(self, t) do + if #tgts <= 0 then break end + local a, id = rng.table(tgts) + table.remove(tgts, id) + + a:setEffect(self.EFF_FLAWED_DESIGN, 10, {power=t.getPower(self, t)}) + a:setEffect(self.EFF_TURN_BACK_THE_CLOCK, 10, {power=t.getPower(self, t)}) + game.level.map:particleEmitter(a.x, a.y, 1, "temporal_teleport") + game:playSoundNear(self, "talents/spell_generic") + end return true end, info = function(self, t) - return (Caster swaps places with a random target.) + return ([[Weakens several creatures.]]) + end, +} + +newTalent{ + name = "Anomaly Dues Ex", + type = {"chronomancy/anomalies", 1}, + points = 1, + range = 6, + direct_hit = true, + type_no_req = true, + getTargetCount = function(self, t) return 1 end, + getHastePower = function(self, t) return ((self:getParadox()/15) / 100) end, + getRegenPower = function(self, t) return (self:getParadox()/15) end, + getStatPower = function(self, t) return (self:getParadox()/30) end, + message = "The odds have tilted.", + action = function(self, t) + local tgts = {} + 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 then + tgts[#tgts+1] = a + end + end end + + -- Randomly take targets + local tg = {type="hit", range=self:getTalentRange(t), talent=t} + for i = 1, t.getTargetCount(self, t) do + if #tgts <= 0 then break end + local a, id = rng.table(tgts) + table.remove(tgts, id) + + a:setEffect(self.EFF_SPEED, 8, {power=t.getHastePower(self, t)}) + a:setEffect(self.EFF_REGENERATION, 8, {power=t.getRegenPower(self, t)}) + a:setEffect(self.EFF_ALL_STAT, 8, {power=t.getStatPower(self, t)}) + a:setEffect(self.EFF_PAIN_SUPPRESSION, 8, {power=t.getStatPower(self, t)}) + game.level.map:particleEmitter(a.x, a.y, 1, "temporal_teleport") + game:playSoundNear(self, "talents/spell_generic") + end + return true + end, + info = function(self, t) + return ([[Seriously buffs one target.]]) + end, +} + +--[[newTalent{ + name = "Anomaly Terrain Change", + type = {"chronomancy/anomalies", 1}, + points = 1, + type_no_req = true, + action = function(self, t) + return true + end, + info = function(self, t) + return (Random Terrain in a ball.) + end, +} + +newTalent{ + name = "Anomaly Stat Reorder", + type = {"chronomancy/anomalies", 1}, + points = 1, + type_no_req = true, + action = function(self, t) + return true + end, + info = function(self, t) + return (Target loses stats.) + end, +} + +newTalent{ + name = "Anomaly Heal", + type = {"chronomancy/anomalies", 1}, + points = 1, + type_no_req = true, + action = function(self, t) + return true + end, + info = function(self, t) + return (Target is healed to full life.) + end, +} + +newTalent{ + name = "Anomaly Double", + type = {"chronomancy/anomalies", 1}, + points = 1, + type_no_req = true, + action = function(self, t) + return true + end, + info = function(self, t) + return (Clones a random non-elite creature. Clone may or may not be hostile to the caster.) end, } diff --git a/game/modules/tome/data/talents/chronomancy/chronomancer.lua b/game/modules/tome/data/talents/chronomancy/chronomancer.lua index f565392822fbe038ccbfe0d5da3e9be941e16ce1..3c07ebbddd20d2884aa37b88954f85760cbfe49c 100644 --- a/game/modules/tome/data/talents/chronomancy/chronomancer.lua +++ b/game/modules/tome/data/talents/chronomancy/chronomancer.lua @@ -104,8 +104,15 @@ temporal_req5 = { -- Backfire Function checkBackfire = function(self, x, y) - local modifier = self:getWil() * (1 + (self:getTalentLevel(self.T_PARADOX_MASTERY)/10) or 0 ) - local backfire = math.pow (((self:getParadox() - modifier)/300), 3) + + --check for Paradox Mastery + if self:knowTalent(self.T_PARADOX_MASTERY) and self:isTalentActive(self.T_PARADOX_MASTERY) then + modifier = self:getWil() * (1 + (self:getTalentLevel(self.T_PARADOX_MASTERY)/10) or 0 ) + else + modifier = self:getWil() + end + + local backfire = math.pow (((self:getParadox() - modifier)/300), 3)*((100 + self:combatFatigue()) / 100) -- print("[Paradox] Backfire chance: ", backfire, "::", self:getParadox()) if rng.percent(backfire) and not self:attr("no_paradox_fail") then game.logPlayer(self, "The fabric of spacetime ripples and your spell backfires!!") @@ -116,7 +123,7 @@ checkBackfire = function(self, x, y) end checkTimeline = function(self) - if self:hasEffect(self.EFF_SEE_THE_THREADS) or self:hasEffect(self.EFF_PRECOGNITION) or self:hasEffect(self.EFF_PARADOX_CLONE) or self:hasEffect(self.EFF_IMMINENT_PARADOX_CLONE) then + if self:isTalentActive(self.T_DOOR_TO_THE_PAST) or self:hasEffect(self.EFF_SEE_THE_THREADS) or self:hasEffect(self.EFF_PRECOGNITION) or self:hasEffect(self.EFF_PARADOX_CLONE) or self:hasEffect(self.EFF_IMMINENT_PARADOX_CLONE) then game.logPlayer(self, "The timeline is to fractured right now to use this ability.") return true else diff --git a/game/modules/tome/data/talents/chronomancy/chronomancy.lua b/game/modules/tome/data/talents/chronomancy/chronomancy.lua index 049da3437d3219cf53347c72cd09974195c38d68..9dbccdd9d40894af572245f3575bf751d91286c5 100644 --- a/game/modules/tome/data/talents/chronomancy/chronomancy.lua +++ b/game/modules/tome/data/talents/chronomancy/chronomancy.lua @@ -20,11 +20,27 @@ newTalent{ name = "Probability Weaving", type = {"chronomancy/chronomancy", 1}, + mode = "sustained", require = temporal_req1, - mode = "passive", + sustain_paradox = 75, points = 5, + cooldown = 10, + tactical = { BUFF = 2 }, + getDefense = function(self, t) return self:combatTalentSpellDamage(t, 4, 20) end, + activate = function(self, t) + game:playSoundNear(self, "talents/heal") + return { + def = self:addTemporaryValue("combat_def", t.getDefense(self, t)), + } + end, + deactivate = function(self, t, p) + self:removeTemporaryValue("combat_def", p.def) + return true + end, info = function(self, t) - return ([[Bends the laws of probability, increasing your defense by %d and reducing the chance you'll be critically hit by %d%%]]):format(self:getTalentLevel(t) * 2, self:getTalentLevel(t)) + local defense = t.getDefense(self, t) + return ([[Bends the laws of probability, increasing your defense by %d and reducing the chance you'll be critically hit by melee or ranged attacks by %d%%. + The defense increase will scale with the Magic stat.]]):format(defense, self:getTalentLevel(t)) end, } @@ -44,7 +60,8 @@ newTalent{ end, info = function(self, t) local radius = t.getRadius(self, t) - return ([[Your keen intuition allows you to form a mental picture of your surroundings in a radius of %d.]]): + return ([[Your keen intuition allows you to form a mental picture of your surroundings in a radius of %d. + The radius will scale with your Paradox and Magic stat.]]): format(radius) end, } diff --git a/game/modules/tome/data/talents/chronomancy/energy.lua b/game/modules/tome/data/talents/chronomancy/energy.lua index 64b322bb9ef84b017b0dede2ea148397c408fd37..edac1b4ec7b6cf28466bd89e0fe2d802b5a1a980 100644 --- a/game/modules/tome/data/talents/chronomancy/energy.lua +++ b/game/modules/tome/data/talents/chronomancy/energy.lua @@ -75,20 +75,31 @@ newTalent{ newTalent{ name = "Entropic Field", type = {"chronomancy/energy", 2}, + mode = "sustained", require = chrono_req2, points = 5, - paradox = 5, + sustain_paradox = 150, cooldown = 20, - tactical = { DEFEND = 2 }, - getPower = function(self, t) return 10 + (self:combatTalentSpellDamage(t, 10, 50)*getParadoxModifier(self, pm)) end, - action = function(self, t) - self:setEffect(self.EFF_ENTROPIC_FIELD, 10, {power=t.getPower(self, t)}) + tactical = { BUFF = 2 }, + getPower = function(self, t) return 10 + (self:combatTalentSpellDamage(t, 10, 50)) end, + activate = function(self, t) + game:playSoundNear(self, "talents/heal") + return { + particle = self:addParticles(Particles.new("time_shield", 1)), + phys = self:addTemporaryValue("resists", {[DamageType.PHYSICAL]=t.getPower(self, t)/2}), + proj = self:addTemporaryValue("slow_projectiles", t.getPower(self, t)), + } + end, + deactivate = function(self, t, p) + self:removeParticles(p.particle) + self:removeTemporaryValue("resists", p.phys) + self:removeTemporaryValue("slow_projectiles", p.proj) return true end, info = function(self, t) local power = t.getPower(self, t) - return ([[You encase yourself in a shield that slows incoming projectiles by %d%% and grants you %d%% physical resistance for 10 turns. - The effect will scale with your Paradox and Magic stat.]]):format(power, power / 2) + return ([[You encase yourself in a field that slows incoming projectiles by %d%% and grants you %d%% physical resistance. + The effect will scale with the Magic stat.]]):format(power, power / 2) end, } diff --git a/game/modules/tome/data/talents/chronomancy/gravity.lua b/game/modules/tome/data/talents/chronomancy/gravity.lua index 3966c1fd3dbff28228417a5dc07d172dbc6d6bb2..3c914e21011d2ca87243e58239ee768282b125b3 100644 --- a/game/modules/tome/data/talents/chronomancy/gravity.lua +++ b/game/modules/tome/data/talents/chronomancy/gravity.lua @@ -35,7 +35,7 @@ newTalent{ info = function(self, t) local absorb = t.getAbsorb(self, t) local duration = t.getDuration(self, t) - return ([[Surround yourself with a repulsion field for %d turns that will absorb up to %0.2f damage and potentially knocking back attackers. Pinned creatures are immune to this knockback. + return ([[Surround yourself with a repulsion field for %d turns that will absorb up to %0.2f damage and potentially knock back attackers. Pinned creatures are immune to this knockback. The absorption will scale with your Paradox and Magic stat.]]):format(duration, absorb) end, } diff --git a/game/modules/tome/data/talents/chronomancy/matter.lua b/game/modules/tome/data/talents/chronomancy/matter.lua index 164f58c0cd4917fc5cc6141de85fcf8e0a682b6f..788e78c77add311a9f7a6a6276d69bf3c3cec569 100644 --- a/game/modules/tome/data/talents/chronomancy/matter.lua +++ b/game/modules/tome/data/talents/chronomancy/matter.lua @@ -48,7 +48,7 @@ newTalent{ format(damDesc(self, DamageType.TEMPORAL, damage / 2), damDesc(self, DamageType.PHYSICAL, damage / 2)) end, } -newTalent{ +--[[newTalent{ name = "Terraforming", type = {"chronomancy/matter",2}, require = chrono_req2, @@ -73,8 +73,43 @@ newTalent{ return true end, info = function(self, t) - return ([[Makes impassable terrain passable and turns passable terrain into walls, trees, etc. - Additional talent points will lower the cooldown]]):format() + return (Makes impassable terrain passable and turns passable terrain into walls, trees, etc. + Additional talent points will lower the cooldown):format() + end, +}]] + +newTalent{ + name = "Carbon Spikes", + type = {"chronomancy/matter", 2}, + require = chrono_req2, no_sustain_autoreset = true, + points = 5, + mode = "sustained", + sustain_paradox = 150, + cooldown = 12, + tactical = { BUFF =2, DEFEND = 2 }, + getDamageOnMeleeHit = function(self, t) return self:combatTalentSpellDamage(t, 10, 50) end, + getArmor = function(self, t) return math.ceil (self:combatTalentSpellDamage(t, 20, 40)) end, + activate = function(self, t) + local power = t.getArmor(self, t) + self.carbon_armor = power + game:playSoundNear(self, "talents/generic") + return { + armor = self:addTemporaryValue("carbon_spikes", power), + onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.BLEED]=t.getDamageOnMeleeHit(self, t)}), + } + end, + deactivate = function(self, t, p) + self:removeTemporaryValue("carbon_spikes", p.armor) + self:removeTemporaryValue("on_melee_hit", p.onhit) + self.carbon_armor = nil + return true + end, + info = function(self, t) + local damage = t.getDamageOnMeleeHit(self, t) + local armor = t.getArmor(self, t) + return ([[Fragile spikes of carbon protrude from your clothing and armor, increasing your armor rating by %d and inflicting %0.2f bleed damage over six turns on attackers. Each time you're struck the armor increase will be reduced by 1 until the bonus is less then 1, at which point the spikes will crumble completely and the spell will end. + The armor and bleed damage will increase with the Magic stat.]]): + format(armor, damDesc(self, DamageType.PHYSICAL, damage)) end, } @@ -86,17 +121,13 @@ newTalent{ paradox = 20, cooldown = 20, tactical = { ATTACKAREA = 2, DISABLE = 2 }, - range = 6, direct_hit = true, requires_target = true, - getRadius = function (self, t) return 1 + math.floor(self:getTalentLevel(t) / 4) end, + range = function(self, t) return 1 + self:getTalentLevelRaw(t) end, getDuration = function(self, t) return 2 + math.floor(self:getTalentLevel(t) * getParadoxModifier(self, pm)) end, action = function(self, t) - local tg = {type="ball", range=self:getTalentRange(t), radius=t.getRadius(self, 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 tg = {type="ball", range=0, radius=self:getTalentRange(t), friendlyfire=false, talent=t} + self:project(tg, self.x, self.y, function(tx, ty) local target = game.level.map(tx, ty, Map.ACTOR) if not target then return end @@ -106,14 +137,14 @@ newTalent{ game.logSeen(target, "%s resists the calcification.", target.name:capitalize()) end end) - game.level.map:particleEmitter(x, y, tg.radius, "ball_earth", {radius=tg.radius, grids=grids, tx=x, ty=y}) - game:playSoundNear(self, "talents/earth") + game.level.map:particleEmitter(self.x, self.y, tg.radius, "ball_earth", {radius=tg.radius}) + game:playSoundNear(self, "talents/breath") return true end, info = function(self, t) - local radius = t.getRadius(self, t) + local radius = self:getTalentRange(t) local duration = t.getDuration(self, t) - return ([[Attempts to turn all targets in a radius of %d to stone for %d turns. Stoned creatures are unable to act or regen life and are very brittle. + return ([[Attempts to turn all targets around you in a radius of %d to stone for %d turns. Stoned creatures are unable to act or regen life and are very brittle. If a stoned creature is hit by an attack that deals more than 30%% of its life it will shattered and be destroyed. Stoned creatures are highly resistant to fire and lightning and somewhat resistant to physical attacks. The duration will scale with your Paradox.]]):format(radius, duration) diff --git a/game/modules/tome/data/talents/chronomancy/paradox.lua b/game/modules/tome/data/talents/chronomancy/paradox.lua index d1173ec843afbc2679663db690cc966cbbcdf3ca..69e9646ed403f81b9ff2cecee9d8b5f798d57ad4 100644 --- a/game/modules/tome/data/talents/chronomancy/paradox.lua +++ b/game/modules/tome/data/talents/chronomancy/paradox.lua @@ -20,18 +20,29 @@ newTalent{ name = "Paradox Mastery", type = {"chronomancy/paradox", 1}, + mode = "sustained", require = chrono_req_high1, + sustain_paradox = 75, + cooldown = 10, points = 5, - mode = "passive", - on_learn = function(self, t) - self.resists[DamageType.TEMPORAL] = (self.resists[DamageType.TEMPORAL] or 0) + 5 + getResist = function(self, t) return 10 + (self:combatTalentSpellDamage(t, 10, 50)) end, + activate = function(self, t) + game:playSoundNear(self, "talents/arcane") + return { + particle = self:addParticles(Particles.new("temporal_focus", 1)), + temp = self:addTemporaryValue("resists", {[DamageType.TEMPORAL]=t.getResist(self, t)}), + } end, - on_unlearn = function(self, t) - self.resists[DamageType.TEMPORAL] = (self.resists[DamageType.TEMPORAL] or 0) - 5 + deactivate = function(self, t, p) + self:removeParticles(p.particle) + self:removeTemporaryValue("resists", p.temp) + return true end, info = function(self, t) - return ([[You've learned to better control and resist the dangers of chronomancy. Increases your effective willpower for static history, failure, and backfire calculations by %d%% and increases your Temporal resistance by %d%%.]]): - format(self:getTalentLevel(t) * 10, self:getTalentLevelRaw(t) * 5) + local resist = t.getResist(self, t) + return ([[You've learned to focus your control over the spacetime continuum and resist anomalous effects. Increases your effective willpower for static history, failure, and backfire calculations by %d%% and increases your Temporal resistance by %d%%. + The resistance will scale with the Magic stat.]]): + format(self:getTalentLevel(t) * 10, resist) end, } diff --git a/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua b/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua index 3c721616c86d7239fa32cb55fca460711d9dc0ca..f17b318e20e20f99efaeb145f6eccc7ca6f48cf6 100644 --- a/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua +++ b/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua @@ -64,7 +64,14 @@ newTalent{ cooldown = 24, tactical = { PARADOX = 2 }, getReduction = function(self, t) - local modifier = self:getWil() * (1 + (self:getTalentLevel(self.T_PARADOX_MASTERY)/10) or 0 ) + + --check for Paradox Mastery + if self:knowTalent(self.T_PARADOX_MASTERY) and self:isTalentActive(self.T_PARADOX_MASTERY) then + modifier = self:getWil() * (1 + (self:getTalentLevel(self.T_PARADOX_MASTERY)/10) or 0 ) + else + modifier = self:getWil() + end + local reduction = (20 + (modifier * self:getTalentLevel(t)/2)) return reduction end, diff --git a/game/modules/tome/data/talents/chronomancy/timetravel.lua b/game/modules/tome/data/talents/chronomancy/timetravel.lua index 1d5e4da9583f27c32b8d55317ef6b909b99acefe..521326cf46898c6f28fe84f4fbc04dc5ed005a61 100644 --- a/game/modules/tome/data/talents/chronomancy/timetravel.lua +++ b/game/modules/tome/data/talents/chronomancy/timetravel.lua @@ -24,29 +24,27 @@ newTalent{ type = {"chronomancy/timetravel", 1}, require = chrono_req_1, points = 5, - paradox = 5, + paradox = 10, cooldown = 6, tactical = { ATTACKAREA = 2 }, - range = 1, + direct_hit = true, requires_target = true, + range = function(self, t) return 1 + self:getTalentLevelRaw(t) end, getDamage = function(self, t) return (self:combatTalentSpellDamage(t, 10, 120)*getParadoxModifier(self, pm)) end, getPercent = function(self, t) return (30 + (self:combatTalentSpellDamage(t, 10, 30)*getParadoxModifier(self, pm))) / 100 end, - getRadius = function (self, t) return 4 + math.floor(self:getTalentLevelRaw (t)/2) end, action = function(self, t) - local tg = {type="cone", range=0, radius=t.getRadius(self, t), friendlyfire=false, talent=t} - local x, y = self:getTarget(tg) - if not x or not y then return nil end - self:project(tg, x, y, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t))) - self:project(tg, x, y, DamageType.TEMPORAL_ECHO, self:spellCrit(t.getPercent(self, t))) - game.level.map:particleEmitter(self.x, self.y, tg.radius, "temporal_breath", {radius=tg.radius, tx=x-self.x, ty=y-self.y}) - game:playSoundNear(self, "talents/spell_generic2") + local tg = {type="ball", range=0, radius=self:getTalentRange(t), friendlyfire=false, talent=t} + self:project(tg, self.x, self.y, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t))) + self:project(tg, self.x, self.y, DamageType.TEMPORAL_ECHO, t.getPercent(self, t)) + game.level.map:particleEmitter(self.x, self.y, tg.radius, "ball_temporal", {radius=tg.radius}) + game:playSoundNear(self, "talents/teleport") return true end, info = function(self, t) local percent = t.getPercent(self, t) * 100 - local radius = t.getRadius(self, t) + local radius = self:getTalentRange(t) local damage = t.getDamage(self, t) - return ([[Creates a temporal echo in a %d radius cone. Affected targets will take %0.2f temporal damage and %d%% of the difference between their current life and max life as additional temporal damage. + return ([[Creates a temporal echo in a nova around you in a radius of %d. Affected targets will take %0.2f temporal damage and %d%% of the difference between their current life and max life as additional temporal damage. The percentage and damage scales with your Paradox and the Magic stat.]]): format(radius, damage, percent) end, @@ -104,7 +102,7 @@ newTalent{ if target and not target.player then local hit = self:checkHit(self:combatSpellpower(), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) if not hit then - game.logSeen(target, "The spell fizzles!") + game.logSeen(target, "%s resists!", target.name:capitalize()) return true end else @@ -178,20 +176,62 @@ newTalent{ } newTalent{ - name = "Revision", + name = "Door to the Past", type = {"chronomancy/timetravel", 4}, - require = chrono_req4, + require = chrono_req4, no_sustain_autoreset = true, points = 5, - paradox = 100, - cooldown = 100, + mode = "sustained", + sustain_paradox = 300, + cooldown = 50, no_npc_use = true, - getPercent = function(self, t) return 20 - (self:getTalentLevelRaw(t) * 2) end, + getParadoxIncrease = function(self, t) return 28 - (self:getTalentLevelRaw(t) * 4) end, on_learn = function(self, t) - self:attr("game_cloning", 1) + if not self:knowTalent(self.T_REVISION) then + self:learnTalent(self.T_REVISION) + end end, on_unlearn = function(self, t) - self:attr("game_cloning", -1) + if not self:knowTalent(t) then + self:unlearnTalent(self.T_REVISION) + end + end, + activate = function(self, t) + + if checkTimeline(self) == true then + return + end + + game:playSoundNear(self, "talents/arcane") + return { + game:chronoClone("revision"), + drain = self:addTemporaryValue("paradox_regen", t.getParadoxIncrease(self, t)), + particle = self:addParticles(Particles.new("temporal_aura", 1)), + } end, + deactivate = function(self, t, p) + if game._chronoworlds then game._chronoworlds = nil end + self:removeParticles(p.particle) + self:removeTemporaryValue("paradox_regen", p.drain) + return true + end, + info = function(self, t) + local paradox = t.getParadoxIncrease(self, t) + return ([[This powerful spell allows you to mark a point in time that you can later return to by casting Revision. Maintaining such a doorway causes constant damage to the spacetime continuum and will increase your paradox by %d each turn. + Additional talent points will lower the paradox increase incurred each turn.]]): + format(paradox) + end, +} + +newTalent{ + name = "Revision", + type = {"chronomancy/other", 1}, + type_no_req = true, + points = 1, + message = "@Source@ revises history.", + cooldown = 100, + paradox = 100, + no_npc_use = true, + on_pre_use = function(self, t, silent) if not self:isTalentActive(self.T_DOOR_TO_THE_PAST) then if not silent then game.logPlayer(self, "Door to the Past must be active to use this talent.") end return false end return true end, action = function(self, t) -- Prevent Revision After Death @@ -201,23 +241,21 @@ newTalent{ end game:onTickEnd(function() - if not game:chronoRestore("on_level", true) then + if not game:chronoRestore("revision", true) then game.logSeen(self, "#LIGHT_RED#The spell fizzles.") return end - 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!") -- Manualy start the cooldown of the "old player" game.player:startTalentCooldown(t) game.player:incParadox(t.paradox * (1 + (game.player.paradox / 300))) - game.player.max_life = game.player.max_life * (1 - t.getPercent(self, t) / 100) end) + return true end, info = function(self, t) - local percent = t.getPercent(self, t) - return ([[Casting this spell sends you back to the moment you entered the current dungeon level. Traveling through time carries with it inherent penalties and doing so will permanently reduce your hit points by %d%%. - Additional talent points will lower the hit point cost.]]) - :format(percent) + return ([[Casting Revision will return you to the point in time you created a temporal marker using Door to the Past.]]) + :format() end, -} +} \ No newline at end of file diff --git a/game/modules/tome/data/timed_effects.lua b/game/modules/tome/data/timed_effects.lua index 8835265a9ae903ec923bf6e7ff3ab0d0956fdef6..cd7d55ba3ccbab866dee299794a0552e61ed8058 100644 --- a/game/modules/tome/data/timed_effects.lua +++ b/game/modules/tome/data/timed_effects.lua @@ -2357,28 +2357,6 @@ newEffect{ } --Chronomancy Effects - -newEffect{ - name = "ENTROPIC_FIELD", - desc = "Entropic Field", - long_desc = function(self, eff) return ("Target is surrounded by a field that slows projectiles by %d%% and increases physical resistance by %d%%."):format(eff.power, eff.power/2, eff.power/2) end, - type = "magical", - status = "beneficial", - parameters = { power=10 }, - on_gain = function(self, err) return "#Target# is surrounded by an entropic field.", "+Entropic Field" end, - on_lose = function(self, err) return "The entropic shield around #Target# dissipates.", "-Entropic Field" end, - activate = function(self, eff) - eff.particle = self:addParticles(Particles.new("time_shield", 1)) - eff.phys = self:addTemporaryValue("resists", {[DamageType.PHYSICAL]=eff.power/2}) - eff.proj = self:addTemporaryValue("slow_projectiles", eff.power) - end, - deactivate = function(self, eff) - self:removeParticles(eff.particle) - self:removeTemporaryValue("resists", eff.phys) - self:removeTemporaryValue("slow_projectiles", eff.proj) - end, -} - newEffect{ name = "DAMAGE_SMEARING", desc = "Damage Smearing", diff --git a/game/modules/tome/dialogs/DeathDialog.lua b/game/modules/tome/dialogs/DeathDialog.lua index c5b8c7c52531cc3e8cd455b8149785692c19b107..d3976349175a59290fa490eed59f48c3d8baed69 100644 --- a/game/modules/tome/dialogs/DeathDialog.lua +++ b/game/modules/tome/dialogs/DeathDialog.lua @@ -77,7 +77,7 @@ function _M:cleanActor(actor) if eff[1] == "effect" then actor:removeEffect(eff[2]) else - actor:forceUseTalent(eff[2], {ignore_energy=true}) + actor:forceUseTalent(eff[2], {ignore_energy=true, no_equilibrium_fail=true, no_paradox_fail=true}) end end end