# AI_BaseAgentController — AI Controller

## Blueprint Spec — UE 5.5–5.7

---

### Parent Class
`AIController`

### Dependencies
- [`BP_EnemyBase`](58_BP_EnemyBase.md) — Pawn
- [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) — Sensing
- [`BB_AgentBoard`](BB_AgentBoard.md) — Decision-making
- [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) — High-level states
- [`BPC_AlertSystem`](60_BPC_AlertSystem.md) — Threat awareness
- [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) — Self health
- [`I_Damageable`](../01-core/03_I_InterfaceLibrary.md) — Damage reception
- `DA_AIProfile` — AI configuration

### Purpose
Central brain for enemy AI characters. Manages perception, decision-making via Behavior Trees, high-level state machine transitions, alert level propagation, and combat coordination. Possesses `BP_EnemyBase` pawns and controls their actions through Blackboard values and Behavior Tree execution.

### Variables

| Name | Type | Description |
|------|------|-------------|
| `AIProfile` | DA_AIProfile | Configuration asset |
| `PossessedEnemy` | BP_EnemyBase | Current pawn reference |
| `BlackboardComp` | UBlackboardComponent | Blackboard instance |
| `BehaviorTreeComp` | UBehaviorTreeComponent | Behavior tree runner |
| `PerceptionComp` | BPC_AIPerceptionSystem | Sensory input |
| `StateMachine` | BPC_AIStateMachine | State logic |
| `AlertSystem` | BPC_AlertSystem | Threat level |
| `bIsActive` | Bool | Enabled / disabled |
| `AggressionRange` | Float | Engage distance |
| `SuspicionRange` | Float | Investigate distance |
| `CombatRange` | Float | Preferred combat distance |
| `HomeLocation` | FVector | Spawn or patrol anchor |
| `LastKnownPlayerLocation` | FVector | Blackboard updated |
| `bHasLineOfSight` | Bool | LOS status |
| `LostPlayerTimer` | Float | Seconds since last sighting |

### Blackboard Keys

| Key Name | Type | Description |
|----------|------|-------------|
| `SelfActor` | Object | Self reference |
| `TargetActor` | Object | Current threat |
| `TargetLocation` | Vector | Last known threat location |
| `HomeLocation` | Vector | Patrol anchor |
| `AIState` | Enum (EAIState) | Current state |
| `AlertLevel` | Float | 0.0–1.0 awareness |
| `bHasLOS` | Bool | Line of sight |
| `bIsInvestigating` | Bool | Searching location |
| `PatrolIndex` | Int | Current waypoint |
| `CombatStance` | Enum | Aggressive / Defensive |
| `StimulusLocation` | Vector | Sound/sight trigger point |

### Enums

| Enum | Values | Description |
|------|--------|-------------|
| `EAIState` | Idle, Patrol, Suspicious, Alerted, Combat, Searching, Fleeing, Disabled | AI behavior state |

### Functions

| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `OnPossess` | Pawn: APawn | — | Bind blackboard, run BT |
| `OnUnPossess` | — | — | Cleanup |
| `InitializeAI` | Profile: DA_AIProfile | — | Set up components |
| `SetAIState` | NewState: EAIState | — | State machine transition |
| `UpdatePerception` | Stimulus: FAIStimulus | — | Process sensory input |
| `SetTarget` | Target: AActor | — | Update blackboard target |
| `ClearTarget` | — | — | Target lost |
| `StartInvestigation` | Location: FVector | — | Move to investigate |
| `RequestReinforcements` | — | — | Alert nearby allies |
| `GetCombatReadiness` | — | Float | Based on health, ammo, alert |
| `OnTakeDamage` | DamageResult: FDamageResult | — | React to being hit |
| `CanSeeTarget` | — | Bool | LOS check |
| `LostSightOfTarget` | — | — | Start search timer |
| `ReturnToPatrol` | — | — | Reset to patrol state |
| `GetHomeLocation` | — | FVector | Patrol anchor |
| `SetFocalPoint` | Location: FVector | — | Aim facing direction |

### Event Dispatchers

