feat: v0.11.0 crafting integration

Stick pickup neben rock spawnt; 2 Recipes (rock_pick, rock_pile);
C-key oeffnet Crafting-Panel via lib-core.crafting-display.
Craft + Inspect Actions konsumieren notify-Channels fuer Erfolg +
Detail-Modal.

Deps: +crafting 0.1.0, +crafting-display 0.1.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Axel Meyer
2026-06-14 13:57:19 +02:00
parent 0658e87066
commit 31333111c0
3 changed files with 220 additions and 9 deletions

View File

@@ -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.10.0 **Version:** 0.11.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-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 **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.3.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-core.crafting >=0.1.0, lib-core.crafting-display >=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
@@ -23,25 +23,31 @@ demo: pick up a rock into a backpack container, drop it back into the world.
- **W A S D** — direct movement (4-way, normalized for diagonals) - **W A S D** — direct movement (4-way, normalized for diagonals)
- **Shift + WASD** — sprint (2x player speed + 2x walk-anim cadence) - **Shift + WASD** — sprint (2x player speed + 2x walk-anim cadence)
- **Mouse** — body + head look-at targets (same position, different smoothing) - **Mouse** — body + head look-at targets (same position, different smoothing)
- **E** — interact: if within range of the rock, picks it up into the backpack - **E** — interact: if within range of the rock or stick, picks it up into the backpack
- **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 - **C** — toggle Crafting panel (lists known recipes; availability dimmed by inventory)
- **Right-Click on item row** (inventory panel): context menu with **Drop** / **Inspect**
- **Right-Click on recipe row** (crafting panel): context menu with **Craft** / **Inspect**
- **L** — toggle game-log (chronological list of all events) - **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) - **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 - **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 - **Drop (right-click on item → Drop, or Q for LIFO quick-drop)** — toast at top-right + log entry
- **Craft (right-click on recipe → Craft)** — consumes inputs from backpack, adds output, posts a success/failure toast
- **ESC** — return to launcher - **ESC** — return to launcher
## Demo Walkthrough ## Demo Walkthrough
The world contains three notable entities at startup: The world contains four notable entities at startup:
1. **Player** — spawns at (320, 240), controlled via WASD + mouse. 1. **Player** — spawns at (320, 240), controlled via WASD + mouse.
2. **Backpack** — a visible sprite at (350, 400). Acts as a container (kind="list"). 2. **Backpack** — a visible sprite at (350, 400). Acts as a container (kind="list").
Not directly interactable; it receives items via inventory.add. Not directly interactable; it receives items via inventory.add.
3. **Rock** — a standalone stone tile at (270, 400), 80px to the left of the backpack. 3. **Rock** — a standalone stone tile at (270, 400), 80px to the left of the backpack.
Tagged "renderable" + "item"; starts visible on the ground. Tagged "renderable" + "item"; starts visible on the ground.
4. **Stick** — a placeholder pickup-item at (190, 400), 160px to the left of the backpack.
Re-uses the `slot_04_straight` blob_rect_stone tile for its visual (Phase C-Q10
pragmatic resolution — echtes Stick-Sprite folgt mit dem ersten atlas-baker-decals-Slice).
**Pickup flow:** **Pickup flow:**
- Walk toward the rock (it sits to the left of the backpack). - Walk toward the rock (it sits to the left of the backpack).
@@ -115,6 +121,29 @@ Only one rock is spawned per session.
## CHANGELOG ## CHANGELOG
### v0.11.0 — crafting integration
- **New: stick pickup-item** neben rock am Boden (Position 190,400 — 160px links
vom Backpack). Re-uses `slot_04_straight` aus blob_rect_stone als Visual-
Placeholder (Phase C-Q10).
- **New: 2 Recipes** via `lib-core.crafting`:
- **Stone Pick** = 1 rock + 1 stick → `rock_pick`
- **Rock Pile** = 3 rocks → `rock_pile`
- **New: C-Taste** öffnet ein Crafting-Panel via `lib-core.crafting-display`.
Recipe-Zeilen sind dimmed, wenn inputs im Backpack fehlen.
- **New: Right-Click auf Recipe-Row** → Context-Menu mit **Craft** + **Inspect**.
Craft konsumiert inputs aus dem Backpack und legt das Output-Template als
neues item-Entity in den Backpack. Inspect routet recipe details (name,
description, inputs, output) zum notify-detail-channel.
- **New: 3 item-templates** — `stick`, `rock_pick`, `rock_pile` — jedes mit
player-facing properties (name, description, weight, volume,
composition.<material>). Placeholder atlas-slots: stick=slot_04_straight,
rock_pick=slot_05_tee_open, rock_pile=slot_13_solid.
- **Deps**: +lib-core.crafting 0.1.0, +lib-core.crafting-display 0.1.0.
composition bleibt auf 0.3.0 (gebumpt in Phase C.0).
- **Notify events-Channel**: filter um `"craft"` erweitert, damit Craft-
Erfolgs- und Fehler-Toasts im events-channel landen.
### v0.10.0 — notification system integration ### v0.10.0 — notification system integration
- **Inspect modal**: Inspect action replaced with `notify.post{tags={"inspect"}, ...}` routing - **Inspect modal**: Inspect action replaced with `notify.post{tags={"inspect"}, ...}` routing
to a modal detail-panel (`notify-display` detail-channel). Item name, description, weight, to a modal detail-panel (`notify-display` detail-channel). Item name, description, weight,

