Chris' Tutorials
Docs/Item Vault

Item Vault v1.0

Runtime and Pickup Bridge

Use ItemVaultRuntime and InventoryPickupBridge to route pickups into inventory.

StatusDraft
Versionv1.0
UpdatedDevelopment docs generated from GDScript source

This is unreleased documentation in active development. APIs, class names, and behavior may change before the final release.

Draft — Unreleased:This page is in active development. APIs, class names, and behavior may change before the release is finalized. Use it as a preview of what's coming — not as a stable integration target.

ItemVaultRuntime is the scene-owned composition root for ItemVault services. Use it in scenes that need an item database, pickup event routing, and an inventory target.

Why It Is Called a Bridge

Pickup nodes emit PickupEvent values. Inventories mutate state. The InventoryPickupBridge is the small service between those two responsibilities:

PickupEvent -> InventoryPickupBridge -> Inventory or Wallet

This is intentionally tight integration with ItemVault inventory, but it avoids hardwiring every pickup node directly to one inventory instance. The runtime owns the connection for the scene, so tests, menus, split-screen scenes, and temporary demo scenes can each have isolated inventory routing.

What Runtime Owns

Service Role
ItemVaultBus Scoped signal bus for item events.
ItemDatabase Lookup table for ItemDefinition.id.
InventoryPickupBridge Routes PickupEvent.stack into an ItemVault inventory target.

You can use the services directly in your own composition root, but ItemVaultRuntime is the ready-made wiring node.

Basic Setup

var runtime := ItemVaultRuntime.new()
add_child(runtime)
runtime.database_dirs = ["res://items"]
runtime.auto_reload_database = true
runtime.set_inventory_target(player_inventory)
runtime.initialize()

ItemVault's Inventory type is the primary target. Advanced game-owned adapters can provide the same method shape:

func add(stack: ItemStack) -> ItemStack:
	# Return null if fully accepted.
	# Return overflow stack if only partially accepted.
	return null

Connect Pickup Nodes

Connect pickup nodes when they enter the scene or when you spawn them:

if not runtime.connect_pickup(pickup):
	push_warning("Pickup was not connected to ItemVaultRuntime.")

connect_pickup() returns false when the node is null, already connected, or does not expose the expected picked_up signal.

Disconnect when a pickup should stop routing through the runtime:

runtime.disconnect_pickup(pickup)

Bridge Behavior

InventoryPickupBridge listens for PickupEvent values. When enabled, it:

  1. Ignores null events, null stacks, or missing targets.
  2. Sends currency stacks to Inventory.wallet when the item category is &"currency" and the target exposes a wallet.
  3. Otherwise calls target.add(event.stack).
  4. Emits pickup_rejected(event, overflow) when the target cannot accept the full stack.

Use this signal to show feedback when the inventory is full:

runtime.bridge.pickup_rejected.connect(_on_pickup_rejected)

func _on_pickup_rejected(event: PickupEvent, overflow: ItemStack) -> void:
	print("Could not pick up %s x%s" % [overflow.item.id, overflow.quantity])

Overflow Behavior: What Happens When the Inventory Is Full

When a pickup is taken but the inventory cannot accept the full stack, the bridge writes the overflow onto PickupEvent.overflow. After the picked_up signal finishes, take() checks the event and decides what to do:

Outcome Condition What happens
Fully accepted event.overflow == null Pickup frees or disables. Standard happy path.
Fully Rejected event.overflow.quantity >= event.stack.quantity Pickup stays in the world with its original stack. Metadata is restored so the pickup is still collectible.
Partially Accepted event.overflow.quantity < event.stack.quantity Pickup stays in the world. Metadata is updated to the overflow quantity so the remaining items can be collected later.

This means items are never lost — a pickup that cannot be fully absorbed stays in the game world with whatever quantity could not fit.

Reading the Outcome in Your Game Code

After take() returns, check the event:

var event := pickup.take(collector)

if event.fully_accepted():
    # Normal success — play pickup sound, show "Item acquired" toast.
    pass
elif event.fully_rejected():
    # Inventory was completely full — nothing was taken.
    # Show "Inventory full" UI feedback.
    pass
else:
    # Partially accepted — some items went in, rest remain on the ground.
    # event.overflow.quantity is how many did NOT fit.
    print("Took some, %d remaining" % event.overflow.quantity)

Customizing Overflow Behavior

The default behavior (keep the pickup alive with overflow) is safe but you may want different game-feel. Common alternatives:

Destroy overflow (arcade style):

# After take(), if partially accepted, free the pickup anyway.
var event := pickup.take(collector)
if not event.fully_accepted():
    pickup.root.queue_free()  # discard remaining items

Refuse entirely ( Souls-like):

# Check capacity BEFORE calling take().
var bridge := runtime.bridge
# Use inventory query API to check space:
var can_fit := player_inventory.query_containers("", stack.quantity).size() > 0
if not can_fit:
    show_message("Cannot carry more")
    return
var event := pickup.take(collector)

Drop overflow as a new world item:

var event := pickup.take(collector)
if event.overflow != null and not event.fully_accepted():
    # Spawn a new Pickup2D at this position with the overflow stack.
    spawn_world_item(pickup.global_position, event.overflow)

Currency Pickups

Currency stacks (category "currency") route to the wallet, which has no capacity limit. Currency pickups are always fully accepted — overflow does not apply to wallet-routed items.

Scene-Owned Runtime, Game-Owned Architecture

ItemVault does not require a global singleton. If your game has a global inventory service, keep that service in the game and pass the relevant target into ItemVaultRuntime.

For GECS or other world-entity systems, keep the adapter at the game boundary: consume the ItemVault ItemStack, then pass stack.to_dict() or mapped component data into the game-owned world.

Disable Automatic Routing

If a scene should receive pickup events but not add items automatically, disable the bridge:

runtime.bridge_enabled = false
runtime.initialize()

Troubleshooting

Problem Fix
Pickup signal fires but inventory does not change Confirm runtime.set_inventory_target(...) points at an ItemVault Inventory or adapter with add(stack).
connect_pickup() returns false Confirm the pickup node exposes picked_up and is not already connected.
Currency does not enter wallet Confirm the item category is &"currency" and the target exposes a wallet.
Items route between unrelated scenes Use one ItemVaultRuntime per scene scope instead of a global bridge.

What's Next

Source

addons/item_vault/guides/runtime-and-pickup-bridge.md

Plugin docs root:gdscript/plugins/item_vault_dev/addons/item_vault/guides