feat: add Compendium.new, Compendium.noteSighting and Conductivity.isWet
This commit is contained in:
1 parent
972f6e00ec
commit
4c5bae5a00
2 files changed
+173
No files matched your search
@@ -0,0 +1,93 @@
|
||||
-- Cross-run bestiary: which species the player has ever laid eyes on, and how
|
||||
-- many of each they have slain. A compendium lives inside meta.lua's
|
||||
-- persistent save (see game.lua's Game.new, which reuses options.meta's own
|
||||
-- table by reference so every sighting and kill recorded mid-run is already
|
||||
-- sitting in the very table warren.lua writes back out with Meta.save) so it
|
||||
-- survives death exactly like renown and perks; a run started without
|
||||
-- --meta still gets a fresh, un-persisted one so every code path exercises
|
||||
-- the same bookkeeping either way.
|
||||
|
||||
local Monsters = require("monsters")
|
||||
local Bosses = require("bosses")
|
||||
|
||||
local Compendium = {}
|
||||
|
||||
-- One evocative line per species, keyed the same as monsters.lua's species
|
||||
-- key or, for a boss, its bosses.lua catalog key (without the "boss_"
|
||||
-- prefix game.lua stamps onto the actual entity's `species` field).
|
||||
Compendium.FLAVOR = {
|
||||
rat = "Bold enough to bite an ankle, never bold enough to fight fair.",
|
||||
bat = "Flits erratically through the dark on a course only it can hear.",
|
||||
kobold = "Travels in twos and threes; alone it barely has the nerve to bite.",
|
||||
goblin = "Quick with a rusty blade, quicker to run once its pack scatters.",
|
||||
archer = "A skeleton archer, more patient dead than most men are living.",
|
||||
orc = "Broad-shouldered muscle with no plan beyond the next swing.",
|
||||
wretch = "Something that used to be a person, now just hungry and afraid.",
|
||||
troll = "Wounds close almost as fast as they open; fire is the only argument it respects.",
|
||||
mage = "A dark mage who traded caution for whatever is behind that door.",
|
||||
dragon = "Young enough to still be reckless, old enough to already breathe fire.",
|
||||
zombie = "Shambles forward on rot and spite alone.",
|
||||
slime = "Splits when struck; killing one carelessly just makes two problems.",
|
||||
footpad = "Light-fingered and lighter on its feet - it wants your purse, not a fight.",
|
||||
shaman = "Chants curses from a safe distance and flees the moment that fails.",
|
||||
necromancer = "Would rather raise a friend from your kills than make one of its own.",
|
||||
cleric = "Keeps its allies standing longer than they have any right to.",
|
||||
chieftain = "Rallies its kin with a bellow that carries through the whole floor.",
|
||||
shopkeeper = "Minds its own business and its stock, right up until you take both.",
|
||||
warlord = "The Orc Warlord, whose rage only sharpens the longer a fight runs.",
|
||||
lich = "The Lich, a will that outlasted its own body decades ago.",
|
||||
bonequeen = "The Bone Queen, who never lacks for company - the dead keep answering her call.",
|
||||
}
|
||||
|
||||
function Compendium.new()
|
||||
return { seen = {}, kills = {} }
|
||||
end
|
||||
|
||||
-- Marks `speciesKey` as sighted. Returns true only the first time it is ever
|
||||
-- seen, so a caller can choose whether to make anything of that moment; a
|
||||
-- repeat sighting is silently a no-op.
|
||||
function Compendium.noteSighting(compendium, speciesKey)
|
||||
if not speciesKey or compendium.seen[speciesKey] then return false end
|
||||
compendium.seen[speciesKey] = true
|
||||
return true
|
||||
end
|
||||
|
||||
function Compendium.noteKill(compendium, speciesKey)
|
||||
if not speciesKey then return end
|
||||
compendium.kills[speciesKey] = (compendium.kills[speciesKey] or 0) + 1
|
||||
end
|
||||
|
||||
function Compendium.killCount(compendium, speciesKey)
|
||||
return compendium.kills[speciesKey] or 0
|
||||
end
|
||||
|
||||
function Compendium.hasSeen(compendium, speciesKey)
|
||||
return compendium.seen[speciesKey] == true
|
||||
end
|
||||
|
||||
-- Every species named in the wild bestiary or the boss catalog, in a fixed
|
||||
-- order (wild species first, then bosses), each row carrying its catalogued
|
||||
-- name, flavor line and this compendium's own seen/kill tally. `key` here
|
||||
-- matches exactly what an actual entity's `species` field would hold, so a
|
||||
-- caller can cross-reference the two without re-deriving the "boss_" prefix.
|
||||
function Compendium.entries(compendium)
|
||||
local rows = {}
|
||||
for _, spec in ipairs(Monsters.species) do
|
||||
rows[#rows + 1] = {
|
||||
key = spec.key, name = spec.name, flavor = Compendium.FLAVOR[spec.key],
|
||||
seen = Compendium.hasSeen(compendium, spec.key),
|
||||
kills = Compendium.killCount(compendium, spec.key),
|
||||
}
|
||||
end
|
||||
for _, spec in ipairs(Bosses.catalog) do
|
||||
local key = "boss_" .. spec.key
|
||||
rows[#rows + 1] = {
|
||||
key = key, name = spec.name, flavor = Compendium.FLAVOR[spec.key],
|
||||
seen = Compendium.hasSeen(compendium, key),
|
||||
kills = Compendium.killCount(compendium, key),
|
||||
}
|
||||
end
|
||||
return rows
|
||||
end
|
||||
|
||||
return Compendium
|
||||
@@ -0,0 +1,80 @@
|
||||
-- Elemental terrain interaction between open water and the fire/lightning
|
||||
-- damage types resistances.lua already defines. Stepping into water leaves an
|
||||
-- entity soaked for a few turns after it moves on, not just on the exact
|
||||
-- water tile - the lingering dampness other systems read through Conductivity
|
||||
-- rather than re-checking the tile underfoot themselves. A soaked entity
|
||||
-- resists catching fire outright (Status.apply refuses a BURNING application,
|
||||
-- the same way a water tile already douses tile fire in Hazards.step) and,
|
||||
-- for damage that already lands, dampens further fire (Conductivity.applyFire,
|
||||
-- used by hazards.lua's fire tick and status.lua's own BURNING dot) while
|
||||
-- conducting lightning for a bonus (Conductivity.applyLightning, used by
|
||||
-- spells.lua's shock bolt) - the water clinging to them is exactly what makes
|
||||
-- a live wire so much worse to be standing in. This is scoped to fire/
|
||||
-- lightning that already routes through here; an instantaneous hit like
|
||||
-- lava contact or a breath attack that computes its own damage inline is
|
||||
-- left alone - only ignition-prevention (resistsIgnition) reaches those.
|
||||
--
|
||||
-- LIGHTNING was previously a fully declared Resistances type with nothing in
|
||||
-- the game that ever dealt it - elites could roll a resist tier for a damage
|
||||
-- type nothing ever used. Conductivity.applyLightning plus spells.lua's new
|
||||
-- shock bolt spell are what makes an elite's rolled lightning resistance
|
||||
-- (see elites.lua) matter for the first time.
|
||||
|
||||
local Tiles = require("tiles")
|
||||
local Resistances = require("resistances")
|
||||
|
||||
local Conductivity = {}
|
||||
|
||||
Conductivity.SOAK_DURATION = 3
|
||||
Conductivity.LIGHTNING_BONUS_PERCENT = 50
|
||||
Conductivity.FIRE_DAMPEN_PERCENT = 50
|
||||
|
||||
function Conductivity.isWet(entity)
|
||||
return (entity.wet or 0) > 0
|
||||
end
|
||||
|
||||
-- Refreshes `entity`'s soak counter to the full duration when it ends its
|
||||
-- turn standing on open water, or counts it down by one otherwise - the same
|
||||
-- "renew on cause, decay on absence" shape a stacked status.lua effect's own
|
||||
-- duration follows, but tracked as a plain entity field since it is a terrain
|
||||
-- reaction rather than something any status.lua effect key describes.
|
||||
function Conductivity.tick(level, entity)
|
||||
if level.map:get(entity.x, entity.y) == Tiles.WATER then
|
||||
entity.wet = Conductivity.SOAK_DURATION
|
||||
elseif (entity.wet or 0) > 0 then
|
||||
entity.wet = entity.wet - 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Fire damage against a soaked target is dampened on top of whatever
|
||||
-- Resistances.tierOf already resolves - drenched cloth or hide smoulders
|
||||
-- rather than catching properly. Immune still floors it at zero.
|
||||
function Conductivity.applyFire(entity, amount)
|
||||
local dealt = Resistances.apply(entity, amount, Resistances.FIRE)
|
||||
if dealt > 0 and Conductivity.isWet(entity) then
|
||||
dealt = math.max(1, dealt - (dealt * Conductivity.FIRE_DAMPEN_PERCENT) // 100)
|
||||
end
|
||||
return dealt
|
||||
end
|
||||
|
||||
-- Lightning damage against a soaked target conducts through the water
|
||||
-- clinging to them for a bonus on top of whatever Resistances.tierOf already
|
||||
-- resolves - immune still blocks it outright, same as any other type.
|
||||
function Conductivity.applyLightning(entity, amount)
|
||||
local dealt = Resistances.apply(entity, amount, Resistances.LIGHTNING)
|
||||
if dealt > 0 and Conductivity.isWet(entity) then
|
||||
dealt = dealt + (dealt * Conductivity.LIGHTNING_BONUS_PERCENT) // 100
|
||||
end
|
||||
return dealt
|
||||
end
|
||||
|
||||
-- A soaked target's own dampness snuffs any attempt to set them alight
|
||||
-- outright, before the BURNING effect is ever recorded (see status.lua's
|
||||
-- Status.apply) - the same protection a water tile already gives tile fire
|
||||
-- in Hazards.step, extended to an entity that carries its own wetness onto
|
||||
-- dry ground.
|
||||
function Conductivity.resistsIgnition(entity)
|
||||
return Conductivity.isWet(entity)
|
||||
end
|
||||
|
||||
return Conductivity
|
||||
Reference in new issue
Block a user