# BP_PuzzleDeviceActor — Puzzle Device Actor

**Parent Class:** `Actor`
**Category:** Interaction
**Target UE Version:** 5.5–5.7
**Build Phase:** 2 — Interaction

---

## 1. Overview

`BP_PuzzleDeviceActor` is the base actor for all puzzle devices in the world — levers, pressure plates, valve wheels, keypads, combination locks, circuit boards, and custom logic puzzles. Each instance references a [`DA_PuzzleData`](../14-data-assets/DA_PuzzleData.md) that defines the puzzle rules, solution, and completion effects.

---

## 2. Enums

### `E_PuzzleType`

| Value | Description |
|-------|-------------|
| `Lever` | Single or multi-lever sequence |
| `PressurePlate` | Weight-activated plate |
| `ValveWheel` | Rotate-to-open valve |
| `Keypad` | Numeric code entry |
| `CombinationLock` | Multi-dial combination |
| `CircuitBoard` | Connect-the-pins puzzle |
| `Custom` | Script-driven puzzle |

### `E_PuzzleState`

| Value | Description |
|-------|-------------|
| `Unsolved` | Puzzle not yet completed |
| `InProgress` | Player interacting with puzzle |
| `Solved` | Puzzle completed successfully |
| `Failed` | Puzzle failed (if retryable) |
| `Locked` | Puzzle locked (requires item/flag) |

---

## 3. Variables

| Variable | Type | Description |
|----------|------|-------------|
| `PuzzleData` | `DA_PuzzleData*` | Data asset defining this puzzle |
| `CurrentState` | `E_PuzzleState` | Current puzzle state |
| `bIsInteractable` | `bool` | Can player interact with this puzzle |
| `InteractionPrompt` | `FText` | Prompt text for interaction |
| `LinkedActor` | `AActor*` | Actor to notify on solve (door, gate, etc.) |
| `bResetOnFail` | `bool` | Auto-reset after failure |
| `MaxAttempts` | `int32` | Max attempts before lockout (-1 = unlimited) |
| `CurrentAttempts` | `int32` | Attempts used so far |
| `CooldownSeconds` | `float` | Time before re-interaction allowed |

---

## 4. Functions

| Function | Description |
|----------|-------------|
| `BeginInteraction` | Player starts interacting with puzzle |
| `EndInteraction` | Player exits puzzle UI |
| `CheckSolution` | Validates player input against puzzle data |
| `OnPuzzleSolved` | Processes completion — notifies LinkedActor |
| `OnPuzzleFailed` | Processes failure — resets or locks |
| `ResetPuzzle` | Resets puzzle to default state |
| `GetPuzzleState` | Returns current `E_PuzzleState` |

---

## 5. Event Dispatchers

| Dispatcher | Payload | Description |
|------------|---------|-------------|
| `OnPuzzleStateChanged` | `E_PuzzleState NewState` | State transition occurred |
| `OnPuzzleSolved` | `AActor* Solver` | Puzzle completed |
| `OnPuzzleFailed` | `int32 AttemptNumber` | Attempt failed |
| `OnPuzzleLinkedActorTriggered` | `AActor* LinkedActor` | Linked actor notified |

---

## 6. Dependencies & Communication

| System | Relationship |
|--------|--------------|
| `DA_PuzzleData` | Defines puzzle rules and solution |
| `BPC_InteractionDetector` | Focus and interaction initiation |
| `BP_DoorActor` | Common linked actor (unlock on solve) |
| `BPC_NarrativeStateSystem` | Set narrative flags on solve |
| `SS_SaveManager` | Persist puzzle state |

---

## 7. Reuse Notes
- All puzzle logic is data-driven via `DA_PuzzleData`
- LinkedActor pattern decouples puzzle from effect

---

## 8. Manual Implementation Guide

### 8.1 Class Setup
1. Create Blueprint Class: Parent = `Actor`, Name = `BP_PuzzleDeviceActor`
2. Add components: StaticMesh (visual), InteractionCollision (Sphere), WidgetComponent (for interaction prompt)
3. Implement Interfaces: `I_Interactable`
4. Save to: `Content/Framework/Interaction/`

### 8.2 Variable Initialization (BeginPlay / Construction Script)
```
Event BeginPlay
  ├─ Set CurrentState = Unsolved (or Locked if requires unlock first)
  ├─ Set CurrentAttempts = 0
  ├─ Load PuzzleData → if valid:
  │    ├─ Set InteractionPrompt = PuzzleData.PromptText
  │    ├─ Set MaxAttempts = PuzzleData.MaxAttempts
  │    ├─ Set CooldownSeconds = PuzzleData.Cooldown
  │    └─ Apply initial visual state (dim if locked, glowing if interactable)
  └─ Register with SS_SaveManager if implements I_Persistable
```

### 8.3 Function Implementations

#### `I_Interactable: OnInteract(Instigator: Actor)` → `Boolean`

```
[Function: OnInteract] — called by BPC_InteractionDetector
  Step 1: Branch on CurrentState:
    Case Unsolved → Call BeginInteraction(Instigator) → Return true
    Case Solved → Play solved animation/sound → Return false (already solved)
    Case Locked → Check if Instigator has required key:
      If yes → unlock → BeginInteraction
      If no → Play locked feedback → Return false
    Case InProgress → Return false (already interacting)
    Case Failed → If bResetOnFail: ResetPuzzle → BeginInteraction
```

