Loot Chests

Documentation Unreal Engine Player Combat Inventory Multiplayer

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.


Set it up

5 minutes
  1. 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
    UPCWorldInventory
    UPC
    Inventory
    Config
    InventoryConfig_UPC_LootChest
    Starting Items
    5 Array elements
    Index [ 0 ]
    Item_UPC_Potion
    Index [ 1 ]
    Item_UPC_Potion
    Index [ 2 ]
    Item_UPC_Potion
    Index [ 3 ]
    Item_UPC_Ring
    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. 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).
  3. 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:
    BP_MyChest · Take All
    On Option Interacted (UPCInteractable)
    Instigator
    Option
    Break UPC Interaction Option
    UPC Interaction Option
    Option Id
    ==
    Branch
    Condition
    True
    False
    Get Component by Class
    Target
    Component ClassUPC Inventory
    Return Value
    UPCWorldInventory
    Request Take All
    Target is UPC Inventory
    Target
    World Inventory
  4. 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 set up first). Everything that fits moves into your inventory.

Lock it, or set its reach

SettingDoes
Access RadiusDistance 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 TagA 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 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:
NodeDoes
Request Take AllTakes everything that fits; the rest stays in the chest.
Can TransferWhat moving Count units between the two would do (Place, Merge or Refused with a reason), for coloring a drop target.
Request TransferMoves 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).

Debug it

Log LogUPCInventory Verbose logs each request the server turned down.
AdvancedHow items move
  • 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.
The demo
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.
MultiplayerWhat runs where
  • 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.
C++C++
  • 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.
AdvancedTroubleshooting
  • 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.