# 12 — Hiding System (`BPC_HidingSystem`)

## Purpose
Manages the player's ability to hide inside, behind, or under environmental objects. Handles entering/exiting hiding spots, line-of-sight checks against enemies, peeking, and stress reduction while concealed. The central component for stealth gameplay.

## Dependencies
- **Requires:** `FL_GameUtilities` (interface casts), `I_HidingSpot` (interface on world actors)
- **Required By:** `AI_EnemyController` (query hiding state for awareness), `BPC_StressSystem` (stress decay while hidden), `BPC_PlayerController` (movement/input restriction)
- **Engine/Plugin Requirements:** GameplayTags, LineTraceByChannel (LOS checks)

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

---

## 1. Enums

### `E_HideState`
| Value | Description |
|-------|-------------|
| `Exposed = 0` | Not hiding, fully visible |
| `Entering = 1` | Transient — animation playing to enter hide spot |
| `Hidden = 2` | Inside hide spot, concealed |
| `Peeking = 3` | Partially visible, can look around |
| `Exiting = 4` | Transient — animation playing to exit hide spot |

### `E_HideType`
| Value | Description |
|-------|-------------|
| `Locker = 0` | Fully enclosed (locker, wardrobe) |
| `BehindCover = 1` | Behind low wall, crate, counter |
| `Under = 2` | Under bed, table, porch |
| `InShadow = 3` | Standing in a shadow volume |
| `TallGrass = 4` | Crouch-moving through vegetation |

### `E_PeekDirection`
| Value | Description |
|-------|-------------|
| `Left = 0` | Peek left from behind cover |
| `Right = 1` | Peek right from behind cover |
| `Over = 2` | Peek over low cover |

---

## 2. Structs

### `S_HideSpotInfo`
| Field | Type | Description |
|-------|------|-------------|
| `HidingActor` | `AActor` | The hide spot actor |
| `HideType` | `E_HideType` | Type of hiding |
| `SlotCount` | `Integer` | How many can hide here (-1 = infinite) |
| `bHasPeekAbility` | `Boolean` | Can player peek from this spot? |
| `PeekSocketLeft` | `FName` | Socket name for left peek camera |
| `PeekSocketRight` | `FName` | Socket name for right peek camera |
| `PeekSocketOver` | `FName` | Socket name for over peek camera |
| `ExitLocation` | `Vector` | World location to place player on exit |
| `ExitRotation` | `Rotator` | Rotation to face on exit |
| `bBlocksStress` | `Boolean` | Whether this spot reduces stress gain |
| `HideTags` | `GameplayTagContainer` | Tags for filtering |
| `LOSTraceChannel` | `TEnumAsByte<ETraceTypeQuery>` | Trace channel for LOS checks from this spot |

---

## 3. Variables

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

| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `bCanHide` | `Boolean` | `true` | `Hiding Config` | Can this player hide at all? |
| `StressDecayWhileHidden` | `Float` | `5.0` | `Hiding Config` | Stress lost per second while hidden |
| `bEnemyCanFindHidingSpot` | `Boolean` | `true` | `Hiding Config` | Can enemies discover this spot |
| `MaxPeekDuration` | `Float` | `3.0` | `Hiding Config` | Max seconds player can peek before forced back |
| `PeekCooldown` | `Float` | `1.5` | `Hiding Config` | Seconds before player can peek again |
| `CapsuleCheckRadius` | `Float` | `50.0` | `Hiding Config` | Radius for capsule trace when finding hide spots |

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

| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `CurrentHideState` | `E_HideState` | `Exposed` | `Hiding State` | Current hiding state |
| `PreviousHideState` | `E_HideState` | `Exposed` | `Hiding State` | Previous state for transition detection |
| `CurrentHideSpot` | `S_HideSpotInfo` | `-` | `Hiding State` | Active hide spot data |
| `PendingHideSpot` | `AActor` | `None` | `Hiding State` | Hide spot being entered |
| `PeekTimerHandle` | `FTimerHandle` | `-` | `Hiding State` | Timer for max peek duration |
| `PeekCooldownTimerHandle` | `FTimerHandle` | `-` | `Hiding State` | Timer for peek cooldown |
| `bCanPeek` | `Boolean` | `true` | `Hiding State` | Whether peeking is allowed |
| `LastLOSCheckTime` | `Float` | `0.0` | `Hiding State` | World time of last LOS check |
| `LOSTimerHandle` | `FTimerHandle` | `-` | `Hiding State` | Timer for periodic LOS checks |
| `BreathHeldTimer` | `FTimerHandle` | `-` | `Hiding State` | Timer for breath-hold mechanic |
| `bIsHoldingBreath` | `Boolean` | `false` | `Hiding State` | Whether player is holding breath |

### Replicated (if multiplayer)

| Variable | Type | Condition | Description |
|----------|------|-----------|-------------|
| `CurrentHideState` | `E_HideState` | `RepNotify` | Replicated hide state |
| `CurrentHideSpot` | `S_HideSpotInfo` | `Replicated` | Replicated spot reference |

---

## 4. Functions

### Public Functions

#### `EnterHideSpot` → `Boolean`
- **Description:** Attempts to enter a hiding spot. Returns true if successful.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `HideSpotActor` | `AActor` | The hide spot to enter |
- **Blueprint Authority:** Server (if MP), Any (single-player)
- **Flow:**
  1. Early-out if CurrentHideState != Exposed or !bCanHide
  2. Validate that HideSpotActor implements I_HidingSpot
  3. Check slot availability (I_HidingSpot.Execute_HasAvailableSlots)
  4. If available:
     - Set CurrentHideState = Entering
     - Store HideSpotActor info in CurrentHideSpot
     - Disable player movement input
     - Play entering animation (via ABP_GASP)
     - On animation complete: Set CurrentHideState = Hidden
     - Fire OnHideStateChanged
     - Start stress decay timer
     - Start periodic LOS check timer
     - Return true
  5. If full: return false

#### `ExitHideSpot` → `void`
- **Description:** Exits the current hiding spot.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `bForceExit` | `Boolean` | If true, skip exit animation |
- **Flow:**
  1. If CurrentHideState != Hidden and CurrentHideState != Peeking: return
  2. Set CurrentHideState = Exiting
  3. If bForceExit: skip animation, immediately set Exposed
  4. Else: play exiting animation
  5. On animation complete or bForceExit:
     - Teleport player to CurrentHideSpot.ExitLocation with ExitRotation
     - Re-enable movement input
     - Set CurrentHideState = Exposed
     - Clear CurrentHideSpot
     - Stop stress decay and LOS timers
     - Fire OnHideStateChanged
     - Fire OnExitedHidingSpot

#### `StartPeek` → `void`
- **Description:** Begins peeking from behind cover.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `Direction` | `E_PeekDirection` | Which direction to peek |
- **Flow:**
  1. Early-out if CurrentHideState != Hidden or !bCanPeek or !CurrentHideSpot.bHasPeekAbility
  2. Set CurrentHideState = Peeking
  3. Move camera to peek socket location
  4. Start MaxPeekDuration timer
  5. Start PeekCooldown timer (bCanPeek = false during cooldown)
  6. Fire OnPeekStarted

#### `StopPeek` → `void`
- **Description:** Ends the peek and returns to Hidden state.
- **Flow:**
  1. If CurrentHideState != Peeking: return
  2. Set CurrentHideState = Hidden
  3. Return camera to default hide position
  4. Fire OnPeekEnded

#### `IsPlayerDetectable` → `Boolean`
- **Description:** Checks if the player is detectable from a given enemy location, considering hide type and edges.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `EnemyLocation` | `Vector` | Location of the enemy |
  | `DetectionRange` | `Float` | Max range for detection check |
- **Flow:**
  1. If CurrentHideState != Hidden: return true (fully detectable)
  2. If EnemyLocation distance > DetectionRange: return false
  3. Perform line trace from enemy to player's approximate position
  4. If trace hits CurrentHideSpot.HidingActor before player: return false (blocked by cover)
  5. If trace hits player directly: return true (cover not fully effective)
  6. Edge case: peek state increases detection radius

