# `BPC_InventorySystem` — Core Inventory System

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

> **⚡ C++ Status: Full Implementation** — `Source/PG_Framework/Public/Inventory/BPC_InventorySystem.h` provides the complete inventory grid: add/remove/query/sort/consolidate/weight tracking with native-speed TArray operations. **Attach directly to player pawn** (Add Component → `BPC_InventorySystem`). Set `GridWidth`, `GridHeight`, `MaxWeight` in Details panel. Do NOT create a BP child — the C++ component has all logic. See `docs/developer/cpp-integration-guide.md` for usage patterns.
>
> ---## 1. Overview

`BPC_InventorySystem` is the central inventory authority on the player. It manages an array of item instances (stackable, tagged, data-driven), handles add/remove/use/transfer operations, enforces capacity limits, and communicates state changes via Event Dispatchers. All other inventory subsystems (weight, quick slots, UI, equipment) read from this component as their single source of truth.

Items are stored as `S_InventoryEntry` structs referencing a `DA_ItemData` primary data asset plus runtime state (stack count, durability, custom metadata). Operations are validated by category, stack limits, and capacity before execution.

---

## 2. Mermaid — Inventory Architecture

```mermaid
flowchart TD
    A[External Request] --> B{BPC_InventorySystem}
    B --> C{Operation Type}
    C -->|AddItem| D[Validate: Has Space?]
    C -->|RemoveItem| E[Validate: Has Item?]
    C -->|UseItem| F[Validate: Usable?]
    C -->|TransferItem| G[Validate: Target Has Space?]
    C -->|DropItem| H[Spawn BP_ItemPickup in World]

    D -->|Pass| I[Add to Array / Stack]
    D -->|Fail| J[Return Failure Reason]
    E -->|Pass| K[Remove from Array / Unstack]
    E -->|Fail| L[Return Failure Reason]
    F -->|Pass| M[Dispatch OnItemUsed]
    F -->|Fail| N[Return Failure Reason]

    I --> O[Broadcast OnInventoryChanged]
    K --> O
    M --> O
    G --> P[Remove from Source / Add to Target]
    P --> O
```

---

## 3. Enums

### `E_InventoryOperationResult`

| Value | Description |
|-------|-------------|
| `Success` | Operation completed successfully. |
| `InventoryFull` | No free slots available. |
| `ItemNotFound` | Specified item not in inventory. |
| `StackLimitReached` | Item exists but stack is at maximum. |
| `InsufficientQuantity` | Not enough of the item to remove. |
| `ItemNotUsable` | Item cannot be used from inventory. |
| `CategoryRestricted` | Item category not allowed in this inventory. |
| `WeightCapacityExceeded` | Adding would exceed max weight. |

### `E_ItemCategory`

| Value | Description |
|-------|-------------|
| `Consumable` | Health items, food, drink, batteries. |
| `KeyItem` | Story keys, access cards, puzzle tokens. |
| `Weapon` | Firearms, melee weapons, tools. |
| `Ammo` | Bullets, shells, energy cells. |
| `Resource` | Crafting materials, scrap, components. |
| `Document` | Notes, letters, audio logs, data files. |
| `Equipment` | Wearable gear (armor, backpack, goggles). |
| `QuestItem` | Mission-specific objects. |

### `E_SortMode`

| Value | Description |
|-------|-------------|
| `ByName` | Alphabetical by item name. |
| `ByCategory` | Grouped by item category. |
| `ByRarity` | Sorted by item rarity value. |
| `ByWeight` | Sorted by weight ascending. |
| `ByQuantity` | Sorted by stack count descending. |
| `ByRecent` | Most recently acquired first. |

---

## 4. Structs

### `S_InventoryEntry`

| Field | Type | Description |
|-------|------|-------------|
| `ItemData` | `DA_ItemData*` | Reference to the item's data asset. |
| `ItemID` | `FGuid` | Unique runtime ID for this item instance. |
| `StackCount` | `int32` | Current number of this item in the stack. |
| `MaxStackSize` | `int32` | Max per slot (from DA, overrideable). |
| `Durability` | `float` | Current durability (0–1). |
| `CustomTags` | `FGameplayTagContainer` | Runtime-added tags for quest tracking. |
| `CustomData` | `TMap<FName, FString>` | Generic key-value store for per-instance data. |
| `SlotIndex` | `int32` | The slot this entry occupies. |
| `bIsEquipped` | `bool` | Whether this item is currently equipped. |

### `S_InventorySlot`

| Field | Type | Description |
|-------|------|-------------|
| `SlotIndex` | `int32` | Unique slot number within the inventory. |
| `Entry` | `S_InventoryEntry` | The item occupying this slot (empty = null ItemData). |
| `bIsLocked` | `bool` | If true, this slot cannot be modified. |
| `bIsQuickSlot` | `bool` | If true, this slot is mapped to a hotkey. |
| `CategoryRestriction` | `E_ItemCategory` | Only items of this category can occupy this slot. |

