# 16 — Interaction Detector (`BPC_InteractionDetector`)

## Purpose
The player's "interaction eye" — performs trace-based detection of interactable objects in the world, sorts them by priority/distance, tracks the current best target, and manages the interaction prompt widget. Every interactive system in the game funnels through this component.

## Dependencies
- **Requires:** `I_Interactable` (interface on all interactable actors), `PlayerCameraManager` (for trace origin), `BPC_MovementStateSystem` (block interaction during specific states)
- **Required By:** `WBP_InteractionPrompt` (HUD element), `BPC_PickupComponent`, `BPC_InteractableDoorComponent`, `BPC_LeverPuzzleComponent`
- **Engine/Plugin Requirements:** `LineTraceByChannel` (ECC_GameTraceChannel1 = Interaction), `TimerHandle` for scan interval

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

---

## 1. Enums

### `E_InteractionInputMode`
| Value | Description |
|-------|-------------|
| `Press = 0` | Single press to interact |
| `Hold = 1` | Hold over duration to confirm |
| `DoubleTap = 2` | Two rapid presses |
| `Auto = 3` | Automatic on proximity (no input) |

### `E_InteractionPriority`
| Value | Description |
|-------|-------------|
| `Low = 0` | Ambient / cosmetic items |
| `Normal = 1` | Standard pickups, doors |
| `High = 2` | Story-critical, puzzles |
| `Emergency = 3` | Immediate threat (hide spot, weapon) |

### `E_DetectionState`
| Value | Description |
|-------|-------------|
| `NoTarget = 0` | Nothing in range |
| `TargetInRange = 1` | Potential target found |
| `TargetConfirmed = 2` | Best target selected |
| `Interacting = 3` | Currently performing interaction |

---

## 2. Structs

### `S_InteractableTarget`
| Field | Type | Description |
|-------|------|-------------|
| `TargetActor` | `AActor` | The interactable actor |
| `InterfaceRef` | `I_Interactable` | Cast to interface |
| `Priority` | `E_InteractionPriority` | Priority level |
| `Distance` | `Float` | Distance from player to target |
| `InteractionLabel` | `FText` | Display name (e.g. "Open Door", "Pick Up Key") |
| `InputMode` | `E_InteractionInputMode` | How interaction is triggered |
| `HoldDuration` | `Float` | Seconds required if InputMode is Hold |
| `bIsHighlighted` | `Boolean` | Whether target is visually highlighted |
| `TargetLocation` | `Vector` | World location for UI widget |

### `S_InteractionResult`
| Field | Type | Description |
|-------|------|-------------|
| `bSuccess` | `Boolean` | Whether interaction was successful |
| `FailureReason` | `FText` | Human-readable failure reason |
| `TargetActor` | `AActor` | The interacted actor |
| `ContextTag` | `GameplayTag` | Tag for event recording |

---

## 3. Variables

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

| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `InteractionRange` | `Float` | `250.0` | `Detection Config` | Max trace distance (cm) |
| `DetectionAngle` | `Float` | `45.0` | `Detection Config` | Cone half-angle for detection (degrees) |
| `TraceRadius` | `Float` | `10.0` | `Detection Config` | Sphere trace radius |
| `ScanInterval` | `Float` | `0.1` | `Detection Config` | Seconds between detection scans |
| `MaxTargetsInRange` | `Integer` | `16` | `Detection Config` | Max detected targets per scan |
| `bShowDebugTrace` | `Boolean` | `false` | `Detection Config` | Visualise trace in editor |
| `TraceChannel` | `TEnumAsByte<ECollisionChannel>` | `ECC_GameTraceChannel1` | `Detection Config` | Collision channel for interaction |
| `bBlockDuringDeath` | `Boolean` | `true` | `Detection Config` | Block interaction while dead |
| `bBlockDuringHiding` | `Boolean` | `true` | `Detection Config` | Block interaction while hidden |
| `bBlockDuringCombat` | `Boolean` | `false` | `Detection Config` | Block interaction during combat |

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

| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `BestTargetIndex` | `Integer` | `-1` | `Detection State` | Index of currently selected target |
| `DetectedTargets` | `Array<S_InteractableTarget>` | `[]` | `Detection State` | All detected targets this scan |
| `CurrentTarget` | `S_InteractableTarget` | `-` | `Detection State` | Currently selected best target |
| `DetectionState` | `E_DetectionState` | `NoTarget` | `Detection State` | Current state of the detector |
| `bIsPerformingInteraction` | `Boolean` | `false` | `Detection State` | Whether interaction is in progress |
| `HoldInteractionProgress` | `Float` | `0.0` | `Detection State` | Progress for hold-type interactions |
| `OwnerPlayerController` | `APlayerController` | `None` | `Cache` | Cached player controller |
| `OwnerCameraManager` | `APlayerCameraManager` | `None` | `Cache` | Cached camera manager |
| `ScanTimerHandle` | `FTimerHandle` | `-` | `Timing` | Timer for scan interval |
| `DetectedActors_LastFrame` | `Set<AActor>` | `{}` | `Tracking` | Previous frame targets for enter/exit detection |

