import { describe, expect, it } from "vitest"; import { applyAura, evaluateAuraProcs } from "./combatAuras"; import { ROM_PRIEST_CHAMPION_HEART_RUNE_ENERGY_SPELL_ID, ROM_PRIEST_CHAMPION_PROTECTION_RUNE_SEAL_SPELL_ID, ROM_PRIEST_CHAMPION_REGENERATE_PROC_AURA_ID, ROM_PRIEST_CHAMPION_REGENERATE_PROC_CHANCE, ROM_PRIEST_CHAMPION_REGENERATE_PROC_ID, ROM_PRIEST_CHAMPION_REGENERATE_SPELL_ID, romPriestChampionCastEffects, romPriestChampionProtectionRuneSealProcIsEligible, } from "./romPriestChampionOverrides"; import { abilitiesForCharacter } from "./abilityCatalog"; const base = { primaryClassId: "rom-priest", secondaryClassId: "rom-champion", learnedPassiveSpellIds: [ROM_PRIEST_CHAMPION_PROTECTION_RUNE_SEAL_SPELL_ID], trigger: "periodic" as const, triggeringAbilitySpellId: ROM_PRIEST_CHAMPION_REGENERATE_SPELL_ID, healingAmount: 1, }; describe("RoM Priest/Champion Protection Rune Seal proc profile", () => { it("requires the exact pair, learned passive, positive heal tick, and Regenerate periodic event", () => { expect(romPriestChampionProtectionRuneSealProcIsEligible(base)).toBe(true); expect(romPriestChampionProtectionRuneSealProcIsEligible({ ...base, primaryClassId: "rom-champion", secondaryClassId: "rom-priest", })).toBe(false); expect(romPriestChampionProtectionRuneSealProcIsEligible({ ...base, secondaryClassId: "rom-warrior", })).toBe(false); expect(romPriestChampionProtectionRuneSealProcIsEligible({ ...base, learnedPassiveSpellIds: [], })).toBe(false); expect(romPriestChampionProtectionRuneSealProcIsEligible({ ...base, trigger: "heal", })).toBe(false); expect(romPriestChampionProtectionRuneSealProcIsEligible({ ...base, triggeringAbilitySpellId: ROM_PRIEST_CHAMPION_HEART_RUNE_ENERGY_SPELL_ID, })).toBe(false); for (const healingAmount of [0, -1, Number.NaN, Number.POSITIVE_INFINITY]) { expect(romPriestChampionProtectionRuneSealProcIsEligible({ ...base, healingAmount })).toBe(false); } }); it("attaches the chance aura only to the Priest/Champion's Regenerate cast", () => { const regenerate = abilitiesForCharacter("rom-priest", "rom-champion") .find((ability) => ability.dbcSpellId === ROM_PRIEST_CHAMPION_REGENERATE_SPELL_ID)!; const [effect] = romPriestChampionCastEffects(regenerate, regenerate.effects, [ ROM_PRIEST_CHAMPION_PROTECTION_RUNE_SEAL_SPELL_ID, ]); expect(effect).toMatchObject({ kind: "apply-aura", recipient: "caster", triggerSpellId: ROM_PRIEST_CHAMPION_PROTECTION_RUNE_SEAL_SPELL_ID, aura: { id: ROM_PRIEST_CHAMPION_REGENERATE_PROC_AURA_ID, durationMs: 20_001, procs: [{ id: ROM_PRIEST_CHAMPION_REGENERATE_PROC_ID, chance: ROM_PRIEST_CHAMPION_REGENERATE_PROC_CHANCE, triggers: "periodic", }], }, }); expect(romPriestChampionCastEffects(regenerate, regenerate.effects, [])).toBe(regenerate.effects); const heartRuneEnergy = abilitiesForCharacter("rom-priest", "rom-champion") .find((ability) => ability.dbcSpellId === ROM_PRIEST_CHAMPION_HEART_RUNE_ENERGY_SPELL_ID)!; expect(romPriestChampionCastEffects( heartRuneEnergy, heartRuneEnergy.effects, [ROM_PRIEST_CHAMPION_PROTECTION_RUNE_SEAL_SPELL_ID], )).toBe(heartRuneEnergy.effects); }); it("uses the documented 8% proc boundary, with equality excluded", () => { const regenerate = abilitiesForCharacter("rom-priest", "rom-champion") .find((ability) => ability.dbcSpellId === ROM_PRIEST_CHAMPION_REGENERATE_SPELL_ID)!; const auraEffect = romPriestChampionCastEffects(regenerate, regenerate.effects, [ ROM_PRIEST_CHAMPION_PROTECTION_RUNE_SEAL_SPELL_ID, ])[0]; if (auraEffect.kind !== "apply-aura") throw new Error("Protection Rune Seal should add a proc aura."); const activeAura = applyAura([], { definition: auraEffect.aura, sourceId: "priest-champion", targetId: "priest-champion", now: 1_000, }).auras; const event = { trigger: "periodic" as const, subjectId: "priest-champion", otherId: "priest-champion", abilityId: regenerate.id, amount: 1, }; expect(evaluateAuraProcs(activeAura, event, 3_000, () => ROM_PRIEST_CHAMPION_REGENERATE_PROC_CHANCE - 0.000_001) .triggered).toHaveLength(1); expect(evaluateAuraProcs(activeAura, event, 3_000, () => ROM_PRIEST_CHAMPION_REGENERATE_PROC_CHANCE) .triggered).toHaveLength(0); }); });