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 }; }).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(); }); });