import { WOW335_TALENT_NODES } from "./wow335Talents.generated"; import { applyAura, type ActiveAura, type AuraDefinition } from "./combatAuras"; import type { WotlkAttackKind } from "./wotlkStats"; export const WOW335_SHAMAN_FLURRY_TALENT_ID = "talent-602"; export const WOW335_SHAMAN_FLURRY_AURA_TAG = "wow335-shaman-flurry"; /** Local child Spell.dbc rows 16257/16277/16278 map duration 8 to 15 seconds. */ export const WOW335_SHAMAN_FLURRY_DURATION_MS = 15_000; const FLURRY_SOURCE = WOW335_TALENT_NODES.find((node) => ( node.id === WOW335_SHAMAN_FLURRY_TALENT_ID )); export interface Wow335ShamanFlurryProfile { readonly rank: number; readonly meleeHastePercent: number; readonly swingCharges: number; } export interface Wow335ShamanFlurryTriggerEvent { readonly attackKind: WotlkAttackKind; readonly landed: boolean; readonly critical: boolean; } /** Reads Flurry's haste and swing count from the selected generated DBC tooltip rank. */ export function wow335ShamanFlurryProfileForRank(rank: number): Wow335ShamanFlurryProfile | null { const maxRank = FLURRY_SOURCE?.rankDescriptions.length ?? 0; const safeRank = Number.isFinite(rank) ? Math.max(0, Math.min(maxRank, Math.trunc(rank))) : 0; if (safeRank <= 0) return null; const description = FLURRY_SOURCE?.rankDescriptions[safeRank - 1]; const match = description?.match( /increases your melee attack speed by (\d+(?:\.\d+)?)% for your next (\d+) swings after dealing a melee critical strike/i, ); if (!match) return null; const meleeHastePercent = Number(match[1]); const swingCharges = Number(match[2]); return Number.isFinite(meleeHastePercent) && meleeHastePercent > 0 && Number.isSafeInteger(swingCharges) && swingCharges > 0 ? { rank: safeRank, meleeHastePercent, swingCharges } : null; } /** The generated tooltip triggers Flurry after a landed melee critical strike. */ export function wow335ShamanFlurryShouldProc( rank: number, event: Wow335ShamanFlurryTriggerEvent, ): boolean { return wow335ShamanFlurryProfileForRank(rank) !== null && event.attackKind === "melee" && event.landed && event.critical; } export function wow335ShamanFlurryAuraDefinition( profile: Wow335ShamanFlurryProfile, ): AuraDefinition { return { id: WOW335_SHAMAN_FLURRY_AURA_TAG, name: "Flurry", description: `Melee attack speed increased by ${profile.meleeHastePercent}% for the next ${profile.swingCharges} swings.`, disposition: "buff", durationMs: WOW335_SHAMAN_FLURRY_DURATION_MS, maxStacks: profile.swingCharges, stackBehavior: "replace", utilityTags: [WOW335_SHAMAN_FLURRY_AURA_TAG], modifiers: [{ kind: "stat", stat: "melee-haste", operation: "flat", value: profile.meleeHastePercent, perStack: false, }], }; } /** Applies/refreshes the three swing charges after an eligible generated rank proc. */ export function wow335ShamanFlurryApplyProc( auras: readonly ActiveAura[], actorId: string, rank: number, now: number, ): readonly ActiveAura[] { const profile = wow335ShamanFlurryProfileForRank(rank); if (!profile) return auras; return applyAura(auras, { definition: wow335ShamanFlurryAuraDefinition(profile), sourceId: actorId, targetId: actorId, stacks: profile.swingCharges, now, }).auras; } export function wow335ShamanFlurryIsActive( auras: readonly ActiveAura[], actorId: string, now: number, ): boolean { return auras.some((aura) => aura.sourceId === actorId && aura.targetId === actorId && aura.definition.utilityTags?.includes(WOW335_SHAMAN_FLURRY_AURA_TAG) && (aura.expiresAt === null || aura.expiresAt > now)); } /** Consumes one active charge for a completed owner autoattack swing attempt. */ export function wow335ShamanFlurryConsumeSwingCharge( auras: readonly ActiveAura[], actorId: string, now: number, ): readonly ActiveAura[] { return auras.flatMap((aura) => { if (aura.sourceId !== actorId || aura.targetId !== actorId || !aura.definition.utilityTags?.includes(WOW335_SHAMAN_FLURRY_AURA_TAG) || (aura.expiresAt !== null && aura.expiresAt <= now)) return [aura]; return aura.stacks > 1 ? [{ ...aura, stacks: aura.stacks - 1 }] : []; }); } /** Exposes the generated rank row for provenance checks and focused tests. */ export function wow335ShamanFlurryGeneratedSource() { return FLURRY_SOURCE ?? null; }