import referenceData from "./generated/azerothCoreReference.json"; import { DEFAULT_ENEMY_ATTACK, type EnemyAttackDefinition, type EnemyCombatDefinition, } from "./enemyAttacks"; import type { PopulationDefinitionMap, PopulationEntityDefinition, } from "./mobPopulation"; import { creatureTemplateReference } from "./azerothCoreReference"; const ENTITY_SERVER_ENTRIES: Readonly> = Object.freeze({ "deviate-ravager": 3636, "deviate-guardian": 3637, "druid-of-the-fang": 3840, "evolving-ectoplasm": 3640, "deviate-crocolisk": 5053, "deviate-lasher": 5055, "deviate-shambler": 5761, "deviate-faerie-dragon": 5912, "lady-anacondra": 3671, kresh: 3653, "lord-cobrahn": 3669, "lord-pythas": 3670, skum: 3674, "lord-serpentis": 3673, "verdan-the-everliving": 5775, "mutanus-the-devourer": 3654, "disciple-of-naralex": 3678, "nightmare-ectoplasm": 5763, "deviate-viper": 5755, "deviate-adder": 5048, }); interface WailingReferenceShape { readonly wailingCaverns: { readonly abilitiesByEntry: Readonly>; readonly unsupportedRequiredBossActions: readonly unknown[]; }; } const WAILING = (referenceData as unknown as WailingReferenceShape).wailingCaverns; function serverCombat( definition: PopulationEntityDefinition, serverEntry: number, ): EnemyCombatDefinition { const template = creatureTemplateReference(serverEntry); const basicAttack: EnemyAttackDefinition = { ...DEFAULT_ENEMY_ATTACK, cooldownMs: template?.baseAttackTimeMs ?? DEFAULT_ENEMY_ATTACK.cooldownMs, repeatCooldownMs: [ template?.baseAttackTimeMs ?? DEFAULT_ENEMY_ATTACK.cooldownMs, template?.baseAttackTimeMs ?? DEFAULT_ENEMY_ATTACK.cooldownMs, ], initialCooldownMs: [450, 450], range: definition.kind === "boss" ? 5.2 : 4.3, }; const scripted = WAILING.abilitiesByEntry[String(serverEntry)] ?? []; return Object.freeze({ serverEntry, healthMultiplier: definition.kind === "boss" ? 0.86 : 1, damageMultiplier: 1, moveSpeed: definition.combat?.moveSpeed, leashRange: definition.combat?.leashRange ?? (definition.kind === "boss" ? 55 : 42), attacks: Object.freeze([basicAttack, ...scripted]), }); } /** Adds pinned server combat facts without replacing authored models or placement. */ export function withWailingServerCombat( definitions: PopulationDefinitionMap, ): PopulationDefinitionMap { return Object.freeze(Object.fromEntries( Object.entries(definitions).map(([id, definition]) => { const serverEntry = ENTITY_SERVER_ENTRIES[id]; if (!serverEntry) return [id, definition]; return [ id, Object.freeze({ ...definition, combat: serverCombat(definition, serverEntry), }), ]; }), )); } export function wailingServerEntry(entityId: string): number | null { return ENTITY_SERVER_ENTRIES[entityId] ?? null; } export function unsupportedWailingRequiredBossActions(): readonly unknown[] { return WAILING.unsupportedRequiredBossActions; }