#### `TryBreathHold` → `void`
- **Description:** Player holds breath to reduce noise/detectability for a short time.
- **Flow:**
  1. bIsHoldingBreath = true
  2. Start breath-hold timer (e.g., 8 seconds)
  3. On timer end: forced exhale, brief noise event, bIsHoldingBreath = false
  4. Fire OnBreathHoldChanged

### Protected / Private Functions

#### `PerformLOSCheck` → `void`
- **Description:** Timer callback — checks if any enemy has line of sight to this hide spot.
- **Flow:**
  1. Get all AI_EnemyController instances within detection range
  2. For each enemy: call IsPlayerDetectable(EnemyLocation)
  3. If any enemy has LOS:
     - Fire OnHideSpotCompromised
     - If CurrentHideSpot.bEnemyCanFindHidingSpot: fire OnForcedExitWarning

#### `OnAnimationHideEnterComplete` → `void`
- **Description:** Animation notify callback when enter animation finishes.
- **Flow:**
  1. Set CurrentHideState = Hidden
  2. Attach player to hide spot socket if applicable
  3. Fire OnHideStateChanged
  4. Start stress decay loop

#### `OnAnimationHideExitComplete` → `void`
- **Description:** Animation notify callback when exit animation finishes.
- **Flow:**
  1. Perform final exit steps already queued in ExitHideSpot

---

## 5. Event Dispatchers

| Dispatcher | Parameters | Bind Access | Description |
|------------|-----------|-------------|-------------|
| `OnHideStateChanged` | `E_HideState OldState`, `E_HideState NewState` | `Public` | Fired on any hide state change |
| `OnEnteredHidingSpot` | `AActor HideSpot`, `E_HideType HideType` | `Public` | Fired when player successfully hides |
| `OnExitedHidingSpot` | `AActor HideSpot` | `Public` | Fired when player leaves hiding |
| `OnPeekStarted` | `E_PeekDirection Direction` | `Public` | Fired when player starts peeking |
| `OnPeekEnded` | `none` | `Public` | Fired when player stops peeking |
| `OnHideSpotCompromised` | `AActor EnemyActor` | `Public` | Fired when an enemy sees the player's spot |
| `OnForcedExitWarning` | `float TimeUntilDiscovered` | `Public` | Fired when enemy is approaching known spot |
| `OnBreathHoldChanged` | `bool bIsHoldingBreath` | `Public` | Fired when breath hold toggles |

---

## 6. Overridden Events / Custom Events

### Event: `BeginPlay`
- **Description:** Initialises state, binds to input actions.
- **Flow:**
  1. Set CurrentHideState = Exposed
  2. Register with DA_GameTagRegistry if needed
  3. Cache reference to CharacterMovementComponent for disabling during hide

### Custom Event: `ForceKickFromHide`
- **Description:** Called externally (e.g., by enemy discovery) to force player out of hiding.
- **Flow:**
  1. Call ExitHideSpot(bForceExit = true)
  2. Apply brief stun or camera shake
  3. Fire OnForcedExitWarning with 0 seconds

### Custom Event: `OnDamageWhileHiding`
- **Description:** Listener for when damage is taken while hidden.
- **Flow:**
  1. If damage source penetrates cover: force exit
  2. Else: reduce stress instead of health (environmental damage)

---

## 7. Blueprint Graph Logic Flow

```mermaid
flowchart TD
    A[EnterHideSpot called] --> B{Is Exposed?}
    B -->|No| C[Return false]
    B -->|Yes| D[Validate I_HidingSpot]
    D --> E{Has available slots?}
    E -->|No| F[Return false]
    E -->|Yes| G[Set Entering state]
    G --> H[Disable movement input]
    H --> I[Play Enter animation]
    I --> J[Animation notify: OnComplete]
    J --> K[Set Hidden state]
    K --> L[Start stress decay loop]
    L --> M[Start periodic LOS check]
    M --> N[Fire OnEnteredHidingSpot]
    N --> O[Return true]
```

