# Loot Chests

> Chests, stashes and enemy loot keep their items in a UPC World Inventory that every nearby player sees. One press takes everything that fits, or opens the inventory screen with the chest beside it.

Markdown version of https://rigbak.com/docs/universal-player-combat/loot-chests, a page of the Universal Player Combat documentation. Link that page when citing it.

## Set it up (5 minutes)

1. **Make the chest**

   Create Actor Blueprint `BP_MyChest` with **Replicates** ticked in Class Defaults. Add a collidable **Static Mesh** and a **UPC World Inventory** with **Config** `InventoryConfig_UPC_LootChest` and your **Starting Items**.

   **Details panel: UPCWorldInventory** (component, in BP_MyChest)

   **Category: UPC > Inventory**

   | Property | Value |
   | --- | --- |
   | Config | InventoryConfig_UPC_LootChest |
   | Starting Items | 5 Array elements |
   | Starting Items > Index [0] | Item_UPC_Potion |
   | Starting Items > Index [1] | Item_UPC_Potion |
   | Starting Items > Index [2] | Item_UPC_Potion |
   | Starting Items > Index [3] | Item_UPC_Ring |
   | Starting Items > Index [4] | Item_UPC_Necklace |
   | Access Radius | 300 |
   | Access Tag | None |

   *Your chest, filled as the demo chest is: three potions, a ring and a necklace in thirty slots.*