### `S_InventorySnapshot`

| Field | Type | Description |
|-------|------|-------------|
| `Slots` | `TArray<S_InventorySlot>` | Copy of all inventory slots at snapshot time. |
| `TotalWeight` | `float` | Weight at snapshot time. |
| `TotalItems` | `int32` | Count of non-empty slots. |
| `SnapshotTimestamp` | `float` | Game time when snapshot was taken. |

### `S_ItemQuery`

| Field | Type | Description |
|-------|------|-------------|
| `CategoryFilter` | `E_ItemCategory` | Only return items of this category. |
| `TagFilter` | `FGameplayTag` | Only return items with this tag. |
| `NameFilter` | `FString` | Partial name match (case-insensitive). |
| `bIncludeEquipped` | `bool` | If false, exclude equipped items. |
| `bSortResult` | `bool` | If true, sort results by name. |

---

## 5. Variables

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

| Variable | Type | Description |
|----------|------|-------------|
| `MaxSlots` | `int32` | Total number of inventory slots (default 24). |
| `MaxWeightCapacity` | `float` | Maximum carry weight in kg. |
| `bUseWeightSystem` | `bool` | If true, weight limits are enforced. |
| `AllowedCategories` | `TArray<E_ItemCategory>` | Categories permitted in this inventory. Empty = all. |
| `StartingItems` | `TArray<FStartingItemEntry>` | Items to add at BeginPlay. |
| `bReplicates` | `bool` | Whether inventory state is replicated. |

### `FStartingItemEntry`

| Field | Type | Description |
|-------|------|-------------|
| `ItemData` | `DA_ItemData*` | Item data asset to add. |
| `StackCount` | `int32` | Quantity to add. |
| `AutoEquip` | `bool` | If true, equip immediately if possible. |

### State (Blueprint Read Only)

| Variable | Type | Description |
|----------|------|-------------|
| `Slots` | `TArray<S_InventorySlot>` | The actual inventory array. Replicated. |
| `TotalWeight` | `float` | Current total weight of all items. |
| `TotalItems` | `int32` | Current count of non-empty slots. |
| `bIsOverEncumbered` | `bool` | True when TotalWeight exceeds MaxWeightCapacity. |

### Internal (Not Replicated)

| Variable | Type | Description |
|----------|------|-------------|
| `OwningPawn` | `APawn*` | Cached owning pawn reference. |
| `OnItemCooldowns` | `TMap<FGuid, float>` | Map of ItemID → remaining cooldown seconds. |

---

## 6. Functions & Events

### Public Functions — Core Operations

| Function | Description |
|----------|-------------|
| `AddItem` | Adds an item instance. Returns `E_InventoryOperationResult` and the slot index. Handles stacking if item already exists and stack not full. |
| `RemoveItem` | Removes `Quantity` of an item by `ItemID` or `SlotIndex`. Returns result and how many were actually removed. |
| `RemoveItemByTag` | Removes first matching item by `FGameplayTag`. Returns result. |
| `UseItem` | Uses/consumes item at slot. Returns result. Dispatches `OnItemUsed`. If consumable with limited uses, reduces stack. |
| `DropItem` | Drops `Quantity` of item at slot. Creates `BP_ItemPickup` in world. Returns result and spawned actor. |
| `TransferItem` | Moves item from this inventory to a target `BPC_InventorySystem`. Validates target capacity. |
| `MoveItemToSlot` | Moves item from one slot to another (rearrange). Handles swapping if target slot is occupied. |
| `SplitStack` | Splits a stack into two slots. Returns new slot index. |
| `CombineStack` | Combines two stacks of the same item into one (if max allows). |

### Public Functions — Query

| Function | Description |
|----------|-------------|
| `HasItem` | Returns true if inventory contains an item with the given `ItemID` or matching `FGameplayTag`. |
| `HasItemQuantity` | Returns total count of items matching a tag or data asset. |
| `FindItemByTag` | Returns first `S_InventoryEntry` matching a gameplay tag. |
| `FindAllItemsByCategory` | Returns `TArray<S_InventoryEntry>` filtered by category. |
| `FindAllItemsByTag` | Returns `TArray<S_InventoryEntry>` filtered by gameplay tag. |
| `GetItemAtSlot` | Returns `S_InventoryEntry` at the given slot index (or empty if slot is free). |
| `GetSlotCount` | Returns total number of slots (empty + filled). |
| `GetFreeSlotCount` | Returns number of empty slots. |
| `IsInventoryFull` | Returns true if no free slots. |
| `SortInventory` | Reorders slots by the given `E_SortMode`. |
| `GetTotalWeight` | Returns current `TotalWeight`. |
| `IsOverEncumbered` | Returns `bIsOverEncumbered`. |
| `GetItemCooldownRemaining` | Returns seconds remaining for a given ItemID cooldown. |

