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

98 lines
3.9 KiB
TypeScript

import type { ActiveAura, AuraDefinition } from "./combatAuras";
export const ROM_WARDEN_REVIVAL_STATE_TAG = "rom-warden-revival-state";
export const ROM_WARDEN_REVIVAL_CHARGE_TAG = "rom-revival-charge";
export const ROM_WARDEN_REVIVAL_DODGE_TAG = "rom-revival-dodge";
export const ROM_WARDEN_REVIVAL_CHARGE_DURATION_MS = 15_000;
export const ROM_WARDEN_REVIVAL_DODGE_DURATION_MS = 6_000;
export function romWardenRevivalBuildMatches(classId: string, secondaryClassId: string | null | undefined): boolean {
return classId === "rom-rogue" && secondaryClassId === "rom-warden";
}
export function romWardenRevivalStateIsActive(
auras: readonly ActiveAura[],
entityId: string,
now: number,
): boolean {
return auras.some((aura) => aura.sourceId === entityId
&& aura.targetId === entityId
&& (aura.expiresAt === null || aura.expiresAt > now)
&& aura.definition.utilityTags?.includes(ROM_WARDEN_REVIVAL_STATE_TAG));
}
export interface RomWardenRevivalIncomingHitInput {
readonly classId: string;
readonly secondaryClassId?: string | null;
readonly stateActive: boolean;
/** Normal excludes critical, dodge, miss, and fully absorbed source events. */
readonly normal: boolean;
/** Health damage after shields and other absorption. */
readonly healthDamage: number;
/** Source calls AddHP before the proc, so dead owners cannot receive it. */
readonly healthAfter: number;
}
export function romWardenRevivalShouldTriggerOnIncomingHit(input: RomWardenRevivalIncomingHitInput): boolean {
return romWardenRevivalBuildMatches(input.classId, input.secondaryClassId)
&& input.stateActive
&& input.normal
&& Number.isFinite(input.healthDamage)
&& input.healthDamage > 0
&& Number.isFinite(input.healthAfter)
&& input.healthAfter > 0;
}
export const ROM_WARDEN_REVIVAL_CHARGE_AURA: AuraDefinition = {
id: "rom-state-revival-charge",
name: "Revival charge",
description: "This RuneWaker state can stack up to ten times and expires after 15 seconds.",
disposition: "buff",
durationMs: ROM_WARDEN_REVIVAL_CHARGE_DURATION_MS,
maxStacks: 10,
stackBehavior: "add",
utilityTags: [ROM_WARDEN_REVIVAL_CHARGE_TAG],
};
export const ROM_WARDEN_REVIVAL_DODGE_AURA: AuraDefinition = {
id: "rom-state-revival-dodge",
name: "Revival dodge",
description: "RuneWaker component 623527 adds 20 physical dodge-rate points for six seconds.",
disposition: "buff",
durationMs: ROM_WARDEN_REVIVAL_DODGE_DURATION_MS,
stackBehavior: "refresh",
utilityTags: [ROM_WARDEN_REVIVAL_DODGE_TAG],
// Source type 198 is physical dodge rate; the current runtime consumes this
// as physical dodge. Type 200's magical-dodge channel has no resolver yet.
modifiers: [{ kind: "stat", stat: "dodge", operation: "flat", value: 20 }],
};
export interface RomWardenRevivalHealInput {
/** Current stack count of RuneWaker component 623072 (source cap: ten). */
readonly stacks: number;
/** Max HP of the self-target receiving hidden child spell 499543. */
readonly targetMaxHealth: number;
}
/**
* Returns Revival's source-backed, noncritical heal amount before overheal.
*
* Lua un_499542 passes FN_CountBuffLevel(owner, 623072) as 499543's MagicLv.
* Child component 623071 has DmgPower 10 and DmgPower_SkillLVArg 50, so the
* source's FixPer branch computes `(10 + 5 * stacks)%` of the target's MaxHP.
* Its FixValue is zero; the other fixed-damage argument is not used by FixPer.
* The child MagicCollect targets self. This path does not scale from weapon
* damage, attack speed, MagicPow, or critical power. Positive Magic defense
* conversion leaves the heal unchanged in the source's CalMagicDMG routine.
*/
export function romWardenRevivalNormalHeal(input: RomWardenRevivalHealInput): number {
const stacks = Number.isFinite(input.stacks)
? Math.max(0, Math.min(10, Math.floor(input.stacks)))
: 0;
const maxHealth = Number.isFinite(input.targetMaxHealth)
? Math.max(0, input.targetMaxHealth)
: 0;
return maxHealth * (10 + 5 * stacks) / 100;
}