test: cover tests.reanimationActAttackingAnAdjacentMonsterEmitsNoiseT...
This commit is contained in:
1 parent
b35beaeee7
commit
73de8d4a93
1 file changed
+173
+173
@@ -3589,6 +3589,179 @@ function tests.reanimationActBlindedThrallOnlyDetectsAnAdjacentHostileNotOneWith
|
||||
game.level.entities[#game.level.entities + 1] = rat
|
||||
local ratHpBefore = rat.hp
|
||||
Reanimation.act(game, thrall)
|
||||
ok(rat.hp < ratHpBefore, "a blinded thrall still fights a hostile once it stands adjacent")
|
||||
end
|
||||
|
||||
-- Same gap as companionActAttackingAnAdjacentMonsterEmitsNoise..., mirrored
|
||||
-- onto the thrall side. Fresh game per seed rather than a shared one both to
|
||||
-- match that sibling test's shape and because Reanimation.act decrements
|
||||
-- thrall.decay on every call, so a single thrall could not absorb 200 calls
|
||||
-- regardless.
|
||||
function tests.reanimationActAttackingAnAdjacentMonsterEmitsNoiseThatCanStirADormantBystander()
|
||||
local stirred = false
|
||||
for seed = 1, 200 do
|
||||
local game = stagedGame(seed, arena(21, 11), 3, 6)
|
||||
local corpseSource = Monsters.create(Monsters.byKey.rat, 40, game.player.x + 1, game.player.y, 1)
|
||||
Corpses.spawn(game.level, corpseSource)
|
||||
local thrall = Reanimation.raise(game, game.level.corpses[1])
|
||||
local target = Monsters.create(Monsters.byKey.rat, 50, thrall.x + 1, thrall.y, 1)
|
||||
local bystander = Monsters.create(Monsters.byKey.orc, 51, thrall.x + 4, thrall.y, 1)
|
||||
bystander.awareness = Stealth.DORMANT
|
||||
game.level.entities[#game.level.entities + 1] = target
|
||||
game.level.entities[#game.level.entities + 1] = bystander
|
||||
Reanimation.act(game, thrall)
|
||||
if bystander.awareness ~= Stealth.DORMANT then stirred = true; break end
|
||||
end
|
||||
ok(stirred, "a thrall's own melee is exactly as loud as any other ally's or the player's")
|
||||
end
|
||||
|
||||
function tests.reanimationActFollowsThePlayerWhenNoMonsterIsVisible()
|
||||
local game = stagedGame(1, arena(21, 11), 10, 5)
|
||||
local rat0 = Monsters.create(Monsters.byKey.rat, 1, 10, 5, 1)
|
||||
Corpses.spawn(game.level, rat0)
|
||||
local thrall = Reanimation.raise(game, game.level.corpses[1])
|
||||
thrall.x, thrall.y = 3, 5
|
||||
local startDist = math.abs(thrall.x - game.player.x)
|
||||
Reanimation.act(game, thrall)
|
||||
ok(math.abs(thrall.x - game.player.x) < startDist, "a thrall with no monster in sight closes in on the player")
|
||||
end
|
||||
|
||||
-- Same wiring proof as the companion/hireling versions above, but for
|
||||
-- Reanimation.act - placed after its own decay tick/death check (upkeep
|
||||
-- the confusion roll must never skip), just before the behaviour it can
|
||||
-- override.
|
||||
function tests.reanimationActConfusedThrallSometimesStumblesInsteadOfFightingAnAdjacentHostile()
|
||||
local sawAttack, sawStumble = false, false
|
||||
for seed = 1, 200 do
|
||||
local game = stagedGame(seed, arena(21, 11), 3, 6)
|
||||
local rat0 = Monsters.create(Monsters.byKey.rat, 1, 3, 6, 1)
|
||||
Corpses.spawn(game.level, rat0)
|
||||
local thrall = Reanimation.raise(game, game.level.corpses[1])
|
||||
local rat = Monsters.create(Monsters.byKey.rat, 50, thrall.x + 1, thrall.y, 1)
|
||||
game.level.entities[#game.level.entities + 1] = rat
|
||||
Status.apply(thrall, Status.CONFUSE, 5, 1)
|
||||
local hpBefore = rat.hp
|
||||
Reanimation.act(game, thrall)
|
||||
if rat.hp < hpBefore then
|
||||
sawAttack = true
|
||||
else
|
||||
sawStumble = true
|
||||
end
|
||||
if sawAttack and sawStumble then break end
|
||||
end
|
||||
ok(sawAttack, "a confused thrall's roll sometimes lets it fight an adjacent hostile as normal")
|
||||
ok(sawStumble, "a confused thrall's roll sometimes overrides even a point-blank attack")
|
||||
end
|
||||
|
||||
function tests.reanimationActCollapsesOnceItsBorrowedAnimationRunsOut()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
local rat0 = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1)
|
||||
Corpses.spawn(game.level, rat0)
|
||||
local thrall = Reanimation.raise(game, game.level.corpses[1])
|
||||
thrall.decay = 1
|
||||
game:takeMonsterTurn(thrall)
|
||||
eq(#game.level.entities, 0, "a thrall whose animation runs out is swept from the level")
|
||||
ok(contains(game.log:last(), "crumbles"), "a collapsing thrall's fate is logged")
|
||||
end
|
||||
|
||||
function tests.takeMonsterTurnRoutesAThrallThroughReanimationAct()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
local chieftain = Monsters.create(Monsters.byKey.chieftain, 1, 5, 5, 6)
|
||||
Corpses.spawn(game.level, chieftain)
|
||||
local thrall = Reanimation.raise(game, game.level.corpses[1])
|
||||
local rat = Monsters.create(Monsters.byKey.rat, 50, thrall.x + 1, thrall.y, 1)
|
||||
game.level.entities[#game.level.entities + 1] = rat
|
||||
game:takeMonsterTurn(thrall)
|
||||
eq(rat.hp, 0, "the full monster-turn pipeline dispatches a thrall's turn to reanimation.act")
|
||||
end
|
||||
|
||||
function tests.gameRaiseCorpseSucceedsAndLogsTheCallAnswered()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
local rat = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1)
|
||||
Corpses.spawn(game.level, rat)
|
||||
ok(game:raiseCorpse(), "raising the corpse underfoot succeeds")
|
||||
eq(#game.level.corpses, 0, "the corpse is gone after raising")
|
||||
local hasThrall = false
|
||||
for _, e in ipairs(game.level.entities) do
|
||||
if e.kind == "thrall" then hasThrall = true end
|
||||
end
|
||||
ok(hasThrall, "a thrall joins the level once raised through the game action")
|
||||
ok(contains(game.log:last(), "answers the call"), "a successful raise is logged")
|
||||
end
|
||||
|
||||
function tests.gameRaiseCorpseFailsWithoutACorpseUnderfoot()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
ok(not game:raiseCorpse(), "raising empty ground fails")
|
||||
ok(contains(game.log:last(), "nothing here"), "the failure is logged")
|
||||
end
|
||||
|
||||
function tests.scavengingHerbForMapsEachWildFactionToItsOwnFixedHerb()
|
||||
eq(Scavenging.herbFor({ species = "rat" }), Alchemy.NIGHTCAP, "vermin always yields nightcap")
|
||||
eq(Scavenging.herbFor({ species = "goblin" }), Alchemy.BLOODROOT, "goblinoids always yield bloodroot")
|
||||
eq(Scavenging.herbFor({ species = "troll" }), Alchemy.EMBERLEAF, "beasts always yield emberleaf")
|
||||
eq(Scavenging.herbFor({ species = "dragon" }), Alchemy.EMBERLEAF, "drakes yield the same herb as any other beast-aspected foe")
|
||||
end
|
||||
|
||||
function tests.scavengingHerbForReturnsNilForACorpseWithNoBestiaryEntry()
|
||||
eq(Scavenging.herbFor({ species = "hound" }), nil,
|
||||
"a bonded companion's own remains carry no bestiary-mapped herb")
|
||||
end
|
||||
|
||||
function tests.gameScavengeCorpseCarvesTheMatchingHerbAndClearsTheCorpse()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
local goblin = Monsters.create(Monsters.byKey.goblin, 1, 5, 5, 2)
|
||||
Corpses.spawn(game.level, goblin)
|
||||
local before = #game.player.inventory
|
||||
ok(game:scavengeCorpse(), "scavenging the corpse underfoot succeeds")
|
||||
eq(#game.level.corpses, 0, "the corpse is gone after scavenging")
|
||||
eq(#game.player.inventory, before + 1, "the player's inventory gains the carved herb")
|
||||
local herb = game.player.inventory[#game.player.inventory]
|
||||
eq(herb.herb, Alchemy.BLOODROOT, "the carved herb matches the goblin's own goblinoid aspect")
|
||||
ok(contains(game.log:last(), "carve"), "a successful scavenge is logged")
|
||||
end
|
||||
|
||||
function tests.gameScavengeCorpseFailsWithoutACorpseUnderfoot()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
ok(not game:scavengeCorpse(), "scavenging empty ground fails")
|
||||
ok(contains(game.log:last(), "nothing here"), "the failure is logged")
|
||||
end
|
||||
|
||||
function tests.gameScavengeCorpseFailsOnARottenCorpse()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
local rat = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1)
|
||||
Corpses.spawn(game.level, rat)
|
||||
game.level.corpses[1].decay = Corpses.ROT_TURNS
|
||||
local before = #game.player.inventory
|
||||
ok(not game:scavengeCorpse(), "a rotten corpse yields no herb")
|
||||
eq(#game.level.corpses, 1, "a failed scavenge leaves the corpse in place rather than spending it")
|
||||
eq(#game.player.inventory, before, "no herb is added on a failed scavenge")
|
||||
end
|
||||
|
||||
function tests.gameScavengeCorpseFailsForASpeciesWithNoKnownHerb()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
game.level.corpses = { { x = 5, y = 5, species = "hound", name = "hound", nutrition = 40, decay = Corpses.DECAY_TURNS } }
|
||||
ok(not game:scavengeCorpse(), "a companion's own remains carry nothing worth carving")
|
||||
eq(#game.level.corpses, 1, "a failed scavenge leaves the corpse in place")
|
||||
end
|
||||
|
||||
function tests.reanimationFieldsSurviveSerialization()
|
||||
local game = stagedGame(1, arena(21, 11), 5, 5)
|
||||
local rat = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1)
|
||||
Corpses.spawn(game.level, rat)
|
||||
local thrall = Reanimation.raise(game, game.level.corpses[1])
|
||||
thrall.decay = 17
|
||||
local restoredLevel = Level.fromData(game.level:toData())
|
||||
eq(#restoredLevel.entities, 1, "the thrall survives a level round trip")
|
||||
eq(restoredLevel.entities[1].kind, "thrall", "the thrall's kind survives a round trip")
|
||||
eq(restoredLevel.entities[1].decay, 17, "the thrall's remaining animation survives a round trip")
|
||||
end
|
||||
|
||||
function tests.herbItemsSurviveSerialization()
|
||||
local game = Game.new(5)
|
||||
game.player.inventory[#game.player.inventory + 1] = Alchemy.herb(Alchemy.NIGHTCAP)
|
||||
|
||||
local path = os.tmpname()
|
||||
game:save(path)
|
||||
end
|
||||
|
||||
local order = {
|
||||
|
||||
Reference in new issue
Block a user