test: cover tests.choosePlayerActionEntersAWellHealedRiftUnderfootBut...

This commit is contained in:
dualfroz committed 2026-09-12 21:13:48 +02:00
1 parent bb27e6ef22
commit f710e182dd
1 file changed
+173
+173
View File
@@ -12760,6 +12760,179 @@ function tests.gameEnterRiftAndExitRiftBothCarryALivingCompanionAcrossTheCrossin
eq(strandedInPocket, 0, "the companion is not left stranded on the pocket floor either")
end
function tests.choosePlayerActionEntersAWellHealedRiftUnderfootButSkipsItWhenBadlyHurt()
local hurt = stagedGame(1, arena(21, 11), 5, 5)
hurt.level.rifts = { { x = 5, y = 5 } }
hurt.player.inventory[#hurt.player.inventory + 1] = Items.riftstone()
hurt.player.hp = math.floor(hurt.player.maxHp * 0.5)
hurt:choosePlayerAction()
eq(hurt.rift or false, false, "the autopilot never spends a shard while badly hurt")
local healthy = stagedGame(1, arena(21, 11), 5, 5)
healthy.level.rifts = { { x = 5, y = 5 } }
healthy.player.inventory[#healthy.player.inventory + 1] = Items.riftstone()
healthy:choosePlayerAction()
eq(healthy.rift, true, "the autopilot crosses a rift underfoot on sight while at good health")
end
function tests.choosePlayerActionStepsThroughAndThenExitsARiftUnderAutopilot()
local game = stagedGame(1, arena(21, 11), 5, 5)
game.level.rifts = { { x = 5, y = 5 } }
game.player.inventory[#game.player.inventory + 1] = Items.riftstone()
game:choosePlayerAction()
ok(game.rift, "the autopilot has crossed into the pocket floor")
game.player.x, game.player.y = game.level.downX, game.level.downY
game:choosePlayerAction()
eq(game.rift, false, "standing at the pocket floor's own exit, the autopilot immediately steps back out")
end
function tests.renderDrawsARiftGlyphWhenVisible()
local rendered = stagedGame(1, arena(21, 11), 3, 6)
rendered.level.rifts = { { x = 5, y = 6 } }
rendered:computeVisible()
ok(contains(rendered:render(), "Q"), "a rift on a visible cell renders as its glyph")
end
function tests.gameSaveLoadRoundTripsAnActiveRiftCrossing()
local game = Game.new(9)
game.level.rifts = { { x = game.player.x, y = game.player.y } }
game.player.inventory[#game.player.inventory + 1] = Items.riftstone()
ok(game:enterRift(), "the staged rift crossing succeeds")
local path = os.tmpname()
game:save(path)
local restored = Game.load(path)
os.remove(path)
eq(restored.rift, true, "the rift flag survives a save/load round trip")
eq(restored.depth, game.depth, "depth survives the round trip")
eq(table.concat(restored.level.map.tiles), table.concat(game.level.map.tiles),
"the active pocket floor's map survives the round trip")
eq(restored.riftReturn.x, game.riftReturn.x, "the remembered return position survives the round trip")
eq(restored.riftReturn.y, game.riftReturn.y, "the remembered return position survives the round trip")
eq(restored.riftReturn.depth, game.riftReturn.depth, "the remembered return depth survives the round trip")
restored.player.x, restored.player.y = restored.level.downX, restored.level.downY
ok(restored:exitRift(), "exiting still works correctly after a save/load round trip")
eq(restored.rift, false, "the rift flag clears after exiting post-load")
end
function tests.gameSaveLoadRoundTripsAnActiveHordeArena()
local game = Game.new(11)
game:startHorde()
-- Clear wave one so the round trip has to prove more than just "wave one
-- survives" - the wave counter must actually be the one Horde.tick
-- advanced to, not always reset back to its Horde.newState() default.
for _, entity in ipairs(game.level.entities) do entity.hp = 0 end
game:cleanupDead()
game:endPlayerTurn()
eq(game.horde.wave, 2, "the staged run has advanced to wave two before saving")
local path = os.tmpname()
game:save(path)
local restored = Game.load(path)
os.remove(path)
ok(restored.horde, "an active horde run survives a save/load round trip at all")
eq(restored.horde.wave, 2, "the exact wave in progress survives the round trip")
eq(restored.level.generator, "horde", "the restored level is still tagged as the arena")
eq(table.concat(restored.level.map.tiles), table.concat(game.level.map.tiles),
"the arena's own map survives the round trip rather than falling back to the abandoned floor")
eq(#restored.level.entities, #game.level.entities, "wave two's monster count survives the round trip")
eq(restored.player.x, game.player.x, "the player's arena position survives the round trip")
eq(restored.player.y, game.player.y, "the player's arena position survives the round trip")
for _, entity in ipairs(restored.level.entities) do entity.hp = 0 end
restored:cleanupDead()
restored:endPlayerTurn()
eq(restored.horde.wave, 3, "the restored run's Horde.tick still advances waves normally after loading")
end
function tests.gameSaveLoadOfAnOrdinaryRunNeverGainsAHordeField()
local game = Game.new(3)
local path = os.tmpname()
game:save(path)
local restored = Game.load(path)
os.remove(path)
eq(restored.horde, nil, "a run that never entered the arena has no horde field after a round trip")
eq(restored.level.generator, game.level.generator,
"an ordinary floor's own generator survives the round trip unaffected by the horde snapshot fields")
end
-- commands.lua: the typed-command parser/dispatcher layered on top of the
-- same granular Game:<action> methods choosePlayerAction already drives, and
-- Game:manualStep, its integration into the turn cycle (see game.lua's
-- Game:beginTurn/autoStep for the shared pre-turn head both share).
function tests.commandsParseSplitsAVerbAndArgsLowercasedAndTrimmed()
local parsed = Commands.parse(" ThRoW 3 ")
eq(parsed.verb, "throw", "the verb is lowercased")
eq(parsed.args[1], "3", "the remaining token becomes an arg")
eq(Commands.parse(""), nil, "an empty line parses to nil")
eq(Commands.parse(" "), nil, "an all-whitespace line parses to nil")
end
function tests.commandsApplyRejectsAnUnknownVerbWithoutSpendingATurn()
local game = stagedGame(1, arena(21, 11), 5, 5)
local consumesTurn, message = Commands.apply(game, "dance")
eq(consumesTurn, false, "an unrecognized verb spends no turn")
ok(contains(message, "unknown command"), "the message names the bad verb")
end
function tests.commandsApplyMovingIntoAWallFailsWithoutSpendingATurn()
local game = stagedGame(1, arena(21, 11), 2, 2)
local consumesTurn, message = Commands.apply(game, "north")
eq(consumesTurn, false, "walking into the arena's own boundary wall spends no turn")
eq(message, "you cannot go that way", "the failure explains itself")
eq(game.player.x, 2, "the player never actually moves on a rejected step")
eq(game.player.y, 2, "the player never actually moves on a rejected step")
end
function tests.commandsApplyMovementBumpsIntoAnAdjacentHostileAsAnAttack()
local game = stagedGame(1, arena(21, 11), 5, 5)
local rat = Monsters.create(Monsters.byKey.rat, 1, 6, 5, 1)
rat.hp = 20
game.level.entities = { rat }
local consumesTurn = Commands.apply(game, "east")
eq(consumesTurn, true, "bumping into a hostile spends the turn")
eq(game.player.x, 5, "the player does not step onto the monster's own tile")
ok(rat.hp < 20, "the bump attack actually lands through Game:movePlayer")
end
function tests.commandsApplyAttackAliasesMovementIntoAnExplicitDirection()
local game = stagedGame(1, arena(21, 11), 5, 5)
local rat = Monsters.create(Monsters.byKey.rat, 1, 6, 5, 1)
rat.hp = 20
game.level.entities = { rat }
local consumesTurn = Commands.apply(game, "attack east")
eq(consumesTurn, true, "attack <dir> spends the turn")
ok(rat.hp < 20, "attack <dir> lands the same bump attack a plain direction would")
local rejected, message = Commands.apply(game, "attack sideways")
eq(rejected, false, "attack with an unrecognized direction word spends no turn")
eq(message, "attack which direction?", "the failure names what is missing")
end
function tests.commandsApplyWaitAlwaysSucceedsAndSpendsTheTurn()
local game = stagedGame(1, arena(21, 11), 5, 5)
local consumesTurn, message = Commands.apply(game, "wait")
eq(consumesTurn, true, "waiting always spends the turn")
eq(message, "you wait", "waiting reports itself")
end
function tests.commandsApplyPickupSweepsFloorItemsAtThePlayersFeetAndSpendsTheTurn()
local game = stagedGame(1, arena(21, 11), 5, 5)
game.level.floorItems = { { x = 5, y = 5, item = Items.gold(15) } }
local goldBefore = game.player.gold
local consumesTurn = Commands.apply(game, "pickup")
eq(consumesTurn, true, "pickup always spends the turn")
eq(game.player.gold, goldBefore + 15, "gold sitting on the player's own tile is swept up")
eq(#game.level.floorItems, 0, "the picked-up item leaves the floor")
end
function tests.commandsApplyUseDrinksAPotionByInventoryIndexAndSpendsTheTurn()
local game = stagedGame(1, arena(21, 11), 5, 5)
local before = #game.player.inventory
local consumesTurn = Commands.apply(game, "use 1")
eq(consumesTurn, true, "using a valid inventory item spends the turn")
eq(#game.player.inventory, before - 1, "the consumed potion leaves the inventory")
end
end
local order = {
"rngDeterminism", "mapDeterminism", "tilesRegistry", "fovKnownScene", "losSightLine",
"astarPathing", "combatResolution", "itemsAndEquipment", "affixes", "statusEffects",