Files
Commonwealth-Online-Public/docs/weapon-animation-sync.md
T
andrew 89c3e82554 Implement proxy weapon animation sync for ranged guns
Milestone 1: Proxy actors now equip remote players' right-hand weapons and play armed idle/locomotion poses when weaponDrawn=true. Adds rightHand weapon slot to equippedItems protocol, weapon graph variable support (iSyncGunDown: 0=drawn, 1=holstered), and weapon FSM events (weaponDraw, readyStateEnter, gunDownStateEnter). Fixes two critical animation state churn bugs: frustum-visibility flicker no longer resets animation state on every update tick, and idle proxies no longer register as changed on every frame. Includes WeaponBehavior graph loading via AIProcess::RequestLoadAnimationsForWeaponChange() and manual weapon attachment fallback. New docs/weapon-animation-sync.md reference guide maps animation events and variables from F4-Animation-Research. Experimental alert-state reassertion for armed proxies to test NPC combat state gating of weapon-ready poses.
2026-07-02 17:23:09 +12:00

187 lines
7.0 KiB
Markdown

# Weapon Animation Sync Reference
> **Source:** F4-Animation-Research unpacked XML (`RaiderRootBehavior.xml`, `WeaponBehavior.xml`)
## Overview
Fallout 4 weapon animation is hierarchical:
- **RaiderRootBehavior** — Master humanoid graph (1058 events, 299 variables)
- **WeaponWrappingBehavior** — Injury/special-case wrapper
- **WeaponBehavior** — Gun-specific FSM (state machine)
- **Clips** — WPN* shared anims + Weapon/<Type>/* per-weapon overrides
For proxy actors to hold and fire weapons, the animation graph must **switch from MTBehavior (unarmed) to WeaponBehavior** when a weapon is equipped.
---
## Key Graph Variables
### Root Graph (RaiderRootBehavior)
| Variable | Type | Role |
|----------|------|------|
| `iSyncGunDown` | int | Weapon state: 0=ready/drawn, 1=holstered/lowered |
| `iRifleDrawnStateID` | int | Rifle-specific drawn state (confirm exact values in-game) |
| `RifleDrawnCurrentState` | int | Current rifle state mirror |
| `iAttackState` | int | Attack sub-state (0=idle, 1=attacking, etc.) |
| `isFiring` | bool | Currently firing (true only during fire animation) |
| `iSyncFireState` | int | Fire state mirror (syncs with attack events) |
| `IsAttackReady` | bool | Can initiate attack |
| `GunGripPointer` | (struct) | Hand IK to weapon grip |
### WeaponBehavior Graph
Uses similar ints but delegates to RaiderRoot for most state. Primary events drive FSM transitions; graph variables control blending.
---
## Key Animation Events
Events are **edge-triggered** (fire once on transition); graph must reach the target state to play the corresponding clip.
### Draw / Holster Lifecycle
| Event | RaiderRoot | WeaponBehavior | Effect |
|-------|-----------|----------------|--------|
| `weaponDraw` | fires | accepts | Start draw animation (hand to gun) |
| `weaponSheathe` | — | — | (WeaponBehavior may handle internally) |
| `readyStateEnter` | — | fires | Weapon drawn and ready to fire |
| `gunDownStateEnter` | — | fires | Weapon lowered (walking with gun holstered) |
| `gunDownStateExit` | — | fires | Exiting lowered state |
| `unEquip` | fires | — | End draw (return to unarmed) |
### Fire / Attack
| Event | RaiderRoot | WeaponBehavior | Effect | Notes |
|-------|-----------|----------------|--------|-------|
| `fireSingle` | fires | accepts | Single shot, hip-fire | Primary for non-ADS |
| `attackStart` | fires | accepts | Generic attack start (melee also uses) | May be melee fallback |
| `attackStartAuto` | fires | — | Auto-fire start | Hold-to-fire weapons |
| `attackStartOver` | — | ? | Attack overflow / dual-wield? | TBD |
| `attackStartSlave` | fires | — | Upper-body-only fire overlay (while moving) | Combines with locomotion |
| `attackRelease` | fires | — | End attack (auto-fire release) | Holds until received |
| `weaponFire` | — | — | (Internal annotation; not an event) | Marks fire frame in clips |
### Sighted / ADS
| Event | Status | Role |
|-------|--------|------|
| `sightedStateEnter` | TBD | ADS enter |
| `sightedStateExit` | TBD | ADS exit |
Defer ADS until gun draw + single-shot fire are stable.
---
## Weapon Clip Categories
From F4-Animation-Research `extracted/Character/Animations/`:
### Shared (All guns)
| Clip | Use |
|------|-----|
| `WPNEquip` | Draw animation |
| `WPNIdleReady` | Idle, gun drawn |
| `WPNIdleGunDown` | Idle, gun holstered but equipped |
| `WPNIdleSighted` | Idle, aiming down sights |
| `WPNFireSingleReady` | Fire animation, hip |
| `WPNFireAutoReady` | Auto-fire loop, hip |
| `WPNFireSingleSighted` | Fire, aiming |
| `WPNReload` | Generic reload |
| `WPNWalkForwardReady` | Walk with gun ready |
| `WPNRunForwardReady` | Run with gun ready |
| `SneakWPN*` | Sneak with gun |
| `WPNGrenadeThrow` | Thrown weapon |
### Per-Weapon Overrides (Animations/Weapon/<Type>/)
| Folder | Examples | Use |
|--------|----------|-----|
| 44Pistol, 10mm, Pistol | `WPNAssemblyPose`, `WPNFireSingle*Slave` | Grip and fire pose (pistols hold differently than rifles) |
| HuntingRifle, CombatShotgun | Per-weapon-specific fire, reload | Shoulder-fired weapon animations |
| LaserRifle, PlasmaRifle | Per-type variants | Energy weapons (different recoil, charge-up anims) |
| Minigun | `WPNMeleeShredder`, charge-up | Spin-up and charging |
| GaussRifle, ChargeWeapons | `WPNChargeUp`, `WPNBoltCharge` | Charge mechanics |
---
## Phase 1 Validation Checklist
Testing on **local player** (to observe what graph vars actually change when playing animations):
- [ ] Draw weapon (right-hand equipped)
- Observe: Which graph vars flip? Which `NotifyAnimationGraphImpl()` calls return `graphAccepted=true`?
- Expected: `iSyncGunDown` → 0, `readyStateEnter` fires, `WPNIdleReady` plays
- [ ] Holster weapon (lower gun while keeping equipped)
- Expected: `iSyncGunDown` → 1, `gunDownStateEnter` fires, `WPNIdleGunDown` plays
- [ ] Fire single shot (hip-fire)
- Expected: `fireSingle` fires, `WPNFireSingleReady` plays, `isFiring` flips
- [ ] Walk / run with gun drawn
- Expected: armed locomotion clips (`WPNWalk*`, `WPNRun*`) play; speed graph var scales movement
- [ ] Log output in `CommonwealthOnline.log` should show var names and event accept/reject
Testing on **proxy with test weapon equipped** (temporary dev code):
- [ ] Equip test weapon on proxy actor
- [ ] Manually fire same events as local player
- [ ] Verify proxy enters same FSM states and plays same clips
- [ ] Confirm WeaponBehavior graph is active (check logs for which behavior is managing animation)
**Exit Criteria:**
Create a small reference table in this file (update below) with confirmed:
1. Graph variable names, types, and observed value ranges
2. Event names that return `graphAccepted=true` on proxy with weapon equipped
3. Any variables/events that failed or misbehaved
4. Notes on clip naming for armed locomotion
---
## Validation Results
> To be filled after Phase 1 testing
### Confirmed Variables
| Variable | Type | Values | Notes |
|----------|------|--------|-------|
| (pending) | — | — | — |
### Confirmed Events
| Event | Graph Accepted | Notes |
|-------|---|---|
| (pending) | — | — |
### Observed Issues
(none yet)
---
## Integration Notes (for later phases)
### Protocol Fields
- `rightHand: { slot: "rightHand", formId: "0001F4A6" }` — Equipped WEAP form ID
- `weaponDrawn: true/false` — Is weapon ready to fire (draw state; `iSyncGunDown=0`)
- `actionEvents[].eventName` — Fire events: `"fireSingle"`, `"attackStart"` (discrete actions)
### Proxy Animation Apply Order (per frame)
1. **Equip weapon** (if `rightHand.formId` changed) → Forces behavior graph reload
2. **Apply draw state** (write `iSyncGunDown` based on `weaponDrawn`)
3. **Fire draw/holster events** (if `weaponDrawn` edge-triggered)
4. **Apply locomotion** (existing Speed/Sprint/Sneak logic)
5. **Fire locomotion events** (existing moveStart/moveStop/etc.)
6. **Replay action events** (fire `fireSingle` if queued)
### Known Gaps (TBD in later phases)
- ADS / sighted state
- Auto-fire hold duration
- Power armor weapon behaviors
- Melee weapons (separate graph: MeleeBehavior)
- Weapon equip/unequip animations for left hand and off-hand