# 70 — BPC_ProceduralEncounter

## Blueprint Spec — UE 5.5–5.7

---

### Parent Class
`ActorComponent`

### Dependencies
- [`BPC_DifficultyManager`](89_BPC_DifficultyManager.md) — Encounter difficulty scaling
- [`BPC_FearSystem`](90_BPC_FearSystem.md) — Fear-based encounter intensity
- [`BPC_AtmosphereController`](64_BPC_AtmosphereController.md) — Atmosphere-aligned encounters
- [`BP_DynamicEvent`](68_BP_DynamicEvent.md) — Encounter as dynamic event
- [`DA_EncounterTable`](../12-content/76_DA_EncounterTable.md) — Encounter data tables (Phase 12)
- [`I_Damageable`](../08-weapons/58_I_Damageable.md) — Enemy damage integration
- [`AI_EnemyBase`](../09-ai/58_AI_EnemyBase.md) — Enemy AI controller
- [`BP_EnemyBase`](../09-ai/57_BP_EnemyBase.md) — Enemy pawn reference

### Purpose
Procedurally generates enemy encounters, ambushes, and spawn events based on player state, difficulty, fear level, atmosphere, and narrative progression. Operates as a tactical encounter director that decides when, where, and what enemies to spawn, how they behave, and when to retreat or reinforce. Prevents encounter fatigue by managing cooldowns, variety, and pacing. Supports wave-based encounters, ambushes, patrol reinforcement, and boss encounters.

### Enums

**EEncounterType**

| Value | Description |
|-------|-------------|
| Patrol | Wandering enemy, player can evade |
| Ambush | Pre-placed enemies triggered by player |
| WaveBased | Multiple waves of increasing difficulty |
| Reinforcement | Enemies called in during combat |
| Boss | Single powerful enemy |
| Escalation | Increasing intensity until player leaves area |
| Environmental | Hazard-based encounter (not enemies) |
| Scripted | Level designer placed encounter |

**ESpawnMethod**

| Value | Description |
|-------|-------------|
| FromLocations | Use pre-placed spawn points |
| FromNavMesh | Random point on nav mesh |
| FromVolumes | Spawn in volume bounds |
| FromPortals | Enemy emerges from gate/door |
| FromVent | Spawn at vent locations |
| CeilingDrop | Enemy drops from above |
| ClosestNav | Closest valid nav point to player |

**EEncounterIntensity**

| Value | Description |
|-------|-------------|
| Low | 1–2 weak enemies |
| Moderate | 2–3 standard enemies |
| High | 3–5 mixed enemies |
| Critical | 5+ enemies with elites |
| Boss | Single boss enemy |

### Structs

**FEncounterSpawnSlot**

| Field | Type | Description |
|-------|------|-------------|
| SpawnLocation | FVector | World position |
| EnemyClass | TSubclassOf\<BP_EnemyBase\> | Enemy to spawn |
| SpawnDelay | Float | Delay before this enemy appears |
| bDropIn | Bool | Animated drop-in |
| SpawnEffect | UNiagaraSystem | Visual spawn effect |
| PatrolRouteIndex | Int32 | Patrol path to follow |

**FEncounterWave**

| Field | Type | Description |
|-------|------|-------------|
| WaveName | FName | Identifier |
| SpawnSlots | TArray\<FEncounterSpawnSlot\> | Enemies in this wave |
| ActivationDelay | Float | Time before wave starts |
| bWaitForAllDead | Bool | Next wave waits for all dead |
| DifficultyMultiplier | Float | Wave-specific difficulty |
| DialogueTrigger | FName | Optional dialogue on wave start |

### Variables

| Name | Type | Description |
|------|------|-------------|
| `ActiveEncounterType` | EEncounterType | Current encounter type |
| `EncounterState` | FName | Active, Cooldown, Completed |
| `bPlayerInEncounterZone` | Bool | Player proximity trigger |
| `EncounterRadius` | Float | Zone size |
| `MinEnemyDistance` | Float | Min distance from player to spawn |
| `MaxConcurrentEnemies` | Int32 | Hard limit on active enemies |
| `ActiveEnemies` | TArray\<BP_EnemyBase\> | Currently alive enemies |
| `Waves` | TArray\<FEncounterWave\> | Defined waves |
| `CurrentWaveIndex` | Int32 | Active wave |
| `bWaveInProgress` | Bool | Currently executing wave |
| `CooldownRemaining` | Float | Encounter cooldown |
| `MinCooldown` | Float | Min time between encounters |
| `MaxCooldown` | Float | Max time between encounters |
| `EncounterCount` | Int32 | Encounters this session |
| `MaxEncountersPerZone` | Int32 | Encounters before exhaustion |
| `bZoneExhausted` | Bool | No more encounters in this zone |
| `SpawnLocations` | TArray\<FEncounterSpawnSlot\> | Configured spawn points |
| `bUseNavMesh` | Bool | Use nav mesh for spawns |
| `EncounterVolumeBox` | FBox | Spawn volume bounds |

