---
title: "Inventory item pickups keys and equipping"
description: "The player collects keys, potions, or ammo, carries them, and spends them later. There is no inventory or `StatsComponent` (stripped); the inventory is a durable warehouse record and item logic is a script."
engineVersion: v1.0.234
date: 2026-09-28
license: "(c) Gessa, proprietary. Cite with attribution to https://gessa.ai/docs/. Terms: https://gessa.ai/terms/."
canonical: https://gessa.ai/docs/knowledge/playbooks/inventory-and-item-pickups/
---

# Inventory item pickups keys and equipping

## When to use

The player collects keys, potions, or ammo, carries them, and spends them later.
There is no inventory or `StatsComponent` (stripped); the inventory is a durable
warehouse record and item logic is a script.

## The recipe

Author with `project_apply_script_semantic_patch` using the `ai_safe` op pack.
Held items live in a warehouse store (default `storeKey:"inventory"`,
`recordKey:"player"`), one field per item type.

1. Make each world pickup an entity tagged by item type with a trigger
   `ColliderComponent` (`collision:"trigger"`).
2. On pickup, `addTriggerHandler` `{ "handlerKey":"onPickup" }`. Inside it:
   `incrementNumericState` `{ "field":"keys", "by": 1 }` to add a counted item,
   or `writeTypedStateValue` to set a unique key you either hold or do not; then
   `despawnEntity` `{ "reason":"picked_up" }` to remove the world item.
3. For a list of distinct items, append to an array field with
   `ctx.warehouse.append` in a whole-body GessaScript body
   (`replaceScriptFromGessaScript`); read the whole record back with
   `ctx.warehouse.get` or filter with `ctx.warehouse.query`.
4. Gate an action on holding an item: `readTypedStateValue` the item field, and
   only open the locked door (see doors-buttons-and-interactions) or fire the
   weapon when the count is above zero.
5. Consume or equip: on use, `incrementNumericState` the field by -1 (consume) or
   `writeTypedStateValue` an `equipped` field to the item id (equip). Project
   every count to the HUD with `wireHudStateBinding` or `ctx.ui.setWidgetValue`.

## Pitfalls

- Keep the inventory in the warehouse, not on a component; a count written only
  on the entity is lost when the entity despawns or the player reloads.
- `ctx.warehouse.*` writes are prefetch-then-drain and authority-gated
  (`persistence.prefetch_only`); mutate on the server, and read the field you
  intend to change in the same handler so the drain is deterministic.
- Decrement guardedly: check the count is above zero before consuming, or you
  create negative inventory.
- For stackable items increment a number; for unique items (a named key) set a
  boolean or an id, not a counter.

## Verify

- `simulation_run` (`qa.run.start`) to prove a pickup raises the count, using an
  item lowers it, and a gated door opens only while the key is held.
- `project_get_graph_snapshot` to confirm the pickup triggers and handlers exist.
