Pickup Actor & Loot Window
ACrimsonPickupActorBase, ACrimsonContainerActorBase, UCrimsonInventoryRoutingComponent) live in CrimsonInventory; the interaction contract (ICrimsonInteractableTarget) lives in CrimsonInteraction; ACrimsonInteractableContainerActor (CrimsonCore) joins them. This page documents the drop-in actors and screens CrimsonCore builds on top of them.Two world actors bring items into the inventory UI. ACrimsonCorePickupActor is a dropped item or loot bag: one item is taken on interact, several open the loot window. ACrimsonCoreContainerActor is a chest or stash: interact opens the two-pane container screen and asks the routing component for a scoped session (shared, per player, per party, per map instance).
What ships
| Type | Base (plugin) | Shipped asset | Role |
|---|---|---|---|
ACrimsonCorePickupActor | ACrimsonPickupActorBase (CrimsonInventory) + ICrimsonInteractableTarget (CrimsonInteraction) + ICrimsonSaveableActor (CrimsonSaveSystem) | /CrimsonCore/Inventory/B_PickupActor_Base, /CrimsonCore/Inventory/Actors/B_LootBag | World pickup with an InteractionSphere, interaction options, secondary actions, and the loot-window push. |
UCrimsonPickupAction + _Examine, _OpenBag, _PickUp, _TakeAll | UObject (EditInlineNew, DefaultToInstanced) | authored inline on the pickup | Extra interaction options (Label, SubText, HandlerAbility, IsAvailable(Pickup)) that show in the interaction context menu. |
UCrimsonCoreLootWindow | UCrimsonCoreInventoryScreenBase | /CrimsonCore/UserInterface/Inventory/Screens/W_Loot_Window | Loot list plus an optional player pane. BindToPickup(SourceActor, Destination) is called by the pickup by name. |
ACrimsonCoreContainerActor | ACrimsonContainerActorBase (CrimsonInventory) | /CrimsonCore/Inventory/Actors/B_Chest_Shared, B_Stash_Personal (subclasses: B_LootChest, B_OwnedStash, B_ItemVendingMachine) | Container with a Mesh and an InteractionSphere; pushes ContainerScreenClass and calls Routing->OpenContainer. |
UCrimsonCoreContainerScreen | UCrimsonCoreInventoryScreenBase | /CrimsonCore/UserInterface/Inventory/Screens/W_Container_Screen | Player pane + container pane; binds from the routing OnContainerOpened result and closes on OnContainerClosed. |
ACrimsonCorePickupActor
| Property | Category | Meaning |
|---|---|---|
InteractionSphere | Crimson|Pickup | USphereComponent using the Interactable_OverlapDynamic profile - the range the interaction scan sees. |
BagScreenClass | Crimson|Pickup | Activatable widget pushed for a multi-item pickup. Set it to W_Loot_Window (or your UCrimsonCoreLootWindow subclass). Unset = multi-item interact does nothing. |
BagScreenLayerTag | Crimson|Pickup | Layer the loot window is pushed onto (UI.Layer.*). |
SingleItemOptionText | Crimson|Pickup|Display | Interaction option text for a single item; {0} = item name, {1} = stack count. |
BagOptionText | Crimson|Pickup|Display | Interaction option text when IsMultiItem(); {0} = slot count. |
PickupHandlerAbility | Crimson|Pickup | Ability granted to the instigator to run the interaction; defaults to UCrimsonCoreGameplayAbility_InteractionHandler. |
SecondaryActions | Crimson|Pickup | Instanced UCrimsonPickupAction list. Shipped actions: Examine, Open Bag, Pick Up, Take All. They appear as extra options in the interaction context menu, never on the quick press. |
| Condition | Option text | On success |
|---|---|---|
| One item (instance, template, or currency) | SingleItemOptionText | TakeAllItems -> Routing->TakePickupAll(this, Destination): the server moves everything into the instigator's inventory and the actor destroys itself when empty. |
IsMultiItem() | BagOptionText | OpenBagScreen pushes BagScreenClass on BagScreenLayerTag for the local controller, then calls BindToPickup(SourceActor, Destination) on the new widget by name. |
Both OpenBagScreen and TakeAllItems are BlueprintCallable, resolve the instigator's UCrimsonInventoryManagerComponent and UCrimsonInventoryRoutingComponent themselves, and gate on IsLocalController() - the interaction ability runs on the owning client, so the UI appears on the right machine and the take goes through the routing Server RPC.
B_LootBag (/CrimsonCore/Inventory/Actors/), a child of B_PickupActor_Base (/CrimsonCore/Inventory/). Study the Crimson|Pickup category on the parent and the BagMeshOverride / PickupMeshComponent from ACrimsonPickupActorBase. Duplicate B_LootBag for your own loot bags and pre-fill its pickup inventory for placed treasure.Add a pickup to your game
Create a Blueprint child of `B_PickupActor_Base` (or of ACrimsonCorePickupActor directly). In Class Defaults: set BagScreenClass = W_Loot_Window, BagScreenLayerTag = UI.Layer.Menu, add the secondary actions you want (+ on SecondaryActions -> Pickup Action - Examine, ...). Drop items at runtime with the CrimsonInventory drop path (the Drop verb, or Drop Entries As Bag on the inventory manager) and pass your class as the pickup class.
B_LootBag: W_Loot_Window opens, rows list the contents, A takes one row, Y takes all, and the window closes when the bag is empty.UCrimsonCoreLootWindow
BindToPickup(SourceActor, Destination) creates a UCrimsonCoreLootViewModel bound to the ACrimsonPickupActorBase and the destination inventory. Activating a row calls RequestTake(Entry); the Take All verb calls RequestTakeAll(); both go through Routing->TakePickupSlot / TakePickupAll. bCloseWhenEmpty (default on) deactivates the window when bIsEmpty becomes true or the pickup is destroyed. Optional widgets: Text_Title, ListView_Loot (a UCrimsonListView whose entry class is a UCrimsonCoreLootEntryWidget), and Panel_Player (a UCrimsonCoreInventoryPanelWidget so the player can make room while looting).
W_Loot_Window (/CrimsonCore/UserInterface/Inventory/Screens/) - a complete UCrimsonCoreLootWindow with all three optional widgets bound, so it shows exactly what to name yours: Text_Title, ListView_Loot (its entry class is W_Loot_Entry, /CrimsonCore/UserInterface/Inventory/Widgets/), and Panel_Player. B_LootBag (/CrimsonCore/Inventory/Actors/) already sets it as BagScreenClass, so placing the bag and interacting with it is the fastest way to see the pair working. Duplicate the widget as your starting point and keep the bound-widget names.ACrimsonCorePickupActor::OpenBagScreen finds BindToPickup by name with (AActor* SourceActor, UCrimsonInventoryManagerComponent* Destination). Subclass the window freely, but do not rename or re-sign that function.ACrimsonCoreContainerActor
Inherits the CrimsonInventory base: ContainerTag, Scope (ECrimsonContainerScope::Shared / PerPlayer / PerParty / PerMapInstance), DefaultLayout, DefaultIntConstraints, DefaultFloatConstraints, InteractionText, and OpenContainerUIAbility (defaults to UCrimsonCoreGameplayAbility_InteractionHandler). CrimsonCore adds:
| Property | Category | Meaning |
|---|---|---|
Mesh | Crimson|Container | The chest's UStaticMeshComponent (GetMesh()). |
InteractionSphere | Crimson|Container | Interaction range, like the pickup. |
ContainerScreenClass | Crimson|Container|UI | Widget pushed on interact; the shipped chests use W_Container_Screen. |
ContainerScreenLayerTag | Crimson|Container|UI | Layer for the screen (UI.Layer.*). |
ContainerTitle | Crimson|Container|UI | Title of the container pane. Empty = InteractionText. |
OpenContainerScreen(Instigator) runs on the local controller: it pushes ContainerScreenClass, calls SetContainerTitle on the new screen, and once the screen is active calls Routing->OpenContainer(this). The routing component resolves the scope, runs the access check, creates the scoped instance if needed, and fires OnContainerOpened with an FCrimsonContainerOpenResult; the screen is already subscribed and binds its right pane from it (BindToOpenResult). Closing the screen calls CloseContainer (bCloseContainerOnDeactivate), and a session closed from elsewhere fires OnContainerClosed, which closes the screen.
W_Container_Screen (/CrimsonCore/UserInterface/Inventory/Screens/) - a complete UCrimsonCoreContainerScreen. Its two bound panes, Panel_Player and Panel_Container (both UCrimsonCoreInventoryPanelWidget), are each registered as a panel root, which is what lets the Next / Previous panel verbs move focus - and a carried item - between them. Duplicate it as your starting point and keep the bound-widget names.Then open
B_Chest_Shared and B_Stash_Personal (/CrimsonCore/Inventory/Actors/); both already set it as their ContainerScreenClass, so placing one and interacting is the fastest way to see the pair live. Both also share DefaultLayout = DA_CrimsonInventoryLayout_FlatSlot and DefaultIntConstraints = [Crimson.Inventory.Constraint.Slots = 12]; they differ only in Scope. Duplicate one and change Scope and ContainerTitle. B_LootChest (ACrimsonCoreLootChest) is the one-time, take-only variant: authored InitialContents, one slot per remaining item, self-destroys once opened and emptied, and the world save keeps its remaining contents and opened state. B_OwnedStash (ACrimsonCoreOwnedStash) is the player-owned variant: spawned at runtime with SpawnOwnedStash (or the SpawnOwnedStash cheat) or claimed by its first opener, with an Access mode of Private, Party or Public that the owner sets from the interaction prompt's more-options menu (an Access rotator row, UCrimsonCoreContextAction_StashAccess); a refused player sees No Access (Owner: <Name>) on the prompt through an interaction condition (UCrimsonInteractionCondition_StashAccess on the container's OpenConditions); the server saves it with the world like any Shared container.Add a container to your game
Duplicate `B_Chest_Shared` (/CrimsonCore/Inventory/Actors/), set Scope, ContainerTitle, and the constraints, then place it in the level. Nothing else is required: the player's routing component (from CrimsonGameFeatureAction_AddInventory) and W_Container_Screen handle the session.
B_Chest_Shared: items placed by one appear for the other. Both open B_Stash_Personal: each sees only their own contents. Close the screen on one client; the other's stays open.Saving dropped pickups
ACrimsonCorePickupActor implements ICrimsonSaveableActor (CrimsonSaveSystem), so a dropped item survives a save and load with its contents intact - and a collected one stays collected.
| Hook | What it does |
|---|---|
GatherActorSaveData | Returns GetPickupSaveData() (CrimsonInventory) as the actor's custom payload. |
RestoreFromSaveData | Feeds it back through ApplyPickupSaveData, which rebuilds the item instances, re-registers them as replicated subobjects and re-resolves the mesh. |
EndPlay | Reports an explicit Destroy() - the pickup being taken - to UCrimsonSaveWorldManagerSubsystem, which drops the entry so it never respawns. Streaming out or level travel does not count as taken. |
PickupInventory as SaveGame does not work: it holds live item-instance POINTERS, and a saved pointer to a runtime subobject resolves to nothing on load. The pickup would return EMPTY - no mesh, and no interaction prompt either, since a pickup only offers options while it holds something. That is why the contents travel as a flattened FCrimsonInventoryPickupSaveData. See CrimsonInventory -> How-To: Drop and Pick Up Items.Multiplayer
- Server: pickup contents replicate from
ACrimsonPickupActorBase; takes and container mutations run throughUCrimsonInventoryRoutingComponentServer RPCs and are validated there. Only the server spawns pickups. - Owning client:
OpenBagScreen,OpenContainerScreen, and every screen. Both actors gate onIsLocalController(). - All clients: the pickup mesh (
BagMeshOverridewhen multi-item) and the chest mesh.
See also
- Inventory UI
- Inventory Showcases
- Interaction Integration
- Context Menu
- CrimsonInventory -> Overview
- CrimsonSaveSystem -> Quick Start: World Actors