# 61 — BPC_AIStateMachine

## Blueprint Spec — UE 5.5–5.7

---

### Parent Class
`ActorComponent`

### Dependencies
- [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) — Owner controller, blackboard access
- [`BPC_AlertSystem`](60_BPC_AlertSystem.md) — Alert level triggers state transitions
- [`BPC_PerceptionComponent`](56_BPC_PerceptionComponent.md) — Stimulus input
- [`BPC_BehaviorTreeManager`](57_BPC_BehaviorTreeManager.md) — Tree lifecycle requests
- [`BP_EnemyBase`](58_BP_EnemyBase.md) — Pawn animation state requests
- [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) — State timings, cooldowns, transition rules

### Purpose
High-level finite state machine for AI behavior. Manages valid state transitions, cooldowns, priority overrides, and lifecycle events for each AI state (Patrol, Investigate, Search, Combat, Flee, Idle, Stunned). Interprets alert level changes from the alert system and resolves transitions with rule checking before delegating to the behavior tree manager.

### Enums

| Enum | Values | Description |
|------|--------|-------------|
| `EAIState` | Idle, Patrol, Investigate, Search, Combat, Flee, Stunned, Dead | All possible AI states |
| `EStateTransitionRule` | Allow, Block (Cooldown), Block (Priority), Block (Health), Block (Narrative) | Transition permission results |
| `EStatePriority` | None, Low, Medium, High, Critical | State override priority |

### Structs

| Struct | Fields | Description |
|--------|--------|-------------|
| `FStateConfig` | State: EAIState, bCanTransitionFrom: Array<EAIState>, MinTimeInState: Float, MaxTimeInState: Float, CooldownAfterExit: Float, Priority: EStatePriority, bAllowInterrupt: Bool, AssociatedTree: EBehaviorTreeType | Configuration per state |
| `FStateInstance` | State: EAIState, EnterTime: Float, LastExitTime: Float, bIsActive: Bool, CustomData: FName (tag), bCanInterrupt: Bool | Runtime state tracking |
| `FTransitionRequest` | RequestedState: EAIState, Priority: EStatePriority, Reason: FName, Instigator: AActor | Queued transition |

### Variables

| Name | Type | Description |
|------|------|-------------|
| `CurrentState` | EAIState | Active state |
| `PreviousState` | EAIState | State before last transition |
| `StateMap` | Map<EAIState, FStateConfig> | All state configs from profile |
| `StateInstances` | Map<EAIState, FStateInstance> | Runtime state trackers |
| `PendingTransitions` | Array<FTransitionRequest> | Queue of requested transitions |
| `bIsTransitioning` | Bool | Lock during transition processing |
| `MaxTransitionsPerTick` | Int | Safety limit (default 3) |
| `bLockedByNarrative` | Bool | Story script lock |
| `NarrativeLockReason` | FName | Why narrative locked AI |
| `StunnedDuration` | Float | Current stun timer remaining |
| `FleeThreshold` | Float | Health % below which AI flees |
| `bCanFlee` | Bool | Permission to flee |

### Functions — Core

| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `RequestTransition` | NewState: EAIState, Priority: EStatePriority, Reason: FName, Instigator: AActor | Bool | Queue or execute state change |
| `ProcessPendingTransitions` | — | — | Evaluate queue on tick |
| `EvaluateTransitionRules` | FromState: EAIState, ToState: EAIState | EStateTransitionRule | Check if transition is allowed |
| `ForceTransition` | NewState: EAIState | — | Bypass rules (narrative/debug) |
| `GetCurrentState` | — | EAIState | BB getter |
| `GetTimeInCurrentState` | — | Float | Duration in current state |
| `CanTransitionTo` | NewState: EAIState | Bool | Public permission check |
| `IsStateActive` | State: EAIState | Bool | Check any state |

### Functions — State Lifecycle

| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `EnterState` | NewState: EAIState, Instigator: AActor | — | State entry logic |
| `ExitState` | CurrentState: EAIState | — | State exit cleanup |
| `UpdateState` | DeltaTime: Float | — | Per-tick state behavior |
| `OnStateEnter_Patrol` | — | — | Setup patrol BB keys |
| `OnStateEnter_Investigate` | Location: FVector | — | Setup investigation |
| `OnStateEnter_Search` | LastKnownLocation: FVector | — | Setup search area |
| `OnStateEnter_Combat` | Target: AActor | — | Combat initialization |
| `OnStateEnter_Flee` | ThreatLocation: FVector | — | Flee setup |
| `OnStateEnter_Stunned` | Duration: Float | — | Stun timer |
| `OnStateEnter_Idle` | — | — | Idle setup |
| `OnStateEnter_Dead` | — | — | Death state |
| `OnStateExit_All` | — | — | Common exit behavior |

