39 KiB
39 KiB
130 — Central State Authority (BPC_StateManager)
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),GI_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
GI_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