40 KiB
130 — Central State Authority (BPC_StateManager)
⚡ C++ Status: Full Implementation —
Source/PG_Framework/Public/Player/BPC_StateManager.hprovides the complete state authority:IsActionPermitted()hot-path query,RequestStateChange(),ForceStateChange()/RestorePreviousState()force stack, heart rate BPM smoothing with tier detection, gating rule evaluation. Attach directly to player pawn (component slot 0). SetGatingTable→DA_StateGatingTablein Details panel. Do NOT create a BP child. Seedocs/developer/cpp-integration-guide.mdfor usage patterns.
Purpose
Single source of truth for "what can the player do right now?" Every system queries IsActionPermitted(Tag) instead of checking other systems' states directly. Manages exclusive action states, upper-body overlay states, action action gating, vital signs (heart rate), and the force-stack pattern for nested overrides (death, cutscenes, void space). Communicates with GASP exclusively through overlay state variables — never touches motion matching internals.
Dependencies
- Requires:
GI_GameFramework(04 — game phase changes),BPC_HealthSystem(08 — death + injury bindings),BPC_StressSystem(10 — stress tier),BPC_StaminaSystem(09 — exhaustion),BPC_MovementStateSystem(11 — movement mode tracking),DA_GameTagRegistry(01 — tag queries),SS_EnhancedInputManager(128 — context switching),ABP_GASP(external — overlay + intensity variables) - Required By: EVERY gameplay system (central query point for action permission)
- Engine/Plugin Requirements: GameplayTags plugin, GASP plugin (read-only)
Class Info
| Property | Value |
|---|---|
| Parent Class | ActorComponent |
| Class Type | Blueprint Component |
| Asset Path | Content/Framework/Player/BPC_StateManager.uasset |
| Implements Interfaces | I_AnimNotifyInterface (receives GASP notifies) |
1. Enums
E_PlayerActionState — Exclusive Action States (45 values)
Enum Name: E_PlayerActionState
(DisplayName = "Player Action State")
Values:
Idle = 0 // Standing, not performing any action
Walking = 1 // Moving at walk speed
Jogging = 2 // Moving at jog speed
Sprinting = 3 // Moving at sprint speed (stamina draining)
Crouching = 4 // Crouched posture, possibly moving
Crawling = 5 // Prone, crawling
Sitting = 6 // Sitting on surface — immobile, can look around
Vaulting = 7 // Transient — vaulting over obstacle
Climbing = 8 // Ledge or ladder climbing
Sliding = 9 // Transient — sliding under or down
Interacting = 10 // Performing a hold/press interaction
Inspecting = 11 // Inspecting an item in close-up view
InInventory = 12 // Inventory menu is open
InJournal = 13 // Journal/document viewer is open
InPauseMenu = 14 // Pause menu is open
InDialogue = 15 // In dialogue with an NPC
InCutscene = 16 // Pre-authored cutscene playing
SwingMelee = 17 // Performing a melee attack
AimingFirearm = 18 // Aiming a firearm
FiringFirearm = 19 // Firing a firearm (transient)
Reloading = 20 // Reloading a firearm
DeployingShield = 21 // Raising a shield
Hiding = 22 // Inside a hiding spot (enclosed/behind/under/shadow)
Peeking = 23 // Peeking from behind cover
GrabbingObject = 24 // Holding a physics object
UsingObject = 25 // Using a world object (lever, panel, etc.)
Dead = 26 // Dead — death animation playing or death loop active
InVoidSpace = 27 // In alt death / void space
InPuzzle = 28 // Using a puzzle device
InTrialScenario = 29 // In a timed trial scenario
InMainMenu = 30 // At main menu (no gameplay)
Loading = 31 // Loading screen active
DraggingCorpse = 32 // Dragging a physics body
HoldingBreath = 33 // Holding breath while hiding
Injured = 34 // Health ≤ threshold — reduced speed, limp anim, restricted actions
Staggered = 35 // Transient — hit reaction / stagger
KnockedDown = 36 // Knocked to ground
Exhausted = 37 // Stamina fully depleted — panting/immobile
Panicked = 38 // Stress at catatonic tier — stumble/freeze
PushingObject = 39 // Pushing a movable object
AimingThrowable = 40 // Aiming a throwable item
ThrowingItem = 41 // Throwing an item (transient)
UsingConsumable = 42 // Using a consumable (heal, inject, etc.)
InPhotoMode = 43 // Photo mode active — free cam, no gameplay input
CustomAction = 44 // Reserved for mod/expansion actions
E_OverlayState — Upper-Body Overlay for GASP AnimBP (18 values)
Enum Name: E_OverlayState
(DisplayName = "Upper Body Overlay State")
Values:
Empty = 0 // No overlay — default locomotion
Flashlight = 1 // Holding flashlight
Melee_OneHanded = 2 // One-handed melee weapon (knife, baton)
Melee_TwoHanded = 3 // Two-handed melee weapon (axe, pipe)
Pistol = 4 // Pistol / handgun
Shotgun = 5 // Shotgun
Rifle = 6 // Rifle / carbine
Shield = 7 // Shield only
Shield_And_Melee = 8 // Shield + one-handed melee
Shield_And_Pistol = 9 // Shield + pistol
DualWield = 10 // Dual-wielding one-handed weapons
Inspect = 11 // Inspecting an object
DualHand_Flashlight_Melee = 12 // Flashlight + melee
Injured = 13 // Injured arm (lowered, favoring)
Throwable = 14 // Holding a throwable (grenade, bottle)
Consumable = 15 // Using consumable (syringe, bandage)
TwoHanded_Heavy = 16 // Heavy two-handed (sledgehammer, chainsaw)
EmptyHands_Raised = 17 // Empty hands raised (surrender, blocking bare)
E_ActionRequestResult — Result Enum for RequestStateChange (8 values)
Enum Name: E_ActionRequestResult
Values:
Permitted = 0 // State change approved
Blocked_CurrentState = 1 // Blocked because player is in an incompatible state
Blocked_GamePhase = 2 // Blocked by game phase (menu, cutscene, loading)
Blocked_Dead = 3 // Player is dead
Blocked_Stamina = 4 // Insufficient stamina
Blocked_Equipment = 5 // Missing required equipment
Blocked_Cooldown = 6 // Action on cooldown
Blocked_Custom = 7 // Blocked by a custom gating rule
E_PlayerVitalSignals — Vital Signal Enum for HeartRate / Breathing (5 values)
Enum Name: E_PlayerVitalSignals
(DisplayName = "Player Vital Signals")
Values:
Normal = 0 // Heart rate 60-80 BPM — resting/exploring
Elevated = 1 // Heart rate 80-110 BPM — sprinting, light combat
High = 2 // Heart rate 110-150 BPM — heavy combat, chased
Critical = 3 // Heart rate 150+ BPM — near death, terrified
Erratic = 4 // Irregular rhythm — panic state, hallucinating
2. Structs
S_StateChangeRequest
| Field | Type | Description |
|---|---|---|
RequestedState |
E_PlayerActionState |
The state the requester wants to enter |
RequesterTag |
GameplayTag |
Identifies which system is requesting |
Priority |
Integer |
0=Normal, 50=High, 100=Force (bypasses all gating) |
OverrideReason |
Text |
Human-readable reason for override/force |
S_StateGatingRule
| Field | Type | Description |
|---|---|---|
ActionTag |
GameplayTag |
The action being gated |
BlockedStates |
Array<E_PlayerActionState> |
States that block this action |
BlockedGamePhases |
Array<E_GamePhase> |
Game phases that block this |
RequiredTags |
GameplayTagContainer |
Tags the player must have |
BlockedTags |
GameplayTagContainer |
Tags the player must NOT have |
BlockReason |
Text |
Reason shown to player when blocked |
Priority |
Integer |
Rule evaluation order (lower = checked first) |
S_ActionPermissionResult
| Field | Type | Description |
|---|---|---|
bPermitted |
Boolean |
Whether the action is allowed |
BlockReason |
Text |
Why it was blocked (if not permitted) |
ResultCode |
E_ActionRequestResult |
Machine-readable result code |
3. Variables
Configuration (Instance Editable, Expose On Spawn)
| Variable | Type | Default | Category | Description |
|---|---|---|---|---|
GatingRules |
Array<S_StateGatingRule> |
DefaultRules |
State Config |
All gating rules defined in blueprint |
DefaultState |
E_PlayerActionState |
Idle |
State Config |
State to initialize on BeginPlay |
DefaultOverlay |
E_OverlayState |
Empty |
State Config |
Default overlay state |
StateTransitionDelay |
Float |
0.0 |
State Config |
Minimum time between state changes (anti-spam) |
InjuryHealthThreshold |
Float |
0.30 |
State Config |
Health ratio (0-1) below which player becomes Injured |
InjurySpeedMultiplier |
Float |
0.65 |
State Config |
Walk speed multiplier when injured |
HeartRateBase |
Float |
70.0 |
State Config |
Base heart rate in BPM when calm |
HeartRateMax |
Float |
180.0 |
State Config |
Maximum heart rate in BPM |
StateGatingTable |
DA_StateGatingTable |
None |
State Config |
External Data Asset for designer-tunable gating rules |
bLogStateTransitions |
Boolean |
false |
Debug |
Log all state transitions to console |
Internal (Private / Protected, No Expose)
| Variable | Type | Default | Category | Description |
|---|---|---|---|---|
CurrentState |
E_PlayerActionState |
Idle |
State |
Current exclusive action state |
PreviousState |
E_PlayerActionState |
Idle |
State |
Previous state for transitions |
CurrentOverlay |
E_OverlayState |
Empty |
State |
Current upper-body overlay state |
LastStateChangeTime |
Float |
0.0 |
State |
Game time of last state change |
StateHistory |
Array<E_PlayerActionState> |
Empty |
State |
History of last N states for debugging |
ForceStack |
Array<E_PlayerActionState> |
Empty |
State |
Stack of forced states for nested overrides |
bIsTransitioning |
Boolean |
false |
State |
True during a state transition |
PendingRequests |
Array<S_StateChangeRequest> |
Empty |
State |
Queued requests during transition |
CachedABPRef |
ABP_GASP (soft ref) |
None |
State |
Cached reference to GASP AnimBP |
CurrentActionFlags |
Map<GameplayTag, Float> |
Empty |
State |
Active action tags with duration/timestamp |
CurrentHeartRate |
Float |
70.0 |
Vital Signs |
Current heart rate in BPM (recalculated each tick, smoothed) |
CurrentVitalSignal |
E_PlayerVitalSignals |
Normal |
Vital Signs |
Current vital signal category |
bIsInjured |
Boolean |
false |
Vital Signs |
True when health ≤ InjuryHealthThreshold |
4. Functions
Public Functions
RequestStateChange → E_ActionRequestResult
- Description: The primary entry point. Any system requests to transition the player to a new exclusive action state. State Manager checks all gating rules and returns the result.
- Parameters:
Param Type Description NewStateE_PlayerActionStateThe desired new action state RequesterTagGameplayTagIdentifies which system is requesting PriorityInteger(default 0)0=Normal, 50=High, 100=Force - Blueprint Authority: Any
- Flow:
- If Priority == 100 (Force): bypass gating, go directly to state transition
- If
bIsTransitioning: queue the request onPendingRequests, returnBlocked_CurrentState - Check time since
LastStateChangeTime— if <StateTransitionDelay: returnBlocked_Cooldown - If
CurrentState==DeadandNewState!=Idle(respawn): returnBlocked_Dead - Fire
OnStateChangeRequested - Iterate
GatingRulesmatchingRequesterTag:- If
NewStateis inBlockedStatesfor any rule → fireOnStateChangeDenied, returnBlocked_CurrentStatewithBlockReason - If
CurrentGamePhaseis inBlockedGamePhases→ returnBlocked_GamePhase - If required tags missing or blocked tags present → return appropriate result
- If
- Execute state transition (set PreviousState, set CurrentState, update GASP, fire dispatchers)
- Return
Permitted
ForceStateChange → void
- Description: Bypasses ALL gating. Used by death system, cutscene bridge, void space entry, and developer cheats. Pushes current state onto
ForceStack. - Parameters:
Param Type Description NewStateE_PlayerActionStateThe forced state to enter ReasonTextHuman-readable reason for force - Blueprint Authority: Any (Server-authoritative if multiplayer)
- Flow:
- If
ForceStackalready hasNewStateat top: return (prevents double-force) - Push
CurrentStateontoForceStack - Fire
OnForceOverridePushed - Execute state transition with
NewState - Log reason to console if
bLogStateTransitions
- If
RestorePreviousState → void
- Description: Pops the force stack. Used when exiting cutscenes, void space, or force-override states.
- Parameters: (none)
- Blueprint Authority: Any
- Flow:
- If
ForceStackis empty: set toIdleor previously known normal state, return - Pop top of
ForceStack - Fire
OnForceOverridePopped - Execute state transition to popped state
- If
GetCurrentState → E_PlayerActionState
- Description: Pure getter — returns
CurrentState. No side effects. - Parameters: (none)
- Blueprint Authority: Any (Pure)
GetPreviousState → E_PlayerActionState
- Description: Pure getter — returns
PreviousState. No side effects. - Parameters: (none)
- Blueprint Authority: Any (Pure)
IsActionPermitted → S_ActionPermissionResult
- Description: Non-mutating query. Does NOT change state. Any system can ask "can I do this right now?"
- Parameters:
Param Type Description ActionTagGameplayTagThe action tag to check - Blueprint Authority: Any (Pure)
- Flow:
- Find all
GatingRuleswhereActionTagmatches - Check
CurrentStateagainstBlockedStates - Check
GI_GameFramework.CurrentGamePhaseagainstBlockedGamePhases - Check
RequiredTagsandBlockedTags - Return
S_ActionPermissionResultwithbPermitted,BlockReason,ResultCode
- Find all
SetOverlayState → void
- Description: Tells GASP AnimBP which upper-body overlay to use. Does NOT change
E_PlayerActionState. Called by equipment system, weapon system, and inspection. - Parameters:
Param Type Description OverlayE_OverlayStateThe new overlay state - Blueprint Authority: Any
- Flow:
- If
Overlay==CurrentOverlay: return (no-op) - Set
CurrentOverlay=Overlay - If
CachedABPRefis valid: setABP_GASP.OverlayState=Overlay - Fire
OnOverlayStateChanged - Update visible first-person arm mesh via
BPC_EmbodimentSystem
- If
CanTransitionTo → S_ActionPermissionResult
- Description: Pre-flight check without executing. Used by UI to enable/disable buttons.
- Parameters:
Param Type Description TargetStateE_PlayerActionStateThe state to check transition eligibility for - Blueprint Authority: Any (Pure)
UpdateHeartRate → void
- Description: Recalculates heart rate each tick based on current state, stress, stamina, and health. Evaluates
E_PlayerVitalSignalsthresholds. Called automatically from Tick. - Parameters: (none — reads internal state and bound systems)
- Blueprint Authority: Internal (Tick-driven)
- Flow:
- Calculate base rate from current state:
- Idle/Walking/Sitting →
HeartRateBase(60-80) - Jogging → Base + 15
- Sprinting → Base + 30
- Combat (any weapon state) → Base + 40
- Hiding/Peeking → Base + 5 (suppressed by stealth)
- Panicked → Base + 50
- Dead → 0
- Idle/Walking/Sitting →
- Apply health modifier: (1.0 - HealthRatio) * 20 added
- Apply stress modifier: StressRatio * 30 added
- Apply stamina modifier: if Exhausted → +25
- Clamp to
HeartRateMax - Smooth interpolation:
CurrentHeartRate = FMath::FInterpTo(CurrentHeartRate, Target, DeltaTime, 3.0) - Evaluate
E_PlayerVitalSignalsfromCurrentHeartRate:- ≤ 80 → Normal
- ≤ 110 → Elevated
- ≤ 150 → High
- ≤ 180 → Critical
- If Panicked state OR FearSystem active → Erratic (overrides BPM threshold)
- If
CurrentVitalSignalchanged, fireOnVitalSignalChanged - Write
CurrentHeartRatetoABP_GASP.HeartRatefor breathing animation blend
- Calculate base rate from current state:
EvaluateInjuryState → void
- Description: Called when health changes. Sets
bIsInjuredflag and auto-transitions to/fromInjuredstate if health crosses threshold. - Parameters: (none — reads BPC_HealthSystem)
- Blueprint Authority: Internal (called by health change binding)
- Flow:
- Get
HealthRatiofromBPC_HealthSystem.CurrentHealth / MaxHealth - If
HealthRatio <= InjuryHealthThresholdAND!bIsInjured:- Set
bIsInjured = true - If CurrentState is Idle/Walking/Jogging/Sprinting/Crouching:
RequestStateChange(Injured, Tag.Health) - Apply
InjurySpeedMultiplierto movement - Fire
OnInjuryStateChanged(true, HealthRatio)
- Set
- If
HealthRatio > InjuryHealthThresholdANDbIsInjured:- Set
bIsInjured = false - If CurrentState == Injured:
RequestStateChange(Idle, Tag.Health) - Restore movement speed
- Fire
OnInjuryStateChanged(false, HealthRatio)
- Set
- Get
IsInCombat → Boolean
- Description: Convenience — returns true if
CurrentStateisSwingMelee,AimingFirearm,FiringFirearm,Reloading, orDeployingShield. - Parameters: (none)
- Blueprint Authority: Any (Pure)
IsMovementBlocked → Boolean
- Description: Convenience — returns true if player cannot move at all. True when
CurrentStateisDead,InCutscene,InDialogue,InInventory,InJournal,InPauseMenu,InMainMenu,UsingObject,Sitting,KnockedDown,Panicked,Staggered,Exhausted,Loading,InPhotoMode. - Parameters: (none)
- Blueprint Authority: Any (Pure)
IsMovementRestricted → Boolean
- Description: Convenience — returns true if movement is partially restricted (slowed/impaired but not fully blocked). True when
CurrentStateisInjured,Crouching,Crawling,HoldingBreath,AimingFirearm,DeployingShield,Hiding,Peeking,GrabbingObject,PushingObject,UsingConsumable. - Parameters: (none)
- Blueprint Authority: Any (Pure)
GetHeartRate → Float
- Description: Returns
CurrentHeartRatein BPM. Used by audio system for heartbeat SFX tempo, by UI for vitals display, by haptic system for controller pulse rate. - Parameters: (none)
- Blueprint Authority: Any (Pure)
GetVitalSignal → E_PlayerVitalSignals
- Description: Returns
CurrentVitalSignal. Used by atmosphere system (audio tension layers), camera (breathing sway), and narrative system (dialogue conditionals). - Parameters: (none)
- Blueprint Authority: Any (Pure)
CanEquipWeapon → Boolean
- Description: Checks if weapon equip is permitted in current state. Convenience wrapper around
IsActionPermitted. - Parameters: (none)
- Blueprint Authority: Any (Pure)
RegisterActionFlag → void
- Description: Lightweight flag system for transient action permissions. Duration of -1 = permanent until unregistered. Fires
OnActionFlagRegistered. - Parameters:
Param Type Description TagGameplayTagThe flag tag to register DurationFloat(default -1.0)How long the flag persists (-1 = permanent)
UnregisterActionFlag → void
- Description: Removes a previously registered action flag. Fires
OnActionFlagExpiredif the flag was active. - Parameters:
Param Type Description TagGameplayTagThe flag tag to remove
HasActionFlag → Boolean
- Description: Returns true if the given tag is currently registered as an action flag.
- Parameters:
Param Type Description TagGameplayTagThe flag tag to check - Blueprint Authority: Any (Pure)
5. Event Dispatchers
| Dispatcher | Parameters | Bind Access | Description |
|---|---|---|---|
OnStateChanged |
OldState: E_PlayerActionState, NewState: E_PlayerActionState |
Public |
Fired on any state transition |
OnStateChangeRequested |
Request: S_StateChangeRequest |
Public |
Fired when request is made (before evaluation) |
OnStateChangeDenied |
Request: S_StateChangeRequest, Result: E_ActionRequestResult, Reason: Text |
Public |
Fired when a request is denied |
OnOverlayStateChanged |
OldOverlay: E_OverlayState, NewOverlay: E_OverlayState |
Public |
Fired on overlay change |
OnActionFlagRegistered |
Tag: GameplayTag, Duration: Float |
Public |
Fired when a flag is registered |
OnActionFlagExpired |
Tag: GameplayTag |
Public |
Fired when a timed flag expires |
OnForceOverridePushed |
ForcedState: E_PlayerActionState, PoppedState: E_PlayerActionState |
Public |
Fired when force stack is pushed |
OnForceOverridePopped |
RestoredState: E_PlayerActionState |
Public |
Fired when force stack is popped |
OnVitalSignalChanged |
OldSignal: E_PlayerVitalSignals, NewSignal: E_PlayerVitalSignals |
Public |
Fired when vital signal tier changes |
OnHeartRateChanged |
NewBPM: Float |
Public |
Fired when heart rate changes significantly (±5 BPM) |
OnInjuryStateChanged |
bIsInjured: Boolean, HealthRatio: Float |
Public |
Fired when injury state toggles |
6. Overridden Events / Custom Events
Event: BeginPlay → Initialize
- Description: Sets up all bindings, caches references, loads default state, and enables tick.
- Flow:
- Get owner as Character (validate — log error and disable if invalid)
- Cache reference to
ABP_GASPfrom character's skeletal mesh - Set
CurrentState=DefaultState,CurrentOverlay=DefaultOverlay - Populate default
GatingRulesfromDA_StateGatingTableif assigned - Bind to
GI_GameFramework.OnGamePhaseChanged(for game-phase-gated rules) - Bind to
BPC_HealthSystem.OnDeath(auto-callForceStateChange(Dead, "Death")) - Register with
DA_GameTagRegistryfor tag-based queries - Bind to
BPC_HealthSystem.OnHealthChanged→ callEvaluateInjuryState() - Bind to
BPC_StressSystem.OnStressTierChanged→ recalculate heart rate - Bind to
BPC_StaminaSystem.OnExhaustionStateChanged→ recalculate heart rate - Enable component tick (required for heart rate smoothing)
Event: Tick → UpdateHeartRate
- Description: Called every tick. Recalculates heart rate and evaluates vital signals.
- Flow: (see
UpdateHeartRatefunction flow above)
Event: OnComponentDestroyed
- Description: Cleanup — unbind all bound delegates, null cached references.
- Flow:
- Unbind from
GI_GameFramework.OnGamePhaseChanged - Unbind from
BPC_HealthSystem.OnDeathandOnHealthChanged - Unbind from
BPC_StressSystem.OnStressTierChanged - Unbind from
BPC_StaminaSystem.OnExhaustionStateChanged - Set
CachedABPRef=None
- Unbind from
7. Blueprint Graph Logic Flow
RequestStateChange Flow
flowchart TD
A[System calls RequestStateChange] --> B{Priority == 100?}
B -->|Yes Force| C[ExecuteStateTransition]
B -->|No| D{bIsTransitioning?}
D -->|Yes| E[Queue on PendingRequests - return Blocked_CurrentState]
D -->|No| F{Within StateTransitionDelay?}
F -->|Yes| G[Return Blocked_Cooldown]
F -->|No| H{CurrentState == Dead?}
H -->|Yes and NewState != Idle| I[Return Blocked_Dead]
H -->|No| J[Fire OnStateChangeRequested]
J --> K[Evaluate GatingRules for RequesterTag]
K --> L{NewState in BlockedStates?}
L -->|Yes| M[Fire OnStateChangeDenied - return Blocked_CurrentState]
L -->|No| N{GamePhase blocks?}
N -->|Yes| O[Fire OnStateChangeDenied - return Blocked_GamePhase]
N -->|No| P{Required tags missing?}
P -->|Yes| Q[Fire OnStateChangeDenied - return reason]
P -->|No| R{Blocked tags present?}
R -->|Yes| Q
R -->|No| C
C --> S[PreviousState = CurrentState]
S --> T[CurrentState = NewState]
T --> U[Update bIsInAction on GASP]
U --> V[Update ActionIntensity on GASP]
V --> W[Fire OnStateChanged - OldState, NewState]
W --> X[Notify SS_EnhancedInputManager - context change]
X --> Y[Return Permitted]
Overlay State Pipeline
sequenceDiagram
participant ES as EquipmentSystem
participant SM as BPC_StateManager
participant GASP as ABP_GASP
participant AP as AnimPose
ES->>SM: SetOverlayState(Pistol)
SM->>SM: CurrentOverlay = Pistol
SM->>GASP: ABP_GASP.OverlayState = Pistol
GASP->>GASP: Blend upper body to Pistol pose
GASP->>AP: Apply overlay pose
SM->>SM: Fire OnOverlayStateChanged
SM->>ES: Confirm overlay set
8. State Gating Reference Tables
Complete Action → Blocking State Matrix
| Requested Action (Tag) | Blocked If CurrentState Is | Reason |
|---|---|---|
Action.Move.Walk |
Dead, InCutscene, InDialogue, KnockedDown, Panicked, Exhausted, Sitting, InInventory, InJournal, InPauseMenu, UsingObject, GrabbingObject, InPhotoMode | Movement disabled |
Action.Move.Sprint |
Dead, InCutscene, InDialogue, Crouching, Crawling, Hiding, Peeking, Sitting, KnockedDown, Exhausted, Panicked, Reloading, UsingConsumable, Injured | Sprint requires standing + uninjured |
Action.Move.Crouch |
Dead, Vaulting, Climbing, Sliding, InCutscene, InDialogue, Sitting, KnockedDown, Exhausted, Panicked | Cannot crouch during traversal |
Action.Move.Crawl |
Dead, Standing required states, Vaulting, Climbing, InCutscene, Sitting, KnockedDown | Must be prone first |
Action.Move.Jump |
Dead, Hiding, Peeking, Crouching (maybe), Crawling, InCutscene, InDialogue, Sitting, KnockedDown, Panicked, Exhausted, UsingObject, InInventory, InPauseMenu | Jump blocked |
Action.Move.Sit |
Dead, Vaulting, Climbing, Hiding, SwingMelee, FiringFirearm, Reloading, KnockedDown, Panicked, InInventory, InCutscene, InDialogue | Sit requires neutral standing state |
Action.Move.StandUp |
Must be Sitting state | Must be sitting first |
Action.Traversal.Vault |
Dead, Hiding, Peeking, InCutscene, InDialogue, Reloading, SwingMelee, FiringFirearm, KnockedDown, Panicked | Traversal requires free hands |
Action.Traversal.Climb |
Dead, InCutscene, InDialogue, Reloading, SwingMelee, KnockedDown, Panicked | Climbing requires free hands |
Action.Traversal.Slide |
Dead, InCutscene, InDialogue, Reloading, SwingMelee, KnockedDown, Panicked | Slide requires two hands |
Action.Hide.Enter |
Dead, InCutscene, InDialogue, SwingMelee, FiringFirearm, Reloading, Vaulting, Climbing, KnockedDown, Panicked, Exhausted, GrabbingObject | Hide requires exposed state |
Action.Hide.Peek |
Dead, SwingMelee, FiringFirearm, Reloading | Peek only from hidden |
Action.Hide.Exit |
InCutscene, Dead (if not force-exit) | Forced exit allowed |
Action.Interact.Hold |
Dead, InCutscene, SwingMelee, FiringFirearm, Reloading, Hiding, Peeking, Vaulting, Climbing, KnockedDown, Panicked, InInventory, InPauseMenu, InDialogue, UsingObject | Interaction requires free state |
Action.Interact.Instant |
Dead, InCutscene, KnockedDown, Panicked | Instant interaction only blocked by critical |
Action.Inventory.Open |
Dead, InCutscene, Vaulting, Climbing, SwingMelee, FiringFirearm, Reloading, KnockedDown, Panicked, Hiding, GrabbingObject, InDialogue, InPuzzle, InTrialScenario | Inventory blocked during actions |
Action.Inventory.Close |
(always permitted — but may not open if blocked) | Close always works |
Action.Inventory.Equip |
Dead, InCutscene, Vaulting, Climbing, KnockedDown, Panicked | Equip requires stability |
Action.Inventory.UseItem |
Dead, InCutscene, Vaulting, Climbing, KnockedDown, Panicked, Hiding | Use requires stable state |
Action.Weapon.Fire |
Dead, Hiding, Peeking, Reloading, Vaulting, Climbing, InCutscene, InDialogue, KnockedDown, Panicked, InInventory, InPauseMenu, UsingConsumable, GrabbingObject | Cannot fire in these states |
Action.Weapon.Aim |
Dead, Hiding, Peeking, Vaulting, Climbing, InCutscene, InDialogue, KnockedDown, Panicked, InInventory, InPauseMenu, GrabbingObject | Cannot aim |
Action.Weapon.Reload |
Dead, Hiding, Peeking, Vaulting, Climbing, SwingMelee, InCutscene, InDialogue, KnockedDown, Panicked, InPauseMenu, GrabbingObject | Cannot reload |
Action.Weapon.Melee |
Dead, Hiding, Peeking, Reloading, Vaulting, Climbing, InCutscene, InDialogue, KnockedDown, Panicked, InInventory, InPauseMenu, GrabbingObject, UsingConsumable | Cannot melee |
Action.Weapon.Block |
Dead, Reloading, Vaulting, Climbing, InCutscene, KnockedDown, Panicked, Hiding, InInventory, InPauseMenu | Cannot block |
Action.Dialogue.Start |
Dead, InCutscene, Vaulting, Climbing, SwingMelee, FiringFirearm, Hiding, InInventory, InPauseMenu, Reloading, KnockedDown, Panicked | Dialogue requires neutral state |
Action.Dialogue.Choose |
Must be InDialogue state | Must be in dialogue |
Action.Cutscene.Play |
(handled by GI_GameFramework game phase, not action state) | Cutscene overrides via Force |
Action.Cutscene.Skip |
Must be InCutscene, bCanSkip | Must be in cutscene |
Action.HoldBreath |
Hiding state only, not Peeking | Breath hold only while hidden |
Action.Grab.PhysicsObject |
Dead, InCutscene, Hiding, Vaulting, Climbing, Reloading, SwingMelee, FiringFirearm, KnockedDown, Panicked, InInventory, InPauseMenu | Grab requires free hands |
Action.Puzzle.Use |
Dead, InCutscene, InDialogue, Vaulting, Climbing, Hiding, SwingMelee, FiringFirearm, Reloading, KnockedDown, Panicked | Puzzle requires focus |
Action.Consumable.Use |
Dead, InCutscene, Vaulting, Climbing, Hiding, SwingMelee, FiringFirearm, Reloading, KnockedDown, Panicked, InInventory | Consumable requires free hands |
Action.Pause.Open |
InCutscene, Dead, InMainMenu, Loading | Cannot pause in cutscene/death |
Action.Pause.Close |
(always permitted) | Close always works |
Action.Journal.Open |
Dead, InCutscene, InDialogue, Vaulting, Climbing, Hiding, SwingMelee, FiringFirearm, Reloading, KnockedDown, Panicked, InInventory, InPuzzle, InTrialScenario | Journal requires neutral state |
Action.Settings.Open |
InCutscene, Loading | Settings limited during cutscene |
Action.MainMenu.Return |
(always permitted) | Return to menu always works |
Action.QuestLog.Open |
Same as Journal | Same blocking states |
Action.Map.Open |
Dead, InCutscene, Vaulting, Climbing, Hiding, InInventory, InPauseMenu | Map in neutral/explore states |
Action.Throwable.Aim |
Dead, Hiding, Peeking, Vaulting, Climbing, InCutscene, InDialogue, Reloading, KnockedDown, Panicked, InInventory, InPauseMenu | Throw requires free hands |
Action.Throwable.Throw |
Same as Aim + must be in AimingThrowable state | Must be aiming first |
Action.PhotoMode.Enter |
Dead, InCutscene, InDialogue, Vaulting, Climbing, Hiding, SwingMelee, FiringFirearm, KnockedDown, Panicked | Photo mode requires neutral state |
Action.PhotoMode.Exit |
Must be InPhotoMode | Must be in photo mode |
Restricted Movement States
| State | Walk Allowed | Sprint Allowed | Jump Allowed | Crouch Allowed | Camera Freelook | Notes |
|---|---|---|---|---|---|---|
Sitting |
No | No | No | No | Yes (head only) | Must StandUp to move |
Injured |
Slowed (×0.65) | No | No | Yes | Yes | Limp anim. Heals above threshold |
Hiding (Locker/Under) |
No | No | No | No | No (fixed cam) | Fully enclosed — zero movement |
Hiding (BehindCover) |
No | No | No | N/A | Limited (peek) | Cannot leave cover position |
Hiding (InShadow) |
Yes (slow) | No | No | Yes | Yes | Standing in shadow |
Hiding (TallGrass) |
Yes (crouch-walk) | No | No | Yes | Yes | Crouch-moving through vegetation |
Staggered |
No | No | No | No | Locked | Forced animation — no input |
KnockedDown |
No | No | No | No | Locked (ground view) | Must GetUp animation first |
Exhausted |
No | No | No | No | Yes | Heavy breathing — wait for regen |
Panicked |
Stumble | No | No | No | Random jerks | Camera shakes |
HoldingBreath |
No | No | No | No | Yes | Must release breath before moving |
InPhotoMode |
No (free cam) | No | No | No | Full (free cam) | Gameplay input disabled |
9. GASP Integration Notes
What BPC_StateManager READS from GASP
| GASP Variable / Event | How Used |
|---|---|
ABP_GASP.bStrafing |
Read to detect if GASP is in strafing mode — informs combat state eligibility |
ABP_GASP.bSprinting |
Read to confirm sprint state when BPC_MovementStateSystem reports sprint |
ABP_GASP.MovementMode |
Read to cross-validate BPC_MovementStateSystem.CurrentMovementMode |
ABP_GASP.GroundState |
Read to know if airborne (blocks certain actions) |
AnimNotify_StateEnter |
Any GASP notify received via I_AnimNotifyInterface is forwarded to listeners |
What BPC_StateManager WRITES to GASP
| GASP Variable / Event | How Written |
|---|---|
ABP_GASP.OverlayState |
Set via SetOverlayState(E_OverlayState) — direct enum value set on cached AnimBP reference |
ABP_GASP.bIsInAction |
Set to true when CurrentState is any action state (not Idle/Walking/Jogging/Sprinting/Crouching) |
ABP_GASP.ActionIntensity |
Float 0.0–1.0 set based on action type: 0.0=Idle, 0.3=Interacting, 0.7=SwingMelee, 1.0=FiringFirearm |
AnimNotify_OverlayChanged |
Fired when overlay changes — GASP blends to new upper-body pose |
What BPC_StateManager NEVER Touches
- Motion Matching Database — GASP's motion matching is sacred, never modified
- Camera Manager — Camera blends, FOV, and offsets belong to
BPC_CameraStateLayer - GASP's Internal State Machine — Ground/air transitions, rotation mode, strafing logic
- Animation Blueprint Graph Logic — Only sets variables, never modifies graph nodes
- GASP's Root Motion — Root motion extraction is GASP's domain
10. Communication Matrix
| Who Talks | How | What Is Sent |
|---|---|---|
| Any System | RequestStateChange() |
E_PlayerActionState, RequesterTag: GameplayTag, Priority: Int |
| Any System | IsActionPermitted() |
GameplayTag → S_ActionPermissionResult |
| BPC_StateManager | ABP_GASP (Direct variable set) |
OverlayState, bIsInAction, ActionIntensity |
| BPC_StateManager | Dispatcher: OnStateChanged |
OldState, NewState to all bound listeners |
| BPC_StateManager | Dispatcher: OnOverlayStateChanged |
OldOverlay, NewOverlay to BPC_EmbodimentSystem, WBP_HUDController |
| BPC_StateManager | Dispatcher: OnStateChangeDenied |
Request, Result, Reason to WBP_InteractionPromptDisplay, WBP_NotificationToast |
| BPC_StateManager | SS_EnhancedInputManager (Direct) |
Context change notification when state changes |
| BPC_StateManager | BPC_MovementStateSystem (Direct) |
Receives OnMovementModeChanged to track walking/sprinting |
| BPC_StateManager | BPC_HealthSystem (Listener on OnDeath) |
Auto-transitions to Dead state |
| BPC_StateManager | Dispatcher: OnVitalSignalChanged |
OldSignal, NewSignal to SS_AudioManager, BPC_CameraStateLayer |
| BPC_StateManager | Dispatcher: OnHeartRateChanged |
NewBPM: Float to SS_AudioManager, WBP_AccessibilityUI |
GI_GameFramework |
Dispatcher: OnGamePhaseChanged |
Notifies StateManager of phase changes for gating |
BPC_CameraStateLayer |
Dispatcher: OnStateChanged |
Adjusts camera based on state (e.g., combat FOV) |
BPC_StressSystem |
Direct: IsActionPermitted |
Checks if stress effects are currently permitted |
WBP_InteractionPromptDisplay |
Listener on OnStateChanged |
Shows/hides interaction prompts based on state |
11. Validation / Testing Checklist
RequestStateChangewith valid state returnsPermittedand firesOnStateChangedRequestStateChangewhile Dead (except to Idle) returnsBlocked_DeadRequestStateChangeduring transition queues and returnsBlocked_CurrentStateForceStateChangebypasses all gating and pushes to ForceStackRestorePreviousStatepops ForceStack and restores correct stateIsActionPermittedreturns correctS_ActionPermissionResultfor all 35+ actionsSetOverlayStatecorrectly writes toABP_GASP.OverlayState- Overlay change fires
OnOverlayStateChangeddispatcher - Game phase change (MainMenu → InGame → Cutscene) correctly gates actions
- Death auto-transitions to Dead state via
BPC_HealthSystem.OnDeathbinding - Respawn restores previous state from ForceStack
- Rapid state change requests are throttled by
StateTransitionDelay IsInCombat()returns true forSwingMelee,AimingFirearm,FiringFirearm,Reloading,DeployingShieldIsMovementBlocked()returns true for all blocking states- Heart rate correctly interpolates and fires threshold change events
- Vital signal changes fire
OnVitalSignalChanged - Injury state auto-transitions when health crosses threshold
- Edge case:
ForceStateChangecalled twice with same state — second call is no-op - Edge case:
RestorePreviousStatewith empty ForceStack defaults to Idle - Edge case: State Manager handles owner destruction gracefully (null checks on cached ABP ref)
- Edge case:
RequestStateChangewith invalid/missing RequesterTag logs warning
12. Reuse Notes
- BPC_StateManager replaces the need for every system to check every other system's state. Systems call
IsActionPermitted(Tag)instead ofIsPlayerInState(X) && !IsPlayerInState(Y) && ... - New states can be added to
E_PlayerActionStateandDA_StateGatingTablewithout modifying the State Manager blueprint logic. - The
DA_StateGatingTableData Asset allows designers to tune gating rules without opening blueprints. - Force stack pattern supports nested force overrides (e.g., Death → Cutscene flashback → Respawn).
- GASP variables
bIsInActionandActionIntensityallow AnimBP to blend differently during action vs exploration without the AnimBP knowing about game logic. - The action flag system (
RegisterActionFlag/UnregisterActionFlag/HasActionFlag) enables lightweight transient permission tracking without formal state transitions. E_PlayerVitalSignalsdrives audio heartbeat tempo, camera breathing sway, controller haptics, and accessibility visual heartbeat all from a single source of truth.
Blueprint Spec: BPC_StateManager — Central Nervous System for the Modular Game Framework. Architecture document: bpc-statemanager.md