Forked from
tome / Tales of MajEyal
6899 commits behind the upstream repository.
chronomancy.lua 7.56 KiB
-- ToME - Tales of Maj'Eyal
-- Copyright (C) 2009 - 2014 Nicolas Casalini
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--
-- Nicolas Casalini "DarkGod"
-- darkgod@te4.org
-- EDGE TODO: Icons, Particles, Timed Effect Particles
newTalent{
name = "Precognition",
type = {"chronomancy/chronomancy",1},
require = chrono_req1,
points = 5,
paradox = function (self, t) return getParadoxCost(self, t, 10) end,
cooldown = 20,
no_npc_use = true,
getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 14)) end,
range = function(self, t) return 10 + math.min(self:combatTalentSpellDamage(t, 10, 20, getParadoxSpellpower(self))) end,
action = function(self, t)
-- Foresight bonuses
local defense = 0
local crits = 0
if self:knowTalent(self.T_FORESIGHT) then
defense = self:callTalent(self.T_FORESIGHT, "getDefense")
crits = self:callTalent(self.T_FORESIGHT, "getCritDefense")
end
self:setEffect(self.EFF_PRECOGNITION, t.getDuration(self, t), {range=self:getTalentRange(t), actor=1, traps=1, defense=defense, crits=crits})
return true
end,
info = function(self, t)
local range = self:getTalentRange(t)
local duration = t.getDuration(self, t)
return ([[You peer into the future, sensing creatures and traps in a radius of %d for %d turns.
If you know Foresight you'll gain additional defense and chance to shrug off critical hits (equal to your Foresight bonuses) while Precognition is active.
The detection radius will scale with your Spellpower.]]):format(range, duration)
end,
}
newTalent{
name = "Foresight",
type = {"chronomancy/chronomancy",2},
mode = "passive",
require = chrono_req2,
points = 5,
getDefense = function(self, t) return self:combatTalentStatDamage(t, "mag", 10, 50) end,
getCritDefense = function(self, t) return self:combatTalentStatDamage(t, "mag", 5, 25) end,
passives = function(self, t, p)
self:talentTemporaryValue(p, "combat_def", t.getDefense(self, t))
self:talentTemporaryValue(p, "ignore_direct_crits", t.getCritDefense(self, t))
end,
callbackOnStatChange = function(self, t, stat, v)
if stat == self.STAT_MAG then
self:updateTalentPassives(t)
end
end,
info = function(self, t)
local defense = t.getDefense(self, t)
local crits = t.getCritDefense(self, t)
return ([[Gain %d defense and %d%% chance to shrug off critical hits.
If you have Precognition or See the Threads active these bonuses will be added to those effects, granting additional defense and chance to shrug off critical hits.
These bonuses scale with your Magic stat.]]):
format(defense, crits)
end,
}
newTalent{
name = "Contingency",
type = {"chronomancy/chronomancy", 3},
require = chrono_req3,
points = 5,
sustain_paradox = 36,
mode = "sustained",
no_sustain_autoreset = true,
cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 15, 45, 25)) end, -- Limit >15
tactical = { DEFEND = 2 },
no_npc_use = true,
callbackOnHit = function(self, t, cb)
local p = self:isTalentActive(t.id)
local life_after = self.life - cb.value
local cont_trigger = self.max_life * 0.3
-- Cast our contingent spell
if p and p.rest_count <= 0 and cont_trigger > life_after then
local cont_t = p.talent
local cont_id = self:getTalentFromId(cont_t)
local t_level = math.min(self:getTalentLevel(t), self:getTalentLevel(cont_t))
-- Make sure we still know the talent and that the preuse conditions apply
if t_level == 0 or not self:preUseTalent(cont_id, true, true) then
game.logPlayer(self, "#LIGHT_RED#Your Contingency has failed to cast %s!", self:getTalentFromId(cont_t).name)
else
self:forceUseTalent(cont_t, {ignore_ressources=true, ignore_cd=true, ignore_energy=true, force_level=t_level})
game.logPlayer(self, "#STEEL_BLUE#Your Contingency triggered %s!", self:getTalentFromId(cont_t).name)
end
p.rest_count = self:getTalentCooldown(t)
end
return cb.value
end,
callbackOnActBase = function(self, t)
local p = self:isTalentActive(t.id)
if p.rest_count > 0 then p.rest_count = p.rest_count - 1 end
end,
iconOverlay = function(self, t, p)
local val = p.rest_count or 0
if val <= 0 then return "" end
local fnt = "buff_font"
return tostring(math.ceil(val)), fnt
end,
activate = function(self, t)
local talent = self:talentDialog(require("mod.dialogs.talents.ChronomancyContingency").new(self))
if not talent then return nil end
return {
talent = talent, rest_count = 0
}
end,
deactivate = function(self, t, p)
return true
end,
info = function(self, t)
local cooldown = self:getTalentCooldown(t)
local talent = self:isTalentActive(t.id) and self:getTalentFromId(self:isTalentActive(t.id).talent).name or "None"
return ([[Choose an activatable spell that's not targeted. When you take damage that reduces your life below 30%% the spell will automatically cast.
This spell will cast even if it is currently on cooldown, will not consume a turn or resources, and uses the talent level of Contingency or its own, whichever is lower.
This effect can only occur once every %d turns and takes place after the damage is resolved.
Current Contingency Spell: %s]]):
format(cooldown, talent)
end,
}
newTalent{
name = "See the Threads",
type = {"chronomancy/chronomancy", 4},
require = chrono_req4,
points = 5,
paradox = function (self, t) return getParadoxCost(self, t, 20) end,
cooldown = 50,
no_npc_use = true,
getDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 10, 25)) end,
on_pre_use = function(self, t, silent)
if checkTimeline(self) then
if not silent then
game.logPlayer(self, "The timeline is too fractured to do this now.")
end
return false
end
if game.level and game.level.data and game.level.data.see_the_threads_done then
if not silent then
game.logPlayer(self, "You've seen as much as you can here.")
end
return false
end
return true
end,
action = function(self, t)
-- Foresight Bonuses
local defense = 0
local crits = 0
if self:knowTalent(self.T_FORESIGHT) then
defense = self:callTalent(self.T_FORESIGHT, "getDefense")
crits = self:callTalent(self.T_FORESIGHT, "getCritDefense")
end
if game.level and game.level.data then
game.level.data.see_the_threads_done = true
end
self:setEffect(self.EFF_SEE_THREADS, t.getDuration(self, t), {defense=defense, crits=crits})
return true
end,
info = function(self, t)
local duration = t.getDuration(self, t)
return ([[You peer into three possible futures, allowing you to explore each for %d turns. When the effect expires, you'll choose which of the three futures becomes your present.
If you know Foresight you'll gain additional defense and chance to shrug off critical hits (equal to your Foresight values) while See the Threads is active.
This spell splits the timeline. Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful.
Note that seeing visions of your own death can still be fatal.
This spell may only be used once per zone level.]])
:format(duration)
end,
}