#### `BeginInteraction(Instigator: Actor)` → `void`

```
[Function: BeginInteraction]
  Step 1: Set CurrentState = InProgress
  Step 2: Set bIsInteractable = false (block re-interaction during solve)
  Step 3: Fire OnPuzzleStateChanged(InProgress)

  Step 4: Branch on PuzzleData.PuzzleType:
    Lever, PressurePlate, ValveWheel:
      - No UI overlay needed; player manipulates world objects
      - Enable input on the puzzle's interactive levers/buttons
    Keypad, CombinationLock, CircuitBoard:
      - Open puzzle UI widget (attached to WidgetComponent or fullscreen)
      - Set player input mode to UI Only
      - Route input to puzzle widget

  Step 5: Block player movement (optional, per puzzle config)
  Step 6: Set interaction camera view (zoom to puzzle if needed)
```

#### `CheckSolution(PlayerInput: varies by puzzle type)` → `Boolean`

```
[Function: CheckSolution]
  Step 1: Read correct solution from PuzzleData.Solution
  Step 2: Compare player input against solution:
    - Lever sequence: check order of pulled levers matches solution
    - Keypad: check entered code matches solution code
    - CombinationLock: check each dial position
    - CircuitBoard: check all connections match solution
    - PressurePlate: check weight/actor matches
  Step 3: If match:
    Call OnPuzzleSolved(Instigator)
    Return true
  Step 4: If no match:
    Increment CurrentAttempts
    Branch on MaxAttempts != -1 AND CurrentAttempts >= MaxAttempts:
      True → Call OnPuzzleFailed()
      False → Show hint/wrong feedback, allow retry
    Return false
```

**Node example for keypad puzzle:**
```
  Get SolutionString from PuzzleData
  Branch: PlayerInputCode == SolutionString
```

#### `OnPuzzleSolved(Solver: Actor)` → `void`

```
[Function: OnPuzzleSolved]
  Step 1: Set CurrentState = Solved
  Step 2: Set bIsInteractable = false (permanently)
  Step 3: Play solved visual: change material to green/solved glow
  Step 4: Play solved sound via SS_AudioManager
  Step 5: Fire OnPuzzleSolved(Solver)
  Step 6: Fire OnPuzzleStateChanged(Solved)

  Step 7: Notify LinkedActor (if set):
    If LinkedActor valid:
      If LinkedActor implements I_Toggleable → Call Toggle(Solver)
      If LinkedActor implements I_Lockable → Call Unlock(Solver)
      Fire OnPuzzleLinkedActorTriggered(LinkedActor)

  Step 8: Grant rewards from PuzzleData:
    Get BPC_InventorySystem → If PuzzleData.RewardItem valid → Call AddItem
    Get BPC_NarrativeStateSystem → Set flag: PuzzleData.CompletionFlag

  Step 9: EndInteraction()
```

#### `OnPuzzleFailed()` → `void`

```
[Function: OnPuzzleFailed]
  Step 1: Set CurrentState = Failed
  Step 2: Play failure sound/visual
  Step 3: Fire OnPuzzleFailed(CurrentAttempts)
  Step 4: Fire OnPuzzleStateChanged(Failed)

  Step 5: Branch on bResetOnFail:
    True → Start timer for CooldownSeconds → Call ResetPuzzle
    False → If MaxAttempts reached: Set CurrentState = Locked permanently

  Step 6: EndInteraction()
```

#### `EndInteraction()` → `void`

```
[Function: EndInteraction]
  Step 1: Close puzzle UI widget (if open)
  Step 2: Restore player input mode to Game Only
  Step 3: Re-enable player movement
  Step 4: Restore camera to default
  Step 5: If CurrentState != InProgress → Set bIsInteractable = true
```

#### `ResetPuzzle()` → `void`

```
[Function: ResetPuzzle]
  Step 1: Set CurrentState = Unsolved
  Step 2: Set CurrentAttempts = 0
  Step 3: Reset all puzzle levers/dials/inputs to default positions
  Step 4: Set bIsInteractable = true
  Step 5: Fire OnPuzzleStateChanged(Unsolved)
```

### 8.4 Blueprint Build Checklist
- [ ] Create BP_PuzzleDeviceActor actor with mesh + collision + widget components
- [ ] Implement I_Interactable interface
- [ ] Add all variables: PuzzleData, CurrentState, bIsInteractable, LinkedActor, MaxAttempts, etc.
- [ ] Implement BeginInteraction with type-switch (lever vs UI vs custom)
- [ ] Implement CheckSolution with type-specific validation
- [ ] Implement OnPuzzleSolved (visual, sound, LinkedActor, rewards, narrative flag)
- [ ] Implement OnPuzzleFailed (retry or lockout)
- [ ] Implement EndInteraction (UI close, input restore, camera restore)
- [ ] Implement ResetPuzzle for retryable puzzles
- [ ] Bind to BPC_InteractionDetector via I_Interactable
- [ ] Test: approach puzzle → interact → solve → door unlocks → save/load restores state