| Name | Parameters | Fired When |
|------|-----------|-----------|
| `OnAIStateChanged` | OldState: EAIState, NewState: EAIState | State transition |
| `OnTargetAcquired` | Target: AActor | New threat detected |
| `OnTargetLost` | — | Target out of range/perception |
| `OnAlertRaised` | AlertLevel: Float | Alert threshold crossed |
| `OnReinforcementRequested` | Location: FVector | Call for backup |

### Blueprint Flow

```
[OnPossess]
  └─► PossessedEnemy = Cast<BP_EnemyBase>(Pawn)
  └─► AIProfile = PossessedEnemy.AIProfile (or set from spawner)
  └─► InitializeAI(AIProfile)
  └─► RunBehaviorTree(AIProfile.BehaviorTree)
  └─► SetHomeLocation(Pawn.GetActorLocation)

[InitializeAI]
  └─► Create BPC_AIPerceptionSystem if not exists
  └─► Create BPC_AIStateMachine
  └─► Create BPC_AlertSystem
  └─► PerceptionComp.Initialize(AIProfile.PerceptionConfig)
  └─► StateMachine.Initialize(EAIState.Patrol)
  └─► AlertSystem.Initialize()
  └─► Set AIState = Patrol in Blackboard

[UpdatePerception — called by PerceptionComp dispatcher]
  └─► If Stimulus.Type == Sight && Stimulus.bSuccess:
         LastKnownPlayerLocation = Stimulus.StimulusLocation
         AlertSystem.RaiseAlert(SightAlertValue)
         If AlertSystem.AlertLevel >= AIProfile.CombatThreshold:
             SetTarget(Stimulus.Instigator)
             SetAIState(Combat)
         Else:
             SetAIState(Suspicious)
  └─► If Stimulus.Type == Hearing && Stimulus.bSuccess:
         AlertSystem.RaiseAlert(HearingAlertValue)
         StartInvestigation(Stimulus.StimulusLocation)
         SetAIState(Searching)
  └─► If Stimulus.Type == Damage:
         SetTarget(Stimulus.Instigator)
         AlertSystem.RaiseAlert(DamageAlertValue)
         SetAIState(Combat)
         RequestReinforcements()

[OnTakeDamage]
  └─► AlertSystem.RaiseAlert(MaxAlertValue)
  └─► SetTarget(DamageResult.Instigator)
  └─► SetAIState(Combat)
```

### Communications With

| Target | Method | Why |
|--------|--------|-----|
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Possessed pawn | Movement, animation, abilities |
| [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) | Get Component | Sensory stimulus processing |
| [`BB_AgentBoard`](BB_AgentBoard.md) | Get Component | Blackboard updates |
| [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) | Get Component | State transitions |
| [`BPC_AlertSystem`](60_BPC_AlertSystem.md) | Get Component | Threat level queries |
| [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) | Get on Pawn | Health events |
| Nearby AI Controllers | Direct / Event | Reinforcement calls |

### Reuse Notes
- Designed as parent for all enemy types (humanoid, creature, drone)
- Perception, alerts, and state machine are swappable components
- Behavior tree is data-driven via AIProfile
- All decision values (ranges, thresholds, speeds) come from data asset
- Renamed from `BPC_AIControllerBase` to `AI_BaseAgentController` per Master naming convention.
- Cross-references updated: `BPC_PerceptionComponent` → `BPC_AIPerceptionSystem`, `BPC_BehaviorTreeManager` → `BB_AgentBoard`, `BPC_HealthComponent` → `BPC_HealthSystem`.

---

## Manual Implementation Guide

### Class Setup
1. Create Blueprint Class: Parent = `AIController`, Name = `AI_BaseAgentController`
2. Save to: `Content/Framework/AI/`
3. In Class Defaults: set `Auto Possess AI` = `Placed in World or Spawned`

### Variables (Add to Class Defaults)

