Forked from
tome / Tales of MajEyal
2436 commits behind the upstream repository.
flux.lua 8.11 KiB
-- ToME - Tales of Maj'Eyal
-- Copyright (C) 2009 - 2019 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: Particles, Timed Effect Particles
newTalent{
name = "Induce Anomaly",
type = {"chronomancy/flux", 1},
require = chrono_req1,
points = 5,
cooldown = 12,
tactical = { PARADOX = 2 },
getReduction = function(self, t) return self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self, t)) end,
anomaly_type = "no-major",
no_energy = true,
action = function(self, t)
self:paradoxDoAnomaly(100, t.getReduction(self, t), {anomaly_type=t.anomaly_type, ignore_energy=true, allow_target=self:knowTalent(self.T_TWIST_FATE)})
return true
end,
info = function(self, t)
local reduction = t.getReduction(self, t)
return ([[Create an anomaly, reducing your Paradox by %d. This spell will never produce a major anomaly.
Induced Anomalies may not be held by Twist Fate, nor do they cause held anomalies to trigger. However upon learning Twist Fate you may target Induced Anomalies.
The Paradox reduction will increase with your Spellpower.]]):format(reduction)
end,
}
newTalent{
name = "Reality Smearing",
type = {"chronomancy/flux", 2},
require = chrono_req2,
mode = "sustained",
sustain_paradox = 0,
points = 5,
cooldown = 10,
tactical = { DEFEND = 2 },
getPercent = function(self, t) return (100 - self:combatTalentLimit(t, 80, 10, 60))/100 end, -- Limit < 20%
getDuration = function(self, t) return getExtensionModifier(self, t, 3) end,
damage_feedback = function(self, t, p, src)
if p.particle and p.particle._shader and p.particle._shader.shad and src and src.x and src.y then
local r = -rng.float(0.2, 0.4)
local a = math.atan2(src.y - self.y, src.x - self.x)
p.particle._shader:setUniform("impact", {math.cos(a) * r, math.sin(a) * r})
p.particle._shader:setUniform("impact_tick", core.game.getTime())
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,
callbackOnHit = function(self, t, cb, src)
local absorb = cb.value * 0.3
local paradox = absorb * t.getPercent(self, t)
self:setEffect(self.EFF_REALITY_SMEARING, t.getDuration(self, t), {paradox=paradox/t.getDuration(self, t)})
game:delayedLogMessage(self, nil, "reality smearing", "#LIGHT_BLUE##Source# converts damage to paradox!")
game:delayedLogDamage(src, self, 0, ("#LIGHT_BLUE#(%d converted)#LAST#"):format(absorb), false)
cb.value = cb.value - absorb
return cb.value
end,
activate = function(self, t)
game:playSoundNear(self, "talents/arcane")
local ret = {}
return ret
end,
deactivate = function(self, t, p)
return true
end,
info = function(self, t)
local ratio = t.getPercent(self, t)
local duration = t.getDuration(self, t)
return ([[While active 30%% of all damage you take is converted into %0.2f Paradox per point.
The Paradox is gained over three turns.]]):
format(ratio, duration)
end,
}
newTalent{
name = "Attenuate",
type = {"chronomancy/flux", 3},
require = chrono_req3,
points = 5,
cooldown = 4,
tactical = { ATTACKAREA = { TEMPORAL = 2 } },
range = 10,
paradox = function (self, t) return getParadoxCost(self, t, 10) end,
radius = function(self, t) return math.floor(self:combatTalentScale(t, 1, 2)) end,
getDamage = function(self, t) return self:combatTalentSpellDamage(t, 25, 290, getParadoxSpellpower(self, t)) end,
getDuration = function(self, t) return getExtensionModifier(self, t, 4) end,
target = function(self, t)
return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), nowarning=true, can_autoaccept=true, talent=t}
end,
requires_target = true,
direct_hit = true,
action = function(self, t)
local tg = self:getTalentTarget(t)
local x, y = self:getTarget(tg)
if not x or not y then return nil end
local _ _, x, y = self:canProject(tg, x, y)
local damage = self:spellCrit(t.getDamage(self, t))
self:project(tg, x, y, function(px, py)
local target = game.level.map(px, py, Map.ACTOR)
if not target then return end
if target:isTalentActive(target.T_REALITY_SMEARING) then
target:setEffect(target.EFF_ATTENUATE_BEN, t.getDuration(self, t), {power=(damage/4)*0.4, src=self})
else
target:setEffect(target.EFF_ATTENUATE_DET, t.getDuration(self, t), {power=damage/4, src=self, apply_power=getParadoxSpellpower(self, t)})
end
end)
game.level.map:particleEmitter(x, y, tg.radius, "generic_sploom", {rm=100, rM=100, gm=200, gM=220, bm=200, bM=220, am=35, aM=90, radius=tg.radius, basenb=60})
game:playSoundNear(self, "talents/tidalwave")
return true
end,
info = function(self, t)
local damage = t.getDamage(self, t)
local duration = t.getDuration(self, t)
local radius = self:getTalentRadius(t)
return ([[Deals %0.2f temporal damage over %d turns to all targets in a radius of %d. Targets with Reality Smearing active will instead recover %d life over four turns.
If a target is reduced below 20%% life while Attenuate is active it may be instantly slain.
The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration, radius, damage *0.4)
end,
}
newTalent{
name = "Twist Fate",
type = {"chronomancy/flux", 4},
require = chrono_req4,
points = 5,
cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 6, 20, 8)) end, -- Limit >4
tactical = { ATTACKAREA = 2 },
on_pre_use = function(self, t, silent) if not self:hasEffect(self.EFF_TWIST_FATE) then if not silent then game.logPlayer(self, "You must have a twisted anomaly to cast this spell.") end return false end return true end,
getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 1, 6))) end,
doTwistFate = function(self, t, twist)
local eff = self:hasEffect(self.EFF_TWIST_FATE)
if twist then
eff.twisted = twist
local anom = self:getTalentFromId(eff.talent)
-- make it real obvious for the player
game.logPlayer(self, "#STEEL_BLUE#Casts %s.", anom.name)
if self == game.player then
game.bignews:saySimple(180, "#STEEL_BLUE#Targeting %s", anom.name)
end
-- Call the anomoly action function directly
anom.action(self, anom)
self:incParadox(-eff.paradox)
end
self:removeEffect(self.EFF_TWIST_FATE)
end,
setEffect = function(self, t, talent, paradox)
game.logPlayer(self, "#STEEL_BLUE#You take control of %s.", self:getTalentFromId(talent).name or nil)
self:setEffect(self.EFF_TWIST_FATE, t.getDuration(self, t), {talent=talent, paradox=paradox})
game:playSoundNear(self, "talents/echo")
end,
action = function(self, t)
t.doTwistFate(self, t, true)
game:playSoundNear(self, "talents/echo")
return true
end,
info = function(self, t)
local duration = t.getDuration(self, t)
local talent
local t_name = "None"
local t_info = ""
local eff = self:hasEffect(self.EFF_TWIST_FATE)
if eff then
talent = self:getTalentFromId(eff.talent)
t_name = talent.name
t_info = talent.info(self, talent)
end
return ([[If Twist Fate is not on cooldown minor anomalies will be held for %d turns, allowing your spell to cast as normal. While held you may cast Twist Fate in order to trigger the anomaly and may choose the target area.
If a second anomaly occurs while a prior one is held or the timed effect expires the first anomaly will trigger immediately, interrupting your current turn or action.
Paradox reductions from held anomalies occur when triggered.
Current Anomaly: %s
%s]]):
format(duration, t_name, t_info)
end,
}