feat: extend status with Status.remove and Status.cureHostile
This commit is contained in:
1 parent
7516aae0e4
commit
249d2d047b
1 file changed
+115
-5
+115
-5
@@ -2,6 +2,12 @@
|
|||||||
-- damage-over-time drains hp, regeneration restores it, stun skips the action,
|
-- damage-over-time drains hp, regeneration restores it, stun skips the action,
|
||||||
-- and haste/slow/strength feed modifiers into speed and combat. Effects are
|
-- and haste/slow/strength feed modifiers into speed and combat. Effects are
|
||||||
-- plain data on entity.statuses so they serialize without special handling.
|
-- plain data on entity.statuses so they serialize without special handling.
|
||||||
|
-- Poison and burning route their tick damage through resistances.lua, so a
|
||||||
|
-- poison-immune undead shrugs off venom entirely and a fire-resistant hide
|
||||||
|
-- only smoulders at half the usual potency.
|
||||||
|
|
||||||
|
local Resistances = require("resistances")
|
||||||
|
local Conductivity = require("conductivity")
|
||||||
|
|
||||||
local Status = {}
|
local Status = {}
|
||||||
|
|
||||||
@@ -12,20 +18,54 @@ Status.REGEN = "regen"
|
|||||||
Status.HASTE = "haste"
|
Status.HASTE = "haste"
|
||||||
Status.SLOW = "slow"
|
Status.SLOW = "slow"
|
||||||
Status.STRENGTH = "strength"
|
Status.STRENGTH = "strength"
|
||||||
|
Status.SHIELD = "shield"
|
||||||
|
Status.WEAKEN = "weaken"
|
||||||
|
Status.DISCORD = "discord"
|
||||||
|
Status.CONFUSE = "confuse"
|
||||||
|
Status.BLIND = "blind"
|
||||||
|
Status.LEVITATE = "levitate"
|
||||||
|
|
||||||
|
-- Odds, rolled fresh each turn a confused actor tries to act, that its own
|
||||||
|
-- chosen direction gets overridden by a random one instead - see
|
||||||
|
-- Game:movePlayer's stumble branch and Ai.act's mirror of it for monsters.
|
||||||
|
Status.CONFUSE_NUMERATOR = 1
|
||||||
|
Status.CONFUSE_DENOMINATOR = 2
|
||||||
|
|
||||||
|
-- Debuffs a cleansing effect (see effects.lua's "cure" handler) is allowed to
|
||||||
|
-- strip early. Buffs (REGEN/HASTE/STRENGTH/SHIELD/LEVITATE) are deliberately
|
||||||
|
-- excluded - a curative should not also strip a buff the entity chose to be
|
||||||
|
-- carrying.
|
||||||
|
Status.HOSTILE = {
|
||||||
|
[Status.POISON] = true,
|
||||||
|
[Status.BURNING] = true,
|
||||||
|
[Status.STUN] = true,
|
||||||
|
[Status.SLOW] = true,
|
||||||
|
[Status.WEAKEN] = true,
|
||||||
|
[Status.DISCORD] = true,
|
||||||
|
[Status.CONFUSE] = true,
|
||||||
|
[Status.BLIND] = true,
|
||||||
|
}
|
||||||
|
|
||||||
Status.defs = {
|
Status.defs = {
|
||||||
[Status.POISON] = {
|
[Status.POISON] = {
|
||||||
glyph = "p",
|
glyph = "p",
|
||||||
tick = function(entity, potency)
|
tick = function(entity, potency)
|
||||||
entity.hp = entity.hp - potency
|
local dealt = Resistances.apply(entity, potency, Resistances.POISON)
|
||||||
return string.format("poison saps %s for %d", entity.name, potency)
|
entity.hp = entity.hp - dealt
|
||||||
|
if dealt <= 0 then
|
||||||
|
return entity.kind == "player" and "you shrug off the poison"
|
||||||
|
or string.format("%s shrugs off the poison", entity.name)
|
||||||
|
end
|
||||||
|
return string.format("poison saps %s for %d", entity.name, dealt)
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
[Status.BURNING] = {
|
[Status.BURNING] = {
|
||||||
glyph = "b",
|
glyph = "b",
|
||||||
tick = function(entity, potency)
|
tick = function(entity, potency)
|
||||||
entity.hp = entity.hp - potency
|
local dealt = Conductivity.applyFire(entity, potency)
|
||||||
return string.format("flames sear %s for %d", entity.name, potency)
|
entity.hp = entity.hp - dealt
|
||||||
|
if dealt <= 0 then return string.format("the flames wash over %s harmlessly", entity.name) end
|
||||||
|
return string.format("flames sear %s for %d", entity.name, dealt)
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
[Status.REGEN] = {
|
[Status.REGEN] = {
|
||||||
@@ -34,7 +74,9 @@ Status.defs = {
|
|||||||
local before = entity.hp
|
local before = entity.hp
|
||||||
entity.hp = math.min(entity.maxHp, entity.hp + potency)
|
entity.hp = math.min(entity.maxHp, entity.hp + potency)
|
||||||
if entity.hp > before then
|
if entity.hp > before then
|
||||||
return string.format("%s knits flesh for %d", entity.name, entity.hp - before)
|
local healed = entity.hp - before
|
||||||
|
return entity.kind == "player" and string.format("you knit flesh for %d", healed)
|
||||||
|
or string.format("%s knits flesh for %d", entity.name, healed)
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end,
|
end,
|
||||||
@@ -43,9 +85,38 @@ Status.defs = {
|
|||||||
[Status.HASTE] = { glyph = "h", speed = 60 },
|
[Status.HASTE] = { glyph = "h", speed = 60 },
|
||||||
[Status.SLOW] = { glyph = "l", speed = -50 },
|
[Status.SLOW] = { glyph = "l", speed = -50 },
|
||||||
[Status.STRENGTH] = { glyph = "S", attack = 3 },
|
[Status.STRENGTH] = { glyph = "S", attack = 3 },
|
||||||
|
[Status.SHIELD] = { glyph = "d", defense = 4 },
|
||||||
|
[Status.WEAKEN] = { glyph = "c", attack = -3 },
|
||||||
|
-- No tick or stat modifier of its own: infighting.lua reads Status.has
|
||||||
|
-- directly to decide whether a monster's turn goes to Ai.act at all.
|
||||||
|
[Status.DISCORD] = { glyph = "o" },
|
||||||
|
-- No tick or stat modifier of its own either: Game:movePlayer and Ai.act
|
||||||
|
-- both read Status.has directly, at the point they would otherwise commit
|
||||||
|
-- to a chosen direction, to roll whether that direction gets overridden.
|
||||||
|
[Status.CONFUSE] = { glyph = "f" },
|
||||||
|
-- No tick or stat modifier of its own: Game:effectiveRadius and
|
||||||
|
-- Game:canSeePlayer both read Status.has directly, clamping vision instead
|
||||||
|
-- of touching hp or combat stats - confuse scrambles where you go, blind
|
||||||
|
-- scrambles what you can tell is there in the first place.
|
||||||
|
[Status.BLIND] = { glyph = "!" },
|
||||||
|
-- No tick or stat modifier of its own: Game:checkTerrainFor and
|
||||||
|
-- Game:checkTrapFor (plus movePlayer's own trap check) read Status.has
|
||||||
|
-- directly, letting a levitating entity drift clear of lava, a chasm's
|
||||||
|
-- drop, an ice tile's slide, and any ground-pressure trap without ever
|
||||||
|
-- touching whatever would otherwise trigger them.
|
||||||
|
[Status.LEVITATE] = { glyph = "y" },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Records `kind` as active on `entity` for `duration` more ticks at `potency`,
|
||||||
|
-- or refreshes an already-active instance of the same kind to whichever of
|
||||||
|
-- the two durations and potencies is stronger. A soaked entity's own
|
||||||
|
-- dampness (see conductivity.lua) refuses a BURNING application outright,
|
||||||
|
-- before anything is recorded - returns nil and leaves entity.statuses
|
||||||
|
-- untouched, exactly as if the call had never been made.
|
||||||
function Status.apply(entity, kind, duration, potency)
|
function Status.apply(entity, kind, duration, potency)
|
||||||
|
if kind == Status.BURNING and Conductivity.resistsIgnition(entity) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
potency = potency or 1
|
potency = potency or 1
|
||||||
for _, active in ipairs(entity.statuses) do
|
for _, active in ipairs(entity.statuses) do
|
||||||
if active.kind == kind then
|
if active.kind == kind then
|
||||||
@@ -66,6 +137,36 @@ function Status.has(entity, kind)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Strips one named status outright, independent of its remaining duration.
|
||||||
|
-- Returns whether anything was actually removed.
|
||||||
|
function Status.remove(entity, kind)
|
||||||
|
for i, active in ipairs(entity.statuses) do
|
||||||
|
if active.kind == kind then
|
||||||
|
table.remove(entity.statuses, i)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Strips every active status listed in Status.HOSTILE (poison, burning,
|
||||||
|
-- stun, slow, weaken, discord, confuse, blind), leaving buffs untouched.
|
||||||
|
-- Returns the kinds actually removed, in the order they were active, for
|
||||||
|
-- callers that want to report exactly what a cleansing effect lifted.
|
||||||
|
function Status.cureHostile(entity)
|
||||||
|
local removed = {}
|
||||||
|
local kept = {}
|
||||||
|
for _, active in ipairs(entity.statuses) do
|
||||||
|
if Status.HOSTILE[active.kind] then
|
||||||
|
removed[#removed + 1] = active.kind
|
||||||
|
else
|
||||||
|
kept[#kept + 1] = active
|
||||||
|
end
|
||||||
|
end
|
||||||
|
entity.statuses = kept
|
||||||
|
return removed
|
||||||
|
end
|
||||||
|
|
||||||
-- Advances every effect one step, applying its tick and dropping expired ones.
|
-- Advances every effect one step, applying its tick and dropping expired ones.
|
||||||
-- Returns the log lines produced this step, in effect order.
|
-- Returns the log lines produced this step, in effect order.
|
||||||
function Status.tick(entity)
|
function Status.tick(entity)
|
||||||
@@ -104,6 +205,15 @@ function Status.attackModifier(entity)
|
|||||||
return delta
|
return delta
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Status.defenseModifier(entity)
|
||||||
|
local delta = 0
|
||||||
|
for _, active in ipairs(entity.statuses) do
|
||||||
|
local def = Status.defs[active.kind]
|
||||||
|
if def.defense then delta = delta + def.defense end
|
||||||
|
end
|
||||||
|
return delta
|
||||||
|
end
|
||||||
|
|
||||||
function Status.skipsTurn(entity)
|
function Status.skipsTurn(entity)
|
||||||
for _, active in ipairs(entity.statuses) do
|
for _, active in ipairs(entity.statuses) do
|
||||||
if Status.defs[active.kind].skipsTurn then return true end
|
if Status.defs[active.kind].skipsTurn then return true end
|
||||||
|
|||||||
Reference in new issue
Block a user