| Variable | Type | Default | Instance Editable |
|----------|------|---------|-------------------|
| `AIProfile` | `DA_AIProfile` | None | ✓ |
| `PossessedEnemy` | `BP_EnemyBase` (Object Ref) | None | |
| `BlackboardComp` | `Blackboard Component` (Object Ref) | None | |
| `BehaviorTreeComp` | `Behavior Tree Component` (Object Ref) | None | |
| `PerceptionComp` | `BPC_AIPerceptionSystem` (Object Ref) | None | |
| `StateMachine` | `BPC_AIStateMachine` (Object Ref) | None | |
| `AlertSystem` | `BPC_AlertSystem` (Object Ref) | None | |
| `bIsActive` | `Boolean` | `true` | ✓ |
| `AggressionRange` | `Float` | `1500.0` | ✓ |
| `SuspicionRange` | `Float` | `3000.0` | ✓ |
| `CombatRange` | `Float` | `800.0` | ✓ |
| `HomeLocation` | `Vector` | `(0,0,0)` | |
| `LastKnownPlayerLocation` | `Vector` | `(0,0,0)` | |
| `LastKnownPlayerLocation` | `Vector` | `(0,0,0)` | |
| `bHasLineOfSight` | `Boolean` | `false` | |
| `LostPlayerTimer` | `Float` | `0.0` | |

### Function Implementations

#### `Event OnPossess(Pawn: Pawn)`

```
[Event OnPossess]
  Step 1: Call Parent OnPossess
  Step 2: Cast Pawn to BP_EnemyBase → Set PossessedEnemy
  Step 3: Get AIProfile from PossessedEnemy (or a spawner data)
  Step 4: Get Blackboard Component → Set BlackboardComp
  Step 5: Get Behavior Tree Component → Set BehaviorTreeComp
  Step 6: Get Component by Class (BPC_AIPerceptionSystem) → Set PerceptionComp
    └─ If not found on Pawn: Create and attach dynamically
  Step 7: Get Component by Class (BPC_AIStateMachine) → Set StateMachine
  Step 8: Get Component by Class (BPC_AlertSystem) → Set AlertSystem
  Step 9: Set HomeLocation = Pawn.GetActorLocation()
  Step 10: Set Blackboard value "SelfActor" to Self
  Step 11: Set Blackboard value "HomeLocation" to HomeLocation
  Step 12: Set Blackboard value "AIState" to EAIState::Patrol
  Step 13: Run Behavior Tree (AIProfile.BehaviorTree)
  Step 14: PerceptionComp.Initialize(AIProfile.PerceptionConfig)
  Step 15: StateMachine.Initialize(EAIState::Patrol)
  Step 16: AlertSystem.Initialize()
```

**Nodes:** `Cast To BP_EnemyBase`, `Get Blackboard`, `Set Value as Object/Vector/Enum`, `Run Behavior Tree`, `Create Component (if needed)`, `Get Actor Location`

#### `Event OnUnPossess(Pawn: Pawn)`

```
[Event OnUnPossess]
  Step 1: Stop Behavior Tree
  Step 2: Clear Blackboard values (TargetActor = None, AIState = Disabled)
  Step 3: PerceptionComp.Deinitialize()
  Step 4: Call Parent OnUnPossess
```

#### `UpdatePerception(Stimulus: AIStimulus)` → `void`

*This is called by BPC_AIPerceptionSystem dispatcher when any sense triggers.*

```
[Function: UpdatePerception]
  Step 1: Branch on Stimulus.WasSuccessfullySensed()
    False → Return (ignore failed perception)

  Step 2: Switch on Stimulus.Type:
    Case Sight:
      Step 2a: Set LastKnownPlayerLocation = Stimulus.StimulusLocation
      Step 2b: AlertSystem.RaiseAlert(SightAlertValue from AIProfile)
      Step 2c: Branch on AlertSystem.AlertLevel >= AIProfile.CombatThreshold:
        True:
          - SetTarget(Stimulus.StimulusLocation → find closest Actor at that location)
          - SetAIState(EAIState::Combat)
          - Set Blackboard "bHasLOS" = true
        False:
          - SetAIState(EAIState::Suspicious)
          - Set Blackboard "StimulusLocation" = Stimulus.StimulusLocation
          - Set Blackboard "bIsInvestigating" = true

    Case Hearing:
      Step 3a: AlertSystem.RaiseAlert(HearingAlertValue from AIProfile)
      Step 3b: StartInvestigation(Stimulus.StimulusLocation)
      Step 3c: SetAIState(EAIState::Searching)
      Step 3d: Set Blackboard "StimulusLocation" = Stimulus.StimulusLocation

    Case Damage:
      Step 4a: SetTarget(Stimulus.StimulusLocation → find instigator)
      Step 4b: AlertSystem.RaiseAlert(MaxAlertValue)
      Step 4c: SetAIState(EAIState::Combat)
      Step 4d: Call RequestReinforcements() if team-aware

  Step 5: Fire OnTargetAcquired(Stimulus.StimulusLocation nearest Actor) if target found
```

