test: cover tests.sellItemGrantsHalfPriceAndRemovesIt
This commit is contained in:
1 parent
e7b64c1e92
commit
f5f4e93cd3
1 file changed
+173
+173
@@ -2205,6 +2205,179 @@ function tests.passiveShopkeeperIgnoresTheAdjacentPlayer()
|
|||||||
eq(game.player.hp, hpBefore, "an un-robbed shopkeeper does not attack even when adjacent")
|
eq(game.player.hp, hpBefore, "an un-robbed shopkeeper does not attack even when adjacent")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function tests.sellItemGrantsHalfPriceAndRemovesIt()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 3)
|
||||||
|
local index = #game.player.inventory
|
||||||
|
local item = game.player.inventory[index]
|
||||||
|
local expected = Shop.sellValue(item)
|
||||||
|
local goldBefore = game.player.gold
|
||||||
|
local value = game:sellItem(index)
|
||||||
|
eq(value, expected, "selling returns the catalog half-price")
|
||||||
|
eq(game.player.gold, goldBefore + expected, "the sale proceeds land in the wallet")
|
||||||
|
eq(#game.player.inventory, index - 1, "the sold item leaves the inventory")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.builderOccasionallySeedsAShopWithMatchingWares()
|
||||||
|
local level = Builder.build(4, Rng.new(1), {})
|
||||||
|
local keeper
|
||||||
|
for _, e in ipairs(level.entities) do
|
||||||
|
if e.species == "shopkeeper" then keeper = e end
|
||||||
|
end
|
||||||
|
ok(keeper ~= nil, "this seed's depth-4 floor spawns a shopkeeper")
|
||||||
|
local waresFound = 0
|
||||||
|
for _, ground in ipairs(level.floorItems) do
|
||||||
|
if ground.price then
|
||||||
|
waresFound = waresFound + 1
|
||||||
|
eq(ground.shopId, keeper.id, "every priced ware is tagged with its shopkeeper's id")
|
||||||
|
ok(ground.price > 0, "every priced ware has a positive price")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ok(waresFound >= 3, "the shop stocks at least three wares")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.shopAndLooseGoldStayOffTheShallowFloors()
|
||||||
|
for depth = 1, 3 do
|
||||||
|
local level = Builder.build(depth, Rng.new(1), {})
|
||||||
|
for _, ground in ipairs(level.floorItems) do
|
||||||
|
ok(ground.item.kind ~= Items.GOLD, "no loose gold appears above depth 4")
|
||||||
|
ok(not ground.price, "no shop ware appears above depth 4")
|
||||||
|
end
|
||||||
|
for _, e in ipairs(level.entities) do
|
||||||
|
ok(e.species ~= "shopkeeper", "no shopkeeper appears above depth 4")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function arenaDark(w, h)
|
||||||
|
local map = Map.new(w, h)
|
||||||
|
for y = 2, h - 1 do for x = 2, w - 1 do map:set(x, y, Map.FLOOR) end end
|
||||||
|
return Level.new({
|
||||||
|
depth = 1, generator = "arena", map = map,
|
||||||
|
entities = {}, floorItems = {}, traps = {}, lights = {}, dark = true, explored = {},
|
||||||
|
upX = 2, upY = 2, downX = w - 1, downY = h - 1,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.classApplyGrantsFighterStatBonuses()
|
||||||
|
local base = Game.new(9)
|
||||||
|
local fighter = Game.new(9, { class = "fighter" })
|
||||||
|
eq(fighter.player.class, "fighter", "the requested class is stamped onto the player")
|
||||||
|
eq(fighter.player.maxHp, base.player.maxHp + 8, "fighter grants a flat max hp bonus")
|
||||||
|
eq(fighter.player.hp, base.player.hp + 8, "the hp bonus also tops up current hp")
|
||||||
|
eq(fighter.player.attack, base.player.attack + 2, "fighter grants a flat attack bonus")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.classApplyGrantsRogueGoldAndAttack()
|
||||||
|
local base = Game.new(9)
|
||||||
|
local rogue = Game.new(9, { class = "rogue" })
|
||||||
|
eq(rogue.player.gold, base.player.gold + 15, "rogue starts with extra gold")
|
||||||
|
eq(rogue.player.attack, base.player.attack + 1, "rogue grants a small attack bonus")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.classApplyGrantsMageManaAndAttack()
|
||||||
|
local base = Game.new(9)
|
||||||
|
local mage = Game.new(9, { class = "mage" })
|
||||||
|
eq(mage.player.maxMana, base.player.maxMana + 8, "mage grants a flat mana bonus")
|
||||||
|
eq(mage.player.mana, base.player.mana + 8, "the mana bonus also tops up current mana")
|
||||||
|
eq(mage.player.attack, base.player.attack - 1, "mage trades some attack for its mana pool")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.classApplyGrantsRangerBowAndAmmo()
|
||||||
|
local ranger = Game.new(9, { class = "ranger" })
|
||||||
|
ok(ranger.player.bow ~= nil, "ranger starts with a bow equipped")
|
||||||
|
local hasAmmo = false
|
||||||
|
for _, item in ipairs(ranger.player.inventory) do
|
||||||
|
if item.kind == Items.AMMO then hasAmmo = true end
|
||||||
|
end
|
||||||
|
ok(hasAmmo, "ranger starts with a stack of arrows")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.invalidClassIsIgnored()
|
||||||
|
local base = Game.new(9)
|
||||||
|
local weird = Game.new(9, { class = "wizard" })
|
||||||
|
eq(weird.player.class, nil, "an unrecognised class name is not applied")
|
||||||
|
eq(weird.player.maxHp, base.player.maxHp, "stats are unaffected by an invalid class")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.skillLearnRespectsClassLevelAndPoints()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
game.player.class = "fighter"
|
||||||
|
game.player.skillPoints = 0
|
||||||
|
eq(game:learnSkill("cleave"), false, "learning fails without a banked skill point")
|
||||||
|
game.player.skillPoints = 1
|
||||||
|
eq(game:learnSkill("backstab"), false, "learning a skill from another class fails")
|
||||||
|
eq(game.player.skillPoints, 1, "a failed learn spends no point")
|
||||||
|
eq(game:learnSkill("berserk"), false, "learning fails below the skill's level requirement")
|
||||||
|
eq(game:learnSkill("cleave"), true, "a same-class, level-eligible skill with a point banked learns")
|
||||||
|
eq(game.player.skillPoints, 0, "learning spends the point")
|
||||||
|
eq(Skills.has(game.player, "cleave"), true, "the skill is now known")
|
||||||
|
game.player.skillPoints = 1
|
||||||
|
eq(game:learnSkill("cleave"), false, "an already-known skill cannot be learned twice")
|
||||||
|
eq(game.player.skillPoints, 1, "re-learning a known skill spends no point")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.skillCooldownBlocksImmediateReuseAndTicksDown()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
game.player.class = "fighter"
|
||||||
|
game.player.skills.secondWind = true
|
||||||
|
game.player.hp = game.player.maxHp - 10
|
||||||
|
eq(game:useSkill("secondWind"), true, "second wind activates while off cooldown")
|
||||||
|
eq(game:useSkill("secondWind"), false, "an immediate reuse fails while on cooldown")
|
||||||
|
for _ = 1, Skills.active.secondWind.cooldown do
|
||||||
|
game:endPlayerTurn()
|
||||||
|
end
|
||||||
|
eq(game.player.skillCooldowns.secondWind, 0, "the cooldown ticks down to zero over that many player turns")
|
||||||
|
game.player.hp = game.player.maxHp - 10
|
||||||
|
eq(game:useSkill("secondWind"), true, "second wind is usable again once its cooldown fully elapses")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.fighterCleaveHitsPrimaryAndAdjacentMonsters()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
game.player.class = "fighter"
|
||||||
|
game.player.skills.cleave = true
|
||||||
|
local primary = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
|
||||||
|
local splash = Monsters.create(Monsters.byKey.orc, 2, 3, 5, 1)
|
||||||
|
game.level.entities = { primary, splash }
|
||||||
|
local success = game:useSkill("cleave", primary)
|
||||||
|
eq(success, true, "cleave lands on an adjacent primary target")
|
||||||
|
ok(primary.hp < primary.maxHp, "the primary target takes damage")
|
||||||
|
ok(splash.hp < splash.maxHp, "a second monster adjacent to the player also takes splash damage")
|
||||||
|
ok((splash.maxHp - splash.hp) < (primary.maxHp - primary.hp), "the splash hit deals less than the primary hit")
|
||||||
|
eq(game.player.skillCooldowns.cleave, Skills.active.cleave.cooldown, "cleave starts its own cooldown")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cleave's splash targets must be mitigated by the wielder's own weapon
|
||||||
|
-- damage type exactly like the primary target already is through
|
||||||
|
-- Combat.damage/Combat.attack (see combat.lua's damageTypeOf and affixes.lua's
|
||||||
|
-- own comment that a flaming sword "can do nothing at all against something
|
||||||
|
-- fire-immune") - before this test the splash loop subtracted a raw
|
||||||
|
-- attack-minus-defense number straight off hp with no Resistances.apply call
|
||||||
|
-- at all, so a flaming weapon's cleave splash burned through fire immunity
|
||||||
|
-- that the very same swing's primary hit already respected.
|
||||||
|
function tests.fighterCleaveSplashRespectsAFireImmuneTargetLikeThePrimaryHitAlreadyDoes()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
game.player.class = "fighter"
|
||||||
|
game.player.skills.cleave = true
|
||||||
|
game.player.weapon = Affixes.decorate(Items.weapon("dagger", 2), Affixes.weapon[3])
|
||||||
|
eq(game.player.weapon.damageType, "fire", "the flaming affix retags the dagger's swing as fire")
|
||||||
|
local primary = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
|
||||||
|
local immune = Monsters.create(Monsters.byKey.dragon, 2, 3, 5, 6)
|
||||||
|
game.level.entities = { primary, immune }
|
||||||
|
game:useSkill("cleave", primary)
|
||||||
|
ok(primary.hp < primary.maxHp, "the primary target still burns for full mitigated damage")
|
||||||
|
eq(immune.hp, immune.maxHp, "a fire-immune splash target takes zero cleave splash from a flaming weapon")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.fighterSecondWindRestoresPortionOfMaxHp()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
game.player.class = "fighter"
|
||||||
|
game.player.skills.secondWind = true
|
||||||
|
game.player.hp = 5
|
||||||
|
game:useSkill("secondWind")
|
||||||
|
eq(game.player.hp, 5 + math.floor(game.player.maxHp * 0.3), "second wind restores three tenths of max hp")
|
||||||
|
|
||||||
|
game.player.skillCooldowns.secondWind = 0
|
||||||
|
game.player.hp = game.player.maxHp - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local order = {
|
local order = {
|
||||||
|
|||||||
Reference in new issue
Block a user