From 6b4c6989844bc48c45f0a1ed876fddb8363484be Mon Sep 17 00:00:00 2001 From: Axel Meyer Date: Sun, 14 Jun 2026 01:59:28 +0200 Subject: [PATCH] =?UTF-8?q?feat(vagrant):=20notification=20system=20?= =?UTF-8?q?=E2=80=94=20Inspect=20modal,=20pickup-float,=20toast=20events,?= =?UTF-8?q?=20game-log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - manifest: bump to v0.10.0; add deps lib-core.notify, lib-core.notify-display, lib-core.world-overlay (all v0.1.0) - Inspect action migrated from engine.print to notify.post{tags={"inspect"}, data={...}}; routes to modal detail-panel via notify-display detail-channel - Pickup callback: world_overlay.spawn fires "+1 " float anchored on player (yellow, rise_px=24, ttl=1.2s) + notify.post pickup event-toast/log entry - Drop action (right-click menu): notify.post{tags={"drop","inventory"}} added after existing relocate+close_menu logic - Setup block: 3 channels (inspect/events/log), 2 panel widgets (inspect-detail/game-log), 3 attach_mode calls; L bound to game-log panel - M.update: notify_display.update + world_overlay.update after panel.update pause-guard - M.draw: camera_handle constructed after camera.finish(); world_overlay.render between camera.finish and panel.render; notify_display.render at end (toasts on top) - README: version 0.10.0, Requires expanded, Controls section updated, changelog entry added --- README.md | 25 +++++++++- init.lua | 124 +++++++++++++++++++++++++++++++++++++++--------- manifest.module | 5 +- 3 files changed, 129 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index f353c61..decaa64 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ callback, foot-body-orientation ("tactical twist"), scale-deformation walk cycle, and inheritance-decoupled foot sprites. End-to-end inventory pickup/drop demo: pick up a rock into a backpack container, drop it back into the world. -**Version:** 0.9.1 +**Version:** 0.10.0 **Module-ID:** vagrant-skeleton -**Requires:** lib-core.input >=0.4.0, lib-core.camera >=0.3.0, lib-core.render >=0.1.0, lib-core.maps >=0.1.2, lib-core.puppet >=0.4.4, lib-core.interaction >=0.1.0, lib-core.composition >=0.2.0, lib-core.actor >=0.1.0, lib-core.inventory-list >=0.1.0, lib-core.panel >=0.1.0, lib-core.inventory-list-display >=0.1.0, lib-asset.prototype-subterrain >=0.1.0 +**Requires:** lib-core.input >=0.4.0, lib-core.camera >=0.3.0, lib-core.render >=0.1.0, lib-core.maps >=0.1.2, lib-core.puppet >=0.4.4, lib-core.interaction >=0.1.0, lib-core.composition >=0.2.0, lib-core.actor >=0.1.0, lib-core.inventory-list >=0.1.0, lib-core.panel >=0.1.0, lib-core.inventory-list-display >=0.1.0, lib-core.notify >=0.1.0, lib-core.notify-display >=0.1.0, lib-core.world-overlay >=0.1.0, lib-asset.prototype-subterrain >=0.1.0 **Tags:** test-chamber, puppet, walking-sim, interaction ## Topology @@ -27,6 +27,10 @@ demo: pick up a rock into a backpack container, drop it back into the world. - **Q** — quick-drop (LIFO): places the most-recently-added backpack item at player position (+30px right) - **Tab** — toggle inventory panel (Backpack contents) - **Right-Click on item row** (while panel open): context menu with **Drop** / **Inspect** options +- **L** — toggle game-log (chronological list of all events) +- **Right-Click on item row → Inspect** — opens a modal Item Details panel (instead of console-only output) +- **Pickup (E)** — shows "+1 " floating above the player + toast at top-right + log entry +- **Drop (right-click on item → Drop, or Q for LIFO quick-drop)** — toast at top-right + log entry - **ESC** — return to launcher ## Demo Walkthrough @@ -111,6 +115,23 @@ Only one rock is spawned per session. ## CHANGELOG +### v0.10.0 — notification system integration +- **Inspect modal**: Inspect action replaced with `notify.post{tags={"inspect"}, ...}` routing + to a modal detail-panel (`notify-display` detail-channel). Item name, description, weight, + volume, and material composition are passed as structured data. + Inspect no longer calls engine.print directly; the notify lib's engine-log mirror routes the event to engine.log for backward-compat with dev tools. +- **Pickup world-float**: after `inventory.add`, a `world_overlay.spawn` fires a "+1 Rock" + floating text anchored to the player (yellow, rises 24px, fades in 1.2s). A `notify.post` + simultaneously emits a pickup event-toast and log entry. +- **Drop toast**: the Drop action (both right-click menu and Q quick-drop) emits a + `notify.post{tags={"drop","inventory"}}` event-toast after the item is relocated. +- **Game-log panel**: a catch-all log channel (`filter={}`) captures every pickup, drop, and + inspect event chronologically. **L** toggles the panel. +- **New deps**: lib-core.notify v0.1.0, lib-core.notify-display v0.1.0, + lib-core.world-overlay v0.1.0. +- **Render order**: camera.finish() → world_overlay.render(camera_handle) → + panel.render() → notify_display.render() (toasts on top of all UI). + ### v0.9.1 — item-template player-facing properties - **Rock template enriched**: `name`, `description`, `weight` (kg), `volume` (L), and `composition.stone = 1.0` properties added. Other item diff --git a/init.lua b/init.lua index 38a328c..10d4865 100644 --- a/init.lua +++ b/init.lua @@ -1,4 +1,4 @@ --- sporel-module-vagrant-skeleton v0.9.1 +-- sporel-module-vagrant-skeleton v0.10.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, @@ -34,6 +34,13 @@ -- notification system will route this to an in-game text panel. Module- -- side label_resolver override removed — default_label_resolver reads -- the `name` property. +-- +-- v0.10.0: Notification system wired in. Inspect routes to a modal +-- detail-panel (notify-display detail-channel); pickup spawns a +-- world-floating "+1 Rock" overlay; Drop emits an event-toast. L +-- opens the game-log panel listing all events chronologically. +-- engine.print mirror remains enabled — dev tools that parse the log +-- continue to work. local input = require("lib-core.input") local camera = require("lib-core.camera") @@ -45,7 +52,10 @@ 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 inv_display = require("lib-core.inventory-list-display") +local notify = require("lib-core.notify") +local notify_display = require("lib-core.notify-display") +local world_overlay = require("lib-core.world-overlay") local M = {} @@ -62,6 +72,9 @@ local ANIM_SPEED_SPRINT = 0.9 -- 2x local BACKPACK_X, BACKPACK_Y = 350, 400 local INTERACTION_RANGE = 40 -- pixels +-- World-overlay float colors (pickup confirmation) +local COLOR_PICKUP_FLOAT = 0xFFFF80FF + -- Backpack-Template (Phase A.4 — Atlas-UV render-path). -- v0.8.0: container={kind="list"} declared so the backpack receives -- items via inventory.add (capability-by-declaration). @@ -178,6 +191,25 @@ local function register_pickup(item) trigger_id = interaction.register(x, y, INTERACTION_RANGE, "interact", function() inventory.add(state.backpack, item) + + -- World-float "+1 " anchored on the player + local item_name = item:get_property("name") or "item" + world_overlay.spawn{ + kind = "text", + text = "+1 " .. item_name, + anchor = state.actor, -- follow player + offset = { x = -20, y = -48 }, + ttl = 1.2, + font_size = 16, + color = COLOR_PICKUP_FLOAT, + rise_px = 24, + } + -- Pickup event-toast + log entry + notify.post{ + tags = {"pickup", "inventory"}, + text = string.format("Picked up %s", item_name), + } + interaction.unregister(trigger_id) if CI_FRAME_PERF then engine.print("vagrant: event=pickup") end @@ -433,39 +465,66 @@ function M.init(ctx) register_pickup(item) c.close_menu() if CI_FRAME_PERF then engine.print("vagrant: event=drop_via_menu") end + notify.post{ + tags = {"drop", "inventory"}, + text = string.format("Dropped %s", + item:get_property("name") or "item"), + } end) - -- Inspect action: prints player-facing item info to console. Future - -- notification-system (lib-core.toast or similar) will route this to - -- an in-game text panel instead of engine.print. + -- Inspect action: routes to a modal detail-panel via notify.post. + -- engine.print mirror kept for dev-tool compatibility. inv_display.register_action(backpack_widget, "Inspect", function(item, c) - local name = item:get_property("name") or "(unnamed)" - local description = item:get_property("description") or "" - local weight = item:get_property("weight") or 0 - local volume = item:get_property("volume") or 0 - engine.print(string.format("=== %s ===", name)) - if description ~= "" then engine.print(description) end - engine.print(string.format("weight=%.2fkg volume=%.3fL", weight, volume)) - -- Surface material composition by iterating composition.* properties. + -- Collect composition..* properties for the detail payload local props = item:get_properties() - local mats = {} + local comp = {} for k, v in pairs(props) do local mat = k:match("^composition%.(.+)$") - if mat and mat ~= "reg_id" then - table.insert(mats, string.format("%s=%.2f", mat, v)) - end + if mat and mat ~= "reg_id" then comp[mat] = v end end - if #mats > 0 then - engine.print("composition: " .. table.concat(mats, " ")) - end - engine.print(string.format("(reg_id=%s)", - tostring(item:get_property("composition.reg_id")))) + notify.post{ + tags = {"inspect"}, + text = item:get_property("name") or "(unnamed)", + data = { + description = item:get_property("description"), + weight = item:get_property("weight"), + volume = item:get_property("volume"), + composition = comp, + }, + } c.close_menu() end) panel.register("backpack", backpack_widget) panel.bind_default_trigger("tab", "backpack") + -- ====== Notification System Setup ====== + + -- Channels (created BEFORE display attach + widget register) + notify.create_channel{ id = "inspect", filter = {"inspect"}, + display_mode = "detail" } + notify.create_channel{ id = "events", filter = {"pickup", "drop", "crafting"}, + display_mode = "toast" } + notify.create_channel{ id = "log", filter = {}, -- catch-all + display_mode = "log" } + + -- Widgets MUST be registered BEFORE attach_mode("detail"/"log") because + -- attach_mode auto-opens detail-widget on first message (requires registered). + panel.register("inspect-detail", + notify_display.create_detail_widget{ + widget_id = "inspect-detail", title = "Item Details" }) + panel.register("game-log", + notify_display.create_log_widget{ + widget_id = "game-log", title = "Game Log" }) + + -- Attach display-layer modes + notify_display.attach_mode("detail", { panel_widget_id = "inspect-detail" }) + notify_display.attach_mode("toast", { position = "top-right", max_visible = 5 }) + notify_display.attach_mode("log", { panel_widget_id = "game-log" }) + + -- L opens game-log (inspect-detail auto-opens on notify.post; no hotkey) + panel.bind_default_trigger("l", "game-log") + -- Input bindings. input.bind("walk_left", { "a" }) input.bind("walk_right", { "d" }) @@ -501,6 +560,9 @@ function M.update(ctx, dt) panel.update(dt) if panel.is_pausing() then return end + notify_display.update(dt) + world_overlay.update(dt) + -- 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. @@ -514,6 +576,11 @@ function M.update(ctx, dt) item:set_property("position.y", py) register_pickup(item) if CI_FRAME_PERF then engine.print("vagrant: event=drop") end + notify.post{ + tags = {"drop", "inventory"}, + text = string.format("Dropped %s", + item:get_property("name") or "item"), + } -- Inventory inspection trace (unconditional — user-facing demo output). local count = inventory.count(state.backpack) @@ -655,8 +722,21 @@ function M.draw(ctx) ci_sprite_traced = true end end + -- Camera handle for world_overlay (built from lib-core.camera state) + local target = camera.target() -- returns {x, y} + local w, h = engine.window.size() + local camera_handle = { + target_x = target.x, + target_y = target.y, + zoom = camera.zoom(), + offset_x = w / 2, + offset_y = h / 2, + } + world_overlay.render(camera_handle) -- Inventory panel overlay (z-top: drawn after all world content). panel.render() + -- Toast overlay on top of everything. + notify_display.render() end -- Engine lifecycle globals (engine looks up by name; module-table is bridged). diff --git a/manifest.module b/manifest.module index 98af40a..ad0c9e3 100644 --- a/manifest.module +++ b/manifest.module @@ -1,6 +1,6 @@ { "id": "vagrant-skeleton", - "version": "0.9.1", + "version": "0.10.0", "kind": "module", "api": "^0.1", "ci_frames": 60, @@ -19,6 +19,9 @@ {"id": "lib-core.actor", "version": "0.1.0"}, {"id": "lib-core.panel", "version": "0.1.0"}, {"id": "lib-core.inventory-list-display", "version": "0.1.1"}, + {"id": "lib-core.notify", "version": "0.1.0"}, + {"id": "lib-core.notify-display", "version": "0.1.0"}, + {"id": "lib-core.world-overlay", "version": "0.1.0"}, {"id": "lib-core.maps","version":"0.5.7"}, {"id": "lib-core.puppet", "version": "0.5.0"}, {"id": "lib-core.interaction", "version": "0.1.0"},