# BPC_PacingDirector — Pacing Director

## Blueprint Spec — UE 5.5–5.7

---

### Parent Class
`ActorComponent`

### Dependencies
- **Requires:** [`BPC_AdaptiveEnvironmentDirector`](BPC_AdaptiveEnvironmentDirector.md) — Receives pacing guidance requests
- **Required By:** [`BPC_ScareEventSystem`](BPC_ScareEventSystem.md) — Scare scheduling governed by tension curve
- **Required By:** [`SS_AudioManager`](../10-adaptive/132_SS_AudioManager.md) — Music intensity driven by tension (via `UpdateGameplayParameter("MusicIntensity")`)
- **Engine/Plugin Requirements:** GameplayTags, CurveFloat asset

### Purpose
Monitors overall session pacing: tension curves, quiet beats, and escalation patterns. Prevents scare fatigue and intensity burnout by enforcing structured peaks and valleys in tension. Uses a designer-authored CurveFloat to define ideal tension over session time and actively measures/steers actual tension toward the curve.

---

## 1. Enums

*No new enums defined. Uses GameplayTags for tension sources.*

---

## 2. Structs

*No new structs defined.*

---

## 3. Variables

### Configuration (Instance Editable, Expose On Spawn)

| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `TensionCurve` | CurveFloat | None | PacingConfig | Designer-authored curve: ideal tension (0–1) over session time (seconds) |
| `QuietBeatMinInterval` | Float | 120.0 | PacingConfig | Minimum seconds between quiet beat enforcement |

### Internal (Private / Protected, No Expose)

| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `CurrentTension` | Float | 0.0 | Internal | Actual measured tension (0.0–1.0) |
| `TensionSources` | Map (GameplayTag → Float) | Empty | Internal | Contributing tension sources and their values |
| `LastQuietBeatTime` | Float | 0.0 | Internal | Game time of last enforced quiet beat |
| `SessionElapsedTime` | Float | 0.0 | Internal | Accumulated session play time |

---

## 4. Functions

### Public Functions

#### `SetTensionSource` → `void`
- **Description:** Registers or updates a tension source. Recalculates CurrentTension as the maximum of all active sources.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `SourceTag` | GameplayTag | Identifier for the tension source |
  | `Value` | Float | Tension contribution 0.0–1.0 |
- **Blueprint Authority:** Any
- **Flow:** Add/update in TensionSources → recalculate CurrentTension = max of all source values → check against TensionCurve

#### `RemoveTensionSource` → `void`
- **Description:** Removes a tension source and recalculates current tension.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `SourceTag` | GameplayTag | Source to remove |
- **Blueprint Authority:** Any

#### `GetCurrentTension` → `Float`
- **Description:** Returns the current measured tension level (0.0–1.0).
- **Parameters:** None
- **Blueprint Authority:** Any

#### `GetIdealTension` → `Float`
- **Description:** Reads the TensionCurve at the current session time to get the ideal tension target.
- **Parameters:** None
- **Blueprint Authority:** Any
- **Flow:** Evaluate TensionCurve at SessionElapsedTime → return value

#### `ShouldEscalate` → `Bool`
- **Description:** Returns true if actual tension is below the ideal curve (room to escalate). False if tension exceeds curve (should back off).
- **Parameters:** None
- **Blueprint Authority:** Any
- **Flow:** Compare CurrentTension to GetIdealTension()

#### `ShouldQuietBeat` → `Bool`
- **Description:** Returns true if a quiet beat should be enforced to prevent sustained high tension.
- **Parameters:** None
- **Blueprint Authority:** Any
- **Flow:** Check if CurrentTension > 0.8 AND time since LastQuietBeatTime > QuietBeatMinInterval

#### `EnforceQuietBeat` → `void`
- **Description:** Forces tension down to a low level temporarily. Clears or reduces active tension sources.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `Duration` | Float | How long the quiet beat lasts |
- **Blueprint Authority:** Any
- **Flow:** Reduce high tension sources → update LastQuietBeatTime → broadcast OnQuietBeatEnforced

#### `GetTensionDelta` → `Float`
- **Description:** Returns the difference between ideal and actual tension (positive = too low, negative = too high).
- **Parameters:** None
- **Blueprint Authority:** Any

