The first grid inventory just wrapped your held items into a bordered strip — a row with a box drawn around it. What you asked for is a menu: something you open over the paused-feeling game, a panel of slots you look across and act on, with a hotbar that lives at the bottom and shows up inside the panel too. Here's the target, the one real architectural fork it raises, and a phased way in.
A centered window over a dimmed world. A grid of slots — filled ones carry an icon and a stack count, empty ones sit waiting. Hovering names the item. A strip along the bottom is the hotbar: the same eight slots you carry into the fight, shown here so the screen and the HUD agree. This is a mock — real icons are the game's own pickup art, not emoji.
The screen (top) and the always-on hotbar (bottom) are one system seen two ways.
The shipped grid reads the same held-item map the row hotbar does and lays it out in rows inside a panel. Functionally it's the hotbar with a border — no new capability, no sense of "opening" your pack. The menu is a different mode: it takes over the screen, gives every item room, and is where arranging, dropping, and (later) crafting happen.
Everything else is presentation over the inventory model we already have. This is the question that changes the data: is the grid a view of your item counts, or do items occupy fixed slots you rearrange — the Minecraft model, where an empty slot 3 is a real, saved fact?
The grid renders the same item → count map, sorted (by category, then name). No new
server state; the screen is pure client. Dropping/eating already work through the model.
For: ships now, reuses the wheel's drop path, nothing to persist or sync.
Against: you can't hand-place items or keep an empty gap; layout is the game's choice, not yours.
The player gains a slots component — an array where index is position and empties are
real. Drag moves an item between indices; the hotbar is just slots 1–8.
For: muscle-memory layout, real hotbar assignment, the base crafting needs.
Against: new authoritative state to persist (Set 13) + sync; migration when items appear/vanish; more UI.
Build the screen against Option A now — a sorted view, drawn client-side, drop-on-click — so
you get the menu feel this playtest. Add Option B's slots component as an opt-in later
step once the screen exists and we know what arranging should feel like. The UI built for A (grid,
hover, selection, hotbar strip) is exactly what B reuses; only the data source under it changes.
Two homes are possible: a DOM modal (like the settings menu) or a canvas overlay (like the item
wheel). The inventory wants the game's own pickup icons — which are canvas art (pickupIcon)
— and it wants to sit in the same input mode the wheel already established. So it lives on the canvas,
reusing the exact plumbing the wheel shipped with:
-- the grid-inventory plugin stays thin: it only claims the mode + the key. local inv = engine.require("inventory") inv.suppressHotbar() -- the row hotbar steps aside; the strip replaces it plugin.control("open", { default = "KeyI", label = "Inventory" }, function() end) -- client.lua opens the built-in screen; the engine draws it (icons, grid, hotbar). on_input("grid-inventory.open", function() screen.toggle("inventory") end)
The screen itself is engine client UI — a new UIService state
(open/closed, selection, hover) plus a renderer.drawInventory, both modeled on the
itemWheel pair already in the tree. Input (hover, click-to-drop, later drag) extends the
same InputService block that handles the wheel. The plugin contributes the key and the
hotbar-suppression; it doesn't reinvent the drawing. This is also the concrete next step toward
Lua-declared menus — the inventory is the first menu rich enough to prove that surface.
Modal grid over a dimmed game (Option A data), a persistent hotbar strip on the HUD, open/close on a key, hover-to-name. Replaces the bordered row. This is the playtestable menu.
Click a slot to drop one (reusing the wheel's validated drop path); number keys 1–8
select the active hotbar item. Still no new server state.
slots componentOpt into Option B: fixed slots, drag to move, real hotbar assignment, split/merge stacks. New authoritative state, persisted with the world (Set 13) and synced.
The tabs in the mock (Crops / Food / Tools / Blocks) filter by the item category we already tag; a filter field for big inventories. Cosmetic once the grid exists.
Sorted view first (Option A), or hold for arrangeable slots (Option B)? My rec is A now, B later — but if hand-arranging is core to the feel you want, we can start with slots and skip the throwaway.
The key. I reads as "inventory," but blocks currently own an I
panel. Reuse and replace it, or a different key (Tab is scores)? Your call on the binding.
Dim + pause feel. The mock dims the world behind the screen. Do you want it to read as paused (dark overlay, muted), or stay light so you can watch the fight while you sort?
I'll build Phase 1 + 2 against Option A on the I key with a dimmed overlay — the
menu you can open, look across, and drop from — and leave arrangement (Phase 3) for after you've
felt it. That's the version worth playtesting next.