feat(vagrant): Tab-Toggle inventory panel + right-click context menu

- lib-core.panel and lib-core.inventory-list-display added to deps (v0.1.0 each)
- backpack-widget created in M.init with label-resolver returning "Rock"
- Drop action registered: removes from backpack, relocates to player position,
  re-registers pickup trigger
- Inspect action registered: prints reg_id via engine.print
- M.update: panel.update(dt) + is_pausing() early-return added after
  quit_to_launcher check
- interaction.update wrapped in panel.is_open() guard to block world triggers
  while panel is visible
- M.draw: panel.render() added at z-top after CI traces
- Q-drop (LIFO quick-drop via Q key) unchanged and parallel to panel Drop action
This commit is contained in:
Axel Meyer
2026-06-13 23:38:03 +02:00
parent e38d396b87
commit 60eac65bce
3 changed files with 75 additions and 8 deletions

View File

@@ -1,4 +1,4 @@
-- sporel-module-vagrant-skeleton v0.8.0
-- sporel-module-vagrant-skeleton v0.9.0
-- Sprite-mode test-chamber: 13-bone humanoid puppet + sprite-tilemap +
-- hardcoded furniture. Subterrain-style puppet control model:
-- multi-look-target (aim/soft), slerp body rotation, hip-static legs,
@@ -22,6 +22,11 @@
-- position. engine.print shows inventory count + item-IDs after each
-- add/remove. Tree-check (CI-gated) verifies child placement in
-- composition tree.
--
-- v0.9.0: Inventory panel UI wired in. Tab toggles a panel overlay
-- showing backpack contents. Right-click on an item row opens a context
-- menu with Drop and Inspect actions. Q-drop (LIFO quick-drop) remains
-- available in parallel.
local input = require("lib-core.input")
local camera = require("lib-core.camera")
@@ -32,6 +37,8 @@ local interaction = require("lib-core.interaction")
local composition = require("lib-core.composition")
local actor = require("lib-core.actor")
local inventory = require("lib-core.inventory-list")
local panel = require("lib-core.panel")
local inv_display = require("lib-core.inventory-list-display")
local M = {}
@@ -389,6 +396,43 @@ function M.init(ctx)
register_pickup(state.rock)
if CI_FRAME_PERF then engine.print("vagrant: rock_spawned") end
-- Inventory-UI Setup
panel.set_theme{ panel_width_frac = 0.4, font_size_title = 20 }
local backpack_widget = inv_display.create(state.backpack, {
title = "Backpack",
widget_id = "backpack",
pause_on_open = false,
})
-- Module-side label-resolver (display lib's default returns "Item").
-- Vagrant currently has one item template (rock); hardcoded for v0.9.
inv_display.set_label_resolver(backpack_widget, function(item)
return "Rock"
end)
-- Drop action: knows world-reparenting + player position
inv_display.register_action(backpack_widget, "Drop", function(item, c)
inventory.remove(state.backpack, item)
local px = state.actor:get_property("position.x")
local py = state.actor:get_property("position.y")
item:set_property("position.x", px + 30)
item:set_property("position.y", py)
register_pickup(item)
c.close_menu()
if CI_FRAME_PERF then engine.print("vagrant: event=drop_via_menu") end
end)
-- Inspect action: dumps reg_id to console
inv_display.register_action(backpack_widget, "Inspect", function(item, c)
engine.print(string.format("vagrant: inspect: reg_id=%s",
tostring(item:get_property("composition.reg_id"))))
c.close_menu()
end)
panel.register("backpack", backpack_widget)
panel.bind_default_trigger("tab", "backpack")
-- Input bindings.
input.bind("walk_left", { "a" })
input.bind("walk_right", { "d" })
@@ -420,6 +464,10 @@ function M.update(ctx, dt)
return
end
-- Inventory panel input handling
panel.update(dt)
if panel.is_pausing() then return end
-- Drop: Q pops the most-recently-added item from backpack, relocates
-- it to world at player position + 30px right offset, and re-registers
-- a pickup-trigger. inventory.remove restores the renderable tag.
@@ -436,9 +484,9 @@ function M.update(ctx, dt)
-- Inventory inspection trace (unconditional — user-facing demo output).
local count = inventory.count(state.backpack)
local items = inventory.contents(state.backpack)
local remaining_items = inventory.contents(state.backpack) -- re-read post-remove
local ids = {}
for _, it in ipairs(items) do
for _, it in ipairs(remaining_items) do
table.insert(ids, tostring(it:get_property("composition.reg_id")))
end
engine.print(string.format("vagrant: backpack count=%d items=[%s]",
@@ -525,7 +573,9 @@ function M.update(ctx, dt)
-- Interaction: dispatches the nearest matching proximity-trigger when
-- 'interact' was pressed this frame. Uses actor.position as anchor.
interaction.update(actor.position(state.actor))
if not panel.is_open() then
interaction.update(actor.position(state.actor))
end
-- Camera follows actor position each frame.
camera.set_target(actor.position(state.actor))
@@ -572,6 +622,8 @@ function M.draw(ctx)
ci_sprite_traced = true
end
end
-- Inventory panel overlay (z-top: drawn after all world content).
panel.render()
end
-- Engine lifecycle globals (engine looks up by name; module-table is bridged).