import { beforeEach, describe, expect, it } from "vitest"; import { applyAura, expireAuras, resolveDamageValue } from "./combatAuras"; import { ROM_ALL_ABILITIES } from "./romAbilityCatalog"; import { clearMobRuntimeRegistry, getMobPosition, setMobPosition } from "./mobRuntimeRegistry"; import { PLAYER_AURA_ENTITY_ID, stableCombatUnit, useCombatStore } from "./combatStore"; import { romEffectsForCast, type RomCastMechanics } from "./romCombatMechanics"; import { ROM_CHAMPION_SOUL_FORGE_PROC_ACTION_ID, ROM_CHAMPION_SOUL_FORGE_PROFILE, ROM_CHAMPION_WAY_OF_FRENZY_PROC_ACTION_ID, ROM_CHAMPION_WAY_OF_FRENZY_PROFILE, romChampionCastEffects, } from "./romChampionOverrides"; import { ROM_CHAMPION_CHAIN_DRIVE_WINDOW_AURA_ID, ROM_CHAMPION_FORGE_RANK_TWO_AURA_ID, } from "./romChampionForgeRuntime"; const empty: RomCastMechanics = { sealStacks: 0, bleedCount: 0, charged: false, mainHandDagger: false, casterMaxHealth: 2_000, targetHealthPercent: 1, }; function sourceAbility(spellId: number) { const ability = ROM_ALL_ABILITIES.find((candidate) => candidate.dbcSpellId === spellId); if (!ability) throw new Error(`Missing RoM source ability ${spellId}`); return ability; } function castEffects(spellId: number, context: Partial = {}) { return romEffectsForCast(sourceAbility(spellId), { ...empty, ...context }); } describe("RoM Champion source-specific behavior", () => { beforeEach(() => { clearMobRuntimeRegistry(); useCombatStore.setState({ playerPosition: [0, 0, 0] }); }); it("ends Rune Overload when its Rage drain reaches zero", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-champion", raceId: "rom-dwarf", level: 40 }); useCombatStore.setState((state) => ({ resource: 30, resourcePools: { ...state.resourcePools, rage: 30 }, })); expect(useCombatStore.getState().castAbility("rom-champion-primary-17", undefined, 1_000)) .toMatchObject({ ok: true }); expect(useCombatStore.getState().auras.some((aura) => ( aura.definition.utilityTags?.includes("rom-champion-rune-overload") ))).toBe(true); expect(useCombatStore.getState().resourcePools.rage).toBe(15); useCombatStore.getState().tick(1, 2_000); expect(useCombatStore.getState().resourcePools.rage).toBe(0); expect(useCombatStore.getState().auras.some((aura) => ( aura.definition.utilityTags?.includes("rom-champion-rune-overload") ))).toBe(false); }); it("applies Fearless Blow's bonus only below 30% and gives its Rage rider to the caster", () => { const atThreshold = castEffects(498531, { targetHealthPercent: 0.3 }); const belowThreshold = castEffects(498531, { targetHealthPercent: 0.299 }); const baseDamage = atThreshold.find((effect) => effect.kind === "damage"); const boostedDamage = belowThreshold.find((effect) => effect.kind === "damage"); expect(baseDamage?.kind).toBe("damage"); expect(boostedDamage?.kind).toBe("damage"); if (baseDamage?.kind === "damage" && boostedDamage?.kind === "damage") { expect(boostedDamage.coefficient).toBeCloseTo(baseDamage.coefficient * 1.3); expect(boostedDamage.basePoints ?? 0).toBeCloseTo((baseDamage.basePoints ?? 0) * 1.3); } expect(atThreshold.some((effect) => effect.kind === "apply-aura" && effect.aura.id.includes("621179"))).toBe(false); const rage = belowThreshold.find((effect) => effect.kind === "apply-aura" && effect.aura.id.includes("621183")); expect(rage?.kind).toBe("apply-aura"); if (rage?.kind === "apply-aura") expect(rage.recipient).toBe("caster"); }); it("adds Rune Draw's source-described pull and moves the selected target toward the Champion", () => { const runeDraw = sourceAbility(498564); expect(runeDraw.effects.filter((effect) => effect.kind === "pull")).toHaveLength(1); expect(runeDraw.effects.some((effect) => effect.kind === "interrupt")).toBe(true); expect(runeDraw.effects.some((effect) => effect.kind === "taunt")).toBe(true); useCombatStore.getState().initializeCharacter({ classId: "rom-champion", raceId: "rom-dwarf", level: 20 }); useCombatStore.getState().registerMob("champion-rune-draw-target", { name: "Rune Draw Target", maxHealth: 100_000, }); setMobPosition("champion-rune-draw-target", [8, 0, 0]); useCombatStore.getState().selectMob("champion-rune-draw-target"); expect(useCombatStore.getState().castAbility("rom-champion-primary-1", undefined, 1_000)).toMatchObject({ ok: true }); expect(getMobPosition("champion-rune-draw-target")?.[0]).toBeCloseTo(1.5); }); it("rejects Indomitable Spirit at 65% health and permits it below the threshold", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-champion", secondaryClassId: "rom-warlock", raceId: "rom-dwarf", level: 70, }); const maxHealth = useCombatStore.getState().maxHealth; useCombatStore.setState((state) => ({ health: Math.ceil(maxHealth * 0.65), resource: 20, resourcePools: { ...state.resourcePools, rage: 20 }, })); expect(useCombatStore.getState().castAbility("rom-champion-rom-warlock-elite-35", undefined, 1_000)) .toMatchObject({ ok: false, reason: "condition-not-met" }); useCombatStore.setState((state) => ({ health: Math.floor(maxHealth * 0.65) - 1, resource: 20, resourcePools: { ...state.resourcePools, rage: 20 }, })); expect(useCombatStore.getState().castAbility("rom-champion-rom-warlock-elite-35", undefined, 1_000)) .toMatchObject({ ok: true }); }); it("activates Forge for five minutes and grants one stack and Rage on a normal hit", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-champion", raceId: "rom-dwarf", level: 20 }); useCombatStore.getState().registerMob("champion-forge-target", { name: "Forge Target", maxHealth: 100_000 }); setMobPosition("champion-forge-target", [3, 0, 0]); useCombatStore.getState().selectMob("champion-forge-target"); const forge = ROM_ALL_ABILITIES.find((ability) => ability.dbcSpellId === 498523)!; expect(useCombatStore.getState().castAbility(forge.id, undefined, 1_000)).toMatchObject({ ok: true }); const activeForge = useCombatStore.getState().auras.find((aura) => aura.definition.id === "rom-champion-forge-active"); expect(activeForge?.expiresAt).toBe(301_000); expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === "rom-champion-forge-stack")).toBe(false); useCombatStore.setState({ autoAttackTargetId: "champion-forge-target", nextAutoAttackAt: 1_000 }); useCombatStore.getState().tick(0.1, 1_000); const forgeStack = useCombatStore.getState().auras.find((aura) => aura.definition.id === "rom-champion-forge-stack"); expect(forgeStack?.stacks).toBe(1); expect(forgeStack?.expiresAt).toBe(9_000); expect(useCombatStore.getState().resourcePools.rage).toBeGreaterThanOrEqual(10); }); it("keeps rank 1 at level 21 and unlocks the rank 2 Forge source aura at level 22", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-champion", secondaryClassId: "rom-rogue", raceId: "rom-dwarf", level: 21, }); const forge = sourceAbility(498523); expect(useCombatStore.getState().castAbility(forge.id, undefined, 1_000)).toMatchObject({ ok: true }); expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === "rom-champion-forge-active")) .toBe(true); expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === ROM_CHAMPION_FORGE_RANK_TWO_AURA_ID)) .toBe(false); useCombatStore.setState({ level: 22 }); expect(useCombatStore.getState().castAbility(forge.id, undefined, 4_000)).toMatchObject({ ok: true }); const rankTwo = useCombatStore.getState().auras.find((aura) => ( aura.definition.id === ROM_CHAMPION_FORGE_RANK_TWO_AURA_ID )); expect(rankTwo?.expiresAt).toBe(304_000); expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === "rom-champion-forge-active")) .toBe(false); }); it("grants Chain Drive on an eligible rank 2 auto attack and resets only learned owner cooldowns", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-champion", secondaryClassId: "rom-rogue", raceId: "rom-dwarf", level: 22, }); useCombatStore.getState().registerMob("champion-chain-drive-target", { name: "Chain Drive Target", maxHealth: 100_000, }); const forge = sourceAbility(498523); const runePulse = sourceAbility(498529); const shadowPulse = sourceAbility(498726); expect(useCombatStore.getState().castAbility(forge.id, undefined, 1_000)).toMatchObject({ ok: true }); useCombatStore.setState((state) => ({ resource: 0, resourcePools: { ...state.resourcePools, rage: 0 }, cooldowns: { ...state.cooldowns, [runePulse.id]: 90_000, [shadowPulse.id]: 90_000 }, })); const hitSeed = Array.from({ length: 100 }, (_, index) => `champion-chain-drive-${index}`) .find((seed) => stableCombatUnit(`${seed}:498590:0`) < 0.3); expect(hitSeed).toBeDefined(); const hit = useCombatStore.getState().damageMob( "champion-chain-drive-target", 100, 2_000, undefined, "physical", { attackKind: "melee", seed: hitSeed!, abilityId: "party-auto-attack", rules: { isAutoAttack: true, canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false }, }, ); expect(hit.amount).toBeGreaterThan(0); expect(useCombatStore.getState().auras.some((aura) => ( aura.definition.id === ROM_CHAMPION_CHAIN_DRIVE_WINDOW_AURA_ID && aura.targetId === PLAYER_AURA_ENTITY_ID && aura.expiresAt === 10_000 ))).toBe(true); expect(useCombatStore.getState().cooldowns[runePulse.id]).toBe(0); expect(useCombatStore.getState().cooldowns[shadowPulse.id]).toBe(0); expect(useCombatStore.getState().resourcePools.rage).toBeGreaterThanOrEqual(10); expect(useCombatStore.getState().auras.find((aura) => aura.definition.id === "rom-champion-forge-stack")?.stacks) .toBe(1); }); it("keeps the rank 2 stack and Rage when Chain Drive's fallback roll fails", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-champion", secondaryClassId: "rom-rogue", raceId: "rom-dwarf", level: 22, }); useCombatStore.getState().registerMob("champion-chain-drive-failed-target", { name: "Chain Drive Failed Target", maxHealth: 100_000, }); const forge = sourceAbility(498523); expect(useCombatStore.getState().castAbility(forge.id, undefined, 1_000)).toMatchObject({ ok: true }); useCombatStore.setState((state) => ({ resourcePools: { ...state.resourcePools, rage: 0 }, resource: 0 })); const failedSeed = Array.from({ length: 100 }, (_, index) => `chain-drive-failed-${index}`) .find((seed) => stableCombatUnit(`${seed}:498590:0`) >= 0.8 && stableCombatUnit(`${seed}:498590:1`) >= 0.3)!; const hit = useCombatStore.getState().damageMob( "champion-chain-drive-failed-target", 100, 2_000, undefined, "physical", { attackKind: "melee", seed: failedSeed, abilityId: "party-auto-attack", rules: { isAutoAttack: true, canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false }, }, ); expect(hit.amount).toBeGreaterThan(0); expect(useCombatStore.getState().auras.find((aura) => aura.definition.id === "rom-champion-forge-stack")?.stacks) .toBe(1); expect(useCombatStore.getState().auras.some((aura) => ( aura.definition.id === ROM_CHAMPION_CHAIN_DRIVE_WINDOW_AURA_ID ))).toBe(false); expect(useCombatStore.getState().resourcePools.rage).toBeGreaterThanOrEqual(10); }); it("does not proc rank 2 Forge from zero or fully absorbed physical packets", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-champion", secondaryClassId: "rom-rogue", raceId: "rom-dwarf", level: 22, }); useCombatStore.getState().registerMob("champion-forge-zero-target", { name: "Zero Packet Target", maxHealth: 100_000, }); useCombatStore.getState().registerMob("champion-forge-absorbed-target", { name: "Absorbed Packet Target", maxHealth: 100_000, }); const forge = sourceAbility(498523); expect(useCombatStore.getState().castAbility(forge.id, undefined, 1_000)).toMatchObject({ ok: true }); const zeroHit = useCombatStore.getState().damageMob( "champion-forge-zero-target", 0, 2_000, undefined, "physical", { attackKind: "melee", seed: "champion-forge-zero-hit", abilityId: "party-auto-attack", rules: { isAutoAttack: true, canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false }, }, ); expect(zeroHit.amount).toBe(0); const shieldedAuras = applyAura(useCombatStore.getState().auras, { definition: { id: "test:champion-forge-physical-absorb", name: "Physical absorb fixture", disposition: "buff", durationMs: null, absorbs: [{ id: "test:champion-forge-physical-absorb-pool", amount: 1_000_000, school: "physical" }], }, sourceId: "champion-forge-absorbed-target", targetId: "champion-forge-absorbed-target", now: 3_000, }).auras; useCombatStore.setState({ auras: shieldedAuras }); const absorbedHit = useCombatStore.getState().damageMob( "champion-forge-absorbed-target", 100, 3_000, undefined, "physical", { attackKind: "melee", seed: "champion-forge-absorbed-hit", abilityId: "party-auto-attack", rules: { isAutoAttack: true, canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false }, }, ); expect(absorbedHit.amount).toBe(0); const absorbedEvent = useCombatStore.getState().combatEvents.filter((event) => ( event.kind === "damage" && event.targetActorId === "champion-forge-absorbed-target" )).at(-1); expect(absorbedEvent?.absorbed).toBeGreaterThan(0); expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === "rom-champion-forge-stack")) .toBe(false); expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === ROM_CHAMPION_CHAIN_DRIVE_WINDOW_AURA_ID)) .toBe(false); expect(useCombatStore.getState().resourcePools.rage).toBe(0); }); it("does not proc rank 2 Forge from a positive melee spell packet", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-champion", raceId: "rom-dwarf", level: 22 }); useCombatStore.getState().registerMob("champion-forge-melee-spell-target", { name: "Melee Spell Target", maxHealth: 100_000, }); expect(useCombatStore.getState().castAbility(sourceAbility(498523).id, undefined, 1_000)) .toMatchObject({ ok: true }); useCombatStore.setState((state) => ({ resource: 0, resourcePools: { ...state.resourcePools, rage: 0 } })); useCombatStore.getState().damageMob( "champion-forge-melee-spell-target", 100, 2_000, undefined, "physical", { attackKind: "melee", seed: "champion-direct-melee-spell", abilityId: sourceAbility(498529).id, rules: { canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false }, }, ); expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === "rom-champion-forge-stack")) .toBe(false); expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === ROM_CHAMPION_CHAIN_DRIVE_WINDOW_AURA_ID)) .toBe(false); expect(useCombatStore.getState().resourcePools.rage).toBe(0); }); it("restores Rune Pulse's source-defined Rage only when its damage lands", () => { const runePulse = sourceAbility(498529); expect(runePulse.description).toContain("When it hits, it will restore"); const effects = castEffects(498529); const hit = effects.find((effect) => effect.kind === "damage"); expect(hit?.kind).toBe("damage"); if (hit?.kind === "damage") expect(hit.onDamageResource).toEqual({ resource: "rage", amount: 5 }); expect(effects.some((effect) => effect.kind === "apply-aura" && effect.aura.id.includes("-622327-"))) .toBe(false); expect(effects.some((effect) => effect.kind === "apply-aura" && effect.aura.id === "rom-aura-498604-621253-bonus")) .toBe(false); expect(castEffects(498726) .some((effect) => effect.kind === "apply-aura" && effect.aura.id === "rom-aura-498604-621253-bonus")) .toBe(false); }); it("grants Rune Pulse's one-second bonus at cast start and expires it before the two-second impact", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-champion", secondaryClassId: "rom-rogue", raceId: "rom-dwarf", level: 22, }); useCombatStore.getState().registerMob("champion-rune-pulse-target", { name: "Rune Pulse Target", maxHealth: 100_000, }); setMobPosition("champion-rune-pulse-target", [3, 0, 0]); useCombatStore.getState().selectMob("champion-rune-pulse-target"); const forge = sourceAbility(498523); expect(useCombatStore.getState().castAbility(forge.id, undefined, 1_000)).toMatchObject({ ok: true }); useCombatStore.setState((state) => ({ cooldowns: { ...state.cooldowns, [forge.id]: 0 } })); const hitSeed = Array.from({ length: 100 }, (_, index) => `rune-pulse-window-${index}`) .find((seed) => stableCombatUnit(`${seed}:498590:0`) < 0.3)!; useCombatStore.getState().damageMob( "champion-rune-pulse-target", 100, 3_000, undefined, "physical", { attackKind: "melee", seed: hitSeed, abilityId: "party-auto-attack", rules: { isAutoAttack: true, canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false }, }, ); expect(useCombatStore.getState().auras.some((aura) => ( aura.definition.id === ROM_CHAMPION_CHAIN_DRIVE_WINDOW_AURA_ID && aura.expiresAt === 11_000 ))).toBe(true); const runePulse = sourceAbility(498529); expect(runePulse.castTimeMs).toBe(2_000); expect(useCombatStore.getState().castAbility(runePulse.id, undefined, 3_100)).toMatchObject({ ok: true }); const startedAura = useCombatStore.getState().auras.find((aura) => ( aura.definition.id === "rom-aura-498604-621253-bonus" )); const runePulseBonuses = useCombatStore.getState().auras.filter((aura) => ( aura.definition.id === "rom-aura-498604-621253-bonus" )); expect(startedAura?.appliedAt).toBe(3_100); expect(startedAura?.expiresAt).toBe(4_100); expect(runePulseBonuses).toHaveLength(1); const activeAurasDuringBonus = expireAuras(useCombatStore.getState().auras, 3_500).active; const activeAurasWithoutBonus = activeAurasDuringBonus.filter((aura) => ( aura.definition.id !== "rom-aura-498604-621253-bonus" )); const outgoingDamageWithBonus = resolveDamageValue(100, activeAurasDuringBonus, { direction: "dealt", school: "physical", attackKind: "melee", }); const outgoingDamageWithoutBonus = resolveDamageValue(100, activeAurasWithoutBonus, { direction: "dealt", school: "physical", attackKind: "melee", }); expect(outgoingDamageWithBonus - outgoingDamageWithoutBonus).toBeCloseTo(20, 10); useCombatStore.getState().tick(2, 5_100); const aurasAtImpact = expireAuras(useCombatStore.getState().auras, 5_100).active; const runePulseBonusesAtImpact = aurasAtImpact.filter((aura) => ( aura.definition.id === "rom-aura-498604-621253-bonus" )); const outgoingDamageAtImpact = resolveDamageValue(100, aurasAtImpact, { direction: "dealt", school: "physical", attackKind: "melee", }); expect(outgoingDamageAtImpact).toBe(resolveDamageValue(100, aurasAtImpact.filter((aura) => ( aura.definition.id !== "rom-aura-498604-621253-bonus" )), { direction: "dealt", school: "physical", attackKind: "melee" })); expect(runePulseBonusesAtImpact).toHaveLength(0); }); it("routes Rune Siphon's positive source component to the caster as healing", () => { const runeSiphon = sourceAbility(498595); expect(runeSiphon.secondaryClassId).toBe("rom-warlock"); expect(runeSiphon.description).toContain("restores (DMG8) HP to the caster"); const effects = castEffects(498595); expect(effects.filter((effect) => effect.kind === "damage")).toHaveLength(2); expect(effects.filter((effect) => effect.kind === "heal")).toEqual([ expect.objectContaining({ kind: "heal", recipient: "caster", basePoints: 24, pointsPerLevel: 4 }), ]); useCombatStore.getState().initializeCharacter({ classId: "rom-champion", secondaryClassId: "rom-warlock", raceId: "rom-dwarf", level: 70, }); useCombatStore.getState().registerMob("champion-rune-siphon-target", { name: "Rune Siphon Target", maxHealth: 100_000, }); setMobPosition("champion-rune-siphon-target", [3, 0, 0]); useCombatStore.getState().selectMob("champion-rune-siphon-target"); useCombatStore.getState().damagePlayer(Math.floor(useCombatStore.getState().maxHealth / 2)); useCombatStore.setState((state) => ({ resource: 100, resourcePools: { ...state.resourcePools, rage: 100, focus: 100 }, })); const healthBeforeCast = useCombatStore.getState().health; expect(useCombatStore.getState().castAbility("rom-champion-rom-warlock-elite-20", undefined, 1_000)) .toMatchObject({ ok: true }); expect(useCombatStore.getState().health).toBeGreaterThan(healthBeforeCast); }); it("arms Soul Forge Mystery's source-backed dark-magic bonus on critical attacks", () => { const soulForge = sourceAbility(498652); expect(soulForge.secondaryClassId).toBe("rom-warlock"); expect(soulForge.description).toContain("If critical attack occurs while in effect"); const localCritAuras = soulForge.effects.filter((effect) => effect.kind === "apply-aura" && effect.aura.id.includes(`-${ROM_CHAMPION_SOUL_FORGE_PROFILE.criticalAuraComponentId}-`)); const localDarkAura = soulForge.effects.find((effect) => effect.kind === "apply-aura" && effect.aura.id.includes(`-${ROM_CHAMPION_SOUL_FORGE_PROFILE.darkPowerComponentId}-`)); expect(localCritAuras.length).toBeGreaterThan(0); expect(localCritAuras.every((effect) => effect.kind === "apply-aura" && effect.aura.durationMs === 300_000)).toBe(true); expect(localDarkAura).toMatchObject({ kind: "apply-aura", aura: { durationMs: 10_000, modifiers: [expect.objectContaining({ kind: "damage", direction: "dealt", school: "shadow", value: 0.1 })], }, }); const effects = castEffects(498652); const criticalAura = effects.find((effect) => effect.kind === "apply-aura" && effect.aura.id.includes(`-${ROM_CHAMPION_SOUL_FORGE_PROFILE.criticalAuraComponentId}-`)); expect(criticalAura?.kind).toBe("apply-aura"); if (criticalAura?.kind === "apply-aura") { expect(criticalAura.aura.procs).toEqual(expect.arrayContaining([ expect.objectContaining({ triggers: "crit", chance: 1, action: expect.objectContaining({ kind: "custom", id: ROM_CHAMPION_SOUL_FORGE_PROC_ACTION_ID, data: expect.objectContaining({ sourceSpellId: 498652, sourceComponentId: 621341, durationMs: 10_000, disposition: "buff", maxStacks: 1, stackBehavior: "refresh", target: "aura-source", modifierSchool: "shadow", modifierValue: 0.1, }), }), }), ])); } expect(effects.some((effect) => effect.kind === "apply-aura" && effect.aura.id.includes(`-${ROM_CHAMPION_SOUL_FORGE_PROFILE.darkPowerComponentId}-`))).toBe(false); }); it("grants Soul Forge Mystery's shadow bonus after a critical hit", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-champion", secondaryClassId: "rom-warlock", raceId: "rom-dwarf", level: 80, xp: 0, }); const soulForge = useCombatStore.getState().abilities.find((ability) => ability.dbcSpellId === 498652)!; expect(useCombatStore.getState().castAbility(soulForge.id, undefined, 1_000).ok).toBe(true); expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === "rom-champion-soul-forge-dark-magic")) .toBe(false); useCombatStore.getState().registerMob("soul-forge-crit-target", { name: "Target", maxHealth: 100_000, xpReward: 0 }); useCombatStore.getState().damageMob( "soul-forge-crit-target", 100, 2_000, undefined, "shadow", { attackKind: "spell", seed: "soul-forge-guaranteed-critical", abilityId: soulForge.id, criticalChanceBonusPercent: 100, rules: { canMiss: false, canCrit: true, canDodge: false, canParry: false, canBlock: false }, }, ); expect(useCombatStore.getState().auras).toEqual(expect.arrayContaining([ expect.objectContaining({ sourceId: PLAYER_AURA_ENTITY_ID, targetId: PLAYER_AURA_ENTITY_ID, definition: expect.objectContaining({ id: "rom-champion-soul-forge-dark-magic", durationMs: ROM_CHAMPION_SOUL_FORGE_PROFILE.darkMagicAuraDurationMs, modifiers: [expect.objectContaining({ kind: "damage", direction: "dealt", school: "shadow", operation: "percent", value: ROM_CHAMPION_SOUL_FORGE_PROFILE.darkMagicBonusPercent, })], }), }), ])); }); it("arms Way of Frenzy only on Divine Vengeance and caps its local 2% Stamina stacks at five", () => { const wayOfFrenzy = sourceAbility(ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.sourceSpellId); const divineVengeance = sourceAbility(ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.triggerSpellId); expect(wayOfFrenzy).toMatchObject({ classId: "rom-champion", secondaryClassId: "rom-priest", passive: true }); expect(divineVengeance.id).toBe(ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.triggerAbilityId); expect(wayOfFrenzy.effects).toEqual(expect.arrayContaining([ expect.objectContaining({ kind: "apply-aura", recipient: "caster", aura: expect.objectContaining({ id: "rom-champion-way-of-frenzy-passive", durationMs: null, procs: [expect.objectContaining({ triggers: ["hit", "crit"], abilityIds: [divineVengeance.id], chance: 1, action: expect.objectContaining({ kind: "custom", id: ROM_CHAMPION_WAY_OF_FRENZY_PROC_ACTION_ID, data: expect.objectContaining({ sourceSpellId: ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.sourceSpellId, sourceComponentId: ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.sourceComponentId, triggerSpellId: ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.triggerSpellId, durationMs: 60_000, maxStacks: ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.maxStacks, staminaPercentPerStack: ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.staminaPercentPerStack, }), }), })], }), }), ])); expect(wayOfFrenzy.effects.some((effect) => effect.kind === "apply-aura" && effect.aura.modifiers?.some((modifier) => modifier.kind === "stat" && modifier.stat === "stamina"))) .toBe(false); useCombatStore.getState().initializeCharacter({ classId: "rom-champion", secondaryClassId: "rom-priest", raceId: "rom-dwarf", level: 70, }); useCombatStore.getState().registerMob("way-of-frenzy-target", { name: "Target", maxHealth: 100_000, xpReward: 0 }); const stackAuraId = "rom-champion-way-of-frenzy-stacks"; const stackAura = () => useCombatStore.getState().auras.find((aura) => aura.definition.id === stackAuraId); expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === "rom-champion-way-of-frenzy-passive")) .toBe(true); expect(stackAura()).toBeUndefined(); const unrelatedAbility = sourceAbility(498528); useCombatStore.getState().damageMob("way-of-frenzy-target", 100, 2_000, undefined, "physical", { attackKind: "melee", seed: "way-of-frenzy-unrelated-hit", abilityId: unrelatedAbility.id, rules: { canMiss: false, canCrit: false, canDodge: false, canParry: false, canBlock: false }, }); expect(stackAura()).toBeUndefined(); for (let hit = 0; hit < 6; hit += 1) { useCombatStore.getState().damageMob("way-of-frenzy-target", 100, 3_000 + hit * 1_000, undefined, "physical", { attackKind: "melee", seed: `way-of-frenzy-divine-vengeance-${hit}`, abilityId: divineVengeance.id, rules: { canMiss: false, canCrit: false, canDodge: false, canParry: false, canBlock: false }, }); } expect(stackAura()).toMatchObject({ stacks: ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.maxStacks, expiresAt: 68_000, definition: { durationMs: 60_000, maxStacks: 5, stackBehavior: "add", modifiers: [{ kind: "stat", stat: "stamina", operation: "percent", value: ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.staminaPercentPerStack, perStack: true, }], }, }); }); it("scales Determination Rune's source max-health shield and leaves unrelated casts unchanged", () => { const determinationRune = sourceAbility(498584); const sourceShield = determinationRune.effects.find((effect) => effect.kind === "apply-aura" && effect.aura.absorbs?.length); expect(sourceShield?.kind).toBe("apply-aura"); if (sourceShield?.kind !== "apply-aura") throw new Error("Missing Determination Rune shield effect"); expect(sourceShield.aura.absorbs?.[0]?.amount).toBe(30); expect(determinationRune.ranks?.[0]?.effects.find((effect) => effect.kind === "apply-aura" && effect.aura.absorbs?.length)).toMatchObject({ kind: "apply-aura", aura: { absorbs: [expect.objectContaining({ amount: 30 })] }, }); const twoThousandHp = castEffects(498584, { casterMaxHealth: 2_000 }); const fiveThousandHp = castEffects(498584, { casterMaxHealth: 5_000 }); expect(twoThousandHp.find((effect) => effect.kind === "apply-aura" && effect.aura.absorbs?.length)) .toMatchObject({ kind: "apply-aura", aura: { absorbs: [expect.objectContaining({ amount: 600 })] } }); expect(fiveThousandHp.find((effect) => effect.kind === "apply-aura" && effect.aura.absorbs?.length)) .toMatchObject({ kind: "apply-aura", aura: { absorbs: [expect.objectContaining({ amount: 1_500 })] } }); const otherClass = { ...determinationRune, classId: "rom-mage" as const }; expect(romChampionCastEffects(otherClass, determinationRune.effects, 1, 2_000)) .toBe(determinationRune.effects); const otherPair = { ...determinationRune, secondaryClassId: "rom-mage" as const }; expect(romChampionCastEffects(otherPair, determinationRune.effects, 1, 2_000)) .toBe(determinationRune.effects); const otherChampionAbility = sourceAbility(498568); expect(romChampionCastEffects(otherChampionAbility, otherChampionAbility.effects, 1, 2_000)) .toBe(otherChampionAbility.effects); }); it("casts Determination Rune with an absorb equal to 30% of the current max HP", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-champion", secondaryClassId: "rom-warrior", raceId: "rom-dwarf", level: 30, }); const maxHealth = useCombatStore.getState().maxHealth; expect(useCombatStore.getState().castAbility("rom-champion-rom-warrior-elite-30", undefined, 1_000)) .toMatchObject({ ok: true }); const shield = useCombatStore.getState().auras.find((aura) => aura.definition.absorbs?.length); expect(shield?.definition.absorbs?.[0]?.amount).toBe(Math.round(maxHealth * 0.3)); }); it("keeps Champion cooldown resets source-scoped and removes undefined proc chances", () => { const heartRuneResets = castEffects(498651) .filter((effect) => effect.kind === "cooldown-reset"); expect(heartRuneResets.length).toBeGreaterThan(0); expect(heartRuneResets.every((effect) => effect.kind === "cooldown-reset" && effect.abilityName === "Rune Siphon")).toBe(true); expect(castEffects(498599).some((effect) => effect.kind === "damage")).toBe(true); expect(castEffects(498599).some((effect) => effect.kind === "cooldown-reset")).toBe(false); expect(castEffects(498535).some((effect) => effect.kind === "cooldown-reset")).toBe(false); }); it("cleanses every modeled harmful aura on the party with Righteous Explosion", () => { const cleanse = castEffects(499836).find((effect) => effect.kind === "dispel"); expect(cleanse).toMatchObject({ kind: "dispel", mode: "cleanse", relationship: "friendly", scope: "party", maxCount: 99, }); }); });