forked from phenom/healer-man
95 lines
3.8 KiB
TypeScript
95 lines
3.8 KiB
TypeScript
import { ActiveAura, AuraDefinition } from "./combatAuras";
|
|
import { WOW335_TALENT_NODES } from "./wow335Talents.generated";
|
|
|
|
export const WOW335_DEATH_KNIGHT_MARK_OF_BLOOD_TAG = "wow335-death-knight-mark-of-blood";
|
|
export const WOW335_DEATH_KNIGHT_MARK_OF_BLOOD_SPELL_ID = 49005;
|
|
export const WOW335_DEATH_KNIGHT_MARK_OF_BLOOD_ABILITY_ID = "wow335-death-knight-mark-of-blood";
|
|
|
|
export interface Wow335DeathKnightMarkOfBloodProfile {
|
|
readonly healingFraction: number;
|
|
readonly durationMs: number;
|
|
readonly hitCap: number;
|
|
}
|
|
|
|
export interface Wow335DeathKnightMarkOfBloodDamageResult {
|
|
readonly auras: readonly ActiveAura[];
|
|
readonly triggered: boolean;
|
|
readonly healingAmount: number;
|
|
readonly sourceActorId?: string;
|
|
readonly abilityId?: string;
|
|
readonly remainingHits?: number;
|
|
}
|
|
|
|
const MARK_OF_BLOOD_TALENT = WOW335_TALENT_NODES.find((node) => node.id === "talent-1949");
|
|
|
|
/** Read Mark of Blood's heal, duration, and charge cap from the local generated rank. */
|
|
export function wow335DeathKnightMarkOfBloodProfile(rank = 1): Wow335DeathKnightMarkOfBloodProfile | null {
|
|
const normalizedRank = Math.trunc(rank);
|
|
const description = MARK_OF_BLOOD_TALENT?.rankDescriptions[normalizedRank - 1];
|
|
const rankEffect = MARK_OF_BLOOD_TALENT?.rankEffects[normalizedRank - 1];
|
|
if (!description || !rankEffect) return null;
|
|
|
|
const healPercent = /healed for\s+(\d+(?:\.\d+)?)% of its maximum health/i.exec(description);
|
|
const durationSeconds = /Lasts for\s+(\d+(?:\.\d+)?) seconds/i.exec(description);
|
|
const healingFraction = healPercent ? Number(healPercent[1]) / 100 : 0;
|
|
const durationMs = durationSeconds ? Math.round(Number(durationSeconds[1]) * 1_000) : 0;
|
|
const hitCap = Math.floor(rankEffect.procCharges ?? 0);
|
|
if (!(healingFraction > 0) || !(durationMs > 0) || !(hitCap > 0)) return null;
|
|
|
|
return { healingFraction, durationMs, hitCap };
|
|
}
|
|
|
|
/** Attach the generated Mark of Blood hit count and trigger tag to its DBC aura. */
|
|
export function wow335DeathKnightMarkOfBloodAuraDefinition(
|
|
definition: AuraDefinition,
|
|
): AuraDefinition {
|
|
const profile = wow335DeathKnightMarkOfBloodProfile();
|
|
if (!profile) return definition;
|
|
return {
|
|
...definition,
|
|
durationMs: profile.durationMs,
|
|
maxStacks: profile.hitCap,
|
|
initialStacks: profile.hitCap,
|
|
stackBehavior: "replace",
|
|
utilityTags: [...new Set([...(definition.utilityTags ?? []), WOW335_DEATH_KNIGHT_MARK_OF_BLOOD_TAG])],
|
|
};
|
|
}
|
|
|
|
/** Consume one Mark charge for landed damage; only surviving targets can receive the heal. */
|
|
export function wow335DeathKnightMarkOfBloodOnDamage(
|
|
auras: readonly ActiveAura[],
|
|
markedEnemyId: string,
|
|
targetHealthAfterDamage: number,
|
|
targetMaxHealth: number,
|
|
now: number,
|
|
landedDamage: boolean,
|
|
): Wow335DeathKnightMarkOfBloodDamageResult {
|
|
if (!landedDamage || !(targetMaxHealth > 0)) {
|
|
return { auras, triggered: false, healingAmount: 0 };
|
|
}
|
|
const mark = auras.find((aura) => aura.targetId === markedEnemyId
|
|
&& (aura.expiresAt === null || aura.expiresAt > now)
|
|
&& aura.definition.utilityTags?.includes(WOW335_DEATH_KNIGHT_MARK_OF_BLOOD_TAG));
|
|
const profile = wow335DeathKnightMarkOfBloodProfile();
|
|
if (!mark || !profile || mark.stacks <= 0) {
|
|
return { auras, triggered: false, healingAmount: 0 };
|
|
}
|
|
|
|
const remainingHits = mark.stacks - 1;
|
|
const nextAuras = remainingHits > 0
|
|
? auras.map((aura) => aura.instanceId === mark.instanceId
|
|
? { ...aura, stacks: remainingHits }
|
|
: aura)
|
|
: auras.filter((aura) => aura.instanceId !== mark.instanceId);
|
|
return {
|
|
auras: nextAuras,
|
|
triggered: true,
|
|
healingAmount: targetHealthAfterDamage > 0
|
|
? Math.round(targetMaxHealth * profile.healingFraction)
|
|
: 0,
|
|
sourceActorId: mark.sourceId,
|
|
abilityId: WOW335_DEATH_KNIGHT_MARK_OF_BLOOD_ABILITY_ID,
|
|
remainingHits,
|
|
};
|
|
}
|