184
init.lua
View File

@@ -1,4 +1,4 @@
-- sporel-module-vagrant-skeleton v0.10.0 -- sporel-module-vagrant-skeleton v0.11.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,
@@ -41,6 +41,14 @@
-- opens the game-log panel listing all events chronologically. -- opens the game-log panel listing all events chronologically.
-- engine.print mirror remains enabled — dev tools that parse the log -- engine.print mirror remains enabled — dev tools that parse the log
-- continue to work. -- continue to work.
--
-- v0.11.0: Crafting system wired in (lib-core.crafting +
-- lib-core.crafting-display). Stick spawns as a second pickup-item
-- next to the rock. C-key opens a Crafting panel listing the two
-- known recipes (rock_pick = 1 rock + 1 stick, rock_pile = 3 rocks).
-- Right-click a recipe row → Craft + Inspect actions; Craft consumes
-- inputs from the backpack and adds the output; Inspect routes
-- recipe details to a notify-display detail-channel.
local input = require("lib-core.input") local input = require("lib-core.input")
local camera = require("lib-core.camera") local camera = require("lib-core.camera")
@@ -56,6 +64,8 @@ local inv_display = require("lib-core.inventory-list-display")
local notify = require("lib-core.notify") local notify = require("lib-core.notify")
local notify_display = require("lib-core.notify-display") local notify_display = require("lib-core.notify-display")
local world_overlay = require("lib-core.world-overlay") local world_overlay = require("lib-core.world-overlay")
local crafting = require("lib-core.crafting")
local crafting_display = require("lib-core.crafting-display")
local M = {} local M = {}
@@ -116,6 +126,65 @@ composition.define_template{
tags = {"renderable", "item"}, tags = {"renderable", "item"},
} }
-- Stick-Template: placeholder pickup-item that joins rock in the world.
-- Re-uses the blob_rect_stone atlas for visual placeholder (per Phase C-Q10
-- pragmatic resolution — echtes Stick-Sprite kommt mit dem ersten
-- atlas-baker-decals-Slice). slot_04_straight reads as a long horizontal
-- bar — visually distinct from the rock's slot_00_isolated blob.
composition.define_template{
id = "stick",
properties = {
stack_mode = "individual",
name = "Stick",
description = "A wooden stick. Useful for crafting tool handles.",
weight = 0.3, -- kg
volume = 0.0005, -- L
["composition.wood"] = 1.0,
sprite_atlas = "",
sprite_uv = {x = 0, y = 0, w = 1, h = 1},
position = {x = 0, y = 0},
},
tags = {"renderable", "item"},
}
-- Stone-Pick-Template: output of rock_pick recipe (1 rock + 1 stick).
-- Lives only in the backpack post-craft in v0.11 (no world-drop path yet
-- via the Crafting-Panel). slot_05_tee_open reads as a tool-shaped tile.
composition.define_template{
id = "rock_pick",
properties = {
stack_mode = "individual",
name = "Stone Pick",
description = "A crude pick for mining stone.",
weight = 2.8, -- rock + stick (real units)
volume = 0.0015,
["composition.stone"] = 0.9,
["composition.wood"] = 0.1,
sprite_atlas = "",
sprite_uv = {x = 0, y = 0, w = 1, h = 1},
position = {x = 0, y = 0},
},
tags = {"renderable", "item"},
}
-- Rock-Pile-Template: output of 3×rock recipe. slot_13_solid reads as
-- a dense cluster tile — visually distinct from a single rock.
composition.define_template{
id = "rock_pile",
properties = {
stack_mode = "individual",
name = "Rock Pile",
description = "A stack of rocks bundled together for storage.",
weight = 7.5, -- 3x rock
volume = 0.003,
["composition.stone"] = 1.0,
sprite_atlas = "",
sprite_uv = {x = 0, y = 0, w = 1, h = 1},
position = {x = 0, y = 0},
},
tags = {"renderable", "item"},
}
-- Player-Actor-Template (Phase A.6). Vagrant's player is an actor whose -- Player-Actor-Template (Phase A.6). Vagrant's player is an actor whose
-- position drives the puppet (sync each frame). NOT tagged renderable — -- position drives the puppet (sync each frame). NOT tagged renderable —
-- the puppet handles rendering, render.draw_entities doesn't see this -- the puppet handles rendering, render.draw_entities doesn't see this
@@ -157,6 +226,8 @@ local state = {
furniture_uvs = nil, -- name -> { x, y, w, h } from atlas JSON furniture_uvs = nil, -- name -> { x, y, w, h } from atlas JSON
backpack = nil, -- composition-entity, created in M.init after atlas load backpack = nil, -- composition-entity, created in M.init after atlas load
rock = nil, -- composition-entity (item), created in M.init after blob_rect_stone load rock = nil, -- composition-entity (item), created in M.init after blob_rect_stone load
stick = nil, -- composition-entity (item), placeholder for crafting demo
crafting_widget = nil, -- crafting-display widget handle
-- Subterrain-style controller state: -- Subterrain-style controller state:
walk_anim_current = nil, -- "walk_fwd_lower" / "walk_back_lower" / etc. (nil = idle_lower) walk_anim_current = nil, -- "walk_fwd_lower" / "walk_back_lower" / etc. (nil = idle_lower)
lower_control_rot_deg = 0, lower_control_rot_deg = 0,
@@ -443,6 +514,45 @@ function M.init(ctx)
register_pickup(state.rock) register_pickup(state.rock)
if CI_FRAME_PERF then engine.print("vagrant: rock_spawned") end if CI_FRAME_PERF then engine.print("vagrant: rock_spawned") end
-- Stick spawn 80px to the right of the rock (160px left of the backpack
-- on the same Y as rock + backpack). Placeholder visual: slot_04_straight
-- from blob_rect_stone reads as a long horizontal bar (Phase C-Q10 atlas
-- re-use; echtes Stick-Sprite folgt mit atlas-baker-decals).
local STICK_X, STICK_Y = BACKPACK_X - 160, BACKPACK_Y
local stick_uv = stone_uvs["slot_04_straight"]
state.stick = composition.create{
template = "stick",
properties = {
sprite_atlas = stone_atlas_png,
sprite_uv = {x = stick_uv.x, y = stick_uv.y,
w = stick_uv.w, h = stick_uv.h},
position = {x = STICK_X, y = STICK_Y},
},
}
register_pickup(state.stick)
if CI_FRAME_PERF then engine.print("vagrant: stick_spawned") end
-- ====== Crafting Recipes ======
crafting.define_recipe{
id = "rock_pick",
inputs = {
{template = "rock", count = 1},
{template = "stick", count = 1},
},
output = {template = "rock_pick", count = 1},
name = "Stone Pick",
description = "A pick for mining stone.",
}
crafting.define_recipe{
id = "rock_pile",
inputs = {
{template = "rock", count = 3},
},
output = {template = "rock_pile", count = 1},
name = "Rock Pile",
description = "Stack rocks for storage.",
}
-- Inventory-UI Setup -- Inventory-UI Setup
panel.set_theme{ panel_width_frac = 0.4, font_size_title = 20 } panel.set_theme{ panel_width_frac = 0.4, font_size_title = 20 }
@@ -498,12 +608,82 @@ function M.init(ctx)
panel.register("backpack", backpack_widget) panel.register("backpack", backpack_widget)
panel.bind_default_trigger("tab", "backpack") panel.bind_default_trigger("tab", "backpack")
-- ====== Crafting Widget ======
state.crafting_widget = crafting_display.create(state.backpack, {
title = "Crafting",
widget_id = "crafting",
pause_on_open = false,
ctx_factory = function()
return { actor = state.actor }
end,
})
-- Craft action: invokes crafting.craft on the backpack, posts a
-- notify-toast on success or a warn-toast on missing inputs.
crafting_display.register_action(state.crafting_widget, "Craft",
function(recipe_id, c)
local r = crafting.craft(recipe_id, c.container,
{ actor = state.actor })
if r.ok then
local recipe = crafting.get_recipe(recipe_id)
notify.post{
tags = {"craft", "event"},
text = "Crafted " .. (recipe.name or recipe_id),
severity = "info",
}
if CI_FRAME_PERF then
engine.print("vagrant: event=craft_ok recipe=" .. recipe_id)
end
else
local msg
if r.error == "missing_inputs" then
msg = "Missing inputs"
else
msg = "Cannot craft: " .. tostring(r.error)
end
notify.warn({"craft", "event"}, msg)
if CI_FRAME_PERF then
engine.print("vagrant: event=craft_fail recipe=" .. recipe_id
.. " error=" .. tostring(r.error))
end
end
c.close_menu()
end)
-- Inspect action: routes recipe details to the notify detail-channel.
crafting_display.register_action(state.crafting_widget, "Inspect",
function(recipe_id, c)
local recipe = crafting.get_recipe(recipe_id)
local lines = {
recipe.name or recipe.id,
"",
recipe.description or "",
"",
"Inputs:",
}
for _, inp in ipairs(recipe.inputs) do
lines[#lines + 1] = " " .. inp.template .. " \xc3\x97 " .. inp.count
end
lines[#lines + 1] = ""
lines[#lines + 1] = "Output: " .. recipe.output.template
.. " \xc3\x97 " .. recipe.output.count
notify.post{
tags = {"craft", "inspect", "detail"},
text = table.concat(lines, "\n"),
severity = "info",
}
c.close_menu()
end)
panel.register("crafting", state.crafting_widget)
panel.bind_default_trigger("c", "crafting")
-- ====== Notification System Setup ====== -- ====== Notification System Setup ======
-- Channels (created BEFORE display attach + widget register) -- Channels (created BEFORE display attach + widget register)
notify.create_channel{ id = "inspect", filter = {"inspect"}, notify.create_channel{ id = "inspect", filter = {"inspect"},
display_mode = "detail" } display_mode = "detail" }
notify.create_channel{ id = "events", filter = {"pickup", "drop", "crafting"}, notify.create_channel{ id = "events", filter = {"pickup", "drop", "crafting", "craft"},
display_mode = "toast" } display_mode = "toast" }
notify.create_channel{ id = "log", filter = {}, -- catch-all notify.create_channel{ id = "log", filter = {}, -- catch-all
display_mode = "log" } display_mode = "log" }

View File

@@ -1,6 +1,6 @@
{ {
"id": "vagrant-skeleton", "id": "vagrant-skeleton",
"version": "0.10.0", "version": "0.11.0",
"kind": "module", "kind": "module",
"api": "^0.1", "api": "^0.1",
"ci_frames": 60, "ci_frames": 60,
@@ -26,6 +26,8 @@
{"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"},
{"id": "lib-core.inventory-list", "version": "0.1.0"}, {"id": "lib-core.inventory-list", "version": "0.1.0"},
{"id": "lib-core.crafting", "version": "0.1.0"},
{"id": "lib-core.crafting-display", "version": "0.1.0"},
{"id": "lib-asset.prototype-subterrain", "version": "0.2.0"}, {"id": "lib-asset.prototype-subterrain", "version": "0.2.0"},
{"id": "lib-asset.prototype-blob-geom", "version": "0.1.0"} {"id": "lib-asset.prototype-blob-geom", "version": "0.1.0"}
] ]