**Nodes:** `Switch on EAIPerceptionSense`, `RaiseAlert`, `Set Blackboard Value`, `Branch`, `Find Nearest Actor` or `Get Actor at Location`

#### `SetAIState(NewState: EAIState)` → `void`

```
[Function: SetAIState]
  Step 1: Get old state from Blackboard "AIState"
  Step 2: If NewState == OldState → Return
  Step 3: Set Blackboard "AIState" = NewState
  Step 4: Call StateMachine.SetState(NewState)
  Step 5: Fire OnAIStateChanged(OldState, NewState)
  Step 6: If NewState == Combat: Call RequestReinforcements()
```

#### `SetTarget(Target: Actor)` → `void`

```
[Function: SetTarget]
  Step 1: Set Blackboard "TargetActor" = Target
  Step 2: If Target is valid:
    - Set Blackboard "TargetLocation" = Target.GetActorLocation()
    - SetFocus(Target)
    - Fire OnTargetAcquired(Target)
  Else:
    - ClearFocus()
    - Fire OnTargetLost()
```

#### `StartInvestigation(Location: Vector)` → `void`

```
[Function: StartInvestigation]
  Step 1: Set Blackboard "bIsInvestigating" = true
  Step 2: Set Blackboard "TargetLocation" = Location
  Step 3: Call AI MoveTo(Location)
  Step 4: On Move Completed:
    - If target not found at location:
      - Set Blackboard "bIsInvestigating" = false
      - ReturnToPatrol()
```

#### `RequestReinforcements()` → `void`

```
[Function: RequestReinforcements]
  Step 1: Get all AI_BaseAgentController in radius (ReinforcementRange from AIProfile)
  Step 2: ForEach nearby AI:
    - If AI.StateMachine.CurrentState == Patrol OR Suspicious:
      - Call AI.SetTarget(Self.GetBlackboard("TargetActor"))
      - Call AI.SetAIState(Alerted)
  Step 3: Fire OnReinforcementRequested(Self.GetActorLocation())
```

**Nodes:** `Get All Actors of Class (AI_BaseAgentController)`, `ForEachLoop`, `Get Distance To`, `Branch`, `Call SetTarget/SetAIState on other AI`

#### `LostSightOfTarget()` → `void`

```
[Function: LostSightOfTarget]
  Step 1: Set Blackboard "bHasLOS" = false
  Step 2: Start LostPlayerTimer (timer loops 0.1s)
    └─ Each tick: LostPlayerTimer += 0.1
  Step 3: Branch on LostPlayerTimer >= AIProfile.LostTargetTimeout:
    True:
      - ClearTarget()
      - SetAIState(EAIState::Searching)
      - StartInvestigation(LastKnownPlayerLocation)
      - Clear LostPlayerTimer
    False: continue waiting
```

#### `CanSeeTarget()` → `Boolean`

```
[Function: CanSeeTarget] (Pure)
  Step 1: TargetActor = Get Blackboard "TargetActor"
  Step 2: If TargetActor NOT valid → Return false
  Step 3: Line Trace by Channel from Self location to TargetActor location
    - Trace channel: Visibility
    - Ignore Self
  Step 4: Return (Hit Actor == TargetActor) — true if nothing blocks line of sight
```

### Blueprint Build Checklist

- [ ] Create AI_BaseAgentController (Parent: AIController)
- [ ] Add all variables from the table above
- [ ] Create Blackboard asset: BB_AgentBoard with all key names
- [ ] Configure Behavior Tree asset (or use placeholder)
- [ ] Implement OnPossess: cache components, init perception/state/alert, run BT
- [ ] Implement UpdatePerception: route Sight/Hearing/Damage to correct states
- [ ] Implement SetAIState with blackboard update and dispatcher
- [ ] Implement SetTarget/ClearTarget with blackboard sync
- [ ] Implement StartInvestigation with AI MoveTo
- [ ] Implement RequestReinforcements (nearby AI coordination)
- [ ] Implement LostSightOfTarget with timeout → search
- [ ] Implement CanSeeTarget with line trace
- [ ] Create BP_EnemyBase child classes that use this controller