import { describe, expect, it } from "vitest"; import { applyAura, type ActiveAura, type AuraDefinition } from "./combatAuras"; import { ENEMY_BLOCK_AURA_SOURCE_PINS, enemyBlockAuraContract, enemyBlockAuraInputDefinition, ordinaryCreatureSelfTargetCriticalBlockAuraDefinition, type EnemyNpcCriticalBlockImportProof, enemyNpcBlockValueAfterCritical, enemyNpcCriticalBlockChanceForRoll, resolveEnemyNpcBlockAuraInputs, supportedEnemyBlockAuraDefinition, SPELL_AURA_IGNORE_HIT_DIRECTION, SPELL_AURA_MOD_BLOCK_CRIT_CHANCE, SPELL_AURA_MOD_BLOCK_PERCENT, } from "./enemyBlockAuras"; function addAura( auras: readonly ActiveAura[], definition: AuraDefinition, targetId: string, now: number, ): readonly ActiveAura[] { return applyAura(auras, { definition, sourceId: "source", targetId, now }).auras; } function ordinaryCreatureSelfTargetProof(id = "ordinary-creature-47294"): EnemyNpcCriticalBlockImportProof { return { defenderId: id, casterId: id, targetId: id, defenderKind: "ordinary-creature", isNativeDefender: false, isSummonedDefender: false, isCharacterDefender: false, aura107Applicability: "azerothcore-e822d718-default-spellmod-path", }; } describe("source-bound enemy NPC block auras", () => { it("pins the exact profiles and preserves their aura types, amounts, and durations", () => { expect(ENEMY_BLOCK_AURA_SOURCE_PINS.wow335.spellDbcSha256) .toBe("57011d528fd66cf39f5ab6fb168e389ab9e4cc1b8050f7201a54239d489b415c"); expect(ENEMY_BLOCK_AURA_SOURCE_PINS.ascension.spellDbcSha256) .toBe("2f77f18b91ffbae32a19568e4bc05ec78ee384b6ba8dd2a6e05f0e97abbb2dce"); expect(ENEMY_BLOCK_AURA_SOURCE_PINS.epoch.spellDbcSha256) .toBe("d5cce1a83550dcfa9eb2f0251dbb11fd24c272534b2b1a9b230924a44d817ab3"); for (const profile of ["wow335", "ascension", "epoch"] as const) { expect(enemyBlockAuraContract(3419, profile)).toMatchObject({ durationMs: 6_000, effects: [{ effectType: 6, auraType: SPELL_AURA_MOD_BLOCK_PERCENT, basePoints: 75 }], blockInputs: { blockChanceAuraPercent: 75 }, }); } for (const [profile, chance] of [["wow335", 20], ["epoch", 20], ["ascension", 100]] as const) { const criticalBlock = enemyBlockAuraContract(47294, profile)!; expect(criticalBlock).toMatchObject({ durationMs: null, signature: { durationMs: -1 }, blockInputs: { criticalBlockChancePercent: chance }, }); expect(criticalBlock.effects.map(({ auraType, basePoints, miscValue }) => ( [auraType, basePoints, miscValue] ))).toEqual(profile === "ascension" ? [[SPELL_AURA_MOD_BLOCK_CRIT_CHANCE, 100, 0], [107, 4, 7], [107, 8, 3]] : [[SPELL_AURA_MOD_BLOCK_CRIT_CHANCE, chance, 0], [107, 5, 7]]); expect(criticalBlock.unsupportedEffects.map(({ auraType, basePoints, miscValue }) => ( [auraType, basePoints, miscValue] ))).toEqual(profile === "ascension" ? [[107, 4, 7], [107, 8, 3]] : [[107, 5, 7]]); } const wowDeterrence = enemyBlockAuraContract(67801, "wow335")!; expect(wowDeterrence).toMatchObject({ durationMs: 5_000, blockInputs: { ignoresHitDirection: true }, }); expect(wowDeterrence.effects.map(({ auraType, basePoints }) => [auraType, basePoints])) .toEqual([[SPELL_AURA_IGNORE_HIT_DIRECTION, 100], [185, -100]]); expect(enemyBlockAuraContract(67801, "ascension")?.durationMs).toBe(60_000); expect(enemyBlockAuraContract(667801, "ascension")?.durationMs).toBe(5_000); expect(enemyBlockAuraContract(1167801, "ascension")?.effects[0]).toMatchObject({ auraType: SPELL_AURA_IGNORE_HIT_DIRECTION, basePoints: 100, }); expect(enemyBlockAuraContract(47929, "wow335")).toBeUndefined(); }); it("only imports complete aura definitions and keeps input-only siblings explicit", () => { const improvedBlocking = enemyBlockAuraContract(3419, "epoch")!; expect(supportedEnemyBlockAuraDefinition(improvedBlocking)).toMatchObject({ id: "enemy-source-spell-3419", utilityTags: ["enemy-npc-block-aura:51:75"], }); for (const profile of ["wow335", "ascension", "epoch"] as const) { expect(supportedEnemyBlockAuraDefinition(enemyBlockAuraContract(47294, profile)!)).toBeUndefined(); } expect(supportedEnemyBlockAuraDefinition(enemyBlockAuraContract(67801, "wow335")!)).toBeUndefined(); const definition = enemyBlockAuraInputDefinition(enemyBlockAuraContract(47295, "epoch")!); expect(definition).toMatchObject({ id: "enemy-source-spell-47295", name: "Critical Block", durationMs: null, disposition: "buff", utilityTags: ["enemy-npc-block-aura:253:40"], }); expect(enemyBlockAuraInputDefinition(enemyBlockAuraContract(67801, "epoch")!).utilityTags) .toContain("enemy-npc-block-aura:288:0"); }); it("imports reviewed Critical Block rows only for an ordinary Creature self-target proof", () => { const expectedChances = { wow335: [20, 40, 60], ascension: [100, 100, 100], epoch: [20, 40, 60], } as const; for (const profile of ["wow335", "ascension", "epoch"] as const) { for (const [rankIndex, spellId] of [47294, 47295, 47296].entries()) { const contract = enemyBlockAuraContract(spellId, profile)!; const imported = ordinaryCreatureSelfTargetCriticalBlockAuraDefinition( contract, ordinaryCreatureSelfTargetProof(), ); expect(imported).toMatchObject({ id: `enemy-source-spell-${spellId}`, name: "Critical Block", durationMs: null, disposition: "buff", utilityTags: [`enemy-npc-block-aura:${SPELL_AURA_MOD_BLOCK_CRIT_CHANCE}:${expectedChances[profile][rankIndex]}`], }); // The generic complete-spell importer remains closed for these rows. expect(supportedEnemyBlockAuraDefinition(contract)).toBeUndefined(); } } const proof = ordinaryCreatureSelfTargetProof(); const validContract = enemyBlockAuraContract(47294, "wow335")!; const invalidProofs: readonly unknown[] = [ null, {}, { ...proof, defenderKind: "player" }, { ...proof, isNativeDefender: true }, { ...proof, isSummonedDefender: true }, { ...proof, isCharacterDefender: true }, { ...proof, aura107Applicability: "custom-server-script" }, { ...proof, defenderId: "" }, { ...proof, casterId: "different-caster" }, { ...proof, targetId: "different-target" }, ]; for (const invalidProof of invalidProofs) { expect(ordinaryCreatureSelfTargetCriticalBlockAuraDefinition( validContract, invalidProof as EnemyNpcCriticalBlockImportProof, )).toBeUndefined(); } expect(ordinaryCreatureSelfTargetCriticalBlockAuraDefinition(undefined, proof)).toBeUndefined(); expect(ordinaryCreatureSelfTargetCriticalBlockAuraDefinition(null, proof)).toBeUndefined(); const invalidContracts: readonly unknown[] = [ {}, { ...validContract, profile: "unreviewed-profile" }, { ...validContract, blockInputs: null }, { ...validContract, signature: null }, ]; for (const invalidContract of invalidContracts) { expect(ordinaryCreatureSelfTargetCriticalBlockAuraDefinition( invalidContract as typeof validContract, proof, )).toBeUndefined(); } expect(ordinaryCreatureSelfTargetCriticalBlockAuraDefinition( enemyBlockAuraContract(67801, "wow335"), proof, )).toBeUndefined(); expect(ordinaryCreatureSelfTargetCriticalBlockAuraDefinition( enemyBlockAuraContract(3419, "wow335"), proof, )).toBeUndefined(); }); it("fails closed when a Critical Block raw source signature drifts", () => { const contract = enemyBlockAuraContract(47294, "ascension")!; const proof = ordinaryCreatureSelfTargetProof(); const changedRawSignature = { ...contract, signature: { ...contract.signature, effects: contract.signature.effects.map((sourceEffect, index) => ( index === 2 ? { ...sourceEffect, miscValue: 7 } : sourceEffect )), }, }; expect(ordinaryCreatureSelfTargetCriticalBlockAuraDefinition( changedRawSignature, proof, )).toBeUndefined(); const changedContractEffects = { ...contract, effects: contract.effects.map((sourceEffect, index) => ( index === 1 ? { ...sourceEffect, basePoints: 99 } : sourceEffect )), }; expect(ordinaryCreatureSelfTargetCriticalBlockAuraDefinition( changedContractEffects, proof, )).toBeUndefined(); }); it("sums target-local active aura inputs and ignores expired or unrelated auras", () => { let auras: readonly ActiveAura[] = []; auras = addAura(auras, enemyBlockAuraInputDefinition(enemyBlockAuraContract(3419, "epoch")!), "mob-a", 100); auras = addAura(auras, enemyBlockAuraInputDefinition(enemyBlockAuraContract(47294, "epoch")!), "mob-a", 100); auras = addAura(auras, enemyBlockAuraInputDefinition(enemyBlockAuraContract(67801, "epoch")!), "mob-a", 100); auras = addAura(auras, enemyBlockAuraInputDefinition(enemyBlockAuraContract(47296, "epoch")!), "mob-b", 100); const expiry = enemyBlockAuraInputDefinition({ ...enemyBlockAuraContract(47295, "epoch")!, durationMs: 10, }); auras = addAura(auras, expiry, "mob-a", 100); expect(resolveEnemyNpcBlockAuraInputs({ auras, targetId: "mob-a", now: 111 })).toEqual({ blockChanceAuraPercent: 75, criticalBlockChancePercent: 20, ignoresHitDirection: true, }); expect(resolveEnemyNpcBlockAuraInputs({ auras, targetId: "mob-a", now: 109 })?.criticalBlockChancePercent) .toBe(60); }); it("adapts critical-block chance to integer roll_chance_i and doubles only a successful block value", () => { expect(enemyNpcCriticalBlockChanceForRoll(19.9)).toBe(19); expect(enemyNpcCriticalBlockChanceForRoll(-2.7)).toBe(-2); expect(enemyNpcCriticalBlockChanceForRoll(Number.NaN)).toBeNull(); expect(enemyNpcBlockValueAfterCritical(39, false)).toBe(39); expect(enemyNpcBlockValueAfterCritical(39, true)).toBe(78); expect(enemyNpcBlockValueAfterCritical(Number.MAX_SAFE_INTEGER, true)).toBeNull(); expect(enemyNpcBlockValueAfterCritical(-1, true)).toBeNull(); }); });