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

117 lines
4.6 KiB
TypeScript

import { WOW335_TALENT_NODES } from "./wow335Talents.generated";
export const WOW335_DEATH_KNIGHT_BLOOD_GORGED_TALENT_ID = "talent-2034";
const BLOOD_GORGED_DAMAGE_CHILD_IDS = [50096, 50108, 50109, 50110, 50111] as const;
const BLOOD_GORGED_ARMOR_CHILD_IDS = [61274, 61275, 61276, 61277, 61278] as const;
/** Local Spell.dbc uses Death Knight family 15 and this three-word aura mask. */
export const WOW335_DEATH_KNIGHT_BLOOD_GORGED_FAMILY_NAME = 15;
export const WOW335_DEATH_KNIGHT_BLOOD_GORGED_FAMILY_MASK = [0x01400001, 0, 0] as const;
const BLOOD_GORGED_NODE = WOW335_TALENT_NODES.find(
(node) => node.id === WOW335_DEATH_KNIGHT_BLOOD_GORGED_TALENT_ID,
);
export interface Wow335DeathKnightBloodGorgedProfile {
readonly rank: number;
readonly damageBonusPercent: number;
readonly armorPenetrationPercent: number;
readonly damageChildSpellId: number;
readonly armorChildSpellId: number;
readonly damageAuraType: 79;
readonly damageSchoolMask: 127;
readonly armorAuraType: 280;
readonly spellFamilyName: 15;
readonly spellFamilyMask: readonly [number, number, number];
}
/**
* Read the rank values and child-spell links from the generated local talent
* rows. Aura types and the family mask are verified against the local 3.3.5
* Spell.dbc and recorded in docs/WOW_DEATH_KNIGHT_BLOOD_GORGED.md.
*/
export function wow335DeathKnightBloodGorgedProfile(
rank: number,
): Wow335DeathKnightBloodGorgedProfile | null {
const normalizedRank = Math.trunc(rank);
const rankEffect = BLOOD_GORGED_NODE?.rankEffects[normalizedRank - 1];
const rankDescription = BLOOD_GORGED_NODE?.rankDescriptions[normalizedRank - 1];
if (normalizedRank < 1 || !rankEffect || !rankDescription) return null;
const descriptionValues = rankDescription.match(
/^When you are above 75% health, you deal (\d+)% more damage\. In addition, your attacks ignore up to (\d+)% of your opponent's armor at all times\.$/,
);
const damageBonusPercent = Number(descriptionValues?.[1]);
const armorPenetrationPercent = Number(descriptionValues?.[2]);
const damageChild = rankEffect.effects.find(
(effect) => effect.triggerSpellId === BLOOD_GORGED_DAMAGE_CHILD_IDS[normalizedRank - 1],
);
const armorChild = rankEffect.effects.find(
(effect) => effect.triggerSpellId === BLOOD_GORGED_ARMOR_CHILD_IDS[normalizedRank - 1],
);
if (!descriptionValues
|| !damageChild
|| !armorChild
|| damageChild.basePoints !== damageBonusPercent
|| armorChild.basePoints !== armorPenetrationPercent
|| damageBonusPercent <= 0
|| armorPenetrationPercent <= 0) return null;
return {
rank: normalizedRank,
damageBonusPercent,
armorPenetrationPercent,
damageChildSpellId: damageChild.triggerSpellId,
armorChildSpellId: armorChild.triggerSpellId,
damageAuraType: 79,
damageSchoolMask: 127,
armorAuraType: 280,
spellFamilyName: WOW335_DEATH_KNIGHT_BLOOD_GORGED_FAMILY_NAME,
spellFamilyMask: WOW335_DEATH_KNIGHT_BLOOD_GORGED_FAMILY_MASK,
};
}
/** The local DBC aura applies only to matching Death Knight family-15 spells. */
export function wow335DeathKnightBloodGorgedAffectsSpell(
spellFamilyName: number,
spellFamilyFlags: readonly [number, number, number],
): boolean {
if (spellFamilyName !== WOW335_DEATH_KNIGHT_BLOOD_GORGED_FAMILY_NAME) return false;
if (spellFamilyFlags.some((flags) => !Number.isSafeInteger(flags) || flags < 0 || flags > 0xFFFFFFFF)) return false;
return WOW335_DEATH_KNIGHT_BLOOD_GORGED_FAMILY_MASK.some((mask, index) => (
(spellFamilyFlags[index] & mask) !== 0
));
}
/** Above 75% is strict; exactly 75% does not receive the damage bonus. */
export function wow335DeathKnightBloodGorgedDamageBonusPercent(
rank: number,
currentHealth: number,
maximumHealth: number,
spellFamilyName: number,
spellFamilyFlags: readonly [number, number, number],
): number {
const profile = wow335DeathKnightBloodGorgedProfile(rank);
if (!profile
|| !wow335DeathKnightBloodGorgedAffectsSpell(spellFamilyName, spellFamilyFlags)
|| !Number.isFinite(currentHealth)
|| currentHealth < 0
|| !Number.isFinite(maximumHealth)
|| maximumHealth <= 0
|| currentHealth <= maximumHealth * 0.75) return 0;
return profile.damageBonusPercent;
}
/** Armor penetration is unconditional on health, but remains family-mask gated. */
export function wow335DeathKnightBloodGorgedArmorPenetrationPercent(
rank: number,
spellFamilyName: number,
spellFamilyFlags: readonly [number, number, number],
): number {
const profile = wow335DeathKnightBloodGorgedProfile(rank);
if (!profile || !wow335DeathKnightBloodGorgedAffectsSpell(spellFamilyName, spellFamilyFlags)) return 0;
return profile.armorPenetrationPercent;
}