test: cover tests.branchesBuildNeverPlacesAChasmOrTheWinningAmulet
This commit is contained in:
1 parent
f1dc06a5fc
commit
bb27e6ef22
1 file changed
+173
+173
@@ -12585,6 +12585,179 @@ function tests.branchesBuildIsDeterministicAndGeneratesDeeperThanItsOrigin()
|
|||||||
local b = Branches.build(11, 6, {})
|
local b = Branches.build(11, 6, {})
|
||||||
eq(table.concat(a.map.tiles), table.concat(b.map.tiles),
|
eq(table.concat(a.map.tiles), table.concat(b.map.tiles),
|
||||||
"the same world seed and origin depth always build the same pocket floor")
|
"the same world seed and origin depth always build the same pocket floor")
|
||||||
|
eq(a.depth, 6 + Branches.DEPTH_BONUS, "a pocket floor is generated at its origin depth plus the fixed bonus")
|
||||||
|
local c = Branches.build(11, 7, {})
|
||||||
|
ok(table.concat(a.map.tiles) ~= table.concat(c.map.tiles), "a different origin depth builds a different pocket floor")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.branchesBuildNeverPlacesAChasmOrTheWinningAmulet()
|
||||||
|
for seed = 1, 6 do
|
||||||
|
for depth = 6, 10 do
|
||||||
|
local level = Branches.build(seed, depth, {})
|
||||||
|
for _, glyph in ipairs(level.map.tiles) do
|
||||||
|
ok(glyph ~= Tiles.CHASM, "a pocket floor never generates a chasm with nowhere lower to fall")
|
||||||
|
end
|
||||||
|
for _, ground in ipairs(level.floorItems) do
|
||||||
|
ok(ground.item.kind ~= Items.AMULET, "a pocket floor never generates the winning amulet")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.builderPlacesARiftShardFromDepthFourAndARiftFromDepthSix()
|
||||||
|
local shardLevel = Builder.build(4, Rng.new(1), {})
|
||||||
|
local foundShard = false
|
||||||
|
for _, ground in ipairs(shardLevel.floorItems) do
|
||||||
|
if ground.item.kind == Items.RIFTSTONE then foundShard = true end
|
||||||
|
end
|
||||||
|
ok(foundShard, "this seed's depth-4 floor places a rift shard")
|
||||||
|
|
||||||
|
local riftLevel = Builder.build(6, Rng.new(1), {})
|
||||||
|
ok(#riftLevel.rifts >= 1, "this seed's depth-6 floor tears open a rift")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.riftsAndShardsStayOffFloorsAboveTheirOwnThreshold()
|
||||||
|
for seed = 1, 6 do
|
||||||
|
for depth = 1, 3 do
|
||||||
|
local level = Builder.build(depth, Rng.new(seed), {})
|
||||||
|
for _, ground in ipairs(level.floorItems) do
|
||||||
|
ok(ground.item.kind ~= Items.RIFTSTONE, "no rift shard appears above depth 4")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for depth = 1, 5 do
|
||||||
|
local level = Builder.build(depth, Rng.new(seed), {})
|
||||||
|
eq(#level.rifts, 0, "no rift appears above depth 6")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.riftFieldsSurviveALevelSerializationRoundTrip()
|
||||||
|
local level = arena(21, 11)
|
||||||
|
level.rifts = { { x = 6, y = 5 } }
|
||||||
|
eq(level:riftAt(6, 5), level.rifts[1], "Level:riftAt finds the rift by position")
|
||||||
|
ok(level:riftAt(1, 1) == nil, "Level:riftAt finds nothing off-position")
|
||||||
|
local restored = Level.fromData(level:toData())
|
||||||
|
eq(#restored.rifts, 1, "a rift survives a level round trip")
|
||||||
|
eq(restored.rifts[1].x, 6, "the rift's position survives a round trip")
|
||||||
|
eq(restored:riftAt(6, 5), restored.rifts[1], "Level:riftAt finds the restored rift by position")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.gameEnterRiftFailsWithoutARiftUnderfootOrWithoutAShard()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||||
|
ok(not game:enterRift(), "entering fails with no rift on the current cell")
|
||||||
|
game.level.rifts = { { x = 5, y = 5 } }
|
||||||
|
ok(not game:enterRift(), "a first crossing fails without a carried shard")
|
||||||
|
eq(game.rift or false, false, "a failed crossing never sets the rift flag")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.gameEnterRiftSpendsTheShardAndSwapsToTheCachedPocketFloor()
|
||||||
|
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()
|
||||||
|
local invBefore = #game.player.inventory
|
||||||
|
local mainLevel = game.level
|
||||||
|
ok(game:enterRift(), "entering a rift underfoot with a shard in hand succeeds")
|
||||||
|
eq(#game.player.inventory, invBefore - 1, "the shard is spent on the first crossing")
|
||||||
|
eq(game.rift, true, "the rift flag is set while across a crossing")
|
||||||
|
eq(game.level.depth, game.depth + Branches.DEPTH_BONUS, "the pocket floor is generated deeper than the origin")
|
||||||
|
eq(game.player.x, game.level.upX, "the player lands on the pocket floor's own entry point")
|
||||||
|
eq(game.player.y, game.level.upY, "the player lands on the pocket floor's own entry point")
|
||||||
|
ok(game.riftReturn ~= nil, "a return point is remembered")
|
||||||
|
eq(game.riftReturn.level, mainLevel, "the return point remembers the exact main-track level object")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.gameDescendAndAscendAreBothBlockedWhileAcrossARift()
|
||||||
|
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:enterRift()
|
||||||
|
local depthBefore, levelBefore = game.depth, game.level
|
||||||
|
game.player.x, game.player.y = game.level.downX, game.level.downY
|
||||||
|
ok(not game:descend(), "descend refuses to fire while the player is across a rift")
|
||||||
|
game.player.x, game.player.y = game.level.upX, game.level.upY
|
||||||
|
ok(not game:ascend(), "ascend refuses to fire while the player is across a rift")
|
||||||
|
eq(game.depth, depthBefore, "depth is untouched by either refusal")
|
||||||
|
eq(game.level, levelBefore, "the active level is untouched by either refusal")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.gameExitRiftRestoresTheExactMainTrackLevelDepthAndPosition()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||||
|
local mainLevel = game.level
|
||||||
|
game.level.rifts = { { x = 5, y = 5 } }
|
||||||
|
game.player.inventory[#game.player.inventory + 1] = Items.riftstone()
|
||||||
|
game:enterRift()
|
||||||
|
ok(not game:exitRift(), "exiting fails before the player reaches the pocket floor's own exit")
|
||||||
|
game.player.x, game.player.y = game.level.downX, game.level.downY
|
||||||
|
ok(game:exitRift(), "exiting at the pocket floor's own exit point succeeds")
|
||||||
|
eq(game.rift, false, "the rift flag clears on exit")
|
||||||
|
eq(game.riftReturn, nil, "the return point is cleared once used")
|
||||||
|
eq(game.level, mainLevel, "the exact original level object is restored, not a rebuilt copy")
|
||||||
|
eq(game.depth, 1, "the origin depth is restored")
|
||||||
|
eq(game.player.x, 5, "the player's exact departure position is restored")
|
||||||
|
eq(game.player.y, 5, "the player's exact departure position is restored")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.gameReenteringAStabilizedRiftNeverSpendsASecondShard()
|
||||||
|
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:enterRift()
|
||||||
|
local pocketLevel = game.level
|
||||||
|
game.player.x, game.player.y = game.level.downX, game.level.downY
|
||||||
|
game:exitRift()
|
||||||
|
local invBefore = #game.player.inventory
|
||||||
|
game:enterRift()
|
||||||
|
eq(game.level, pocketLevel, "re-entering the same rift restores the exact cached pocket floor")
|
||||||
|
eq(#game.player.inventory, invBefore, "re-entering an already-stabilized rift spends no second shard")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.gameRiftPocketFloorPersistsStateAcrossALeaveAndReturn()
|
||||||
|
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:enterRift()
|
||||||
|
local orc = Monsters.create(Monsters.byKey.orc, 1, game.level.upX, game.level.upY, game.level.depth)
|
||||||
|
orc.hp = 0
|
||||||
|
game.level.entities = { orc }
|
||||||
|
game.player.x, game.player.y = game.level.downX, game.level.downY
|
||||||
|
game:exitRift()
|
||||||
|
game.player.x, game.player.y = 5, 5
|
||||||
|
game:enterRift()
|
||||||
|
eq(game.level.entities[1].hp, 0, "the pocket floor remembers a monster killed on an earlier crossing")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Same abandonment bug Game:descend/Game:ascend had (see the ally-carrying
|
||||||
|
-- tests near gameAscendCarriesAnActiveNemesisAlongButNeverRollsANewOne),
|
||||||
|
-- just at the two other call sites that swap self.level: Game:enterRift and
|
||||||
|
-- Game:exitRift share the exact same Game:detachCarriedAllies/
|
||||||
|
-- Game:plantCarriedAllies pair.
|
||||||
|
function tests.gameEnterRiftAndExitRiftBothCarryALivingCompanionAcrossTheCrossing()
|
||||||
|
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()
|
||||||
|
local mainLevel = game.level
|
||||||
|
local golem = Companions.summon(game, "golem")
|
||||||
|
ok(golem ~= nil, "the companion bonds before crossing")
|
||||||
|
|
||||||
|
ok(game:enterRift(), "entering the rift succeeds with a shard in hand")
|
||||||
|
eq(game.rift, true, "the game is now across the rift")
|
||||||
|
ok(game:hasCompanion(), "the companion crosses into the rift's pocket floor too")
|
||||||
|
local strandedOnMain = 0
|
||||||
|
for _, entity in ipairs(mainLevel.entities) do
|
||||||
|
if entity.kind == "companion" then strandedOnMain = strandedOnMain + 1 end
|
||||||
|
end
|
||||||
|
eq(strandedOnMain, 0, "the companion is not left stranded on the main-track floor")
|
||||||
|
|
||||||
|
game.player.x, game.player.y = game.level.downX, game.level.downY
|
||||||
|
local pocketLevel = game.level
|
||||||
|
ok(game:exitRift(), "exiting the rift succeeds at its own downstairs")
|
||||||
|
eq(game.rift, false, "back on the main track")
|
||||||
|
ok(game:hasCompanion(), "the companion follows back out of the rift as well")
|
||||||
|
local strandedInPocket = 0
|
||||||
|
for _, entity in ipairs(pocketLevel.entities) do
|
||||||
|
if entity.kind == "companion" then strandedInPocket = strandedInPocket + 1 end
|
||||||
|
end
|
||||||
|
eq(strandedInPocket, 0, "the companion is not left stranded on the pocket floor either")
|
||||||
end
|
end
|
||||||
|
|
||||||
local order = {
|
local order = {
|
||||||
|
|||||||
Reference in new issue
Block a user