# BPC_KeyItemSystem — Key Item System

**Parent Class:** `ActorComponent`
**Category:** Inventory
**Target UE Version:** 5.5–5.7
**Build Phase:** 3 — Inventory

## 1. Overview

`BPC_KeyItemSystem` manages key items — story-critical objects that unlock doors, trigger narrative events, or are required for puzzle completion. Tracks acquired keys, provides query for door/puzzle unlock checks, and supports key usage notifications.

## 2. Variables

| Variable | Type | Description |
|----------|------|-------------|
| `AcquiredKeys` | `TArray<FGameplayTag>` | Tags of all acquired key items |
| `bShowKeyNotifications` | `bool` | Toast on key pickup |
| `KeyItemCategory` | `E_ItemCategory` | KeyItem category filter |

## 3. Functions

| Function | Description |
|----------|-------------|
| `HasKey` | Checks if player has a specific key by tag |
| `AddKey` | Registers a key item acquisition |
| `RemoveKey` | Removes a key (if consumed) |
| `GetAllKeys` | Returns all acquired key tags |
| `CanUnlock` | Checks if player meets unlock requirements for a target |

## 4. Event Dispatchers

| Dispatcher | Payload | Description |
|------------|---------|-------------|
| `OnKeyAcquired` | `FGameplayTag KeyTag` | New key picked up |
| `OnKeyUsed` | `FGameplayTag KeyTag`, `AActor* Target` | Key used on target |
| `OnKeyRemoved` | `FGameplayTag KeyTag` | Key consumed/removed |

## 5. Dependencies

| System | Relationship |
|--------|--------------|
| `BPC_InventorySystem` | Keys as inventory items |
| `BP_DoorActor` | Door unlock queries |
| `BP_PuzzleDeviceActor` | Puzzle unlock gates |
| `BPC_NarrativeStateSystem` | Key-based narrative flags |

## 6. Reuse Notes
- Keys use gameplay tags for generic matching (any key with same tag works)
- Supports consumable keys (removed after single use) and persistent keys

---

## 7. Manual Implementation Guide

### 7.1 Class Setup
1. Create Blueprint Class: Parent = `ActorComponent`, Name = `BPC_KeyItemSystem`
2. Add to Player Character
3. Keys are inventory items with `ItemType = KeyItem` and `bIsKeyItem = true` in DA_ItemData.

### 7.2 Variable Initialization (BeginPlay)
```
Event BeginPlay
  ├─ Set AcquiredKeys = empty Array<GameplayTag>
  ├─ Set bShowKeyNotifications = true
  ├─ Get Owner → Find Component by Class (BPC_InventorySystem) → Cache
  │    └─ Bind to OnItemAdded → OnItemAddedHandler
  └─ Bind to OnItemRemoved → OnItemRemovedHandler
```

### 7.3 Function Implementations

#### `HasKey(KeyTag: GameplayTag)` → `Boolean`

```
[Function: HasKey] (Pure)
  Step 1: ForEach AcquiredKeys:
    If AcquiredKeys[i].MatchesTag(KeyTag) → Return true
  Step 2: Optionally check inventory directly:
    Get InventorySystem → Call FindItemByTag(KeyTag)
    If found → Return true
  Step 3: Return false
```

**Nodes:** `ForEachLoop`, `Matches Tag`, `Branch`, `FindItemByTag`

#### `AddKey(KeyTag: GameplayTag)` → `void`

```
[Function: AddKey]
  Step 1: Check if tag already in AcquiredKeys:
    ForEach → if MatchesTag(KeyTag) → Return (no duplicates)
  Step 2: Add KeyTag to AcquiredKeys array
  Step 3: If bShowKeyNotifications:
    Get WBP_HUDController → Create Notification Toast: "Key acquired: {GetTagDisplayName(KeyTag)}"
  Step 4: Fire OnKeyAcquired(KeyTag)
```

#### `RemoveKey(KeyTag: GameplayTag)` → `Boolean`

```
[Function: RemoveKey]
  Step 1: ForEach AcquiredKeys (iterate backwards):
    If AcquiredKeys[i].MatchesTag(KeyTag):
      Remove from array at index i
      Fire OnKeyRemoved(KeyTag)
      Return true
  Step 2: Return false (key not found)
```

#### `GetAllKeys()` → `Array<GameplayTag>`

```
[Function: GetAllKeys] (Pure)
  Step 1: Get BPC_InventorySystem → Call FindAllItemsByCategory(KeyItem)
  Step 2: Also include AcquiredKeys (permanent keys that were consumed)
  Step 3: Return combined unique list
```

#### `CanUnlock(Target: Actor)` → `Boolean, Text (failReason)`

```
[Function: CanUnlock]
  Step 1: Check if Target implements I_Lockable:
    DoesImplementInterface → If not, return (false, "Cannot be unlocked")
  Step 2: Get RequiredKeyTag from Target:
    Call I_Lockable.GetRequiredKeyTag(Target) → RequiredTag
  Step 3: Call HasKey(RequiredTag):
    True → Return (true, "")
    False → Return (false, "Requires: " + GetTagDisplayName(RequiredTag))
```

#### `UseKeyOnTarget(KeyTag: GameplayTag, Target: Actor)` → `Boolean`

```
[Function: UseKeyOnTarget]
  Step 1: Verify HasKey(KeyTag) → If false, return false
  Step 2: Call CanUnlock(Target):
    If false → return false
  Step 3: Check if Target has I_Lockable.TryUnlock:
    Call I_Lockable.TryUnlock(Target, KeyTag) → bSuccess
  Step 4: If bSuccess:
    Fire OnKeyUsed(KeyTag, Target)
    Step 5: Check if key should be consumed:
    Get InventorySystem → FindItemByTag(KeyTag)
    If ItemData.bConsumeOnUse:
      Call RemoveKey(KeyTag)
      Get InventorySystem → RemoveItemByTag(KeyTag, 1)
  Step 6: Return bSuccess
```

### 7.4 Event Dispatcher Bindings
| Bind to Dispatcher | Custom Event | Logic |
|-------------------|-------------|-------|
| `BPC_InventorySystem.OnItemAdded(Item)` | `OnItemAddedHandler` | If Item.ItemData.ItemType == KeyItem → Call AddKey(Item.ItemData.ItemTag) |
| `BPC_InventorySystem.OnItemRemoved(Item)` | `OnItemRemovedHandler` | If Item.ItemData.ItemType == KeyItem → Call RemoveKey(Item.ItemData.ItemTag) |

### 7.5 Blueprint Build Checklist
- [ ] Create BPC_KeyItemSystem, add to Player Character
- [ ] Add variables: AcquiredKeys (Array<GameplayTag>), bShowKeyNotifications, KeyItemCategory
- [ ] Implement HasKey with tag matching and inventory fallback
- [ ] Implement AddKey/RemoveKey with duplicate prevention
- [ ] Implement CanUnlock using I_Lockable interface
- [ ] Implement UseKeyOnTarget with consume-on-use check
- [ ] Bind to BPC_InventorySystem.OnItemAdded/OnItemRemoved
- [ ] Test: pick up key → attempt locked door → unlock succeeds → key consumed