---

## 8. Communication Matrix

| Who Talks | How | What Is Sent |
|-----------|-----|-------------|
| `BPC_HidingSystem` | `Dispatcher` | `OnHideStateChanged` -> `BPC_PlayerController` (input), `ABP_GASP` (anim), `BPC_StressSystem` (decay), `AI_EnemyController` (awareness reset) |
| `BPC_HidingSystem` | `Dispatcher` | `OnHideSpotCompromised` -> `BPC_StressSystem` (stress spike), `BP_AudioManager` (tension music) |
| `BPC_HidingSystem` | `Dispatcher` | `OnForcedExitWarning` -> `WBP_HUD` (hide icon flash), `BP_AudioManager` (heartbeat) |
| `External (AI)` | `Query` | `IsPlayerDetectable` called by `AI_EnemyController` for LOS checks |
| `External (World)` | `Interface` | `I_HidingSpot` on `BP_Locker`, `BP_Wardrobe`, `BP_Cover` |
| `BPC_HidingSystem` | `Dispatcher` | `OnPeekStarted/Ended` -> `BPC_CameraStateLayer` (FOV/position) |

---

## 9. Validation / Testing Checklist

- [ ] EnterHideSpot correctly transitions through Entering -> Hidden
- [ ] ExitHideSpot correctly transitions through Exiting -> Exposed
- [ ] Forced exit with bForceExit = true skips animation, immediately exits
- [ ] Peek timer forces player back to Hidden after MaxPeekDuration
- [ ] PeekCooldown prevents rapid peek toggling
- [ ] IsPlayerDetectable returns correct values for all hide types and peek states
- [ ] Multiple hide spots with limited slots: full slots reject entry
- [ ] Stress decays while hidden at configured rate
- [ ] LOS check detects enemies within range and fires compromise dispatcher
- [ ] Edge case: EnterHideSpot called while already hiding returns false
- [ ] Edge case: ExitHideSpot called while Exposed does nothing
- [ ] Edge case: Destroying hide spot while inside force-exits player

---

## 10. Reuse Notes

- Enemy AI characters can use a simplified version to find and occupy hide spots.
- Hide spots can be pre-placed (lockers, beds) or dynamic (shadow volumes, tall grass).
- The LOS trace channel should be custom (e.g., DetectionChannel) to ignore small props.
- Breath-hold mechanic adds depth to stealth gameplay near enemies.
- For multiplayer: only the hiding player knows their state; other players see a generic "occupado" on the spot.

---

## 11. Multiplayer Networking (Expanded)

### Server RPCs
| RPC | Direction | Description |
|-----|-----------|-------------|
| `Server_EnterHideSpot` | Client→Server | Client requests entering hide spot. Server validates slot availability, proximity. |
| `Server_ExitHideSpot` | Client→Server | Client requests exiting. Server validates, teleports player, replicates. |
| `Server_StartPeek` | Client→Server | Client starts peeking. Server validates peek ability, starts duration timer. |
| `Server_StopPeek` | Client→Server | Client stops peeking. Server returns to Hidden state. |
| `Server_TryBreathHold` | Client→Server | Client holds breath. Server validates, applies noise reduction on server. |

### Authority
- `EnterHideSpot`, `ExitHideSpot` are server-authoritative.
- `IsPlayerDetectable` is server-only — AI runs on server.
- `PerformLOSCheck` runs on server; results multicast to clients for UI feedback.
- `ForceKickFromHide` is server-only (triggered by enemy discovery / damage).

### Client Prediction
- Enter/exit animations: client predicts; server confirms via replicated `CurrentHideState`.
- Peek: camera offset is local; state replicated.
- Hide spot occupation: `I_HidingSpot.OccupantPawn` is replicated — other clients see "Occupied".
- Hide spots with bBlocksStress = true can double as narrative safe rooms.

---

*Blueprint Spec: Hiding System. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*