Files
healer-man/docs/ENEMY_EQUIPMENT_MAPPING_2026-10-06.md
2026-10-06 22:25:34 -04:00

116 lines
6.5 KiB
Markdown

# Pinned enemy equipment mapping — 2026-10-06
## Result
Recovered the exact AzerothCore `creature_equip_template.sql` at the currently
pinned world commit and added its hash-checked fixture and importer path. The
generated reference now joins imported spawn GUIDs and selectors to equipment
sets, then to Item.dbc class and inventory type. The runtime helper resolves
only an explicit source spawn GUID and entry; unknown, mismatched, unsupported,
or unresolvable assignments produce no equipment contract.
The recovered SQL is
`dungeon-pipeline/source-fixtures/azerothcore-4d9d1d4b5723e819c5c390f6ca1177283dbfb8e3/creature_equip_template.sql`.
It hashes to `877205c149fd447b08f0ff2c65976f20c862cd42f068a6fcc294a76c7108936a`
and belongs to AzerothCore commit
`4d9d1d4b5723e819c5c390f6ca1177283dbfb8e3`, matching the active
`creature.sql` and `creature_template.sql` pins. Its schema is
`CreatureID, ID, ItemID1, ItemID2, ItemID3, VerifiedBuild`; the source contains
10,854 SQL rows. The importer checks the full file hash before publishing the
equipment mapping and records it in `azerothCoreSourceLock.json`.
## Imported runtime scope
`azerothCoreReference.json` stores three related mappings under
`creature.equipment*`:
- `equipmentAssignmentsBySpawnGuid[guid]` records the source `id1`, optional
alternate entries `id2`/`id3`, and original `equipment_id` selector.
- `equipmentTemplatesByEntry[entry][equipmentId]` records raw source item IDs
and the three virtual item IDs retained after core loader validation.
- `equipmentItemsById[itemId]` stores the pinned Item.dbc `itemClass` and
`inventoryType` for IDs referenced by those equipment rows.
These assignments cover the source dungeon maps imported by the pipeline, not
all 149,879 rows in the world spawn table. The current generated snapshot has
13,277 spawn-GUID assignments, 755 equipment entries, 763 equipment sets, and
442 referenced item IDs. The spawn selectors are 7,632 zeroes, 5,637 ones, two
twos, and six `-1` values. Twenty-three spawn rows have alternate creature
entries. All 1,034 nonzero virtual item slots in this imported scope resolve
through the pinned Item.dbc and pass its hand-inventory validation.
The Item.dbc input is the WotLK 3.3.5a client file at
`../wow335a/Data/patch-A.MPQ/DBFilesClient/Item.dbc`, SHA-256
`22061a811523790ddd2ee95b36ade2ac6e0c79a8b3121324d32a639acf50567f`. Its
46,149 rows use eight 4-byte fields per 32-byte record; Class is field 1 and InventoryType is
field 6. The importer rejects a different hash or DBC layout. Both pins and
field indexes appear in the generated source lock and reference.
## Source behavior and selectors
The source behavior was checked against the exact commit-pinned
`Creature.cpp` (SHA-256
`ea19df5312b632bb7b9faf4323c1612fff8d8104e390d7193bae584318e3e11b`) and
`ObjectMgr.cpp` (SHA-256
`e4eae029cc30f84c26c258ba806d39eea61200f623aead1ee0813f6adebeed37`).
The relevant operations are `ObjectMgr::GetEquipmentInfo`,
`ObjectMgr::LoadEquipmentTemplates`, spawn data loading, `Creature::LoadEquipment`,
and `Creature::HasWeapon`.
For a database spawn, selector `0` means no equipment. A positive selector
looks up that exact equipment ID. Selector `-1` chooses a random row from the
entry's equipment map, then mutates the selector to `distance(begin, row) + 1`.
That is an ordinal-based ID, not the selected row's actual key. ObjectMgr checks
nonzero alternate entries using the mutated selector and clears the assignment
if a required alternate row is absent; the later creature load again looks up
the concrete ID. The runtime helper mirrors this sequence with an injected
uniform value in `[0, 1)`. It fails closed if the resulting exact lookup or any
alternate lookup is absent rather than choosing the row's key as a substitute.
This distinction matters for sparse IDs. The pinned table has creature 32239
with only equipment ID 2. Its one-row `-1` selection rewrites to ID 1, so a
later ID-1 lookup has no result; the helper will not grant its ID-2 equipment
under a random selector. An explicit selector `2` can resolve that row. The
focused test covers this ordinal-plus-one behavior.
`ObjectMgr::LoadEquipmentTemplates` also removes missing items and item rows
whose Item.dbc InventoryType is not one of `13, 14, 15, 17, 21, 22, 23, 25,
26` (weapon, shield, ranged, two-handed weapon, main-hand weapon, off-hand
weapon, holdable, thrown, or ranged-right). The importer applies this same
whitelist to all three virtual slots. After that validation,
`Creature::HasWeapon(OFF_ATTACK)` in AUTO mode checks the resolved Item.dbc
`ClassID == ITEM_CLASS_WEAPON` (2). No `item_template` lookup is needed for
that gate; this import does not claim to model item stats or item damage.
The helper `enemySpawnEquipmentContract(entry, sourceCreatureGuid, randomUnit?)`
returns the resolved virtual item IDs and offhand class, plus the existing
`autoOffhand` contract when the offhand item is a weapon. It requires a source
GUID whose imported entry list contains the requested creature. Database
spawns with selector zero resolve to empty virtual slots. Missing GUIDs,
synthetic spawns, entry mismatches, older references without equipment maps,
and failed selector lookups return no contract. Core's separate no-spawn-data
default `LoadEquipment()` selects ID 1; the GUID-based helper does not infer
that creation path for synthetic actors.
The mapping only supplies source equipment and AUTO offhand capability. It
does not schedule attacks, import item stats, change loot, alter the level-80
cap, or certify CoA/native balance. Ready-five remains the certification set;
CoA and native balance remain Needs Testing.
## Validation
- `node --test scripts/dungeon-pipeline/import-azerothcore-world.test.mjs` — 10
focused tests passed.
- `npx vitest run src/game/enemyEquipment.test.ts` — 7 focused tests passed.
- `node scripts/dungeon-pipeline/import-azerothcore-world.mjs --item-dbc
"F:\\Projects\\wow335a\\Data\\patch-A.MPQ\\DBFilesClient\\Item.dbc"` —
green; 54 imported dungeon snapshots, 13,277 creature spawns, 494 encounters,
with the three expected Ascension-only fallback maps.
The isolated source-data subprocess did not run a production build or live QA.
Root's combined continuation subsequently passes the standard build, Ready-five,
avatar checks and the one frozen Cellblock Rush clear. Targorr's live source
GUID 84027 resolves virtual slots `[5305, 5305, 0]` and weapon Class 2; eight
offhand outcomes are observed. Combined evidence and limitations are in
[the continuation report](ENEMY_SOURCE_PARITY_CONTINUATION_2026-10-06.md).