156 lines
5.6 KiB
TypeScript
156 lines
5.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import referenceData from "./generated/azerothCoreReference.json";
|
|
import {
|
|
enemySpawnEquipmentContract,
|
|
type EnemyEquipmentSourceReference,
|
|
} from "./enemyEquipment";
|
|
|
|
function equipmentSource(
|
|
selector: number,
|
|
alternateEntries: readonly number[] = [],
|
|
templates: Readonly<Record<string, { sourceItems: readonly [number, number, number]; items: readonly [number, number, number] }>> = {},
|
|
alternateTemplates: Readonly<Record<string, Readonly<Record<string, { sourceItems: readonly [number, number, number]; items: readonly [number, number, number] }>>>> = {},
|
|
): EnemyEquipmentSourceReference {
|
|
return {
|
|
equipmentAssignmentsBySpawnGuid: {
|
|
"8765": { entry: 1696, alternateEntries, selector },
|
|
},
|
|
equipmentTemplatesByEntry: { "1696": templates, ...alternateTemplates },
|
|
equipmentItemsById: {
|
|
"100": { itemClass: 2, inventoryType: 13 },
|
|
"101": { itemClass: 4, inventoryType: 14 },
|
|
},
|
|
};
|
|
}
|
|
|
|
const setOne = {
|
|
sourceItems: [50, 100, 0] as const,
|
|
items: [50, 100, 0] as const,
|
|
};
|
|
const setTwo = {
|
|
sourceItems: [51, 101, 0] as const,
|
|
items: [51, 101, 0] as const,
|
|
};
|
|
|
|
describe("pinned AzerothCore spawn equipment resolution", () => {
|
|
it("requires a source GUID whose pinned spawn entry matches the requested creature", () => {
|
|
const source = equipmentSource(1, [], { "1": setOne });
|
|
expect(enemySpawnEquipmentContract(1696, 8765, undefined, source)).toMatchObject({
|
|
entry: 1696,
|
|
sourceCreatureGuid: 8765,
|
|
selector: 1,
|
|
equipmentId: 1,
|
|
virtualItemIds: [50, 100, 0],
|
|
});
|
|
expect(enemySpawnEquipmentContract(1711, 8765, undefined, source)).toBeUndefined();
|
|
expect(enemySpawnEquipmentContract(1696, 1, undefined, source)).toBeUndefined();
|
|
expect(enemySpawnEquipmentContract(1696, 0, undefined, source)).toBeUndefined();
|
|
});
|
|
|
|
it("keeps selector zero as an explicit empty equipment assignment", () => {
|
|
const resolved = enemySpawnEquipmentContract(1696, 8765, undefined, equipmentSource(0));
|
|
expect(resolved).toMatchObject({
|
|
selector: 0,
|
|
equipmentId: 0,
|
|
virtualItemIds: [0, 0, 0],
|
|
offhandItemId: 0,
|
|
offhandItemClass: null,
|
|
});
|
|
expect(resolved?.autoOffhand).toBeUndefined();
|
|
});
|
|
|
|
it("uses an exact positive equipment ID and the imported Item.dbc class", () => {
|
|
const resolved = enemySpawnEquipmentContract(
|
|
1696,
|
|
8765,
|
|
undefined,
|
|
equipmentSource(2, [], { "1": setOne, "2": setTwo }),
|
|
);
|
|
expect(resolved).toMatchObject({
|
|
selector: 2,
|
|
equipmentId: 2,
|
|
virtualItemIds: [51, 101, 0],
|
|
offhandItemId: 101,
|
|
offhandItemClass: 4,
|
|
});
|
|
expect(resolved?.autoOffhand).toBeUndefined();
|
|
|
|
const weapon = enemySpawnEquipmentContract(
|
|
1696,
|
|
8765,
|
|
undefined,
|
|
equipmentSource(1, [], { "1": setOne }),
|
|
);
|
|
expect(weapon?.autoOffhand).toMatchObject({
|
|
entry: 1696,
|
|
source: "auto-offhand-weapon",
|
|
dualWieldMode: "auto",
|
|
});
|
|
});
|
|
|
|
it("resolves -1 with source ordinal-plus-one semantics and rejects sparse follow-up lookups", () => {
|
|
const denseSource = equipmentSource(-1, [], { "1": setOne, "2": setTwo });
|
|
expect(enemySpawnEquipmentContract(1696, 8765, 0.75, denseSource)).toMatchObject({
|
|
selector: -1,
|
|
equipmentId: 2,
|
|
virtualItemIds: [51, 101, 0],
|
|
});
|
|
|
|
const sparseSource = equipmentSource(-1, [], { "2": setTwo });
|
|
expect(enemySpawnEquipmentContract(1696, 8765, 0, sparseSource)).toBeUndefined();
|
|
expect(enemySpawnEquipmentContract(1696, 8765, undefined, denseSource)).toBeUndefined();
|
|
expect(enemySpawnEquipmentContract(1696, 8765, 1, denseSource)).toBeUndefined();
|
|
expect(enemySpawnEquipmentContract(1696, 8765, -0.01, denseSource)).toBeUndefined();
|
|
});
|
|
|
|
it("validates positive selectors against alternate creature entries", () => {
|
|
const alternateMissing = equipmentSource(1, [1711], { "1": setOne });
|
|
expect(enemySpawnEquipmentContract(1696, 8765, undefined, alternateMissing)).toBeUndefined();
|
|
|
|
const alternateAvailable = equipmentSource(
|
|
1,
|
|
[1711],
|
|
{ "1": setOne },
|
|
{ "1711": { "1": setOne } },
|
|
);
|
|
expect(enemySpawnEquipmentContract(1696, 8765, undefined, alternateAvailable))
|
|
.toMatchObject({ virtualItemIds: [50, 100, 0] });
|
|
|
|
const randomAlternateMissing = equipmentSource(-1, [1711], { "1": setOne, "2": setTwo });
|
|
expect(enemySpawnEquipmentContract(1696, 8765, 0.75, randomAlternateMissing)).toBeUndefined();
|
|
const randomAlternateAvailable = equipmentSource(
|
|
-1,
|
|
[1711],
|
|
{ "1": setOne, "2": setTwo },
|
|
{ "1711": { "2": setTwo } },
|
|
);
|
|
expect(enemySpawnEquipmentContract(1711, 8765, 0.75, randomAlternateAvailable))
|
|
.toMatchObject({ equipmentId: 2, virtualItemIds: [51, 101, 0] });
|
|
});
|
|
|
|
it("fails closed for older references without equipment maps", () => {
|
|
expect(enemySpawnEquipmentContract(
|
|
1696,
|
|
8765,
|
|
undefined,
|
|
{} as EnemyEquipmentSourceReference,
|
|
)).toBeUndefined();
|
|
});
|
|
|
|
it("resolves a pinned Stockade spawn without inferring its equipment ID from the entry", () => {
|
|
const assignments = (referenceData as unknown as {
|
|
creature: EnemyEquipmentSourceReference;
|
|
}).creature.equipmentAssignmentsBySpawnGuid;
|
|
const spawn = assignments["84027"];
|
|
expect(spawn).toEqual({ entry: 1696, alternateEntries: [], selector: 1 });
|
|
expect(enemySpawnEquipmentContract(1696, 84027)).toMatchObject({
|
|
selector: 1,
|
|
equipmentId: 1,
|
|
virtualItemIds: [5305, 5305, 0],
|
|
offhandItemId: 5305,
|
|
offhandItemClass: 2,
|
|
autoOffhand: { source: "auto-offhand-weapon" },
|
|
});
|
|
});
|
|
});
|