# 14 — Camera State Layer (`BPC_CameraStateLayer`)

## Purpose
Manages the first-person camera's dynamic behaviour: field-of-view changes based on movement speed, camera shake from impacts/events, head bob intensity, and location offsets for crouch/prone/hide. Centralises all camera-modifying logic so other systems can request camera changes without directly manipulating the camera.

## Dependencies
- **Requires:** `BPC_MovementStateSystem` (speed-based FOV), `BPC_StressSystem` (stress-based distortion)
- **Required By:** `PC_PlayerController` (camera management), `BPC_HidingSystem` (peek camera offsets), `WBP_HUD` (post-process materials)
- **Engine/Plugin Requirements:** Camera Shake patterns, Post-Process Materials, Material Parameter Collections

## Class Info
| Property | Value |
|----------|-------|
| **Parent Class** | `ActorComponent` |
| **Class Type** | Blueprint Component |
| **Asset Path** | `Content/Framework/Player/BPC_CameraStateLayer` |
| **Implements Interfaces** | None |

---

## 1. Enums

### `E_CameraState`
| Value | Description |
|-------|-------------|
| `Default = 0` | Normal gameplay FOV |
| `Aiming = 1` | Zoomed in (weapon aim, inspection) |
| `Sprinting = 2` | FOV widened for speed |
| `Crouching = 3` | Slight downward offset |
| `Peeking = 4` | Offset sideways from cover |
| `Hiding = 5` | Constricted FOV, darkness from inside |
| `Stressed = 6` | FOV pulsing, chromatic aberration |
| `Injured = 7` | FOV wobble, blood tint |
| `Death = 8` | Fade to black, camera drop |
| `Cutscene = 9` | Fixed camera, no player control |

### `E_CameraShakePriority`
| Value | Description |
|-------|-------------|
| `Low = 0` | Ambient shake (footstep, wind) |
| `Medium = 1` | Impact shake (damage, explosion) |
| `High = 2` | Critical shake (near-death, supernatural event) |
| `Cinematic = 3` | Scripted shake, overrides everything |

---

## 2. Structs

### `S_CameraShakeRequest`
| Field | Type | Description |
|-------|------|-------------|
| `ShakeClass` | `TSubclassOf<UCameraShakeBase>` | Camera shake blueprint class |
| `Scale` | `Float` | Intensity scale [0..1] |
| `Priority` | `E_CameraShakePriority` | Priority level |
| `Duration` | `Float` | How long the shake lasts |
| `bIsLooping` | `Boolean` | Whether the shake loops |
| `Tag` | `GameplayTag` | Unique identifier to prevent stacking same shake |

### `S_CameraStateConfig`
| Field | Type | Description |
|-------|------|-------------|
| `TargetFOV` | `Float` | FOV value for this state |
| `BlendSpeed` | `Float` | How fast to blend to this FOV |
| `HeadOffset` | `Vector` | Location offset for head/camera |
| `HeadRotation` | `Rotator` | Rotation offset |
| `bConstrainPitch` | `Boolean` | Limit vertical look in this state |
| `MinPitch` | `Float` | Minimum pitch angle |
| `MaxPitch` | `Float` | Maximum pitch angle |
| `bEnableHeadBob` | `Boolean` | Enable head bob in this state |
| `HeadBobAmplitude` | `Float` | Head bob strength |
| `HeadBobFrequency` | `Float` | Head bob speed |

### `S_PostProcessOverride`
| Field | Type | Description |
|-------|------|-------------|
| `bOverrideVignette` | `Boolean` | Override vignette intensity |
| `VignetteIntensity` | `Float` | Vignette strength [0..1] |
| `bOverrideChromaticAberration` | `Boolean` | Override CA intensity |
| `ChromaticAberrationIntensity` | `Float` | CA strength [0..1] |
| `bOverrideColorGrading` | `Boolean` | Override color grading |
| `ColorGradingLUT` | `Texture` | LUT texture for color grading |
| `BlendTime` | `Float` | Time to blend to new post-process settings |

---

## 3. Variables

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

| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `DefaultFOV` | `Float` | `90.0` | `Camera Config` | Default first-person FOV |
| `StateConfigs` | `Map<E_CameraState, S_CameraStateConfig>` | `all states` | `Camera Config` | FOV/offset values per state |
| `DefaultPostProcess` | `S_PostProcessOverride` | `-` | `Camera Config` | Default post-process settings |
| `BaseHeadBobAmplitude` | `Float` | `0.1` | `Camera Config` | Default head bob strength |
| `BaseHeadBobFrequency` | `Float` | `1.0` | `Camera Config` | Default head bob speed |
| `SprintFOVMultiplier` | `Float` | `1.1` | `Camera Config` | FOV multiplied by this when sprinting |
| `StressFOVPulseAmount` | `Float` | `5.0` | `Camera Config` | FOV pulse range when stressed (degrees) |
| `StressFOVPulseSpeed` | `Float` | `0.5` | `Camera Config` | Speed of stress FOV pulse |

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

| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `CurrentCameraState` | `E_CameraState` | `Default` | `Camera State` | Active camera state |
| `CurrentTargetFOV` | `Float` | `90.0` | `Camera State` | Current FOV target being blended to |
| `CurrentFOV` | `Float` | `90.0` | `Camera State` | Actual current FOV |
| `ActiveShakeRequests` | `Map<FName, S_CameraShakeRequest>` | `{}` | `Camera State` | Active shakes by tag |
| `HighestShakePriority` | `E_CameraShakePriority` | `Low` | `Camera State` | Current highest priority shake |
| `CameraOffsetTarget` | `Vector` | `(0,0,0)` | `Camera State` | Target head offset being blended to |
| `CameraRotationTarget` | `Rotator` | `(0,0,0)` | `Camera State` | Target rotation offset |
| `bIsPitchConstrained` | `Boolean` | `false` | `Camera State` | Whether pitch is currently constrained |
| `PostProcessBlendAlpha` | `Float` | `0.0` | `Camera State` | Current post-process blend |
| `FOVTimerHandle` | `FTimerHandle` | `-` | `Camera State` | Timer for FOV pulse when stressed |
| `PlayerCameraManager` | `APlayerCameraManager` | `None` | `Camera State` | Cached camera manager reference |

### Replicated (if multiplayer)

| Variable | Type | Condition | Description |
|----------|------|-----------|-------------|
| `CurrentCameraState` | `E_CameraState` | `Replicated` | Synced camera state |

---

## 4. Functions

### Public Functions

#### `RequestCameraState` → `void`
- **Description:** Sets the camera to a state config. Multiple states can stack; highest priority wins.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `NewState` | `E_CameraState` | Desired camera state |
  | `bImmediate` | `Boolean` | Skip blend |
- **Blueprint Authority:** Any
- **Flow:**
  1. Store CurrentCameraState as PreviousState
  2. CurrentCameraState = NewState
  3. Get S_CameraStateConfig for NewState
  4. Set CurrentTargetFOV to config.TargetFOV
  5. Set CameraOffsetTarget to config.HeadOffset
  6. Set CameraRotationTarget to config.HeadRotation
  7. Set pitch constraints from config
  8. Begin blending toward targets over BlendSpeed
  9. Fire OnCameraStateChanged

#### `PlayCameraShake` → `void`
- **Description:** Plays a camera shake, respecting priority to prevent shake stacking.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `ShakeRequest` | `S_CameraShakeRequest` | Shake parameters |
- **Flow:**
  1. If ShakeRequest.Tag already in ActiveShakeRequests:
     - If new priority >= existing: replace existing shake
     - If new priority < existing: ignore
  2. Ask PlayerCameraManager to play shake
  3. Store in ActiveShakeRequests
  4. If Duration > 0: start timer to auto-stop shake after Duration

#### `StopCameraShake` → `void`
- **Description:** Stops a specific camera shake.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `ShakeTag` | `GameplayTag` | Tag identifying the shake |
- **Flow:**
  1. If ShakeTag exists in ActiveShakeRequests:
     - Stop the shake in PlayerCameraManager
     - Remove from ActiveShakeRequests

