Forked from
tome / Tales of MajEyal
427 commits behind the upstream repository.
-
K'van authored
Update Disintegration. Change proc tracking from callbackOnActBase to a turn_proc, fix call to removeEffectsFilter, restore tracking The Cure achievement.
K'van authoredUpdate Disintegration. Change proc tracking from callbackOnActBase to a turn_proc, fix call to removeEffectsFilter, restore tracking The Cure achievement.
matter.lua 12.37 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
local Object = require "mod.class.Object"
newTalent{
name = "Dust to Dust",
type = {"chronomancy/matter",1},
require = chrono_req1,
points = 5,
paradox = function (self, t) return getParadoxCost(self, t, 10) end,
cooldown = 3,
tactical = { ATTACKAREA = {TEMPORAL = 1, PHYSICAL = 1} },
range = 10,
direct_hit = true,
reflectable = true,
requires_target = true,
radius = function(self, t) return math.floor(self:combatTalentScale(t, 1.25, 3.25)) end,
target = function(self, t)
return {type="beam", range=self:getTalentRange(t), talent=t, nowarning=true}
end,
getAshes = function(self, t) return {type="ball", range=0, radius=self:getTalentRadius(t), selffire=false} end,
getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end,
action = function(self, t)
-- Check for digs first
local digs = self:isTalentActive(self.T_DISINTEGRATION) and self:callTalent(self.T_DISINTEGRATION, "getDigs")
local tg = self:getTalentTarget(t)
-- Just for targeting change to pass terrain
if digs then tg.pass_terrain = true end
local x, y = self:getTarget(tg)
if not x or not y then return nil end
-- Change back pass terrain
tg.pass_terrain = nil
-- Ashes to Ashes
local target = game.level.map(x, y, Map.ACTOR)
if target and target == self then
tg = t.getAshes(self, t)
-- We do our digs seperatly and first so we can damage stuff on the other side
if digs then
game.level.map:addEffect(self,
self.x, self.y, 3,
DamageType.DIG, digs,
tg.radius,
5, nil,
nil,
function(e)
e.x = e.src.x
e.y = e.src.y
return true
end,
tg.selffire
)
end
game.level.map:addEffect(self,
self.x, self.y, 3,
DamageType.WARP, self:spellCrit(t.getDamage(self, t)/3),
tg.radius,
5, nil,
engine.MapEffect.new{alpha=100, color_br=75, color_bg=75, color_bb=25, effect_shader="shader_images/paradox_effect.png"},
function(e)
e.x = e.src.x
e.y = e.src.y
return true
end,
tg.selffire
)
game:playSoundNear(self, "talents/cloud")
else
-- and Dust to Dust
if digs then for i = 1, digs do self:project(tg, x, y, DamageType.DIG, 1) end end
self:project(tg, x, y, DamageType.WARP, self:spellCrit(t.getDamage(self, t)))
local _ _, _, _, x, y = self:canProject(tg, x, y)
if core.shader.active() then
game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(x-self.x), math.abs(y-self.y)), "matter_beam", {tx=x-self.x, ty=y-self.y}, {type="lightning"})
else
game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(x-self.x), math.abs(y-self.y)), "matter_beam", {tx=x-self.x, ty=y-self.y})
end
game:playSoundNear(self, "talents/arcane")
end
return true
end,
info = function(self, t)
local damage = t.getDamage(self, t)
local radius = self:getTalentRadius(t)
return ([[Fires a beam that turns matter into dust, inflicting %0.2f temporal damage and %0.2f physical (warp) damage.
Alternatively you may target yourself, creating a field of radius %d around you that will inflict the damage over three turns.
The damage will scale with your Spellpower.]]):
tformat(damDesc(self, DamageType.TEMPORAL, damage / 2), damDesc(self, DamageType.PHYSICAL, damage / 2), radius)
end,
}
newTalent{
name = "Matter Weaving",
type = {"chronomancy/matter",2},
require = chrono_req2,
points = 5,
sustain_paradox = 24,
mode = "sustained",
cooldown = 10,
tactical = { BUFF = 2 },
getImmunity = function(self, t) return self:combatTalentLimit(t, 1, 0.15, 0.50) end, -- Limit <100%
getArmor = function(self, t) return self:combatTalentStatDamage(t, "mag", 10, 50) end,
activate = function(self, t)
game:playSoundNear(self, "talents/earth")
local ret = {
stun = self:addTemporaryValue("stun_immune", t.getImmunity(self, t)),
cut = self:addTemporaryValue("cut_immune", t.getImmunity(self, t)),
armor = self:addTemporaryValue("combat_armor", t.getArmor(self, t)),
}
if not self:addShaderAura("stone_skin", "crystalineaura", {time_factor=1000, spikeOffset=0.123123, spikeLength=0.6, spikeWidth=4, growthSpeed=2, color={150/255, 150/255, 50/255}}, "particles_images/spikes.png") then
ret.particle = self:addParticles(Particles.new("stone_skin", 1))
end
return ret
end,
deactivate = function(self, t, p)
self:removeShaderAura("stone_skin")
self:removeTemporaryValue("stun_immune", p.stun)
self:removeTemporaryValue("cut_immune", p.cut)
self:removeTemporaryValue("combat_armor", p.armor)
self:removeParticles(p.particle)
return true
end,
info = function(self, t)
local armor = t.getArmor(self, t)
local immune = t.getImmunity(self, t) * 100
return ([[Weave matter into your flesh, becoming incredibly resilient to damage. While active you gain %d armour, %d%% resistance to stunning, and %d%% resistance to cuts.
The bonus to armour will scale with your Magic.]]):
tformat(armor, immune, immune)
end,
}
newTalent{
name = "Materialize Barrier",
type = {"chronomancy/matter",3},
require = chrono_req3,
points = 5,
paradox = function (self, t) return getParadoxCost(self, t, 15) end,
cooldown = 14,
tactical = { DISABLE = 2 },
range = 10,
direct_hit = true,
requires_target = true,
radius = function(self, t) return math.floor(self:combatTalentScale(t, 1, 2)) end,
getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 200, getParadoxSpellpower(self, t)) end,
getDuration = function(self, t) return getExtensionModifier(self, t, 4) end,
getLength = function(self, t) return 3 end,
target = function(self, t)
local halflength = math.floor(t.getLength(self,t)/2)
local block = function(_, lx, ly)
return game.level.map:checkAllEntities(lx, ly, "block_move")
end
return {type="wall", range=self:getTalentRange(t), nolock=true, halflength=halflength, talent=t, halfmax_spots=halflength+1, block_radius=block}
end,
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)
if game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then return nil end
self:project(tg, x, y, function(px, py, tg, self)
local oe = game.level.map(px, py, Map.TERRAIN)
if not oe or oe.special then return end
if not oe or oe:attr("temporary") or game.level.map:checkAllEntities(px, py, "block_move") then return end
local e = Object.new{
old_feat = oe,
name = _t"materialize barrier", image = "terrain/rocky_mountain.png",
display = '#', color_r=255, color_g=255, color_b=255, back_color=colors.GREY,
shader = "shadow_simulacrum",
shader_args = { color = {0.6, 0.6, 0.2}, base = 0.9, time_factor = 1500 },
desc = _t"a summoned wall of stone",
type = "wall", --subtype = "floor",
always_remember = true,
can_pass = {pass_wall=1},
does_block_move = true,
show_tooltip = true,
block_move = true,
block_sight = true,
temporary = t.getDuration(self, t),
x = px, y = py,
canAct = false,
act = function(self)
self:useEnergy()
self.temporary = self.temporary - 1
if self.temporary <= 0 then
game.level.map(self.x, self.y, engine.Map.TERRAIN, self.old_feat)
game.nicer_tiles:updateAround(game.level, self.x, self.y)
game.level:removeEntity(self)
game.level.map:scheduleRedisplay()
end
end,
dig = function(src, x, y, old)
-- Explode!
local self = game.level.map(x, y, engine.Map.TERRAIN)
local t = self.summoner:getTalentFromId(self.summoner.T_MATERIALIZE_BARRIER)
local tg = {type="ball", range=0, radius = self.summoner:getTalentRadius(t), talent=t, x=self.x, y=self.y}
self.summoner.__project_source = self
self.summoner:project(tg, self.x, self.y, engine.DamageType.BLEED, self.summoner:spellCrit(t.getDamage(self.summoner, t)))
self.summoner.__project_source = nil
game.level.map:particleEmitter(x, y, tg.radius, "ball_earth", {radius=tg.radius})
game.level:removeEntity(old, true)
game.level.map:scheduleRedisplay()
return nil, old.old_feat
end,
summoner_gain_exp = true,
summoner = self,
}
e.tooltip = mod.class.Grid.tooltip
game.level:addEntity(e)
game.level.map(px, py, Map.TERRAIN, e)
end)
game:playSoundNear(self, "talents/earth")
-- Update so we don't see things move on the otherside of the wall... at least not without precog >:)
game:onTickEnd(function()
if game.level then
self:resetCanSeeCache()
if self.player then for uid, e in pairs(game.level.entities) do if e.x then game.level.map:updateMap(e.x, e.y) end end game.level.map.changed = true end
end
end)
return true
end,
info = function(self, t)
local length = t.getLength(self, t)
local duration = t.getDuration(self, t)
local damage = t.getDamage(self, t)
local radius = self:getTalentRadius(t)
return ([[Create a tightly bound matter wall of up to a length of %d that lasts %d turns.
If any part of this wall is dug out it will explode, causing targets in a radius of %d to bleed for %0.2f physical damage over six turns.]])
:tformat(length, duration, radius, damDesc(self, DamageType.PHYSICAL, damage))
end,
}
newTalent{
name = "Disintegration",
type = {"chronomancy/matter",4},
require = chrono_req4,
points = 5,
sustain_paradox = 24,
mode = "sustained",
cooldown = 10,
tactical = { BUFF = 2 },
getDigs = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end,
getChance = function(self, t) return self:combatTalentLimit(t, 50, 10, 40) end, -- Limit < 50%end,
doStrip = function(self, t, target, type)
local what = type == "PHYSICAL" and "physical" or "magical"
local p = self:isTalentActive(self.T_DISINTEGRATION)
self.turn_procs.disintegration_physical = self.turn_procs.disintegration_physical or {}
self.turn_procs.disintegration_magical = self.turn_procs.disintegration_magical or {}
if what == "physical" and self.turn_procs.disintegration_physical[target] then return end
if what == "magical" and self.turn_procs.disintegration_magical[target] then return end
if rng.percent(t.getChance(self, t)) then
-- effect filter
local filter = {type = what, status = "beneficial"}
if target:removeEffectsFilter(self, filter, 1) > 0 then
game.logSeen(self, "#CRIMSON#%s's beneficial effect was stripped!#LAST#", target:getName():capitalize())
if what == "physical" then self.turn_procs.disintegration_physical[target] = true end
if what == "magical" then self.turn_procs.disintegration_magical[target] = true end
-- The Cure achievement
local acheive = self.player and not target.training_dummy and target ~= self
if acheive then
world:gainAchievement("THE_CURE", self)
end
end
end
end,
activate = function(self, t)
game:playSoundNear(self, "talents/earth")
local ret = { }
if core.shader.active(4) then
ret.particle = self:addParticles(Particles.new("shader_ring_rotating", 1, {rotation=-0.01, radius=1.2}, {type="stone", hide_center=1, zoom=0.6, color1={0.4, 0.4, 0, 1}, color2={0.5, 0.5, 0, 1}, xy={self.x, self.y}}))
end
return ret
end,
deactivate = function(self, t, p)
if p.particle then self:removeParticles(p.particle) end
return true
end,
info = function(self, t)
local digs = t.getDigs(self, t)
local chance = t.getChance(self, t)
return ([[While active your physical and temporal damage has a %d%% chance to remove one beneficial physical or magical temporary effect (respectively) from targets you hit.
Only one physical and one magical effect may be removed per turn from each target.
Additionally your Dust to Dust spell now digs up to %d tiles into walls.]]):
tformat(chance, digs)
end,
}