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

4.4 KiB

Utility spell full-hit blocking — 2026-10-06

Supported source contract

src/game/enemyUtilitySpellBlocking.ts records one complete utility-spell contract: WoW 3.3.5a Shield Bash, selected spell/rank 72. The local generated class data has this rank as a physical melee spell with attributes0=327696 and attributes3=8 (COMPLETELY_BLOCKED). Its two present Spell.dbc effects are effect 68 (mechanic 26, hostile target) and effect 64 (hostile target, triggering spell 29703). Neither is a direct-damage effect in the pinned AzerothCore SpellMgr list. The source helper supplies only the two known blocking-relevant AttributesCu bits and records both as clear; it does not invent the remainder of that custom word.

The contract requires the selected spell ID and source profile to be exactly 72 and wow335, plus the same DBC defense scalars and a classified hit-table phase. Other Shield Bash ranks, Ascension/Epoch rows, missing metadata, or different signatures use the caller's current fallback. No generic inference is made from COMPLETELY_BLOCKED alone.

Runtime disposition contract

enemyUtilitySpellBlockDisposition consumes a hit result already resolved by the caller. It only handles player or party casts against an ordinary enemy NPC. A resolved block suppresses the whole cast-recipient utility effect list, including interrupt and triggered aura/debuff effects. miss, dodge, parry, or resist also suppress application for this exact hit-table spell; hit or crit allows its utility effects. Missing/unresolved outcomes and unsupported sources return fallback, preserving the caller's existing behavior.

The hit-table path is enabled by melee/ranged damage class, physical school, the COMPLETELY_BLOCKED attribute, and explicit evidence that CU_DIRECT_DAMAGE is absent. NO_ACTIVE_DEFENSE and ALWAYS_HIT override blocking. Facing, active non-melee cast, controlled state, no-block template flag, totem, and skill-adjusted positive chance remain the resolver's gates; this helper does not roll or bypass them. Shield Bash's source evidence also shows CU_REQ_CASTER_BEHIND_TARGET clear for its aura-facing exception.

The helper is intended for the existing player/party outbound-to-ordinary-NPC utility writers. NPC outbound casts into player/party targets need the character defender's separate block model and do not consume this contract. Native RoM casters/targets, character defenders, summoned actors, and unknown targets stay on their existing paths. The helper contains no shared runtime types and does not mutate combat state.

The shared player cast writer and companion executePartyAbility resolve one source-bound block result before their utility effect loops. A full block skips the entire recipient effect list while retaining the accepted cast's resource cost and cooldown. Telemetry records a zero-amount cast result with the block flag; it emits no fake damage packet. This bounded runtime stage rolls block only and preserves existing other utility hit/avoidance behavior. The helper's avoided-outcome disposition is available for a future sourced full hit table, but this pass does not add miss/dodge/parry/resist rolls.

Validation

src/game/enemyUtilitySpellBlocking.test.ts covers the exact signature, block-before-utility suppression, hit-table misses/dodges/parries/resists, missing rank/source/profile/signature and native/target exclusions. Runtime integration, including the active-cast gate and an unblocked interrupt, belongs to the combatStore.ts and partyRuntime.ts caller changes.

The final enemyUtilitySpellBlocking.integration.test.ts passes four tests: an actual player cast blocks once and retains cost/cooldown; an actively casting NPC cannot block and receives the six-second interrupted-school lockout; a synthetic accepted pending companion cast completes through the real companion executor and blocks; unbound metadata/ranks and native, summoned, character and unknown source defenders stay excluded. The player's utility result and existing completed-cast event are separate telemetry; the block result is not a second completed cast or a damage packet.

The actual parent catalog retains its trigger 29703, but triggeredAbilityBySpellId(29703) is currently null. This pass suppresses trigger execution under a full block; it does not implement or certify an imported/live Daze child. That child import remains a separate catalog gap.