### Functions — Transition Rules

| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `CheckCooldown` | State: EAIState | Bool | Has cooldown expired |
| `CheckHealthThreshold` | ToState: EAIState | Bool | Health gate check for flee/stun |
| `CheckNarrativeLock` | — | Bool | Is AI locked by script |
| `CheckPriorityOverride` | Request: FTransitionRequest | Bool | Higher priority can interrupt |
| `CheckMinStateTime` | — | Bool | Minimum time in current state met |

### Blueprint Flow — Transition Resolution

```
[RequestTransition]
  └─► If bLockedByNarrative AND not ForceTransition: deny (return false)
  └─► If bIsTransitioning: queue to PendingTransitions, return false
  └─► EvaluateTransitionRules:
         Call CheckNarrativeLock --> if blocked, deny
         Call CheckCooldown(FromState) --> if active, deny
         Call CheckMinStateTime --> if not met, queue or deny
         Call CheckPriorityOverride --> if request priority >= current, allow
         Else: deny or queue
  └─► If Allowed:
         bIsTransitioning = true
         ExitState(CurrentState)
         EnterState(NewState)
         Update blackboard
         Request BehaviorTree switch from BT Manager
         bIsTransitioning = false
         Return true

[ProcessPendingTransitions]
  └─► Sort PendingTransitions by Priority (high first)
  └─► For each (up to MaxTransitionsPerTick):
         Try RequestTransition again
         If success: remove from queue
         If fails again: increment failure counter, remove if > 3 attempts

[Event Tick]
  └─► UpdateState(DeltaTime):
         Switch CurrentState:
             Patrol: Check patrol timer, check patrol completion
             Investigate: Update investigation timer, check timeout
             Search: Update search timer, check coverage
             Combat: Update combat timer, check health for flee
             Flee: Update flee timer, check distance from threat
             Stunned: StunnedDuration -= DeltaTime; if <= 0, transition
             Idle: Update idle timer
         Check external triggers:
             If bCanFlee && Health < FleeThreshold: RequestTransition(Flee)
             If AlertSystem.IsInCombat && CurrentState != Combat: RequestTransition(Combat)
```

### State Transition Map (Allowed)

| From \ To | Idle | Patrol | Investigate | Search | Combat | Flee | Stunned | Dead |
|-----------|------|--------|------------|--------|--------|------|---------|------|
| Idle | — | Yes | Yes | No | Yes | No | Yes | Yes |
| Patrol | Yes | — | Yes | No | Yes | No | Yes | Yes |
| Investigate | Yes | Yes | — | Yes | Yes | No | Yes | Yes |
| Search | Yes | Yes | No | — | Yes | Yes | Yes | Yes |
| Combat | No | No | No | No | — | Yes | Yes | Yes |
| Flee | Yes | Yes | No | No | No | — | Yes | Yes |
| Stunned | Yes | Yes | Yes | No | Yes | Yes | — | Yes |
| Dead | No | No | No | No | No | No | No | — |

### Event Dispatchers

| Name | Delegate Signature | Purpose |
|------|-------------------|---------|
| `OnAIStateChanged` | EAIState NewState, EAIState PreviousState, AActor Instigator | For all listeners |
| `OnStateTransitionBlocked` | EAIState Requested, FStateTransitionRule Reason | Debug logging |
| `OnAIDefeated` | AActor Killer | Death sequence |
| `OnAIFled` | — | Flee started |
| `OnAIStunned` | Float Duration | Stun entered |
| `OnAIStateTimerWarning` | EAIState, Float RemainingTime | State expiry warning |

### Communications With

| Target | Method | Why |
|--------|--------|-----|
| [`BPC_AlertSystem`](60_BPC_AlertSystem.md) | Event listener | Alert level changes trigger transitions |
| [`BPC_BehaviorTreeManager`](57_BPC_BehaviorTreeManager.md) | Direct call | Request tree switch on state enter |
| [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) | Owner reference | Update BB keys |
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Event dispatcher | Trigger state animations |
| [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) | Data lookup | Load state configs |
| [`BPC_PerceptionComponent`](56_BPC_PerceptionComponent.md) | Direct call | Get stimulus for investigate |
| Narrative system | Event dispatcher | Narrative lock/unlock |
| [`BPC_HealthComponent`](../01-player/01_BPC_HealthComponent.md) | Event listener | Health threshold checks |

### Reuse Notes
- One per AIController; state configs defined per AI type in DA_AIProfile
- Priority queue prevents infinite loops during rapid state changes
- Narrative lock allows scripted moments where AI cannot react
- Stun state can be triggered by weapons, environment, or narrative events
- Flee uses the same path system as patrol (inverted direction)
- Dead state is terminal — no transitions out (death is hard finality by design for this horror framework)