test: cover tests.setsDoNotCrossTalkBetweenDifferentSets

This commit is contained in:
dualfroz committed 2026-09-11 12:15:41 +02:00
1 parent 50131d3e41
commit 6dc9d144d2
1 file changed
+173
+173
View File
@@ -7049,6 +7049,179 @@ end
function tests.setsBonusDropsBackWhenAPieceIsUnequipped()
local player = {
weapon = Sets.create("ironclad", "weapon"),
armor = Sets.create("ironclad", "armor"),
ring = Sets.create("ironclad", "ring"),
}
eq(Sets.attackBonus(player), 2, "the full set starts active")
player.ring = Items.ring("plain band", 0, 0)
eq(Sets.attackBonus(player), 1, "losing a piece drops the bonus back to the 2-piece tier")
player.armor = Items.armor("plain mail", 2)
eq(Sets.attackBonus(player), 0, "a single remaining piece grants no bonus at all")
end
function tests.setsDoNotCrossTalkBetweenDifferentSets()
local player = { weapon = Sets.create("ironclad", "weapon"), bow = Sets.create("duskrunner", "bow") }
eq(Sets.attackBonus(player), 0, "one piece from each of two different sets completes neither")
end
function tests.gameEquippingASetPieceAppliesItsBonusInActualCombat()
local game = stagedGame(1, arena(21, 11), 3, 6)
Items.equipWeapon(Items.weapon("sword", 4), game.player)
local orc = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
orc.defense, orc.baseDefense, orc.hp, orc.maxHp = 0, 0, 40, 40
game.level.entities = { orc }
local before = orc.hp
game:playerAttack(orc)
local plainDamage = before - orc.hp
local game2 = stagedGame(1, arena(21, 11), 3, 6)
Items.equipWeapon(Sets.create("ironclad", "weapon"), game2.player)
Items.equipArmor(Sets.create("ironclad", "armor"), game2.player)
local orc2 = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
orc2.defense, orc2.baseDefense, orc2.hp, orc2.maxHp = 0, 0, 40, 40
game2.level.entities = { orc2 }
local before2 = orc2.hp
game2:playerAttack(orc2)
local setDamage = before2 - orc2.hp
-- The ironclad weapon's own power (3) is one below the plain sword's (4),
-- but the completed 2-piece bonus adds back exactly +1 attack, landing on
-- the same total damage - proof the bonus reads live in actual combat.
eq(setDamage, plainDamage, "a 2-piece Ironclad bonus makes up for the set piece's own weaker power")
end
function tests.gameEquippingTheSecondSetPieceLogsTheSetAwakening()
local game = stagedGame(1, arena(21, 11), 3, 6)
game.player.inventory = { Sets.create("ironclad", "weapon"), Sets.create("ironclad", "armor") }
ok(game:useItem(1), "equipping the first ironclad piece succeeds")
ok(not contains(game.log:last(), "awakens"), "a lone set piece does not announce a bonus")
ok(game:useItem(1), "equipping the second ironclad piece succeeds")
ok(contains(game.log:last(), "Ironclad") and contains(game.log:last(), "awakens"),
"completing the 2-piece threshold announces the set by name")
end
function tests.setsRegenerationAppliesEachTurnAtTheFullIroncladSet()
local game = Game.new(1)
Items.equipWeapon(Sets.create("ironclad", "weapon"), game.player)
Items.equipArmor(Sets.create("ironclad", "armor"), game.player)
Items.equipRing(Sets.create("ironclad", "ring"), game.player)
game.player.hp = game.player.maxHp - 10
local before = game.player.hp
game:hungerTorchTick()
eq(game.player.hp, before + 1, "the full ironclad set regenerates its fixed amount each turn")
end
function tests.builderOccasionallyPlacesASetPieceFromDepthThree()
local found
for seed = 1, 20 do
local level = Builder.build(3, Rng.new(seed), {})
for _, ground in ipairs(level.floorItems) do
if ground.item.set then found = ground.item end
end
if found then break end
end
ok(found ~= nil, "a depth-3 floor eventually places a set piece across a handful of seeds")
ok(Sets.byKey[found.set] ~= nil, "the placed piece names a real catalog set")
end
function tests.setPiecesStayOffTheShallowestTwoFloors()
for seed = 1, 6 do
for depth = 1, 2 do
local level = Builder.build(depth, Rng.new(seed), {})
for _, ground in ipairs(level.floorItems) do
ok(not ground.item.set, "no set piece appears on depth 1 or 2")
end
end
end
end
function tests.setFieldsSurviveSerialization()
local game = Game.new(3)
Items.equipWeapon(Sets.create("ironclad", "weapon"), game.player)
local path = os.tmpname()
game:save(path)
local restored = Game.load(path)
os.remove(path)
eq(restored.player.weapon.set, "ironclad", "an equipped set piece's set key survives a save/load round trip")
end
function tests.gamePickupTracksSetPiecesFoundForMetaStats()
local game = stagedGame(1, arena(21, 11), 5, 5)
game.level.floorItems = { { x = 5, y = 5, item = Sets.create("ironclad", "ring") } }
game:pickupHere()
eq(game.stats.setPiecesFound, 1, "picking up a set piece counts toward set pieces found")
ok(game.player.ring == nil, "picking up a set piece only stashes it in inventory, it is not auto-equipped")
end
function tests.metaRenownForScalesWithSetPiecesFound()
local base = Meta.renownFor({ deepestDepth = 3, bossKills = 0, questsCompleted = 0, setPiecesFound = 0, won = false })
local withPieces = Meta.renownFor({ deepestDepth = 3, bossKills = 0, questsCompleted = 0, setPiecesFound = 2, won = false })
eq(withPieces, base + 6, "each set piece found adds a fixed renown bonus")
end
function tests.metaRecordRunUnlocksSetCollectorOnceThreePiecesFound()
local meta = Meta.new()
local stats = {
deepestDepth = 1, kills = 0, bossKills = 0, questsCompleted = 0,
artifactsFound = 0, vaultsOpened = 0, setPiecesFound = 3, won = false,
}
local unlocked = Meta.recordRun(meta, stats)
eq(unlocked[1], "Set Collector", "finding three set pieces in a run unlocks Set Collector")
eq(meta.achievements.set_collector, true, "set_collector is marked unlocked")
local again = Meta.recordRun(meta, stats)
eq(#again, 0, "an already-unlocked Set Collector is not re-announced on a later run")
end
function tests.renderPanelShowsTheActiveSetBonusWhenOneApplies()
local game = stagedGame(1, arena(21, 11), 3, 6)
ok(not contains(Render.panel(game), "set "), "the panel omits the set line without an active bonus")
Items.equipWeapon(Sets.create("ironclad", "weapon"), game.player)
Items.equipArmor(Sets.create("ironclad", "armor"), game.player)
ok(contains(Render.panel(game), "Ironclad 2pc"), "the panel names the active set and its piece count")
end
function tests.mimicsFloorItemBuildsADisguisedLootShape()
local item = Mimics.floorItem(Rng.new(1))
eq(item.kind, "mimic", "a mimic's floor item is its own kind, not a real item kind")
eq(item.mimic, true, "a mimic's floor item is tagged so pickupHere knows to unmask it")
local matched
for _, disguise in ipairs(Mimics.DISGUISES) do
if disguise.name == item.name and disguise.glyph == item.glyph then matched = true end
end
ok(matched, "the rolled disguise's name and glyph both come from the catalog")
end
function tests.mimicsRevealBuildsAHostileMonsterScaledByDepth()
local shallow = Mimics.reveal(1, 4, 6, 1)
eq(shallow.kind, "monster", "a revealed mimic is a genuine monster entity")
eq(shallow.glyph, "M", "a revealed mimic shows its own glyph, not a disguise")
eq(shallow.mimic, true, "a revealed mimic stays tagged as a mimic")
eq(shallow.hp, 12, "a depth-1 mimic has its base hp with no bonus")
local deep = Mimics.reveal(2, 4, 6, 5)
eq(deep.hp, 16, "a mimic's hp scales with depth the same way Monsters.create scales a wild species")
end
function tests.gamePickupHereRevealsAMimicAndAmbushesThePlayer()
local game = stagedGame(1, arena(21, 11), 5, 5)
game.player.defense, game.player.baseDefense = 0, 0
game.level.floorItems = { { x = 5, y = 5, item = Mimics.floorItem(Rng.new(1)) } }
local hpBefore = game.player.hp
game:pickupHere()
eq(#game.level.floorItems, 0, "the disguised item leaves the floor the instant it is disturbed")
eq(#game.level.entities, 1, "a revealed mimic joins the level as a real hostile monster")
eq(game.level.entities[1].mimic, true, "the spawned monster is tagged as a mimic")
eq(game.player.hp, hpBefore - 5, "the mimic lands one free ambush hit for its full attack")
ok(contains(game.log:last(), "damage"), "the ambush hit is logged")
end
function tests.gameKillingARevealedMimicGrantsItsFixedEssenceReward()
local game = stagedGame(1, arena(21, 11), 3, 6)
local mimic = Mimics.reveal(1, 4, 6, 1)
mimic.hp = 1
game.level.entities = { mimic }
local essenceBefore = game.player.essence
game:playerAttack(mimic)
eq(game.player.essence, essenceBefore + Crafting.essenceFor(Mimics.xpFor()),
"felling a mimic grants essence scaled to its own fixed xp reward, not a plain monster's")
end
local order = {