Update Healer Man server
This commit is contained in:
1 parent
39458d5de6
commit
6cc1b9217f
203 files changed
+133929
-31271
No files matched your search
@@ -1,3 +1,5 @@
|
||||
import { enemyDamageTypeSourceForSpell, resolveEnemyDamageType, type EnemyDamageOrigin } from "./mobDamageTypes";
|
||||
import { romNativeEnemyPeriodicDamageSources } from "./romNativePeriodicDamage";
|
||||
import {
|
||||
normalizeRunewakerDamageFormula,
|
||||
type RunewakerDamageFormulaSource,
|
||||
@@ -72,6 +74,8 @@ export interface EnemyAttackTelegraph {
|
||||
|
||||
export type EnemyAtomicAbilityEffect =
|
||||
| { readonly kind: "damage"; readonly multiplier: number }
|
||||
| { readonly kind: "dot"; readonly multiplier: number; readonly ticks: number; readonly intervalMs: number;
|
||||
readonly sourceSkillId?: number; readonly componentId?: number }
|
||||
| { readonly kind: "heal"; readonly multiplier: number }
|
||||
| {
|
||||
readonly kind: "leech";
|
||||
@@ -162,6 +166,19 @@ export interface EnemyAttackDefinition {
|
||||
readonly delivery: EnemyAttackDelivery;
|
||||
readonly target: EnemyAttackTarget;
|
||||
readonly school: EnemyDamageSchool;
|
||||
/** Hit-table channel, independent from projectile/area presentation and damage school. */
|
||||
readonly attackKind?: "melee" | "ranged" | "spell";
|
||||
readonly damageClass?: number;
|
||||
/** Original WoW school bits; composite schools retain the authored scalar policy. */
|
||||
readonly schoolMask?: number;
|
||||
/** Source RuneWaker HP calculation channel (0 magic, 1 physical). */
|
||||
readonly runewakerEffectType?: number;
|
||||
readonly runewakerComponentId?: number;
|
||||
readonly damageOrigin?: EnemyDamageOrigin;
|
||||
/** Runtime pulse of a previously applied periodic debuff. */
|
||||
readonly periodicTick?: boolean;
|
||||
readonly targetMitigationSnapshotted?: boolean;
|
||||
readonly periodicNoKill?: boolean;
|
||||
readonly animation: EnemyAttackAnimation;
|
||||
/** Zero keeps the legacy instant-execution behavior. */
|
||||
readonly castTimeMs?: number;
|
||||
@@ -184,6 +201,8 @@ export interface EnemyAttackDefinition {
|
||||
|
||||
export interface EnemyCombatDefinition {
|
||||
readonly serverEntry?: number;
|
||||
/** RuneWaker NPC source identity; independent from AzerothCore creature entries. */
|
||||
readonly runewakerNpcId?: number;
|
||||
readonly level?: number;
|
||||
readonly healthMultiplier?: number;
|
||||
readonly damageMultiplier?: number;
|
||||
@@ -226,6 +245,7 @@ function harmfulEnemyAttack(value: EnemyAttackDefinition): boolean {
|
||||
if (value.effect.kind === "composite") {
|
||||
return value.effect.effects.some((effect) => (
|
||||
effect.kind === "damage"
|
||||
|| effect.kind === "dot"
|
||||
|| effect.kind === "leech"
|
||||
|| effect.kind === "control"
|
||||
|| effect.kind === "target-aura"
|
||||
@@ -233,6 +253,7 @@ function harmfulEnemyAttack(value: EnemyAttackDefinition): boolean {
|
||||
));
|
||||
}
|
||||
return value.effect.kind === "damage"
|
||||
|| value.effect.kind === "dot"
|
||||
|| value.effect.kind === "leech"
|
||||
|| value.effect.kind === "control"
|
||||
|| value.effect.kind === "target-aura"
|
||||
@@ -310,6 +331,7 @@ function normalizeTelegraph(value: EnemyAttackTelegraph): EnemyAttackTelegraph {
|
||||
export function normalizeEnemyAttack(
|
||||
value: EnemyAttackDefinition,
|
||||
fallback: EnemyAttackDefinition = DEFAULT_ENEMY_ATTACK,
|
||||
runewakerNpcId?: number,
|
||||
): EnemyAttackDefinition {
|
||||
const cooldownMs = Math.round(finiteAtLeast(value.cooldownMs, fallback.cooldownMs, 1));
|
||||
const telegraph = value.telegraph ?? inferEnemyAttackTelegraph(value);
|
||||
@@ -321,6 +343,33 @@ export function normalizeEnemyAttack(
|
||||
telegraph?.warningMs ?? 0,
|
||||
);
|
||||
const runewakerFormula = normalizeRunewakerDamageFormula(value.runewakerFormula);
|
||||
const damageOrigin = value.damageOrigin ?? (runewakerFormula || value.id.startsWith("rw-") ? "runewaker" : "wow335");
|
||||
const sourceType = value.spellId ? enemyDamageTypeSourceForSpell(damageOrigin, value.spellId) : undefined;
|
||||
// Script/control placeholder spell IDs must not override authored damage packets.
|
||||
const sourceHasDamage = sourceType && ("components" in sourceType
|
||||
? sourceType.components.length > 0 : sourceType.damageEffects.length > 0);
|
||||
const damageType = resolveEnemyDamageType({ ...value,
|
||||
...(sourceHasDamage ? { origin: damageOrigin,
|
||||
componentId: value.runewakerComponentId ?? runewakerFormula?.componentId } : {}),
|
||||
});
|
||||
const periodicSources = damageOrigin === "runewaker" && value.spellId
|
||||
? romNativeEnemyPeriodicDamageSources(value.spellId, runewakerNpcId) : [];
|
||||
const uniquePeriodicSources = periodicSources.filter((source, index, all) => all.findIndex((entry) =>
|
||||
entry.sourceSkillId === source.sourceSkillId && entry.component.componentId === source.component.componentId) === index);
|
||||
const periodicEffects: EnemyAtomicAbilityEffect[] = uniquePeriodicSources.map((source) => ({
|
||||
kind: "dot", multiplier: value.damageMultiplier,
|
||||
ticks: Math.max(1, Math.trunc(source.component.effectTimeSeconds / source.component.dotTimeSeconds)),
|
||||
intervalMs: source.component.dotTimeSeconds * 1_000,
|
||||
sourceSkillId: source.sourceSkillId, componentId: source.component.componentId,
|
||||
}));
|
||||
const baseEffect = value.effect ?? { kind: "damage" as const, multiplier: value.damageMultiplier };
|
||||
const existingEffects = baseEffect.kind === "composite" ? baseEffect.effects : [baseEffect];
|
||||
const directEffects = (runewakerFormula?.powerKind === "dot-only" ? [] : existingEffects)
|
||||
.filter((effect) => !(effect.kind === "dot" && periodicEffects.some((periodic) => periodic.kind === "dot"
|
||||
&& periodic.componentId === effect.componentId && periodic.sourceSkillId === effect.sourceSkillId)));
|
||||
const resolvedEffect = periodicEffects.length
|
||||
? { kind: "composite" as const, effects: [...directEffects, ...periodicEffects] }
|
||||
: value.effect;
|
||||
return Object.freeze({
|
||||
id: value.id.trim() || fallback.id,
|
||||
name: value.name.trim() || fallback.name,
|
||||
@@ -329,7 +378,15 @@ export function normalizeEnemyAttack(
|
||||
: {}),
|
||||
delivery: value.delivery,
|
||||
target: value.target,
|
||||
school: value.school,
|
||||
school: damageType.school ?? value.school,
|
||||
damageOrigin,
|
||||
...(damageType.attackKind ? { attackKind: damageType.attackKind } : {}),
|
||||
...(damageType.damageClass !== undefined ? { damageClass: damageType.damageClass } : {}),
|
||||
...(damageType.schoolMask !== undefined ? { schoolMask: damageType.schoolMask } : {}),
|
||||
...(damageType.sourceDamageClass === "magical" ? { runewakerEffectType: 0 }
|
||||
: damageType.sourceDamageClass === "physical" ? { runewakerEffectType: 1 }
|
||||
: value.runewakerEffectType !== undefined ? { runewakerEffectType: value.runewakerEffectType } : {}),
|
||||
...(value.runewakerComponentId ? { runewakerComponentId: value.runewakerComponentId } : {}),
|
||||
animation: value.animation,
|
||||
castTimeMs,
|
||||
interruptible: value.interruptible ?? fallback.interruptible ?? true,
|
||||
@@ -350,7 +407,7 @@ export function normalizeEnemyAttack(
|
||||
? { radius: finiteAtLeast(value.radius, 0, 0) }
|
||||
: {}),
|
||||
...(telegraph ? { telegraph: normalizeTelegraph(telegraph) } : {}),
|
||||
...(value.effect ? { effect: value.effect } : {}),
|
||||
...(resolvedEffect ? { effect: resolvedEffect } : {}),
|
||||
...(value.condition ? { condition: value.condition } : {}),
|
||||
...(value.source ? { source: value.source } : {}),
|
||||
});
|
||||
@@ -359,9 +416,10 @@ export function normalizeEnemyAttack(
|
||||
export function normalizeEnemyAttacks(
|
||||
attacks: readonly EnemyAttackDefinition[] | undefined,
|
||||
fallback: EnemyAttackDefinition = DEFAULT_ENEMY_ATTACK,
|
||||
runewakerNpcId?: number,
|
||||
): readonly EnemyAttackDefinition[] {
|
||||
const source = attacks?.length ? attacks : [fallback];
|
||||
const normalized = source.map((attack) => normalizeEnemyAttack(attack, fallback));
|
||||
const normalized = source.map((attack) => normalizeEnemyAttack(attack, fallback, runewakerNpcId));
|
||||
const seen = new Set<string>();
|
||||
return Object.freeze(normalized.filter((attack) => {
|
||||
if (seen.has(attack.id)) return false;
|
||||
|
||||
Reference in new issue
Block a user