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

7.9 KiB

Enemy Defensive and Battle Stance audit — 2026-10-05

This audit covers the Stockade enemy stance casts Defensive Stance 7164 and Battle Stance 7165. Their form auras are state, not direct damage. The reviewed profile-specific rows and the ActiveAura-supported subset are in src/game/enemyStances.ts.

Pinned client rows

The Ascension source is the recipe compiler's pinned 3.3.5a patch-D table:

../LadiksMPQEditor/client-current/area-52/patch-D/DBFilesClient/Spell.dbc

SHA-256: 2f77f18b91ffbae32a19568e4bc05ec78ee384b6ba8dd2a6e05f0e97abbb2dce.

Epoch uses the compiler's pinned table:

../HealerMan-Storage/pipeline-work/dungeons/epoch-client/DBFilesClient/Spell.dbc

SHA-256: d5cce1a83550dcfa9eb2f0251dbb11fd24c272534b2b1a9b230924a44d817ab3. Both profiles use the compiler's shared patch-S SpellDuration.dbc fallback for duration indices because neither client folder contains that table. Its SHA-256 is eeae44ef0185924f3d6ec57ab2701c66566db86e51ab0bae017a9449126eb3a0.

The two NPC casts have the same Spell.dbc behavior in both profiles:

Spell Effect row (effect/aura/base/misc/trigger) Duration Stance masks
7164 Defensive Stance 6/36/0/18/0 180,000 ms required 0/0, excluded 0/0
7165 Battle Stance 6/36/0/17/0 permanent (-1) required 0/0, excluded 0/0

Both have range index 1, school mask 1, damage class 0 for 7164 and 2 for 7165, dispel 0, stack amount 0, and Attribute0 327696. Their equipped-item class is -1 with subclass and inventory masks 0. The direct effects target self (targetA=1, targetB=0), use radius index/value 0, mechanic 0, and period 0. That attribute value does not include AzerothCore's SPELL_ATTR0_NOT_SHAPESHIFT bit (0x00010000). The casts themselves therefore have no source stance mask or “not while shapeshifted” restriction in these rows. This statement applies to 7164 and 7165 only; it does not infer restrictions for unrelated abilities.

Server-linked passive rows

The historical AzerothCore form handler links the forms to passive spells in src/server/game/Spells/Auras/SpellAuraEffects.cpp:

  • form 18 → Defensive Stance Passive 7376;
  • form 17 → Battle Stance Passive 21156.

This is a server-side mapping. Neither stance cast has a nonzero EffectTriggerSpell in either profile. Both child rows have Attribute0 327888, the passive flag, and zero required/excluded stance masks. Their actual effect rows differ by client profile:

Child passive Ascension patch-D Epoch client
7376 Defensive Stance Passive 6/87/-10/127/0, 6/79/-5/127/0, 6/4/+42/127/0 6/87/-10/127/0, 6/79/-5/127/0, 6/10/+45/127/0
21156 Battle Stance Passive 6/10/0/127/0, 6/280/0/0/0 6/10/-20/127/0

Rows use effect/aura/base points/misc/trigger. The final value in each row is zero: these passive rows also have no triggered child spell. A separate player stance spell such as 2457 is not a substitute for these NPC rows; its own profile rows contain additional effects and are not the source cast IDs here.

The passive scalar fields are identical in both profiles: dispel 0, duration index 0, range index 1, school mask 1, damage class 0, stack amount 0, equipped-item class -1 with subclass and inventory masks 0, and required and excluded stance masks 0/0. Each active effect is type 6, targets self (targetA=1, targetB=0), has mechanic 0, radius index/value 0, period 0, and trigger spell 0. The helper pins these scalar fields alongside each profile's effect rows so the campaign compiler can compare the complete child source.

