Update Healer Man server
This commit is contained in:
1 parent
f90ba7d989
commit
189b94d6b7
51 files changed
+3448
-154
No files matched your search
@@ -0,0 +1,118 @@
|
||||
import type { AbilityDefinition, AbilityEffect } from "./abilityCatalog";
|
||||
|
||||
const ROGUE_BLEED_ABILITIES = new Set([
|
||||
"rom-rogue-primary-1", // Low Blow's conditional Grievous Wound
|
||||
"rom-rogue-primary-4", // Blind Spot
|
||||
"rom-rogue-primary-9", // Sneak Attack
|
||||
"rom-rogue-general-0", // Shadowstab
|
||||
]);
|
||||
|
||||
export interface RomCastMechanics {
|
||||
readonly sealStacks: number;
|
||||
readonly bleedCount: number;
|
||||
readonly charged: boolean;
|
||||
readonly mainHandDagger: boolean;
|
||||
readonly casterMaxHealth: number;
|
||||
}
|
||||
|
||||
/** Source periodic values for self-sacrifice toggle skills are percentages of HP. */
|
||||
export function romSelfDrainPercent(spellId: number): number {
|
||||
if (spellId === 490304) return 6; // Priest Holy Candle
|
||||
if (spellId === 490434) return 3; // Scout Blood Arrow
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function romSingleTargetControl(ability: AbilityDefinition | null): boolean {
|
||||
return ability?.source === "runewaker"
|
||||
&& [490347, 490454, 491172].includes(ability.dbcSpellId);
|
||||
}
|
||||
|
||||
export function romAbilityNeedsShield(ability: AbilityDefinition): boolean {
|
||||
return ability.source === "runewaker"
|
||||
&& [490175, 490186, 490192, 490468].includes(ability.dbcSpellId);
|
||||
}
|
||||
|
||||
export function romTargetBleedCount(
|
||||
effects: readonly { readonly kind: string; readonly sourceAbilityId: string; readonly sourceActorId: string; readonly targetId: string | null; readonly remainingTicks: number }[],
|
||||
sourceActorId: string,
|
||||
targetId: string | null,
|
||||
): number {
|
||||
if (!targetId) return 0;
|
||||
return new Set(effects.filter((effect) => effect.kind === "dot"
|
||||
&& effect.remainingTicks > 0
|
||||
&& effect.sourceActorId === sourceActorId
|
||||
&& effect.targetId === targetId
|
||||
&& ROGUE_BLEED_ABILITIES.has(effect.sourceAbilityId))
|
||||
.map((effect) => effect.sourceAbilityId)).size;
|
||||
}
|
||||
|
||||
function scaleOutput(effect: AbilityEffect, factor: number): AbilityEffect {
|
||||
if (effect.kind !== "damage" && effect.kind !== "dot") return effect;
|
||||
return {
|
||||
...effect,
|
||||
coefficient: effect.coefficient * factor,
|
||||
...(effect.coefficientPerLevel === undefined ? {} : { coefficientPerLevel: effect.coefficientPerLevel * factor }),
|
||||
...(effect.basePoints === undefined ? {} : { basePoints: effect.basePoints * factor }),
|
||||
...(effect.pointsPerLevel === undefined ? {} : { pointsPerLevel: effect.pointsPerLevel * factor }),
|
||||
...(effect.bonusPowerCoefficient === undefined ? {} : { bonusPowerCoefficient: effect.bonusPowerCoefficient * factor }),
|
||||
};
|
||||
}
|
||||
|
||||
/** Select source-conditional RoM payloads once for both player and companion casts. */
|
||||
export function romEffectsForCast(ability: AbilityDefinition, context: RomCastMechanics): readonly AbilityEffect[] {
|
||||
if (ability.source !== "runewaker") return ability.effects;
|
||||
const effects = ability.effects;
|
||||
const damage = effects.filter((effect) => effect.kind === "damage");
|
||||
const seals = Math.max(0, Math.min(4, context.sealStacks));
|
||||
switch (ability.dbcSpellId) {
|
||||
case 490148: // A new Holy Strike increases the target's Holy Seal bleed stack.
|
||||
case 490150: { // Charge adds two stacks.
|
||||
const gained = ability.dbcSpellId === 490148 ? 1 : 2;
|
||||
const nextStacks = Math.min(4, seals + gained);
|
||||
return effects.map((effect) => effect.kind === "dot" ? scaleOutput(effect, nextStacks) : effect);
|
||||
}
|
||||
case 490313: // Wound Attack: one hit plus one rider per self-applied bleed.
|
||||
return damage.slice(0, 1 + Math.min(2, context.bleedCount));
|
||||
case 490323: // Low Blow only creates Grievous Wound on a bleeding target.
|
||||
return context.bleedCount > 0 ? effects : effects.filter((effect) => effect.kind !== "dot");
|
||||
case 491170: // Electric Explosion has mutually exclusive normal/Charged hits.
|
||||
return context.charged ? damage.slice(0, 1) : damage.slice(-1);
|
||||
case 490151: // Punishment spends Holy Seals and scales its one payoff.
|
||||
return seals > 0 ? effects.map((effect) => scaleOutput(effect, seals / 4)) : [];
|
||||
case 490153: { // Threaten: target debuffs and extra hit require Holy Seals.
|
||||
const base = damage[0];
|
||||
const threat = effects.find((effect) => effect.kind === "apply-aura" && effect.recipient === "caster");
|
||||
const debuffs = seals > 0 ? effects.filter((effect) => effect.kind === "apply-aura" && effect.recipient !== "caster") : [];
|
||||
const bonus = seals > 0 && damage.length > 1 ? scaleOutput(damage[damage.length - 1]!, seals / 4) : null;
|
||||
return [...(base ? [base] : []), ...(threat ? [threat] : []), ...debuffs, ...(bonus ? [bonus] : [])];
|
||||
}
|
||||
case 490154: { // Shock: one hit, one stun, plus a seal-dependent rider.
|
||||
const stun = effects.find((effect) => effect.kind === "apply-aura" && effect.control?.kind === "stun");
|
||||
const scaledStun = stun && stun.kind === "apply-aura"
|
||||
? { ...stun, aura: { ...stun.aura, durationMs: 1_000 * (1 + seals) } }
|
||||
: null;
|
||||
const bonus = seals > 0 && damage.length > 1 ? scaleOutput(damage[damage.length - 1]!, seals / 4) : null;
|
||||
return [...(damage[0] ? [damage[0]] : []), ...(scaledStun ? [scaledStun] : []), ...(bonus ? [bonus] : [])];
|
||||
}
|
||||
case 490186: { // Shield Bash shields its caster only when consuming seals.
|
||||
const absorb = effects.find((effect) => effect.kind === "apply-aura" && effect.aura.absorbs?.length);
|
||||
const shield = seals > 0 && absorb?.kind === "apply-aura" ? {
|
||||
...absorb,
|
||||
recipient: "caster" as const,
|
||||
aura: {
|
||||
...absorb.aura,
|
||||
absorbs: absorb.aura.absorbs?.map((pool) => ({
|
||||
...pool, amount: Math.max(1, Math.round(context.casterMaxHealth * 0.05 * seals)),
|
||||
})),
|
||||
},
|
||||
} : null;
|
||||
return [...(damage[0] ? [damage[0]] : []), ...(shield ? [shield, { kind: "combo" as const, amount: -5 }] : [])];
|
||||
}
|
||||
case 490306: // Shadowstab's bleed needs a main-hand dagger.
|
||||
return context.mainHandDagger ? effects : effects.filter((effect) => effect.kind !== "dot");
|
||||
case 490337: // Sneak Attack always stuns, but its bleed needs a dagger.
|
||||
return context.mainHandDagger ? effects : effects.filter((effect) => effect.kind !== "dot");
|
||||
default:
|
||||
return effects;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user