#### `ApplyPostProcessOverride` → `void`
- **Description:** Applies temporary post-process settings (vignette, CA, color grading).
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `Override` | `S_PostProcessOverride` | Post-process settings |
  | `bInstant` | `Boolean` | If true, blend instantly |
- **Flow:**
  1. If bInstant: apply immediately
  2. Else: start blend over Override.BlendTime
  3. Update Material Parameter Collection values

#### `ClearPostProcessOverride` → `void`
- **Description:** Returns post-process to default settings.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `BlendTime` | `Float` | Seconds to blend back to default |
- **Flow:**
  1. Blend all post-process parameters to 0 / neutral over BlendTime

#### `GetCameraStateConfig` → `S_CameraStateConfig`
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `State` | `E_CameraState` | Which state to query |
- **Flow:** Return StateConfigs[State]

### Protected / Private Functions

#### `BlendToTargetFOV (Tick)` → `void`
- **Description:** Each tick, blend CurrentFOV toward CurrentTargetFOV.
- **Flow:**
  1. Delta = CurrentTargetFOV - CurrentFOV
  2. If Abs(Delta) < 0.1: CurrentFOV = CurrentTargetFOV
  3. Else: CurrentFOV += Delta * BlendSpeed * DeltaTime
  4. Apply CurrentFOV to PlayerCameraManager

#### `BlendCameraOffset (Tick)` → `void`
- **Description:** Each tick, blend camera location/rotation toward target.
- **Flow:**
  1. InterpVTo(CameraOffsetTarget) for location
  2. InterpRTo(CameraRotationTarget) for rotation
  3. Apply offset to camera boom or view target

#### `UpdateStressPulse` → `void`
- **Description:** If stressed, pulse FOV for uneasy effect.
- **Flow:**
  1. If CurrentCameraState != Stressed: return
  2. PulseValue = Sin(Time * StressFOVPulseSpeed * PI * 2) * StressFOVPulseAmount
  3. CurrentTargetFOV = StateConfigs[Stressed].TargetFOV + PulseValue

---

## 5. Event Dispatchers

| Dispatcher | Parameters | Bind Access | Description |
|------------|-----------|-------------|-------------|
| `OnCameraStateChanged` | `E_CameraState OldState`, `E_CameraState NewState` | `Public` | Fired when camera state changes |
| `OnFOVChanged` | `float OldFOV`, `float NewFOV` | `Public` | Fired when FOV changes |
| `OnShakeStarted` | `GameplayTag ShakeTag`, `float Scale` | `Public` | Fired when a camera shake begins |
| `OnShakeEnded` | `GameplayTag ShakeTag` | `Public` | Fired when a camera shake ends |
| `OnPostProcessChanged` | `float Vignette`, `float ChromaticAb` | `Public` | Fired on post-process change |
| `OnCameraConstrained` | `bool bIsConstrained`, `float MinPitch`, `float MaxPitch` | `Public` | Fired when pitch constraints change |

---

## 6. Overridden Events / Custom Events

### Event: `BeginPlay`
- **Description:** Cache camera manager, set initial FOV, bind to state systems.
- **Flow:**
  1. Get PlayerCameraManager from owner's controller
  2. Set CurrentFOV = DefaultFOV
  3. Apply to camera
  4. Bind to BPC_MovementStateSystem.OnMovementModeChanged
  5. Bind to BPC_StressSystem.OnStressTierChanged

### Custom Event: `OnMovementModeChangedHandler`
- **Description:** Reacts to movement changes for FOV adjustments.
- **Flow:**
  1. If Sprinting: RequestCameraState(Sprinting)
  2. If Crouching: RequestCameraState(Crouching)
  3. If Idle/Walking: RequestCameraState(Default)

### Custom Event: `OnStressTierChangedHandler`
- **Description:** Reacts to stress tier changes for camera effects.
- **Flow:**
  1. If tier >= Panicked: RequestCameraState(Stressed), apply CA post-process
  2. If tier < Distressed: return to Default camera, clear post-process