2. **Give it a Take All option**

   Add **UPC Interactable** with one **Options** element: **Option Id** `TakeAll`, **Activation Input Tag** `UPC.Input.Interact`, **Prompt** **Verb** `Take All`, **Name** `Chest`. Leave **Payload** (what the character plays first, such as a montage or delay) empty so the option fires at once ([Interaction](https://rigbak.com/docs/universal-player-combat/interaction)).
3. **Take everything on the option**

   Select **UPCInteractable** and press **+** beside **On Option Interacted**. It fires for each option the chest offers, so check the **Option Id**, then hand the chest to the interacting character's inventory:

   Blueprint graph, nodes in reading order:

   - **Comment box: BP_MyChest · Take All**

     - **On Option Interacted (UPCInteractable)** (event); outputs: Instigator, Option
     - **Break UPC Interaction Option** (break struct); inputs: UPC Interaction Option; outputs: Option Id
     - **==** (operator); inputs: A, B = TakeAll; outputs: Return Value
     - **Branch** (branch); inputs: Condition; outputs: True, False
     - **Get Component by Class** (pure function); inputs: Target, Component Class = UPC Inventory; outputs: Return Value
     - **UPCWorldInventory** (variable)
     - **Request Take All** (function; Target is UPC Inventory); inputs: Target, World Inventory
4. **Press Play and press E at the chest**

   Place `BP_MyChest` in `LVL_UPC_Demo`, press Play, and press E at it (a character of your own needs [Interaction](https://rigbak.com/docs/universal-player-combat/interaction) set up first). Everything that fits moves into your inventory.

---

## Lock it, or set its reach

| Setting | Does |
| --- | --- |
| **Access Radius** | Distance from the actor's collision within which a character may take or put items, in cm (default 300; 0 is any distance). Keep it at least the interaction reach, 225 cm by default. |
| **Access Tag** | A tag the character's ability system must hold, such as a key for a locked chest; your own code adds it. Empty lets any character in reach use it. |

Lock a chest with the World Inventory's Access Tag, and leave the **Access Tag** on a container in the chest's config empty: the chest itself would have to hold it, and no character could reach the container.

**Is Reachable By** answers both for one character, with reason `UPC.Inventory.Refused.OutOfReach` or `NoAccess`; a chest screen asks it to close as the player walks away.

For an enemy's loot, spawn a loot actor where it fell. Keep a World Inventory off characters: code that looks up a character's inventory takes the first one it finds.

---

## Open it beside the inventory screen

1. Add a second option: **Option Id** `OpenInventory`, **Activation Input Tag** `UPC.Input.Interact`, **Verb** `Open`, and a **Delay** payload of 0 seconds. Unlike Take All, this option needs a payload: the screen opens only for an option that has one.
2. Move Take All to `UPC.Input.Interact.Secondary`, the F key.

`BPC_UPC_InventoryScreens`, on the demo's `BP_UPC_PlayerController`, opens the player's screen with the chest's items beside it when the payload starts. **On Option Interacted** then fires for Open too, where the Option Id check from the quick start keeps it from taking everything.

To skip these steps, duplicate the demo's `BP_UPC_LootChest`, which has both options, and change its **Starting Items**. [Inventory Screens](https://rigbak.com/docs/universal-player-combat/inventory-screens) shows what each style's screen offers at a chest.

---

## Move items from a screen of your own

Call these on the player's inventory with the chest's World Inventory as a pin:

| Node | Does |
| --- | --- |
| **Request Take All** | Takes everything that fits; the rest stays in the chest. |
| **Can Transfer** | What moving **Count** units between the two would do (**Place**, **Merge** or **Refused** with a reason), for coloring a drop target. |
| **Request Transfer** | Moves those units as Can Transfer answers. |

No player's connection owns the chest, so players cannot rearrange its slots; only server code changes its layout. Read it like any inventory with **Get Items in Container** and **On Layout Changed** ([Inventory Screens](https://rigbak.com/docs/universal-player-combat/inventory-screens#read-the-inventory)).

---

## Debug it

`Log LogUPCInventory Verbose` logs each request the server turned down.

<details>
<summary>How items move (Advanced)</summary>

- **Request Take All** goes through the chest in list order. Each stack lands as an add would, as a pickup does: the survival grid puts a key in Key Items and the rest in the case, and leaves what the case has no room for. With no Config, everything lands in the one list.
- A stack carrying stat tags moves whole or stays.
- The 225 cm reach is the **UPC Interactor**'s **Detection Radius** (200) plus the **UPC Interact Ability**'s **Server Range Tolerance** (25).
- **Can Transfer** takes an item in either inventory and a **Destination** in the other. **Place** splits part of a stack off; **Merge** moves as many units as the target stack has room for. Inventories never trade places, so an occupied place answers Occupied.
- The item that arrives is a new item of the same kind, with the same count and stat tags; a bag moves with what it holds. A whole stack on the belt or in hand stays with the player.

</details>

<details>
<summary>The demo</summary>

`BP_UPC_LootChest` (`Demo/Interactables/LootChest`, in the `LVL_UPC_Demo` hallway) is a replicating actor whose World Inventory uses `InventoryConfig_UPC_LootChest` (thirty slots under `UPC.Inventory.Container.LootChest`) and starts with three potions, a ring and a necklace. **Open**, on E, shows the chest beside the player's screen: all thirty slots, scrolling when they do not fit (`WBP_UPC_ChestPanel` on the survival grid and party RPG screens, a mirrored panel on the open world list). A right click on your own item stores it in the open chest, letting go of it first when the belt or a hand holds it; a weapon stays in hand when the chest is full and the message line reads Full. **Take All**, on F, calls **Request Take All**.

</details>

<details>
<summary>What runs where (Multiplayer)</summary>

- The items reach every machine the actor reaches, so the actor must replicate. Everyone near a chest receives its entries, and a dormant chest wakes for each change.
- A Development build can log an ensure naming `SwapReferencesForDormancy` once, when a change wakes the chest as it goes dormant and a lost packet delays that moment for one player. Nothing is lost; set the chest's **Net Dormancy** to Awake to silence it.
- The server checks every request: reach, Access Tag, that the units are still there, and that the receiving inventory takes them. A request it turns down changes nothing and sends no reply.
- Of two players reaching for the last potion, the first request gets it, and the second finds it gone; no listener sees the units in both places.

</details>

<details>
<summary>C++</summary>

- `UUPCWorldInventoryComponent` is a `UUPCInventoryComponent` whose `IsWorldInventory()` answers true, so its list and items replicate to every machine rather than to an owning player.
- `RequestTransfer`, `RequestTakeAll` and `CanTransfer` live on `UUPCInventoryComponent` and take the world inventory as a typed parameter; one called on a world inventory itself is refused. `IsReachableBy(Character, OutReason)` measures with the distance the interaction confirms with.

</details>

<details>
<summary>Troubleshooting (Advanced)</summary>

- **The chest shows its items on the server only.** Tick **Replicates** in its Class Defaults; the Output Log warns once.
- **Take All or a transfer moves nothing.** The character stands beyond **Access Radius**, lacks the **Access Tag** or has no room, or the call went to the World Inventory in place of the player's own.
- **A bag in the chest shows none of its items.** A world inventory opens no bags; the bag keeps its items until a player carries it where bags open. The Output Log warns once about a chest container with **Opens Bags** ticked.

</details>

---

Previous page: [Combines](https://rigbak.com/docs/universal-player-combat/inventory-combines). Next page: [Inventory Screens](https://rigbak.com/docs/universal-player-combat/inventory-screens)
