# 60 — BPC_AlertSystem

## Blueprint Spec — UE 5.5–5.7

---

### Parent Class
`ActorComponent`

### Dependencies
- [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) — Owner controller
- [`BP_EnemyBase`](58_BP_EnemyBase.md) — Pawn that receives commands
- [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) — Alert thresholds and response times
- [`BPC_PerceptionComponent`](56_BPC_PerceptionComponent.md) — Stimulus input source
- [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) — State transition triggers
- [`I_Damageable`](../01-foundation/05_I_Damageable.md) — Damage stimulus source

### Purpose
Manages AI threat levels, alert escalation, and reinforcement propagation. Receives stimuli from perception and damage systems, evaluates threat severity, escalates through alert levels (Unaware → Suspicious → Alert → Combat), and notifies nearby AI units. Acts as the central threat-response coordinator for each AI.

### Enums

| Enum | Values | Description |
|------|--------|-------------|
| `EAlertLevel` | Unaware, Suspicious, Alert, Combat | Current alert tier |
| `EAlertCause` | SightStimulus, HearingStimulus, DamageTaken, AllyAlert, Environmental | What triggered the alert |

### Structs

| Struct | Fields | Description |
|--------|--------|-------------|
| `FAlertState` | AlertLevel: EAlertLevel, AlertCause: EAlertCause, LastAlertTime: Float, SourceLocation: FVector, KnownTarget: AActor, bHasValidTarget: Bool, SuspicionValue: Float, CombatTimer: Float | Snapshot of current alert status |
| `FAlertPropagationInfo` | AlertLevel: EAlertLevel, SourceLocation: FVector, Target: AActor, PropagationRadius: Float, Timestamp: Float | Data sent to nearby AI |

### Variables

| Name | Type | Description |
|------|------|-------------|
| `CurrentAlertState` | FAlertState | Live alert tracking |
| `AlertThresholds` | FVector2D | Suspicion threshold X, Combat threshold Y |
| `SuspicionDecayRate` | Float | Points per second lost when no stimulus |
| `SuspicionGrowthRate` | Float | Points per second gained on stimulus |
| `CombatExitDelay` | Float | Time without combat before dropping to Alert |
| `ReinforcementRadius` | Float | Max distance to notify allies |
| `bCanCallReinforcements` | Bool | Permission to propagate |
| `ReinforcementCooldown` | Float | Min seconds between calls |
| `LastReinforcementTime` | Float | Cooldown tracker |
| `bIsInCombat` | Bool | Fast check for combat state |
| `InvestigationTimer` | Float | Time spent investigating last known position |
| `MaxInvestigationTime` | Float | Give up after this long |

### Functions

| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `EvaluateStimulus` | StimulusType: EAlertCause, Intensity: Float, Location: FVector, Instigator: AActor | — | Process incoming stimulus |
| `CalculateThreatScore` | StimulusType: EAlertCause, Intensity: Float, Distance: Float | Float | Compute weighted threat value |
| `EscalateAlert` | NewLevel: EAlertLevel, Cause: EAlertCause | — | Raise alert level |
| `DeescalateAlert` | — | — | Lower alert level (decay timer) |
| `UpdateSuspicion` | DeltaTime: Float | — | Tick suspicion up/down |
| `CheckAlertTransition` | — | EAlertLevel | Determine if threshold crossed |
| `EnterCombat` | Target: AActor | — | Transition to combat state |
| `ExitCombat` | — | — | Transition out of combat |
| `CallReinforcements` | — | — | Broadcast alert to allies |
| `ReceiveReinforcementCall` | Info: FAlertPropagationInfo | — | Handle ally alert broadcast |
| `GetCurrentAlertLevel` | — | EAlertLevel | Getter for blackboard updates |
| `GetKnownTarget` | — | AActor | Current combat target |
| `IsInCombat` | — | Bool | Combat state check |
| `SetInvestigationPoint` | Location: FVector | — | Mark last known target position |
| `ClearInvestigationPoint` | — | — | Reset investigation |
| `ShouldGiveUpSearch` | — | Bool | Timeout check for investigation |

### Blueprint Flow

```
[EvaluateStimulus]
  └─► Calculate Threat Score
  └─► If Score > AlertThresholds.Y --> Escalate to Combat
  └─► Else If Score > AlertThresholds.X --> Escalate to Suspicious
  └─► Else --> Update SuspicionValue (accumulate)
  └─► If AlertLevel >= Suspicious:
         Set Investigation Point
         If AlertLevel == Combat:
             Set KnownTarget
             EnterCombat
             If bCanCallReinforcements:
                 CallReinforcements

[Event Tick]
  └─► UpdateSuspicion(DeltaTime):
         If no stimulus:
             SuspicionValue -= SuspicionDecayRate * DeltaTime
             If SuspicionValue <= 0:
                 DeescalateAlert (back to Unaware)
         If AlertLevel == Combat:
             CombatTimer += DeltaTime
             If CombatTimer > CombatExitDelay && no damage/stimulus:
                 ExitCombat -> set AlertLevel = Alert
         If AlertLevel == Suspicious:
             InvestigationTimer += DeltaTime
             If InvestigationTimer > MaxInvestigationTime:
                 ClearInvestigationPoint
                 DeescalateAlert

[CallReinforcements]
  └─► If (Time - LastReinforcementTime) < ReinforcementCooldown: return
  └─► Get all AI controllers within ReinforcementRadius
  └─► For each:
         Call ReceiveReinforcementCall with current alert info
  └─► LastReinforcementTime = CurrentTime

[ReceiveReinforcementCall]
  └─► If CurrentAlertLevel >= Info.AlertLevel: ignore (already higher)
  └─► Else: EscalateAlert(Info.AlertLevel, Environmental)
         Set InvestigationPoint = Info.SourceLocation
         If Info.AlertLevel == Combat:
             Set KnownTarget = Info.Target
```

### Event Dispatchers

| Name | Delegate Signature | Purpose |
|------|-------------------|---------|
| `OnAlertLevelChanged` | EAlertLevel, EAlertCause | UI, sound, state machine listeners |
| `OnCombatEntered` | AActor Target | Combat music, HUD, behavior tree |
| `OnCombatExited` | — | Combat cleanup |
| `OnReinforcementCalled` | FAlertPropagationInfo | Debug logging, analytics |
| `OnInvestigationStarted` | FVector Location | Sound/investigation music |
| `OnInvestigationEnded` | — | Return to patrol |

### Communications With

| Target | Method | Why |
|--------|--------|-----|
| [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) | Call | Request state change |
| [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) | Owner reference | Coordinate BB values |
| [`BPC_PerceptionComponent`](56_BPC_PerceptionComponent.md) | Direct call | Get stimulus data |
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Owner reference | Trigger combat animations |
| [`WBP_HUD`](../06-ui/36_WBP_HUD.md) | Event dispatcher | Show combat indicator |
| [`BPC_CombatFeedback`](../08-combat/54_BPC_CombatFeedbackComponent.md) | Event dispatcher | Combat music/vignette |

### Reuse Notes
- Attached to AIControllerBase; profiles tune thresholds per enemy type
- Works with any perception system that produces EAlertCause stimuli
- Reinforcement chain prevents infinite loops by comparing alert levels
- Investigation system handles "last seen" behavior without combat
- Decay system ensures AI eventually returns to patrol state