Update Healer Man server

This commit is contained in:
phenom committed 2026-10-07 13:05:04 -04:00
1 parent 69809f7228
commit ff20ab8779
101 files changed
+16713 -945

No files matched your search

+146 -26
View File
@@ -4,6 +4,7 @@ import {
type EnemyNpcSpellDefenseMetadata,
type EnemyNpcSpellSourceProfile,
} from "./enemyBlocking";
import type { AbilityDefinition, AbilityEffect } from "./abilityCatalog";
export type EnemyUtilitySpellCasterKind = "player" | "party" | "enemy-npc" | "rom-native" | "unknown";
export type EnemyUtilitySpellTargetKind = "ordinary-npc" | "character" | "native-rom" | "summon" | "unknown";
@@ -11,54 +12,173 @@ export type EnemyUtilitySpellHitOutcome = "hit" | "crit" | "miss" | "dodge" | "p
export type EnemyUtilitySpellBlockDisposition = "apply" | "blocked" | "avoided" | "fallback";
export interface EnemyUtilitySpellBlockSourceContract {
readonly spellId: 72;
readonly spellId: 72 | 1766 | 6552;
readonly sourceProfile: "wow335";
readonly abilityId: string;
/** Raw imported chain ID, accepted only with the same exact source projection. */
readonly sourceAbilityId?: string;
readonly classId: "warrior" | "rogue";
readonly abilityName: "Shield Bash" | "Kick" | "Pummel";
readonly spellDefense: EnemyNpcSpellDefenseMetadata;
/** Relevant fields from the two exact Shield Bash Spell.dbc effect slots. */
readonly dbcEffects: readonly [
{ readonly index: 0; readonly effectType: 68; readonly mechanic: 26; readonly targetA: 6; readonly triggerSpellId: 0 },
{ readonly index: 1; readonly effectType: 64; readonly mechanic: 0; readonly targetA: 6; readonly triggerSpellId: 29703 },
];
/** Relevant source fields from the exact selected Spell.dbc rank. */
readonly dbcEffects: readonly EnemyUtilitySpellBlockDbcEffect[];
/** Exact projection produced by the playable catalog for this selected rank. */
readonly projectedEffects: readonly AbilityEffect[];
}
const SHIELD_BASH_72_EFFECTS = Object.freeze([
Object.freeze({ index: 0 as const, effectType: 68 as const, mechanic: 26 as const, targetA: 6 as const, triggerSpellId: 0 as const }),
Object.freeze({ index: 1 as const, effectType: 64 as const, mechanic: 0 as const, targetA: 6 as const, triggerSpellId: 29703 as const }),
]) as EnemyUtilitySpellBlockSourceContract["dbcEffects"];
interface EnemyUtilitySpellBlockDbcEffect {
readonly index: number;
readonly effectType: number;
readonly auraType: number;
readonly mechanic: number;
readonly targetA: number;
readonly targetB: number;
readonly basePoints: number;
readonly triggerSpellId: number;
}
interface EnemyUtilitySpellBlockRankContract {
readonly spellId: EnemyUtilitySpellBlockSourceContract["spellId"];
readonly abilityId: string;
readonly sourceAbilityId?: string;
readonly classId: "warrior" | "rogue";
readonly abilityName: EnemyUtilitySpellBlockSourceContract["abilityName"];
readonly dbcEffects: readonly EnemyUtilitySpellBlockDbcEffect[];
readonly projectedEffects: readonly AbilityEffect[];
}
const UTILITY_SPELL_BLOCK_RANKS: readonly EnemyUtilitySpellBlockRankContract[] = Object.freeze([
Object.freeze({
spellId: 72,
abilityId: "wow335-warrior-shield-bash",
classId: "warrior",
abilityName: "Shield Bash",
dbcEffects: Object.freeze([
Object.freeze({ index: 0, effectType: 68, auraType: 0, mechanic: 26, targetA: 6, targetB: 0, basePoints: 0, triggerSpellId: 0 }),
Object.freeze({ index: 1, effectType: 64, auraType: 0, mechanic: 0, targetA: 6, targetB: 0, basePoints: 0, triggerSpellId: 29703 }),
]),
projectedEffects: Object.freeze([
Object.freeze({ kind: "interrupt", lockoutMs: 6_000 }),
Object.freeze({ kind: "trigger-spell", spellId: 29_703 }),
]),
}),
Object.freeze({
spellId: 1766,
abilityId: "rogue-kick",
sourceAbilityId: "wow335-rogue-kick",
classId: "rogue",
abilityName: "Kick",
dbcEffects: Object.freeze([
Object.freeze({ index: 0, effectType: 68, auraType: 0, mechanic: 26, targetA: 6, targetB: 0, basePoints: 1, triggerSpellId: 0 }),
Object.freeze({ index: 1, effectType: 0, auraType: 0, mechanic: 0, targetA: 0, targetB: 0, basePoints: 0, triggerSpellId: 0 }),
]),
projectedEffects: Object.freeze([
Object.freeze({ kind: "interrupt", lockoutMs: 5_000 }),
]),
}),
Object.freeze({
spellId: 6552,
abilityId: "wow335-warrior-pummel",
classId: "warrior",
abilityName: "Pummel",
dbcEffects: Object.freeze([
Object.freeze({ index: 0, effectType: 68, auraType: 0, mechanic: 26, targetA: 6, targetB: 0, basePoints: 1, triggerSpellId: 0 }),
Object.freeze({ index: 1, effectType: 0, auraType: 0, mechanic: 0, targetA: 0, targetB: 0, basePoints: 0, triggerSpellId: 0 }),
]),
projectedEffects: Object.freeze([
Object.freeze({ kind: "interrupt", lockoutMs: 4_000 }),
]),
}),
]);
/**
* Returns the only audited utility full-hit-block spell contract. The rank and
* profile are intentionally exact; the DBC effect signature is interrupt plus
* its triggered aura, with no direct-damage effect.
* Returns an audited utility full-block contract for an exact WoW 3.3.5a
* selected rank. Other ranks and source profiles remain on their current path.
*/
export function enemyUtilitySpellBlockSourceContract(
spellId: number | undefined,
sourceProfile: EnemyNpcSpellSourceProfile | undefined,
): EnemyUtilitySpellBlockSourceContract | undefined {
if (spellId !== 72 || sourceProfile !== "wow335") return undefined;
const spellDefense = enemyNpcSourceSpellBlockMetadata(72, "wow335");
if (sourceProfile !== "wow335") return undefined;
const rank = UTILITY_SPELL_BLOCK_RANKS.find((candidate) => candidate.spellId === spellId);
if (!rank) return undefined;
const spellDefense = enemyNpcSourceSpellBlockMetadata(rank.spellId, "wow335");
if (!spellDefense) return undefined;
return Object.freeze({
spellId: 72,
spellId: rank.spellId,
sourceProfile: "wow335",
abilityId: rank.abilityId,
...(rank.sourceAbilityId ? { sourceAbilityId: rank.sourceAbilityId } : {}),
classId: rank.classId,
abilityName: rank.abilityName,
spellDefense,
dbcEffects: SHIELD_BASH_72_EFFECTS,
dbcEffects: rank.dbcEffects,
projectedEffects: rank.projectedEffects,
});
}
function matchesShieldBashBlockMetadata(
function matchesUtilityBlockMetadata(
metadata: EnemyNpcSpellDefenseMetadata | undefined,
contract: EnemyUtilitySpellBlockSourceContract,
reconstructSourceProof = false,
): boolean {
if (!metadata) return false;
const expected = contract.spellDefense;
return metadata.damageClass === expected.damageClass
&& metadata.schoolMask === expected.schoolMask
&& metadata.attributes0 === expected.attributes0
&& metadata.attributes3 === expected.attributes3
&& metadata.directDamageFromEffects === expected.directDamageFromEffects
&& metadata.sourceProfile === expected.sourceProfile
&& classifyEnemyNpcSpellBlockPhase(metadata) === "hit-table";
if (metadata.damageClass !== expected.damageClass
|| metadata.schoolMask !== expected.schoolMask
|| metadata.attributes0 !== expected.attributes0
|| metadata.attributes3 !== expected.attributes3
|| metadata.directDamageFromEffects !== expected.directDamageFromEffects
|| metadata.sourceProfile !== expected.sourceProfile) return false;
const knownCustom = expected.customAttributeEvidence;
if (knownCustom) {
if (metadata.customAttributes !== undefined
&& ((metadata.customAttributes & knownCustom.knownMask) >>> 0) !== knownCustom.value) return false;
const evidence = metadata.customAttributeEvidence;
if (evidence && ((evidence.knownMask & knownCustom.knownMask) !== knownCustom.knownMask
|| ((evidence.value & knownCustom.knownMask) >>> 0) !== knownCustom.value)) return false;
}
const withSourceProof = reconstructSourceProof
&& metadata.customAttributes === undefined
&& metadata.customAttributeEvidence === undefined && knownCustom
? { ...metadata, customAttributeEvidence: knownCustom }
: metadata;
return classifyEnemyNpcSpellBlockPhase(withSourceProof) === "hit-table";
}
function stableSignature(value: unknown): string {
if (Array.isArray(value)) return `[${value.map(stableSignature).join(",")}]`;
if (value && typeof value === "object") {
const entries = Object.entries(value as Record<string, unknown>)
.sort(([left], [right]) => left.localeCompare(right));
return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${stableSignature(entry)}`).join(",")}}`;
}
return JSON.stringify(value) ?? "undefined";
}
/**
* Binds the spell-block contract to the actual selected playable catalog row.
* Matching a spell ID alone cannot authorize another class, source, wrapper,
* or modified effect list to borrow this source proof.
*/
export function enemyUtilitySpellBlockAbilityContract(
ability: AbilityDefinition | undefined,
): EnemyUtilitySpellBlockSourceContract | undefined {
if (!ability) return undefined;
const contract = enemyUtilitySpellBlockSourceContract(
ability.dbcSpellId,
ability.wowSpellDefense?.sourceProfile,
);
if (!contract
|| (ability.id !== contract.abilityId && ability.id !== contract.sourceAbilityId)
|| ability.classId !== contract.classId
|| ability.name !== contract.abilityName
|| ability.source !== "wow335-progression"
|| ability.dbcSpellId !== contract.spellId
|| !matchesUtilityBlockMetadata(ability.wowSpellDefense, contract, true)
|| stableSignature(ability.effects) !== stableSignature(contract.projectedEffects)) return undefined;
return contract;
}
/**
@@ -78,7 +198,7 @@ export function enemyUtilitySpellBlockDisposition(input: {
if (!contract
|| input.casterKind !== "player" && input.casterKind !== "party"
|| input.targetKind !== "ordinary-npc"
|| !matchesShieldBashBlockMetadata(input.spellDefense, contract)) return "fallback";
|| !matchesUtilityBlockMetadata(input.spellDefense, contract)) return "fallback";
if (input.outcome === "block") return "blocked";
if (input.outcome === "miss" || input.outcome === "dodge"