Document Batch 4A Pass 8 animation archaeology: AnimNotify callbacks and ability cleanup wiring.

Resolves UseAbility* callers via shared AN_Ability notifies, qualifies Pass 6 ability statuses, and records AnimBP/Montage dependencies without modifying Unreal assets.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-16 21:00:59 -07:00
co-authored by Cursor
parent 0d045d142f
commit 4e926d1ccc
6 changed files with 327 additions and 334 deletions
@@ -2,14 +2,14 @@
**Status: Draft**
- **Scope:** Shared `BP_PlayerCharacterBase` ability architecture (Batch 2) plus per-character Ability 001004 connected-path reconstruction (Batch 3 Pass 6).
- **Inspection method:** Read-only BlueprintTools `find_nodes` / `get_node_infos` / `get_connected_subgraph` / `read_graph_dsl`; AssetTools deps; ObjectTools CDO samples.
- **Scope:** Shared `BP_PlayerCharacterBase` ability architecture (Batch 2) plus per-character Ability 001004 connected-path reconstruction (Batch 3 Pass 6), qualified by animation-notify wiring (Batch 4A Pass 8).
- **Inspection method:** BlueprintTools connected subgraphs (Pass 6); AnimNotify `Received_Notify` DSL + montage dependency/referencer graphs (Pass 8).
- **Evidence classifications used:** Observed Implementation, Documented Intent, Observed Runtime Behavior, Inferred, Unknown.
- **Tool or visibility limitations:** CustomEvent Reliable/Unreliable flags not exposed; comment-box regions not found via `find_nodes(title=Comment)`; Montage notify tracks not deep-inspected (Pass 8).
- **Tool or visibility limitations:** CustomEvent Reliable flags not exposed; Montage Notifies array / trigger times unreadable via ObjectTools; AnimGraph DSL empty.
- **Last updated:** 2026-07-16
- **Current phase:** Phase 1 — Static Architecture
- **Last completed pass:** Pass 6 (Batch 3)
- **Next pass expected to contribute:** Batch 4 Pass 7 (GAS systemwide) / Pass 8 (AnimBP/Montage notifies)
- **Last completed pass:** Pass 8 (Batch 4A)
- **Next pass expected to contribute:** Pass 7 (GAS) when authorized
## Draft tracking
@@ -18,20 +18,21 @@
- Base Server_Ability_* stubs
- Multicast FX helpers
- Death / respawn / HUD (DEPRECATED floats)
- **Per-character Ability 001004 connected paths for all five playables**
- Per-character Ability 001004 connected paths for all five playables
- Parent-call inventory
- Resource / damage / projectile inventories
- Montage dependency seeds
- **Pass 8:** UseAbility* callers resolved (`AN_Ability_001004`); `AN_AttackComplete` cleanup; Pass 6 reclassification table in `10`
### Pending sections
- Cross-character migration matrix (Pass 11 → doc 07)
- Montage notify → UseAbility exact wiring (Pass 8)
- Runtime verification of CDO damage/mana values (PIE)
- Interrupt cleanup if `AN_AttackComplete` skipped
### Evidence still required
- Who fires UseAbility* (anim notifies vs other) — Requires Manual Editor Inspection / Pass 8
- FCanUseMana implementation body (function on base — not re-walked this batch beyond call sites)
- Client_Charge timeline details beyond ChargeTimeline / LineTrace / SetActorLocation
- Notify trigger times (Manual Editor Inspection)
- FCanUseMana implementation body (call sites only)
- Client_Charge timeline numeric details
---
@@ -44,12 +45,17 @@ flowchart TD
SBegin -->|base| Print[PrintString Override]
SBegin -->|child override| Child[Character body]
Child --> MC[Multicast_PlayMontage]
Child --> Use[UseAbilityN Authority]
MC --> AN[AN_Ability_00N notify]
AN --> Use[UseAbilityN Authority]
Use --> Dmg[ApplyDamage or RecoverHealth]
MC --> ANC[AN_AttackComplete]
ANC --> Clear[SetIsAttacking clears CurrentlyAttacking]
```
**Observed Implementation:** No `UGameplayAbility` activation. Reliability: **Unknown — MCP property visibility limitation**.
**Pass 8:** `UseAbility*` is invoked from AnimNotify Blueprints (`AN_Ability_001004`), not from AnimBP EventGraphs. `Multicast_PlayMontage` completion pins are unwired — cleanup for notify-driven attacks uses `AN_AttackComplete``SetIsAttacking`. Full animation tables: `10-AnimationDependencies.md`.
| Base event | Body |
|---|---|
| Server_Ability_*_Begin | PrintString `"Override Ability N"` |
@@ -76,10 +82,10 @@ flowchart TD
| Slot/System | Entry point | Parent called | Server path | Multicast/client path | Validation | Resource | Damage/effect | Animation | End/cleanup | Status | Evidence |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Ability 001 | Server_Ability_001_Begin; UseAbility001 | No | Gate !attacking && !dead → CurrentlyAttacking → SetPlayerCanMove(true) → Multicast_PlayMontage(random Ability001Montages) | Multicast_PlayMontage | Gate only | None | UseAbility001 Authority: BoxOverlap(90,50,100) forward×150 → ApplyDamage(DEPRECATED_Ability001Damage) | Attack_A/B/C/D_Fast_Montage | Server_Ability_001_End not implemented locally | **Implemented** (End Partial) | get_connected_subgraph |
| Ability 002 | Server_Ability_002_Begin; Server_Ability_002_End | No | Begin: gate → CurrentlyAttacking → CanMove false → IsBlocking true | None in Begin | Gate | None | Block state only (no ApplyDamage in Begin) | None in Begin (IsBlocking likely AnimBP) | End: CurrentlyAttacking=false → CanMove true → IsBlocking false | **Partial** (block state; no hit absorb path traced) | subgraph Event_5 / Event_7 |
| Ability 003 | Server_Ability_003_Begin; Charge; Client_Charge; ChargeBox overlap | No | Gate → FCanUseMana(DEPRECATED_Ability003ManaCost) → CurrentlyAttacking → CanMove false → ChargeBox QueryOnly → Charge + Client_Charge | Client_Charge | Gate + mana | DEPRECATED_Ability003ManaCost via FCanUseMana | ChargeBox Authority: NOT tag Player → ApplyDamage(DEPRECATED_Ability003Damage) | ChargeTimeline + LineTrace + SetActorLocation lerp | Ends not locally implemented for 003 | **Implemented** (charge + overlap damage) | subgraph Event_6 / Charge / ChargeBox |
| Ability 004 | Server_Ability_004_Begin; UseAbility004 | No | Gate → FCanUseMana(DEPRECATED_Ability004ManaCost) → CurrentlyAttacking → CanMove false → Multicast_PlayMontage(Paladin_Ability004_Montage) | Multicast_PlayMontage; UseAbility004: Multi_SpawnParticle (EmitterTemplate empty) + Multicast_PlaySFX(MagicSpell_01_Cue) | Gate + mana | DEPRECATED_Ability004ManaCost | UseAbility004 Authority: SphereOverlap radius 300 → ApplyDamage(DEPRECATED_Ability004Damage) | Paladin_Ability004_Montage | End not local | **Implemented** (FX particle template empty) | subgraph Event_8 / Event_11 |
| Ability 001 | Server_Ability_001_Begin; UseAbility001 | No | Gate !attacking && !dead → CurrentlyAttacking → SetPlayerCanMove(true) → Multicast_PlayMontage(random Ability001Montages) | Multicast_PlayMontage | Gate only | None | UseAbility001 Authority BoxOverlap ApplyDamage — **caller: AN_Ability_001** | Attack_A/B/C/D_Fast_Montage | **AN_AttackComplete** → SetIsAttacking (no local End) | **Implemented** (Pass 8 qualified) | Pass 6 + Pass 8 |
| Ability 002 | Server_Ability_002_Begin; Server_Ability_002_End | No | Begin: gate → CurrentlyAttacking → CanMove false → IsBlocking true | ABP Block (no Montage) | Gate | None | Block state only | ABP_Paladin Block via IsBlocking | End clears attacking/CanMove/IsBlocking | **Partial** | Pass 6 + Pass 8 |
| Ability 003 | Server_Ability_003_Begin; Charge; Client_Charge; ChargeBox overlap | No | Gate → FCanUseMana → Charge + Client_Charge | Client_Charge | Gate + mana | DEPRECATED_Ability003ManaCost | ChargeBox Authority ApplyDamage | ChargeTimeline (not Montage) | Not notify-driven | **Implemented** | Pass 6 |
| Ability 004 | Server_Ability_004_Begin; UseAbility004 | No | Gate → FCanUseMana → Multicast_PlayMontage(Paladin_Ability004_Montage) | Multicast_PlayMontage | Gate + mana | DEPRECATED_Ability004ManaCost | UseAbility004 Authority AoE — **caller: AN_Ability_004**; empty particle | Paladin_Ability004_Montage | **AN_AttackComplete** cleanup | **Implemented** (Pass 8 qualified; FX hole) | Pass 6 + Pass 8 |
| Basic attack | Same as Ability 001 | No | Same | Same | Same | Same | Same | Attack_* montages | Same | **Implemented** (=001) | — |
| Passive | — | — | — | — | — | — | — | — | — | **Missing** | — |
| Death | Inherited PlayerDeath | — | Base | Base | — | DEPRECATED health (if damaged via AnyDamage — Paladin has no local AnyDamage) | Base | Base | Base | **Inherited** | list_events |
@@ -97,7 +103,7 @@ flowchart TD
| Slot/System | Entry point | Parent called | Server path | Multicast/client path | Validation | Resource | Damage/effect | Animation | End/cleanup | Status | Evidence |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Ability 001 | Server_Ability_001_Begin; UseAbility001 | No | Gate → CurrentlyAttacking → CanMove true → Multicast_PlayMontage(random Ability001Montage) | Multicast_PlayMontage | Gate | None | UseAbility001 Authority: BoxOverlap ApplyDamage(DEPRECATED_Ability001Damage) | A_Hu_F_Combat_2H_Attack* montages | End not local; CurrentlyAttacking cleared via base SetIsAttacking (Inferred) | **Implemented** | subgraph |
| Ability 001 | Server_Ability_001_Begin; UseAbility001 | No | Gate → CurrentlyAttacking → CanMove true → Multicast_PlayMontage(random Ability001Montage) | Multicast_PlayMontage | Gate | None | UseAbility001 Authority BoxOverlap ApplyDamage**caller: AN_Ability_001** | A_Hu_F_Combat_2H_Attack* montages | **AN_AttackComplete** SetIsAttacking | **Implemented** (Pass 8 confirmed) | Pass 6 + Pass 8 |
| Ability 002004 | Inherited only | N/A | Base PrintString | — | — | — | — | — | Base empty End | **Stub** (inherited) | list_events |
| Basic attack | Ability 001 | No | Same | Same | Same | Same | Same | Attack montages | Same | **Implemented** (=001); **Observed Runtime Behavior:** known working melee | parity brief + subgraph |
| Passive | — | — | — | — | — | — | — | — | — | **Missing** | — |
@@ -116,10 +122,10 @@ flowchart TD
| Slot/System | Entry point | Parent called | Server path | Multicast/client path | Validation | Resource | Damage/effect | Animation | End/cleanup | Status | Evidence |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Ability 001 | Server_Ability_001_Begin | No | Gate → CurrentlyAttacking → CanMove false → Multicast_PlayMontage(random Ability001Montages) | Multicast_PlayMontage | Gate | None | **No UseAbility001** — no hit path | Cleric_PrimaryAttack_* | End not local | **Partial** (montage only) | subgraph |
| Ability 002 | Server_Ability_002_Begin; UseAbility002 | No | Gate → FCanUseMana(Ability002ManaCost) → CurrentlyAttacking → CanMove true → Multicast_PlayMontage(Cleric_Ability002_Montage) | Multi_SpawnParticle (EmitterTemplate **empty**); UseAbility002 Authority heal | Gate + mana | DEPRECATED_Ability002ManaCost | SphereOverlap 400 tag Player → DEPRECATED_RecoverPlayerHealth(Ability002Damage); self heal | Cleric_Ability002_Montage | End not local | **Partial** (heal works; particle empty) | subgraph |
| Ability 003 | Server_Ability_003_Begin; Server_Ability_003_End; ConcentrateHandle; EndConcentrate | No | Mana≥cost → DivineCircleActive + SetTimer 0.25 ConcentrateHandle | ConcentrateHandle: Multicast_PlayMontage(Cleric_Ability003) + ApplyDamage(Ability003Damage) on non-Player | Gate + mana | DEPRECATED_Ability003ManaCost | ApplyDamage DoT | Cleric_Ability003_Montage (in timer) | End → EndConcentrate clears timer/state | **Implemented** | subgraph + DSL |
| Ability 004 | Server_Ability_004_Begin; UseAbility004 | No | Gate → FCanUseMana called but Branch Condition=**literal true** (CanUse unwired) → montage | UseAbility004: find ClosestEnemy tag Enemy → SpawnActor **Class empty** | Gate; mana bypassed | DEPRECATED_Ability004ManaCost (unused result) | Spawn params Damage=20 HealPerHit=10 (unwired class) | Cleric_Ability004_Montage | End not local | **Partial / Existing Baseline Defect** | subgraph |
| Ability 001 | Server_Ability_001_Begin | No | Gate → CurrentlyAttacking → CanMove false → Multicast_PlayMontage(random Ability001Montages) | Multicast_PlayMontage | Gate | None | **AN_Ability_001 present but UseAbility001 unimplemented** — no hit | Cleric_PrimaryAttack_* | AN_AttackComplete still clears attacking | **Partial** (montage + dead notify) | Pass 6 + Pass 8 |
| Ability 002 | Server_Ability_002_Begin; UseAbility002 | No | Gate → FCanUseMana → Multicast_PlayMontage(Cleric_Ability002_Montage) | Multicast_PlayMontage | Gate + mana | DEPRECATED_Ability002ManaCost | UseAbility002 Authority heal — **caller: AN_Ability_002**; empty particle | Cleric_Ability002_Montage | AN_AttackComplete | **Partial** (notify-driven heal; FX hole) | Pass 6 + Pass 8 |
| Ability 003 | Server_Ability_003_Begin; Server_Ability_003_End; ConcentrateHandle; EndConcentrate | No | Mana≥cost → DivineCircleActive + SetTimer 0.25 ConcentrateHandle | ConcentrateHandle plays montage + ApplyDamage; ABP Divine Circle state | Gate + mana | DEPRECATED_Ability003ManaCost | ApplyDamage in timer; **AN_Ability_003 → UseAbility003 dead/vestigial** | Cleric_Ability003_Montage (timer byproduct) | EndConcentrate | **Implemented** (timer-driven; vestigial notify) | Pass 6 + Pass 8 |
| Ability 004 | Server_Ability_004_Begin; UseAbility004 | No | Gate → FCanUseMana called but Branch Condition=**literal true** → montage | Multicast_PlayMontage | Gate; mana bypassed | DEPRECATED_Ability004ManaCost unused | UseAbility004 — **caller: AN_Ability_004**; SpawnActor Class empty | Cleric_Ability004_Montage | AN_AttackComplete | **Partial / Existing Baseline Defect** | Pass 6 + Pass 8 |
| Basic attack | Ability 001 montage | No | Same | Same | Same | — | Missing hit | PrimaryAttack montages | — | **Partial** | — |
| Passive | — | — | — | — | — | — | — | — | — | **Missing** | — |
| Death / HUD | Inherited | — | Base | Base | — | — | — | — | — | **Inherited** | — |
@@ -195,9 +201,9 @@ Reliability: **Unknown — MCP property visibility limitation** for all CustomEv
| DEPRECATED_CurrentHealth | Amazonian AnyDamage | Subtract + death check |
| DEPRECATED_RecoverPlayerHealth | Cleric UseAbility002 | Interface message |
| CurrentlyAttacking / IsDead | All ability gates | Replicated on base |
| IsBlocking | Paladin 002 Begin/End | Local; AnimBP dependency Unknown |
| PerformingWhirlwind | Amazonian AnyDamage | Get only — never Set |
| DivineCircleActive / TimerHandle | Cleric 003 | Timer loop |
| IsBlocking | Paladin 002 Begin/End | Local; **ABP_Paladin Block state** (Pass 8 Observed Implementation) |
| PerformingWhirlwind | Amazonian AnyDamage | Get only — never Set; **not in AnimBP/montages** (Pass 8) |
| DivineCircleActive / TimerHandle | Cleric 003 | Timer loop; **ABP_Cleric Divine Circle state** reads flag (Pass 8) |
**No ASC attribute reads/writes in ability graphs.** No GameplayAbility grant/activate.
@@ -208,10 +214,12 @@ Reliability: **Unknown — MCP property visibility limitation** for all CustomEv
- Client IA → Server Begin: Observed Implementation
- Damage on UseAbility* / ChargeBox: Authority SwitchHasAuthority — Observed Implementation
- Multicast used for montage/SFX/particle presentation — Observed Implementation
- **Pass 8:** Multicast montage → AnimNotifies fire on evaluating machines; gameplay still Authority-gated inside UseAbility* / SetIsAttacking
- **Pass 8 static risk:** hit windows depend on server evaluating Multicast montage notifies; interrupt may skip `AN_AttackComplete` (completion pins unwired)
- Cleric Ability 004: mana CanUse **unwired** (literal true) — Existing Baseline Defect
- Cleric Ability 004: SpawnActor Class empty — Existing Baseline Defect
- Multi_SpawnParticle EmitterTemplate empty (Paladin 004, Cleric 002) — Partially Implemented
- End events missing for most slots — Incomplete Refactor
- End events missing for most slots — Incomplete Refactor (cleanup often via `AN_AttackComplete` instead)
- Amazonian/Paladin Ability 001: no mana cost — Observed Implementation
- Dual HUD still uses DEPRECATED floats on Server_UpdateHUD — Incomplete Refactor (Batch 2)
@@ -221,15 +229,22 @@ Reliability: **Unknown — MCP property visibility limitation** for all CustomEv
| Classification | Characters / slots |
|---|---|
| **Implemented** | Paladin 001, 003, 004 (with noted FX holes); Amazonian 001; Cleric 003 |
| **Partial** | Paladin 002; Cleric 001, 002, 004; Amazonian End/whirlwind |
| **Implemented** | Paladin 001, 003, 004 (FX hole on 004); Amazonian 001; Cleric 003 (timer; vestigial AN_Ability_003) |
| **Partial** | Paladin 002; Cleric 001 (dead notify), 002 (heal + FX hole), 004 (broken spawn); Amazonian whirlwind |
| **Stub** | Wizard 001004; Gunslinger 001004 (inherited PrintString) |
| **Missing** | Wizard/Gunslinger combat bodies; most Ends; UseAbility on Wizard/Gunslinger |
| **Present but Unconnected** | NS_Wizard_Ability001; GE_* DefaultAttributes orphans; BP_PlayerProjectile; PerformingWhirlwind setter |
| **Missing** | Wizard/Gunslinger combat bodies; most Server Ends |
| **Present but Unconnected** | NS_Wizard_Ability001; GE_* orphans; BP_PlayerProjectile; PerformingWhirlwind setter; Cleric_Hu_M_Spell_Staff_Up_Impact_Montage (0 refs) |
| **Deprecated connected** | All DEPRECATED_* damage/mana/health in active paths |
| **Notify Present; Receiver Unimplemented** | Cleric AN_Ability_001 → UseAbility001; Cleric AN_Ability_003 → UseAbility003 |
---
## Pass 8 animation callback note
Full tables and reclassification: `10-AnimationDependencies.md`. Primary status labels unchanged except qualifications above; no silent rewrites.
---
## Migration relevance (inventory only)
Replacement targets: IA→Server custom events; ApplyDamage/RecoverPlayerHealth; FCanUseMana + DEPRECATED costs; Multicast_PlayMontage; Charge timeline; DivineCircle timer; dual HUD floats.
Replacement targets: IA→Server custom events; AnimNotify→UseAbility timing; ApplyDamage/RecoverPlayerHealth; FCanUseMana + DEPRECATED costs; Multicast_PlayMontage; Charge timeline; DivineCircle timer; dual HUD floats; `AN_AttackComplete` lockout cleanup.