For compiler drift checks, the WDBC scalar field indices are: Dispel=2, Attribute0=4, ShapeshiftMask=12..13, ShapeshiftExclude=14..15, DurationIndex=40, RangeIndex=46, StackAmount=49, EquippedItemClass=68, EquippedItemSubclassMask=69, EquippedItemInventoryMask=70, DamageClass=213, and SchoolMask=225. The three per-effect EffectTriggerSpell fields are 116..118; active effect rows also use Effect=71+j, BasePoints=80+j, Mechanic=83+j, ImplicitTargetA=86+j, ImplicitTargetB=89+j, RadiusIndex=92+j, Aura=95+j, Period=98+j, and MiscValue=110+j. In the campaign compiler, array fields at index 77 or greater shift by six only when a Spell.dbc has at least 239 fields. Both pinned client tables have 234 fields, so none of these indices shift for the current Ascension or Epoch data; stance and equipped-item fields are below 77 and never use that localized-string adjustment.

Aura 79 is percent damage done, aura 87 is percent damage taken, and aura 10 modifies threat. Their miscValue 127 covers all schools. The runtime can therefore apply Defensive Stance's shared -10% damage taken and -5% damage dealt in both profiles. Epoch's passive rows additionally supply +45% threat in Defensive Stance and -20% threat in Battle Stance. HealerMan already has a generic stat: "threat" resolver used by party and summon threat generation, so these profile-specific scalars are retained in the helper. Current combat threat tables record party and summon threat against hostile mobs; hostile NPCs have no outgoing-threat consumer in the current combat runtime. The two threat modifiers therefore do not change direct enemy hits, party threat, or the current threat tables.

Ascension's aura 4 row on 7376 is DUMMY with base points +42. No spell-specific HandleAuraDummy case, server script registration, linked spell, or triggered child was found for 7376, so its meaning is not guessed. Aura 280 on Ascension's 21156 row has base points 0; it has no numerical delta and is kept as source metadata without an unsupported-effect warning. Aura 10 with base points 0 on that same row is also a sourced zero. No defensive or battle stance row is treated as an NPC direct hit.

Apply, replacement, expiry, and removal

In SpellAuraEffects.cpp, applying a new shapeshift removes an existing shapeshift aura before setting the new form. The form boost handler applies the mapped passive only when the form changes and removes that owned passive when the stance aura is removed. The two stances are therefore mutually exclusive; the helper uses the enemy-warrior-stance exclusive group to preserve that state. Reapplying 7164 refreshes its timed form aura; 7165 remains until another form replaces it or it is explicitly removed.

When a warrior stance aura is removed, AzerothCore caps Rage to its computed stance removal value. Player-only spellbook bonuses contribute to that value; an ordinary NPC has no player spellbook contribution, so the default cap is zero. On expiry or removal with no replacement stance, the server also clears the form. The current enemy helper records the sourced form and duration; it does not invent a creature Rage baseline or treat the passive DUMMY row as resource regeneration. Its aura uses the shared enemy-source-spell-<id> ID format and carries utility tags for the form and linked passive ID, allowing existing source-aura cleanup and telemetry to identify it.

Historical source checked

Read-only reference: ../wow-pvt-server-backups-2/azerothcore-wotlk.

  • src/server/game/Spells/Auras/SpellAuraEffects.cpp: form-to-passive mapping, child aura cleanup, replacement of existing shapeshift auras, and stance Rage reset on removal.
  • src/server/game/Spells/SpellInfo.cpp: CheckShapeshift reads ShapeshiftMask, ShapeshiftExclude, and NOT_SHAPESHIFT.
  • src/server/game/Spells/Auras/SpellAuraDefines.h: aura IDs 4, 10, 79, 87, and 280.
  • src/server/game/Combat/ThreatManager.cpp: aura 10 is consumed as a multiplicative threat modifier by school mask.
  • src/server/game/Spells/Auras/SpellAuraEffects.cpp and the server scripts plus SQL update tree: no 7376-specific dummy effect or linked-spell consumer was found.

The targeted helper tests check both profile rows, exclusivity, supported damage/threat modifiers, and the permanent versus timed duration. No full test suite, build, or live combat run is part of this source audit.