test: cover tests.terrainIceSlideStopsBeforeAWall

This commit is contained in:
dualfroz committed 2026-09-10 17:43:34 +02:00
1 parent a24ad322c5
commit aca2e06b3c
1 file changed
+173
+173
View File
@@ -6011,6 +6011,179 @@ function tests.terrainIceSlidesATravellerUntilSomethingStopsIt()
eq(game.player.y, 6, "sliding on ice does not drift off the axis of travel")
end
function tests.terrainIceSlideStopsBeforeAWall()
local game = stagedGame(1, arena(21, 11), 2, 6)
game.level.map:set(3, 6, Tiles.ICE)
game.level.map:set(4, 6, Tiles.ICE)
game.level.map:set(5, 6, Tiles.WALL)
game:movePlayer(1, 0)
eq(game.player.x, 4, "an ice slide stops on the last ice tile before a wall")
end
function tests.terrainIceSlideStopsAtAnOccupiedCell()
local game = stagedGame(1, arena(21, 11), 2, 6)
game.level.map:set(3, 6, Tiles.ICE)
game.level.map:set(4, 6, Tiles.ICE)
local rat = Monsters.create(Monsters.byKey.rat, 1, 5, 6, 1)
game.level.entities = { rat }
game:movePlayer(1, 0)
eq(game.player.x, 4, "an ice slide stops just short of another actor")
end
-- Status.LEVITATE (see status.lua and Game:checkTerrainFor) short-circuits
-- every branch below before any of it runs - no burn, no fall, no slide -
-- so these mirror the three terrain tests directly above/below them with
-- one difference: the mover carries the status first.
function tests.checkTerrainForSkipsLavaDamageAndIgnitionForALevitatingPlayer()
local game = stagedGame(1, arena(21, 11), 3, 6)
game.level.map:set(4, 6, Tiles.LAVA)
Status.apply(game.player, Status.LEVITATE, 20, 1)
local hpBefore = game.player.hp
game:movePlayer(1, 0)
eq(game.player.hp, hpBefore, "a levitating player takes no damage crossing lava")
eq(Status.has(game.player, Status.BURNING), false, "a levitating player is never set alight by lava")
ok(contains(game.log:last(), "drift"), "the skip over a visible hazard is still logged, not silent")
end
function tests.checkTerrainForSkipsTheChasmFallForALevitatingPlayer()
local game = stagedGame(3, arena(21, 11), 5, 6)
game.level.map:set(6, 6, Tiles.CHASM)
Status.apply(game.player, Status.LEVITATE, 20, 1)
local hpBefore = game.player.hp
local depthBefore = game.depth
game:movePlayer(1, 0)
eq(game.player.x, 6, "a levitating player actually reaches the chasm cell instead of refusing to enter it")
eq(game.player.hp, hpBefore, "a levitating player takes no fall damage over a chasm")
eq(game.depth, depthBefore, "a levitating player drifts over a chasm instead of dropping to the floor below")
end
function tests.checkTerrainForSkipsTheIceSlideForALevitatingPlayer()
local game = stagedGame(1, arena(21, 11), 2, 6)
for x = 3, 6 do game.level.map:set(x, 6, Tiles.ICE) end
Status.apply(game.player, Status.LEVITATE, 20, 1)
game:movePlayer(1, 0)
eq(game.player.x, 3, "a levitating player enters the first ice cell but never slides across the rest")
end
function tests.checkTerrainForSkipsLavaForALevitatingMonsterToo()
local game = stagedGame(1, arena(21, 11), 2, 2)
game.level.map:set(6, 6, Tiles.LAVA)
local bat = Monsters.create(Monsters.byKey.bat, 1, 5, 6, 1)
Status.apply(bat, Status.LEVITATE, 20, 1)
game.level.entities = { bat }
local hpBefore = bat.hp
game:moveMonster(bat, 1, 0)
eq(bat.x, 6, "the levitating monster still moves onto the lava cell")
eq(bat.hp, hpBefore, "a levitating monster crossing lava takes no damage either - the guard is not player-only")
end
function tests.terrainGlyphsSurviveMapSerialization()
local map = Map.new(10, 6)
for y = 2, 5 do for x = 2, 9 do map:set(x, y, Map.FLOOR) end end
map:set(4, 4, Tiles.LAVA)
map:set(5, 4, Tiles.CHASM)
map:set(6, 4, Tiles.ICE)
local restored = Map.fromData(map:toData())
eq(restored:get(4, 4), Tiles.LAVA, "a lava tile survives a map round trip")
eq(restored:get(5, 4), Tiles.CHASM, "a chasm tile survives a map round trip")
eq(restored:get(6, 4), Tiles.ICE, "an ice tile survives a map round trip")
end
function tests.chasmFallFieldsSurviveGameSaveLoad()
local game = stagedGame(3, arena(21, 11), 5, 6)
game.level.map:set(6, 6, Tiles.CHASM)
game:movePlayer(1, 0)
local path = os.tmpname()
game:save(path)
local restored = Game.load(path)
os.remove(path)
eq(restored.depth, game.depth, "the depth reached by falling survives a save/load round trip")
eq(restored.player.hp, game.player.hp, "hp after a chasm fall survives a save/load round trip")
end
function tests.lavaChasmAndIceStayOffTheShallowFloors()
for seed = 1, 5 do
for depth = 1, 3 do
local level = Builder.build(depth, Rng.new(seed), {})
for _, tile in ipairs(level.map.tiles) do
ok(tile ~= Tiles.LAVA and tile ~= Tiles.CHASM and tile ~= Tiles.ICE,
"no volatile terrain appears above depth 4")
end
end
end
end
function tests.builderOccasionallyPlacesLavaChasmAndIceFromDepthFour()
local sawLava, sawChasm, sawIce = false, false, false
for seed = 1, 60 do
local level = Builder.build(4, Rng.new(seed), {})
for _, tile in ipairs(level.map.tiles) do
if tile == Tiles.LAVA then sawLava = true end
if tile == Tiles.CHASM then sawChasm = true end
if tile == Tiles.ICE then sawIce = true end
end
if sawLava and sawChasm and sawIce then break end
end
ok(sawLava, "a depth-4 floor eventually places lava on some seed")
ok(sawChasm, "a depth-4 floor eventually places a chasm on some seed")
ok(sawIce, "a depth-4 floor eventually places ice on some seed")
end
function tests.chasmNeverAppearsOnTheFinalFloor()
for seed = 1, 30 do
local level = Builder.build(Builder.MAX_DEPTH, Rng.new(seed), {})
for _, tile in ipairs(level.map.tiles) do
ok(tile ~= Tiles.CHASM, "no chasm appears on the final floor since there is no floor below it")
end
end
end
function tests.eliteNeverPromotesBelowMinDepth()
for seed = 1, 40 do
local orc = Monsters.create(Monsters.byKey.orc, 1, 5, 5, 2)
Elites.maybePromote(Rng.new(seed), orc, 2)
ok(not orc.elite, "no promotion below depth 3 regardless of the roll")
end
end
function tests.eliteEligibleExcludesBossesAndTheShopkeeper()
local boss = Bosses.create(Bosses.byKey.warlord, 1, 5, 5)
local keeper = Monsters.create(Monsters.byKey.shopkeeper, 2, 6, 5, 4)
local orc = Monsters.create(Monsters.byKey.orc, 3, 7, 5, 3)
ok(not Elites.eligible(boss), "a boss is never eligible for elite promotion")
ok(not Elites.eligible(keeper), "the shopkeeper is never eligible for elite promotion")
ok(Elites.eligible(orc), "a plain bestiary spawn is eligible for elite promotion")
end
function tests.eliteArmoredAffixAddsFlatDefenseAndPrefixesTheName()
local orc = Monsters.create(Monsters.byKey.orc, 1, 5, 5, 3)
local baseDefense = orc.defense
Elites.maybePromote(Rng.new(8), orc, 3)
eq(orc.eliteTier, "elite", "a single-affix promotion is a plain elite, not a champion")
eq(orc.eliteAffixes[1], "armored", "seed 8 rolls the armored affix at depth 3")
eq(orc.defense, baseDefense + 3, "armored adds its flat defense bonus")
eq(orc.baseDefense, baseDefense + 3, "armored's bonus also lands on baseDefense")
eq(orc.name, "armored orc", "the affix name prefixes the species name")
end
function tests.eliteChampionTierAtDeepDepthRollsTwoDistinctAffixes()
local orc = Monsters.create(Monsters.byKey.orc, 1, 5, 5, 7)
Elites.maybePromote(Rng.new(9), orc, 7)
eq(orc.eliteTier, "champion", "seed 9 at depth 7 rolls a champion")
eq(#orc.eliteAffixes, 2, "a champion carries exactly two affixes")
ok(orc.eliteAffixes[1] ~= orc.eliteAffixes[2], "a champion never doubles up on the same affix")
end
function tests.eliteFieldsSurviveSerialization()
local orc = Monsters.create(Monsters.byKey.orc, 1, 5, 5, 3)
Elites.maybePromote(Rng.new(2), orc, 3)
local restored = Entity.fromData(orc:toData())
eq(restored.elite, true, "the elite flag survives serialization")
eq(restored.eliteTier, "elite", "the elite tier survives serialization")
eq(restored.eliteAffixes[1], "swift", "the rolled affix list survives serialization")
eq(restored.speed, orc.speed, "the swift affix's speed bonus survives serialization")
end
end
local order = {