import { WOW335_TALENT_NODES } from "./wow335Talents.generated"; export const WOW335_WARRIOR_BLOOD_CRAZE_TALENT_ID = "talent-661"; export const WOW335_WARRIOR_BLOOD_CRAZE_COOLDOWN_KEY = "wow335-warrior-blood-craze"; export const WOW335_WARRIOR_BLOOD_CRAZE_DURATION_MS = 6_000; export const WOW335_WARRIOR_BLOOD_CRAZE_INTERNAL_COOLDOWN_MS = 12_000; export const WOW335_WARRIOR_SECOND_WIND_TALENT_ID = "talent-1663"; export const WOW335_WARRIOR_SECOND_WIND_COOLDOWN_KEY = "wow335-warrior-second-wind"; export const WOW335_WARRIOR_SECOND_WIND_INTERVAL_MS = 2_000; const WOW335_WARRIOR_SECOND_WIND_SOURCE = WOW335_TALENT_NODES.find((node) => ( node.id === WOW335_WARRIOR_SECOND_WIND_TALENT_ID )); export interface Wow335WarriorSecondWindProfile { readonly rank: 1 | 2 | 3; readonly healthThresholdPercent: number; readonly procChancePercent: number; readonly durationMs: number; readonly cooldownMs: number; readonly ticks: number; readonly intervalMs: number; readonly totalHealing: number; readonly healingPerTick: number; readonly tickAmounts: readonly number[]; } /** * Uses the populated generated rank tooltip for threshold, duration, heal, * cooldown, and the generated rank procChance. WotLKDB spells 29841/29842 * both show a 2-second periodic interval; the generated custom rank-3 child * (829839) has no WotLKDB row, so the same confirmed cadence is carried forward. */ export function wow335WarriorSecondWindProfile( baseHealth: number, rank: number, ): Wow335WarriorSecondWindProfile | null { const safeRank = Math.min(3, Math.max(0, Math.trunc(Number.isFinite(rank) ? rank : 0))); const rankDescription = WOW335_WARRIOR_SECOND_WIND_SOURCE?.rankDescriptions[safeRank - 1]; const rankEffect = WOW335_WARRIOR_SECOND_WIND_SOURCE?.rankEffects[safeRank - 1]; if (safeRank === 0 || !rankDescription || !rankEffect || !Number.isFinite(baseHealth) || baseHealth <= 0) { return null; } const healthThresholdPercent = Number(rankDescription.match(/below (\d+)% health/i)?.[1]); const healPercent = Number(rankDescription.match(/(\d+)% of your base health over (\d+) seconds/i)?.[1]); const durationSeconds = Number(rankDescription.match(/base health over (\d+) seconds/i)?.[1]); const cooldownSeconds = Number(rankDescription.match(/once every (\d+) seconds/i)?.[1]); const durationMs = durationSeconds * 1_000; if (!Number.isFinite(healthThresholdPercent) || !Number.isFinite(healPercent) || !Number.isFinite(durationMs) || durationMs <= 0 || durationMs % WOW335_WARRIOR_SECOND_WIND_INTERVAL_MS !== 0 || !Number.isFinite(cooldownSeconds) || cooldownSeconds <= 0) return null; const ticks = durationMs / WOW335_WARRIOR_SECOND_WIND_INTERVAL_MS; const totalHealing = Math.round(baseHealth * healPercent / 100); const baseTickHealing = Math.floor(totalHealing / ticks); const bonusTicks = totalHealing - baseTickHealing * ticks; const tickAmounts = Array.from({ length: ticks }, (_, index) => baseTickHealing + Number(index < bonusTicks)); return { rank: safeRank as 1 | 2 | 3, healthThresholdPercent, procChancePercent: Math.max(0, Math.min(100, rankEffect.procChance)), durationMs, cooldownMs: cooldownSeconds * 1_000, ticks, intervalMs: WOW335_WARRIOR_SECOND_WIND_INTERVAL_MS, totalHealing, healingPerTick: totalHealing / ticks, tickAmounts, }; } export interface Wow335WarriorBloodCrazeProfile { readonly rank: 1 | 2 | 3; readonly ticks: number; readonly intervalMs: number; readonly totalHealing: number; readonly healingPerTick: number; } /** * Builds the tooltip-defined total heal and uses each rank's hidden child aura * interval only to distribute it through the six-second duration. Rank 3's * one-second interval is also exposed by WotLKDB spell 16491. */ export function wow335WarriorBloodCrazeProfile( baseHealth: number, rank: number, enraged: boolean, ): Wow335WarriorBloodCrazeProfile | null { const safeRank = Math.min(3, Math.max(0, Math.trunc(Number.isFinite(rank) ? rank : 0))); if (safeRank === 0 || !Number.isFinite(baseHealth) || baseHealth <= 0) return null; const intervalMs = [0, 3_000, 1_500, 1_000][safeRank]; const ticks = WOW335_WARRIOR_BLOOD_CRAZE_DURATION_MS / intervalMs; const totalHealing = Math.round(baseHealth * safeRank / 100 * (enraged ? 3 : 1)); return { rank: safeRank as 1 | 2 | 3, ticks, intervalMs, totalHealing, healingPerTick: totalHealing / ticks, }; }