test: cover tests.momentumFieldSurvivesSerialization

This commit is contained in:
dualfroz committed 2026-09-11 14:41:19 +02:00
1 parent 3115db3fe3
commit 607c19a95f
1 file changed
+173
+173
View File
@@ -7568,6 +7568,179 @@ function tests.renderPanelShowsComboOnlyOnceItIsBuilt()
ok(contains(Render.panel(game), "combo x3"), "a built-up combo is reported in the status panel")
end
function tests.momentumFieldSurvivesSerialization()
local game = Game.new(3)
game.player.combo = 4
local path = os.tmpname()
game:save(path)
local restored = Game.load(path)
os.remove(path)
eq(restored.player.combo, 4, "the player's momentum stack survives a save/load round trip")
end
function tests.conductivityIsWetReadsTheEntitysOwnWetField()
eq(Conductivity.isWet({}), false, "an entity with no wet field at all reads as dry")
eq(Conductivity.isWet({ wet = 0 }), false, "a wet counter already run down to zero reads as dry")
eq(Conductivity.isWet({ wet = 1 }), true, "any positive wet counter reads as wet")
end
function tests.conductivityTickSoaksOnWaterAndDecaysOneStepAtATimeOffIt()
local level = arena(21, 11)
level.map:set(6, 6, Tiles.WATER)
local entity = { x = 6, y = 6 }
Conductivity.tick(level, entity)
eq(entity.wet, Conductivity.SOAK_DURATION, "standing on water soaks an entity to the full duration")
entity.x = 7
for i = Conductivity.SOAK_DURATION - 1, 0, -1 do
Conductivity.tick(level, entity)
eq(entity.wet, i, "the soak counter decays by exactly one per turn spent off water")
end
eq(Conductivity.isWet(entity), false, "the counter bottoms out at zero rather than going negative")
end
function tests.conductivityTickReSoaksImmediatelyOnSteppingBackOntoWater()
local level = arena(21, 11)
level.map:set(6, 6, Tiles.WATER)
local entity = { x = 6, y = 6, wet = 1 }
Conductivity.tick(level, entity)
eq(entity.wet, Conductivity.SOAK_DURATION,
"stepping back onto water refreshes the counter to full rather than adding to it")
end
function tests.conductivityApplyFireDampensDamageOnlyOnceSoaked()
eq(Conductivity.applyFire({}, 10), 10, "fire damage against a dry entity is untouched")
eq(Conductivity.applyFire({ wet = 2 }, 10), 5,
"fire damage against a soaked entity is halved on top of its resistance tier")
end
function tests.conductivityApplyLightningBonusesDamageOnlyOnceSoaked()
eq(Conductivity.applyLightning({}, 10), 10, "lightning damage against a dry entity is untouched")
eq(Conductivity.applyLightning({ wet = 2 }, 10), 15,
"lightning damage against a soaked entity gets a 50 percent conductivity bonus")
end
function tests.conductivityNeverConjuresDamageOutOfAnImmuneResistance()
eq(Conductivity.applyFire({ wet = 2, resist = { fire = Resistances.IMMUNE } }, 10), 0,
"fire immunity still floors damage at zero even while soaked")
eq(Conductivity.applyLightning({ wet = 2, resist = { lightning = Resistances.IMMUNE } }, 10), 0,
"lightning immunity still floors damage at zero even while soaked")
end
function tests.conductivityResistsIgnitionOnlyWhileWet()
eq(Conductivity.resistsIgnition({}), false, "a dry entity does not resist ignition")
eq(Conductivity.resistsIgnition({ wet = 3 }), true, "a soaked entity resists catching fire")
end
function tests.statusApplyRefusesBurningOnlyWhileSoakedButOtherEffectsStillApply()
local entity = Entity.new({ x = 1, y = 1, glyph = "@", name = "you", kind = "player", hp = 20, attack = 6, defense = 2 })
entity.wet = 2
local result = Status.apply(entity, Status.BURNING, 3, 2)
eq(result, nil, "applying burning to a soaked entity is refused outright")
eq(Status.has(entity, Status.BURNING), false, "a refused burning application leaves no trace on the entity")
Status.apply(entity, Status.POISON, 3, 2)
eq(Status.has(entity, Status.POISON), true, "a soaked entity still catches every other status effect normally")
entity.wet = 0
Status.apply(entity, Status.BURNING, 3, 2)
eq(Status.has(entity, Status.BURNING), true, "once dry again, the same entity catches fire normally")
end
function tests.statusRemoveStripsANamedStatusRegardlessOfItsRemainingDuration()
local entity = Entity.new({ x = 1, y = 1, glyph = "@", name = "you", kind = "player", hp = 20, attack = 6, defense = 2 })
Status.apply(entity, Status.POISON, 99, 2)
eq(Status.remove(entity, Status.POISON), true, "removing a status that is present reports success")
eq(Status.has(entity, Status.POISON), false, "the removed status no longer reads as active")
eq(Status.remove(entity, Status.POISON), false, "removing an already-absent status reports nothing to do")
eq(Status.remove(entity, Status.CONFUSE), false, "removing a status that was never applied at all also reports nothing to do")
end
function tests.statusCureHostileLiftsEveryDebuffButLeavesBuffsUntouched()
local entity = Entity.new({ x = 1, y = 1, glyph = "@", name = "you", kind = "player", hp = 20, attack = 6, defense = 2 })
Status.apply(entity, Status.POISON, 5, 2)
Status.apply(entity, Status.CONFUSE, 6, 1)
Status.apply(entity, Status.STUN, 2, 1)
Status.apply(entity, Status.STRENGTH, 30, 3)
Status.apply(entity, Status.SHIELD, 15, 1)
local lifted = Status.cureHostile(entity)
eq(#lifted, 3, "cureHostile reports exactly the three debuffs it lifted")
eq(Status.has(entity, Status.POISON), false, "poison is gone after cureHostile")
eq(Status.has(entity, Status.CONFUSE), false, "confuse is gone after cureHostile")
eq(Status.has(entity, Status.STUN), false, "stun is gone after cureHostile")
ok(Status.has(entity, Status.STRENGTH), "a buff like strength survives cureHostile untouched")
ok(Status.has(entity, Status.SHIELD), "a buff like shield survives cureHostile untouched")
eq(#Status.cureHostile(entity), 0, "calling cureHostile again with nothing hostile left returns an empty list")
end
function tests.hazardsAfflictDampensFireAndBlocksIgnitionForASoakedVictim()
local level = arena(21, 11)
Hazards.ignite(level, 5, 5, 3, 4)
local victim = Monsters.create(Monsters.byKey.rat, 1, 5, 5, 1)
victim.wet = 2
local hpBefore = victim.hp
local line = Hazards.afflict(level, victim)
eq(victim.hp, hpBefore - 2, "standing in fire while soaked only deals half the usual potency")
ok(contains(line, "soaked"), "the afflict message calls out the victim's own soaked skin")
eq(Status.has(victim, Status.BURNING), false, "a soaked victim standing in fire never actually catches alight")
end
function tests.gameConductivityTicksAMonsterEachTurnBasedOnTheTileUnderfoot()
local level = arena(21, 11)
level.map:set(6, 6, Tiles.WATER)
local game = stagedGame(1, level, 3, 6)
local rat = Monsters.create(Monsters.byKey.rat, 1, 6, 6, 1)
game.level.entities = { rat }
game:takeMonsterTurn(rat)
eq(rat.wet, Conductivity.SOAK_DURATION, "a monster ending its turn on water is soaked to the full duration")
rat.x, rat.y = 7, 6
game:takeMonsterTurn(rat)
eq(rat.wet, Conductivity.SOAK_DURATION - 1, "moving off water the following turn lets the soak counter decay by one")
end
function tests.gameConductivityTicksThePlayerEachAutoStepBasedOnTheTileUnderfoot()
local level = arena(21, 11)
level.map:set(3, 6, Tiles.WATER)
local game = stagedGame(1, level, 3, 6)
game:autoStep()
eq(game.player.wet, Conductivity.SOAK_DURATION, "the player ending a turn on water is soaked to the full duration")
end
function tests.spellShockBoltDealsMoreDamageToASoakedTargetThanADryOne()
local dryGame = stagedGame(1, arena(21, 11), 3, 6)
Spells.learn(dryGame.player, "shock")
local dry = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
dry.hp, dry.maxHp = 100, 100
dryGame.level.entities = { dry }
local castOk = dryGame:castSpell("shock", dry)
ok(castOk, "casting a learned, in-range shock bolt succeeds")
local dryDealt = 100 - dry.hp
ok(dryDealt > 0, "shock bolt deals real lightning damage to a dry target")
local wetGame = stagedGame(1, arena(21, 11), 3, 6)
Spells.learn(wetGame.player, "shock")
local soaked = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
soaked.hp, soaked.maxHp = 100, 100
soaked.wet = 2
wetGame.level.entities = { soaked }
wetGame:castSpell("shock", soaked)
local wetDealt = 100 - soaked.hp
eq(wetDealt, dryDealt + dryDealt // 2,
"shock bolt deals exactly its 50 percent conductivity bonus against a soaked target")
ok(contains(wetGame.log:last(), "water clinging"),
"casting shock bolt on a soaked target reports the conductivity through water")
end
function tests.renderPanelShowsWetOnlyWhileSoaked()
local game = stagedGame(1, arena(21, 11), 3, 6)
ok(not contains(Render.panel(game), "wet"), "a fresh dry player shows no wet line")
game.player.wet = 2
ok(contains(Render.panel(game), "wet (2)"), "a soaked player's status panel reports the remaining soak turns")
end
function tests.builderCanRollATomeOfTheShockBoltSpell()
-- Range widened from 100 to 200: adding "cleanse" to Builder's `learnable`
-- list shifted rng:range(1, #learnable)'s result for effectively every
-- seed, and the nearest shock tome under the new list happens to sit at
-- seed 174 - still well within "a reasonable seed range".
local found
end
local order = {