# BPC_StateManager — Implementation Checklist **Target:** Code Agent (Game Dev mode) **Architecture Doc:** [`docs/architecture/bpc-statemanager.md`](../architecture/bpc-statemanager.md) **Parent System:** Player Character (sits alongside BPC_MovementStateSystem, BPC_HealthSystem, etc.) --- ## Pre-Implementation Verification - [ ] Read [`docs/architecture/bpc-statemanager.md`](../architecture/bpc-statemanager.md) — Sections 4–7 (Enums, Structs, Variables, Functions) - [ ] Read [`CONTEXT.md`](../../CONTEXT.md) — Sections "State Management Conventions" and "Animation Notify Contract" - [ ] Confirm GASP AnimBP (`ABP_GASP`) has variable `OverlayState` (enum), `bIsInAction` (bool), `ActionIntensity` (float 0–1) - [ ] Confirm `BPC_MovementStateSystem` (11) fires dispatchers `OnMovementModeChanged`, `OnPostureChanged`, `OnSprintStateChanged` - [ ] Confirm `BPC_HealthSystem` (08) fires dispatcher `OnDeath` with death context - [ ] Confirm `GI_GameFramework` (04) has `E_GamePhase` enum and fires `OnGamePhaseChanged` --- ## Phase 1 — Enum & Struct Creation (Core/) ### Step 1.1: Create `E_PlayerActionState` Enum - [ ] Asset path: `Content/Framework/Core/E_PlayerActionState` - [ ] 42 values per [Section 4.1](../architecture/bpc-statemanager.md#41-e_playeractionstate--exclusive-action-states) - [ ] Display names match exactly ### Step 1.2: Create `E_OverlayState` Enum - [ ] Asset path: `Content/Framework/Core/E_OverlayState` - [ ] 18 values per [Section 4.2](../architecture/bpc-statemanager.md#42-e_overlaystate--upper-body-overlay-for-gasp-animbp) - [ ] Display names match exactly ### Step 1.3: Create `E_ActionRequestResult` Enum - [ ] Asset path: `Content/Framework/Core/E_ActionRequestResult` - [ ] 7 values per [Section 4.3](../architecture/bpc-statemanager.md#43-e_actionrequestresult) ### Step 1.4: Create Structs - [ ] `S_StateChangeRequest` — fields per [Section 5.1](../architecture/bpc-statemanager.md#51-s_statechangerequest) - [ ] `S_StateGatingRule` — fields per [Section 5.2](../architecture/bpc-statemanager.md#52-s_stategatingrule) - [ ] `S_ActionPermissionResult` — fields per [Section 5.3](../architecture/bpc-statemanager.md#53-s_actionpermissionresult) ### Step 1.5: Create `DA_StateGatingTable` Data Asset - [ ] Parent class: `PrimaryDataAsset` - [ ] Asset path: `Content/Framework/Core/DA_StateGatingTable` - [ ] Variable: `GatingRules: Array` - [ ] Populate with [Section 9 gating rules](../architecture/bpc-statemanager.md#9-state-gating-function-table) (25+ rules) --- ## Phase 2 — BPC_StateManager Component Creation ### Step 2.1: Create Blueprint Component - [ ] Asset path: `Content/Framework/Player/BPC_StateManager` - [ ] Parent class: `ActorComponent` - [ ] Network: Check `bReplicates = true` if multiplayer planned ### Step 2.2: Add Configuration Variables Per [Section 6 — Configuration Variables](../architecture/bpc-statemanager.md#6-variables): - [ ] `GatingRules: Array` — Expose On Spawn - [ ] `DefaultState: E_PlayerActionState` — default `Idle` - [ ] `bLogStateTransitions: Boolean` — default `false` - [ ] `StateTransitionDelay: Float` — default `0.0` - [ ] `DefaultOverlay: E_OverlayState` — default `Empty` ### Step 2.3: Add Internal Variables Per [Section 6 — Internal Variables](../architecture/bpc-statemanager.md#6-variables): - [ ] `CurrentState: E_PlayerActionState` — default `Idle` - [ ] `PreviousState: E_PlayerActionState` — default `Idle` - [ ] `CurrentOverlay: E_OverlayState` — default `Empty` - [ ] `LastStateChangeTime: Float` - [ ] `StateHistory: Array` - [ ] `ForceStack: Array` - [ ] `bIsTransitioning: Boolean` - [ ] `PendingRequests: Array` - [ ] `CachedABPRef: ABP_GASP` (soft object reference) - [ ] `CurrentActionFlags: Map` --- ## Phase 3 — Event Dispatchers Create all 8 dispatchers per [Section 8](../architecture/bpc-statemanager.md#8-event-dispatchers): - [ ] `OnStateChanged` → `OldState: E_PlayerActionState`, `NewState: E_PlayerActionState` - [ ] `OnStateChangeRequested` → `Request: S_StateChangeRequest` - [ ] `OnStateChangeDenied` → `Request: S_StateChangeRequest`, `Result: E_ActionRequestResult`, `Reason: Text` - [ ] `OnOverlayStateChanged` → `OldOverlay: E_OverlayState`, `NewOverlay: E_OverlayState` - [ ] `OnActionFlagRegistered` → `Tag: GameplayTag`, `Duration: Float` - [ ] `OnActionFlagExpired` → `Tag: GameplayTag` - [ ] `OnForceOverridePushed` → `ForcedState: E_PlayerActionState`, `PoppedState: E_PlayerActionState` - [ ] `OnForceOverridePopped` → `RestoredState: E_PlayerActionState` --- ## Phase 4 — Core Functions ### Step 4.1: `BeginPlay` (Override) Per [Section 7.13](../architecture/bpc-statemanager.md#713-initialize-beginplay-override): - [ ] Get Owner as Character - [ ] Cache `ABP_GASP` from character's skeletal mesh `->GetAnimInstance()` - [ ] Set `CurrentState = DefaultState`, `CurrentOverlay = DefaultOverlay` - [ ] Load `GatingRules` from `DA_StateGatingTable` if assigned - [ ] Bind to `GI_GameFramework.OnGamePhaseChanged` - [ ] Bind to `BPC_HealthSystem.OnDeath` → call `ForceStateChange(Dead, "Death")` ### Step 4.2: `RequestStateChange` Per [Section 7.1](../architecture/bpc-statemanager.md#71-requeststatechange): - [ ] **Priority 100 check:** If `Priority == 100` → bypass gating → go to transition - [ ] **Transition guard:** If `bIsTransitioning` → queue request → return `Blocked_CurrentState` - [ ] **Cooldown check:** If `GameTime - LastStateChangeTime < StateTransitionDelay` → return `Blocked_Cooldown` - [ ] **Dead check:** If `CurrentState == Dead` AND `NewState != Idle` → return `Blocked_Dead` - [ ] **Gating evaluation:** Iterate `GatingRules` matching `RequesterTag`: - BlockedStates contains NewState → return `Blocked_CurrentState` with reason - BlockedGamePhases contains CurrentGamePhase → return `Blocked_GamePhase` - RequiredTags missing or BlockedTags present → return appropriate result - [ ] **Transition:** Call `ExecuteStateTransition(NewState)` - [ ] Return `Permitted` ### Step 4.3: `ExecuteStateTransition` (Private) - [ ] Set `bIsTransitioning = true` - [ ] `PreviousState = CurrentState` - [ ] `CurrentState = NewState` - [ ] `LastStateChangeTime = GameTime` - [ ] Push `NewState` onto `StateHistory` (max 20 entries) - [ ] Update GASP: `ABP_GASP.bIsInAction = (NewState in action states)` - [ ] Update GASP: `ABP_GASP.ActionIntensity` per state mapping - [ ] Fire `OnStateChanged` dispatcher - [ ] Notify `SS_EnhancedInputManager` of context change - [ ] If `bLogStateTransitions` → print transition to log - [ ] Set `bIsTransitioning = false` - [ ] Process `PendingRequests` queue ### Step 4.4: `ForceStateChange` Per [Section 7.2](../architecture/bpc-statemanager.md#72-forcestatechange): - [ ] If `ForceStack` top == `NewState` → return (no-op) - [ ] Push `CurrentState` onto `ForceStack` - [ ] Fire `OnForceOverridePushed(NewState, CurrentState)` - [ ] Call `ExecuteStateTransition(NewState)` - [ ] Log reason if `bLogStateTransitions` ### Step 4.5: `RestorePreviousState` Per [Section 7.3](../architecture/bpc-statemanager.md#73-restorepreviousstate): - [ ] If `ForceStack` is empty → set `NewState = Idle` - [ ] Else → Pop `ForceStack` → `NewState = popped` - [ ] Fire `OnForceOverridePopped(NewState)` - [ ] Call `ExecuteStateTransition(NewState)` ### Step 4.6: `IsActionPermitted` Per [Section 7.6](../architecture/bpc-statemanager.md#76-isactionpermitted): - [ ] Find all `GatingRules` where `ActionTag` matches - [ ] Check CurrentState, GamePhase, RequiredTags, BlockedTags - [ ] Return `S_ActionPermissionResult` with bool, reason, result code ### Step 4.7: `SetOverlayState` Per [Section 7.7](../architecture/bpc-statemanager.md#77-setoverlaystate): - [ ] If `Overlay == CurrentOverlay` → return - [ ] `CurrentOverlay = Overlay` - [ ] Set `ABP_GASP.OverlayState = Overlay` if cached ref valid - [ ] Fire `OnOverlayStateChanged` - [ ] Notify `BPC_EmbodimentSystem` (if on same actor) ### Step 4.8: Convenience Functions - [ ] `GetCurrentState()` → pure getter - [ ] `GetPreviousState()` → pure getter - [ ] `CanTransitionTo(TargetState)` → pre-flight check (calls gating, no transition) - [ ] `IsInCombat()` → CurrentState in `[SwingMelee, AimingFirearm, FiringFirearm, Reloading, DeployingShield]` - [ ] `IsMovementBlocked()` → CurrentState in blocking list per Section 7.10 - [ ] `CanEquipWeapon()` → delegates to `IsActionPermitted(Action.Weapon.Equip)` ### Step 4.9: Action Flag Functions - [ ] `RegisterActionFlag(Tag, Duration)` → add to `CurrentActionFlags` map, start timer if Duration > 0 - [ ] `UnregisterActionFlag(Tag)` → remove from map, clear timer - [ ] `HasActionFlag(Tag)` → lookup in map --- ## Phase 5 — GASP Integration Per [Section 10](../architecture/bpc-statemanager.md#10-gasp-integration-notes): - [ ] **Read from GASP:** Subscribe to `ABP_GASP` dispatchers for `bStrafing`, `bSprinting`, `MovementMode`, `GroundState` - [ ] **Write to GASP:** Set `OverlayState`, `bIsInAction`, `ActionIntensity` as variables on cached AnimBP ref - [ ] **Never touch:** Motion Matching database, Camera Manager, GASP internal state machine, root motion - [ ] **Overlay pipeline:** Verify `SetOverlayState(Pistol)` → blends upper body correctly --- ## Phase 6 — System Integration (Bind Other Systems) - [ ] **`BPC_HealthSystem` (08):** Bind `OnDeath` → `ForceStateChange(Dead)`. Bind `OnRespawn` → `RestorePreviousState()` - [ ] **`BPC_MovementStateSystem` (11):** `OnMovementModeChanged` sets walking/jogging/sprinting state. `OnPostureChanged` sets crouching/crawling - [ ] **`BPC_HidingSystem` (12):** `OnHideStateChanged` → Hidden triggers `RequestStateChange(Hiding)`; Exposed triggers restore - [ ] **`BPC_ContextualTraversalSystem` (21):** Traversal start → `RequestStateChange(Vaulting/Climbing/Sliding)`; end → restore - [ ] **`BPC_InteractionDetector` (16):** Interaction start → `RequestStateChange(Interacting)`; end → restore - [ ] **`BPC_MeleeSystem` (76):** StartSwing → `RequestStateChange(SwingMelee)`; OnSwingComplete → restore - [ ] **`BPC_FirearmSystem` (74):** Fire → `RequestStateChange(FiringFirearm)` (transient, auto-restore) - [ ] **`BPC_ReloadSystem` (78):** StartReload → `RequestStateChange(Reloading)`; Complete → restore - [ ] **`BPC_DeathHandlingSystem` (39):** OnPlayerDeath → `ForceStateChange(Dead)`. OnRespawn → `RestorePreviousState()` - [ ] **`SS_UIManager` (44):** Inventory open → `RequestStateChange(InInventory)`; close → restore. Pause → `InPauseMenu` - [ ] **`BPC_DialoguePlaybackSystem` (60):** Dialogue start → `RequestStateChange(InDialogue)`; end → restore - [ ] **`BPC_CutsceneBridge` (64):** PlayCutscene → `ForceStateChange(InCutscene)`. OnCutsceneCompleted → `RestorePreviousState()` - [ ] **`BPC_AltDeathSpaceSystem` (38):** Enter → `ForceStateChange(InVoidSpace)`. Exit → `RestorePreviousState()` - [ ] **`SS_EnhancedInputManager` (128):** Bind to `OnStateChanged` → switch `IMC_` contexts based on state - [ ] **`BPC_CameraStateLayer` (14):** Bind to `OnStateChanged` → adjust FOV/offset per state --- ## Phase 7 — Integration Pattern (Replace Hardcoded State Checks) For each of the 36+ state-gated actions: **OLD pattern (remove):** ``` If (PlayerState != Dead && PlayerState != Hiding && PlayerState != Cutscene...) → Execute Action ``` **NEW pattern (replace with):** ``` If (BPC_StateManager.IsActionPermitted(ActionTag).bPermitted) → Execute Action Else → Show blocked feedback using result.BlockReason ``` ### Systems to Update: - [ ] `BPC_HidingSystem.EnterHideSpot` → check `Action.Hide.Enter` - [ ] `BPC_FirearmSystem.Fire` → check `Action.Weapon.Fire` - [ ] `BPC_ReloadSystem.StartReload` → check `Action.Weapon.Reload` - [ ] `BPC_MeleeSystem.StartSwing` → check `Action.Weapon.Melee` - [ ] `SS_UIManager.OpenInventory` → check `Action.Inventory.Open` - [ ] `BPC_InteractionDetector.PerformInteraction` → check `Action.Interact.Hold` - [ ] `BPC_ContextualTraversalSystem.AttemptTraversal` → check `Action.Traversal.*` - [ ] `BPC_EquipmentSlotSystem.EquipItem` → check `Action.Inventory.Equip` - [ ] `BPC_ConsumableSystem.UseConsumable` → check `Action.Consumable.Use` - [ ] `BPC_PhysicsDragSystem.GrabObject` → check `Action.Grab.PhysicsObject` - [ ] `BPC_DialoguePlaybackSystem.StartDialogue` → check `Action.Dialogue.Start` - [ ] `WBP_PauseMenu.OpenPause` → check `Action.Pause.Open` --- ## Phase 8 — Testing Per [Section 13 Testing Checklist](../architecture/bpc-statemanager.md#13-testing-checklist): - [ ] Valid state changes return `Permitted` - [ ] Dead blocks all actions except respawn - [ ] Force bypasses all gating - [ ] ForceStack push/pop restores correct state - [ ] Overlay state correctly writes to GASP - [ ] Game phase changes gate actions correctly - [ ] Rapid requests are throttled by `StateTransitionDelay` - [ ] 25+ action gating rules all work - [ ] Edge case: double Force same state = no-op - [ ] Edge case: RestorePreviousState with empty stack = Idle - [ ] Edge case: Owner destroyed during transition = graceful null check --- ## Files Created (Summary) | File | Type | Path | |------|------|------| | `E_PlayerActionState` | Enum | `Content/Framework/Core/` | | `E_OverlayState` | Enum | `Content/Framework/Core/` | | `E_ActionRequestResult` | Enum | `Content/Framework/Core/` | | `S_StateChangeRequest` | Struct | (Blueprint-internal) | | `S_StateGatingRule` | Struct | (Blueprint-internal) | | `S_ActionPermissionResult` | Struct | (Blueprint-internal) | | `DA_StateGatingTable` | Data Asset | `Content/Framework/Core/` | | `BPC_StateManager` | Blueprint Component | `Content/Framework/Player/` | --- *Checklist v1.0 — Aligned with [`docs/architecture/bpc-statemanager.md`](../architecture/bpc-statemanager.md). Ready for Code agent implementation.*