Files
healer-man/src/game/wow335WarriorTalentRuntime.ts
T
2026-09-30 09:57:13 -04:00

106 lines
4.4 KiB
TypeScript

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