add blueprints
This commit is contained in:
663
docs/blueprints/16-state/130_BPC_StateManager.md
Normal file
663
docs/blueprints/16-state/130_BPC_StateManager.md
Normal file
@@ -0,0 +1,663 @@
|
||||
# 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)
|
||||
|
||||
```text
|
||||
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)
|
||||
|
||||
```text
|
||||
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)
|
||||
|
||||
```text
|
||||
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)
|
||||
|
||||
```text
|
||||
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 |
|
||||
|-------|------|-------------|
|
||||
| `NewState` | `E_PlayerActionState` | The desired new action state |
|
||||
| `RequesterTag` | `GameplayTag` | Identifies which system is requesting |
|
||||
| `Priority` | `Integer` (default 0) | 0=Normal, 50=High, 100=Force |
|
||||
- **Blueprint Authority:** Any
|
||||
- **Flow:**
|
||||
1. If Priority == 100 (Force): bypass gating, go directly to state transition
|
||||
2. If `bIsTransitioning`: queue the request on `PendingRequests`, return `Blocked_CurrentState`
|
||||
3. Check time since `LastStateChangeTime` — if < `StateTransitionDelay`: return `Blocked_Cooldown`
|
||||
4. If `CurrentState` == `Dead` and `NewState` != `Idle` (respawn): return `Blocked_Dead`
|
||||
5. Fire `OnStateChangeRequested`
|
||||
6. Iterate `GatingRules` matching `RequesterTag`:
|
||||
- If `NewState` is in `BlockedStates` for any rule → fire `OnStateChangeDenied`, return `Blocked_CurrentState` with `BlockReason`
|
||||
- If `CurrentGamePhase` is in `BlockedGamePhases` → return `Blocked_GamePhase`
|
||||
- If required tags missing or blocked tags present → return appropriate result
|
||||
7. Execute state transition (set PreviousState, set CurrentState, update GASP, fire dispatchers)
|
||||
8. 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 |
|
||||
|-------|------|-------------|
|
||||
| `NewState` | `E_PlayerActionState` | The forced state to enter |
|
||||
| `Reason` | `Text` | Human-readable reason for force |
|
||||
- **Blueprint Authority:** Any (Server-authoritative if multiplayer)
|
||||
- **Flow:**
|
||||
1. If `ForceStack` already has `NewState` at top: return (prevents double-force)
|
||||
2. Push `CurrentState` onto `ForceStack`
|
||||
3. Fire `OnForceOverridePushed`
|
||||
4. Execute state transition with `NewState`
|
||||
5. Log reason to console if `bLogStateTransitions`
|
||||
|
||||
#### `RestorePreviousState` → `void`
|
||||
- **Description:** Pops the force stack. Used when exiting cutscenes, void space, or force-override states.
|
||||
- **Parameters:** *(none)*
|
||||
- **Blueprint Authority:** Any
|
||||
- **Flow:**
|
||||
1. If `ForceStack` is empty: set to `Idle` or previously known normal state, return
|
||||
2. Pop top of `ForceStack`
|
||||
3. Fire `OnForceOverridePopped`
|
||||
4. Execute state transition to popped state
|
||||
|
||||
#### `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 |
|
||||
|-------|------|-------------|
|
||||
| `ActionTag` | `GameplayTag` | The action tag to check |
|
||||
- **Blueprint Authority:** Any (Pure)
|
||||
- **Flow:**
|
||||
1. Find all `GatingRules` where `ActionTag` matches
|
||||
2. Check `CurrentState` against `BlockedStates`
|
||||
3. Check `GI_GameFramework.CurrentGamePhase` against `BlockedGamePhases`
|
||||
4. Check `RequiredTags` and `BlockedTags`
|
||||
5. Return `S_ActionPermissionResult` with `bPermitted`, `BlockReason`, `ResultCode`
|
||||
|
||||
#### `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 |
|
||||
|-------|------|-------------|
|
||||
| `Overlay` | `E_OverlayState` | The new overlay state |
|
||||
- **Blueprint Authority:** Any
|
||||
- **Flow:**
|
||||
1. If `Overlay` == `CurrentOverlay`: return (no-op)
|
||||
2. Set `CurrentOverlay` = `Overlay`
|
||||
3. If `CachedABPRef` is valid: set `ABP_GASP.OverlayState` = `Overlay`
|
||||
4. Fire `OnOverlayStateChanged`
|
||||
5. Update visible first-person arm mesh via `BPC_EmbodimentSystem`
|
||||
|
||||
#### `CanTransitionTo` → `S_ActionPermissionResult`
|
||||
- **Description:** Pre-flight check without executing. Used by UI to enable/disable buttons.
|
||||
- **Parameters:**
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `TargetState` | `E_PlayerActionState` | The 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_PlayerVitalSignals` thresholds. Called automatically from Tick.
|
||||
- **Parameters:** *(none — reads internal state and bound systems)*
|
||||
- **Blueprint Authority:** Internal (Tick-driven)
|
||||
- **Flow:**
|
||||
1. 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
|
||||
2. Apply health modifier: (1.0 - HealthRatio) * 20 added
|
||||
3. Apply stress modifier: StressRatio * 30 added
|
||||
4. Apply stamina modifier: if Exhausted → +25
|
||||
5. Clamp to `HeartRateMax`
|
||||
6. Smooth interpolation: `CurrentHeartRate = FMath::FInterpTo(CurrentHeartRate, Target, DeltaTime, 3.0)`
|
||||
7. Evaluate `E_PlayerVitalSignals` from `CurrentHeartRate`:
|
||||
- ≤ 80 → Normal
|
||||
- ≤ 110 → Elevated
|
||||
- ≤ 150 → High
|
||||
- ≤ 180 → Critical
|
||||
- If Panicked state OR FearSystem active → Erratic (overrides BPM threshold)
|
||||
8. If `CurrentVitalSignal` changed, fire `OnVitalSignalChanged`
|
||||
9. Write `CurrentHeartRate` to `ABP_GASP.HeartRate` for breathing animation blend
|
||||
|
||||
#### `EvaluateInjuryState` → `void`
|
||||
- **Description:** Called when health changes. Sets `bIsInjured` flag and auto-transitions to/from `Injured` state if health crosses threshold.
|
||||
- **Parameters:** *(none — reads BPC_HealthSystem)*
|
||||
- **Blueprint Authority:** Internal (called by health change binding)
|
||||
- **Flow:**
|
||||
1. Get `HealthRatio` from `BPC_HealthSystem.CurrentHealth / MaxHealth`
|
||||
2. If `HealthRatio <= InjuryHealthThreshold` AND `!bIsInjured`:
|
||||
- Set `bIsInjured = true`
|
||||
- If CurrentState is Idle/Walking/Jogging/Sprinting/Crouching: `RequestStateChange(Injured, Tag.Health)`
|
||||
- Apply `InjurySpeedMultiplier` to movement
|
||||
- Fire `OnInjuryStateChanged(true, HealthRatio)`
|
||||
3. If `HealthRatio > InjuryHealthThreshold` AND `bIsInjured`:
|
||||
- Set `bIsInjured = false`
|
||||
- If CurrentState == Injured: `RequestStateChange(Idle, Tag.Health)`
|
||||
- Restore movement speed
|
||||
- Fire `OnInjuryStateChanged(false, HealthRatio)`
|
||||
|
||||
#### `IsInCombat` → `Boolean`
|
||||
- **Description:** Convenience — returns true if `CurrentState` is `SwingMelee`, `AimingFirearm`, `FiringFirearm`, `Reloading`, or `DeployingShield`.
|
||||
- **Parameters:** *(none)*
|
||||
- **Blueprint Authority:** Any (Pure)
|
||||
|
||||
#### `IsMovementBlocked` → `Boolean`
|
||||
- **Description:** Convenience — returns true if player cannot move at all. True when `CurrentState` is `Dead`, `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 `CurrentState` is `Injured`, `Crouching`, `Crawling`, `HoldingBreath`, `AimingFirearm`, `DeployingShield`, `Hiding`, `Peeking`, `GrabbingObject`, `PushingObject`, `UsingConsumable`.
|
||||
- **Parameters:** *(none)*
|
||||
- **Blueprint Authority:** Any (Pure)
|
||||
|
||||
#### `GetHeartRate` → `Float`
|
||||
- **Description:** Returns `CurrentHeartRate` in 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 |
|
||||
|-------|------|-------------|
|
||||
| `Tag` | `GameplayTag` | The flag tag to register |
|
||||
| `Duration` | `Float` (default -1.0) | How long the flag persists (-1 = permanent) |
|
||||
|
||||
#### `UnregisterActionFlag` → `void`
|
||||
- **Description:** Removes a previously registered action flag. Fires `OnActionFlagExpired` if the flag was active.
|
||||
- **Parameters:**
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `Tag` | `GameplayTag` | The flag tag to remove |
|
||||
|
||||
#### `HasActionFlag` → `Boolean`
|
||||
- **Description:** Returns true if the given tag is currently registered as an action flag.
|
||||
- **Parameters:**
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `Tag` | `GameplayTag` | The 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:**
|
||||
1. Get owner as Character (validate — log error and disable if invalid)
|
||||
2. Cache reference to `ABP_GASP` from character's skeletal mesh
|
||||
3. Set `CurrentState` = `DefaultState`, `CurrentOverlay` = `DefaultOverlay`
|
||||
4. Populate default `GatingRules` from `DA_StateGatingTable` if assigned
|
||||
5. Bind to `GI_GameFramework.OnGamePhaseChanged` (for game-phase-gated rules)
|
||||
6. Bind to `BPC_HealthSystem.OnDeath` (auto-call `ForceStateChange(Dead, "Death")`)
|
||||
7. Register with `GI_GameTagRegistry` for tag-based queries
|
||||
8. Bind to `BPC_HealthSystem.OnHealthChanged` → call `EvaluateInjuryState()`
|
||||
9. Bind to `BPC_StressSystem.OnStressTierChanged` → recalculate heart rate
|
||||
10. Bind to `BPC_StaminaSystem.OnExhaustionStateChanged` → recalculate heart rate
|
||||
11. Enable component tick (required for heart rate smoothing)
|
||||
|
||||
### Event: `Tick` → `UpdateHeartRate`
|
||||
- **Description:** Called every tick. Recalculates heart rate and evaluates vital signals.
|
||||
- **Flow:** (see `UpdateHeartRate` function flow above)
|
||||
|
||||
### Event: `OnComponentDestroyed`
|
||||
- **Description:** Cleanup — unbind all bound delegates, null cached references.
|
||||
- **Flow:**
|
||||
1. Unbind from `GI_GameFramework.OnGamePhaseChanged`
|
||||
2. Unbind from `BPC_HealthSystem.OnDeath` and `OnHealthChanged`
|
||||
3. Unbind from `BPC_StressSystem.OnStressTierChanged`
|
||||
4. Unbind from `BPC_StaminaSystem.OnExhaustionStateChanged`
|
||||
5. Set `CachedABPRef` = `None`
|
||||
|
||||
---
|
||||
|
||||
## 7. Blueprint Graph Logic Flow
|
||||
|
||||
### RequestStateChange Flow
|
||||
|
||||
```mermaid
|
||||
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
|
||||
|
||||
```mermaid
|
||||
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
|
||||
|
||||
- [ ] `RequestStateChange` with valid state returns `Permitted` and fires `OnStateChanged`
|
||||
- [ ] `RequestStateChange` while Dead (except to Idle) returns `Blocked_Dead`
|
||||
- [ ] `RequestStateChange` during transition queues and returns `Blocked_CurrentState`
|
||||
- [ ] `ForceStateChange` bypasses all gating and pushes to ForceStack
|
||||
- [ ] `RestorePreviousState` pops ForceStack and restores correct state
|
||||
- [ ] `IsActionPermitted` returns correct `S_ActionPermissionResult` for all 35+ actions
|
||||
- [ ] `SetOverlayState` correctly writes to `ABP_GASP.OverlayState`
|
||||
- [ ] Overlay change fires `OnOverlayStateChanged` dispatcher
|
||||
- [ ] Game phase change (MainMenu → InGame → Cutscene) correctly gates actions
|
||||
- [ ] Death auto-transitions to Dead state via `BPC_HealthSystem.OnDeath` binding
|
||||
- [ ] Respawn restores previous state from ForceStack
|
||||
- [ ] Rapid state change requests are throttled by `StateTransitionDelay`
|
||||
- [ ] `IsInCombat()` returns true for `SwingMelee`, `AimingFirearm`, `FiringFirearm`, `Reloading`, `DeployingShield`
|
||||
- [ ] `IsMovementBlocked()` 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: `ForceStateChange` called twice with same state — second call is no-op
|
||||
- [ ] Edge case: `RestorePreviousState` with empty ForceStack defaults to Idle
|
||||
- [ ] Edge case: State Manager handles owner destruction gracefully (null checks on cached ABP ref)
|
||||
- [ ] Edge case: `RequestStateChange` with 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 of `IsPlayerInState(X) && !IsPlayerInState(Y) && ...`
|
||||
- New states can be added to `E_PlayerActionState` and `DA_StateGatingTable` without modifying the State Manager blueprint logic.
|
||||
- The `DA_StateGatingTable` Data 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 `bIsInAction` and `ActionIntensity` allow 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_PlayerVitalSignals` drives 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`](../../architecture/bpc-statemanager.md)*
|
||||
252
docs/blueprints/16-state/131_DA_StateGatingTable.md
Normal file
252
docs/blueprints/16-state/131_DA_StateGatingTable.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# 131 — State Gating Table (`DA_StateGatingTable`)
|
||||
|
||||
## Purpose
|
||||
Data Asset that holds all state gating rules for the framework. Loaded by `BPC_StateManager` on BeginPlay to populate its `GatingRules` array. Designers modify gating rules entirely through this Data Asset — no blueprint changes required to add, remove, or tweak which states block which actions.
|
||||
|
||||
## Dependencies
|
||||
- **Requires:** `BPC_StateManager` (130 — loads this DA during Initialize), `E_PlayerActionState` (defined in BPC_StateManager), `S_StateGatingRule` (struct defined in BPC_StateManager), `E_GamePhase` (defined in GI_GameFramework / GS_CoreGameState)
|
||||
- **Required By:** `BPC_StateManager` (130 — direct consumer), Design Team (primary editor target)
|
||||
- **Engine/Plugin Requirements:** GameplayTags plugin
|
||||
|
||||
## Class Info
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Parent Class** | `PrimaryDataAsset` |
|
||||
| **Class Type** | Data Asset |
|
||||
| **Asset Path** | `Content/Framework/Core/DA_StateGatingTable.uasset` |
|
||||
| **Implements Interfaces** | *(none)* |
|
||||
|
||||
---
|
||||
|
||||
## 1. Enums
|
||||
|
||||
*Uses enums defined in BPC_StateManager (130) and GI_GameFramework (04):*
|
||||
- `E_PlayerActionState` — 45 exclusive action states
|
||||
- `E_GamePhase` — Game phases (MainMenu, InGame, Cutscene, Loading, etc.)
|
||||
|
||||
---
|
||||
|
||||
## 2. Structs
|
||||
|
||||
*Uses struct defined in BPC_StateManager (130):*
|
||||
|
||||
### `S_StateGatingRule` (repeated from 130 for reference)
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `ActionTag` | `GameplayTag` | The action being gated (e.g., `Action.Weapon.Fire`, `Action.Move.Jump`) |
|
||||
| `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) |
|
||||
|
||||
---
|
||||
|
||||
## 3. Variables
|
||||
|
||||
### Configuration (Instance Editable)
|
||||
|
||||
| Variable | Type | Default | Category | Description |
|
||||
|----------|------|---------|----------|-------------|
|
||||
| `GatingRules` | `Array<S_StateGatingRule>` | *(see default rules below)* | `State Gating` | All gating rules evaluated at runtime |
|
||||
| `DefaultStateTransitionDelay` | `Float` | `0.0` | `State Config` | Minimum seconds between state changes (anti-spam) |
|
||||
| `DefaultInjuryHealthThreshold` | `Float` | `0.30` | `State Config` | Health ratio (0-1) below which player becomes Injured |
|
||||
| `DefaultHeartRateBase` | `Float` | `70.0` | `State Config` | Base heart rate in BPM when calm |
|
||||
| `DefaultHeartRateMax` | `Float` | `180.0` | `State Config` | Maximum heart rate in BPM |
|
||||
| `DefaultState` | `E_PlayerActionState` | `Idle` | `State Config` | Default state on spawn/respawn |
|
||||
| `DefaultOverlay` | `E_OverlayState` | `Empty` | `State Config` | Default upper-body overlay |
|
||||
|
||||
---
|
||||
|
||||
## 4. Default Gating Rules — Complete Rule Set
|
||||
|
||||
The table below defines the **37 action gating rules** that ship as defaults in this Data Asset. Each row represents one `S_StateGatingRule` entry in the `GatingRules` array.
|
||||
|
||||
### Movement Actions
|
||||
|
||||
| Priority | ActionTag | BlockedStates | BlockedGamePhases | BlockReason |
|
||||
|----------|-----------|---------------|-------------------|-------------|
|
||||
| 1 | `Action.Move.Walk` | Dead, InCutscene, InDialogue, KnockedDown, Panicked, Exhausted, Sitting, InInventory, InJournal, InPauseMenu, UsingObject, GrabbingObject, InPhotoMode | MainMenu, Loading | Movement disabled |
|
||||
| 2 | `Action.Move.Sprint` | Dead, InCutscene, InDialogue, Crouching, Crawling, Hiding, Peeking, Sitting, KnockedDown, Exhausted, Panicked, Reloading, UsingConsumable, Injured | MainMenu, Loading | Sprint requires standing + uninjured |
|
||||
| 3 | `Action.Move.Crouch` | Dead, Vaulting, Climbing, Sliding, InCutscene, InDialogue, Sitting, KnockedDown, Exhausted, Panicked | MainMenu, Loading | Cannot crouch during traversal |
|
||||
| 4 | `Action.Move.Crawl` | Dead, Vaulting, Climbing, InCutscene, Sitting, KnockedDown | MainMenu, Loading | Must be prone first |
|
||||
| 5 | `Action.Move.Jump` | Dead, Hiding, Peeking, Crawling, InCutscene, InDialogue, Sitting, KnockedDown, Panicked, Exhausted, UsingObject, InInventory, InPauseMenu | MainMenu, Loading | Jump blocked |
|
||||
| 6 | `Action.Move.Sit` | Dead, Vaulting, Climbing, Hiding, SwingMelee, FiringFirearm, Reloading, KnockedDown, Panicked, InInventory, InCutscene, InDialogue | MainMenu, Loading | Sit requires neutral standing state |
|
||||
| 7 | `Action.Move.StandUp` | *(none — requires Sitting state, checked in BPC_StateManager)* | MainMenu, Loading | Must be sitting first |
|
||||
|
||||
### Traversal Actions
|
||||
|
||||
| Priority | ActionTag | BlockedStates | BlockedGamePhases | BlockReason |
|
||||
|----------|-----------|---------------|-------------------|-------------|
|
||||
| 10 | `Action.Traversal.Vault` | Dead, Hiding, Peeking, InCutscene, InDialogue, Reloading, SwingMelee, FiringFirearm, KnockedDown, Panicked | MainMenu, Cutscene, Loading | Traversal requires free hands |
|
||||
| 11 | `Action.Traversal.Climb` | Dead, InCutscene, InDialogue, Reloading, SwingMelee, KnockedDown, Panicked | MainMenu, Cutscene, Loading | Climbing requires free hands |
|
||||
| 12 | `Action.Traversal.Slide` | Dead, InCutscene, InDialogue, Reloading, SwingMelee, KnockedDown, Panicked | MainMenu, Cutscene, Loading | Slide requires two hands |
|
||||
|
||||
### Hiding Actions
|
||||
|
||||
| Priority | ActionTag | BlockedStates | BlockedGamePhases | BlockReason |
|
||||
|----------|-----------|---------------|-------------------|-------------|
|
||||
| 20 | `Action.Hide.Enter` | Dead, InCutscene, InDialogue, SwingMelee, FiringFirearm, Reloading, Vaulting, Climbing, KnockedDown, Panicked, Exhausted, GrabbingObject | MainMenu, Cutscene, Loading | Hide requires exposed state |
|
||||
| 21 | `Action.Hide.Peek` | Dead, SwingMelee, FiringFirearm, Reloading | *(none)* | Peek only from hidden |
|
||||
| 22 | `Action.Hide.Exit` | InCutscene | *(none)* | Forced exit allowed |
|
||||
|
||||
### Interaction Actions
|
||||
|
||||
| Priority | ActionTag | BlockedStates | BlockedGamePhases | BlockReason |
|
||||
|----------|-----------|---------------|-------------------|-------------|
|
||||
| 30 | `Action.Interact.Hold` | Dead, InCutscene, SwingMelee, FiringFirearm, Reloading, Hiding, Peeking, Vaulting, Climbing, KnockedDown, Panicked, InInventory, InPauseMenu, InDialogue, UsingObject | MainMenu, Cutscene, Loading | Interaction requires free state |
|
||||
| 31 | `Action.Interact.Instant` | Dead, InCutscene, KnockedDown, Panicked | MainMenu, Loading | Instant interaction only blocked by critical |
|
||||
|
||||
### Inventory Actions
|
||||
|
||||
| Priority | ActionTag | BlockedStates | BlockedGamePhases | BlockReason |
|
||||
|----------|-----------|---------------|-------------------|-------------|
|
||||
| 40 | `Action.Inventory.Open` | Dead, InCutscene, Vaulting, Climbing, SwingMelee, FiringFirearm, Reloading, KnockedDown, Panicked, Hiding, GrabbingObject, InDialogue, InPuzzle, InTrialScenario | MainMenu, Cutscene, Loading | Inventory blocked during actions |
|
||||
| 41 | `Action.Inventory.Close` | *(none)* | *(none)* | Close always works |
|
||||
| 42 | `Action.Inventory.Equip` | Dead, InCutscene, Vaulting, Climbing, KnockedDown, Panicked | MainMenu, Loading | Equip requires stability |
|
||||
| 43 | `Action.Inventory.UseItem` | Dead, InCutscene, Vaulting, Climbing, KnockedDown, Panicked, Hiding | MainMenu, Loading | Use requires stable state |
|
||||
|
||||
### Weapon Actions
|
||||
|
||||
| Priority | ActionTag | BlockedStates | BlockedGamePhases | BlockReason |
|
||||
|----------|-----------|---------------|-------------------|-------------|
|
||||
| 50 | `Action.Weapon.Fire` | Dead, Hiding, Peeking, Reloading, Vaulting, Climbing, InCutscene, InDialogue, KnockedDown, Panicked, InInventory, InPauseMenu, UsingConsumable, GrabbingObject | MainMenu, Loading | Cannot fire in these states |
|
||||
| 51 | `Action.Weapon.Aim` | Dead, Hiding, Peeking, Vaulting, Climbing, InCutscene, InDialogue, KnockedDown, Panicked, InInventory, InPauseMenu, GrabbingObject | MainMenu, Loading | Cannot aim |
|
||||
| 52 | `Action.Weapon.Reload` | Dead, Hiding, Peeking, Vaulting, Climbing, SwingMelee, InCutscene, InDialogue, KnockedDown, Panicked, InPauseMenu, GrabbingObject | MainMenu, Loading | Cannot reload |
|
||||
| 53 | `Action.Weapon.Melee` | Dead, Hiding, Peeking, Reloading, Vaulting, Climbing, InCutscene, InDialogue, KnockedDown, Panicked, InInventory, InPauseMenu, GrabbingObject, UsingConsumable | MainMenu, Loading | Cannot melee |
|
||||
| 54 | `Action.Weapon.Block` | Dead, Reloading, Vaulting, Climbing, InCutscene, KnockedDown, Panicked, Hiding, InInventory, InPauseMenu | MainMenu, Loading | Cannot block |
|
||||
|
||||
### Dialogue / Narrative Actions
|
||||
|
||||
| Priority | ActionTag | BlockedStates | BlockedGamePhases | BlockReason |
|
||||
|----------|-----------|---------------|-------------------|-------------|
|
||||
| 60 | `Action.Dialogue.Start` | Dead, InCutscene, Vaulting, Climbing, SwingMelee, FiringFirearm, Hiding, InInventory, InPauseMenu, Reloading, KnockedDown, Panicked | MainMenu, Cutscene, Loading | Dialogue requires neutral state |
|
||||
| 61 | `Action.Cutscene.Skip` | *(checked via bCanSkip flag in BPC_CutsceneBridge)* | *(none)* | Must be in cutscene |
|
||||
| 62 | `Action.HoldBreath` | *(only Hidable states, not Peeking — checked by BPC_HidingSystem)* | *(none)* | Breath hold only while hidden |
|
||||
|
||||
### Physics / World Actions
|
||||
|
||||
| Priority | ActionTag | BlockedStates | BlockedGamePhases | BlockReason |
|
||||
|----------|-----------|---------------|-------------------|-------------|
|
||||
| 70 | `Action.Grab.PhysicsObject` | Dead, InCutscene, Hiding, Vaulting, Climbing, Reloading, SwingMelee, FiringFirearm, KnockedDown, Panicked, InInventory, InPauseMenu | MainMenu, Loading | Grab requires free hands |
|
||||
| 71 | `Action.Puzzle.Use` | Dead, InCutscene, InDialogue, Vaulting, Climbing, Hiding, SwingMelee, FiringFirearm, Reloading, KnockedDown, Panicked | MainMenu, Loading | Puzzle requires focus |
|
||||
| 72 | `Action.Consumable.Use` | Dead, InCutscene, Vaulting, Climbing, Hiding, SwingMelee, FiringFirearm, Reloading, KnockedDown, Panicked, InInventory | MainMenu, Loading | Consumable requires free hands |
|
||||
|
||||
### UI / Menu Actions
|
||||
|
||||
| Priority | ActionTag | BlockedStates | BlockedGamePhases | BlockReason |
|
||||
|----------|-----------|---------------|-------------------|-------------|
|
||||
| 80 | `Action.Pause.Open` | InCutscene, Dead, InMainMenu | Loading | Cannot pause in cutscene/death |
|
||||
| 81 | `Action.Pause.Close` | *(none)* | *(none)* | Close always works |
|
||||
| 82 | `Action.Journal.Open` | Dead, InCutscene, InDialogue, Vaulting, Climbing, Hiding, SwingMelee, FiringFirearm, Reloading, KnockedDown, Panicked, InInventory, InPuzzle, InTrialScenario | MainMenu, Cutscene, Loading | Journal requires neutral state |
|
||||
| 83 | `Action.Settings.Open` | *(none)* | Cutscene, Loading | Settings limited during cutscene |
|
||||
| 84 | `Action.Map.Open` | Dead, InCutscene, Vaulting, Climbing, Hiding, InInventory, InPauseMenu | MainMenu, Loading | Map in neutral/explore states |
|
||||
|
||||
### Throwable / Photo Actions
|
||||
|
||||
| Priority | ActionTag | BlockedStates | BlockedGamePhases | BlockReason |
|
||||
|----------|-----------|---------------|-------------------|-------------|
|
||||
| 90 | `Action.Throwable.Aim` | Dead, Hiding, Peeking, Vaulting, Climbing, InCutscene, InDialogue, Reloading, KnockedDown, Panicked, InInventory, InPauseMenu | MainMenu, Loading | Throw requires free hands |
|
||||
| 91 | `Action.Throwable.Throw` | Dead, Hiding, Peeking, Vaulting, Climbing, InCutscene, InDialogue, Reloading, KnockedDown, Panicked, InInventory, InPauseMenu | MainMenu, Loading | Must be aiming first |
|
||||
| 92 | `Action.PhotoMode.Enter` | Dead, InCutscene, InDialogue, Vaulting, Climbing, Hiding, SwingMelee, FiringFirearm, KnockedDown, Panicked | MainMenu, Loading | Photo mode requires neutral state |
|
||||
| 93 | `Action.PhotoMode.Exit` | *(none — checked by state)* | *(none)* | Must be in photo mode |
|
||||
|
||||
---
|
||||
|
||||
## 5. Functions
|
||||
|
||||
### Public Functions
|
||||
|
||||
#### `GetRulesForAction` → `Array<S_StateGatingRule>`
|
||||
- **Description:** Returns all gating rules that match a given ActionTag. Called by `BPC_StateManager.IsActionPermitted`.
|
||||
- **Parameters:**
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `ActionTag` | `GameplayTag` | The action tag to look up |
|
||||
- **Blueprint Authority:** Any (Pure)
|
||||
- **Flow:** Iterates `GatingRules` array, returns all entries where `ActionTag` matches.
|
||||
|
||||
#### `GetRuleByPriority` → `S_StateGatingRule`
|
||||
- **Description:** Returns the highest-priority (lowest Priority value) rule for a given action tag. If multiple rules match, the one with the lowest `Priority` wins.
|
||||
- **Parameters:**
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `ActionTag` | `GameplayTag` | The action tag to look up |
|
||||
- **Blueprint Authority:** Any (Pure)
|
||||
|
||||
#### `HasAnyBlockedState` → `Boolean`
|
||||
- **Description:** Returns true if any rule's `BlockedStates` contains the given state. Used for debugging/validation.
|
||||
- **Parameters:**
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `State` | `E_PlayerActionState` | The state to check |
|
||||
- **Blueprint Authority:** Any (Pure)
|
||||
|
||||
---
|
||||
|
||||
## 6. Data Asset Edibility Notes
|
||||
|
||||
### Designer Workflow
|
||||
1. Open `DA_StateGatingTable` in the Content Browser
|
||||
2. Edit the `GatingRules` array directly in the Details Panel
|
||||
3. Each rule maps one `ActionTag` to its blocked states, blocked game phases, and required/blocked tags
|
||||
4. Set `Priority` to control evaluation order (lower = checked first)
|
||||
5. Set `BlockReason` to the text displayed to the player when this action is denied
|
||||
6. Changes take effect on next `BeginPlay` — no recompilation required
|
||||
|
||||
### Adding a New Action
|
||||
1. Create the `GameplayTag` (e.g., `Action.Weapon.Throw`)
|
||||
2. Add a new `S_StateGatingRule` entry to the `GatingRules` array
|
||||
3. Set `ActionTag` to the new tag
|
||||
4. Populate `BlockedStates` with all states that should prevent this action
|
||||
5. Set `BlockReason` to a user-facing message
|
||||
6. Optionally set `RequiredTags` / `BlockedTags` for item/equipment-based gating
|
||||
|
||||
### Adding a New State
|
||||
1. Add the new value to `E_PlayerActionState` enum
|
||||
2. Update any existing gating rules whose `BlockedStates` should include the new state
|
||||
3. Optionally create new gating rules that use the new state as a blocker
|
||||
4. No blueprint changes needed in `BPC_StateManager` — it reads `GatingRules` dynamically
|
||||
|
||||
---
|
||||
|
||||
## 7. Communication Matrix
|
||||
|
||||
| Who Talks | How | What Is Sent |
|
||||
|-----------|-----|-------------|
|
||||
| `BPC_StateManager` (130) | `Direct: Load Data Asset` on BeginPlay | Reads entire `GatingRules` array |
|
||||
| `BPC_StateManager` (130) | `Function Call: GetRulesForAction` | Queries rules by `GameplayTag` |
|
||||
| Design Team | Data Asset Editor | Edits `GatingRules`, `DefaultStateTransitionDelay`, thresholds |
|
||||
|
||||
---
|
||||
|
||||
## 8. Validation / Testing Checklist
|
||||
|
||||
- [ ] Data Asset loads correctly in `BPC_StateManager.BeginPlay`
|
||||
- [ ] All 37 default gating rules are present in the array
|
||||
- [ ] Each rule has a non-empty `ActionTag`
|
||||
- [ ] Each rule with `BlockedStates` has at least one state
|
||||
- [ ] Each rule has a non-empty `BlockReason` text
|
||||
- [ ] Priority values are correctly ordered (movement=1-9, traversal=10-19, hiding=20-29, etc.)
|
||||
- [ ] No duplicate `ActionTag` entries with the same `Priority` (ambiguity)
|
||||
- [ ] `DefaultStateTransitionDelay` > 0 prevents rapid-fire state changes
|
||||
- [ ] `DefaultInjuryHealthThreshold` between 0.0 and 1.0
|
||||
- [ ] `DefaultHeartRateBase` < `DefaultHeartRateMax`
|
||||
- [ ] Edge case: Empty `GatingRules` array → `BPC_StateManager` falls back to blueprint-default rules
|
||||
- [ ] Edge case: Null `ActionTag` in a rule → skipped with warning log
|
||||
|
||||
---
|
||||
|
||||
## 9. Reuse Notes
|
||||
|
||||
- This Data Asset is the **designer-facing interface** to the entire state gating system. No blueprint access required.
|
||||
- Multiple `DA_StateGatingTable` instances can exist for different game modes or difficulty levels (e.g., `DA_StateGatingTable_Hardcore`, `DA_StateGatingTable_Easy`).
|
||||
- The `Priority` field in `S_StateGatingRule` controls evaluation order. Use standard ranges: Movement=1-9, Traversal=10-19, Hiding=20-29, Interaction=30-39, Inventory=40-49, Weapons=50-59, Narrative=60-69, Physics=70-79, UI=80-89, Throwable/Photo=90-99.
|
||||
- `BlockedGamePhases` allows cutting off actions globally during MainMenu, Cutscene, and Loading without per-state rules.
|
||||
- `RequiredTags` and `BlockedTags` enable item/equipment-based gating beyond state checking (e.g., "Sprint requires Tag.Equipment.NotExhausted").
|
||||
- The `BlockReason` text is displayed directly to the player via `WBP_InteractionPromptDisplay` — use clear, localized strings.
|
||||
|
||||
---
|
||||
|
||||
*Blueprint Spec: DA_StateGatingTable — Designer-editable gating rules Data Asset. Reference architecture document: [`bpc-statemanager.md`](../../architecture/bpc-statemanager.md) Section 9.*
|
||||
Reference in New Issue
Block a user