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:
39
README.md
39
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.10.0
|
||||
**Version:** 0.11.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-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
|
||||
|
||||
## 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)
|
||||
- **Shift + WASD** — sprint (2x player speed + 2x walk-anim cadence)
|
||||
- **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)
|
||||
- **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)
|
||||
- **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
|
||||
- **Craft (right-click on recipe → Craft)** — consumes inputs from backpack, adds output, posts a success/failure toast
|
||||
- **ESC** — return to launcher
|
||||
|
||||
## 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.
|
||||
2. **Backpack** — a visible sprite at (350, 400). Acts as a container (kind="list").
|
||||
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.
|
||||
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:**
|
||||
- Walk toward the rock (it sits to the left of the backpack).
|
||||
@@ -115,6 +121,29 @@ Only one rock is spawned per session.
|
||||
|
||||
## 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
|
||||
- **Inspect modal**: Inspect action replaced with `notify.post{tags={"inspect"}, ...}` routing
|
||||
to a modal detail-panel (`notify-display` detail-channel). Item name, description, weight,
|
||||
|
||||
184
init.lua
184
init.lua
@@ -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 +
|
||||
-- hardcoded furniture. Subterrain-style puppet control model:
|
||||
-- multi-look-target (aim/soft), slerp body rotation, hip-static legs,
|
||||
@@ -41,6 +41,14 @@
|
||||
-- opens the game-log panel listing all events chronologically.
|
||||
-- engine.print mirror remains enabled — dev tools that parse the log
|
||||
-- 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 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_display = require("lib-core.notify-display")
|
||||
local world_overlay = require("lib-core.world-overlay")
|
||||
local crafting = require("lib-core.crafting")
|
||||
local crafting_display = require("lib-core.crafting-display")
|
||||
|
||||
local M = {}
|
||||
|
||||
@@ -116,6 +126,65 @@ composition.define_template{
|
||||
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
|
||||
-- position drives the puppet (sync each frame). NOT tagged renderable —
|
||||
-- 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
|
||||
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
|
||||
stick = nil, -- composition-entity (item), placeholder for crafting demo
|
||||
crafting_widget = nil, -- crafting-display widget handle
|
||||
-- Subterrain-style controller state:
|
||||
walk_anim_current = nil, -- "walk_fwd_lower" / "walk_back_lower" / etc. (nil = idle_lower)
|
||||
lower_control_rot_deg = 0,
|
||||
@@ -443,6 +514,45 @@ function M.init(ctx)
|
||||
register_pickup(state.rock)
|
||||
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
|
||||
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.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 ======
|
||||
|
||||
-- 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"},
|
||||
notify.create_channel{ id = "events", filter = {"pickup", "drop", "crafting", "craft"},
|
||||
display_mode = "toast" }
|
||||
notify.create_channel{ id = "log", filter = {}, -- catch-all
|
||||
display_mode = "log" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "vagrant-skeleton",
|
||||
"version": "0.10.0",
|
||||
"version": "0.11.0",
|
||||
"kind": "module",
|
||||
"api": "^0.1",
|
||||
"ci_frames": 60,
|
||||
@@ -22,10 +22,12 @@
|
||||
{"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.interaction", "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-blob-geom", "version": "0.1.0"}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user