Update Healer Man server
This commit is contained in:
1 parent
4c768fbec6
commit
b16821e92e
330 files changed
+35406
-1760
No files matched your search
+232
-78
@@ -5,9 +5,13 @@ import type { RomGeneratedAbilityLike } from "./romAbilitySemantics";
|
||||
export const ROM_KNIGHT_DIVINE_PUNISHMENT_TAG = "rom-divine-punishment";
|
||||
export const ROM_KNIGHT_GODS_VENGEANCE_TAG = "rom-gods-vengeance";
|
||||
export const ROM_KNIGHT_SHIELD_OF_VALOR_TAG = "rom-shield-of-valor";
|
||||
export const ROM_KNIGHT_HOLY_LIGHTS_FURY_TAG = "rom-holy-lights-fury";
|
||||
export const ROM_KNIGHT_COMPASSION_OF_HOLY_LIGHT_TAG = "rom-compassion-of-holy-light";
|
||||
export const ROM_KNIGHT_HOLY_ILLUMINATION_TAG = "rom-holy-illumination";
|
||||
export const ROM_KNIGHT_MASTER_OF_PARRY_TAG = "rom-knight-master-of-parry";
|
||||
export const ROM_KNIGHT_MASTER_OF_PARRY_BLOCK_PROC = "rom-knight-master-of-parry-block";
|
||||
export const ROM_KNIGHT_DEADLY_WHIRLWIND_TAG = "rom-deadly-whirlwind";
|
||||
export const ROM_KNIGHT_CURRENT_COA_WHIRLWIND_SHIELD_ID = 1_493_719;
|
||||
export const ROM_KNIGHT_LOCAL_WHIRLWIND_SHIELD_ID = 490_175;
|
||||
|
||||
/** Shield of Valor reduces Holy Seal's remaining cooldown only on an active-aura parry. */
|
||||
export function romKnightShieldOfValorCooldownReductionMs(auraActive: boolean, parried: boolean): number {
|
||||
@@ -39,22 +43,97 @@ export function romKnightWarriorWhirlwindAdjustment(
|
||||
return { rageCostReduction: 10, cooldownReductionMs: 20_000 };
|
||||
}
|
||||
|
||||
/** RuneWaker Knight/Rogue passive 491621 boosts only Holy Strike's critical rate. */
|
||||
/** Current CoA changed God's Sword to passive, but the current passive payload is unpublished. */
|
||||
export function romKnightRogueHolyStrikeCriticalRateIncreasePercent(
|
||||
characterClassId: string,
|
||||
secondaryClassId: string | null | undefined,
|
||||
ability: Pick<AbilityDefinition, "source" | "classId" | "dbcSpellId">,
|
||||
learnedPassiveSpellIds: readonly number[],
|
||||
): number {
|
||||
if (characterClassId !== "rom-knight"
|
||||
|| secondaryClassId !== "rom-rogue"
|
||||
|| ability.source !== "runewaker"
|
||||
|| ability.classId !== "rom-knight"
|
||||
|| ability.dbcSpellId !== 490148
|
||||
|| !learnedPassiveSpellIds.includes(491621)) return 0;
|
||||
// The RuneWaker snapshot's 40% Holy Strike bonus conflicts with current CoA,
|
||||
// which only says God's Sword became passive and does not publish its payload.
|
||||
void characterClassId;
|
||||
void secondaryClassId;
|
||||
void ability;
|
||||
void learnedPassiveSpellIds;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The populated local tooltip specifies a 40% Holy Strike critical-rate increase.
|
||||
return 40;
|
||||
function hasPassiveTag(
|
||||
passiveTags: readonly string[] | ReadonlySet<string>,
|
||||
tag: string,
|
||||
): boolean {
|
||||
return Array.isArray(passiveTags)
|
||||
? passiveTags.includes(tag)
|
||||
: (passiveTags as ReadonlySet<string>).has(tag);
|
||||
}
|
||||
|
||||
/** CoA 6-1: Deadly Wound reduces healing received by 30%; player targets last 5s. */
|
||||
export function romKnightDeadlyWhirlwindAura(targetIsPlayer: boolean): Extract<AbilityEffect, { kind: "apply-aura" }> {
|
||||
return {
|
||||
kind: "apply-aura",
|
||||
recipient: "ability-target",
|
||||
aura: {
|
||||
id: "rom-knight-deadly-wound",
|
||||
name: "Deadly Wound",
|
||||
disposition: "debuff",
|
||||
durationMs: targetIsPlayer ? 5_000 : 10_000,
|
||||
dispelCategory: "magic",
|
||||
modifiers: [{ kind: "healing", direction: "taken", operation: "percent", value: -0.3 }],
|
||||
utilityTags: [ROM_KNIGHT_DEADLY_WHIRLWIND_TAG],
|
||||
},
|
||||
sourceEffectType: -1,
|
||||
sourceAuraType: -1,
|
||||
miscValue: 0,
|
||||
triggerSpellId: 491_639,
|
||||
};
|
||||
}
|
||||
|
||||
/** CoA 10.6.1: Divine Punishment makes the target take 2% more damage per Seal for 10s. */
|
||||
export function romKnightDivinePunishmentAuraForSealStacks(
|
||||
sealStacks: number,
|
||||
): Extract<AbilityEffect, { kind: "apply-aura" }> | null {
|
||||
const stacks = Math.max(0, Math.min(4, Math.trunc(sealStacks)));
|
||||
if (stacks <= 0) return null;
|
||||
return {
|
||||
kind: "apply-aura",
|
||||
recipient: "ability-target",
|
||||
aura: {
|
||||
id: "rom-knight-divine-punishment-damage-taken",
|
||||
name: "Divine Punishment",
|
||||
disposition: "debuff",
|
||||
durationMs: 10_000,
|
||||
dispelCategory: "magic",
|
||||
modifiers: [{ kind: "damage", direction: "taken", operation: "percent", value: stacks * 0.02 }],
|
||||
},
|
||||
sourceEffectType: -1,
|
||||
sourceAuraType: -1,
|
||||
miscValue: stacks * 2,
|
||||
triggerSpellId: 491_608,
|
||||
};
|
||||
}
|
||||
|
||||
/** Current CoA 11.1.5 cooldown rider, bridged by the exact Whirlwind Shield name. */
|
||||
export function romKnightCompassionWhirlwindShieldCooldownReductionMs(
|
||||
characterClassId: string,
|
||||
secondaryClassId: string | null | undefined,
|
||||
ability: Pick<AbilityDefinition, "source" | "classId" | "dbcSpellId">,
|
||||
learnedPassiveSpellIds: readonly number[],
|
||||
): number {
|
||||
return characterClassId === "rom-knight"
|
||||
&& secondaryClassId === "rom-priest"
|
||||
&& ability.source === "runewaker"
|
||||
&& ability.classId === "rom-knight"
|
||||
&& (ability.dbcSpellId === ROM_KNIGHT_LOCAL_WHIRLWIND_SHIELD_ID
|
||||
|| ability.dbcSpellId === ROM_KNIGHT_CURRENT_COA_WHIRLWIND_SHIELD_ID)
|
||||
&& learnedPassiveSpellIds.includes(491_635)
|
||||
? 1_000
|
||||
: 0;
|
||||
}
|
||||
|
||||
/** CoA 11.1.5 Compassion heal formula; called only after a Whirlwind Shield hit lands. */
|
||||
export function romKnightCompassionWhirlwindShieldHealAmount(stamina: number): number {
|
||||
return Math.max(0, Math.round(120 + Math.max(0, stamina) * 0.2));
|
||||
}
|
||||
|
||||
const ROM_KNIGHT_MASTER_OF_PARRY_PROC: AuraProcDefinition = {
|
||||
@@ -83,11 +162,83 @@ function divinePunishmentPassive(row: RomGeneratedAbilityLike): readonly Ability
|
||||
}];
|
||||
}
|
||||
|
||||
function deadWhirlwindPassive(row: RomGeneratedAbilityLike): readonly AbilityEffect[] {
|
||||
return [{
|
||||
kind: "apply-aura",
|
||||
recipient: "caster",
|
||||
aura: {
|
||||
id: "rom-knight-passive-491639",
|
||||
name: row.name,
|
||||
disposition: "buff",
|
||||
durationMs: null,
|
||||
utilityTags: [ROM_KNIGHT_DEADLY_WHIRLWIND_TAG],
|
||||
},
|
||||
sourceEffectType: -1,
|
||||
sourceAuraType: -1,
|
||||
miscValue: 0,
|
||||
triggerSpellId: 491_639,
|
||||
}];
|
||||
}
|
||||
|
||||
function compassionOfHolyLightPassive(row: RomGeneratedAbilityLike): readonly AbilityEffect[] {
|
||||
return [{
|
||||
kind: "apply-aura",
|
||||
recipient: "caster",
|
||||
aura: {
|
||||
id: "rom-knight-passive-491635",
|
||||
name: "Compassion of Holy Light",
|
||||
disposition: "buff",
|
||||
durationMs: null,
|
||||
utilityTags: [ROM_KNIGHT_COMPASSION_OF_HOLY_LIGHT_TAG],
|
||||
},
|
||||
sourceEffectType: -1,
|
||||
sourceAuraType: -1,
|
||||
miscValue: 0,
|
||||
triggerSpellId: 491_635,
|
||||
}];
|
||||
}
|
||||
|
||||
function holyIlluminationPassive(row: RomGeneratedAbilityLike): readonly AbilityEffect[] {
|
||||
return [{
|
||||
kind: "apply-aura",
|
||||
recipient: "caster",
|
||||
aura: {
|
||||
id: "rom-knight-passive-491632",
|
||||
name: row.name,
|
||||
disposition: "buff",
|
||||
durationMs: null,
|
||||
utilityTags: [ROM_KNIGHT_HOLY_ILLUMINATION_TAG],
|
||||
},
|
||||
sourceEffectType: -1,
|
||||
sourceAuraType: -1,
|
||||
miscValue: 0,
|
||||
triggerSpellId: 491_632,
|
||||
}];
|
||||
}
|
||||
|
||||
/** Source-backed Knight passive and cast adjustments. */
|
||||
export function overrideRomKnightEffects(
|
||||
row: RomGeneratedAbilityLike,
|
||||
translated: readonly AbilityEffect[],
|
||||
): readonly AbilityEffect[] | null {
|
||||
if (row.classId === "rom-knight"
|
||||
&& row.secondaryClassId === "rom-warrior"
|
||||
&& row.sourceSkillId === 490088) {
|
||||
// Fearless's local description promises Fear immunity, while its source
|
||||
// component only exposes the +3% Strength and Stamina modifiers. The
|
||||
// [SC_SKILLTIPS_FEAR] suffix is a tooltip category marker; discard the
|
||||
// generic parser's false-positive Fear control and attach immunity to its
|
||||
// actual stat buff rows.
|
||||
return translated
|
||||
.filter((effect) => !(effect.kind === "apply-aura" && effect.control?.kind === "fear"))
|
||||
.map((effect) => effect.kind === "apply-aura" && (effect.aura.modifiers?.length ?? 0) > 0 ? {
|
||||
...effect,
|
||||
aura: {
|
||||
...effect.aura,
|
||||
controlImmunities: [...new Set([...(effect.aura.controlImmunities ?? []), "fear" as const])],
|
||||
},
|
||||
} : effect);
|
||||
}
|
||||
if (row.sourceSkillId === 492935) {
|
||||
const durationMs = Math.max(0, ...(row.source?.components ?? []).map((component) => (
|
||||
Math.max(0, Number(component.effectTime ?? 0)) * 1_000
|
||||
@@ -113,6 +264,7 @@ export function overrideRomKnightEffects(
|
||||
return effect;
|
||||
});
|
||||
}
|
||||
if (row.sourceSkillId === 491639 && row.passive) return deadWhirlwindPassive(row);
|
||||
if (row.sourceSkillId === 491608 && row.passive) return divinePunishmentPassive(row);
|
||||
if (row.sourceSkillId === 499934 && row.passive) return [{
|
||||
kind: "apply-aura",
|
||||
@@ -129,21 +281,8 @@ export function overrideRomKnightEffects(
|
||||
miscValue: 0,
|
||||
triggerSpellId: 499934,
|
||||
}];
|
||||
if (row.sourceSkillId === 491635 && row.passive) return [{
|
||||
kind: "apply-aura",
|
||||
recipient: "caster",
|
||||
aura: {
|
||||
id: "rom-knight-passive-491635",
|
||||
name: row.name,
|
||||
disposition: "buff",
|
||||
durationMs: null,
|
||||
utilityTags: [ROM_KNIGHT_HOLY_LIGHTS_FURY_TAG],
|
||||
},
|
||||
sourceEffectType: -1,
|
||||
sourceAuraType: -1,
|
||||
miscValue: 0,
|
||||
triggerSpellId: 491635,
|
||||
}];
|
||||
if (row.sourceSkillId === 491635 && row.passive) return compassionOfHolyLightPassive(row);
|
||||
if (row.sourceSkillId === 491632 && row.passive) return holyIlluminationPassive(row);
|
||||
if (row.sourceSkillId === 490468) return translated.map((effect) => effect.kind === "apply-aura" ? {
|
||||
...effect,
|
||||
aura: {
|
||||
@@ -159,61 +298,76 @@ export function romKnightCastEffects(
|
||||
ability: AbilityDefinition,
|
||||
effects: readonly AbilityEffect[],
|
||||
passiveTags: readonly string[] | ReadonlySet<string> = [],
|
||||
sealStacks = 0,
|
||||
): readonly AbilityEffect[] {
|
||||
const hasHolyLightsFury = Array.isArray(passiveTags)
|
||||
? passiveTags.includes(ROM_KNIGHT_HOLY_LIGHTS_FURY_TAG)
|
||||
: (passiveTags as ReadonlySet<string>).has(ROM_KNIGHT_HOLY_LIGHTS_FURY_TAG);
|
||||
if (ability.source === "runewaker"
|
||||
&& ability.classId === "rom-knight"
|
||||
&& ability.dbcSpellId === 490149
|
||||
&& hasHolyLightsFury) {
|
||||
// RuneWaker's local tooltip gives a flat 3-second reduction to Holy Seal.
|
||||
// The added damage rider has no numeric payload in this source snapshot.
|
||||
return [...effects, {
|
||||
kind: "cooldown-reset",
|
||||
mode: "reduce",
|
||||
abilityName: "Holy Seal",
|
||||
amountMs: 3_000,
|
||||
}];
|
||||
if (ability.source !== "runewaker"
|
||||
|| (ability.classId !== "rom-knight" && ability.dbcSpellId !== 490_133)) return effects;
|
||||
|
||||
let adjusted = effects;
|
||||
if (ability.dbcSpellId === 490_151 && hasPassiveTag(passiveTags, ROM_KNIGHT_DIVINE_PUNISHMENT_TAG)) {
|
||||
adjusted = adjusted.map((effect) => {
|
||||
if (effect.kind !== "damage" && effect.kind !== "dot") return effect;
|
||||
return {
|
||||
...effect,
|
||||
coefficient: effect.coefficient * 1.285,
|
||||
...(effect.coefficientPerLevel === undefined ? {} : { coefficientPerLevel: effect.coefficientPerLevel * 1.285 }),
|
||||
...(effect.basePoints === undefined ? {} : { basePoints: effect.basePoints * 1.285 }),
|
||||
...(effect.pointsPerLevel === undefined ? {} : { pointsPerLevel: effect.pointsPerLevel * 1.285 }),
|
||||
...(effect.bonusPowerCoefficient === undefined ? {} : { bonusPowerCoefficient: effect.bonusPowerCoefficient * 1.285 }),
|
||||
};
|
||||
});
|
||||
const punishmentAura = romKnightDivinePunishmentAuraForSealStacks(sealStacks);
|
||||
if (punishmentAura) adjusted = [...adjusted, { ...punishmentAura, requiresLandedHit: true }];
|
||||
}
|
||||
|
||||
if (ability.source !== "runewaker"
|
||||
|| ability.classId !== "rom-knight"
|
||||
|| ability.dbcSpellId !== 490151
|
||||
|| !(Array.isArray(passiveTags)
|
||||
? passiveTags.includes(ROM_KNIGHT_DIVINE_PUNISHMENT_TAG)
|
||||
: (passiveTags as ReadonlySet<string>).has(ROM_KNIGHT_DIVINE_PUNISHMENT_TAG))) return effects;
|
||||
if (ability.dbcSpellId === 490_133 && hasPassiveTag(passiveTags, ROM_KNIGHT_DEADLY_WHIRLWIND_TAG)) {
|
||||
adjusted = [...adjusted, { ...romKnightDeadlyWhirlwindAura(false), requiresLandedHit: true }];
|
||||
}
|
||||
|
||||
const increasedDamage = effects.map((effect) => {
|
||||
if (effect.kind !== "damage" && effect.kind !== "dot") return effect;
|
||||
return {
|
||||
...effect,
|
||||
coefficient: effect.coefficient * 1.01,
|
||||
...(effect.coefficientPerLevel === undefined ? {} : { coefficientPerLevel: effect.coefficientPerLevel * 1.01 }),
|
||||
...(effect.basePoints === undefined ? {} : { basePoints: effect.basePoints * 1.01 }),
|
||||
...(effect.pointsPerLevel === undefined ? {} : { pointsPerLevel: effect.pointsPerLevel * 1.01 }),
|
||||
...(effect.bonusPowerCoefficient === undefined ? {} : { bonusPowerCoefficient: effect.bonusPowerCoefficient * 1.01 }),
|
||||
};
|
||||
});
|
||||
|
||||
// Generated passive 491608 includes component 502052: a ten-second, +1%
|
||||
// Light-damage vulnerability. The generic modifier map treats type 225 as
|
||||
// Light resistance, which would make the target take less damage despite
|
||||
// the local tooltip explicitly saying it takes more Light damage.
|
||||
return [...increasedDamage, {
|
||||
kind: "apply-aura",
|
||||
recipient: "ability-target",
|
||||
aura: {
|
||||
id: "rom-knight-divine-punishment-light-vulnerability",
|
||||
name: "Divine Punishment",
|
||||
disposition: "debuff",
|
||||
durationMs: 10_000,
|
||||
modifiers: [{ kind: "damage", direction: "taken", school: "holy", operation: "percent", value: 0.01 }],
|
||||
dispelCategory: "magic",
|
||||
},
|
||||
sourceEffectType: -1,
|
||||
sourceAuraType: 225,
|
||||
miscValue: 1,
|
||||
triggerSpellId: 502052,
|
||||
}];
|
||||
if (ability.dbcSpellId === ROM_KNIGHT_LOCAL_WHIRLWIND_SHIELD_ID) {
|
||||
adjusted = adjusted.map((effect) => effect.kind === "damage"
|
||||
? {
|
||||
...effect,
|
||||
coefficient: 0,
|
||||
basePoints: 0,
|
||||
dieSides: 1,
|
||||
weaponDpsCoefficient: 7.014,
|
||||
school: "holy" as const,
|
||||
}
|
||||
: effect);
|
||||
if (hasPassiveTag(passiveTags, ROM_KNIGHT_COMPASSION_OF_HOLY_LIGHT_TAG)) {
|
||||
adjusted = [...adjusted, {
|
||||
kind: "heal",
|
||||
coefficient: 0,
|
||||
basePoints: 120,
|
||||
dieSides: 1,
|
||||
staminaCoefficient: 0.2,
|
||||
scope: "party",
|
||||
center: "caster",
|
||||
radius: 100,
|
||||
requiresLandedHit: true,
|
||||
}];
|
||||
}
|
||||
if (hasPassiveTag(passiveTags, ROM_KNIGHT_HOLY_ILLUMINATION_TAG)
|
||||
|| hasPassiveTag(passiveTags, ROM_KNIGHT_COMPASSION_OF_HOLY_LIGHT_TAG)) {
|
||||
adjusted = [...adjusted, {
|
||||
kind: "apply-aura",
|
||||
scope: "party",
|
||||
radius: 100,
|
||||
requiresLandedHit: true,
|
||||
aura: {
|
||||
id: "rom-knight-holy-illumination-proc",
|
||||
name: "Holy Illumination",
|
||||
disposition: "buff",
|
||||
durationMs: 10_000,
|
||||
modifiers: [{ kind: "damage", direction: "taken", operation: "percent", value: -0.15 }],
|
||||
},
|
||||
sourceEffectType: -1,
|
||||
sourceAuraType: -1,
|
||||
miscValue: 0,
|
||||
triggerSpellId: 491_632,
|
||||
}];
|
||||
}
|
||||
}
|
||||
return adjusted;
|
||||
}
|
||||
Reference in new issue
Block a user