test: cover tests.gameCaptureAtRemovesTheCaughtMonsterFromTheLevelWit...

This commit is contained in:
dualfroz committed 2026-09-12 18:42:28 +02:00
1 parent fce4742c18
commit 6f260eb25f
1 file changed
+173
+173
View File
@@ -12239,6 +12239,179 @@ function tests.captivesResolveCatchesABatteredTargetIntoACaptiveAndZeroesItsHp()
eq(captive.value, Monsters.xpFor(rat), "the captive's value matches Captives.valueOf")
eq(rat.hp, 0, "the caught monster's hp is zeroed")
ok(not rat:isAlive(), "the caught monster is no longer alive")
ok(contains(lines[1], "bound"), "the log reports the net dropping true")
end
function tests.gameCaptureAtRemovesTheCaughtMonsterFromTheLevelWithoutAwardingAKill()
local game = stagedGame(4, arena(21, 11), 3, 6)
local rat = Monsters.create(Monsters.byKey.rat, 1, 6, 6, 1)
rat.hp = 1
game.level.entities = { rat }
local before = #game.player.inventory
game.player.inventory[#game.player.inventory + 1] = Items.net()
local index = #game.player.inventory
game.rng = Rng.new(2)
local xpBefore = game.player.xp
local killsBefore = game.stats.kills
local success = game:captureAt(index, rat)
eq(success, true, "a successful net capture reports the item as thrown")
eq(#game.player.inventory, before, "the net leaves the inventory once thrown")
eq(#game.level.entities, 0, "the captured monster is swept from the level's entities")
eq(#game.level.captives, 1, "the captured monster becomes exactly one level captive")
eq(game.player.xp, xpBefore, "a capture grants no xp")
eq(game.stats.kills, killsBefore, "a capture is never counted as a kill")
eq(#game.level.corpses, 0, "a captured monster leaves no corpse behind")
end
function tests.gameCaptureAtLeavesTheTargetAliveAndTheNetUnspentWhenOutOfRange()
local game = stagedGame(4, arena(21, 11), 2, 2)
local rat = Monsters.create(Monsters.byKey.rat, 1, 19, 9, 1)
rat.hp = 1
game.level.entities = { rat }
game.player.inventory[#game.player.inventory + 1] = Items.net()
local index = #game.player.inventory
local before = #game.player.inventory
local success = game:captureAt(index, rat)
eq(success, false, "a capture attempt beyond range fails")
eq(#game.player.inventory, before, "a failed capture attempt does not consume the net")
ok(rat:isAlive(), "a failed capture attempt leaves the target alive")
end
function tests.gameRansomCaptiveWarmsFactionStandingWithNoGoldChange()
local game = stagedGame(4, arena(21, 11), 5, 5)
game.level.captives = { { x = 5, y = 5, species = "rat", faction = Factions.VERMIN, name = "rat", value = 3 } }
local goldBefore = game.player.gold
local repBefore = Factions.reputationFor(game.player, Factions.VERMIN)
local success = game:ransomCaptive()
eq(success, true, "ransoming a held captive succeeds")
eq(#game.level.captives, 0, "ransoming removes the captive from the level")
eq(game.player.gold, goldBefore, "ransoming changes no gold")
eq(Factions.reputationFor(game.player, Factions.VERMIN), repBefore + Captives.RANSOM_REP_GAIN,
"ransoming warms the captive's own faction standing")
end
function tests.gameSellCaptivePaysGoldAndCoolsFactionStanding()
local game = stagedGame(4, arena(21, 11), 5, 5)
game.level.captives = { { x = 5, y = 5, species = "rat", faction = Factions.VERMIN, name = "rat", value = 7 } }
local goldBefore = game.player.gold
local repBefore = Factions.reputationFor(game.player, Factions.VERMIN)
local success = game:sellCaptive()
eq(success, true, "selling a held captive succeeds")
eq(#game.level.captives, 0, "selling removes the captive from the level")
eq(game.player.gold, goldBefore + 7, "selling pays exactly the captive's own value in gold")
eq(Factions.reputationFor(game.player, Factions.VERMIN), repBefore - Captives.SELL_REP_LOSS,
"selling cools the captive's own faction standing")
end
function tests.gameInterrogateCaptiveRevealsEveryHiddenDoorAndGrantsEssence()
local game = stagedGame(4, arena(21, 11), 5, 5)
game.level.secretDoors = {
{ x = 8, y = 8, discovered = false },
{ x = 9, y = 9, discovered = false },
{ x = 10, y = 10, discovered = true },
}
game.level.captives = { { x = 5, y = 5, species = "rat", faction = Factions.VERMIN, name = "rat", value = 8 } }
local essenceBefore = game.player.essence
local success = game:interrogateCaptive()
eq(success, true, "interrogating a held captive succeeds")
eq(#game.level.captives, 0, "interrogating removes the captive from the level")
ok(game.level.secretDoors[1].discovered, "interrogating reveals a previously hidden door")
ok(game.level.secretDoors[2].discovered, "interrogating reveals every previously hidden door, not just one")
eq(game.player.essence, essenceBefore + 4, "interrogating grants essence equal to half the captive's value")
end
function tests.captiveFieldsSurviveALevelSerializationRoundTrip()
local level = arena(21, 11)
level.captives = { { x = 5, y = 5, species = "rat", faction = Factions.VERMIN, name = "rat", value = 3 } }
local restored = Level.fromData(level:toData())
eq(#restored.captives, 1, "a held captive survives a level round trip")
eq(restored.captives[1].name, "rat", "the captive's name survives a round trip")
eq(restored.captives[1].faction, Factions.VERMIN, "the captive's faction survives a round trip")
eq(restored.captives[1].value, 3, "the captive's value survives a round trip")
eq(restored:captiveAt(5, 5), restored.captives[1], "Level:captiveAt finds the restored captive by position")
end
function tests.autopilotNetsABatteredAdjacentMonsterInsteadOfFinishingItInMeleeWhenCarryingANet()
local game = stagedGame(4, arena(21, 11), 5, 5)
local rat = Monsters.create(Monsters.byKey.rat, 1, 6, 5, 1)
rat.hp = 1
game.level.entities = { rat }
game.player.inventory[#game.player.inventory + 1] = Items.net()
game.rng = Rng.new(2)
game:choosePlayerAction()
eq(#game.level.captives, 1, "the autopilot nets the battered adjacent monster instead of finishing it off")
eq(#game.level.entities, 0, "the netted monster is removed from the level's entities")
end
function tests.autopilotSellsAHeldCaptiveOnSight()
local game = stagedGame(4, arena(21, 11), 5, 5)
game.level.captives = { { x = 5, y = 5, species = "rat", faction = Factions.VERMIN, name = "rat", value = 5 } }
local goldBefore = game.player.gold
game:choosePlayerAction()
eq(#game.level.captives, 0, "the autopilot cashes in a held captive on sight")
eq(game.player.gold, goldBefore + 5, "the autopilot sells rather than ransoms or interrogates on its own")
end
function tests.builderOccasionallyPlacesANetFromDepthFive()
local level = Builder.build(5, Rng.new(10), {})
local found
for _, ground in ipairs(level.floorItems) do
if ground.item.kind == Items.NET then found = ground.item end
end
ok(found ~= nil, "this seed's depth-5 floor places a net")
end
function tests.netsStayOffFloorsAboveDepthFive()
for seed = 1, 8 do
for depth = 1, 4 do
local level = Builder.build(depth, Rng.new(seed), {})
for _, ground in ipairs(level.floorItems) do
ok(ground.item.kind ~= Items.NET, "no net appears above depth 5")
end
end
end
end
function tests.hordeBuildArenaIsFullyEnclosedWithAClearCentreReachableFromEveryCorner()
local map, cx, cy = Horde.buildArena(Rng.new(1), Horde.WIDTH, Horde.HEIGHT)
for x = 1, Horde.WIDTH do
eq(map:get(x, 1), Tiles.WALL, "the top border is solid wall")
eq(map:get(x, Horde.HEIGHT), Tiles.WALL, "the bottom border is solid wall")
end
for y = 1, Horde.HEIGHT do
eq(map:get(1, y), Tiles.WALL, "the left border is solid wall")
eq(map:get(Horde.WIDTH, y), Tiles.WALL, "the right border is solid wall")
end
ok(map:isWalkable(cx, cy), "the centre cell the player starts on is always clear")
for _, anchor in ipairs(Horde.spawnAnchors(Horde.WIDTH, Horde.HEIGHT)) do
ok(Astar.find(map, cx, cy, anchor.x, anchor.y) ~= nil,
"every corner anchor is reachable from the centre")
end
end
function tests.hordeBuildArenaIsDeterministicForAFixedSeedAndDiffersAcrossSeeds()
local mapA = Horde.buildArena(Rng.new(5), Horde.WIDTH, Horde.HEIGHT)
local mapB = Horde.buildArena(Rng.new(5), Horde.WIDTH, Horde.HEIGHT)
eq(table.concat(mapA.tiles), table.concat(mapB.tiles), "the same seed always carves the same pillars")
local mapC = Horde.buildArena(Rng.new(6), Horde.WIDTH, Horde.HEIGHT)
ok(table.concat(mapA.tiles) ~= table.concat(mapC.tiles), "a different seed carves a different arena")
end
function tests.hordeSpawnPointsAreUniqueWalkableCellsAndAlwaysCoverAMaxWave()
local map = Horde.buildArena(Rng.new(3), Horde.WIDTH, Horde.HEIGHT)
local points = Horde.spawnPoints(map)
ok(#points >= Horde.MAX_COUNT, "the spawn queue always has room for a maximum-size wave")
local seen = {}
for _, point in ipairs(points) do
local key = point.x .. "," .. point.y
ok(not seen[key], "no spawn cell is ever offered twice")
seen[key] = true
ok(map:isWalkable(point.x, point.y), "every offered spawn cell is walkable")
end
end
function tests.hordeCountForClimbsWithWaveThenCapsAtMaxCount()
eq(Horde.countFor(1), Horde.BASE_COUNT, "wave one spawns the base count")
end
local order = {