### Replicated (if multiplayer)

| Variable | Type | Condition | Description |
|----------|------|-----------|-------------|
| `CurrentTarget` | `S_InteractableTarget` | `Replicated` | Synced selected target |

---

## 4. Functions

### Public Functions

#### `StartDetection` → `void`
- **Description:** Begins the scan loop for interactable targets.
- **Parameters:** None
- **Flow:**
  1. Get owner Player Controller
  2. Get Player Camera Manager
  3. Start scan timer (ScanInterval)
  4. If bShowDebugTrace: enable debug line visualisation

#### `StopDetection` → `void`
- **Description:** Stops the scan loop and clears detected targets.
- **Parameters:** None
- **Flow:**
  1. Clear scan timer
  2. Clear DetectedTargets array
  3. Set DetectionState = NoTarget
  4. Fire OnTargetLost
  5. Clear any highlight effects

#### `PerformInteraction` → `S_InteractionResult`
- **Description:** Executes interaction on the current best target.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `InputMode` | `E_InteractionInputMode` | How the input was triggered |
- **Flow:**
  1. If not CurrentTarget valid or CurrentTarget actor is None: return failure
  2. If bIsPerformingInteraction: return failure (already interacting)
  3. If InputMode != CurrentTarget.InputMode: return failure (input mismatch)
  4. Set bIsPerformingInteraction = true
  5. Set DetectionState = Interacting
  6. Fire OnInteractionStarted
  7. Call CurrentTarget.InterfaceRef.ExecuteInteraction (owner)
  8. Wait for I_Interactable.OnInteractionCompleted or timeout
  9. On completion: set bIsPerformingInteraction = false
  10. Resume scanning
  11. Return S_InteractionResult with success/failure

#### `CancelInteraction` → `void`
- **Description:** Aborts the current interaction if possible.
- **Parameters:** None
- **Flow:**
  1. If not bIsPerformingInteraction: return
  2. Call CurrentTarget.InterfaceRef.CancelInteraction
  3. Set bIsPerformingInteraction = false
  4. HoldInteractionProgress = 0.0
  5. Set DetectionState = TargetConfirmed
  6. Fire OnInteractionCancelled

#### `GetBestTarget` → `S_InteractableTarget`
- **Description:** Returns the current best target.
- **Parameters:** None
- **Flow:** Return CurrentTarget

#### `HasTarget` → `Boolean`
- **Parameters:** None
- **Flow:** Return DetectionState >= TargetInRange

#### `ForceSetTarget` → `void`
- **Description:** Forcefully sets a specific target (for scripted interactions).
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `TargetActor` | `AActor` | The specific actor to target |
- **Flow:**
  1. If TargetActor implements I_Interactable:
     - Build S_InteractableTarget
     - Set CurrentTarget
     - Set DetectionState = TargetConfirmed
     - Fire OnTargetFound
  2. Else: fire OnInteractionError(not interactable)

### Private Functions

#### `ScanForInteractables (Timer)` → `void`
- **Description:** Timer callback — performs trace and sorts targets.
- **Flow:**
  1. If DetectionState == Interacting: return (skip scan while interacting)
  2. Get camera forward vector and world location
  3. Sphere trace forward (TraceRadius, InteractionRange, TraceChannel)
  4. For each hit actor:
     - If implements I_Interactable: add to DetectedTargets
     - If not: skip
  5. Remove duplicates by actor reference
  6. Sort by Priority (descending), then Distance (ascending)
  7. Clamp to MaxTargetsInRange
  8. Track enter/exit for each actor — fire OnTargetEntered / OnTargetExited
  9. Select BestTarget from sorted list
  10. Update DetectionState accordingly
  11. Fire OnTargetFound or OnTargetLost

#### `CalculateInteractionScore` → `Float`
- **Description:** Computes a score for a target based on priority, distance, and facing angle.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `Target` | `S_InteractableTarget` | The target to score |
- **Flow:**
  1. Score = Priority * 100 - Distance * 0.5
  2. Facing bonus: if angle < 15 deg, add 50
  3. Return Max(0, Score)

