feat: add Corpses.leavesCorpse and nutritionFor
This commit is contained in:
1 parent
4c5bae5a00
commit
41f23f8398
1 file changed
+103
+103
@@ -0,0 +1,103 @@
|
||||
-- Raw meat left behind by a fallen wild monster - a second, turn-costed food
|
||||
-- source alongside a found or bought ration (see Items.food, shop.lua). A
|
||||
-- corpse decays over a fixed number of rounds whether or not the player is
|
||||
-- even on this floor to see it (Corpses.step runs off Game:endPlayerTurn
|
||||
-- exactly like hazards.lua's fire/gas and nodes.lua's vein cooldown); left
|
||||
-- alone past its rot threshold it still butchers in one stroke, but the
|
||||
-- reward turns from a meal into a bout of poisoning instead. Undead crumble
|
||||
-- to dust and leave nothing to carve (see factions.lua's UNDEAD tag), a mimic
|
||||
-- was never alive to begin with (mimics.lua), and a boss's fixed loot drop
|
||||
-- (bosses.lua) is reward enough on its own - none of the three spawn a
|
||||
-- corpse. Corpses are plain data on level.corpses, exactly like
|
||||
-- level.hazards and level.nodes, so they serialize with no special
|
||||
-- save-format handling.
|
||||
|
||||
local Factions = require("factions")
|
||||
local Items = require("items")
|
||||
local Status = require("status")
|
||||
|
||||
local Corpses = {}
|
||||
|
||||
Corpses.DECAY_TURNS = 60
|
||||
Corpses.ROT_TURNS = 25
|
||||
|
||||
Corpses.NUTRITION_MIN = 30
|
||||
Corpses.NUTRITION_MAX = 200
|
||||
Corpses.NUTRITION_PER_HP = 8
|
||||
|
||||
-- Wild monster corpses only: the undead were already dead before the player
|
||||
-- got there, a mimic's "corpse" would just be its loot disguise falling
|
||||
-- apart, and a boss's own scripted drop already covers its reward.
|
||||
function Corpses.leavesCorpse(target)
|
||||
return target.faction ~= Factions.UNDEAD and not target.mimic and not target.isBoss
|
||||
end
|
||||
|
||||
-- Nutrition scales with how tough the felled species was (maxHp, itself
|
||||
-- depth-scaled per monsters.lua), clamped to a fixed band so a late-game
|
||||
-- dragon corpse never trivializes hunger outright.
|
||||
local function nutritionFor(maxHp)
|
||||
local raw = maxHp * Corpses.NUTRITION_PER_HP
|
||||
return math.max(Corpses.NUTRITION_MIN, math.min(Corpses.NUTRITION_MAX, raw))
|
||||
end
|
||||
|
||||
function Corpses.new(target)
|
||||
return {
|
||||
x = target.x, y = target.y,
|
||||
species = target.species, name = target.name,
|
||||
nutrition = nutritionFor(target.maxHp),
|
||||
decay = Corpses.DECAY_TURNS,
|
||||
}
|
||||
end
|
||||
|
||||
-- Appends a fresh corpse for `target` onto `level.corpses`, or does nothing
|
||||
-- for anything Corpses.leavesCorpse rules out.
|
||||
function Corpses.spawn(level, target)
|
||||
if not Corpses.leavesCorpse(target) then return end
|
||||
level.corpses[#level.corpses + 1] = Corpses.new(target)
|
||||
end
|
||||
|
||||
function Corpses.isRotten(corpse)
|
||||
return corpse.decay <= Corpses.ROT_TURNS
|
||||
end
|
||||
|
||||
-- Advances every corpse on the level one round toward fully rotting away,
|
||||
-- exactly like hazards.step and nodes.step - called once per round, not per
|
||||
-- actor, so a seeded run always loses a given corpse on the identical turn
|
||||
-- regardless of how many actors acted that round.
|
||||
function Corpses.step(level)
|
||||
for i = #level.corpses, 1, -1 do
|
||||
local corpse = level.corpses[i]
|
||||
corpse.decay = corpse.decay - 1
|
||||
if corpse.decay <= 0 then table.remove(level.corpses, i) end
|
||||
end
|
||||
end
|
||||
|
||||
-- Drops `corpse` off level.corpses outright - shared by every way of
|
||||
-- spending one (see Corpses.butcher below and scavenging.lua's own harvest),
|
||||
-- so a corpse can never be carved twice over.
|
||||
function Corpses.remove(level, corpse)
|
||||
for i, candidate in ipairs(level.corpses) do
|
||||
if candidate == corpse then
|
||||
table.remove(level.corpses, i)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Carves up `corpse` in one stroke, always spending it whether the meat is
|
||||
-- still good or not. A corpse still inside its rot threshold yields a fresh
|
||||
-- Items.food ration into the player's inventory, eaten later exactly like a
|
||||
-- found one; a corpse butchered past that threshold instead poisons the
|
||||
-- player outright - the corpse itself is just as gone either way.
|
||||
function Corpses.butcher(game, corpse)
|
||||
Corpses.remove(game.level, corpse)
|
||||
if Corpses.isRotten(corpse) then
|
||||
Status.apply(game.player, Status.POISON, 5, 2)
|
||||
return string.format("the %s corpse has rotted; you gag it down and immediately regret it", corpse.name)
|
||||
end
|
||||
local food = Items.food(corpse.name .. " meat", corpse.nutrition)
|
||||
game.player.inventory[#game.player.inventory + 1] = food
|
||||
return string.format("you carve %d nutrition worth of meat from the %s corpse", corpse.nutrition, corpse.name)
|
||||
end
|
||||
|
||||
return Corpses
|
||||
Reference in new issue
Block a user