feat: add Disease.isInfected and Disease.hasErupted

This commit is contained in:
dualfroz committed 2026-09-04 19:20:05 +02:00
1 parent c214f5c127
commit fc02e0fe8d
1 file changed
+131
+131
View File
@@ -0,0 +1,131 @@
-- Progressive affliction caught from a carrier's bite, distinct from both
-- status.lua's per-turn buffs/debuffs (a fixed potency for a fixed
-- duration) and curses.lua's item-bound penalties (permanent until a
-- specific remedy). A disease sits silently through its incubation with no
-- outward sign at all, then erupts and starts dealing damage every turn,
-- worsening to a harsher stage on a fixed clock if nothing cures it first.
-- Its tick damage routes through resistances.lua exactly like poison's own
-- damage-over-time (see status.lua's POISON tick), so a plague-resistant
-- survivor shrugs off what would gut anyone else. A disease is plain data
-- on entity.disease, so it serializes with no special handling; the carrier
-- tag a species bite leaves on entity.biteDisease (see monsters.lua) is
-- copied on exactly like breath or ability already are.
local Resistances = require("resistances")
local Disease = {}
Disease.ROT = "rot"
Disease.PLAGUE = "plague"
Disease.PARASITES = "parasites"
-- Fixed order so a given rng draw always names the same affliction.
Disease.order = { Disease.ROT, Disease.PLAGUE, Disease.PARASITES }
Disease.MAX_STAGE = 3
Disease.defs = {
[Disease.ROT] = {
name = "wound rot", incubation = 4, stageTurns = 8,
stagePotency = { 2, 4, 7 },
},
[Disease.PLAGUE] = {
name = "black plague", incubation = 6, stageTurns = 6,
stagePotency = { 3, 6, 10 },
},
[Disease.PARASITES] = {
name = "gut parasites", incubation = 3, stageTurns = 10,
stagePotency = { 1, 3, 5 },
},
}
function Disease.isInfected(entity)
return entity.disease ~= nil
end
-- Whether the infection has finished incubating and is actively dealing
-- damage each tick, as opposed to sitting silent and undetected.
function Disease.hasErupted(entity)
return entity.disease ~= nil and entity.disease.stage > 0
end
-- Seeds a fresh infection of `key` on `entity`, refusing to pile a second
-- affliction on top of one already incubating or erupted - the same
-- one-at-a-time rule status.lua's own stacking already enforces for e.g.
-- poison overwriting duration rather than compounding. Returns whether the
-- infection actually took hold.
function Disease.infect(entity, key)
if Disease.isInfected(entity) then return false end
local def = Disease.defs[key]
if not def then return false end
entity.disease = { key = key, incubation = def.incubation, stage = 0, sinceStage = 0 }
return true
end
-- Lifts whatever infection `entity` is carrying, incubating or already
-- erupted alike. Returns false without state change when there was nothing
-- to cure.
function Disease.cure(entity)
if not Disease.isInfected(entity) then return false end
entity.disease = nil
return true
end
-- The stage's flat tick damage before resistance is applied, or 0 while
-- still incubating (no outward symptom yet) or entirely uninfected.
function Disease.stagePotency(entity)
local infection = entity.disease
if not infection or infection.stage == 0 then return 0 end
return Disease.defs[infection.key].stagePotency[infection.stage]
end
-- One tick per turn on the host's own clock: counts incubation down in
-- silence, breaks out into stage one the moment it reaches zero, then
-- deals that stage's potency each further tick and escalates to the next
-- stage once it has lingered untreated for its full stageTurns window (the
-- top stage simply keeps ticking rather than escalating further). Returns a
-- log line for anything a player would actually notice - the eruption
-- itself, an escalation, or a nonzero tick of damage - and nil for a turn
-- that only ticked incubation down uneventfully.
-- True only for the player, whose entity.name is the pronoun "you" - a
-- subject that takes plain ("you break out", "you are wracked") verb forms
-- in English rather than the third-person-singular ("breaks out", "is
-- wracked") a named host like "orc" takes. Every message below branches on
-- this exactly like Game:springTrap and Game:checkTerrainFor already branch
-- on the same pronoun problem elsewhere in the engine.
local function isPlayer(entity)
return entity.kind == "player"
end
function Disease.tick(entity)
local infection = entity.disease
if not infection then return nil end
local def = Disease.defs[infection.key]
if infection.stage == 0 then
infection.incubation = infection.incubation - 1
if infection.incubation <= 0 then
infection.stage = 1
infection.sinceStage = 0
return isPlayer(entity) and string.format("you break out in %s", def.name)
or string.format("%s breaks out in %s", entity.name, def.name)
end
return nil
end
local dealt = Resistances.apply(entity, def.stagePotency[infection.stage], Resistances.POISON)
entity.hp = entity.hp - dealt
infection.sinceStage = infection.sinceStage + 1
local line = nil
if dealt > 0 then
line = isPlayer(entity) and string.format("you are wracked by %s for %d", def.name, dealt)
or string.format("%s is wracked by %s for %d", entity.name, def.name, dealt)
end
if infection.sinceStage >= def.stageTurns and infection.stage < Disease.MAX_STAGE then
infection.stage = infection.stage + 1
infection.sinceStage = 0
line = isPlayer(entity) and string.format("your %s worsens", def.name)
or string.format("%s's %s worsens", entity.name, def.name)
end
return line
end
return Disease