test: cover tests.factionAndProvokedFieldsSurviveEntitySerialization

This commit is contained in:
dualfroz committed 2026-09-10 16:38:26 +02:00
1 parent 90bf3238ac
commit 6995189118
1 file changed
+173
+173
View File
@@ -5667,6 +5667,179 @@ function tests.factionsAppeaseFailsOnceAFactionIsAlreadyMaxedOut()
eq(player.gold, 1000, "nothing is spent when there is nothing to buy")
end
function tests.factionAndProvokedFieldsSurviveEntitySerialization()
local kobold = Monsters.create(Monsters.byKey.kobold, 1, 1, 1, 1)
kobold.provoked = true
local restored = Entity.fromData(kobold:toData())
eq(restored.faction, Factions.GOBLINOID, "a monster's faction tag survives a round trip")
eq(restored.provoked, true, "a monster's provoked flag survives a round trip")
end
function tests.gameTakeMonsterTurnLeavesAWaryGoblinoidAloneUntilProvoked()
local game = stagedGame(1, arena(21, 11), 3, 6)
local kobold = Monsters.create(Monsters.byKey.kobold, 1, 4, 6, 1)
game.level.entities = { kobold }
local hpBefore = game.player.hp
game:takeMonsterTurn(kobold)
eq(kobold.x, 4, "an unprovoked kobold does not close in")
eq(game.player.hp, hpBefore, "an unprovoked kobold does not attack even when adjacent")
end
function tests.gamePlayerAttackProvokesAWaryGoblinoidSoItFightsBackNextTurn()
local game = stagedGame(1, arena(21, 11), 3, 6)
local kobold = Monsters.create(Monsters.byKey.kobold, 1, 4, 6, 1)
kobold.hp = 100
game.level.entities = { kobold }
game:playerAttack(kobold)
eq(kobold.provoked, true, "aiming a melee attack at a wary goblinoid provokes it")
local hpBefore = game.player.hp
game:takeMonsterTurn(kobold)
ok(game.player.hp < hpBefore, "a provoked, adjacent kobold now fights back")
end
function tests.gameAwardKillSoursFactionReputationWithTheFallensFaction()
local game = stagedGame(1, arena(21, 11), 3, 6)
local orc = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
orc.hp = 1
game.level.entities = { orc }
game:playerAttack(orc)
eq(orc:isAlive(), false, "the orc dies to the hit")
eq(Factions.reputationFor(game.player, Factions.ORCISH), -5, "the kill sours standing with the orc's faction")
end
function tests.shopBuyIsMarkedUpWhenMerchantsAreHostile()
local game = stagedGame(1, arena(21, 11), 3, 3)
local keeper = Monsters.create(Monsters.byKey.shopkeeper, 1, 10, 10, 4)
game.level.entities = { keeper }
game.player.gold = 100
Factions.adjust(game.player, Factions.MERCHANT, -40)
game.level.floorItems = { { x = 4, y = 3, item = Items.potionOf("heal"), price = 15, shopId = keeper.id } }
game:movePlayer(1, 0)
eq(game.player.gold, 78, "a hostile guild marks the stamped price up by half, floored")
end
function tests.shopBuyIsDiscountedWhenMerchantsAreHonored()
local game = stagedGame(1, arena(21, 11), 3, 3)
local keeper = Monsters.create(Monsters.byKey.shopkeeper, 1, 10, 10, 4)
game.level.entities = { keeper }
game.player.gold = 100
Factions.adjust(game.player, Factions.MERCHANT, 60)
game.level.floorItems = { { x = 4, y = 3, item = Items.potionOf("heal"), price = 15, shopId = keeper.id } }
game:movePlayer(1, 0)
eq(game.player.gold, 91, "an honored guild discounts the stamped price, floored")
end
function tests.shopSellValueRisesWithMerchantStanding()
local game = stagedGame(1, arena(21, 11), 3, 3)
game.player.inventory = { Items.potionOf("heal") }
local goldBefore = game.player.gold
Factions.adjust(game.player, Factions.MERCHANT, 60)
local value = game:sellItem(1)
eq(value, 10, "an honored guild pays more than the neutral half-price for the same ware")
eq(game.player.gold, goldBefore + 10, "the sale proceeds land in the wallet")
end
function tests.stealingFromShopAlsoSoursMerchantReputation()
local game = stagedGame(1, arena(21, 11), 3, 3)
local keeper = Monsters.create(Monsters.byKey.shopkeeper, 1, 10, 10, 4)
game.level.entities = { keeper }
game.level.floorItems = { { x = 4, y = 3, item = Items.scroll("identify"), price = 18, shopId = keeper.id } }
game:stealFromShop(1)
eq(Factions.reputationFor(game.player, Factions.MERCHANT), -20, "a theft sours the merchant guild's opinion of the player")
end
function tests.resolveQuestNudgesMerchantReputationUpward()
local game = stagedGame(1, arena(21, 11), 5, 5)
game.player.quest = { kind = Quests.CULL, species = "rat", need = 1, have = 1, rewardGold = 30, rewardXp = 15 }
game:resolveQuest()
eq(Factions.reputationFor(game.player, Factions.MERCHANT), 3, "turning in a bounty pads the merchant guild's opinion of the player")
end
function tests.gameAppeaseFactionSpendsGoldAndLogsTheOutcome()
local game = stagedGame(1, arena(21, 11), 3, 3)
game.player.gold = 100
local success = game:appeaseFaction(Factions.GOBLINOID)
eq(success, true, "a well-funded tribute succeeds")
eq(game.player.gold, 80, "the tribute's cost is deducted from the wallet")
eq(Factions.reputationFor(game.player, Factions.GOBLINOID), 15, "the tribute raises standing")
ok(contains(game.log:last(), "goblinoid"), "the successful tribute is logged")
game.player.gold = 0
local failure = game:appeaseFaction(Factions.BANDIT)
eq(failure, false, "an empty purse cannot pay any tribute")
ok(contains(game.log:last(), "gold"), "the failed tribute explains the shortfall")
end
function tests.playerReputationSurvivesGameSaveLoadRoundTrip()
local game = stagedGame(1, arena(21, 11), 3, 3)
Factions.adjust(game.player, Factions.GOBLINOID, -40)
Factions.adjust(game.player, Factions.MERCHANT, 10)
local restored = Game.fromData(game:toData())
eq(Factions.reputationFor(restored.player, Factions.GOBLINOID), -40, "goblinoid standing survives a save/load round trip")
eq(Factions.reputationFor(restored.player, Factions.MERCHANT), 10, "merchant standing survives a save/load round trip")
end
-- Carry weight (encumbrance.lua): a hoard heavy enough to cross a capacity
-- threshold slows the carrier down, and a wide overage on top of that also
-- costs defense. Weight is derived purely from inventory/equipped items and
-- level, so nothing here needs its own save-format handling.
function tests.encumbranceItemWeightScalesWithGearPower()
eq(Encumbrance.itemWeight(Items.weapon("dagger", 2)), 6, "a weak weapon carries close to its flat base weight")
eq(Encumbrance.itemWeight(Items.weapon("greatsword", 9)), 9, "a powerful weapon weighs more than a weak one of the same kind")
eq(Encumbrance.itemWeight(Items.armor("padded vest", 0)), 9, "armor has its own, heavier, flat base")
end
function tests.encumbranceAmmoWeightScalesWithStackCount()
eq(Encumbrance.itemWeight(Items.ammo("arrow", 1)), 1, "a single arrow weighs its unit weight")
eq(Encumbrance.itemWeight(Items.ammo("arrow", 5)), 5, "a stack of arrows weighs the unit weight times the count")
end
function tests.encumbranceCapacityGrowsWithLevel()
eq(Encumbrance.capacityFor({ level = 1 }), 16, "a level 1 carrier's capacity is the flat base plus one level's worth")
eq(Encumbrance.capacityFor({ level = 5 }), 32, "capacity scales linearly with level")
eq(Encumbrance.capacityFor({}), 16, "a missing level defaults to level 1's capacity")
end
function tests.encumbranceEquippedGearCountsTowardCarriedWeight()
local carrier = {
inventory = {},
weapon = Items.weapon("sword", 3), armor = Items.armor("mail", 6),
ring = Items.ring("band", 1, 1), bow = Items.bow("bow", 2, 3),
}
eq(Encumbrance.carriedWeight(carrier), 26, "worn and wielded gear counts exactly like the same items in the pack")
end
function tests.encumbranceLightLoadHasNoPenalty()
local game = Game.new(1)
eq(game.player:encumbranceTier(), Encumbrance.LIGHT, "a fresh player's starting kit is well under capacity")
eq(game.player:effectiveSpeed(), 100, "a light load never slows the carrier down")
eq(game.player:effectiveDefense(), 1, "a light load never costs defense")
end
-- Each padding armor has zero power (so bestUpgrade never mistakes it for an
-- upgrade) and so weighs exactly the base 9.
local function padWith(entity, count)
for _ = 1, count do
entity.inventory[#entity.inventory + 1] = Items.armor("lockbox", 0)
end
end
function tests.encumbranceBurdenedSlowsMovementButNotDefense()
local game = Game.new(1)
game.player.inventory = {}
padWith(game.player, 2)
ok(Encumbrance.carriedWeight(game.player) > Encumbrance.capacityFor(game.player),
"two heavy armors alone clear the fresh player's capacity")
eq(game.player:encumbranceTier(), Encumbrance.BURDENED, "a moderate overage is merely burdened")
eq(game.player:effectiveSpeed(), 80, "a burdened carrier loses a flat chunk of speed")
eq(game.player:effectiveDefense(), 1, "a burdened carrier's defense is untouched")
end
function tests.encumbranceOverloadedSlowsMovementAndWeakensDefense()
local game = Game.new(1)
end
local order = {
"rngDeterminism", "mapDeterminism", "tilesRegistry", "fovKnownScene", "losSightLine",
"astarPathing", "combatResolution", "itemsAndEquipment", "affixes", "statusEffects",