Files
healer-man/src/game/romPriestChampionOverrides.ts

141 lines
5.6 KiB
TypeScript

import type { AbilityDefinition, AbilityEffect } from "./abilityCatalog";
import type { ProcTrigger } from "./combatAuras";
export const ROM_PRIEST_WARRIOR_CONDENSED_RAGE_PHYSICAL_DAMAGE_BONUS = 0.22;
export const ROM_PRIEST_CHAMPION_RAGE_CONTROL_PER_COMPLETED_CHANNEL_SECOND = 20;
export const ROM_PRIEST_CHAMPION_RAGE_CONTROL_CHANNEL_SECOND_MS = 1_000;
export const ROM_PRIEST_VOW_OF_SILENCE_NPC_DURATION_MS = 8_400;
export const ROM_PRIEST_VOW_OF_SILENCE_CHARACTER_DURATION_MS = 4_200;
/** Current CoA Vow of Silence is 8.4s on NPCs and 4.2s on player targets. */
export function romPriestVowOfSilenceDurationMs(characterDefender: boolean): number {
return characterDefender
? ROM_PRIEST_VOW_OF_SILENCE_CHARACTER_DURATION_MS
: ROM_PRIEST_VOW_OF_SILENCE_NPC_DURATION_MS;
}
/** Current CoA Condensed Rage (493038) passively increases Physical Damage by 22%. */
export function romPriestWarriorCondensedRagePhysicalDamageMultiplier(
hasCondensedRage: boolean,
): number {
return hasCondensedRage
? 1 + ROM_PRIEST_WARRIOR_CONDENSED_RAGE_PHYSICAL_DAMAGE_BONUS
: 1;
}
export type RomPriestChampionRageControlProgress =
| { kind: "channel"; durationMs: number }
| { kind: "ordinary-cast"; durationMs: number };
/**
* Current CoA Rage Control (498610) grants 20 Rage per second of channeling.
* Only whole completed channel-seconds pay out; an ordinary cast never counts.
*/
export function romPriestChampionRageControlGain(
learnedRageControl: boolean,
progress: RomPriestChampionRageControlProgress,
): number {
if (!learnedRageControl || progress.kind !== "channel"
|| !Number.isFinite(progress.durationMs) || progress.durationMs <= 0) return 0;
const completedSeconds = Math.floor(
progress.durationMs / ROM_PRIEST_CHAMPION_RAGE_CONTROL_CHANNEL_SECOND_MS,
);
return completedSeconds * ROM_PRIEST_CHAMPION_RAGE_CONTROL_PER_COMPLETED_CHANNEL_SECOND;
}
export const ROM_PRIEST_CHAMPION_REGENERATE_PROC_ID = "rom-priest-protection-rune-seal-reset";
export const ROM_PRIEST_CHAMPION_REGENERATE_PROC_ACTION_ID = "rom-priest-protection-rune-seal-reset-heart-rune";
export const ROM_PRIEST_CHAMPION_REGENERATE_PROC_AURA_ID = "rom-priest-protection-rune-seal-regenerate";
export const ROM_PRIEST_CHAMPION_PROTECTION_RUNE_SEAL_SPELL_ID = 498_613;
export const ROM_PRIEST_CHAMPION_REGENERATE_SPELL_ID = 490_269;
export const ROM_PRIEST_CHAMPION_HEART_RUNE_ENERGY_SPELL_ID = 498_612;
/** RuneWaker's exact passive tooltip supplies 8%; current CoA says only "slight chance." */
export const ROM_PRIEST_CHAMPION_REGENERATE_PROC_CHANCE = 0.08;
const ROM_PRIEST_REGENERATE_ABILITY_ID = "rom-priest-general-2";
const PROTECTION_RUNE_SEAL_REGENERATE_PROC_AURA = {
id: ROM_PRIEST_CHAMPION_REGENERATE_PROC_AURA_ID,
name: "Protection Rune Seal: Regenerate",
disposition: "buff" as const,
// Keep the exact 20-second final HoT tick eligible before aura expiry runs.
durationMs: 20_001,
hideFromAuraStrip: true,
procs: [{
id: ROM_PRIEST_CHAMPION_REGENERATE_PROC_ID,
triggers: "periodic" as const,
abilityIds: [ROM_PRIEST_REGENERATE_ABILITY_ID],
chance: ROM_PRIEST_CHAMPION_REGENERATE_PROC_CHANCE,
action: { kind: "custom" as const, id: ROM_PRIEST_CHAMPION_REGENERATE_PROC_ACTION_ID },
}],
};
export interface RomPriestChampionProtectionRuneSealProcContext {
readonly primaryClassId: string | null;
readonly secondaryClassId?: string | null;
readonly learnedPassiveSpellIds: readonly number[];
readonly trigger: ProcTrigger;
readonly triggeringAbilitySpellId?: number;
readonly healingAmount?: number;
}
/**
* Protection Rune Seal resets Heart Rune Energy only for a learned exact pair
* and a positive periodic heal from Regenerate. The chance roll is owned by
* the aura proc definition above.
*/
export function romPriestChampionProtectionRuneSealProcIsEligible(
context: RomPriestChampionProtectionRuneSealProcContext,
): boolean {
return context.primaryClassId === "rom-priest"
&& context.secondaryClassId === "rom-champion"
&& context.learnedPassiveSpellIds.includes(ROM_PRIEST_CHAMPION_PROTECTION_RUNE_SEAL_SPELL_ID)
&& context.trigger === "periodic"
&& context.triggeringAbilitySpellId === ROM_PRIEST_CHAMPION_REGENERATE_SPELL_ID
&& Number.isFinite(context.healingAmount)
&& (context.healingAmount ?? 0) > 0;
}
/**
* The local Infused Light Chain cooldown rider is not present in current CoA:
* ID 498610 is now Rage Control, so it no longer overrides Chain of Light.
*/
export function romPriestChampionCooldownOverride(
_ability: AbilityDefinition,
_learnedPassiveSourceIds: readonly number[],
): number | null {
return null;
}
/** Righteous Explosion removes every modeled harmful effect from the party. */
export function romPriestChampionCastEffects(
ability: AbilityDefinition,
effects: readonly AbilityEffect[],
learnedPassiveSpellIds: readonly number[] = [],
): readonly AbilityEffect[] {
if (ability.source === "runewaker"
&& ability.classId === "rom-priest"
&& ability.dbcSpellId === 490269
&& learnedPassiveSpellIds.includes(ROM_PRIEST_CHAMPION_PROTECTION_RUNE_SEAL_SPELL_ID)) {
return [{
kind: "apply-aura",
aura: PROTECTION_RUNE_SEAL_REGENERATE_PROC_AURA,
recipient: "caster",
sourceEffectType: -1,
sourceAuraType: -1,
miscValue: 0,
triggerSpellId: 498613,
}, ...effects];
}
if (ability.source !== "runewaker"
|| ability.classId !== "rom-champion"
|| ability.secondaryClassId !== "rom-priest"
|| ability.dbcSpellId !== 499836) return effects;
return effects.map((effect) => effect.kind === "dispel" && effect.mode === "cleanse"
? { ...effect, maxCount: 99, scope: "party" }
: effect);
}