# 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<S_StateGatingRule>`
- [ ] 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<S_StateGatingRule>` — 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<E_PlayerActionState>`
- [ ] `ForceStack: Array<E_PlayerActionState>`
- [ ] `bIsTransitioning: Boolean`
- [ ] `PendingRequests: Array<S_StateChangeRequest>`
- [ ] `CachedABPRef: ABP_GASP` (soft object reference)
- [ ] `CurrentActionFlags: Map<GameplayTag, Float>`

---

## 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.*