# 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).