feat(vagrant): v0.15.0 stone-age slice — saw_planks property-constraint recipe (log→4 planks, cutting tool); crafting output-alias for bw-compat icon resolvers

This commit is contained in:
calic
2026-07-28 10:42:21 +00:00
parent 08413e9c29
commit 8994b24567
3 changed files with 124 additions and 4 deletions

111
init.lua
View File

@@ -212,6 +212,45 @@ composition.define_template{
tags = {"renderable", "item"},
}
-- Stone-Age crafting slice (crafting v0.3 / ADR-0055 consumer). `log` is a
-- heavy wood item; the `saw_planks` recipe matches it by property-constraint
-- (composition.wood > 0.5 AND weight > 5) rather than by template-id, so a
-- light `plank` (weight 2) is correctly NOT sawable into more planks.
composition.define_template{
id = "log",
properties = {
stack_mode = "individual",
name = "Log",
description = "A heavy wooden log. Saw it into planks with a cutting edge.",
weight = 20, -- kg — heavy (> saw threshold)
volume = 0.05,
["composition.wood"] = 1.0,
sprite_atlas = "",
sprite_uv = {x = 0, y = 0, w = 1, h = 1},
position = {x = 0, y = 0},
},
tags = {"renderable", "item"},
}
-- `sharp_stone` is a NON-consumed cutting TOOL: it carries the derived
-- affordance `affordance.cutting`, which `saw_planks` presence-checks.
composition.define_template{
id = "sharp_stone",
properties = {
stack_mode = "individual",
name = "Sharp Stone",
description = "A knapped stone with a cutting edge. Used as a tool, not consumed.",
weight = 1,
volume = 0.0004,
["composition.stone"] = 1.0,
["affordance.cutting"] = true, -- tool affordance (crafting v0.3)
sprite_atlas = "",
sprite_uv = {x = 0, y = 0, w = 1, h = 1},
position = {x = 0, y = 0},
},
tags = {"renderable", "item"},
}
-- NOTE: Crafted-output templates (rock_pick, rock_pile) are defined inside
-- M.init rather than at top-level, because their sprite_atlas defaults need
-- the resolved blob_rect_stone PNG path. Crafting calls composition.create
@@ -657,6 +696,28 @@ function M.init(ctx)
tags = {"renderable", "item"},
}
-- plank: crafted output of `saw_planks` (crafting v0.3, ×4 multi-output).
-- Placeholder visual reuses the TC_Basics bench sprite until a dedicated
-- plank decal lands.
local plank_uv = tc_sprites.bench1
composition.define_template{
id = "plank",
properties = {
stack_mode = "individual",
name = "Plank",
description = "A sawn wooden plank.",
weight = 2, -- light: NOT re-sawable (< 5)
volume = 0.004,
["composition.wood"] = 1.0,
sprite_atlas = tc_atlas_png,
sprite_uv = {x = plank_uv.x, y = plank_uv.y,
w = plank_uv.w, h = plank_uv.h},
sprite_scale = tc_scale,
position = {x = 0, y = 0},
},
tags = {"renderable", "item"},
}
-- Workbench-Template (Phase D v0.2). Container-kind=list so it can
-- accept items via inventory.add (crafting output landing spot).
-- Placeholder visual re-uses the blob_rect_stone atlas with
@@ -724,6 +785,36 @@ function M.init(ctx)
register_pickup(state.stick)
if CI_FRAME_PERF then engine.print("vagrant: stick_spawned") end
-- Stone-Age slice: a Log (saw-able) + a Sharp Stone (cutting tool) in the
-- world, so `saw_planks` (crafting v0.3 property-constraint recipe) is
-- demonstrable end-to-end: pick both up, craft at the workbench → 4 planks.
local LOG_X, LOG_Y = BACKPACK_X - 240, BACKPACK_Y
local log_uv = tc_sprites.log_pile1
state.log = composition.create{
template = "log",
properties = {
sprite_atlas = tc_atlas_png,
sprite_uv = {x = log_uv.x, y = log_uv.y, w = log_uv.w, h = log_uv.h},
sprite_scale = tc_scale,
position = {x = LOG_X, y = LOG_Y},
},
}
register_pickup(state.log)
local SAW_X, SAW_Y = BACKPACK_X - 240, BACKPACK_Y - 70
local saw_uv = tc_sprites.spade
state.sharp_stone = composition.create{
template = "sharp_stone",
properties = {
sprite_atlas = tc_atlas_png,
sprite_uv = {x = saw_uv.x, y = saw_uv.y, w = saw_uv.w, h = saw_uv.h},
sprite_scale = tc_scale,
position = {x = SAW_X, y = SAW_Y},
},
}
register_pickup(state.sharp_stone)
if CI_FRAME_PERF then engine.print("vagrant: stoneage_spawned") end
-- Workbench spawn (Phase D v0.2). Placed at (350, 380) — 20px above
-- the backpack (at 350, 400) so they are visually distinct but still
-- on the visible-on-spawn area. register_interaction_use registers a
@@ -756,6 +847,25 @@ function M.init(ctx)
name = "Rock Pile",
description = "Stack rocks for storage.",
}
-- saw_planks: first property-constraint recipe (crafting v0.3 / ADR-0055).
-- Input matched by MATERIAL, not template-id: any item that is mostly wood
-- and heavy enough (a log qualifies, a plank does not). Requires a cutting
-- TOOL (sharp_stone) that is checked for presence but NOT consumed.
-- Multi-output: one log yields four planks.
crafting.define_recipe{
id = "saw_planks",
inputs = {
{match = {["composition.wood"] = ">0.5", weight = ">5"}, count = 1},
},
tools = {
{match = {["affordance.cutting"] = true}},
},
outputs = {
{template = "plank", count = 4},
},
name = "Saw Planks",
description = "Saw a wooden log into four planks (needs a cutting edge).",
}
-- v0.14.0: per-recipe icon resolver for the Crafting + Workbench-Crafting
-- widgets. Maps recipe.output.template → {atlas, uv} for the icon
@@ -766,6 +876,7 @@ function M.init(ctx)
local craft_icons = {
rock_pick = { atlas = tc_atlas_png, uv = tc_sprites.spade },
rock_pile = { atlas = stone_atlas_png, uv = stone_uvs.slot_13_solid },
plank = { atlas = tc_atlas_png, uv = tc_sprites.bench1 },
}
local function craft_icon_for(recipe)
if recipe and recipe.output and recipe.output.template then