test: cover tests.ascensionSightPenaltySumsAcrossActiveMutators
This commit is contained in:
1 parent
1c5b6ab591
commit
a9d2900e79
1 file changed
+173
+173
@@ -8779,6 +8779,179 @@ function tests.ascensionEssenceMultiplierAppliesFamineAlone()
|
||||
eq(Ascension.essenceMultiplier({ "famine" }), 0.5, "famine halves essence income")
|
||||
end
|
||||
|
||||
function tests.ascensionSightPenaltySumsAcrossActiveMutators()
|
||||
eq(Ascension.sightPenalty(nil), 0, "no active mutators carries no sight penalty")
|
||||
eq(Ascension.sightPenalty({ "nightblind" }), 2, "nightblind carries its own fixed sight penalty")
|
||||
end
|
||||
|
||||
function tests.ascensionRenownBonusPercentSumsRatherThanCompounds()
|
||||
eq(Ascension.renownBonusPercent(nil), 0, "no active mutators adds no renown bonus")
|
||||
eq(Ascension.renownBonusPercent({ "famine" }), 10, "famine alone adds its own fixed bonus")
|
||||
eq(Ascension.renownBonusPercent({ "famine", "nightblind" }), 20, "two mutators add their bonuses, not compound them")
|
||||
end
|
||||
|
||||
function tests.ascensionApplyToPlayerHalvesMaxHpAndDoublesAttackForGlassCannon()
|
||||
local player = { maxHp = 30, hp = 30, baseAttack = 5, attack = 5, baseDefense = 1, defense = 1 }
|
||||
Ascension.applyToPlayer({ "glasscannon" }, player)
|
||||
eq(player.maxHp, 15, "glass cannon halves max hp")
|
||||
eq(player.hp, 15, "current hp is clamped down to the new max")
|
||||
eq(player.attack, 10, "glass cannon doubles attack")
|
||||
eq(player.baseAttack, 10, "glass cannon doubles base attack alongside the equipped total")
|
||||
eq(player.defense, 1, "glass cannon leaves defense untouched")
|
||||
end
|
||||
|
||||
function tests.ascensionApplyToPlayerHalvesDefenseForFrail()
|
||||
local player = { maxHp = 30, hp = 30, baseAttack = 5, attack = 5, baseDefense = 3, defense = 3 }
|
||||
Ascension.applyToPlayer({ "frail" }, player)
|
||||
eq(player.defense, 1, "frail floors defense down by half")
|
||||
eq(player.baseDefense, 1, "frail floors base defense down by half")
|
||||
eq(player.attack, 5, "frail leaves attack untouched")
|
||||
end
|
||||
|
||||
function tests.gameNewResolvesRequestedMutatorsAgainstMetaAndAppliesTheirDeltas()
|
||||
local meta = Meta.new()
|
||||
meta.bestDepth = 4
|
||||
meta.wins = 1
|
||||
local game = Game.new(5, { meta = meta, mutators = { "unknownkey", "glasscannon", "frail" } })
|
||||
eq(#game.ascension, 2, "only the two unlocked, known mutators survive resolution")
|
||||
eq(game.ascension[1], "frail", "active mutators are ordered by the catalog, not the request")
|
||||
eq(game.ascension[2], "glasscannon", "active mutators are ordered by the catalog, not the request")
|
||||
eq(game.player.maxHp, 15, "glasscannon halves the fresh player's max hp")
|
||||
eq(game.player.defense, 0, "frail halves the fresh player's base defense down from 1")
|
||||
end
|
||||
|
||||
function tests.gameMonsterMeleeDamageStacksBloodboundWithBloodmoonMultiplicatively()
|
||||
local meta = Meta.new()
|
||||
meta.bestDepth = 3
|
||||
local game = Game.new(1, { meta = meta, mutators = { "bloodbound" } })
|
||||
game.level = arena(21, 11)
|
||||
game.player.x, game.player.y = 3, 6
|
||||
game:computeVisible()
|
||||
local orc = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
|
||||
|
||||
local before = game.player.hp
|
||||
game.ascension = {}
|
||||
game:monsterMelee(orc)
|
||||
local plainDamage = before - game.player.hp
|
||||
|
||||
game.player.hp = before
|
||||
game.ascension = { "bloodbound" }
|
||||
game:monsterMelee(orc)
|
||||
local bloodboundDamage = before - game.player.hp
|
||||
eq(bloodboundDamage, math.floor(plainDamage * 1.5), "bloodbound alone scales melee damage by its own multiplier")
|
||||
|
||||
game.player.hp = before
|
||||
game.level.event = { key = Events.BLOODMOON, turnsLeft = 5 }
|
||||
game:monsterMelee(orc)
|
||||
local stacked = before - game.player.hp
|
||||
eq(stacked, math.floor(plainDamage * 1.5 * Events.BLOODMOON_MULTIPLIER),
|
||||
"bloodbound and bloodmoon stack multiplicatively on the same hit")
|
||||
end
|
||||
|
||||
function tests.gameKillEssenceStacksFamineWithRichVeinsMultiplicatively()
|
||||
local meta = Meta.new()
|
||||
meta.bestDepth = 2
|
||||
local game = Game.new(1, { meta = meta, mutators = { "famine" } })
|
||||
game.level = arena(21, 11)
|
||||
game.player.x, game.player.y = 3, 6
|
||||
game:computeVisible()
|
||||
|
||||
local rat = Monsters.create(Monsters.byKey.rat, 1, 4, 6, 1)
|
||||
rat.hp = 1
|
||||
game.level.entities = { rat }
|
||||
game.ascension = {}
|
||||
local before = game.player.essence
|
||||
game:playerAttack(rat)
|
||||
local plainEssence = game.player.essence - before
|
||||
|
||||
local rat2 = Monsters.create(Monsters.byKey.rat, 2, 4, 6, 1)
|
||||
rat2.hp = 1
|
||||
game.level.entities = { rat2 }
|
||||
game.player.essence = before
|
||||
game.ascension = { "famine" }
|
||||
game:playerAttack(rat2)
|
||||
local famineEssence = game.player.essence - before
|
||||
eq(famineEssence, math.floor(plainEssence * 0.5), "famine alone halves essence earned from a kill")
|
||||
|
||||
local rat3 = Monsters.create(Monsters.byKey.rat, 3, 4, 6, 1)
|
||||
rat3.hp = 1
|
||||
game.level.entities = { rat3 }
|
||||
game.player.essence = before
|
||||
game.level.event = { key = Events.RICHVEINS, turnsLeft = 5 }
|
||||
game:playerAttack(rat3)
|
||||
local stackedEssence = game.player.essence - before
|
||||
eq(stackedEssence, plainEssence,
|
||||
"famine's half and rich veins' double multiply to exactly cancel back to the plain amount")
|
||||
end
|
||||
|
||||
function tests.gameEffectiveRadiusStacksNightblindWithHush()
|
||||
local meta = Meta.new()
|
||||
meta.bestDepth = 2
|
||||
local game = Game.new(1, { meta = meta, mutators = { "nightblind" } })
|
||||
game.ascension = {}
|
||||
local noAscension = game:effectiveRadius()
|
||||
game.ascension = { "nightblind" }
|
||||
eq(game:effectiveRadius(), noAscension - 2, "nightblind alone shrinks the effective sight radius by its own penalty")
|
||||
game.level.event = { key = Events.HUSH, turnsLeft = 5 }
|
||||
eq(game:effectiveRadius(), noAscension - 2 - Events.HUSH_SIGHT_PENALTY,
|
||||
"nightblind and hush both shrink the effective sight radius, summed together")
|
||||
end
|
||||
|
||||
function tests.metaRenownForAddsTheAscensionBonusOnTopOfTheBaseScore()
|
||||
local stats = { deepestDepth = 5, bossKills = 0, questsCompleted = 0, won = false }
|
||||
local base = Meta.renownFor(stats)
|
||||
stats.mutators = { "famine" }
|
||||
eq(Meta.renownFor(stats), base + (base * 10) // 100, "famine's 10% renown bonus is added on top of the base score")
|
||||
stats.mutators = { "famine", "nightblind" }
|
||||
eq(Meta.renownFor(stats), base + (base * 20) // 100, "two mutators' renown bonuses sum before being applied")
|
||||
end
|
||||
|
||||
function tests.ascensionFieldSurvivesASaveLoadRoundTrip()
|
||||
local meta = Meta.new()
|
||||
meta.bestDepth = 3
|
||||
local game = Game.new(4, { meta = meta, mutators = { "bloodbound" } })
|
||||
local path = os.tmpname()
|
||||
game:save(path)
|
||||
local restored = Game.load(path)
|
||||
os.remove(path)
|
||||
eq(#restored.ascension, 1, "the active ascension mutator list survives a save/load round trip")
|
||||
eq(restored.ascension[1], "bloodbound", "the restored mutator key matches what was active")
|
||||
end
|
||||
|
||||
function tests.renderPanelShowsActiveAscensionMutatorsByKey()
|
||||
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||
ok(not contains(Render.panel(game), "ascension"), "no active mutators shows no ascension line at all")
|
||||
game.ascension = { "famine", "nightblind" }
|
||||
ok(contains(Render.panel(game), "ascension famine,nightblind"), "active mutators are listed by key in the panel")
|
||||
end
|
||||
|
||||
-- durability.lua: weapon/armor/bow wear and repair.
|
||||
|
||||
function tests.durabilityFreshWeaponStartsAtMaxAndIsNotBroken()
|
||||
local sword = Items.weapon("sword", 4)
|
||||
eq(sword.durability, Durability.MAX, "a freshly forged weapon starts at max durability")
|
||||
eq(sword.maxDurability, Durability.MAX, "a freshly forged weapon's max durability matches the catalog default")
|
||||
ok(not Durability.isBroken(sword), "a freshly forged weapon is not broken")
|
||||
end
|
||||
|
||||
function tests.durabilityWearWeaponDecrementsAndBreaksAtZeroSubtractingItsPower()
|
||||
local hero = { attack = 5, inventory = {}, kind = "player", name = "you" }
|
||||
Items.equipWeapon(Items.weapon("sword", 4), hero)
|
||||
eq(hero.attack, 9, "the sword's power is baked into attack on equip")
|
||||
local broke
|
||||
for _ = 1, Durability.MAX - 1 do
|
||||
broke = Durability.wearWeapon(hero)
|
||||
ok(broke == nil, "the sword survives every hit before its last")
|
||||
end
|
||||
eq(hero.weapon.durability, 1, "one point of durability remains before the final hit")
|
||||
broke = Durability.wearWeapon(hero)
|
||||
ok(broke ~= nil, "the hit that empties durability reports a break")
|
||||
ok(contains(broke, "breaks apart"), "the break line names what happened")
|
||||
eq(hero.weapon.durability, 0, "durability floors at exactly zero")
|
||||
eq(hero.attack, 5, "the sword's power is pulled back out of attack the instant it breaks")
|
||||
end
|
||||
|
||||
function tests.durabilityWearWeaponIsANoOpOnceAlreadyBroken()
|
||||
end
|
||||
|
||||
local order = {
|
||||
|
||||
Reference in new issue
Block a user