Files
healer-man/src/game/wow335WarriorUnbridledWrathRuntime.ts
T
2026-10-02 10:38:12 -04:00

77 lines
3.3 KiB
TypeScript

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;
}