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:
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" }
|
||||
|
||||
Reference in New Issue
Block a user