import { WOW335_TALENT_NODES } from "./wow335Talents.generated"; export const WOW335_SHAMAN_MAELSTROM_WEAPON_TALENT_ID = "talent-2057"; export const WOW335_SHAMAN_MAELSTROM_WEAPON_AURA_TAG = "wow335-shaman-maelstrom-weapon"; export const WOW335_SHAMAN_MAELSTROM_WEAPON_DURATION_MS = 30_000; export const WOW335_SHAMAN_MAELSTROM_WEAPON_MAX_STACKS = 5; export const WOW335_SHAMAN_MAELSTROM_WEAPON_DEFAULT_SPEED_MS = 2_400; export const WOW335_SHAMAN_MAELSTROM_WEAPON_ELIGIBLE_SPELLS: ReadonlySet = new Set([ "Lightning Bolt", "Chain Lightning", "Lava Burst", "Lesser Healing Wave", "Fireball", "Frostbolt", ]); const WOW335_SHAMAN_MAELSTROM_WEAPON_RANK_SPELL_IDS = [51528, 51529, 51530] as const; // The generated rank rows omit proc rates (procChance is 0 for each rank). // WotLKDB spells 51528, 51529, and 51530 supply 4/8/12 procs per minute. const WOW335_SHAMAN_MAELSTROM_WEAPON_PROCS_PER_MINUTE = [4, 8, 12] as const; const WOW335_SHAMAN_MAELSTROM_WEAPON_SOURCE = WOW335_TALENT_NODES.find((node) => ( node.id === WOW335_SHAMAN_MAELSTROM_WEAPON_TALENT_ID )); export interface Wow335ShamanMaelstromWeaponProfile { readonly rank: 1 | 2 | 3; readonly spellId: number; readonly procsPerMinute: number; readonly procChancePerWeaponHit: number; readonly durationMs: number; readonly maxStacks: number; readonly castTimeReductionPerStackPercent: number; readonly pvpDamageReductionPerStackPercent: number; } /** * Resolves the generated Maelstrom Weapon rank and the missing 3.3.5a PPM * value. WotLKDB child spell 53817 supplies the missing 30-second aura * duration; the generated rank tooltip remains authoritative for 20% per * stack and the five-stack cap. */ export function wow335ShamanMaelstromWeaponProfile( rank: number, weaponSpeedMs = WOW335_SHAMAN_MAELSTROM_WEAPON_DEFAULT_SPEED_MS, ): Wow335ShamanMaelstromWeaponProfile | null { const safeRank = Math.min(3, Math.max(0, Math.trunc(Number.isFinite(rank) ? rank : 0))); if (safeRank === 0 || !Number.isFinite(weaponSpeedMs) || weaponSpeedMs <= 0) return null; const rankDescription = WOW335_SHAMAN_MAELSTROM_WEAPON_SOURCE?.rankDescriptions[safeRank - 1]; const rankEffect = WOW335_SHAMAN_MAELSTROM_WEAPON_SOURCE?.rankEffects[safeRank - 1]; const expectedSpellId = WOW335_SHAMAN_MAELSTROM_WEAPON_RANK_SPELL_IDS[safeRank - 1]; if (!rankDescription || !rankEffect || rankEffect.spellId !== expectedSpellId) return null; const castTimeReductionPerStackPercent = Number(rankDescription.match(/reduce the cast time .*? by (\d+)%/i)?.[1]); const maxStacks = Number(rankDescription.match(/Stacks up to (\d+)/i)?.[1]); const pvpDamageReductionPerStackPercent = Number( rankDescription.match(/deal (\d+)% reduced damage in PvP combat per stack/i)?.[1], ); if (!Number.isFinite(castTimeReductionPerStackPercent) || castTimeReductionPerStackPercent <= 0 || !Number.isFinite(maxStacks) || maxStacks <= 0 || !Number.isFinite(pvpDamageReductionPerStackPercent) || pvpDamageReductionPerStackPercent < 0) return null; const procsPerMinute = WOW335_SHAMAN_MAELSTROM_WEAPON_PROCS_PER_MINUTE[safeRank - 1]; return { rank: safeRank as 1 | 2 | 3, spellId: expectedSpellId, procsPerMinute, // The source supplies a PPM rate, so chance scales with the attacking // weapon's speed. This simulator models at most one proc per hit. procChancePerWeaponHit: Math.min(1, procsPerMinute * weaponSpeedMs / 60_000), durationMs: WOW335_SHAMAN_MAELSTROM_WEAPON_DURATION_MS, maxStacks, castTimeReductionPerStackPercent, pvpDamageReductionPerStackPercent, }; } export function wow335ShamanMaelstromWeaponCastTimeReductionPercent( profile: Pick, stacks: number, ): number { const safeStacks = Math.min( profile.maxStacks, Math.max(0, Math.trunc(Number.isFinite(stacks) ? stacks : 0)), ); return Math.min(100, safeStacks * profile.castTimeReductionPerStackPercent); } /** * Resolves the generated Maelstrom Weapon PvP damage penalty for an eligible * damage spell. Lesser Healing Wave is eligible for the cast-time proc but is * a heal, so this damage-only rider does not apply to it. */ export function wow335ShamanMaelstromWeaponPvpDamageReductionPercent( rank: number, consumedStacks: number, spellName: string, ): number { if (!WOW335_SHAMAN_MAELSTROM_WEAPON_ELIGIBLE_SPELLS.has(spellName) || spellName === "Lesser Healing Wave") return 0; const profile = wow335ShamanMaelstromWeaponProfile(rank); if (!profile) return 0; const safeStacks = Math.min( profile.maxStacks, Math.max(0, Math.trunc(Number.isFinite(consumedStacks) ? consumedStacks : 0)), ); return safeStacks * profile.pvpDamageReductionPerStackPercent; }