@yagejs-addons/inventory
v0.2.0
Published
Slot-based inventory model with stacking, item actions, and a themeable slot view with swappable cell presets for YAGE
Downloads
397
Maintainers
Readme
@yagejs-addons/inventory
Slot-based inventory for YAGE: a headless model (stacking, partial acceptance, move/merge/split, sorting, transfers, snapshots, item actions) plus one windowed slot view with swappable icon/row cell presets and a zero-asset default theme.
Install
npm install @yagejs-addons/inventoryEngine packages (@yagejs/core, @yagejs/input, and — for the default
presenters — @yagejs/renderer) are peer dependencies; your game's install is
reused.
Entry points
@yagejs-addons/inventory— headless: catalog +Inventorymodel,filteredView(a subset projection of one model),InventorySession,InventoryController, engine events, input bindings. Never pulls pixi.@yagejs-addons/inventory/presenters— the renderer views: thecreateInventoryPanelfactory,SlotsView+iconCell/rowCellcell presets,defaultInventoryTheme,INVENTORY_LAYERS.
Quick start
import { defineItems, instanceData, Inventory, InventoryController, InventoryActionEvent } from "@yagejs-addons/inventory";
import { createInventoryPanel, INVENTORY_LAYERS } from "@yagejs-addons/inventory/presenters";
const catalog = defineItems({
potion: { name: "Potion", maxStack: 5, description: "Heals 20 HP." },
sword: { name: "Iron Sword" },
key: { name: "Gold Key", instance: instanceData<{ opens: string }>() },
});
const inventory = new Inventory({
catalog,
capacity: 15,
actions: [{ id: "use", label: "Use", consumes: true }, { id: "drop", label: "Drop" }],
});
class MyScene extends Scene {
readonly layers = [...INVENTORY_LAYERS];
onEnter() {
const bundle = createInventoryPanel(); // zero-asset default theme
const host = this.spawn("inventory");
// Default input = keyboard/gamepad + mouse/touch, already wired.
const controller = host.add(new InventoryController({ ...bundle, inventory }));
host.on(InventoryActionEvent, (e) => {
if (e.actionId === "use" && e.itemId === "potion") healPlayer(20);
});
}
}
// Anywhere in game logic — UI open or not:
inventory.add("potion", 3);
if (inventory.has("sword")) equip();
// Per-instance items (durability, rolled stats) carry a `data` payload.
// Query or grab them by a data predicate, then act on the exact stack:
inventory.add("key", 1, { data: { opens: "boss-lair" } });
const bossKey = inventory.find("key", (d) => d.opens === "boss-lair");
if (bossKey) {
inventory.remove(bossKey); // returns { removed, stacks } — the payload comes back
openDoor();
}The key def declares its per-stack data shape with
instance: instanceData<{ opens: string }>(), so d in find("key", (d) => …)
is typed { opens: string } and a wrong field is a compile error. An item that
declares no instance carries no per-stack data, so passing a data payload
to add on it is a compile error too. To keep the permissive
Record<string, unknown> instead, type the inventory by id only
(new Inventory<ItemId>(…)) or leave the catalog untyped.
Press the inventory action (or call controller.toggle()) to open the panel.
The model is always live — pickups, quest checks (inventory.has("goldKey")),
and removals work with the panel closed. Embedding in an existing menu is
configuration, not a different API: chrome: false + bounds on the factory,
input: null + closeOnCancel: false on the controller, then drive
open/move/confirm from your menu's focus handling.
Full docs: yage.dev → Addons → Inventory.
Status: pre-1.0. Breaking changes land in minor versions.
