test: cover tests.hungerEatClampsAMealThatWouldOverflowPastTheCapWith...

This commit is contained in:
dualfroz committed 2026-09-09 17:41:03 +02:00
1 parent c58888f5c0
commit b35beaeee7
1 file changed
+173
+173
View File
@@ -3416,6 +3416,179 @@ function tests.hungerEatBelowTheCeilingBanksNutritionWithNoWaste()
local bloated = Hunger.eat(entity, 100) local bloated = Hunger.eat(entity, 100)
ok(not bloated, "an ordinary meal below the cap is never wasted") ok(not bloated, "an ordinary meal below the cap is never wasted")
eq(entity.satiation, Hunger.HUNGRY_MAX + 100, "the full nutrition value is banked") eq(entity.satiation, Hunger.HUNGRY_MAX + 100, "the full nutrition value is banked")
ok(not Status.has(entity, Status.SLOW), "an ordinary meal never bloats the diner")
end
function tests.hungerEatClampsAMealThatWouldOverflowPastTheCapWithoutBloating()
local entity = { satiation = Hunger.CAP - 50, statuses = {} }
local bloated = Hunger.eat(entity, 400)
ok(not bloated, "a meal eaten while still short of the cap is never wasted, even if it overflows")
eq(entity.satiation, Hunger.CAP, "the surplus past the cap is simply clamped away")
ok(not Status.has(entity, Status.SLOW), "clamping alone never bloats the diner")
end
function tests.gameHungerTorchTickSuppressesRingRegenOnceThePlayerIsWeak()
local game = Game.new(1)
Items.equipRing(Artifacts.create("ring_of_the_tide"), game.player)
game.player.hp = game.player.maxHp - 10
game.player.satiation = Hunger.WEAK_MAX
local before = game.player.hp
game:hungerTorchTick()
eq(game.player.hp, before, "a weak player's ring of the tide grants no regen this turn")
end
function tests.gameUseItemEatingFoodRestoresSatiationAndLogsTheMeal()
local game = stagedGame(1, arena(21, 11), 3, 6)
game.player.satiation = 200
game.player.inventory = { Items.food("ration", 400) }
ok(game:useItem(1), "eating a carried ration succeeds")
eq(game.player.satiation, 600, "the ration's full nutrition is banked")
eq(#game.player.inventory, 0, "the eaten ration leaves the inventory")
ok(contains(game.log:last(), "eat"), "the meal is logged")
end
function tests.gameUseItemEatingFoodWhileAlreadyFullAppliesSlowAndLogsBloat()
local game = stagedGame(1, arena(21, 11), 3, 6)
game.player.satiation = Hunger.CAP
game.player.inventory = { Items.food("ration", 400) }
ok(game:useItem(1), "eating while already full still succeeds")
eq(game.player.satiation, Hunger.CAP, "an overfull meal never lifts satiation past the cap")
ok(Status.has(game.player, Status.SLOW), "eating past the cap leaves the player bloated and slow")
ok(contains(game.log:last(), "bloated"), "the wasted meal is logged as bloating")
end
function tests.reanimationRaiseCreatesAThrallAdjacentToThePlayerAndSpendsTheCorpseAndSanity()
local game = stagedGame(1, arena(21, 11), 5, 5)
local rat = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1)
Corpses.spawn(game.level, rat)
local corpse = game.level.corpses[1]
local sanityBefore = game.player.sanity
local thrall = Reanimation.raise(game, corpse)
ok(thrall ~= nil, "raising a fresh corpse succeeds")
eq(thrall.kind, "thrall", "a raised thrall is tagged with the thrall kind")
eq(math.max(math.abs(thrall.x - 5), math.abs(thrall.y - 5)), 1, "the thrall rises adjacent to the player")
eq(#game.level.corpses, 0, "raising the corpse consumes it")
eq(game.player.sanity, sanityBefore - Reanimation.SANITY_COST, "raising a thrall costs a fixed bite of sanity")
end
function tests.reanimationRaiseRefusesARottenCorpseWithoutSpendingAnything()
local game = stagedGame(1, arena(21, 11), 5, 5)
local rat = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1)
Corpses.spawn(game.level, rat)
game.level.corpses[1].decay = Corpses.ROT_TURNS
local sanityBefore = game.player.sanity
local thrall, line = Reanimation.raise(game, game.level.corpses[1])
eq(thrall, nil, "a rotten corpse cannot be raised")
eq(#game.level.corpses, 1, "a refused raise leaves the corpse in place")
eq(game.player.sanity, sanityBefore, "a refused raise spends no sanity")
ok(contains(line, "rotted"), "the refusal explains the corpse has rotted too far")
end
function tests.reanimationRaiseRefusesWithoutEnoughSanityAndLeavesTheCorpseAlone()
local game = stagedGame(1, arena(21, 11), 5, 5)
local rat = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1)
Corpses.spawn(game.level, rat)
game.player.sanity = Reanimation.SANITY_COST - 1
local thrall, line = Reanimation.raise(game, game.level.corpses[1])
eq(thrall, nil, "raising fails when sanity is too low to pay the cost")
eq(#game.level.corpses, 1, "a refused raise for lack of sanity leaves the corpse untouched")
ok(contains(line, "mind"), "the refusal blames the player's own frail grip on their mind")
end
function tests.reanimationRaiseScalesThrallStatsDownFromTheOriginSpecies()
local game = stagedGame(1, arena(21, 11), 5, 5)
local troll = Monsters.create(Monsters.byKey.troll, 1, 5, 5, 5)
Corpses.spawn(game.level, troll)
local thrall = Reanimation.raise(game, game.level.corpses[1])
eq(thrall.hp, math.floor(Monsters.byKey.troll.hp * Reanimation.HP_FRACTION),
"a thrall's hp is a fixed fraction of the living species' own hp")
eq(thrall.attack, Monsters.byKey.troll.attack - Reanimation.ATTACK_PENALTY,
"a thrall's attack is blunted by a fixed amount from the living species")
eq(thrall.defense, Monsters.byKey.troll.defense, "a thrall keeps the living species' own defense")
end
function tests.reanimationRaiseFallsBackToAPlainSkeletonForAnUnrecognisedSpecies()
local game = stagedGame(1, arena(21, 11), 5, 5)
game.level.corpses[1] = { x = 5, y = 5, species = "unknown-thing", name = "unknown-thing", decay = Corpses.DECAY_TURNS }
local thrall = Reanimation.raise(game, game.level.corpses[1])
ok(thrall ~= nil, "a corpse of an unrecognised species can still be raised")
eq(thrall.hp, 8, "an unrecognised species falls back to a flat skeleton hp")
eq(thrall.attack, 2, "an unrecognised species falls back to a flat skeleton attack")
end
function tests.reanimationRaiseRetiresAnEarlierThrallWhenRaisingAnother()
local game = stagedGame(1, arena(21, 11), 5, 5)
local rat1 = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1)
Corpses.spawn(game.level, rat1)
local first = Reanimation.raise(game, game.level.corpses[1])
local rat2 = Monsters.create(Monsters.byKey.rat, 2, 6, 5, 1)
Corpses.spawn(game.level, rat2)
local second = Reanimation.raise(game, game.level.corpses[1])
ok(not first:isAlive(), "raising a new thrall retires the earlier one")
ok(second:isAlive(), "the freshly raised thrall is alive")
local alive = 0
for _, e in ipairs(game.level.entities) do
if e.kind == "thrall" and e:isAlive() then alive = alive + 1 end
end
eq(alive, 1, "only one thrall is ever alive at a time")
end
function tests.reanimationActAttacksAnAdjacentHostileMonsterAndCanKillIt()
local game = stagedGame(1, arena(21, 11), 5, 5)
local chieftain = Monsters.create(Monsters.byKey.chieftain, 1, 5, 5, 6)
Corpses.spawn(game.level, chieftain)
local thrall = Reanimation.raise(game, game.level.corpses[1])
local rat = Monsters.create(Monsters.byKey.rat, 50, thrall.x + 1, thrall.y, 1)
game.level.entities[#game.level.entities + 1] = rat
local xpBefore = game.player.xp
Reanimation.act(game, thrall)
eq(rat.hp, 0, "a thrall's hit kills the weak rat standing next to it")
ok(game.player.xp > xpBefore, "the player is credited xp for its thrall's kill")
end
function tests.reanimationActAdvancesOnAVisibleMonsterItCannotYetReach()
local game = stagedGame(1, arena(21, 11), 5, 5)
local rat0 = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1)
Corpses.spawn(game.level, rat0)
local thrall = Reanimation.raise(game, game.level.corpses[1])
local orc = Monsters.create(Monsters.byKey.orc, 50, thrall.x + 5, thrall.y, 1)
game.level.entities[#game.level.entities + 1] = orc
local startX = thrall.x
Reanimation.act(game, thrall)
ok(thrall.x > startX, "the thrall closes the distance to a monster it can see but not reach")
end
function tests.reanimationActIgnoresAMonsterBeyondItsSightRadius()
local game = stagedGame(1, arena(21, 11), 3, 3)
local rat0 = Monsters.create(Monsters.byKey.rat, 1, 3, 3, 1)
Corpses.spawn(game.level, rat0)
local thrall = Reanimation.raise(game, game.level.corpses[1])
local orc = Monsters.create(Monsters.byKey.orc, 50, 19, 9, 1)
game.level.entities[#game.level.entities + 1] = orc
local startX, startY = thrall.x, thrall.y
Reanimation.act(game, thrall)
eq(thrall.x, startX, "a monster far outside sight range does not pull the thrall off course (x)")
eq(thrall.y, startY, "a monster far outside sight range does not pull the thrall off course (y)")
end
-- Same gap as companionActBlindedCompanionOnlyDetectsAnAdjacentHostileNot
-- OneWithinItsSightRadius, but for Reanimation.act's own nearestHostile.
function tests.reanimationActBlindedThrallOnlyDetectsAnAdjacentHostileNotOneWithinItsSightRadius()
local game = stagedGame(1, arena(21, 11), 5, 5)
local rat0 = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1)
Corpses.spawn(game.level, rat0)
local thrall = Reanimation.raise(game, game.level.corpses[1])
local farOrc = Monsters.create(Monsters.byKey.orc, 50, thrall.x + 5, thrall.y, 1)
game.level.entities[#game.level.entities + 1] = farOrc
Status.apply(thrall, Status.BLIND, 6, 1)
local startX = thrall.x
Reanimation.act(game, thrall)
eq(thrall.x, startX, "a blinded thrall loses a sighting well within its listed sight radius")
local rat = Monsters.create(Monsters.byKey.rat, 51, thrall.x + 1, thrall.y, 1)
game.level.entities[#game.level.entities + 1] = rat
local ratHpBefore = rat.hp
Reanimation.act(game, thrall)
end end
local order = { local order = {