# Automatic enemy offhands — 2026-10-05 Current source-table recovery supersedes the missing equipment-map limitation recorded below. See [the 2026-10-06 equipment mapping audit](ENEMY_EQUIPMENT_MAPPING_2026-10-06.md) for the pinned SQL, Item.dbc validation, importer output, and runtime helper. ## Result Added narrow helpers for the two historical AzerothCore automatic offhand gates: the creature-template `USE_OFFHAND_ATTACK` flag and the default `AUTO` mode's resolved offhand weapon check. The flag helper binds only the five matching creatures present in HealerMan's imported template-reference subset. The item helper requires a nonzero item ID and an already resolved Item.dbc class of `WEAPON` (2). It does not map an `equipment_id` to an item because that table is absent from the pinned world import. The helpers are in [enemyAutomaticOffhand.ts](../src/game/enemyAutomaticOffhand.ts). They do not schedule attacks or mutate combat state. Runtime should treat `enemyFlagDrivenOffhandContract(entry)` as an intrinsic creature capability and feed it into the existing ordinary WoW offhand path. Keep this source flag separate from the simulator's temporary spell-granted capability: core sets the flag mode in creature initialization and entry updates, so evade, death, and encounter reset must not clear it while the creature entry remains the same. The simulator's existing reset policy can continue to clear spell-granted Dual Wield state. `enemyAutoEquippedOffhandContract({ entry, name?, offhandItemId, offhandItemClass })` resolves the default `AUTO` item gate only after a caller has loaded the actual offhand virtual item and looked up its class. An absent, zero, unresolved, or non-weapon item returns no contract. Do not use a model, creature appearance, or `equipment_id` alone as a substitute for the missing slot-to-item mapping. Keep both helpers on the source WoW creature path; the existing RuneWaker, summon, and character-defender exclusions remain in force. ## World source and coverage The imported AzerothCore world source is commit `4d9d1d4b5723e819c5c390f6ca1177283dbfb8e3`. Its lock pins `creature.sql` at SHA-256 `f54ddfe7ee00080b4b0bc970a809d6f947f6fb2627dbcd67e92b402fb34cb701` and `creature_template.sql` at SHA-256 `0066e4fd35b809986dd325487515087f5a95f4ffe50ea03846c8d8bf5196870a`. `creature_template.sql` has 29,947 rows, 43 of which set `flags_extra & 0x00000800`. The imported runtime reference contains 1,671 creature templates; five carry that bit: | Entry | Creature | `flags_extra` | | ---: | --- | ---: | | 4540 | Scarlet Monk | 2048 | | 10478 | Splintered Skeleton | 2048 | | 10488 | Risen Construct | 2048 | | 16593 | Shattered Hand Brawler | 134219776 | | 16808 | Warchief Kargath Bladefist | 2048 | The source lock includes `creature.sql` rows with an `equipment_id` field, but does not include `creature_equip_template.sql`. The pinned spawn table has 149,879 rows; 51,248 have a nonzero `equipment_id` value (the distinct values are `-1`, `1`, and `2`). Those values select equipment-set IDs; they do not identify an offhand item by themselves. I found `creature_equip_template.sql` in two local AzerothCore backup trees: `../wow-pvt-server-backups-2/azerothcore-wotlk/data/sql/base/db_world/creature_equip_template.sql` and `../WoW-Pvt-Server-Backups/azerothcore-wotlk/data/sql/base/db_world/creature_equip_template.sql`. The copies are byte-identical at SHA-256 `D95423028E4D9CD7068E317BDE66ED847DC8238F4A6AA952D9EA40EF36109A1A`, and their rows include Stockade mappings such as `1696/1 -> 5305, 5305`, `1711/1 -> 2184, 2184`, and `1716/1 -> 2179, 1897`. These are useful leads, but they are not bound to this import: the adjacent archived `creature_template.sql` and `creature.sql` hashes are respectively `e20e921613a02f8a86e997fd0d1d959dbe868bd6886151c8ca5f6c187a549356` and `135c78413da1bbbce0c65ee4ee833c0f19343933b3a0e30f82ff023f37aa6164`, which do not match the pinned inputs. The equipment-table path is also absent from the pinned Git tree. The importer therefore does not bind those mappings. The local WotLK client `../wow335a/Data/patch-A.MPQ/DBFilesClient/Item.dbc` and AzerothCore server `../wow-pvt-server-backups-2/azerothcore-wotlk/data/Item.dbc` have the same SHA-256: `22061a811523790ddd2ee95b36ade2ac6e0c79a8b3121324d32a639acf50567f`. `ItemEntry::ClassID` is field 1 in the core DBC structure and the client DBC uses the matching `Class` field. Item class 2 is `ITEM_CLASS_WEAPON`. The core `Creature::HasWeapon(OFF_ATTACK)` checks only that the offhand virtual item resolves and its class is weapon; it does not check subclass or inventory type. This pins the gate logic and the local DBC bytes, but does not supply the missing equipment-set-to-item mapping for the locked world rows. ## Core behavior reference The behavior audit uses the local read-only AzerothCore checkout at HEAD `e822d718322289dd036fb2daf8867ba41283bf9a`. This is a core-behavior reference, not the world-data commit above. - `CreatureData.h:57` defines `CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK = 0x800`. - `Creature::InitEntry` and `Creature::UpdateEntry` set `DualWieldMode::ENABLED` when the flag is present (`Creature.cpp:565-566, 614-615`). - `Creature::HasWeapon(OFF_ATTACK)` returns true in `ENABLED` mode. In `AUTO` mode it resolves virtual item slot 1 and accepts only a valid item whose `ClassID == ITEM_CLASS_WEAPON` (`Creature.cpp:3299-3317`). The core enum is in `Unit.h:587-590`; `ITEM_CLASS_WEAPON` is 2 at `ItemTemplate.h:293`. - `Creature::SelectLevel` initializes both base and offhand from the same generated creature range (`Creature.cpp:1540-1549`). `Creature::CalculateMinMaxDamage` uses the same range, template variance, attack-power contribution, and damage modifier for either hand (`StatSystem.cpp:1119-1167`). `UNIT_MOD_DAMAGE_OFFHAND` applies the default 0.5 total damage factor (`Unit.cpp:401`). - Both swing timers use `CreatureTemplate.BaseAttackTime` (`Creature.cpp:617-618`). Ordinary white swings add 19 percentage points to miss chance when an offhand is available (`Unit.cpp:15288-15290`). The existing Dual Wield source contract supplies the half-swing initial offset and 200 ms simultaneous-swing presentation stagger. Relevant paths in the reference checkout are `src/server/game/Entities/Creature/CreatureData.h`, `src/server/game/Entities/Creature/Creature.cpp`, `src/server/game/Entities/Unit/StatSystem.cpp`, `src/server/game/Entities/Unit/Unit.cpp`, `src/server/game/Entities/Unit/Unit.h`, `src/server/game/Entities/Item/ItemTemplate.h`, and `src/server/shared/DataStores/DBCStructure.h`. ## Validation and limits `npm test -- --run src/game/enemyAutomaticOffhand.test.ts` passes four focused tests. npm's `pretest` hook also ran the existing loot-catalog generator; it reported the existing 9-boss/23-item catalog, with no loot-file change. The flag path is implemented from the locked creature-template data. The AUTO path is implemented as a strict, reusable item-class gate, but no locked world equipment mapping currently supplies it with item IDs. Exact automatic offhand availability for the imported `equipment_id` and template-default cases remains unresolved until a matching equipment-template source is pinned or imported.