### Functions

| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `Initialize` | — | — | Register spawn points, validate setup |
| `TriggerEncounter` | Type: EEncounterType, Intensity: EEncounterIntensity | — | Start encounter |
| `TriggerWaveBasedEncounter` | Intensity: EEncounterIntensity | — | Multi-wave encounter |
| `SpawnWave` | WaveIndex: Int32 | — | Execute wave spawning |
| `SpawnSingleEnemy` | Slot: FEncounterSpawnSlot | BP_EnemyBase | Spawn one enemy |
| `GetRandomSpawnLocation` | — | FVector | Pick valid spawn point |
| `GetSpawnLocationByMethod` | Method: ESpawnMethod | FVector | Contextual spawn position |
| `OnEnemyKilled` | Enemy: BP_EnemyBase | — | Track enemy death |
| `OnAllEnemiesDead` | — | — | Wave completed or encounter ended |
| `ReinforceEncounter` | Count: Int32 | — | Spawn additional enemies |
| `EndEncounter` | — | — | Cleanup and cooldown |
| `RetreatEnemies` | — | — | All enemies flee |
| `CalculateIntensity` | — | EEncounterIntensity | Based on difficulty + fear + atmosphere |
| `IsSpawnLocationValid` | Location: FVector | Bool | Check line of sight, nav mesh |
| `IsInEncounterZone` | PlayerLocation: FVector | Bool | Proximity check |
| `StartCooldown` | — | — | Begin cooldown timer |
| `ExhaustZone` | — | — | Mark zone as depleted |
| `ResetEncounter` | — | — | Full reset for reuse |

### Blueprint Flow

```
[Initialize]
  └─► Register spawn point locations from child actors or volume
  └─► If bUseNavMesh:
         Get nav mesh bounds from EncounterVolumeBox
  └─► Listen for player proximity via overlap events
  └─► Set initial EncounterState = Ready

[TriggerEncounter]
  └─► If CooldownRemaining > 0 or bZoneExhausted: return
  └─► EncounterState = Active
  └─► ActiveEncounterType = Type
  └─► CalculateIntensity()
  └─► Based on Type:
         Patrol:       Spawn 1-2 patrol enemies, set patrol routes
         Ambush:       All enemies spawn at once, no delay, trigger dialogue
         WaveBased:    TriggerWaveBasedEncounter(Intensity)
         Reinforcement: Spawn 1-3 enemies near existing combat
         Boss:         Spawn single boss enemy, lock area
         Escalation:   Start wave timer, spawn escalating until retreat or all dead
         Environmental: Trigger hazard events instead of enemies
         Scripted:     Execute designer-defined encounter
  └─► Broadcast OnEncounterStarted

[TriggerWaveBasedEncounter]
  └─► CurrentWaveIndex = 0
  └─► For each wave in Waves:
         Wave.DifficultyMultiplier = Get difficulty scale from BPC_DifficultyManager
         Apply difficulty scaling to each enemy class
  └─► SpawnWave(0)

[SpawnWave]
  └─► bWaveInProgress = true
  └─► For each FEncounterSpawnSlot in Waves[WaveIndex].SpawnSlots:
         Delay(SpawnSlot.SpawnDelay)
         SSpawnSingleEnemy(SpawnSlot)
  └─► If Waves[WaveIndex].DialogueTrigger:
         Trigger dialogue via BPC_DialoguePlayback

[SpawnSingleEnemy]
  └─► SpawnLocation = Slot.SpawnLocation
  └─► If Slot.SpawnLocation == ZeroVector:
         SpawnLocation = GetSpawnLocationByMethod(FromLocations or FromNavMesh)
  └─► If not IsSpawnLocationValid(SpawnLocation): return
  └─► Enemy = SpawnActor Deferred(Slot.EnemyClass, SpawnLocation)
  └─► Set difficulty-scaled stats on Enemy:
         Health *= DifficultyManager.HealthMultiplier
         Damage *= DifficultyManager.DamageMultiplier
         Speed *= DifficultyManager.SpeedMultiplier
  └─► If Slot.PatrolRouteIndex >= 0:
         Assign patrol path to enemy
  └─► FinishSpawning(Enemy)
  └─► If Slot.bDropIn:
         Play drop-in animation on Enemy
  └─► If Slot.SpawnEffect:
         UNiagaraFunctionLibrary::SpawnSystemAtLocation
  └─► Bind OnEnemyKilled to Enemy.OnDestroyed
  └─► ActiveEnemies.Add(Enemy)

[OnEnemyKilled]
  └─► ActiveEnemies.Remove(Enemy)
  └─► Add to MetricsTracker for scoring
  └─► If ActiveEnemies.Num() == 0:
         OnAllEnemiesDead()
  └─► If EncounterState == Active and ActiveEncounterType == Escalation:
         CalculateIntensity()
         If should escalate:
              SpawnWave more enemies
         If should de-escalate:
              RetreatEnemies()

[OnAllEnemiesDead]
  └─► If ActiveEncounterType == WaveBased:
         CurrentWaveIndex++
         If CurrentWaveIndex < Waves.Num():
              SpawnWave(CurrentWaveIndex)
         Else:
              EndEncounter()
  └─► Else:
         EndEncounter()

[EndEncounter]
  └─► EncounterState = Completed
  └─► EncounterCount++
  └─► If EncounterCount >= MaxEncountersPerZone:
         ExhaustZone()
  └─► Else:
         StartCooldown(MinCooldown, MaxCooldown)
  └─► Broadcast OnEncounterEnded
  └─► Reward player via BPC_PlayerMetricsTracker

[CalculateIntensity]
  └─► Base intensity from BPC_DifficultyManager.CurrentDifficulty
  └─► Multiply by BPC_FearSystem.CurrentFearLevel factor (0.5 at Calm, 2.0 at Panic)
  └─► Adjust by BPC_AtmosphereController.CurrentState:
         Exploration: *0.5 (fewer enemies)
         Suspense:    *1.0 (standard)
         Combat:      *2.0 (more enemies)
         Panic:       *1.5 (intense but fewer than combat)
  └─► Clamp to EEncounterIntensity range
  └─► Return

[GetSpawnLocationByMethod]
  └─► FromLocations: Pick random from SpawnLocations array
  └─► FromNavMesh: Query UNavigationSystemV1::GetRandomPointInNavigableRadius
         Ensure distance from player >= MinEnemyDistance
  └─► FromVolumes: Random point within EncounterVolumeBox
         Project to nav mesh
  └─► FromPortals: Pre-placed portal actor locations
  └─► FromVent: Vent location list
  └─► CeilingDrop: Above player location + random offset
         Ensure valid nav mesh below

[IsSpawnLocationValid]
  └─► Check distance from player >= MinEnemyDistance
  └─► Line trace from player to spawn location:
         If blocked and distance < 500: location is behind obstacle, valid (ambush)
         If clear and distance < 500: location is visible, valid for patrol
         If distance > 2000: always valid
  └─► Check nav mesh projection is valid
  └─► Check not overlapping existing enemies
```