#### `HighlightTarget` → `void`
- **Description:** Applies highlight effect to target actor.
- **Parameters:**
  | Param | Type | Description |
  |-------|------|-------------|
  | `Target` | `S_InteractableTarget` | Target to highlight |
  | `bHighlighted` | `Boolean` | Whether to enable or disable |
- **Flow:**
  1. Call Target.InterfaceRef.SetHighlighted(bHighlighted)

#### `GetViewTraceOrigin` → `Vector, Vector`
- **Description:** Returns camera location and forward direction for trace.
- **Parameters:** None
- **Flow:**
  1. If OwnerCameraManager valid:
     - Return cam location and forward vector
  2. Else: fall back to owner actor location and forward

---

## 5. Event Dispatchers

| Dispatcher | Parameters | Bind Access | Description |
|------------|-----------|-------------|-------------|
| `OnTargetFound` | `S_InteractableTarget NewTarget` | `Public` | New best target selected |
| `OnTargetLost` | `S_InteractableTarget LostTarget` | `Public` | Current target lost from view / range |
| `OnTargetEntered` | `AActor TargetActor` | `Public` | Actor entered detectable range |
| `OnTargetExited` | `AActor TargetActor` | `Public` | Actor left detectable range |
| `OnInteractionStarted` | `S_InteractableTarget Target` | `Public` | Interaction began |
| `OnInteractionCompleted` | `S_InteractionResult Result` | `Public` | Interaction finished |
| `OnInteractionCancelled` | `S_InteractableTarget Target` | `Public` | Interaction was cancelled |
| `OnInteractionError` | `FText ErrorMessage` | `Public` | Interaction blocked or failed |
| `OnHoldProgressUpdated` | `Float Progress`, `S_InteractableTarget Target` | `Public` | Hold interaction progress [0..1] |

---

## 6. Overridden Events / Custom Events

### Event: `BeginPlay`
- **Description:** Cache references, bind to relevant systems, start detection.
- **Flow:**
  1. Get owning actor → ensure it is a Pawn
  2. Get Player Controller from owner
  3. Get Player Camera Manager from controller
  4. Bind to BPC_MovementStateSystem.OnPostureChanged — block when hidden
  5. Bind to BPC_HealthSystem.OnDeathStateChanged — block when dead
  6. StartDetection()

### Custom Event: `OnInputInteractPressed`
- **Description:** Handles press input for interaction.
- **Flow:**
  1. If DetectionState < TargetConfirmed: return
  2. If CurrentTarget.InputMode == Press:
     - PerformInteraction(Press)
  3. If CurrentTarget.InputMode == Hold:
     - Start hold timer
  4. If CurrentTarget.InputMode == DoubleTap:
     - Wait for second press within threshold

### Custom Event: `OnHoldProgressTick (Timer)` → `void`
- **Description:** Ticks hold interaction progress and fires progress dispatcher.
- **Flow:**
  1. HoldInteractionProgress += DeltaTime / CurrentTarget.HoldDuration
  2. Clamp to 1.0
  3. Fire OnHoldProgressUpdated
  4. If HoldInteractionProgress >= 1.0: PerformInteraction(Hold)

### Custom Event: `OnInputInteractReleased`
- **Description:** Cancels hold interaction if button released early.
- **Flow:**
  1. If CurrentTarget.InputMode == Hold and not completed:
     - CancelInteraction()
     - HoldInteractionProgress = 0.0

---

## 7. Blueprint Graph Logic Flow

```mermaid
flowchart TD
    A[Scan Timer Fires] --> B[Get Camera Location / Forward]
    B --> C[SphereTraceForObjects]
    C --> D[Hit actors found?]
    D -->|No| E[Clear targets]
    E --> F[DetectionState = NoTarget]
    F --> G[Fire OnTargetLost]
    
    D -->|Yes| H[Filter by I_Interactable]
    H --> I[Sort by Priority then Distance]
    I --> J[Select BestTarget]
    J --> K{BestTarget changed?}
    K -->|Yes| L[Unhighlight old target]
    L --> M[Highlight new target]
    M --> N[Fire OnTargetFound]
    K -->|No| O[Keep current target]
    
    P[OnInputInteractPressed] --> Q{DetectionState >= Confirmed?}
    Q -->|No| R[Return]
    Q -->|Yes| S{InputMode match?}
    S -->|Press| T[PerformInteraction]
    S -->|Hold| U[Start hold timer]
    S -->|DoubleTap| V[Start double-tap timer]
    T --> W[Call ExecuteInteraction on target]
    W --> X[Fire OnInteractionStarted]
    X --> Y{Interaction succeeded?}
    Y -->|Yes| Z[Fire OnInteractionCompleted]
    Y -->|No| AA[Fire OnInteractionError]
```

