test: cover tests.potionOfFortificationAppliesAShield

This commit is contained in:
dualfroz committed 2026-09-09 18:01:40 +02:00
1 parent 73de8d4a93
commit bc7d3c88d5
1 file changed
+173
+173
View File
@@ -3762,6 +3762,179 @@ function tests.herbItemsSurviveSerialization()
local path = os.tmpname()
game:save(path)
local restored = Game.load(path)
os.remove(path)
local restoredHerb
for _, item in ipairs(restored.player.inventory) do
if item.kind == Items.HERB then restoredHerb = item end
end
ok(restoredHerb ~= nil, "a foraged herb survives a save/load round trip")
eq(restoredHerb.herb, Alchemy.NIGHTCAP, "a herb's species survives a save/load round trip")
end
function tests.potionOfFortificationAppliesAShield()
local game = Game.new(31)
Effects.apply("fortify", game, game.player, {})
ok(Status.has(game.player, Status.SHIELD), "a fortify effect wards the drinker")
end
function tests.usingAFortifyPotionAppliesShieldAndConsumesIt()
local game = stagedGame(1, arena(21, 11), 5, 5)
local potion = Items.potionOf("fortify")
game.player.inventory = { potion }
ok(game:useItem(1), "drinking a fortify potion succeeds")
eq(#game.player.inventory, 0, "drinking the potion consumes it")
ok(Status.has(game.player, Status.SHIELD), "the fortify potion wards the player")
end
function tests.potionOfLevitationAppliesTheLevitateStatus()
local game = Game.new(31)
Effects.apply("levitate", game, game.player, {})
ok(Status.has(game.player, Status.LEVITATE), "a levitate effect lifts the drinker off the ground")
end
function tests.usingAPotionOfLevitationAppliesLevitateAndConsumesIt()
local game = stagedGame(1, arena(21, 11), 5, 5)
local potion = Items.potionOf("levitate")
game.player.inventory = { potion }
ok(game:useItem(1), "drinking a levitation potion succeeds")
eq(#game.player.inventory, 0, "drinking the potion consumes it")
ok(Status.has(game.player, Status.LEVITATE), "the levitation potion lifts the player off the ground")
end
function tests.companionSummonPlacesItAdjacentToThePlayer()
local game = stagedGame(1, arena(21, 11), 5, 5)
local hound = Companions.summon(game, "hound")
ok(hound ~= nil, "summoning a valid species succeeds next to the player")
eq(hound.kind, "companion", "a summoned companion is tagged with the companion kind")
eq(math.max(math.abs(hound.x - 5), math.abs(hound.y - 5)), 1, "the companion lands adjacent to the player")
eq(#game.level.entities, 1, "the companion joins the level's entity list")
end
function tests.companionSummonRejectsAnUnknownSpecies()
local game = stagedGame(1, arena(21, 11), 5, 5)
eq(Companions.summon(game, "wyrm"), nil, "an unknown species key summons nothing")
eq(#game.level.entities, 0, "no entity is added for an unknown species")
end
function tests.companionSummonReplacesAnEarlierBond()
local game = stagedGame(1, arena(21, 11), 5, 5)
local first = Companions.summon(game, "hound")
local second = Companions.summon(game, "golem")
ok(not first:isAlive(), "bonding a new companion retires the earlier one")
ok(second:isAlive(), "the freshly bonded companion is alive")
local alive = 0
for _, e in ipairs(game.level.entities) do
if e.kind == "companion" and e:isAlive() then alive = alive + 1 end
end
eq(alive, 1, "only one companion is ever alive at a time")
end
function tests.companionAttacksAdjacentHostileMonsterAndCanKillIt()
local game = stagedGame(1, arena(21, 11), 5, 5)
local hound = Companions.summon(game, "hound")
local rat = Monsters.create(Monsters.byKey.rat, 50, hound.x + 1, hound.y, 1)
game.level.entities[#game.level.entities + 1] = rat
local xpBefore = game.player.xp
Companions.act(game, hound)
eq(rat.hp, 0, "a hound's hit kills the weak rat standing next to it")
ok(game.player.xp > xpBefore, "the player is credited xp for its companion's kill")
end
-- Before this fix, companions.lua's own "adjacent" helper was Manhattan-1
-- (cardinal only) while mercenaries.lua's and reanimation.lua's were
-- Chebyshev-1 (8-directional) despite both explicitly claiming to mirror
-- Companions.act's "same three-branch shape" - a hound diagonally next to a
-- monster fell through to stepMonsterToward instead of attacking, unlike a
-- hireling or thrall caught in the identical geometry.
function tests.companionAttacksADiagonallyAdjacentHostileMonsterJustLikeACardinalOne()
local game = stagedGame(1, arena(21, 11), 5, 5)
local hound = Companions.summon(game, "hound")
local rat = Monsters.create(Monsters.byKey.rat, 50, hound.x + 1, hound.y + 1, 1)
game.level.entities[#game.level.entities + 1] = rat
Companions.act(game, hound)
eq(rat.hp, 0, "a hound's hit kills a weak rat standing diagonally next to it, not just cardinally")
end
function tests.companionAdvancesOnAVisibleMonsterItCannotYetReach()
local game = stagedGame(1, arena(21, 11), 5, 5)
local hound = Companions.summon(game, "hound")
local orc = Monsters.create(Monsters.byKey.orc, 50, hound.x + 5, hound.y, 1)
game.level.entities[#game.level.entities + 1] = orc
local startX = hound.x
Companions.act(game, hound)
ok(hound.x > startX, "the companion closes the distance to a monster it can see but not reach")
end
function tests.companionIgnoresAMonsterBeyondItsSightRadius()
local game = stagedGame(1, arena(21, 11), 3, 3)
local hound = Companions.summon(game, "hound")
local orc = Monsters.create(Monsters.byKey.orc, 50, 19, 9, 1)
game.level.entities[#game.level.entities + 1] = orc
local startX, startY = hound.x, hound.y
Companions.act(game, hound)
eq(hound.x, startX, "a monster far outside sight range does not pull the companion off course (x)")
eq(hound.y, startY, "a monster far outside sight range does not pull the companion off course (y)")
end
-- Before Companions.act's own nearestHostile clamped its sight to melee
-- range under Status.BLIND, a blinded companion kept its full listed sight
-- radius - the same gap Game:canSeePlayer's own clamp already closed for a
-- blinded hostile monster (canSeePlayerBlindedMonsterOnlyDetectsThePlayer
-- AtMeleeRange), just never mirrored onto the ally side.
function tests.companionActBlindedCompanionOnlyDetectsAnAdjacentHostileNotOneWithinItsSightRadius()
local game = stagedGame(1, arena(21, 11), 5, 5)
local hound = Companions.summon(game, "hound")
local farOrc = Monsters.create(Monsters.byKey.orc, 50, hound.x + 5, hound.y, 1)
game.level.entities[#game.level.entities + 1] = farOrc
Status.apply(hound, Status.BLIND, 6, 1)
local startX = hound.x
Companions.act(game, hound)
eq(hound.x, startX, "a blinded companion loses a sighting well within its listed sight radius")
local rat = Monsters.create(Monsters.byKey.rat, 51, hound.x + 1, hound.y, 1)
game.level.entities[#game.level.entities + 1] = rat
local ratHpBefore = rat.hp
Companions.act(game, hound)
ok(rat.hp < ratHpBefore, "a blinded companion still fights a hostile once it stands adjacent")
end
-- Companions.act called Combat.attack directly without ever routing through
-- Game:emitNoise, unlike Game:playerAttack/Game:monsterMelee which both do
-- (see gamePlayerAttackCanStirADormantBystanderThatIsNotEvenTheTarget and
-- gameMonsterMeleeCanStirADormantBystanderTooNotJustPlayerActions) - a
-- companion's own melee was silent to every dormant monster nearby, no
-- matter how loud its species' attack would be for the player or a hostile.
-- Fresh game per seed (unlike those two tests' single reused game) since a
-- companion attack, unlike a repeatable playerAttack/monsterMelee call, can
-- kill its one-hp-pool rat target outright.
function tests.companionActAttackingAnAdjacentMonsterEmitsNoiseThatCanStirADormantBystander()
local stirred = false
for seed = 1, 200 do
local game = stagedGame(seed, arena(21, 11), 3, 6)
local hound = Companions.summon(game, "hound")
local target = Monsters.create(Monsters.byKey.rat, 50, hound.x + 1, hound.y, 1)
local bystander = Monsters.create(Monsters.byKey.orc, 51, hound.x + 4, hound.y, 1)
bystander.awareness = Stealth.DORMANT
game.level.entities[#game.level.entities + 1] = target
game.level.entities[#game.level.entities + 1] = bystander
Companions.act(game, hound)
if bystander.awareness ~= Stealth.DORMANT then stirred = true; break end
end
ok(stirred, "a companion's own melee is exactly as loud as the player's or a hostile monster's")
end
function tests.companionFollowsThePlayerWhenNoMonsterIsVisible()
local game = stagedGame(1, arena(21, 11), 10, 5)
local golem = Companions.summon(game, "golem")
golem.x, golem.y = 3, 5
local startDist = math.abs(golem.x - game.player.x)
Companions.act(game, golem)
ok(math.abs(golem.x - game.player.x) < startDist, "a companion with no monster in sight closes in on the player")
end
-- Companions.act never dispatches through ai.lua, so without Game:allyActs
end
local order = {