feat(vagrant): notification system — Inspect modal, pickup-float, toast events, game-log
- 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 <name>" 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
This commit is contained in:
25
README.md
25
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
|
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.
|
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
|
**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
|
**Tags:** test-chamber, puppet, walking-sim, interaction
|
||||||
|
|
||||||
## Topology
|
## 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)
|
- **Q** — quick-drop (LIFO): places the most-recently-added backpack item at player position (+30px right)
|
||||||
- **Tab** — toggle inventory panel (Backpack contents)
|
- **Tab** — toggle inventory panel (Backpack contents)
|
||||||
- **Right-Click on item row** (while panel open): context menu with **Drop** / **Inspect** options
|
- **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 <name>" 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
|
- **ESC** — return to launcher
|
||||||
|
|
||||||
## Demo Walkthrough
|
## Demo Walkthrough
|
||||||
@@ -111,6 +115,23 @@ Only one rock is spawned per session.
|
|||||||
|
|
||||||
## CHANGELOG
|
## 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
|
### v0.9.1 — item-template player-facing properties
|
||||||
- **Rock template enriched**: `name`, `description`, `weight` (kg),
|
- **Rock template enriched**: `name`, `description`, `weight` (kg),
|
||||||
`volume` (L), and `composition.stone = 1.0` properties added. Other item
|
`volume` (L), and `composition.stone = 1.0` properties added. Other item
|
||||||
|
|||||||
122
init.lua
122
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 +
|
-- Sprite-mode test-chamber: 13-bone humanoid puppet + sprite-tilemap +
|
||||||
-- hardcoded furniture. Subterrain-style puppet control model:
|
-- hardcoded furniture. Subterrain-style puppet control model:
|
||||||
-- multi-look-target (aim/soft), slerp body rotation, hip-static legs,
|
-- 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-
|
-- notification system will route this to an in-game text panel. Module-
|
||||||
-- side label_resolver override removed — default_label_resolver reads
|
-- side label_resolver override removed — default_label_resolver reads
|
||||||
-- the `name` property.
|
-- 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 input = require("lib-core.input")
|
||||||
local camera = require("lib-core.camera")
|
local camera = require("lib-core.camera")
|
||||||
@@ -46,6 +53,9 @@ local actor = require("lib-core.actor")
|
|||||||
local inventory = require("lib-core.inventory-list")
|
local inventory = require("lib-core.inventory-list")
|
||||||
local panel = require("lib-core.panel")
|
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 = {}
|
local M = {}
|
||||||
|
|
||||||
@@ -62,6 +72,9 @@ local ANIM_SPEED_SPRINT = 0.9 -- 2x
|
|||||||
local BACKPACK_X, BACKPACK_Y = 350, 400
|
local BACKPACK_X, BACKPACK_Y = 350, 400
|
||||||
local INTERACTION_RANGE = 40 -- pixels
|
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).
|
-- Backpack-Template (Phase A.4 — Atlas-UV render-path).
|
||||||
-- v0.8.0: container={kind="list"} declared so the backpack receives
|
-- v0.8.0: container={kind="list"} declared so the backpack receives
|
||||||
-- items via inventory.add (capability-by-declaration).
|
-- 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",
|
trigger_id = interaction.register(x, y, INTERACTION_RANGE, "interact",
|
||||||
function()
|
function()
|
||||||
inventory.add(state.backpack, item)
|
inventory.add(state.backpack, item)
|
||||||
|
|
||||||
|
-- World-float "+1 <item-name>" 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)
|
interaction.unregister(trigger_id)
|
||||||
if CI_FRAME_PERF then engine.print("vagrant: event=pickup") end
|
if CI_FRAME_PERF then engine.print("vagrant: event=pickup") end
|
||||||
|
|
||||||
@@ -433,39 +465,66 @@ function M.init(ctx)
|
|||||||
register_pickup(item)
|
register_pickup(item)
|
||||||
c.close_menu()
|
c.close_menu()
|
||||||
if CI_FRAME_PERF then engine.print("vagrant: event=drop_via_menu") end
|
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)
|
end)
|
||||||
|
|
||||||
-- Inspect action: prints player-facing item info to console. Future
|
-- Inspect action: routes to a modal detail-panel via notify.post.
|
||||||
-- notification-system (lib-core.toast or similar) will route this to
|
-- engine.print mirror kept for dev-tool compatibility.
|
||||||
-- an in-game text panel instead of engine.print.
|
|
||||||
inv_display.register_action(backpack_widget, "Inspect", function(item, c)
|
inv_display.register_action(backpack_widget, "Inspect", function(item, c)
|
||||||
local name = item:get_property("name") or "(unnamed)"
|
-- Collect composition.<material>.* properties for the detail payload
|
||||||
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.
|
|
||||||
local props = item:get_properties()
|
local props = item:get_properties()
|
||||||
local mats = {}
|
local comp = {}
|
||||||
for k, v in pairs(props) do
|
for k, v in pairs(props) do
|
||||||
local mat = k:match("^composition%.(.+)$")
|
local mat = k:match("^composition%.(.+)$")
|
||||||
if mat and mat ~= "reg_id" then
|
if mat and mat ~= "reg_id" then comp[mat] = v end
|
||||||
table.insert(mats, string.format("%s=%.2f", mat, v))
|
|
||||||
end
|
end
|
||||||
end
|
notify.post{
|
||||||
if #mats > 0 then
|
tags = {"inspect"},
|
||||||
engine.print("composition: " .. table.concat(mats, " "))
|
text = item:get_property("name") or "(unnamed)",
|
||||||
end
|
data = {
|
||||||
engine.print(string.format("(reg_id=%s)",
|
description = item:get_property("description"),
|
||||||
tostring(item:get_property("composition.reg_id"))))
|
weight = item:get_property("weight"),
|
||||||
|
volume = item:get_property("volume"),
|
||||||
|
composition = comp,
|
||||||
|
},
|
||||||
|
}
|
||||||
c.close_menu()
|
c.close_menu()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
panel.register("backpack", backpack_widget)
|
panel.register("backpack", backpack_widget)
|
||||||
panel.bind_default_trigger("tab", "backpack")
|
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 bindings.
|
||||||
input.bind("walk_left", { "a" })
|
input.bind("walk_left", { "a" })
|
||||||
input.bind("walk_right", { "d" })
|
input.bind("walk_right", { "d" })
|
||||||
@@ -501,6 +560,9 @@ function M.update(ctx, dt)
|
|||||||
panel.update(dt)
|
panel.update(dt)
|
||||||
if panel.is_pausing() then return end
|
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
|
-- Drop: Q pops the most-recently-added item from backpack, relocates
|
||||||
-- it to world at player position + 30px right offset, and re-registers
|
-- it to world at player position + 30px right offset, and re-registers
|
||||||
-- a pickup-trigger. inventory.remove restores the renderable tag.
|
-- a pickup-trigger. inventory.remove restores the renderable tag.
|
||||||
@@ -514,6 +576,11 @@ function M.update(ctx, dt)
|
|||||||
item:set_property("position.y", py)
|
item:set_property("position.y", py)
|
||||||
register_pickup(item)
|
register_pickup(item)
|
||||||
if CI_FRAME_PERF then engine.print("vagrant: event=drop") end
|
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).
|
-- Inventory inspection trace (unconditional — user-facing demo output).
|
||||||
local count = inventory.count(state.backpack)
|
local count = inventory.count(state.backpack)
|
||||||
@@ -655,8 +722,21 @@ function M.draw(ctx)
|
|||||||
ci_sprite_traced = true
|
ci_sprite_traced = true
|
||||||
end
|
end
|
||||||
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).
|
-- Inventory panel overlay (z-top: drawn after all world content).
|
||||||
panel.render()
|
panel.render()
|
||||||
|
-- Toast overlay on top of everything.
|
||||||
|
notify_display.render()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Engine lifecycle globals (engine looks up by name; module-table is bridged).
|
-- Engine lifecycle globals (engine looks up by name; module-table is bridged).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "vagrant-skeleton",
|
"id": "vagrant-skeleton",
|
||||||
"version": "0.9.1",
|
"version": "0.10.0",
|
||||||
"kind": "module",
|
"kind": "module",
|
||||||
"api": "^0.1",
|
"api": "^0.1",
|
||||||
"ci_frames": 60,
|
"ci_frames": 60,
|
||||||
@@ -19,6 +19,9 @@
|
|||||||
{"id": "lib-core.actor", "version": "0.1.0"},
|
{"id": "lib-core.actor", "version": "0.1.0"},
|
||||||
{"id": "lib-core.panel", "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.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.maps","version":"0.5.7"},
|
||||||
{"id": "lib-core.puppet", "version": "0.5.0"},
|
{"id": "lib-core.puppet", "version": "0.5.0"},
|
||||||
{"id": "lib-core.interaction", "version": "0.1.0"},
|
{"id": "lib-core.interaction", "version": "0.1.0"},
|
||||||
|
|||||||
Reference in New Issue
Block a user