forked from phenom/healer-man
121 lines
8.0 KiB
Markdown
121 lines
8.0 KiB
Markdown
# Enemy NPC blocking source audit — 2026-10-05
|
||
|
||
## Result
|
||
|
||
The historical AzerothCore source supports ordinary Creature block chance,
|
||
block value, aura, and eligibility without a shield or equipped-item check.
|
||
Its `Unit::GetUnitBlockChance()` returns zero for Totems; otherwise it starts
|
||
at 5 percentage points, adds all `SPELL_AURA_MOD_BLOCK_PERCENT` amounts, and
|
||
floors the result at zero. `Creature::GetShieldBlockValue()` uses
|
||
`level / 2 + uint32(Strength / 20)`.
|
||
|
||
This checkout's ordinary Creature initialization never populates the Strength
|
||
unit stat. `Creature::SelectLevel` reads `CreatureBaseStats` but assigns health,
|
||
mana, base weapon damage, attack power, and ranged attack power only. It does
|
||
not call `SetCreateStat` or `SetStat` for Strength. `Creature::UpdateStats` is
|
||
a no-op. `Unit::GetStat` reads the zero-initialized `UNIT_FIELD_STAT0`, and
|
||
`Object` initializes its unit-value buffer to zero. The core's
|
||
`CreatureBaseStats.Strength` column is loaded but has no use in the historical
|
||
server game code. For a regular, unmodified Creature in this source tree, the
|
||
effective block-value formula is therefore `floor(level / 2)`. The helper
|
||
keeps that source-specific zero Strength conclusion explicit instead of
|
||
importing the unused class-level Strength column.
|
||
|
||
The pinned Ascension and Epoch DBC rows for Improved Blocking (3419) both
|
||
contain aura type 51 (`SPELL_AURA_MOD_BLOCK_PERCENT`), +75 percentage points,
|
||
and a 6,000 ms duration. Applying that aura to a creature raises its base
|
||
block chance from 5% to 80% for six seconds. The source spell row does not
|
||
require a shield. Existing aura contracts remain the source of the profile
|
||
effect value and duration.
|
||
|
||
## Eligibility in the historical core
|
||
|
||
- `CREATURE_FLAG_EXTRA_NO_BLOCK` is `0x10`; it prevents block outcomes for
|
||
creature templates.
|
||
- A normal melee or ranged weapon attack can block only from the front. A
|
||
target with an ignore-hit-direction aura bypasses that facing gate.
|
||
- A target cannot block while performing a non-melee spell cast or while in
|
||
`UNIT_STATE_CONTROLLED` (which includes the controlled/stunned gate).
|
||
- The ordinary melee-outcome path starts with integer hundredths of a
|
||
percentage point, then subtracts `4 * (attackerWeaponSkill -
|
||
defenderMaxSkill)` and requires a positive remaining chance. This is 0.04
|
||
percentage points per skill point: higher attacker weapon skill lowers block
|
||
chance. `GetMaxSkillValueForLevel` is effective level times five. The helper
|
||
maps attacker weapon skill to attacker level times five and creature defense
|
||
maximum to creature level times five. The source path does not exclude
|
||
ranged weapon attacks from block outcomes.
|
||
- The spell hit-table full-block phase has a distinct source gate. `SPELL_ATTR3_COMPLETELY_BLOCKED`
|
||
must be set and `SPELL_ATTR0_CU_DIRECT_DAMAGE` must be absent. `ALWAYS_HIT`
|
||
and `NO_ACTIVE_DEFENSE` short-circuit the block result. Behind-target,
|
||
creature no-block, casting, and controlled checks still apply. A ranged
|
||
damage-class spell disables dodge and parry in this path; it does not by
|
||
itself disable a source-authorized block.
|
||
- Direct physical melee/ranged spells have a later independent damage-block
|
||
phase which can coexist with a crit and does not require `COMPLETELY_BLOCKED`.
|
||
Its skill modifier has the opposite sign. The continuation imports per-rank
|
||
source metadata and implements this phase; see
|
||
[`NPC_SPELL_BLOCKING_2026-10-05.md`](NPC_SPELL_BLOCKING_2026-10-05.md).
|
||
- `IsTotem()` tests the live UNIT_MASK_TOTEM bit on the runtime object. The
|
||
Totem constructor sets this bit; it is not the same data as
|
||
`CREATURE_FLAG_EXTRA_NO_BLOCK` and cannot safely be inferred from a creature
|
||
template without preserving whether the actor is a runtime Totem.
|
||
|
||
## Integration data contract
|
||
|
||
The isolated helper in `src/game/enemyBlocking.ts` takes only values that its
|
||
caller can source or resolve at the point of the attack:
|
||
|
||
- creature level (within the simulator's level-80 boundary);
|
||
- active block-aura percentage-point total, including 3419 when active;
|
||
- creature template `flags_extra` and the runtime `isTotem` bit;
|
||
- whether the party attacker is behind the NPC, whether hit direction is
|
||
ignored, and the target's active non-melee cast/controlled/evading states;
|
||
- attack packet kind. This helper supports only melee and ranged auto-attacks;
|
||
spell and other packets fail closed in this auto-attack helper. The separate
|
||
`canEnemyNpcBlockSpell` helper handles imported source spell metadata;
|
||
- attacker and creature level, mapped to weapon skill and max defense skill at
|
||
level * 5 for the skill adjustment.
|
||
|
||
Use the raw `blockPercent` returned by `resolveEnemyNpcBlockStats` when calling
|
||
`canEnemyNpcBlock`; it applies the skill adjustment to decide whether a block
|
||
can occur. Put `enemyNpcBlockChanceAfterSkill`'s result in the shared defender
|
||
stats so the common resolver uses the adjusted chance exactly once.
|
||
|
||
The integration maps stun/fear/confuse to the core CONTROLLED set. It also
|
||
treats sleep/blind/knockdown/freeze as unable to defend under HealerMan's
|
||
incapacitation model; those extra labels are a simulator approximation rather
|
||
than newly verified core unit states. A plain root does not prevent blocking.
|
||
|
||
Current `MobCombatState` has `level`, `serverEntry`, `activeCast`, status
|
||
records, and positions, but it does not itself contain the template
|
||
`flags_extra`, runtime Totem identity, or a direct
|
||
"attacker is behind" result. This batch's importer now preserves `flags_extra`
|
||
in `CreatureTemplateReference`; the runtime resolves it through `serverEntry`
|
||
and takes actual facing from the registered transform/spatial provider.
|
||
Unknown, summoned, native RoM and character defenders do not inherit this
|
||
ordinary-creature baseline. Historical core can block a spell only when
|
||
`SPELL_ATTR3_COMPLETELY_BLOCKED` is set and
|
||
`SPELL_ATTR0_CU_DIRECT_DAMAGE` is absent; `ALWAYS_HIT`, `NO_ACTIVE_DEFENSE`,
|
||
facing, template flag, cast, and controlled checks also matter. Shared
|
||
player/party defenders retain their existing source-provided block stats.
|
||
|
||
## Historical source locations
|
||
|
||
Read-only reference tree: `../wow-pvt-server-backups-2/azerothcore-wotlk`.
|
||
|
||
| Source path | Relevant code |
|
||
| --- | --- |
|
||
| `src/server/game/Entities/Unit/Unit.cpp` | `RollMeleeOutcomeAgainst` checks facing, `NO_BLOCK`, cast/control state, and skill-adjusted chance (around lines 3049–3098); `MeleeSpellHitResult` derives `canBlock` from spell attributes and applies facing/flag/state/skill gates (around lines 3327–3498); `GetUnitBlockChance` implements the 5% + aura creature baseline and Totem exception (around lines 3873–3895). |
|
||
| `src/server/game/Entities/Creature/Creature.h` | `Creature::GetShieldBlockValue` is `GetLevel() / 2 + uint32(GetStat(STAT_STRENGTH) / 20)` (around lines 158–161). |
|
||
| `src/server/game/Entities/Creature/Creature.cpp` | `Creature::SelectLevel` selects level data and initializes health, mana, weapon damage, and AP but not Strength (around lines 1497–1557). |
|
||
| `src/server/game/Entities/Unit/StatSystem.cpp` | `Creature::UpdateStats` returns without changing stats (around lines 1036–1039). |
|
||
| `src/server/game/Entities/Unit/Unit.h` | `GetStat` reads `UNIT_FIELD_STAT0`; `GetMaxSkillValueForLevel` returns effective level times five (around lines 1058–1059 and 1204). |
|
||
| `src/server/game/Entities/Unit/Unit.cpp` | `Unit` initializes its create-stat values to zero (around line 413). |
|
||
| `src/server/game/Entities/Object/Object.cpp` | Object value fields, including Unit stat fields, start zeroed (around lines 115–116). |
|
||
| `src/server/game/Entities/Creature/CreatureData.h` | `CREATURE_FLAG_EXTRA_NO_BLOCK = 0x10`; `CreatureBaseStats` includes a Strength column (around lines 50 and 297–305). |
|
||
| `src/server/game/Entities/Totem/Totem.cpp` | Runtime Totem construction sets `UNIT_MASK_TOTEM` (around lines 27–29). |
|
||
| `src/server/game/Entities/Unit/Unit.h` | `IsTotem()` checks the live `UNIT_MASK_TOTEM` (around line 799). |
|
||
|
||
The exact pinned spell signatures and hashes for 3419 in both profiles are
|
||
recorded in [`STOCKADE_ENEMY_BUFFS_2026-10-05.md`](./STOCKADE_ENEMY_BUFFS_2026-10-05.md).
|