107 lines
4.3 KiB
Lua
107 lines
4.3 KiB
Lua
-- Cross-run bones: the moment a run ends in death (see warren.lua's --meta
|
|
-- path and Game:graveEntry), a grave entry is appended to meta.lua's
|
|
-- persistent save - what the fallen was still carrying, and how deep they
|
|
-- got. From the very next run on, Builder.build may roll one of those
|
|
-- graves onto a floor at or shallower than where it was dug (bones drift
|
|
-- upward over time, never down - see Graves.eligibleFor) as an inert
|
|
-- floorItem, drawn dead last of all so a run without any recorded deaths
|
|
-- (or one played without --meta at all) makes no extra rng draw and its
|
|
-- floor layouts stay byte-identical to before this module existed.
|
|
--
|
|
-- Unlike mimics.lua's disguise, a grave marker is honest about what it is -
|
|
-- disturbing it (Game:pickupHere, mirroring Game:triggerMimic) wakes a
|
|
-- vengeful shade rather than pretending to be ordinary loot. The shade's
|
|
-- hp and attack scale with how much the fallen was carrying at death, and
|
|
-- felling it in turn pays out a fraction of that same hoard - a richer
|
|
-- death makes for a tougher fight and a richer reward, but always strictly
|
|
-- less than what was actually lost.
|
|
|
|
local Entity = require("entity")
|
|
local Factions = require("factions")
|
|
|
|
local Graves = {}
|
|
|
|
-- Oldest graves fall off once a save accumulates more than this many, so a
|
|
-- long-lived meta file never grows without bound.
|
|
Graves.MAX_RECORDED = 20
|
|
|
|
-- Appends `entry` (see Game:graveEntry) to meta.graves, most recent first,
|
|
-- trimming anything past Graves.MAX_RECORDED.
|
|
function Graves.record(meta, entry)
|
|
meta.graves = meta.graves or {}
|
|
table.insert(meta.graves, 1, entry)
|
|
while #meta.graves > Graves.MAX_RECORDED do
|
|
table.remove(meta.graves)
|
|
end
|
|
end
|
|
|
|
-- Graves eligible to be rolled onto `depth`: any dug at that depth or
|
|
-- deeper. A grave dug on floor 7 can still turn up waiting on floor 4, but
|
|
-- a floor 2 grave never surfaces on floor 7 - bones drift shallower with
|
|
-- time, never the other way around.
|
|
function Graves.eligibleFor(graves, depth)
|
|
local eligible = {}
|
|
if not graves then return eligible end
|
|
for _, grave in ipairs(graves) do
|
|
if grave.depth >= depth then eligible[#eligible + 1] = grave end
|
|
end
|
|
return eligible
|
|
end
|
|
|
|
-- The inert floorItem a builder drops for a rolled grave: visibly a grave
|
|
-- from the moment it is seen (no disguise to unmask), carrying just enough
|
|
-- of the fallen's own data for Graves.reveal to build their shade later.
|
|
function Graves.floorItem(grave)
|
|
return {
|
|
kind = "grave", grave = true, name = "a weathered grave marker", glyph = "b",
|
|
graveClass = grave.className, graveGold = grave.gold, graveEssence = grave.essence,
|
|
}
|
|
end
|
|
|
|
-- The hostile shade a grave marker becomes the instant it is disturbed:
|
|
-- hp and attack both scale with the wealth it is guarding, capped so even
|
|
-- the richest grave never spawns something the player cannot fight back
|
|
-- against. Mirrors Mimics.reveal's shape (a fresh monster entity, tagged so
|
|
-- awardKill and corpses.lua both know to treat it specially).
|
|
function Graves.reveal(id, x, y, ground)
|
|
local gold = ground.graveGold or 0
|
|
local essence = ground.graveEssence or 0
|
|
local wealth = gold + essence * 2
|
|
local bonusHp = math.min(20, wealth // 8)
|
|
local bonusAttack = math.min(6, wealth // 30)
|
|
local entity = Entity.new({
|
|
id = id, x = x, y = y,
|
|
glyph = "G", name = "vengeful shade", kind = "monster",
|
|
species = "shade", ai = "chase",
|
|
hp = 10 + bonusHp, maxHp = 10 + bonusHp,
|
|
attack = 4 + bonusAttack, defense = 1, speed = 105,
|
|
})
|
|
entity.cooldown = 0
|
|
entity.faction = Factions.UNDEAD
|
|
entity.grave = true
|
|
entity.graveClass = ground.graveClass
|
|
entity.graveGold = gold
|
|
entity.graveEssence = essence
|
|
return entity
|
|
end
|
|
|
|
-- Flat xp reward for felling a revealed shade - kept separate from
|
|
-- Monsters.xpFor exactly like Mimics.xpFor, since a shade is never a
|
|
-- member of the wild bestiary Monsters.forDepth draws its per-floor
|
|
-- population from.
|
|
function Graves.xpFor()
|
|
return 12
|
|
end
|
|
|
|
-- What a felled shade actually leaves behind: half the gold and a third of
|
|
-- the essence the fallen was carrying at the moment of death - the rest is
|
|
-- gone for good, so a grave is always a net loss across the two runs it
|
|
-- touches, never a way to launder wealth forward.
|
|
function Graves.lootFor(entity)
|
|
local gold = (entity.graveGold or 0) // 2
|
|
local essence = (entity.graveEssence or 0) // 3
|
|
return gold, essence
|
|
end
|
|
|
|
return Graves
|