Update Healer Man server

This commit is contained in:
phenom committed 2026-09-30 09:57:13 -04:00
1 parent 35dfdaed61
commit 4fb7a0b9c8
161 files changed
+24618 -529

No files matched your search

+193
View File
@@ -1,5 +1,6 @@
import type { AbilityDefinition, AbilityEffect } from "./abilityCatalog";
import type { AuraDefinition } from "./combatAuras";
import type { RomGeneratedAbilityLike } from "./romAbilitySemantics";
export const ROM_CHAMPION_FORGE_STACK_AURA: AuraDefinition = {
id: "rom-champion-forge-stack",
@@ -20,6 +21,28 @@ export const ROM_CHAMPION_FORGE_STACK_AURA: AuraDefinition = {
export const ROM_CHAMPION_RUNE_OVERLOAD_TAG = "rom-champion-rune-overload";
export const ROM_CHAMPION_RUNE_OVERLOAD_RAGE_PER_SECOND = 15;
export const ROM_CHAMPION_SOUL_FORGE_PROC_ACTION_ID = "rom-champion-soul-forge-critical";
export const ROM_CHAMPION_SOUL_FORGE_PROFILE = Object.freeze({
sourceSpellId: 498652,
criticalAuraComponentId: 621339,
criticalAuraDurationMs: 300_000,
darkPowerComponentId: 621341,
trigger: "crit" as const,
chance: 1,
darkMagicAuraDurationMs: 10_000,
darkMagicBonusPercent: 0.1,
darkMagicSchool: "shadow" as const,
});
export const ROM_CHAMPION_WAY_OF_FRENZY_PROC_ACTION_ID = "rom-champion-way-of-frenzy-hit";
export const ROM_CHAMPION_WAY_OF_FRENZY_PROFILE = Object.freeze({
sourceSpellId: 498720,
sourceComponentId: 621458,
triggerSpellId: 498715,
triggerAbilityId: "rom-champion-rom-priest-elite-20",
durationMs: 60_000,
maxStacks: 5,
staminaPercentPerStack: 0.02,
});
const ROM_CHAMPION_FORGE_ACTIVE_AURA: AuraDefinition = {
id: "rom-champion-forge-active",
@@ -37,6 +60,56 @@ const ROM_CHAMPION_FORGE_ACTIVE_AURA: AuraDefinition = {
}],
};
/** Replace the source component's unconditional stat aura with its described hit trigger. */
export function overrideRomChampionEffects(
row: RomGeneratedAbilityLike,
_translated: readonly AbilityEffect[],
): readonly AbilityEffect[] | null {
if (row.classId !== "rom-champion" || row.secondaryClassId !== "rom-priest"
|| row.sourceSkillId !== ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.sourceSpellId) return null;
const component = row.source?.components?.find((entry) => (
entry.id === ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.sourceComponentId
));
const staminaModifier = component?.abilityModifiers?.find((modifier) => modifier.type === 162);
const durationMs = Math.max(0, Math.round((component?.effectTime ?? 0) * 1_000));
if (!component || !staminaModifier || staminaModifier.value <= 0 || durationMs <= 0) return [];
return [{
kind: "apply-aura",
aura: {
id: "rom-champion-way-of-frenzy-passive",
name: row.name,
disposition: "buff",
durationMs: null,
hideFromAuraStrip: true,
procs: [{
id: "rom-champion-way-of-frenzy-divine-vengeance",
triggers: ["hit", "crit"],
abilityIds: [ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.triggerAbilityId],
chance: 1,
action: {
kind: "custom",
id: ROM_CHAMPION_WAY_OF_FRENZY_PROC_ACTION_ID,
data: {
sourceSpellId: ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.sourceSpellId,
sourceComponentId: ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.sourceComponentId,
triggerSpellId: ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.triggerSpellId,
durationMs,
maxStacks: 5,
staminaPercentPerStack: staminaModifier.value / 100,
},
},
}],
},
recipient: "caster",
sourceEffectType: -1,
sourceAuraType: -1,
miscValue: 0,
triggerSpellId: ROM_CHAMPION_WAY_OF_FRENZY_PROFILE.sourceSpellId,
}];
}
function scaleDamage(effect: Extract<AbilityEffect, { kind: "damage" }>, factor: number): AbilityEffect {
return {
...effect,
@@ -48,6 +121,83 @@ function scaleDamage(effect: Extract<AbilityEffect, { kind: "damage" }>, factor:
};
}
function casterHealFromDamage(effect: Extract<AbilityEffect, { kind: "damage" }>): AbilityEffect {
return {
kind: "heal",
coefficient: effect.coefficient,
...(effect.basePoints === undefined ? {} : { basePoints: effect.basePoints }),
...(effect.dieSides === undefined ? {} : { dieSides: effect.dieSides }),
...(effect.pointsPerLevel === undefined ? {} : { pointsPerLevel: effect.pointsPerLevel }),
...(effect.coefficientPerLevel === undefined ? {} : { coefficientPerLevel: effect.coefficientPerLevel }),
...(effect.bonusPowerCoefficient === undefined ? {} : { bonusPowerCoefficient: effect.bonusPowerCoefficient }),
...(effect.attackPowerCoefficient === undefined ? {} : { attackPowerCoefficient: effect.attackPowerCoefficient }),
...(effect.spiritCoefficient === undefined ? {} : { spiritCoefficient: effect.spiritCoefficient }),
...(effect.sourceLevel === undefined ? {} : { sourceLevel: effect.sourceLevel }),
...(effect.maxScalingLevel === undefined ? {} : { maxScalingLevel: effect.maxScalingLevel }),
recipient: "caster",
};
}
function soulForgeMysteryEffects(effects: readonly AbilityEffect[]): readonly AbilityEffect[] {
const critAuraIndex = effects.findIndex((effect) => effect.kind === "apply-aura"
&& effect.aura.id.includes(`-${ROM_CHAMPION_SOUL_FORGE_PROFILE.criticalAuraComponentId}-`));
const darkAuraIndex = effects.findIndex((effect) => effect.kind === "apply-aura"
&& effect.aura.id.includes(`-${ROM_CHAMPION_SOUL_FORGE_PROFILE.darkPowerComponentId}-`));
const critAuraEffect = effects[critAuraIndex];
const darkAuraEffect = effects[darkAuraIndex];
if (critAuraIndex < 0 || darkAuraIndex < 0
|| critAuraEffect?.kind !== "apply-aura" || darkAuraEffect?.kind !== "apply-aura") return effects;
const darkModifier = darkAuraEffect.aura.modifiers?.find((modifier) => modifier.kind === "damage"
&& modifier.direction === "dealt" && modifier.school === ROM_CHAMPION_SOUL_FORGE_PROFILE.darkMagicSchool
&& modifier.operation === "percent");
if (darkModifier?.kind !== "damage") return effects;
const output: AbilityEffect[] = [];
effects.forEach((effect, index) => {
if (index === darkAuraIndex) return;
if (index === critAuraIndex && effect.kind === "apply-aura") {
output.push({
...effect,
aura: {
...effect.aura,
procs: [
...(effect.aura.procs ?? []),
{
id: "rom-champion-soul-forge-dark-magic-on-crit",
triggers: ROM_CHAMPION_SOUL_FORGE_PROFILE.trigger,
chance: ROM_CHAMPION_SOUL_FORGE_PROFILE.chance,
action: {
kind: "custom",
id: ROM_CHAMPION_SOUL_FORGE_PROC_ACTION_ID,
data: {
sourceSpellId: ROM_CHAMPION_SOUL_FORGE_PROFILE.sourceSpellId,
sourceComponentId: ROM_CHAMPION_SOUL_FORGE_PROFILE.darkPowerComponentId,
auraId: "rom-champion-soul-forge-dark-magic",
auraName: darkAuraEffect.aura.name,
durationMs: darkAuraEffect.aura.durationMs ?? ROM_CHAMPION_SOUL_FORGE_PROFILE.darkMagicAuraDurationMs,
disposition: "buff",
maxStacks: 1,
stackBehavior: "refresh",
target: "aura-source",
modifierKind: "damage",
modifierDirection: "dealt",
modifierSchool: ROM_CHAMPION_SOUL_FORGE_PROFILE.darkMagicSchool,
modifierOperation: darkModifier.operation,
modifierValue: darkModifier.value,
},
},
},
],
},
});
return;
}
output.push(effect);
});
return output;
}
/** Apply source-backed Champion cast branches that need runtime state. */
export function romChampionCastEffects(
ability: AbilityDefinition,
@@ -58,6 +208,49 @@ export function romChampionCastEffects(
if (ability.source !== "runewaker" || ability.classId !== "rom-champion") return effects;
switch (ability.dbcSpellId) {
case 498652: // Soul Forge Mystery adds its 10-second dark magic buff on a critical attack.
if (ability.secondaryClassId !== "rom-warlock") return effects;
return soulForgeMysteryEffects(effects);
case 498595: { // Rune Siphon's positive component 621264 restores the caster's HP.
if (ability.secondaryClassId !== "rom-warlock") return effects;
let healIndex = -1;
for (let index = effects.length - 1; index >= 0; index -= 1) {
if (effects[index]?.kind === "damage") {
healIndex = index;
break;
}
}
if (healIndex < 0) return effects;
return effects.flatMap((effect, index): AbilityEffect[] => {
if (index === healIndex && effect.kind === "damage") return [casterHealFromDamage(effect)];
if (effect.kind === "heal" && effect.coefficient === 0 && effect.basePoints === undefined
&& effect.pointsPerLevel === undefined && effect.percentMaxHealth === undefined) return [];
return [effect];
});
}
case 498529: { // Rune Pulse restores Rage on a landed hit (component 622327).
const restoreAura = effects.find((effect) => effect.kind === "apply-aura"
&& effect.aura.id.includes("-622327-")
&& effect.aura.modifiers?.some((modifier) => modifier.kind === "resource"
&& modifier.resource === "rage" && modifier.property === "regeneration"));
if (restoreAura?.kind !== "apply-aura") return effects;
const rageModifier = restoreAura.aura.modifiers?.find((modifier) => modifier.kind === "resource"
&& modifier.resource === "rage" && modifier.property === "regeneration");
const ragePerHit = rageModifier
? Math.max(0, Math.round(rageModifier.value * (restoreAura.aura.durationMs ?? 0) / 1_000))
: 0;
const damageIndex = effects.findIndex((effect) => effect.kind === "damage");
if (ragePerHit <= 0 || damageIndex < 0) return effects;
const output: AbilityEffect[] = [];
effects.forEach((effect, index) => {
if (index === damageIndex && effect.kind === "damage") {
output.push({ ...effect, onDamageResource: { resource: "rage", amount: ragePerHit } });
} else if (effect !== restoreAura) {
output.push(effect);
}
});
return output;
}
case 498584: // Champion/Warrior Determination Rune is a 30%-of-max-HP caster shield.
if (ability.secondaryClassId !== "rom-warrior") return effects;
return effects.map((effect) => effect.kind === "apply-aura" && effect.aura.absorbs?.length