feat: add Spells.knows and Spells.learn to spells
This commit is contained in:
1 parent
73bc8c133b
commit
7516aae0e4
1 file changed
+91
@@ -156,3 +156,94 @@ Spells.catalog = {
|
|||||||
-- Saps the target's fighting strength for a few turns (see status.lua's
|
-- Saps the target's fighting strength for a few turns (see status.lua's
|
||||||
-- Status.WEAKEN). Until now WEAKEN only ever flowed toward the player - a
|
-- Status.WEAKEN). Until now WEAKEN only ever flowed toward the player - a
|
||||||
-- shaman's ranged curse, a cursed shrine's backfired prayer - with no way
|
-- shaman's ranged curse, a cursed shrine's backfired prayer - with no way
|
||||||
|
-- for the player to return the favor against a monster; this spell is
|
||||||
|
-- that missing direction, shaped exactly like confuse/blind above (no
|
||||||
|
-- direct damage, just a debuff riding on Status.apply).
|
||||||
|
weaken = {
|
||||||
|
name = "withering hex", cost = 4, targeted = true, range = 5,
|
||||||
|
cast = function(_, _, target)
|
||||||
|
Status.apply(target, Status.WEAKEN, 6, 1)
|
||||||
|
return { string.format("%s's strength withers", target.name) }
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
-- Rewrites the target's body outright rather than damaging or debuffing
|
||||||
|
-- it (see polymorph.lua). A pricier cast than any other targeted spell
|
||||||
|
-- here, since a lucky roll can turn a real threat into something the
|
||||||
|
-- caster can safely ignore - and an unlucky one can just as easily make
|
||||||
|
-- it worse, the same double-edged risk a shrine's backfired prayer carries.
|
||||||
|
polymorph = {
|
||||||
|
name = "polymorph", cost = 9, targeted = true, range = 5,
|
||||||
|
cast = function(game, _, target)
|
||||||
|
local oldName = target.name
|
||||||
|
local pool = Polymorph.pool(game.depth)
|
||||||
|
local spec = Polymorph.transform(target, game.rng, pool)
|
||||||
|
if not spec then
|
||||||
|
return { string.format("the magic washes over %s, changing nothing", oldName) }
|
||||||
|
end
|
||||||
|
return { string.format("%s convulses and reforms into a %s", oldName, spec.name) }
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
-- A self-buff shaped exactly like shield, but see status.lua's
|
||||||
|
-- Status.LEVITATE for the payoff: for as long as it lasts, the caster
|
||||||
|
-- drifts clear of lava, a chasm's drop, an ice tile's slide, and any
|
||||||
|
-- ground-pressure trap (Game:checkTerrainFor/checkTrapFor) instead of
|
||||||
|
-- soaking a hit.
|
||||||
|
levitate = {
|
||||||
|
name = "levitation", cost = 5, targeted = false,
|
||||||
|
cast = function(_, caster)
|
||||||
|
Status.apply(caster, Status.LEVITATE, 20, 1)
|
||||||
|
return { "you rise off the ground" }
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
blink = {
|
||||||
|
name = "blink", cost = 6, targeted = false,
|
||||||
|
cast = function(game, caster)
|
||||||
|
local free = {}
|
||||||
|
for _, cell in ipairs(reachableWithin(game, caster, 5)) do
|
||||||
|
if not game.level:entityAt(cell.x, cell.y) then
|
||||||
|
free[#free + 1] = cell
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #free == 0 then return { "there is nowhere to blink" } end
|
||||||
|
local pick = free[game.rng:range(1, #free)]
|
||||||
|
caster.x, caster.y = pick.x, pick.y
|
||||||
|
return { "you blink through space" }
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
function Spells.knows(caster, key)
|
||||||
|
for _, known in ipairs(caster.spellsKnown) do
|
||||||
|
if known == key then return true end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Spells.learn(caster, key)
|
||||||
|
if not Spells.catalog[key] then return false end
|
||||||
|
if Spells.knows(caster, key) then return false end
|
||||||
|
caster.spellsKnown[#caster.spellsKnown + 1] = key
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Attempts to cast `key` on behalf of `caster`. Returns (true, lines) on a
|
||||||
|
-- successful cast, or (false, lines) with an explanatory line and no state
|
||||||
|
-- changed - no mana spent, no cooldown, nothing - on failure.
|
||||||
|
function Spells.cast(game, caster, key, target)
|
||||||
|
local spell = Spells.catalog[key]
|
||||||
|
if not spell then return false, { "you don't know that spell" } end
|
||||||
|
if not Spells.knows(caster, key) then return false, { "you haven't learned that spell" } end
|
||||||
|
if caster.mana < spell.cost then return false, { "not enough mana" } end
|
||||||
|
if spell.targeted then
|
||||||
|
if not target or not target:isAlive() then return false, { "no target" } end
|
||||||
|
local dist = math.max(math.abs(target.x - caster.x), math.abs(target.y - caster.y))
|
||||||
|
if dist > spell.range then return false, { "out of range" } end
|
||||||
|
if not Los.clear(game.level.map, caster.x, caster.y, target.x, target.y) then
|
||||||
|
return false, { "no clear shot" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
caster.mana = caster.mana - spell.cost
|
||||||
|
return true, spell.cast(game, caster, target) or {}
|
||||||
|
end
|
||||||
|
|
||||||
|
return Spells
|
||||||
Reference in new issue
Block a user