---

## 7. Blueprint Graph Logic Flow

```mermaid
flowchart TD
    A[RequestCameraState called] --> B{NewState == Current?}
    B -->|Yes| C[Return]
    B -->|No| D[Look up S_CameraStateConfig]
    D --> E[Set FOV target]
    E --> F[Set head offset/rotation targets]
    F --> G[Set pitch constraints]
    G --> H[Fire OnCameraStateChanged]
    H --> I[BlendTick: each frame]
    I --> J[Interp FOV toward target]
    J --> K[Interp offset toward target]
    K --> L[Apply to PlayerCameraManager]
    L --> M{Is Stressed state?}
    M -->|Yes| N[Apply FOV pulse via sine wave]
    M -->|No| O[Clear pulse effect]
```

---

## 8. Communication Matrix

| Who Talks | How | What Is Sent |
|-----------|-----|-------------|
| `BPC_CameraStateLayer` | `Dispatcher` | `OnCameraStateChanged` -> `ABP_GASP` (anim offset), `WBP_HUD` (post-process) |
| `BPC_CameraStateLayer` | `Dispatcher` | `OnShakeStarted` / `OnShakeEnded` -> `BP_AudioManager` |
| `BPC_CameraStateLayer` | `Listener` | Binds to `BPC_MovementStateSystem.OnMovementModeChanged` for FOV |
| `BPC_CameraStateLayer` | `Listener` | Binds to `BPC_StressSystem.OnStressTierChanged` for distortion |
| `BPC_CameraStateLayer` | `Listener` | Binds to `BPC_HealthSystem.OnDamageTaken` for injury shake |
| `External (Any)` | `Direct` | Calls `PlayCameraShake` for any gameplay event |
| `BPC_HidingSystem` | `Direct` | Calls `RequestCameraState(Peeking)` with peek offset |
| `BPC_HidingSystem` | `Direct` | Calls `RequestCameraState(Hiding)` with hide FOV |

---

## 9. Validation / Testing Checklist

- [ ] FOV blends smoothly between states at configured speed
- [ ] Sprinting FOV wider than Default, returns on stop
- [ ] Camera shake priority prevents lower-priority shakes from overriding high-priority ones
- [ ] Stress pulsing creates smooth FOV oscillation without jitter
- [ ] Post-process overrides blend in/out correctly
- [ ] Pitch constraints apply correctly in hiding/peeking states
- [ ] Multiple state changes are queued — last request wins
- [ ] Edge case: Rapid state switching between sprint and crouch transitions smoothly
- [ ] Edge case: Camera shake with same tag replaces shake without stacking
- [ ] Edge case: ClearPostProcessOverride returns to default with correct blend
- [ ] All active shakes stop on component destroy or pawn unpossess

---

## 10. Reuse Notes

- Camera shake patterns should be authored as Blueprint Camera Shake classes in Content/Framework/Player/Shakes/.
- Post-process settings use a Material Parameter Collection (MPC_CameraEffects) for real-time parameter updates.
- The stress FOV pulse creates a subtle "unease" effect — pair with audio breathing changes for best results.
- For multiplayer: camera shakes are local-only; state changes replicate for shared visual consistency.
- Custom camera states can be added by extending E_CameraState and adding a config entry.

---

## 11. Multiplayer Networking

**Replication: State only — effects are local.** Camera FOV blending, shake, and post-processing are per-client experiences. Only the state enum syncs.

| Variable | Condition | Purpose |
|----------|-----------|---------|
| `CurrentCameraState` | `Replicated` | Synced for shared context (e.g., all players know someone is aiming) |

### Authority
- `RequestCameraState` can be called by client; state replicates to all.
- `PlayCameraShake`, `ApplyPostProcessOverride`, `BlendToTargetFOV` are **local only**.
- FOV pulse, head bob, camera offset blending are **local only**.

### Client Prediction
- Camera is inherently local — no prediction needed. State changes replicate for other players' awareness.

---

*Blueprint Spec: Camera State Layer. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*