import type { AbilityDefinition, AbilityEffect } from "./abilityCatalog"; import type { RomGeneratedAbilityLike } from "./romAbilitySemantics"; function passiveState(sourceId: number, name: string, tag: string): readonly AbilityEffect[] { return [{ kind: "apply-aura", recipient: "caster", aura: { id: `rom-pair-passive-${sourceId}`, name, disposition: "buff", durationMs: null, utilityTags: [tag], }, sourceEffectType: -1, sourceAuraType: -1, miscValue: 0, triggerSpellId: sourceId, }]; } /** Source-specific pair mechanics that the generic component translator cannot infer. */ export function overrideRomPriestKnightEffects( row: RomGeneratedAbilityLike, translated: readonly AbilityEffect[], ): readonly AbilityEffect[] | null { switch (row.sourceSkillId) { case 492355: // Enhanced Grace of Life: Grace of Life also raises maximum MP. return passiveState(492355, row.name, "rom-enhanced-grace-of-life"); case 492361: // Life Link: Urgent Heal also heals its caster. return passiveState(492361, row.name, "rom-life-link"); case 499968: // Splendor Ripple: a triggered extra heal on source skill 499967. return passiveState(499968, row.name, "rom-splendor-ripple"); case 499957: // Light Guidance: activate healing bonus only with source state 499966. return passiveState(499957, row.name, "rom-light-guidance"); case 492638: // Lightning Rod: Lightning shocks targets already afflicted by Bone Chill. return passiveState(492638, row.name, "rom-priest-mage-lightning-rod"); case 491610: // Enhanced Holy Strike: improves Holy Strike weapon damage. return passiveState(491610, row.name, "rom-enhanced-holy-strike"); case 490066: // Weaken Weapon Strength: Heavenly Arrow weakens target physical damage. return passiveState(490066, row.name, "rom-weaken-weapon-strength"); case 499926: // Evil Shackles: Holy Chains can root any creature with shorter duration. return passiveState(499926, row.name, "rom-evil-shackles"); case 499923: // Blood Punishment: source skill 499924 grants conditional bonuses. return passiveState(499923, row.name, "rom-blood-punishment"); case 491648: // Last Prayer affects only the next spell's cast time. return [{ kind: "apply-aura", recipient: "caster", aura: { id: "rom-pair-last-prayer-next-spell", name: row.name, disposition: "buff", durationMs: null, utilityTags: ["rom-last-prayer"], dispelCategory: "magic", }, sourceEffectType: -1, sourceAuraType: 51, miscValue: -1_000, triggerSpellId: 0, }]; case 493271: // Light Fairy has source lifetime -1; keep it until dismissal or session end. return translated.map((effect) => effect.kind === "summon" ? { ...effect, durationMs: 86_400_000 } : effect); case 492639: // Calm Heart, component 502908: 10 seconds of Fear immunity. return [{ kind: "apply-aura", recipient: "ability-target", aura: { id: "rom-pair-calm-heart-fear-immunity", name: row.name, disposition: "buff", durationMs: 10_000, controlImmunities: ["fear"], dispelCategory: "magic", }, sourceEffectType: -1, sourceAuraType: -1, miscValue: 0, triggerSpellId: 0, }]; case 493046: // Sacred Protection, component 503290: +100 shield block for 20 seconds. return [{ kind: "apply-aura", recipient: "caster", aura: { id: "rom-pair-sacred-protection-block", name: row.name, disposition: "buff", durationMs: 20_000, modifiers: [{ kind: "stat", stat: "block-chance", operation: "flat", value: 100 }], utilityTags: ["rom-sacred-protection"], dispelCategory: "magic", }, sourceEffectType: -1, sourceAuraType: 172, miscValue: 100, triggerSpellId: 0, }]; case 493273: // Disregard Danger: redirect 40% of one hit; caster gains 20 Parry. return [ { kind: "apply-aura", recipient: "ability-target", aura: { id: "rom-pair-disregard-danger-redirect", name: row.name, disposition: "buff", durationMs: 10_000, redirectDamagePercent: 0.4, utilityTags: ["rom-disregard-danger-one-hit"], dispelCategory: "magic", }, sourceEffectType: -1, sourceAuraType: 143, miscValue: 40, triggerSpellId: 491614, }, { kind: "apply-aura", recipient: "caster", aura: { id: "rom-pair-disregard-danger-parry", name: `${row.name}: Parry`, disposition: "buff", durationMs: 10_000, modifiers: [{ kind: "stat", stat: "parry", operation: "flat", value: 20 }], dispelCategory: "magic", }, sourceEffectType: -1, sourceAuraType: 22, miscValue: 20, triggerSpellId: 0, }, ]; case 491613: // Holy Chains only roots; source flag 67 is not three controls. return translated.filter((effect) => effect.kind === "apply-aura" && effect.control?.kind === "root"); default: return null; } } function hasTag(tags: readonly string[] | ReadonlySet, tag: string): boolean { return Array.isArray(tags) ? tags.includes(tag) : (tags as ReadonlySet).has(tag); } /** Apply passive pair upgrades when their parent skill is actually cast. */ export function romPriestKnightCastEffects( ability: AbilityDefinition, effects: readonly AbilityEffect[], passiveTags: readonly string[] | ReadonlySet, casterMaxHealth: number, targetTags: readonly string[] | ReadonlySet = [], friendlyTargetHealthPercent = 1, learnedPassiveSpellIds: readonly number[] = [], ): readonly AbilityEffect[] { if (ability.source !== "runewaker" || casterMaxHealth <= 0) return effects; if (ability.dbcSpellId === 490261 && learnedPassiveSpellIds.includes(493039) && friendlyTargetHealthPercent < 0.3) { // Saint's Blessing's active source components grant +30 healing points and // +1% healing power for 10 seconds. Put the aura before Heal so the // triggering cast itself receives the sourced bonus as well. return [{ kind: "apply-aura", recipient: "caster", aura: { id: "rom-priest-saints-blessing-healing", name: "Saint's Blessing", disposition: "buff", durationMs: 10_000, modifiers: [ { kind: "healing", direction: "done", operation: "flat", value: 30 }, { kind: "healing", direction: "done", operation: "percent", value: 0.01 }, ], dispelCategory: "magic", }, sourceEffectType: -1, sourceAuraType: -1, miscValue: 0, triggerSpellId: 493039, }, ...effects]; } switch (ability.dbcSpellId) { case 490290: // Enhanced Grace of Life mirrors source Grace's +50 and +1% HP as MP. if (!hasTag(passiveTags, "rom-enhanced-grace-of-life")) return effects; return [...effects, { kind: "apply-aura", recipient: "ability-target", aura: { id: "rom-pair-enhanced-grace-mana", name: "Enhanced Grace of Life: Mana", disposition: "buff", durationMs: 1_800_000, modifiers: [ { kind: "resource", resource: "mana", property: "maximum", operation: "flat", value: 50 }, { kind: "resource", resource: "mana", property: "maximum", operation: "percent", value: 0.01 }, ], dispelCategory: "magic", }, sourceEffectType: -1, sourceAuraType: 8, miscValue: 50, triggerSpellId: 492355, }]; case 491147: // Life Link duplicates Urgent Heal's effective payload on the caster. if (!hasTag(passiveTags, "rom-life-link")) return effects; return [...effects, ...effects.filter((effect) => effect.kind === "heal") .map((effect) => ({ ...effect, recipient: "caster" as const }))]; case 490148: // Enhanced Holy Strike adds a conservative hybrid weapon bonus. if (!hasTag(passiveTags, "rom-enhanced-holy-strike")) return effects; return effects.map((effect) => effect.kind === "damage" ? { ...effect, coefficient: effect.coefficient * 1.1 } : effect); case 491354: // Weaken Weapon Strength debuffs targets hit by Heavenly Arrow. if (!hasTag(passiveTags, "rom-weaken-weapon-strength")) return effects; return [...effects, { kind: "apply-aura", recipient: "ability-target", aura: { id: "rom-pair-heavenly-arrow-weaken", name: "Weaken Weapon Strength", disposition: "debuff", durationMs: 10_000, modifiers: [{ kind: "damage", direction: "dealt", school: "physical", operation: "percent", value: -0.1 }], dispelCategory: "magic", }, sourceEffectType: -1, sourceAuraType: -1, miscValue: -10, triggerSpellId: 490066, }]; case 491613: { // Evil Shackles halves duration on non-undead/demon targets. if (!hasTag(passiveTags, "rom-evil-shackles") || hasTag(targetTags, "rom-target-undead-demon")) return effects; return effects.map((effect) => effect.kind === "apply-aura" && effect.control?.kind === "root" ? { ...effect, aura: { ...effect.aura, durationMs: Math.max(1_000, Math.round((effect.aura.durationMs ?? 10_000) / 2)) } } : effect); } case 490273: // Source 499967 keeps Wave Armor's shield and adds heal component 623009. if (!hasTag(passiveTags, "rom-splendor-ripple")) return effects; return [...effects, { kind: "heal", recipient: "ability-target", coefficient: 0, basePoints: 106.25, pointsPerLevel: 21.25, sourceLevel: 1, }]; case 499966: // Devotion Halo refreshes Light Guidance while the halo is active. if (!hasTag(passiveTags, "rom-light-guidance")) return effects; return [...effects, { kind: "apply-aura", recipient: "caster", aura: { id: "rom-pair-light-guidance-healing", name: "Light Guidance", disposition: "buff", durationMs: 900_000, modifiers: [{ kind: "healing", direction: "done", operation: "percent", value: 0.05 }], dispelCategory: "magic", }, sourceEffectType: -1, sourceAuraType: 149, miscValue: 5, triggerSpellId: 623015, }]; case 490212: // Lightning Rod adds source child 502907 only while Bone Chill is on the target. if (!hasTag(passiveTags, "rom-priest-mage-lightning-rod") || !hasTag(targetTags, "rom-491165-501548-0")) return effects; return [...effects, { kind: "apply-aura", recipient: "ability-target", aura: { id: "rom-priest-mage-lightning-rod-shock", name: "Electric Shock", disposition: "debuff", durationMs: 20_000, modifiers: [ { kind: "stat", stat: "resistance:frost", operation: "flat", value: -50 }, { kind: "stat", stat: "resistance:nature", operation: "flat", value: -50 }, ], utilityTags: ["rom-priest-mage-lightning-rod-shock"], }, sourceEffectType: -1, sourceAuraType: -1, miscValue: 0, triggerSpellId: 502907, }]; case 490434: // Source 499924 adds component 623167 to the base Blood Arrow. if (!hasTag(passiveTags, "rom-blood-punishment")) return effects; return [...effects, { kind: "apply-aura", recipient: "caster", aura: { id: "rom-pair-blood-arrow-healing-received", name: "Blood Punishment", disposition: "buff", durationMs: 30_000, modifiers: [{ kind: "healing", direction: "taken", operation: "percent", value: 0.5 }], utilityTags: ["rom-blood-arrow-upgrade"], dispelCategory: "magic", }, sourceEffectType: -1, sourceAuraType: 144, miscValue: 50, triggerSpellId: 623167, }]; default: return effects; } } /** Select the pair-specific payload branches that depend on target state. */ export function romPriestKnightConditionalCastEffects( ability: AbilityDefinition, effects: readonly AbilityEffect[], sealStacks: number, targetIsCaster: boolean, ): readonly AbilityEffect[] { if (ability.source !== "runewaker" || ability.classId !== "rom-knight" || ability.secondaryClassId !== "rom-priest") return effects; if (ability.dbcSpellId === 490194) { const baseHit = effects.find((effect) => effect.kind === "damage"); if (!baseHit) return effects; // The generated row carries a placeholder heal with no source-backed // amount; only the Holy Seal components grant healing. const nonDamage = effects.filter((effect) => effect.kind !== "damage" && effect.kind !== "heal"); const seals = Math.min(4, Math.max(0, Math.trunc(sealStacks))); if (seals === 0) return [baseHit, ...nonDamage]; const healPercent = Math.min(8, seals * 2) / 100; return [ baseHit, ...nonDamage, { kind: "heal", coefficient: 0, percentMaxHealth: healPercent, recipient: "caster" }, { kind: "heal", coefficient: 0, percentMaxHealth: healPercent, scope: "party", center: "caster", maximumTargets: 1, excludeCaster: true, }, { kind: "combo", amount: -seals }, ]; } if (ability.dbcSpellId === 491640) { const selectedThreatPolarity = targetIsCaster ? 1 : -1; const hasThreatPayload = effects.some((effect) => effect.kind === "apply-aura" && effect.aura.modifiers?.some((modifier) => modifier.kind === "stat" && modifier.stat === "threat")); if (!hasThreatPayload) return effects; return effects.map((effect) => effect.kind === "apply-aura" && effect.aura.modifiers?.some((modifier) => modifier.kind === "stat" && modifier.stat === "threat") ? { ...effect, aura: { ...effect.aura, modifiers: effect.aura.modifiers?.map((modifier) => modifier.kind === "stat" && modifier.stat === "threat" ? { ...modifier, value: Math.abs(modifier.value) * selectedThreatPolarity } : modifier), }, } : effect); } return effects; } /** Source skill 499966 is granted by the level-70 Priest/Knight elite pair. */ export function romPriestKnightAdditionalAbilities( catalog: readonly AbilityDefinition[], ): readonly AbilityDefinition[] { const lightGuidance = catalog.find((ability) => ability.dbcSpellId === 499957 && ability.classId === "rom-priest" && ability.secondaryClassId === "rom-knight"); if (!lightGuidance) return []; const effects: readonly AbilityEffect[] = [{ kind: "apply-aura", recipient: "caster", scope: "party", aura: { id: "rom-pair-devotion-halo", name: "Devotion Halo", disposition: "buff", durationMs: 900_000, modifiers: [{ kind: "stat", stat: "wisdom", operation: "percent", value: 0.05 }], dispelCategory: "magic", }, sourceEffectType: -1, sourceAuraType: 164, miscValue: 5, triggerSpellId: 623016, }, { kind: "apply-aura", recipient: "caster", scope: "party", excludeCaster: true, aura: { id: "rom-pair-devotion-halo-mana", name: "Devotion Halo Mana", disposition: "buff", durationMs: 900_000, modifiers: [{ kind: "resource", resource: "mana", property: "cost", operation: "percent", value: -0.06 }], dispelCategory: "magic", }, sourceEffectType: -1, sourceAuraType: 164, miscValue: -6, triggerSpellId: 623016, }]; return [{ id: "rom-priest-rom-knight-elite-70-devotion-halo", classId: "rom-priest", secondaryClassId: "rom-knight", dbcSpellId: 499966, name: "Devotion Halo", unlockLevel: 70, icon: lightGuidance.icon, sigil: "DH", description: "Raises party Wisdom by 5% and lowers party mana costs by 6% for 15 minutes.", target: "self", range: { min: 0, max: 5 }, radius: 20, castMode: "instant", castTimeMs: 0, cooldownMs: 0, gcdMs: 1_500, cost: { resource: "mana", amount: 0 }, costs: [], effects, passive: false, source: "runewaker", sourceUnlockLevel: 70, romSkillGroup: "elite", }]; }