---

## 8. Communication Matrix

| Who Talks | How | What Is Sent |
|-----------|-----|-------------|
| `BPC_InteractionDetector` | `Dispatcher` | `OnTargetFound` -> `WBP_InteractionPrompt` (show prompt) |
| `BPC_InteractionDetector` | `Dispatcher` | `OnTargetLost` -> `WBP_InteractionPrompt` (hide prompt) |
| `BPC_InteractionDetector` | `Dispatcher` | `OnInteractionStarted` -> `BPC_PlayerMetricsTracker` (log event) |
| `BPC_InteractionDetector` | `Dispatcher` | `OnInteractionCompleted` -> `BPC_PlayerMetricsTracker` (log result) |
| `BPC_InteractionDetector` | `Dispatcher` | `OnHoldProgressUpdated` -> `WBP_InteractionPrompt` (progress bar) |
| `BPC_InteractionDetector` | `Dispatcher` | `OnInteractionCancelled` -> `WBP_InteractionPrompt` (hide) |
| `BPC_InteractionDetector` | `Listener` | Binds to `BPC_MovementStateSystem.OnPostureChanged` |
| `BPC_InteractionDetector` | `Listener` | Binds to `BPC_HealthSystem.OnDeathStateChanged` |
| `BPC_InteractionDetector` | `Direct` | Calls `I_Interactable.ExecuteInteraction` on target |
| `BPC_InteractionDetector` | `Direct` | Calls `I_Interactable.CancelInteraction` on target |
| `BPC_InteractionDetector` | `Direct` | Calls `I_Interactable.SetHighlighted` on target |
| `BPC_InteractionDetector` | `Direct call (other)` | `GetBestTarget` called by `WBP_InteractionPrompt` for name |
| `PC_PlayerController` | `Input Event` | Binds press / release / hold to `OnInputInteractPressed` etc. |

---

## 9. Validation / Testing Checklist

- [ ] Trace detects actors implementing I_Interactable at correct range
- [ ] Priority sorting: Emergency targets always selected over Low
- [ ] Same priority: closest target selected
- [ ] Detection respects blocking states (dead, hiding)
- [ ] Hold interaction progress updates and fires progress dispatcher
- [ ] Double-tap interaction waits for second press within window
- [ ] Cancelling hold interaction resets progress and fires cancelled
- [ ] Interaction blocked during combat if bBlockDuringCombat is true
- [ ] Targeting auto-switches if closer target enters range
- [ ] Target exit detection fires when actor moves beyond max range
- [ ] Edge case: No targets in range — DetectionState stays NoTarget
- [ ] Edge case: Target destroyed mid-interaction — cancels gracefully
- [ ] Edge case: Multiple interactables in trace — selects correctly
- [ ] Debug trace visualisation draws correct sphere/line
- [ ] Scan timer pauses while interacting, resumes after completion

---

## 10. Reuse Notes

- TraceChannel should be set to a custom object channel (ECC_GameTraceChannel1) with only interactable actors responding.
- Priority values can be overridden per actor via the `I_Interactable` interface's `GetInteractionPriority` function.
- The hold interaction progress is designed to drive a radial progress widget in the HUD.
- For controller/console: `E_InteractionInputMode.Hold` is preferred to prevent accidental interactions.
- The same component can be attached to NPCs if they need interaction detection.
- To support networked games: replicate CurrentTarget and have each client run their own trace locally.

---

## 11. Multiplayer Networking (Expanded)

### Replication
| Variable | Condition | Purpose |
|----------|-----------|---------|
| `CurrentTarget` | `Replicated` | Synced target for UI (other players see what you're looking at) |

### Server RPCs
| RPC | Direction | Description |
|-----|-----------|-------------|
| `Server_PerformInteraction` | Client→Server | Client requests interaction. Server validates range, conditions, then calls I_Interactable. |

### Authority
- **Detection** (trace, sorting, target selection): runs locally per client.
- **Interaction execution**: server-authoritative. Client calls `Server_PerformInteraction`; server validates and executes.
- `ForceSetTarget`: server-only for scripted interactions.
- `HighlightTarget`: local cosmetic effect per client.

### Client Prediction
- Interaction prompt: shown immediately based on local trace.
- Hold progress: shown locally; server confirms completion.
- Blocked feedback: shown locally if conditions fail client-side; server may also reject.

---

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