29 lines
5.8 KiB
Markdown
29 lines
5.8 KiB
Markdown
# Stockade Chain Lightning fan-out — 2026-10-05
|
||
|
||
## Verified source contract
|
||
|
||
The Stockade attack is Hamhock (creature entry 1717), SmartAI row 0, spell 421. The imported row in `src/game/generated/azerothCoreWorldAi.json` casts spell 421 on `target_type=2` (`SMART_TARGET_VICTIM`); the campaign import preserves it as `target: "primary"`, range 30, nature school, and its existing 1.35 attack damage multiplier. Its cast timing remains the imported 1-second initial delay and 4–7-second repeat interval. No source row targets the whole party as the initial hit.
|
||
|
||
Both pinned Spell.dbc profiles were read directly and match their recorded SHA-256 values:
|
||
|
||
| Profile | SHA-256 | Spell 421 effect 0 | Total targets | Chain damage multiplier |
|
||
| --- | --- | --- | ---: | ---: |
|
||
| Ascension patch-D | `2f77f18b91ffbae32a19568e4bc05ec78ee384b6ba8dd2a6e05f0e97abbb2dce` | `SCHOOL_DAMAGE` (effect 2), `TARGET_UNIT_TARGET_ENEMY` (target A 6), range index 4, nature school mask 8, magic damage class 1 | `EffectChainTarget[0]` (field 104) = 3 | `EffectDamageMultiplier[0]` (field 216) = 0.6 |
|
||
| Epoch pinned client | `d5cce1a83550dcfa9eb2f0251dbb11fd24c272534b2b1a9b230924a44d817ab3` | Same target, range index, school, and damage class; source base points differ | `EffectChainTarget[0]` (field 104) = 3 | `EffectDamageMultiplier[0]` (field 216) = 0.7 |
|
||
|
||
Field 216 is named `EffectDamageMultiplier` in the matching AzerothCore `DBCStructure.h` and is exposed as `SpellEffectInfo::DamageMultiplier`; the comment identifies the DBC field as `m_effectChainAmplitude`. AzerothCore's spell resolver applies the factor after each successive chain hit. The resulting damage factors are `1.0, 0.6, 0.36` for Ascension and `1.0, 0.7, 0.49` for Epoch. The currently imported Stockade record uses the Ascension profile. `EffectChainTarget=3` means three total hits, including the explicit primary target: core requests two additional chain targets.
|
||
|
||
The source range index is 4; the existing compiler pin resolves it to 30 yards. Spell.dbc stores the range index, while this task's chain jump distance comes from the matching core behavior. `Spell.cpp::SearchChainTargets` uses a 10-yard jump radius for magic spells. It searches a candidate pool centered on the primary with radius `jumpRange × total target count` (30 yards here), then selects the nearest eligible target within 10 yards of the previous hit at each hop. The chain starts from the primary target because neither pinned profile has `SPELL_ATTR2_CHAIN_FROM_CASTER` (0x1000). It removes the primary from the candidate pool and removes every chosen candidate, so a target cannot be hit twice. Each bounce must have line of sight from the previous hit. In AzerothCore, the ordinary hostile-target cast path validates the primary's cast range and line of sight before chain selection; HealerMan integration must preserve that primary-target contract. A dead primary at resolution ends the attack instead of choosing a replacement.
|
||
|
||
These runtime rules were checked against the read-only matching core source at `../wow-pvt-server-backups-2/azerothcore-wotlk/src/server/game/Spells/Spell.cpp` (`SelectImplicitChainTargets`, `SearchChainTargets`, and damage multiplier application), `SpellInfo.cpp` (`ChainTarget` and damage multiplier mapping), and `src/server/shared/DataStores/DBCStructure.h` (field names). The imported database snapshot is pinned to commit `4d9d1d4b5723e819c5c390f6ca1177283dbfb8e3` by `azerothCoreWorldAi.json`.
|
||
|
||
## Helper and runtime integration
|
||
|
||
`src/game/enemyChainTargeting.ts` provides pure ordered target selection. It accepts a primary target, current living candidates with positions, target count, jump range, per-bounce damage multiplier, and a line-of-sight function. It returns the primary first and each reachable hop with its cumulative damage factor. The focused tests cover nearest-hop traversal, both source decay profiles, living filtering, LOS filtering, no repeats, search-pool range, and the primary-target contract.
|
||
|
||
For the compiler/runtime integration, keep these sourced values together on the imported attack as `chain: { targetCount, jumpRange, damageMultiplier }`. Parse `EffectChainTarget[0]` from field 104 and `EffectDamageMultiplier[0]` from field 216 (using the parser's localized-field shift helper when applicable); pin only spell 421's chain signature so unrelated source signatures do not change. Emit target count 3 and jump range 10 for the verified Ascension and Epoch signatures, with profile-specific multipliers 0.6 and 0.7. Keep the existing primary-target attack, 30-yard cast range, cooldowns, and attack scalar.
|
||
|
||
At resolution, validate the cast's primary target through the existing hostile-target range/LOS path, then pass that target and current living registered party actors to `selectEnemyChainTargets`. For each returned hit, multiply the existing attack damage by that hit's `damageMultiplier` and resolve the hit through the existing per-target damage, mitigation, resistance, and event path. This keeps each hit in normal combat accounting while the primary receives factor 1 and subsequent hits receive the source decay. Keep chain metadata absent on attacks without a verified chain signature. This subtask does not change the shared compiler, attack schema, combat runtime, generated catalogs, loot, ammo, level cap, or readiness policy; the integrating worker owns those reserved files.
|
||
|
||
The parent integration is complete in the shared compiler, attack schema and combat runtime. The three helper checks and five runtime cases pass, including actor-local player/NPC defenses in both stat modes, hop LOS/range, the original cast victim after a threat change and a secondary target outside the caster's home leash. Primary aggro eligibility remains unchanged. Final build and full Stormwind Stockade evidence are recorded in `STOCKADE_ENEMY_MECHANICS_2026-10-05.md` and its linked playtest report.
|