---

## 5. Event Dispatchers

| Dispatcher | Parameters | Bind Access | Description |
|------------|-----------|-------------|-------------|
| `OnTensionChanged` | CurrentTension: Float, IdealTension: Float | Public | Tension level changed |
| `OnEscalationNeeded` | Delta: Float | Public | Tension below curve — escalate recommended |
| `OnDeescalationNeeded` | Delta: Float | Public | Tension above curve — back off recommended |
| `OnQuietBeatEnforced` | Duration: Float | Public | Quiet beat enforced |

---

## 6. Overridden Events / Custom Events

### Event: `Tick`
- **Description:** Updates session elapsed time and evaluates pacing against the tension curve.
- **Flow:**
  1. SessionElapsedTime += DeltaTime
  2. GetIdealTension from curve
  3. Compare CurrentTension to IdealTension
  4. If delta significant → broadcast appropriate signal (escalation/deescalation)
  5. Check ShouldQuietBeat → enforce if needed

---

## 7. Blueprint Graph Logic Flow

```mermaid
flowchart TD
    A[Tick] --> B[SessionElapsedTime += DeltaTime]
    B --> C[IdealTension = TensionCurve.Evaluate SessionElapsedTime]
    C --> D[Calculate TensionDelta = IdealTension - CurrentTension]
    D --> E{abs TensionDelta > threshold?}
    E -->|No| F[No action needed]
    E -->|Yes| G{TensionDelta > 0?}
    G -->|Yes - below curve| H[Broadcast OnEscalationNeeded]
    G -->|No - above curve| I[Broadcast OnDeescalationNeeded]
    H --> J[AdaptiveDirector escalates difficulty/intensity]
    I --> K[AdaptiveDirector reduces difficulty/intensity]
    
    L[Check ShouldQuietBeat] --> M{CurrentTension > 0.8 AND elapsed > QuietBeatMinInterval?}
    M -->|Yes| N[EnforceQuietBeat Duration]
    N --> O[Broadcast OnQuietBeatEnforced]
    M -->|No| P[Continue monitoring]
```

---

## 8. Communication Matrix

| Who Talks | How | What Is Sent |
|-----------|-----|-------------|
| [`BPC_ScareEventSystem`](BPC_ScareEventSystem.md) | Direct read | `ShouldEscalate()` — determines if scare events should fire |
| [`SS_AudioManager`](../10-adaptive/132_SS_AudioManager.md) | Direct call | `UpdateGameplayParameter("MusicIntensity")` — `CurrentTension: Float` |
| [`BPC_AdaptiveEnvironmentDirector`](BPC_AdaptiveEnvironmentDirector.md) | Dispatcher | `OnEscalationNeeded`, `OnDeescalationNeeded` |
| All tension-contributing systems | Direct call | `SetTensionSource(Tag, Value)` — AI presence, stress, atmosphere, combat |

---

## 9. Validation / Testing Checklist

- [ ] TensionCurve asset is assigned and valid
- [ ] SetTensionSource correctly updates TensionSources map and recalculates CurrentTension
- [ ] RemoveTensionSource removes entry and recalculates
- [ ] CurrentTension is always max of all active sources (or 0 if none)
- [ ] GetIdealTension reads correct value from curve at current time
- [ ] ShouldEscalate returns true when below curve, false when above
- [ ] ShouldQuietBeat enforces periodic relief during sustained high tension
- [ ] EnforceQuietBeat reduces tension and updates LastQuietBeatTime
- [ ] Edge case: no TensionCurve assigned → system runs with neutral pacing
- [ ] Edge case: all sources removed → CurrentTension drops to 0

---

## 10. Reuse Notes

- TensionCurve is the primary designer tool for pacing — author different curves per level/chapter
- TensionSources map allows any system to contribute to tension without coupling
- QuietBeatMinInterval prevents scare fatigue; tune per project for desired horror rhythm
- For non-adaptive games, leave TensionCurve unassigned and the system passes through neutral
- SessionElapsedTime resets on level load; use persistent elapsed time for full-session pacing

---

*Specification based on Master Section 9.8, line 2905.*