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

121 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).