### Protected Functions

| Function | Description |
|----------|-------------|
| `BeginPlay` | Caches owner, adds starting items, initialises empty slots array. |
| `FindFreeSlot` | Returns the index of the first empty slot. Returns -1 if full. |
| `FindExistingStack` | Finds slot index with same ItemData that is below MaxStackSize. |
| `RecalculateWeight` | Iterates all slots and sums item weight. Updates `TotalWeight` and `bIsOverEncumbered`. |
| `ValidateAddItem` | Pre-checks slot availability, weight capacity, category restrictions. Returns result. |
| `ValidateRemoveItem` | Pre-checks item exists and has sufficient quantity. |
| `SetSlot` | Internal: writes an entry to a specific slot index. |
| `ClearSlot` | Internal: empties a slot. |
| `OnRep_Slots` | RepNotify: recalculates weight and broadcasts `OnInventoryChanged`. |

### Event Dispatchers

| Dispatcher | Payload | Description |
|------------|---------|-------------|
| `OnInventoryChanged` | — | Fired on any add/remove/transfer/sort operation. |
| `OnItemAdded` | `S_InventoryEntry Item`, `int32 SlotIndex` | Fired after an item is successfully added. |
| `OnItemRemoved` | `S_InventoryEntry Item`, `int32 SlotIndex`, `int32 Quantity` | Fired after an item is removed. |
| `OnItemUsed` | `S_InventoryEntry Item`, `AActor* Instigator` | Fired when UseItem successfully executes. |
| `OnItemDropped` | `S_InventoryEntry Item`, `BP_ItemPickup* DroppedActor` | Fired when an item is dropped into the world. |
| `OnItemTransferred` | `S_InventoryEntry Item`, `BPC_InventorySystem* TargetInventory` | Fired after transfer to another inventory. |
| `OnStackSplit` | `S_InventoryEntry Original`, `S_InventoryEntry NewStack` | Fired when a stack is split into two. |
| `OnStackCombined` | `S_InventoryEntry CombinedStack` | Fired when two stacks are merged. |
| `OnOverEncumberedStateChanged` | `bool bIsOverEncumbered` | Fired when weight crosses the threshold. |
| `OnItemCooldownStarted` | `FGuid ItemID`, `float Duration` | Fired when an item cooldown begins. |
| `OnInventoryFull` | — | Fired when all slots are filled. |

---

## 7. Blueprint Graph Flow

```
Event BeginPlay
    → Set MaxSlots (from config)
    → Create empty Slots array (Size = MaxSlots)
    → For each StartingItem:
        → Call AddItem (StartingItem.ItemData, StartingItem.StackCount)
        → If StartingItem.AutoEquip → Call EquipItem on slot (via EquipmentSlotSystem)

AddItem (ItemData, Quantity)
    → Call ValidateAddItem → If Fail → return result
    → FindExistingStack (same ItemData, not full)
    → Branch: Found?
        → Yes → Increment stack by Quantity (clamped to MaxStackSize)
            → If Quantity > remaining → FindFreeSlot for overflow
        → No → FindFreeSlot
            → If no free slot → return InventoryFull
            → Create new S_InventoryEntry → Assign SlotIndex
    → RecalculateWeight
    → If bReplicates → Mark slots array dirty
    → Broadcast OnItemAdded, OnInventoryChanged
    → Return Success

RemoveItem (SlotIndex, Quantity)
    → Call ValidateRemoveItem → If Fail → return result
    → Decrement stack count by Quantity
    → If stack count <= 0 → ClearSlot
    → RecalculateWeight
    → Broadcast OnItemRemoved, OnInventoryChanged
    → Return Success

UseItem (SlotIndex)
    → GetItemAtSlot → If empty → return ItemNotFound
    → Check cooldown → If on cooldown → return failure
    → Check bIsUsable from DA_ItemData → If not → return ItemNotUsable
    → If item has cooldown → Add to OnItemCooldowns map
    → Broadcast OnItemUsed
    → If item is consumable (bConsumeOnUse from DA):
        → Reduce stack by 1
        → If stack = 0 → ClearSlot
    → Broadcast OnInventoryChanged
    → Return Success

DropItem (SlotIndex, Quantity, SpawnLocation, SpawnRotation)
    → RemoveItem (SlotIndex, Quantity) → If fail → return
    → SpawnActor BP_ItemPickup at location/rotation
    → Set pickup's ItemData and StackCount
    → Broadcast OnItemDropped
    → Return Success + spawned actor

TransferItem (SlotIndex, TargetInventory, Quantity)
    → Validate item exists and quantity sufficient
    → Call TargetInventory.ValidateAddItem → If fail → return
    → RemoveItem (SlotIndex, Quantity)
    → TargetInventory.AddItem (ItemData, Quantity)
    → Broadcast OnItemTransferred
    → Return Success

SortInventory (SortMode)
    → Build TMap of slot index → entry for non-empty slots
    → Sort entries by SortMode rules
    → Clear all slots
    → Write sorted entries back into sequential slots
    → Broadcast OnInventoryChanged

RecalculateWeight
    → Sum = 0
    → For each slot with non-null ItemData:
        → Sum += ItemData.Weight * StackCount
    → TotalWeight = Sum
    → OldEncumbered = bIsOverEncumbered
    → bIsOverEncumbered = Sum > MaxWeightCapacity
    → If OldEncumbered != bIsOverEncumbered
        → Broadcast OnOverEncumberedStateChanged
```

