test: cover tests.craftingTemperRefusesWithoutEnoughEssence
This commit is contained in:
1 parent
d46d2a49d7
commit
bf8645b0ad
1 file changed
+173
+173
@@ -2724,6 +2724,179 @@ end
|
|||||||
function tests.craftingTemperRaisesPowerAtRisingCostUpToACap()
|
function tests.craftingTemperRaisesPowerAtRisingCostUpToACap()
|
||||||
local game = stagedGame(1, arena(21, 11), 3, 6)
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
local weapon = Items.weapon("dagger", 2)
|
local weapon = Items.weapon("dagger", 2)
|
||||||
|
Items.equipWeapon(weapon, game.player)
|
||||||
|
local attackBaseline = game.player.attack
|
||||||
|
local totalCost = 0
|
||||||
|
for level = 0, Crafting.TEMPER_MAX - 1 do
|
||||||
|
totalCost = totalCost + (level + 1) * 4
|
||||||
|
end
|
||||||
|
game.player.essence = totalCost
|
||||||
|
|
||||||
|
for level = 1, Crafting.TEMPER_MAX do
|
||||||
|
ok(Crafting.temper(game.player, weapon), "temper step " .. level .. " succeeds with enough essence")
|
||||||
|
end
|
||||||
|
eq(weapon.power, 2 + Crafting.TEMPER_MAX, "each temper step adds exactly one flat power")
|
||||||
|
eq(game.player.attack, attackBaseline + Crafting.TEMPER_MAX,
|
||||||
|
"tempering an equipped weapon updates attack immediately")
|
||||||
|
eq(game.player.essence, 0, "the full ladder of rising costs is spent exactly")
|
||||||
|
|
||||||
|
local success, reason = Crafting.temper(game.player, weapon)
|
||||||
|
eq(success, false, "a fully tempered item refuses a further step")
|
||||||
|
eq(reason, "fully tempered", "the refusal names the reason")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.craftingTemperRefusesWithoutEnoughEssence()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
local weapon = Items.weapon("mace", 3)
|
||||||
|
Items.equipWeapon(weapon, game.player)
|
||||||
|
game.player.essence = 3
|
||||||
|
local success, reason = Crafting.temper(game.player, weapon)
|
||||||
|
eq(success, false, "tempering fails when essence is short of the cost")
|
||||||
|
eq(reason, "not enough essence", "the refusal names the reason")
|
||||||
|
eq(weapon.power, 3, "a failed temper leaves power untouched")
|
||||||
|
eq(game.player.essence, 3, "a failed temper spends nothing")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.craftingEnchantGrantsAChosenAffixOnce()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
local weapon = Items.weapon("dagger", 2)
|
||||||
|
Items.equipWeapon(weapon, game.player)
|
||||||
|
local attackBaseline = game.player.attack
|
||||||
|
game.player.essence = Crafting.ENCHANT_COST
|
||||||
|
|
||||||
|
ok(Crafting.enchant(game.player, weapon, "flaming"), "enchanting an unaffixed weapon succeeds")
|
||||||
|
eq(weapon.affix.key, "flaming", "the chosen affix is attached, not a random roll")
|
||||||
|
eq(weapon.power, 3, "the affix's power lands on the item")
|
||||||
|
eq(weapon.name, "flaming dagger", "the affix prefix is applied to the name")
|
||||||
|
eq(game.player.attack, attackBaseline + 1, "enchanting an equipped weapon updates attack immediately")
|
||||||
|
eq(game.player.essence, 0, "the enchant cost is spent")
|
||||||
|
|
||||||
|
local repeatSuccess, repeatReason = Crafting.enchant(game.player, weapon, "vicious")
|
||||||
|
eq(repeatSuccess, false, "an already-affixed weapon refuses a second enchant")
|
||||||
|
eq(repeatReason, "already bears an affix", "the refusal names the reason")
|
||||||
|
|
||||||
|
local badSuccess, badReason = Crafting.enchant(game.player, Items.armor("leather armor", 1), "not-a-real-affix")
|
||||||
|
eq(badSuccess, false, "an unknown affix key is refused")
|
||||||
|
eq(badReason, "unknown affix", "the refusal names the reason")
|
||||||
|
|
||||||
|
local ringSuccess, ringReason = Crafting.enchant(game.player, Items.ring("ring of power", 1, 1), "sharp")
|
||||||
|
eq(ringSuccess, false, "gear without an affix pool cannot be enchanted")
|
||||||
|
eq(ringReason, "cannot be enchanted", "the refusal names the reason")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.craftingDisenchantStripsAffixAndRefundsEssence()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
local armor = Affixes.decorate(Items.armor("chain mail", 2), Affixes.armor[2])
|
||||||
|
Items.equipArmor(armor, game.player)
|
||||||
|
local defenseBaseline = game.player.defense
|
||||||
|
eq(armor.name, "reinforced chain mail", "sanity check: the drop rolled the expected affix")
|
||||||
|
|
||||||
|
local success, refund = Crafting.disenchant(game.player, armor)
|
||||||
|
ok(success, "disenchanting an affixed armor succeeds")
|
||||||
|
eq(refund, Crafting.FORGE_COST + 2 * 3, "the refund scales with the stripped affix's power")
|
||||||
|
eq(armor.power, 2, "power drops back to its base value")
|
||||||
|
eq(armor.name, "chain mail", "the affix prefix is removed from the name")
|
||||||
|
eq(armor.affix, nil, "the affix is gone")
|
||||||
|
eq(game.player.defense, defenseBaseline - 2, "disenchanting an equipped armor updates defense immediately")
|
||||||
|
eq(game.player.essence, refund, "the refund lands in the player's essence")
|
||||||
|
|
||||||
|
local again, reason = Crafting.disenchant(game.player, armor)
|
||||||
|
eq(again, false, "an already-plain item has nothing left to disenchant")
|
||||||
|
eq(reason, "nothing to disenchant", "the refusal names the reason")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.craftingSocketedGemRidersStackWithARolledAffixOnHit()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
local weapon = Affixes.decorate(Items.weapon("dagger", 2), Affixes.weapon[4])
|
||||||
|
Items.equipWeapon(weapon, game.player)
|
||||||
|
ok(Crafting.socket(game.player, weapon, Crafting.gem(Crafting.SHOCK, 1)),
|
||||||
|
"a shock gem sockets alongside the rolled affix")
|
||||||
|
local orc = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
|
||||||
|
orc.hp, orc.maxHp = 30, 30
|
||||||
|
game.level.entities = { orc }
|
||||||
|
game:playerAttack(orc)
|
||||||
|
eq(Status.has(orc, Status.POISON), true, "the rolled venomed affix still lands its rider")
|
||||||
|
eq(Status.has(orc, Status.STUN), true, "the socketed shock gem lands its own rider on the same hit")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Regression: a shock rider applied during the PLAYER's turn is ticked for
|
||||||
|
-- the first time on the very same round, inside the victim's own upcoming
|
||||||
|
-- Game:takeMonsterTurn (Status.tick runs there before Status.skipsTurn is
|
||||||
|
-- ever checked) - so a duration of 1 would already read 0 and be gone
|
||||||
|
-- before skipsTurn saw it, and the "stun" would silently never do anything.
|
||||||
|
-- This confirms the fix (duration 2, see crafting.lua) actually skips the
|
||||||
|
-- victim's very next turn, not just that the status gets attached.
|
||||||
|
function tests.craftingShockRiderActuallyStunsTheVictimOnItsNextTurnNotJustOnPaper()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
local weapon = Items.weapon("dagger", 2)
|
||||||
|
Items.equipWeapon(weapon, game.player)
|
||||||
|
ok(Crafting.socket(game.player, weapon, Crafting.gem(Crafting.SHOCK, 1)),
|
||||||
|
"a bare shock gem sockets onto an unaffixed weapon")
|
||||||
|
local orc = Monsters.create(Monsters.byKey.orc, 1, 4, 6, 1)
|
||||||
|
orc.hp, orc.maxHp = 30, 30
|
||||||
|
game.level.entities = { orc }
|
||||||
|
|
||||||
|
game:playerAttack(orc)
|
||||||
|
eq(Status.has(orc, Status.STUN), true, "the shock rider lands immediately on the hit")
|
||||||
|
|
||||||
|
local hpBefore, xBefore, yBefore = game.player.hp, orc.x, orc.y
|
||||||
|
game:takeMonsterTurn(orc)
|
||||||
|
eq(game.player.hp, hpBefore, "a stunned orc's very next turn deals no melee damage")
|
||||||
|
eq(orc.x, xBefore, "a stunned orc's very next turn does not move")
|
||||||
|
eq(orc.y, yBefore, "a stunned orc's very next turn does not move")
|
||||||
|
|
||||||
|
game:takeMonsterTurn(orc)
|
||||||
|
eq(Status.has(orc, Status.STUN), false, "the stun has fully expired by the turn after that")
|
||||||
|
ok(game.player.hp < hpBefore, "once unstunned the orc resumes attacking normally")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.gameCraftingWrappersDriveTheFullPipeline()
|
||||||
|
local game = stagedGame(1, arena(21, 11), 3, 6)
|
||||||
|
local weapon = Items.weapon("dagger", 2)
|
||||||
|
Items.equipWeapon(weapon, game.player)
|
||||||
|
local attackBaseline = game.player.attack
|
||||||
|
|
||||||
|
game.player.essence = Crafting.FORGE_COST * 2
|
||||||
|
ok(game:forgeGem(Crafting.FIRE), "forging a gem through the game wrapper succeeds")
|
||||||
|
ok(game:forgeGem(Crafting.FIRE), "forging a second matching gem succeeds")
|
||||||
|
local invBefore = #game.player.inventory
|
||||||
|
|
||||||
|
local firstGemIndex, secondGemIndex
|
||||||
|
for i, item in ipairs(game.player.inventory) do
|
||||||
|
if item.kind == Items.GEM then
|
||||||
|
if not firstGemIndex then firstGemIndex = i else secondGemIndex = i end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ok(game:mergeGemsAt(firstGemIndex, secondGemIndex), "merging through the game wrapper succeeds")
|
||||||
|
eq(#game.player.inventory, invBefore - 1, "merging removes both source gems and adds one merged gem")
|
||||||
|
|
||||||
|
local mergedIndex
|
||||||
|
for i, item in ipairs(game.player.inventory) do
|
||||||
|
if item.kind == Items.GEM then mergedIndex = i end
|
||||||
|
end
|
||||||
|
ok(game:socketItem("weapon", mergedIndex), "socketing the equipped weapon through the game wrapper succeeds")
|
||||||
|
eq(weapon.socket.element, Crafting.FIRE, "the socketed gem's element lands on the weapon")
|
||||||
|
eq(game.player.attack, attackBaseline + 2, "socketing a tier-2 gem adds its power to attack")
|
||||||
|
ok(not game:socketItem("weapon", mergedIndex), "the gem slot is consumed and cannot be socketed twice")
|
||||||
|
|
||||||
|
game.player.essence = Crafting.temperCost(weapon)
|
||||||
|
ok(game:temperItem("weapon"), "tempering the equipped weapon through the game wrapper succeeds")
|
||||||
|
eq(weapon.power, 2 + 2 + 1, "temper stacks on top of the socket's power")
|
||||||
|
eq(game.player.attack, attackBaseline + 3, "tempering the equipped weapon updates attack immediately")
|
||||||
|
|
||||||
|
game.player.essence = Crafting.ENCHANT_COST
|
||||||
|
ok(game:enchantItem("weapon", "vicious"), "enchanting the equipped weapon through the game wrapper succeeds")
|
||||||
|
eq(weapon.affix.key, "vicious", "the chosen affix lands on the weapon")
|
||||||
|
|
||||||
|
local beforeDisenchant = game.player.attack
|
||||||
|
local expectedRefund = Crafting.FORGE_COST + 2 * 3
|
||||||
|
ok(game:disenchantItem("weapon"), "disenchanting the equipped weapon through the game wrapper succeeds")
|
||||||
|
eq(game.player.attack, beforeDisenchant - 2, "disenchanting removes the affix's power from attack")
|
||||||
|
eq(game.player.essence, expectedRefund, "the disenchant refund lands in essence")
|
||||||
|
ok(contains(game.log:recent(1)[1] or "", "disenchant"), "the disenchant is logged")
|
||||||
|
end
|
||||||
|
|
||||||
|
function tests.monsterKillsGrantEssenceScaledToTheirXpReward()
|
||||||
end
|
end
|
||||||
|
|
||||||
local order = {
|
local order = {
|
||||||
|
|||||||
Reference in new issue
Block a user