forked from phenom/healer-man
141 lines
5.6 KiB
TypeScript
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);
|
|
}
|