test: cover tests.gameOpenChestRefusesWhenNothingIsHereOrItIsAlreadyE...
This commit is contained in:
1 parent
2498a1129b
commit
b0648df4e0
1 file changed
+173
+173
@@ -9644,6 +9644,179 @@ function tests.gameOpenChestFallsBackToALockpickWhenNoKeyIsCarried()
|
|||||||
for _, item in ipairs(game.player.inventory) do
|
for _, item in ipairs(game.player.inventory) do
|
||||||
if item.kind == Items.LOCKPICK then stillHasPick = true end
|
if item.kind == Items.LOCKPICK then stillHasPick = true end
|
||||||
end
|
end
|
||||||
|
ok(not stillHasPick, "the lockpick is spent gambling against the lock with no key on hand")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.gameOpenChestRefusesWhenNothingIsHereOrItIsAlreadyEmpty()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
game.level.chests = {}
|
||||||
|
eq(game:openChest(), false, "opening with no chest underfoot fails")
|
||||||
|
local chest = { x = 3, y = 6, locked = false, trapped = false, opened = true, loot = {} }
|
||||||
|
game.level.chests = { chest }
|
||||||
|
eq(game:openChest(), false, "opening an already-emptied chest fails")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.chestsFieldsSurviveSerialization()
|
||||||
|
local level = arena(21, 11)
|
||||||
|
local chest = Chests.new(Rng.new(4), 3)
|
||||||
|
chest.x, chest.y = 5, 5
|
||||||
|
chest.locked = true
|
||||||
|
chest.trapped = true
|
||||||
|
level.chests = { chest }
|
||||||
|
local restored = Level.fromData(level:toData())
|
||||||
|
eq(#restored.chests, 1, "the chest list survives a round trip")
|
||||||
|
eq(restored.chests[1].locked, true, "a chest's locked flag survives a round trip")
|
||||||
|
eq(restored.chests[1].trapped, true, "a chest's trapped flag survives a round trip")
|
||||||
|
eq(restored.chests[1].opened, false, "a chest's opened flag survives a round trip")
|
||||||
|
eq(#restored.chests[1].loot, #chest.loot, "a chest's loot list survives a round trip intact")
|
||||||
|
eq(restored.chests[1].loot[1].kind, chest.loot[1].kind, "a chest's loot items keep their own kind")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.renderShowsAClosedChestGlyphAndAnOpenedOneOnceEmptied()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
local chest = { x = 4, y = 6, locked = true, trapped = false, opened = false, loot = { Items.gold(5) } }
|
||||||
|
game.level.chests = { chest }
|
||||||
|
game:computeVisible()
|
||||||
|
local frame = game:render()
|
||||||
|
ok(contains(frame, "B"), "a closed chest renders as its own glyph")
|
||||||
|
chest.opened = true
|
||||||
|
chest.loot = {}
|
||||||
|
frame = game:render()
|
||||||
|
ok(contains(frame, "b"), "an emptied chest renders as its own distinct glyph")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.autopilotOpensAnUnlockedChestOnSightButSkipsALockedOneWithNoKeyOrPick()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
local chest = { x = 3, y = 6, locked = false, trapped = false, opened = false, loot = { Items.gold(5) } }
|
||||||
|
game.level.chests = { chest }
|
||||||
|
game:choosePlayerAction()
|
||||||
|
eq(chest.opened, true, "the autopilot opens a free chest the instant it stands on one")
|
||||||
|
|
||||||
|
local lockedGame = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
local locked = { x = 3, y = 6, locked = true, trapped = false, opened = false, loot = { Items.gold(5) } }
|
||||||
|
lockedGame.level.chests = { locked }
|
||||||
|
local beforeX, beforeY = lockedGame.player.x, lockedGame.player.y
|
||||||
|
lockedGame:choosePlayerAction()
|
||||||
|
eq(locked.opened, false, "the autopilot never gambles a lock it has neither a key nor a lockpick for")
|
||||||
|
ok(lockedGame.player.x ~= beforeX or lockedGame.player.y ~= beforeY,
|
||||||
|
"the autopilot still makes progress toward the stairs instead of parking on an unopenable chest")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.morgueScoreWeighsEveryStatByItsOwnFixedWeight()
|
||||||
|
local w = Morgue.SCORE_WEIGHTS
|
||||||
|
local report = {
|
||||||
|
deepestDepth = 4, kills = 12, bossKills = 1, questsCompleted = 2, artifactsFound = 1,
|
||||||
|
vaultsOpened = 1, setPiecesFound = 3, chestsOpened = 5, playerLevel = 6, gold = 40, won = false,
|
||||||
|
}
|
||||||
|
local expected = 4 * w.depth + 12 * w.kill + 1 * w.bossKill + 2 * w.quest + 1 * w.artifact
|
||||||
|
+ 1 * w.vault + 3 * w.setPiece + 5 * w.chest + 6 * w.playerLevel + 40
|
||||||
|
eq(Morgue.score(report), expected, "score sums every weighted stat plus flat gold")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.morgueScoreAddsTheWinBonusOnlyWhenWon()
|
||||||
|
local base = {
|
||||||
|
deepestDepth = 1, kills = 0, bossKills = 0, questsCompleted = 0, artifactsFound = 0,
|
||||||
|
vaultsOpened = 0, setPiecesFound = 0, chestsOpened = 0, playerLevel = 1, gold = 0, won = false,
|
||||||
|
}
|
||||||
|
local lost = Morgue.score(base)
|
||||||
|
base.won = true
|
||||||
|
local won = Morgue.score(base)
|
||||||
|
eq(won - lost, Morgue.SCORE_WEIGHTS.win, "winning adds exactly the win bonus and nothing else")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.morgueEquipmentListsOnlyOccupiedSlotsInFixedOrder()
|
||||||
|
local game = Game.new(9)
|
||||||
|
eq(#Morgue.equipment(game.player), 0, "a fresh player with nothing worn has no equipment lines")
|
||||||
|
game.player.armor = Items.armor("plate", 4)
|
||||||
|
game.player.weapon = Items.weapon("sword", 3)
|
||||||
|
local lines = Morgue.equipment(game.player)
|
||||||
|
eq(#lines, 2, "only the two worn slots produce a line")
|
||||||
|
eq(lines[1], "weapon: sword", "weapon is listed before armor regardless of assignment order")
|
||||||
|
eq(lines[2], "armor: plate", "armor keeps its own slot label")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.morgueEquipmentPrefixesARevealedCurseButNotAHiddenOne()
|
||||||
|
local game = Game.new(9)
|
||||||
|
local ring = Items.ring("ring of woe", 0, 0)
|
||||||
|
Curses.afflict(ring, Curses.pool.ring[1])
|
||||||
|
game.player.ring = ring
|
||||||
|
eq(Morgue.equipment(game.player)[1], "ring: ring of woe", "an unrevealed curse reads like an ordinary item")
|
||||||
|
Curses.reveal(ring)
|
||||||
|
eq(Morgue.equipment(game.player)[1], "ring: cursed ring of woe", "a revealed curse is prefixed")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.morgueKillTallySortsByCountThenByCatalogOrderOnTies()
|
||||||
|
local compendium = Compendium.new()
|
||||||
|
Compendium.noteKill(compendium, "goblin")
|
||||||
|
Compendium.noteKill(compendium, "rat")
|
||||||
|
Compendium.noteKill(compendium, "rat")
|
||||||
|
Compendium.noteKill(compendium, "bat")
|
||||||
|
local rows = Morgue.killTally(compendium)
|
||||||
|
eq(#rows, 3, "only species with at least one kill appear")
|
||||||
|
eq(rows[1].key, "rat", "the heaviest kill count sorts first")
|
||||||
|
eq(rows[1].kills, 2, "rat's tally is exactly two")
|
||||||
|
eq(rows[2].key, "bat", "a tie between bat and goblin breaks toward bat's earlier catalog slot")
|
||||||
|
eq(rows[3].key, "goblin", "goblin follows bat on the same tied count")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.morgueBuildMirrorsGameStatsPlayerAndCompendium()
|
||||||
|
local game = Game.new(21)
|
||||||
|
game.stats.kills = 3
|
||||||
|
game.stats.bossKills = 1
|
||||||
|
game.deepestDepth = 5
|
||||||
|
game.turn = 120
|
||||||
|
game.player.gold = 77
|
||||||
|
Compendium.noteKill(game.compendium, "orc")
|
||||||
|
local report = Morgue.build(game)
|
||||||
|
eq(report.kills, 3, "build copies the live kill tally")
|
||||||
|
eq(report.bossKills, 1, "build copies the live boss kill tally")
|
||||||
|
eq(report.deepestDepth, 5, "build copies the deepest depth reached")
|
||||||
|
eq(report.turn, 120, "build copies the current turn")
|
||||||
|
eq(report.gold, 77, "build copies the player's gold")
|
||||||
|
eq(report.alive, true, "a living player's report says so")
|
||||||
|
eq(#report.killTally, 1, "the compendium's own kill feeds straight into the tally")
|
||||||
|
eq(report.killTally[1].key, "orc", "the tallied species matches what was actually killed")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.morgueFormatRendersOutcomeGearKillsAndScoreInOrder()
|
||||||
|
local game = Game.new(3)
|
||||||
|
game.player.weapon = Items.weapon("axe", 2)
|
||||||
|
Compendium.noteKill(game.compendium, "kobold")
|
||||||
|
game.player.hp = 0
|
||||||
|
local text = Morgue.format(Morgue.build(game))
|
||||||
|
ok(contains(text, "died in the dark"), "a dead player's obituary names the outcome")
|
||||||
|
ok(contains(text, "weapon: axe"), "the report lists the worn weapon")
|
||||||
|
ok(contains(text, "kobold"), "the report lists a slain species")
|
||||||
|
local scoreLine = "score: " .. Morgue.score(Morgue.build(game))
|
||||||
|
ok(contains(text, scoreLine), "the report ends with the matching numeric score")
|
||||||
|
local scorePos = text:find("score:", 1, true)
|
||||||
|
local killsPos = text:find("kills:", 1, true)
|
||||||
|
ok(killsPos < scorePos, "the kill tally is printed before the score line")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.morgueFormatNamesTheWinOutcomeWhenWon()
|
||||||
|
local game = Game.new(3)
|
||||||
|
game.won = true
|
||||||
|
local text = Morgue.format(Morgue.build(game))
|
||||||
|
ok(contains(text, "Victorious the wanderer"), "a won run's heading calls the player victorious")
|
||||||
|
ok(contains(text, "escaped the warren with the Amulet"), "a won run's outcome line names the escape")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.morgueFormatOmitsEquipmentAndKillsSectionsWhenEmpty()
|
||||||
|
local game = Game.new(3)
|
||||||
|
local text = Morgue.format(Morgue.build(game))
|
||||||
|
ok(not contains(text, "equipment:"), "an unarmed player's report has no equipment section")
|
||||||
|
ok(not contains(text, "kills:"), "a report with no recorded kills has no kill section")
|
||||||
|
ok(contains(text, "still drawing breath"), "a living player's report says so")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.leaderboardRecordOnAnEmptyBoardLandsAtRankOne()
|
||||||
|
local board = Leaderboard.new()
|
||||||
|
local rank = Leaderboard.record(board, { score = 100 })
|
||||||
|
eq(rank, 1, "the first entry ever recorded ranks first")
|
||||||
|
eq(#board, 1, "the board now holds exactly one entry")
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local order = {
|
local order = {
|
||||||
|
|||||||
Reference in new issue
Block a user