### Event Dispatchers

| Name | Payload | Description |
|------|---------|-------------|
| `OnEncounterStarted` | EncounterType: EEncounterType, Intensity: EEncounterIntensity | Encounter began |
| `OnEncounterEnded` | bPlayerSurvived: Bool, EnemiesKilled: Int32 | Encounter finished |
| `OnWaveStarted` | WaveIndex: Int32, WaveName: FName | New wave beginning |
| `OnWaveCompleted` | WaveIndex: Int32 | Wave cleared |
| `OnEnemySpawned` | Enemy: BP_EnemyBase | Individual enemy spawned |

### Communications With

| Target | Method | Why |
|--------|--------|-----|
| [`BPC_DifficultyManager`](89_BPC_DifficultyManager.md) | Direct call | Difficulty scaling for encounter |
| [`BPC_FearSystem`](90_BPC_FearSystem.md) | Get owner | Fear influence on intensity |
| [`BPC_AtmosphereController`](64_BPC_AtmosphereController.md) | Get owner | Atmosphere alignment |
| [`BP_DynamicEvent`](68_BP_DynamicEvent.md) | Cast | Encounter as dynamic event |
| [`AI_EnemyBase`](../09-ai/55_AI_EnemyBase.md) | On spawned | Set encounter-aware behaviors |
| [`BP_EnemyBase`](../09-ai/57_BP_EnemyBase.md) | On spawned | Set difficulty-scaled stats |
| [`BPC_PlayerMetricsTracker`](../02-player/14_BPC_PlayerMetricsTracker.md) | Event | Encounter scoring |
| [`BPC_NarrativeState`](../07-narrative/36_BPC_NarrativeState.md) | Event | Narrative-gated encounters |
| [`BPC_DialoguePlayback`](../07-narrative/37_BPC_DialoguePlayback.md) | Event | Wave-start dialogue |

### Reuse Notes
- Place BPC_ProceduralEncounter on a volume actor or game mode
- Configure spawn points as child actors or arrays in details panel
- Encounter exhaustion prevents infinite grinding in one area
- Difficulty + Fear + Atmosphere combine to produce dynamic intensity
- Nav mesh spawning ensures enemies always have valid pathfinding
- Encounter state machine: Ready -> Active -> Cooldown -> Exhausted
- All encounters work with or without pre-placed spawn locations
- Each enemy spawned gets difficulty-scaled stats at spawn time