test: cover tests.gameManualStepEndsTheTurnOnlyWhenTheCommandActually...
This commit is contained in:
1 parent
808016f925
commit
851e57ec97
1 file changed
+173
+173
@@ -13796,6 +13796,179 @@ end
|
||||
|
||||
-- Game:manualStep - the turn-cycle integration around commands.lua.
|
||||
|
||||
function tests.gameManualStepEndsTheTurnOnlyWhenTheCommandActuallySucceeds()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
local turnBefore = game.turn
|
||||
local consumesTurn = game:manualStep("north")
|
||||
eq(consumesTurn, true, "a valid move spends the turn")
|
||||
eq(game.turn, turnBefore + 1, "Game:manualStep advances the turn counter on a successful command")
|
||||
end
|
||||
|
||||
function tests.gameManualStepLeavesTheTurnCounterUntouchedOnARejectedCommand()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
local turnBefore = game.turn
|
||||
local consumesTurn, message = game:manualStep("nonsense")
|
||||
eq(consumesTurn, false, "an unrecognized command reports failure")
|
||||
ok(message ~= nil, "the rejection carries a reason")
|
||||
eq(game.turn, turnBefore, "the turn counter never advances on a rejected command")
|
||||
end
|
||||
|
||||
function tests.gameManualStepIsIdempotentAcrossRepeatedRejectedCommandsWithinOneUnspentTurn()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
Status.apply(game.player, Status.POISON, 5, 5)
|
||||
game:manualStep("nonsense")
|
||||
local hpAfterFirst = game.player.hp
|
||||
local durationAfterFirst = game.player.statuses[1].duration
|
||||
game:manualStep("gibberish")
|
||||
eq(game.player.hp, hpAfterFirst,
|
||||
"a second rejected command within the same unspent turn does not re-tick poison damage")
|
||||
eq(game.player.statuses[1].duration, durationAfterFirst,
|
||||
"...nor does it re-decrement the poison's own remaining duration")
|
||||
end
|
||||
|
||||
function tests.gameManualStepStunnedPlayerIgnoresTheTypedCommandButStillSpendsTheTurn()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
-- Duration 2, not 1: Status.tick (run inside beginTurn, before the skip
|
||||
-- check) decrements duration first, so a duration-1 stun would already
|
||||
-- have expired by the time Status.skipsTurn looks for it this same turn.
|
||||
Status.apply(game.player, Status.STUN, 2, 0)
|
||||
local turnBefore = game.turn
|
||||
local px, py = game.player.x, game.player.y
|
||||
local consumesTurn = game:manualStep("north")
|
||||
eq(consumesTurn, true, "a stunned turn is spent regardless of the typed command")
|
||||
eq(game.player.x, px, "the player never actually moves while stunned")
|
||||
eq(game.player.y, py, "the player never actually moves while stunned")
|
||||
eq(game.turn, turnBefore + 1, "the stunned turn still advances the turn counter")
|
||||
end
|
||||
|
||||
function tests.gameManualStepHaltsWithoutSpendingATurnOnceTheRunIsAlreadyWon()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
game.won = true
|
||||
local turnBefore = game.turn
|
||||
local consumesTurn = game:manualStep("north")
|
||||
eq(consumesTurn, false, "a manual command after victory spends no turn")
|
||||
eq(game.turn, turnBefore, "the turn counter never advances once the run is already won")
|
||||
end
|
||||
|
||||
-- Game:autoExplore / commands.lua's "explore" - autoexplore, repeating
|
||||
-- Game:exploreStep turn after turn inside a single call instead of forcing
|
||||
-- the player to retype "explore" once per step, and stopping itself the
|
||||
-- instant something worth their attention shows up.
|
||||
|
||||
function tests.gameAutoExploreWalksTowardUnexploredTerrainUntilTheWholeReachableAreaIsSeen()
|
||||
local level = arena(25, 15)
|
||||
local game = stagedGame(1, level, 3, 3)
|
||||
local turnBefore = game.turn
|
||||
local consumesTurn, message = game:autoExplore()
|
||||
eq(consumesTurn, true, "a run with real ground left to cover spends turns")
|
||||
eq(message, "you have finished exploring", "a completed run reports it finished")
|
||||
ok(game.turn > turnBefore, "autoExplore actually advances the turn counter")
|
||||
local map = level.map
|
||||
local allSeen = true
|
||||
for y = 2, 14 do
|
||||
for x = 2, 24 do
|
||||
if not level.explored[map:index(x, y)] then allSeen = false end
|
||||
end
|
||||
end
|
||||
ok(allSeen, "every walkable cell in the reachable room ends up explored")
|
||||
end
|
||||
|
||||
function tests.gameAutoExploreReportsNothingLeftWithoutSpendingATurnWhenAlreadyFullyExplored()
|
||||
local game = stagedGame(1, arena(11, 9), 5, 5)
|
||||
local turnBefore = game.turn
|
||||
local consumesTurn, message = game:autoExplore()
|
||||
eq(consumesTurn, false, "a room already fully visible from the start spends no turn")
|
||||
eq(message, "there is nothing left to explore", "the refusal explains why")
|
||||
eq(game.turn, turnBefore, "the turn counter never advances")
|
||||
end
|
||||
|
||||
function tests.gameAutoExploreHaltsWithoutSpendingATurnOnceTheRunIsAlreadyWon()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
game.won = true
|
||||
local turnBefore = game.turn
|
||||
local consumesTurn, message = game:autoExplore()
|
||||
eq(consumesTurn, false, "an autoexplore attempt after victory spends no turn")
|
||||
eq(message, "there is nothing left to do", "the refusal matches Game:manualStep's own halted message")
|
||||
eq(game.turn, turnBefore, "the turn counter never advances once the run is already won")
|
||||
end
|
||||
|
||||
function tests.gameAutoExploreStopsTheInstantThePlayerTakesDamage()
|
||||
local game = stagedGame(1, arena(25, 15), 3, 3)
|
||||
Status.apply(game.player, Status.POISON, 5, 4)
|
||||
local turnBefore = game.turn
|
||||
local hpBefore = game.player.hp
|
||||
local consumesTurn, message = game:autoExplore()
|
||||
eq(consumesTurn, true, "the interrupted turn itself still spends")
|
||||
eq(message, "you stop exploring - you have taken damage", "the interrupt explains itself")
|
||||
eq(game.turn, turnBefore + 1, "exactly the one turn that took the damage was spent")
|
||||
eq(game.player.hp, hpBefore - 4, "the poison tick's own damage lands normally before the interrupt fires")
|
||||
end
|
||||
|
||||
function tests.gameAutoExploreRefusesToStartWhileAHostileIsAlreadyInSight()
|
||||
local level = arena(21, 11)
|
||||
local game = stagedGame(1, level, 5, 5)
|
||||
local orc = Monsters.create(Monsters.byKey.orc, 1, 7, 5, 1)
|
||||
orc.awareness = Stealth.DORMANT
|
||||
level.entities = { orc }
|
||||
game:computeVisible()
|
||||
local turnBefore = game.turn
|
||||
local consumesTurn, message = game:autoExplore()
|
||||
eq(consumesTurn, false, "a hostile already visible refuses to even start")
|
||||
eq(message, "a monster is already in sight", "the refusal names the reason")
|
||||
eq(game.turn, turnBefore, "the turn counter never advances")
|
||||
end
|
||||
|
||||
-- The room is wide open (not a corridor) on purpose: Game:exploreStep's own
|
||||
-- BFS treats an entity's tile as unreachable (see game.lua), so a monster
|
||||
-- sitting in a one-wide corridor would simply wall off the far end and
|
||||
-- "nothing left to explore" would fire before the player ever got close
|
||||
-- enough to see it - a real, separately-confirmed property of exploreStep,
|
||||
-- not the thing this test means to exercise. A big room lets the BFS route
|
||||
-- around the orc's single tile instead, so sight is what actually stops
|
||||
-- this run. Both the room's height and the orc's row sit well within the
|
||||
-- player's initial vision radius (8), so the whole approach plays out along
|
||||
-- a single row and Stealth.WAKE_RADIUS (6, smaller than that same vision
|
||||
-- radius) guarantees the orc is seen before it is ever close enough to roll
|
||||
-- a wake of its own (see stealth.lua's canSpot) - the interrupt below is
|
||||
-- proven to fire on sight alone, never on contact, without relying on RNG.
|
||||
function tests.gameAutoExploreStopsTheMomentAHostileComesIntoSightWithoutEngagingIt()
|
||||
local level = arena(30, 11)
|
||||
local game = stagedGame(2, level, 3, 6)
|
||||
local orc = Monsters.create(Monsters.byKey.orc, 1, 25, 6, 1)
|
||||
orc.awareness = Stealth.DORMANT
|
||||
level.entities = { orc }
|
||||
local turnBefore = game.turn
|
||||
local hpBefore = game.player.hp
|
||||
local orcHpBefore = orc.hp
|
||||
local consumesTurn, message = game:autoExplore()
|
||||
eq(consumesTurn, true, "real ground was covered before the interrupt fired")
|
||||
eq(message, "you stop exploring - a monster is in sight", "the interrupt names a sighted hostile")
|
||||
eq(game.turn, turnBefore + 14, "exactly the steps needed to bring the orc within vision range 8 were taken")
|
||||
eq(game.player.hp, hpBefore, "no damage was taken - explore stops on sight, not on contact")
|
||||
eq(orc.hp, orcHpBefore, "the orc is only spotted, never engaged")
|
||||
ok(game.visible[level.map:index(orc.x, orc.y)], "the orc really is the thing that got seen")
|
||||
eq(orc.x, 25, "the still-dormant orc never took a single step of its own")
|
||||
eq(orc.awareness, Stealth.DORMANT, "and never woke up at all")
|
||||
end
|
||||
|
||||
function tests.gameManualStepDrivenExploreSpendsExactlyAsManyTurnsAsCallingGameAutoExploreDirectly()
|
||||
local direct = stagedGame(9, arena(25, 15), 3, 3)
|
||||
direct:autoExplore()
|
||||
local viaManualStep = stagedGame(9, arena(25, 15), 3, 3)
|
||||
viaManualStep:manualStep("explore")
|
||||
eq(viaManualStep.turn, direct.turn,
|
||||
"routing explore through Game:manualStep must not end the turn a second time on top of what " ..
|
||||
"autoExplore already closed itself")
|
||||
end
|
||||
|
||||
function tests.commandsApplyExploreDrivesGameAutoExploreAndReturnsItsResult()
|
||||
local game = stagedGame(1, arena(11, 9), 5, 5)
|
||||
local consumesTurn, message = Commands.apply(game, "explore")
|
||||
eq(consumesTurn, false, "a room already fully visible from the start spends no turn")
|
||||
eq(message, "there is nothing left to explore", "the command surfaces Game:autoExplore's own message verbatim")
|
||||
end
|
||||
|
||||
function tests.commandsApplyHelpMentionsExplore()
|
||||
end
|
||||
|
||||
local order = {
|
||||
|
||||
Reference in new issue
Block a user