Files
healer-man/src/game/partyBuffPolicy.ts
T
2026-09-26 17:51:56 -04:00

34 lines
1.8 KiB
TypeScript

import type { AbilityDefinition, AbilityEffect } from "./abilityCatalog";
export type PartyBuffScope = "party" | "tank" | "healer";
export interface PartyBuffRule { readonly abilityId: string; readonly scope: PartyBuffScope }
export interface PartyBuffPolicy { readonly schemaVersion: 1; readonly rules: readonly PartyBuffRule[] }
export const DEFAULT_PARTY_BUFF_POLICY: PartyBuffPolicy = { schemaVersion: 1, rules: [] };
export function normalizePartyBuffPolicy(value: unknown): PartyBuffPolicy {
if (!value || typeof value !== "object") return DEFAULT_PARTY_BUFF_POLICY;
const policy = value as Partial<PartyBuffPolicy>;
if (policy.schemaVersion !== 1 || !Array.isArray(policy.rules)) return DEFAULT_PARTY_BUFF_POLICY;
const seen = new Set<string>();
return {
schemaVersion: 1,
rules: policy.rules.flatMap((rule) => {
if (!rule || typeof rule.abilityId !== "string" || !/^[a-z0-9][a-z0-9:._-]{0,127}$/i.test(rule.abilityId)
|| !["party", "tank", "healer"].includes(rule.scope) || seen.has(rule.abilityId)) return [];
seen.add(rule.abilityId);
return [{ abilityId: rule.abilityId, scope: rule.scope }];
}).slice(0, 500),
};
}
export function permanentBuffEffects(ability: AbilityDefinition): readonly Extract<AbilityEffect, { kind: "apply-aura" }>[] {
if (ability.passive || ability.target === "hostile" || ability.talentNodeId || ability.talentEntryId !== undefined) return [];
if (ability.effects.some((effect) => effect.kind !== "apply-aura" && effect.kind !== "unsupported")) return [];
return ability.effects.filter((effect): effect is Extract<AbilityEffect, { kind: "apply-aura" }> => (
effect.kind === "apply-aura" && effect.aura.disposition === "buff"
&& !effect.aura.procs?.length && !effect.aura.controlImmunities?.length
&& Boolean(effect.aura.modifiers?.length)
));
}