Storage
Usage guide of storage.containers: chests, crates and silos with slots, stacks and accepted items, a world side for scripts and machines, a player side against the bag, the storage screen, settings, events and hooks.
Storage containers (storage.containers) #
What it is for #
Somewhere to put things. A container is any entity that holds items: a chest, a crate, a silo, a fridge, a warehouse shelf. It has a number of slots, a stack size and, when wanted, a list of the only items it takes. The player opens one and moves items between the bag and the container; scripts and other kits (carriers, machines, quests) put and take without the player. The contents live in the entity's component, so the save keeps them. The bag itself is Inventory.
Turn it on #
gripforge_gamekit_install { project_id: "gkp_…", id: "storage.containers" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "storage.containers",
config: { "defaultSlots": 12, "defaultStack": 50, "reach": 4 } }
It requires only game.engine. The player side works with inventory.items (the bag), and a container shows its prompt through interaction.use; with automation.logistics installed the screen also offers the open container's logistics (offer its contents to drones, keep a number in stock).
There are three ways to make a container: an archetype with the component storage_container, a structure of build.placement whose components include "storage.container": { … }, or place(at, opts?) from a script (opts: label, slots, stack, accepts, items, variant, yaw).
{ "label": "Crate", "slots": 8, "stack": 50, "accepts": ["wood", "stone"], "items": { "wood": 20 } }
First example #
<!-- test: kits/game_engine/host/tests/parity.rs::rust_host_matches_the_typescript_traces (fixture storage.containers.boxes) --> The parity scenario creates a chest with 2 slots, a stack of 8 and a list of accepted items, then calls the world side: containers() and contents(entity) describe it, room(entity, item) tells how many units still fit (0 for an item that is not accepted), put(entity, item, n) returns the units really stored and take(entity, item, n) the units really removed. Anything that does not fit stays with the caller:
room ["e2", "ore"] -> units that still fit
put ["e2", "ore", 9] -> units stored (what the stack of 8 still leaves)
put ["e2", "gold", 1] -> 0 (not in accepts)
take ["e2", "half", 5] -> units removed (never more than it holds)
Settings and defaults #
| Setting | Default | Meaning |
|---|---|---|
openLabel | Open {name} | prompt of a container; {name} is its label |
defaultName | Container | name of a container without a label |
reach | 4 | the screen closes when the player walks further than this (m) |
defaultSlots | 12 | distinct items a container holds when it does not say |
defaultStack | 50 | units per item of a container that does not say |
A container's own fields are label, slots (one item takes one slot), stack, accepts (the only items that go in; absent: anything) and items (what it holds, { "<item>": count }).
Services, events, variables #
storage.containers(service), world side, never involving the player:contents(entity),count(entity, item),room(entity, item),put(entity, item, n),take(entity, item, n),total(item),containers(),find(item),place(at, opts?).- Player side:
deposit(entity, item, n?)(all of it by default),withdraw(entity, item, n?)(as much as fits),depositAll(entity),withdrawAll(entity), each returning the units moved;open(entity),close(),current(). - Events:
storage.changed(entity,item,count,delta),storage.full(a put or deposit did not fully fit),storage.opened,storage.closed,storage.refused(entity,action,item,reason). - Variables:
storage.open,storage.containers. - Condition
stored_at_least(item,count): every container together holds at least that many.
Hooks and modding #
The player side is asked; the world side (put, take) never is.
| Hook | Payload | Effect |
|---|---|---|
storage.open | entity, label | cancelled: the screen stays shut (a locked chest) |
storage.deposit | entity, label, item, count | mutable count (never more than the bag holds); cancelled: nothing moves |
storage.withdraw | entity, label, item, count | mutable count (never more than the container holds); cancelled: nothing moves |
A cancelled hook emits storage.refused with the reason of the tap.
{ "id": "locked_vault", "hook": "storage.open", "match": { "label": "Vault" }, "cancel": true, "reason": "locked" }
A mod may override openLabel, defaultName, reach, defaultSlots and defaultStack. The kit has no collection of its own.
Engines #
The web player and the native kit host run the same rules. The storage screen shows the bag and the open container side by side (move one, move all, deposit all, take all), drawn as a ui.webui fragment.
When it does not do what you expect #
- Nothing goes in: the item is not in
accepts, the slots are used, or the stack is full;roomsays how many fit. - The prompt never appears: the entity has no
storage_containercomponent, orinteraction.useis not installed. - The screen closes by itself: the player walked further than
reach. - A deposit does nothing: a tap of
storage.depositcancelled it; read the reason instorage.refused.
Limits #
- Nothing is ever lost: what does not fit stays with the caller.
- It stores counts of named items.
Versions #
Kit 1.1.1, service contract 1.0.0. 1.1.0: the game.engine hook contract and storage.refused; 1.0.0: first release.