# Fallout 4 Animation Graph Variables & Descriptor Mapping ## Overview This document maps Fallout 4's animation graph variable names and indices for the humanoid master behavior graph. This is the foundation for implementing descriptor-based animation synchronization similar to TiltedEvolution's model. **Goal:** Replace per-frame string-based graph variable writes (e.g., `SetGraphVariableFloat("Speed", 85.0)`) with efficient indexed bulk writes from a pre-computed descriptor table. --- ## Animation Graph Variable Categories Fallout 4's humanoid master behavior graph organizes variables into three types: ### 1. Float Variables (Locomotion & Direction) Used to control movement speed, direction, and animation blending. | Variable Name | Index | Type | Role | Network Sync | Notes | |---|---|---|---|---|---| | `Speed` | TBD | Float | Locomotion blend speed (0-105) | ✅ YES | Primary locomotion driver | | `direction` | TBD | Float | Facing/movement direction (degrees) | ✅ YES | Rotation relative to movement | | `speedSampled` | TBD | Float | Sampled speed for smoothing | ❌ Derive | Computed from velocity | | `speedDamped` | TBD | Float | Damped/smoothed speed | ❌ Derive | Animation system internal | | `pitchGunAim` | TBD | Float | Pitch angle when aiming | ❌ No | Combat specific | | `weaponAdjust` | TBD | Float | Weapon positioning | ❌ No | Combat specific | | `velocityZ` | TBD | Float | Vertical velocity | ❌ No | Handled by Havok | **Speed Buckets (Estimated):** - Idle: 0.0 - Walk: 35-55 - Run: 55-85 - Sprint: 85-105 ### 2. Boolean Variables (State Flags) Control state transitions and mode changes. | Variable Name | Index | Type | Role | Network Sync | Notes | |---|---|---|---|---|---| | `isSprinting` | TBD | Bool | Sprint mode active | ✅ YES | Triggers sprint animations | | `isSneaking` | TBD | Bool | Sneak/crouch mode | ✅ YES | Triggers sneak locomotion | | `isMoving` | TBD | Bool | Any movement active | ✅ YES | Idle ↔ movement transition | | `bMotionDriven` | TBD | Bool | Motion-driven locomotion | ❌ No | Engine internal | | `bInMoveState` | TBD | Bool | In locomotion state machine | ❌ No | Engine internal | | `bSprintOK` | TBD | Bool | Sprint available | ❌ No | Game state dependent | ### 3. Integer Variables (Equipment & State) Track equipped items and combat state. | Variable Name | Index | Type | Role | Network Sync | Notes | |---|---|---|---|---|---| | `iLeftHandType` | TBD | Int | Left hand weapon/item type | ❌ No | Inventory sync separate | | `iRightHandEquipped` | TBD | Int | Right hand equipped? (0/1) | ❌ No | Inventory sync separate | | `iIsInSneak` | TBD | Int | Sneak state (0/1/2) | ❌ No | Derived from isSneaking bool | --- ## Variable Index Discovery Process To determine actual indices for FO4, we need to: ### 1. Extract Indices via AnimationGraphManager ```cpp // Pseudocode to dump actual variable indices void DumpAnimationGraphVariables(RE::Actor* actor) { auto graphHolder = static_cast(actor); RE::BSTSmartPointer manager; if (graphHolder->GetAnimationGraphManagerImpl(manager) && manager) { // Access manager->behaviorGraph->animationVariables // Iterate through variable cache and log names + indices // Compare with known string names to build reverse mapping } } ``` ### 2. Verify via Creation Kit - Open the humanoid master behavior graph in Behavior Editor - Right-click variables to see internal indices/cache layout - Cross-reference with runtime dumps ### 3. Cross-Reference with CommonLibF4 Headers - Search `RE/H/BSAnimationGraphManager.h` for hint structs - Look for variable container definitions --- ## Comparison with TiltedEvolution (Skyrim SE) TiltedEvolution's `AnimationGraphDescriptor_Master_Behavior.cpp` syncs: **Skyrim SE Synced Booleans:** - `kIsSprinting` (index 50) - `kIsSneaking` (index 186) - `kbInMoveState` (index 98) - `kbMotionDriven` (index 41) - `kisMoving` (index 284) **Skyrim SE Synced Floats:** - `kSpeed` (index 0) - `kSpeedSampled` (index 40) - `kSpeedDamped` (index 183) - `kDirection` (index 1) - `kSpeedWalk`, `kSpeedRun` (indices 4, 5) **FO4 Likely Similar:** - Speed float: core locomotion - Direction float: facing - isSprinting, isSneaking: bool state flags - velocityZ: vertical velocity (Havok-driven, derived) --- ## Minimum Viable Sync Set For initial Phase 1-2 implementation, sync only the essential variables needed for basic locomotion: | Variable | Type | Index | Reason | |----------|------|-------|--------| | `Speed` | Float | TBD | Must have; drives walk/run/sprint | | `direction` | Float | TBD | Must have; face remote player's heading | | `isSprinting` | Bool | TBD | Should have; sprint visual distinction | | `isSneaking` | Bool | TBD | Should have; sneak locomotion + crouch | | `isMoving` | Bool | TBD | Should have; idle ↔ movement transition | **Deferred (Phase 2+):** - Combat variables (aiming, weapon adjust, etc.) - Jumping/falling (vertical velocity) - Facial expressions / emotion - IK targets --- ## Current F4T Implementation (Before Refactor) Current `F4TProxyAnimationSync.cpp` writes these by string name: ```cpp // Per-frame, on every proxy actor: TrySetGraphFloat(graphHolder, "Speed", desired.graphSpeed); TrySetGraphFloat(graphHolder, "speed", desired.graphSpeed); // fallback TrySetGraphFloat(graphHolder, "SpeedLower", desired.graphSpeed); TrySetGraphBool(graphHolder, "IsSprinting", desired.isSprinting); ``` **Issues:** - String lookup overhead (one `BSFixedString` allocation per write, per frame, per proxy) - No caching of variable indices - If a variable name is wrong, silent failure (write returns false) - No explicit velocity injection (character controller velocity = 0) --- ## Proposed F4AnimationDescriptor Model After Phase 1 research, we'll build: ```cpp // plugin/include/F4AnimationDescriptor.h class F4AnimationDescriptor { public: // Per-graph-type lookup static F4AnimationDescriptor& GetHumanoidDescriptor(); // Bulk read/write by index void SaveVariablesFromCache( RE::IAnimationGraphManagerHolder& holder, AnimationVariableSnapshot& snapshot); void LoadVariablesToCache( RE::IAnimationGraphManagerHolder& holder, const AnimationVariableSnapshot& snapshot); private: std::vector floatVariableNames; std::vector boolVariableNames; std::vector intVariableNames; }; ``` **Benefits:** - Pre-computed once at startup - O(1) index lookups instead of O(n) string searches - Batch reads/writes in one graph manager lock - Matches TiltedEvolution's proven architecture --- ## Testing Checklist (Phase 1.3) - [ ] Verify `Actor::Move(0.016f, {0,0,0}, false)` works on dynamically spawned actors - [ ] Verify `SetLinearVelocityImpl()` accessible via Move return value - [ ] Test with proxy in same cell as player - [ ] Confirm velocity persists across frames - [ ] Log animation graph state transitions (idle → walk → run) - [ ] Verify no crashes with velocity injection - [ ] Document any FO4-specific gotchas vs Skyrim --- ## Next Steps 1. **Phase 1.1:** Use debug logging to extract actual variable indices from running F4 2. **Phase 1.2:** Map TiltedEvolution animation flow into F4T terms (see `animation-architecture-alignment.md`) 3. **Phase 1.3:** Test character controller velocity injection on dynamic proxies 4. **Phase 2:** Extend `RemotePlayerState` with actor state flags + action events 5. **Phase 3:** Implement `F4AnimationDescriptor` class with bulk read/write --- ## References - TiltedEvolution: `Code/encoding/Structs/Skyrim/AnimationGraphDescriptor_Master_Behavior.cpp` - CommonLibF4: `RE/H/BSAnimationGraphManager.h`, `RE/H/IAnimationGraphManagerHolder.h` - Fallout 4 CK: Behavior Editor (humanoid master behavior graph)