Update Healer Man server
This commit is contained in:
1 parent
35dfdaed61
commit
4fb7a0b9c8
161 files changed
+24618
-529
No files matched your search
@@ -5,6 +5,15 @@ import {
|
||||
ROM_ROGUE_DAGGER_MASTERY_TAG,
|
||||
ROM_ROGUE_TREASURE_HUNTER_TAG,
|
||||
} from "./romRogueMechanics";
|
||||
import {
|
||||
ROM_ROGUE_CHAMPION_NERVE_GEAR_MARKER_TAG,
|
||||
ROM_ROGUE_CHAMPION_NERVE_GEAR_PASSIVE_SPELL_ID,
|
||||
} from "./romRogueChampionNerveGearRuntime";
|
||||
|
||||
export const ROM_ROGUE_MAGE_ENCHANTED_THROW_TAG = "rom-491178-enchanted-throw";
|
||||
export const ROM_ROGUE_PRIEST_SINISTER_METHODS_TAG = "rom-rogue-priest-sinister-methods";
|
||||
export const ROM_ROGUE_PRIEST_SINISTER_METHODS_COOLDOWN_KEY = "rom-rogue-priest-sinister-methods";
|
||||
export const ROM_SCOUT_PRIEST_SNIPER_TAG = "rom-scout-priest-sniper";
|
||||
|
||||
function firstDamage(effects: readonly AbilityEffect[]): AbilityEffect[] {
|
||||
return effects.filter((effect) => effect.kind === "damage").slice(0, 1);
|
||||
@@ -56,14 +65,18 @@ function customProc(
|
||||
sourceId: number,
|
||||
trigger: "hit" | "crit",
|
||||
actionId: string,
|
||||
options: { readonly chance?: number; readonly internalCooldownMs?: number } = {},
|
||||
options: {
|
||||
readonly chance?: number;
|
||||
readonly internalCooldownMs?: number;
|
||||
readonly data?: Readonly<Record<string, string | number | boolean>>;
|
||||
} = {},
|
||||
): AuraProcDefinition {
|
||||
return {
|
||||
id: `rom-pair-proc-${sourceId}`,
|
||||
triggers: trigger === "hit" ? ["hit", "crit"] : trigger,
|
||||
chance: options.chance ?? 1,
|
||||
...(options.internalCooldownMs ? { internalCooldownMs: options.internalCooldownMs } : {}),
|
||||
action: { kind: "custom", id: actionId },
|
||||
action: { kind: "custom", id: actionId, ...(options.data ? { data: options.data } : {}) },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -91,8 +104,8 @@ function timedCasterAura(
|
||||
}
|
||||
|
||||
/**
|
||||
* Source-specific translations for the accepted Scout/Rogue and Rogue/Warrior
|
||||
* pairings. Passive tags and custom proc IDs are consumed by combat-store
|
||||
* Source-specific translations for the accepted Scout paired-class
|
||||
* mechanics. Passive tags and custom proc IDs are consumed by combat-store
|
||||
* transitions; they intentionally do not apply unrelated generic stat buffs.
|
||||
*/
|
||||
export function overrideRomScoutRogueEffects(
|
||||
@@ -100,6 +113,28 @@ export function overrideRomScoutRogueEffects(
|
||||
translated: readonly AbilityEffect[],
|
||||
): readonly AbilityEffect[] | null {
|
||||
switch (row.sourceSkillId) {
|
||||
case 497206: { // Dodge arms the usable spell; its child applies the three-second concealment.
|
||||
if (row.classId !== "rom-rogue" || row.secondaryClassId !== "rom-mage") return null;
|
||||
const concealment = row.source?.components?.find((component) => component.id === 502597);
|
||||
const durationMs = Math.max(1_000, Math.round((concealment?.effectTime ?? 3) * 1_000));
|
||||
return [{
|
||||
kind: "apply-aura",
|
||||
recipient: "caster",
|
||||
aura: {
|
||||
id: "rom-rogue:instant-shadow",
|
||||
name: "Instant Shadow",
|
||||
disposition: "buff",
|
||||
durationMs,
|
||||
maxStacks: 1,
|
||||
stackBehavior: "refresh",
|
||||
utilityTags: ["stealth"],
|
||||
},
|
||||
sourceEffectType: row.source?.sourceEffectType ?? -1,
|
||||
sourceAuraType: -1,
|
||||
miscValue: 0,
|
||||
triggerSpellId: 502597,
|
||||
}];
|
||||
}
|
||||
case 490504:
|
||||
return taggedRoguePassive(490504, "Dagger Mastery", ROM_ROGUE_DAGGER_MASTERY_TAG);
|
||||
case 490316:
|
||||
@@ -109,6 +144,46 @@ export function overrideRomScoutRogueEffects(
|
||||
// is no distinct RoM off-hand hit yet, so do not turn either value into a
|
||||
// generic player melee modifier.
|
||||
return [];
|
||||
case 493020: { // Successful Low Blow/Blind Spot hits trigger the local 10s crit buff.
|
||||
if (row.classId !== "rom-rogue" || row.secondaryClassId !== "rom-priest") return null;
|
||||
const buff = row.source?.components?.find((component) => component.id === 503265);
|
||||
const criticalModifier = buff?.abilityModifiers?.find((modifier) => modifier.type === 18);
|
||||
const cooldown = row.source?.components?.find((component) => component.id === 503300);
|
||||
const durationMs = Math.max(0, Math.round((buff?.effectTime ?? 0) * 1_000));
|
||||
const cooldownMs = Math.max(0, Math.round((cooldown?.effectTime ?? 0) * 1_000));
|
||||
const proc = criticalModifier && durationMs > 0 && cooldownMs > 0
|
||||
? customProc(493020, "hit", "rom-sinister-methods", {
|
||||
data: { criticalStrike: criticalModifier.value, durationMs, cooldownMs },
|
||||
})
|
||||
: undefined;
|
||||
return taggedPassive(493020, "Sinister Methods", ROM_ROGUE_PRIEST_SINISTER_METHODS_TAG, proc);
|
||||
}
|
||||
case ROM_ROGUE_CHAMPION_NERVE_GEAR_PASSIVE_SPELL_ID:
|
||||
if (row.classId !== "rom-rogue" || row.secondaryClassId !== "rom-champion") return null;
|
||||
// The timed child only triggers after an elite skill use. Keep a learned
|
||||
// marker, not its generic aura, so party passive loading cannot grant the
|
||||
// attack-speed/dodge payload on an unrelated cast.
|
||||
return taggedPassive(
|
||||
ROM_ROGUE_CHAMPION_NERVE_GEAR_PASSIVE_SPELL_ID,
|
||||
"Nerve Gear",
|
||||
ROM_ROGUE_CHAMPION_NERVE_GEAR_MARKER_TAG,
|
||||
);
|
||||
case 491178:
|
||||
if (row.classId !== "rom-rogue" || row.secondaryClassId !== "rom-mage") return null;
|
||||
// Hidden child 491185 has no generated payload. The auto-attack runtime
|
||||
// applies its referenced Throw (490309) damage explicitly, so keep the
|
||||
// tag and avoid also registering the untranslated generic child proc.
|
||||
if (translated.some((effect) => effect.kind === "apply-aura")) {
|
||||
return translated.map((effect) => effect.kind === "apply-aura" ? {
|
||||
...effect,
|
||||
aura: {
|
||||
...effect.aura,
|
||||
procs: [],
|
||||
utilityTags: [...(effect.aura.utilityTags ?? []), ROM_ROGUE_MAGE_ENCHANTED_THROW_TAG],
|
||||
},
|
||||
} : effect);
|
||||
}
|
||||
return [...translated, ...taggedRoguePassive(491178, "Enchanted Throw", ROM_ROGUE_MAGE_ENCHANTED_THROW_TAG)];
|
||||
case 491552:
|
||||
if (row.classId !== "rom-rogue") return null;
|
||||
return translated.map((effect) => effect.kind === "apply-aura" ? {
|
||||
@@ -164,6 +239,17 @@ export function overrideRomScoutRogueEffects(
|
||||
case 493006:
|
||||
return taggedPassive(493006, "Accurate Targeting", "rom-accurate-targeting",
|
||||
customProc(493006, "hit", "rom-accurate-targeting", { internalCooldownMs: 30_000 }));
|
||||
case 491489:
|
||||
if (row.classId !== "rom-scout" || row.secondaryClassId !== "rom-warrior") return null;
|
||||
return taggedPassive(491489, "Enhanced Throat Attack", "rom-enhanced-throat-attack",
|
||||
customProc(491489, "hit", "rom-enhanced-throat-attack"));
|
||||
case 491532:
|
||||
if (row.classId !== "rom-rogue" || row.secondaryClassId !== "rom-scout") return null;
|
||||
return taggedPassive(491532, "Slit Throat", "rom-slit-throat",
|
||||
customProc(491532, "hit", "rom-slit-throat"));
|
||||
case 492344:
|
||||
if (row.classId !== "rom-scout" || row.secondaryClassId !== "rom-priest") return null;
|
||||
return taggedPassive(492344, "Sniper", ROM_SCOUT_PRIEST_SNIPER_TAG);
|
||||
case 493007:
|
||||
return taggedPassive(493007, "Deadly Counterattack", "rom-deadly-counterattack",
|
||||
customProc(493007, "hit", "rom-deadly-counterattack", { internalCooldownMs: 20_000 }));
|
||||
@@ -301,8 +387,14 @@ export function romScoutRogueCastEffects(
|
||||
breakOnDamage: true,
|
||||
}];
|
||||
}
|
||||
if (ability.dbcSpellId === 490309 && passives.has("rom-throwing-mastery")) {
|
||||
return [...effects, { kind: "status", status: "slow", durationMs: 5_000, magnitude: 0.4 }];
|
||||
if (ability.dbcSpellId === 490309) {
|
||||
return [
|
||||
...effects,
|
||||
...(passives.has("rom-throwing-mastery")
|
||||
? [{ kind: "status" as const, status: "slow" as const, durationMs: 5_000, magnitude: 0.4 }]
|
||||
: []),
|
||||
{ kind: "cooldown-reset", mode: "reduce", abilityName: "Day of Rain", amountMs: 1_000 },
|
||||
];
|
||||
}
|
||||
if (ability.dbcSpellId === 492621) {
|
||||
const hasSourcePoison = target.has("rom-deadly-poison") || target.has("rom-501690");
|
||||
@@ -316,6 +408,26 @@ export function romScoutRogueCastEffects(
|
||||
return effects;
|
||||
}
|
||||
|
||||
/** Day of Rain has an exact 50% miss chance while Enchanted Throw is active. */
|
||||
export function romRogueMageDayOfRainBaseMissPercent(
|
||||
classId: string,
|
||||
secondaryClassId: string | null | undefined,
|
||||
abilitySpellId: number | undefined,
|
||||
activeAuraTags: readonly string[],
|
||||
attackerHitPercent: number,
|
||||
): number | null {
|
||||
if (classId !== "rom-rogue"
|
||||
|| secondaryClassId !== "rom-mage"
|
||||
|| abilitySpellId !== 491183
|
||||
|| !activeAuraTags.includes(ROM_ROGUE_MAGE_ENCHANTED_THROW_TAG)) return null;
|
||||
const hitPercent = Number.isFinite(attackerHitPercent)
|
||||
? Math.max(0, Math.min(100, attackerHitPercent))
|
||||
: 0;
|
||||
// combatAttackTable subtracts attacker hit rating from base miss chance.
|
||||
// Offset it here so the tooltip's 50% rate remains exact at any hit rating.
|
||||
return 50 + hitPercent;
|
||||
}
|
||||
|
||||
/** 499481/622993 adds a hit only to targets already carrying 622992's poison. */
|
||||
export function romScoutRogueWhirlwindBonusEffect(
|
||||
ability: AbilityDefinition,
|
||||
@@ -333,6 +445,7 @@ export type RomScoutRogueProcActionSpec =
|
||||
readonly recipient: "caster" | "target";
|
||||
readonly aura: AuraDefinition;
|
||||
readonly provenance: "source" | "hybrid-estimate";
|
||||
readonly cooldownMs?: number;
|
||||
}
|
||||
| {
|
||||
readonly kind: "dot";
|
||||
@@ -352,6 +465,13 @@ export type RomScoutRogueProcActionSpec =
|
||||
readonly recipient: "target";
|
||||
readonly effect: Extract<AbilityEffect, { kind: "damage" }>;
|
||||
readonly provenance: "source" | "hybrid-estimate";
|
||||
}
|
||||
| {
|
||||
readonly kind: "status";
|
||||
readonly recipient: "target";
|
||||
readonly status: "silence";
|
||||
readonly durationMs: number;
|
||||
readonly provenance: "source";
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -364,8 +484,37 @@ export function romScoutRogueProcActionSpec(
|
||||
actionId: string,
|
||||
triggeringSourceSkillId: number,
|
||||
casterPoisonous: boolean,
|
||||
data: Readonly<Record<string, string | number | boolean>> = {},
|
||||
): RomScoutRogueProcActionSpec | null {
|
||||
switch (actionId) {
|
||||
case "rom-enhanced-throat-attack":
|
||||
case "rom-slit-throat":
|
||||
if (triggeringSourceSkillId !== 490428) return null;
|
||||
return { kind: "status", recipient: "target", status: "silence", durationMs: 4_000, provenance: "source" };
|
||||
case "rom-sinister-methods": {
|
||||
if (![490323, 490311].includes(triggeringSourceSkillId)) return null;
|
||||
const criticalStrike = data.criticalStrike;
|
||||
const durationMs = data.durationMs;
|
||||
const cooldownMs = data.cooldownMs;
|
||||
if (typeof criticalStrike !== "number" || !Number.isFinite(criticalStrike)
|
||||
|| typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs <= 0
|
||||
|| typeof cooldownMs !== "number" || !Number.isFinite(cooldownMs) || cooldownMs <= 0) return null;
|
||||
return {
|
||||
kind: "aura",
|
||||
recipient: "caster",
|
||||
aura: {
|
||||
id: "rom-pair-sinister-methods-critical",
|
||||
name: "Sinister Methods",
|
||||
disposition: "buff",
|
||||
durationMs,
|
||||
maxStacks: 1,
|
||||
stackBehavior: "refresh",
|
||||
modifiers: [{ kind: "stat", stat: "physical-critical-strike", operation: "flat", value: criticalStrike }],
|
||||
},
|
||||
cooldownMs,
|
||||
provenance: "source",
|
||||
};
|
||||
}
|
||||
case "rom-accurate-targeting": {
|
||||
if (triggeringSourceSkillId !== 490423) return null;
|
||||
return {
|
||||
|
||||
Reference in new issue
Block a user