---

## 8. Replication

| Variable | Replication | Callback |
|----------|-------------|----------|
| `Slots` | `RepNotify` | `OnRep_Slots` — refreshes UI, recalculates weight |
| `TotalWeight` | `Replicated` | — |
| `bIsOverEncumbered` | `Replicated` | — |
| `TotalItems` | `Replicated` | — |

**Authority:** Server is authoritative for all add/remove/use operations. Clients predict UI updates but server validates and corrects.

**Cooldowns:** Managed locally (not replicated) but validated on server for authoritative actions.

---

## 9. Dependencies & Communication

| System | Relationship |
|--------|--------------|
| `DA_ItemData` | Each slot references a Data Asset for item properties. |
| `BPC_ActiveItemSystem` | Listens to `OnInventoryChanged` and `OnOverEncumberedStateChanged`. |
| `WBP_InventoryMenu` | Subscribes to all event dispatchers to update UI. |
| `BPC_EquipmentSlotSystem` | Reads `bIsEquipped` flag; equips/unequips items via slot. |
| `BP_ItemPickup` | Receives dropped items; sends pickup requests to `AddItem`. |
| `BPC_InteractionDetector` | Routes pickup interaction to `AddItem`. |
| `BPC_HealthSystem` | Consumed health items via `UseItem` trigger healing. |
| `BPC_StaminaSystem` | Consumed stamina items via `UseItem` trigger stamina restore. |
| `Save/Load System` | Serializes `Slots` array for save/restore. |
| `BPC_PlayerMetricsTracker` | Logs item acquisition events. |
| `BPC_DifficultyManager` | May adjust `MaxSlots` or `MaxWeightCapacity` based on difficulty. |

---

## 10. Success Criteria

1. Items can be added to inventory; duplicates stack up to `MaxStackSize`.
2. Adding beyond slot capacity returns `InventoryFull`.
3. Adding beyond weight capacity returns `WeightCapacityExceeded`.
4. Items can be removed by slot index or gameplay tag; stacks decrement correctly.
5. Using a consumable item reduces stack; item is removed at 0.
6. Dropping an item spawns `BP_ItemPickup` in the world with correct data.
7. Items can be transferred between inventories (player ↔ container).
8. Inventory can be sorted by all sort modes.
9. Stack splitting creates a new slot; combining merges stacks.
10. Event dispatchers fire correctly for all operations.
11. Multiplayer: inventory state is server-authoritative and syncs to clients.
12. Save/load restores exact inventory state including custom data.

---

## 11. Data Flow Summary

```
Player picks up item
    → BPC_InteractionDetector detects BP_ItemPickup
    → Player presses Interact
    → BP_ItemPickup.ExecuteInteraction → BPC_InventorySystem.AddItem
    → Validate (space, weight, category)
    → Add to array / increment stack
    → RecalculateWeight
    → Broadcast OnItemAdded / OnInventoryChanged
    → UI updates / Weight system reacts / Metrics logs

Player uses health item from inventory
    → UI calls BPC_InventorySystem.UseItem (SlotIndex)
    → Validate (exists, usable, cooldown)
    → Broadcast OnItemUsed
    → BPC_HealthSystem.OnItemUsed → ApplyHealing
    → If consumable → reduce stack
    → Broadcast OnInventoryChanged
```

## 12. Reuse Notes

- Renamed from `BPC_InventoryComponent` to `BPC_InventorySystem` per Master naming convention.
- Cross-references updated: `BPC_InventoryWeightSystem` → `BPC_ActiveItemSystem`, `BPC_InventoryQuickSlot` → `BPC_ActiveItemSystem`, `BPC_InventoryUIManager` → `WBP_InventoryMenu`, `BPC_EquipmentSystem` → `BPC_EquipmentSlotSystem`.