# 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.*