From a24ad322c55c7f97b942ae53cdb4ada4d94c18fd Mon Sep 17 00:00:00 2001 From: DualFroz Date: Thu, 10 Sep 2026 17:19:46 +0200 Subject: [PATCH] test: cover tests.gameDropItemReturnsItemToTheFloorAndFreesCapacity --- tests/run.lua | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/tests/run.lua b/tests/run.lua index 316da60..6a92952 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -5838,6 +5838,179 @@ end function tests.encumbranceOverloadedSlowsMovementAndWeakensDefense() local game = Game.new(1) + game.player.inventory = {} + padWith(game.player, 3) + eq(game.player:encumbranceTier(), Encumbrance.OVERLOADED, "a wide overage is overloaded") + eq(game.player:effectiveSpeed(), 55, "an overloaded carrier loses far more speed than a merely burdened one") + eq(game.player:effectiveDefense(), -1, "an overloaded carrier also fumbles, losing defense") +end + +function tests.gameDropItemReturnsItemToTheFloorAndFreesCapacity() + local game = stagedGame(1, arena(21, 11), 3, 3) + game.player.inventory = { Items.potionOf("heal") } + local before = Encumbrance.carriedWeight(game.player) + local dropped = game:dropItem(1) + eq(dropped.effect, "heal", "dropItem returns the item that was shed") + eq(#game.player.inventory, 0, "the item leaves the inventory") + eq(#game.level.floorItems, 1, "the item lands on the floor") + eq(game.level.floorItems[1].x, game.player.x, "the dropped item lands exactly at the player's feet") + eq(game.level.floorItems[1].y, game.player.y, "the dropped item lands exactly at the player's feet") + ok(Encumbrance.carriedWeight(game.player) < before, "dropping an item frees up carried weight") + ok(contains(game.log:last(), "drop"), "dropping an item is logged") +end + +function tests.gamePickupWarnsWhenNewWeightTipsIntoOverloaded() + local game = stagedGame(1, arena(21, 11), 3, 3) + game.player.inventory = {} + padWith(game.player, 1) + eq(game.player:encumbranceTier(), Encumbrance.LIGHT, "one heavy armor alone still fits under capacity") + game.level.floorItems = {} + for _ = 1, 2 do + game.level.floorItems[#game.level.floorItems + 1] = { x = 3, y = 3, item = Items.armor("lockbox", 0) } + end + game:pickupHere() + eq(game.player:encumbranceTier(), Encumbrance.OVERLOADED, "the pickup tips the total weight past the overloaded threshold") + ok(contains(game.log:last(), "overloaded"), "tipping straight into overloaded is called out by name") +end + +function tests.gamePickupWarnsWithALighterMessageWhenOnlyTippingIntoBurdened() + local game = stagedGame(1, arena(21, 11), 3, 3) + game.player.inventory = {} + game.level.floorItems = {} + for _ = 1, 2 do + game.level.floorItems[#game.level.floorItems + 1] = { x = 3, y = 3, item = Items.armor("lockbox", 0) } + end + game:pickupHere() + eq(game.player:encumbranceTier(), Encumbrance.BURDENED, "two heavy armors in one pickup lands in the burdened band") + ok(contains(game.log:last(), "heavy"), "tipping only into burdened gets the softer warning") +end + +function tests.autopilotDropsCheapestJunkWhenOverloadedAndSafe() + local game = stagedGame(1, arena(21, 11), 3, 3) + game.level.entities = {} + game.player.inventory = {} + padWith(game.player, 3) + local amulet, key = Items.amulet(), Items.key() + game.player.inventory[#game.player.inventory + 1] = amulet + game.player.inventory[#game.player.inventory + 1] = key + local countBefore = #game.player.inventory + eq(game:encumbranceTier(), Encumbrance.OVERLOADED, "the staged inventory is overloaded before the autopilot acts") + game:choosePlayerAction() + eq(#game.player.inventory, countBefore - 1, "the autopilot sheds exactly one item while overloaded and safe") + ok(contains(game.log:last(), "drop"), "the shed item is logged as a drop") + ok(game:findInInventory(function(item) return item == amulet end) ~= nil, "the amulet is never sacrificed to encumbrance") + ok(game:findInInventory(function(item) return item == key end) ~= nil, "a key is never sacrificed to encumbrance") +end + +function tests.encumbranceTierIsDerivedAcrossASaveLoadRoundTrip() + local game = stagedGame(1, arena(21, 11), 3, 3) + game.player.inventory = {} + padWith(game.player, 2) + local tierBefore = game:encumbranceTier() + local restored = Game.fromData(game:toData()) + eq(restored:encumbranceTier(), tierBefore, "the same inventory yields the same tier after a save/load round trip") +end + +function tests.renderPanelShowsCarriedLoad() + local game = Game.new(1) + local panel = Render.panel(game) + ok(contains(panel, "load light ("), "the status panel reports the carrier's current load tier and weight") +end + +function tests.tilesClassifyVolatileTerrain() + eq(Tiles.isLava(Tiles.LAVA), true, "isLava recognizes the lava glyph") + eq(Tiles.isLava(Tiles.FLOOR), false, "isLava rejects plain floor") + eq(Tiles.isChasm(Tiles.CHASM), true, "isChasm recognizes the chasm glyph") + eq(Tiles.isChasm(Tiles.FLOOR), false, "isChasm rejects plain floor") + eq(Tiles.isIce(Tiles.ICE), true, "isIce recognizes the ice glyph") + eq(Tiles.isIce(Tiles.FLOOR), false, "isIce rejects plain floor") + eq(Tiles.isWalkable(Tiles.LAVA), true, "lava is walkable at the player's own risk") + eq(Tiles.isWalkable(Tiles.CHASM), true, "a chasm is walkable right up until it swallows you") + eq(Tiles.isWalkable(Tiles.ICE), true, "ice is walkable, if slippery") +end + +function tests.terrainAfflictLavaDoesNothingOffLava() + local level = arena(21, 11) + local rat = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1) + eq(Terrain.afflictLava(level, rat), nil, "afflictLava does nothing to an entity off any lava cell") +end + +function tests.terrainLavaRespectsFireResistanceAndImmunity() + local level = arena(21, 11) + level.map:set(5, 5, Tiles.LAVA) + + local resistant = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1) + resistant.resist = { fire = Resistances.RESIST } + local hpBefore = resistant.hp + local line = Terrain.afflictLava(level, resistant) + eq(resistant.hp, hpBefore - math.floor(Terrain.LAVA_DAMAGE / 2), + "lava damage is halved for a fire-resistant target") + ok(contains(line, "sears"), "a damaging lava tick describes itself as searing") + eq(Status.has(resistant, Status.BURNING), true, "a scorched target is also set alight") + + local immune = Monsters.create(Monsters.byKey.rat, 2, 5, 5, 1) + immune.resist = { fire = Resistances.IMMUNE } + local hpBefore2 = immune.hp + local immuneLine = Terrain.afflictLava(level, immune) + eq(immune.hp, hpBefore2, "a fire-immune target takes no damage from lava") + ok(contains(immuneLine, "unharmed"), "a harmless lava tick says so") + eq(Status.has(immune, Status.BURNING), false, "a fire-immune target is not set alight by lava") +end + +function tests.terrainLavaBurnsAndIgnitesAPlayerWhoStepsOnIt() + local game = stagedGame(1, arena(21, 11), 3, 6) + game.level.map:set(4, 6, Tiles.LAVA) + local hpBefore = game.player.hp + game:movePlayer(1, 0) + eq(game.player.hp, hpBefore - Terrain.LAVA_DAMAGE, + "stepping onto lava deals its full damage to an unresistant player") + eq(Status.has(game.player, Status.BURNING), true, "lava also sets the player alight") + ok(contains(game.log:last(), "sears"), "the burn is logged") +end + +function tests.terrainLavaCanKillAWeakMonsterAndCreditsThePlayer() + local game = stagedGame(1, arena(21, 11), 3, 6) + game.level.map:set(5, 6, Tiles.LAVA) + local bat = Monsters.create(Monsters.byKey.bat, 1, 4, 6, 1) + bat.hp, bat.maxHp = 3, 3 + game.level.entities = { bat } + local killsBefore = game.stats.kills + game:moveMonster(bat, 1, 0) + eq(#game.level.entities, 0, "a monster killed by lava is removed from the level") + eq(game.stats.kills, killsBefore + 1, "a lava death still credits the player with a kill") +end + +function tests.terrainChasmDropsThePlayerToTheFloorBelowWithFallDamage() + local game = stagedGame(3, arena(21, 11), 5, 6) + game.level.map:set(6, 6, Tiles.CHASM) + local hpBefore = game.player.hp + local depthBefore = game.depth + game:movePlayer(1, 0) + ok(game.player.hp < hpBefore, "falling into a chasm deals fall damage") + eq(game.depth, depthBefore + 1, "falling into a chasm advances to the floor below") + eq(game.deepestDepth, depthBefore + 1, "falling into a chasm counts toward the deepest depth reached") + ok(contains(game.log:last(), "plunge"), "the fall is logged") +end + +function tests.terrainChasmSwallowsAMonsterAndCreditsTheKill() + local game = stagedGame(1, arena(21, 11), 3, 6) + game.level.map:set(5, 6, Tiles.CHASM) + local rat = Monsters.create(Monsters.byKey.rat, 1, 4, 6, 1) + game.level.entities = { rat } + local killsBefore = game.stats.kills + game:moveMonster(rat, 1, 0) + eq(#game.level.entities, 0, "a monster that falls into a chasm is removed from the level") + eq(game.stats.kills, killsBefore + 1, "a chasm death still credits the player with a kill, like a lethal trap") +end + +function tests.terrainIceSlidesATravellerUntilSomethingStopsIt() + local game = stagedGame(1, arena(21, 11), 2, 6) + for x = 3, 6 do game.level.map:set(x, 6, Tiles.ICE) end + game:movePlayer(1, 0) + eq(game.player.x, 7, "stepping onto ice slides the player through every consecutive ice tile") + eq(game.player.y, 6, "sliding on ice does not drift off the axis of travel") +end + end local order = {