test: cover tests.autopilotHiresACarriedContractWhenNoHirelingIsYetUn...

This commit is contained in:
dualfroz committed 2026-09-09 23:34:15 +02:00
1 parent 277f28a49e
commit 4d07d9597f
1 file changed
+173
+173
View File
@@ -4454,6 +4454,179 @@ function tests.shopStockCanRollAContractWithAPositiveCatalogPrice()
local sawContract = false
for seed = 1, 40 do
for _, item in ipairs(Shop.stock(Rng.new(seed))) do
if item.kind == Items.CONTRACT then
sawContract = true
ok(Shop.priceOf(item) > 0, "a rolled contract carries a positive catalog price")
ok(Mercenaries.byKey[item.hireling] ~= nil, "a rolled contract names a real roster key")
end
end
end
ok(sawContract, "shop stock can roll a mercenary contract across a handful of seeds")
end
function tests.autopilotHiresACarriedContractWhenNoHirelingIsYetUnderContract()
local game = stagedGame(1, arena(21, 11), 3, 6)
game.level.entities = {}
game.player.inventory = { Items.contract("skirmisher") }
game:choosePlayerAction()
eq(#game.player.inventory, 0, "the autopilot uses a carried contract on sight when no hireling is yet under contract")
local hired = false
for _, e in ipairs(game.level.entities) do
if e.kind == "hireling" then hired = true end
end
ok(hired, "the autopilot's contract use actually hires the mercenary")
end
function tests.artifactsCreateBuildsTheCatalogedItemShape()
local blade = Artifacts.create("bloodfang")
eq(blade.kind, Items.WEAPON, "bloodfang is a weapon-slot artifact")
eq(blade.unique, Artifacts.LIFESTEAL, "bloodfang carries the lifesteal tag")
eq(blade.artifact, "bloodfang", "the item remembers its catalog key")
local ring = Artifacts.create("ring_of_the_tide")
eq(ring.kind, Items.RING, "ring of the tide is a ring-slot artifact")
eq(ring.unique, Artifacts.REGEN, "ring of the tide carries the regen tag")
eq(Artifacts.create("nonexistent"), nil, "an unknown catalog key builds nothing")
end
function tests.lifestealWeaponHealsThePlayerOnAHit()
local game = stagedGame(1, arena(21, 11), 3, 6)
Items.equipWeapon(Artifacts.create("bloodfang"), game.player)
game.player.hp = game.player.maxHp - 10
local orc = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
orc.hp, orc.maxHp = 30, 30
game.level.entities = { orc }
local before = game.player.hp
game:playerAttack(orc)
ok(game.player.hp > before, "bloodfang heals the player for a share of the damage it deals")
end
function tests.thornedArmorReflectsDamageBackAtAMeleeAttacker()
local game = stagedGame(1, arena(21, 11), 3, 6)
Items.equipArmor(Artifacts.create("aegis_of_embers"), game.player)
local orc = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
game.level.entities = { orc }
local before = orc.hp
game:monsterMelee(orc)
ok(orc.hp < before, "aegis of embers burns the monster that hits its wearer")
end
function tests.thornedArmorCanKillAWeakAttacker()
local game = stagedGame(1, arena(21, 11), 3, 6)
Items.equipArmor(Artifacts.create("aegis_of_embers"), game.player)
local rat = Monsters.create(Monsters.byKey.rat, 1, 4, 6, 1)
rat.hp, rat.maxHp = 1, 1
game.level.entities = { rat }
local xpBefore = game.player.xp
game:monsterMelee(rat)
ok(not rat:isAlive(), "a thorn burn can finish off a weak attacker")
ok(game.player.xp > xpBefore, "the player is credited xp for a thorn kill")
end
-- Aegis of Embers is fire damage by name ("Embers") and by its own log line
-- ("your armor sears") - before this fix the reflect subtracted a raw amount
-- straight off the attacker's hp with no Resistances.apply call at all, so a
-- fire-immune dragon (bestiary-real, reachable from depth 6) burned through
-- its own immunity attacking a thorned wearer. Same shape of bug
-- fighterCleaveSplashRespectsAFireImmuneTargetLikeThePrimaryHitAlreadyDoes
-- caught in skills.lua earlier the same day, this time in the thorns path.
function tests.thornedArmorRespectsAFireImmuneAttacker()
local game = stagedGame(1, arena(21, 11), 3, 6)
Items.equipArmor(Artifacts.create("aegis_of_embers"), game.player)
local dragon = Monsters.create(Monsters.byKey.dragon, 1, 4, 6, 1)
game.level.entities = { dragon }
local before = dragon.hp
game:monsterMelee(dragon)
eq(dragon.hp, before, "a fire-immune dragon takes zero thorns reflect from a fire-flavoured artifact")
end
function tests.thornedArmorIsHalvedAgainstAFireResistantAttacker()
local function reflectDealt(resistant)
local game = stagedGame(1, arena(21, 11), 3, 6)
Items.equipArmor(Artifacts.create("aegis_of_embers"), game.player)
local orc = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
orc.hp, orc.maxHp = 100, 100
if resistant then orc.resist = { fire = Resistances.RESIST } end
game.level.entities = { orc }
game:monsterMelee(orc)
return 100 - orc.hp
end
local plain = reflectDealt(false)
local resisted = reflectDealt(true)
ok(resisted < plain, "a fire-resistant attacker takes measurably less thorns reflect than an unresisted one")
eq(resisted, math.floor(plain / 2), "fire resistance halves the thorns reflect, the same mitigation Resistances.apply gives every other fire source")
end
function tests.ringOfTheTideRegeneratesHpEachTurn()
local game = Game.new(1)
Items.equipRing(Artifacts.create("ring_of_the_tide"), game.player)
game.player.hp = game.player.maxHp - 10
local before = game.player.hp
game:hungerTorchTick()
eq(game.player.hp, before + 2, "ring of the tide regenerates its fixed amount each turn")
end
function tests.piercingBowIgnoresPartOfTargetDefense()
local game = stagedGame(1, arena(21, 11), 3, 6)
game.player.baseAttack = 5
Items.equipBow(Artifacts.create("gale_bow"), game.player)
game.player.inventory[#game.player.inventory + 1] = Items.ammo("arrow", 5)
local orc = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
orc.defense, orc.baseDefense, orc.hp, orc.maxHp = 6, 6, 40, 40
game.level.entities = { orc }
game:fireAt(orc)
eq(orc.hp, 35, "a piercing bow's shot ignores a flat share of the target's defense")
end
function tests.builderOccasionallyPlacesAnArtifactFromDepthFive()
local level = Builder.build(5, Rng.new(4), {})
local found
for _, ground in ipairs(level.floorItems) do
if ground.item.artifact then found = ground.item end
end
ok(found ~= nil, "this seed's depth-5 floor places a named artifact")
ok(Artifacts.byKey[found.artifact] ~= nil, "the artifact names a real catalog entry")
end
function tests.artifactsStayOffFloorsAboveDepthFive()
for seed = 1, 4 do
for depth = 1, 4 do
local level = Builder.build(depth, Rng.new(seed), {})
for _, ground in ipairs(level.floorItems) do
ok(not ground.item.artifact, "no artifact appears above depth 5")
end
end
end
end
function tests.artifactFieldsSurviveSerialization()
local game = Game.new(3)
Items.equipWeapon(Artifacts.create("bloodfang"), game.player)
local path = os.tmpname()
game:save(path)
local restored = Game.load(path)
os.remove(path)
eq(restored.player.weapon.artifact, "bloodfang", "an equipped artifact's catalog key survives a save/load round trip")
eq(restored.player.weapon.unique, Artifacts.LIFESTEAL, "an equipped artifact's unique tag survives a save/load round trip")
eq(restored.player.weapon.value, 0.5, "an equipped artifact's tuning value survives a save/load round trip")
end
function tests.useItemOnAKitPlantsAHiddenTrapOnThePlayersTile()
local game = stagedGame(1, arena(21, 11), 5, 5)
game.player.inventory = { Items.kit("dart") }
ok(game:useItem(1), "using a trap kit succeeds")
eq(#game.player.inventory, 0, "the kit leaves the inventory once planted")
local trap = game.level:trapAt(5, 5)
ok(trap ~= nil, "a trap is planted on the player's tile")
eq(trap.effect, "dart", "the planted trap matches the kit's effect")
eq(trap.revealed, false, "a freshly planted trap starts hidden")
eq(game.level.map:get(5, 5), Tiles.FLOOR, "a hidden trap does not change the tile's glyph")
end
function tests.plantingATrapWhereOneAlreadyExistsFails()
local game = stagedGame(1, arena(21, 11), 5, 5)
game.level.traps = { { x = 5, y = 5, effect = "poison", revealed = false } }
game.player.inventory = { Items.kit("dart") }
ok(not game:useItem(1), "planting a trap on an occupied tile fails")
end
local order = {