import { WOW335_TALENT_NODES } from "./wow335Talents.generated"; export const WOW335_WARRIOR_UNBRIDLED_WRATH_TALENT_ID = "talent-159"; export const WOW335_WARRIOR_UNBRIDLED_WRATH_ABILITY_ID = "wow335-warrior-unbridled-wrath"; export const WOW335_WARRIOR_UNBRIDLED_WRATH_PPM_PER_RANK = 3; const UNBRIDLED_WRATH_SOURCE = WOW335_TALENT_NODES.find((node) => ( node.id === WOW335_WARRIOR_UNBRIDLED_WRATH_TALENT_ID )); export interface Wow335WarriorUnbridledWrathProfile { readonly rank: 1 | 2; readonly ragePerProc: number; /** PPM for each weapon hand, matching the simulator's PPM manager. */ readonly procsPerMinutePerWeapon: number; } /** * Reads Rage per proc from this project's generated tooltip and supplies the * otherwise missing rate from the upstream WotLK Simulator's 3 PPM per rank * model. The simulator applies that rate to melee white-hit proc masks and * awards 1 Rage; local tooltip values remain authoritative for our 2/4 Rage. * * The generated rank-1 tooltip says 2 Rage but links child 12964, whose local * payload says 1. Rank 2 says 4 Rage and links custom child 812964, which is * absent from stock 3.3.5a. This helper intentionally trusts tooltip amounts * and does not use child payloads or the generated procChance field (zero). * Sources: https://github.com/wowsims/wotlk/blob/master/sim/warrior/talents.go * and https://wotlkdb.com/?spell=12964 */ export function wow335WarriorUnbridledWrathProfile( talentRank: number, ): Wow335WarriorUnbridledWrathProfile | null { const maximumRank = Math.min(2, UNBRIDLED_WRATH_SOURCE?.rankDescriptions.length ?? 0); const rank = Math.min(maximumRank, Math.max(0, Math.trunc( Number.isFinite(talentRank) ? talentRank : 0, ))); if (rank === 0) return null; const description = UNBRIDLED_WRATH_SOURCE?.rankDescriptions[rank - 1]; const rankEffect = UNBRIDLED_WRATH_SOURCE?.rankEffects[rank - 1]; const ragePerProc = Number(description?.match(/generate (\d+) additional rage points/i)?.[1]); if (!description || !rankEffect || rankEffect.spellId !== UNBRIDLED_WRATH_SOURCE?.rankSpellIds[rank - 1] || !Number.isFinite(ragePerProc) || ragePerProc <= 0) return null; return { rank: rank as 1 | 2, ragePerProc, procsPerMinutePerWeapon: WOW335_WARRIOR_UNBRIDLED_WRATH_PPM_PER_RANK * rank, }; } /** * The upstream simulator's PPM manager converts per-weapon PPM to a proc * chance using the weapon's un-hasted swing speed: PPM * speed / 60 seconds. * Call only for a landed damaging hit matching its melee-white-hit mask. */ export function wow335WarriorUnbridledWrathChancePerMeleeWhiteHit( talentRank: number, weaponSwingSpeedMs: number, ): number { const profile = wow335WarriorUnbridledWrathProfile(talentRank); if (!profile || !Number.isFinite(weaponSwingSpeedMs) || weaponSwingSpeedMs <= 0) return 0; return Math.min(1, profile.procsPerMinutePerWeapon * weaponSwingSpeedMs / 60_000); } /** Resolve a deterministic 0..1 proc roll for an eligible melee white hit. */ export function wow335WarriorUnbridledWrathShouldProc( talentRank: number, weaponSwingSpeedMs: number, roll: number, ): boolean { const chance = wow335WarriorUnbridledWrathChancePerMeleeWhiteHit(talentRank, weaponSwingSpeedMs); if (chance <= 0 || !Number.isFinite(roll) || roll < 0 || roll >= 1) return false; return roll < chance; }