Files
healer-man/src/game/enemyAutomaticOffhand.test.ts
T
2026-10-06 22:25:34 -04:00

115 lines
4.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { creatureTemplateReference } from "./azerothCoreReference";
import referenceData from "./generated/azerothCoreReference.json";
import {
CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK,
ENEMY_AUTOMATIC_OFFHAND_ITEM_SOURCE,
ENEMY_AUTOMATIC_OFFHAND_WORLD_SOURCE,
ENEMY_OFFHAND_ITEM_CLASS_WEAPON,
enemyAutoEquippedOffhandContract,
enemyFlagDrivenOffhandContract,
} from "./enemyAutomaticOffhand";
describe("automatic AzerothCore creature offhand contracts", () => {
it("pins the world table and the exact USE_OFFHAND_ATTACK flag bit", () => {
expect(ENEMY_AUTOMATIC_OFFHAND_WORLD_SOURCE).toEqual({
repository: "https://github.com/azerothcore/azerothcore-wotlk",
commit: "4d9d1d4b5723e819c5c390f6ca1177283dbfb8e3",
table: "creature_template",
path: "data/sql/base/db_world/creature_template.sql",
sha256: "0066e4fd35b809986dd325487515087f5a95f4ffe50ea03846c8d8bf5196870a",
});
expect(ENEMY_AUTOMATIC_OFFHAND_ITEM_SOURCE).toEqual({
path: "../wow335a/Data/patch-A.MPQ/DBFilesClient/Item.dbc",
sha256: "22061a811523790ddd2ee95b36ade2ac6e0c79a8b3121324d32a639acf50567f",
classField: 1,
});
expect(CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK).toBe(0x800);
expect(ENEMY_OFFHAND_ITEM_CLASS_WEAPON).toBe(2);
});
it("enables source creatures whose pinned template carries the offhand flag", () => {
const cases = [
[4540, "Scarlet Monk", 0x800],
[10478, "Splintered Skeleton", 0x800],
[10488, "Risen Construct", 0x800],
[16593, "Shattered Hand Brawler", 0x0800_0000 | 0x800],
[16808, "Warchief Kargath Bladefist", 0x800],
] as const;
const templates = (referenceData as unknown as {
creature: { templates: Record<string, { entry: number; name: string; flagsExtra?: number }> };
}).creature.templates;
const allFlagged = Object.values(templates)
.filter((template) => (template.flagsExtra ?? 0) & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK)
.sort((left, right) => left.entry - right.entry)
.map(({ entry, name, flagsExtra }) => [entry, name, flagsExtra]);
expect(Object.keys(templates)).toHaveLength(1_671);
expect(allFlagged).toEqual(cases.map(([entry, name, flagsExtra]) => [entry, name, flagsExtra]));
for (const [entry, name, flagsExtra] of cases) {
expect(creatureTemplateReference(entry)).toMatchObject({ entry, name, flagsExtra });
expect(enemyFlagDrivenOffhandContract(entry)).toMatchObject({
entry,
name,
source: "creature-template-flag",
dualWieldMode: "enabled",
requiresEquippedWeapon: false,
attackType: "offhand",
damageMultiplier: 0.5,
whiteSwingMissPenaltyPercent: 19,
attackTimeFrom: "creature-template-base-attack-time",
independentSwingTimer: true,
firstSwingDelayFractionOnEngage: 0.5,
simultaneousSwingDelayMs: 200,
});
}
});
it("fails closed for absent templates, unflagged rows, and invalid entry IDs", () => {
expect(enemyFlagDrivenOffhandContract(6)).toBeUndefined();
expect(enemyFlagDrivenOffhandContract(999_999_999)).toBeUndefined();
expect(enemyFlagDrivenOffhandContract(0)).toBeUndefined();
expect(enemyFlagDrivenOffhandContract(-4540)).toBeUndefined();
expect(enemyFlagDrivenOffhandContract(4540.5)).toBeUndefined();
});
it("uses the core AUTO-mode item-class gate without inventing item mappings", () => {
expect(enemyAutoEquippedOffhandContract({
entry: 1696,
offhandItemId: 123,
offhandItemClass: ENEMY_OFFHAND_ITEM_CLASS_WEAPON,
})).toMatchObject({
entry: 1696,
name: "Targorr the Dread",
source: "auto-offhand-weapon",
dualWieldMode: "auto",
requiresEquippedWeapon: true,
attackType: "offhand",
baseDamageFrom: "same-creature-template-offhand-range-and-ap-formula",
damageMultiplier: 0.5,
whiteSwingMissPenaltyPercent: 19,
});
for (const offhandItemClass of [null, undefined, 0, 1, 3, 4, -1]) {
expect(enemyAutoEquippedOffhandContract({
entry: 1696,
offhandItemId: 123,
offhandItemClass,
})).toBeUndefined();
}
for (const offhandItemId of [0, -1, 1.5, Number.NaN]) {
expect(enemyAutoEquippedOffhandContract({
entry: 1696,
offhandItemId,
offhandItemClass: ENEMY_OFFHAND_ITEM_CLASS_WEAPON,
})).toBeUndefined();
}
expect(enemyAutoEquippedOffhandContract({
entry: 999_999_999,
offhandItemId: 123,
offhandItemClass: ENEMY_OFFHAND_ITEM_CLASS_WEAPON,
})).toBeUndefined();
});
});