From 8994b24567b2d3cb18951c7f1f0c079a4a953b79 Mon Sep 17 00:00:00 2001 From: calic Date: Tue, 28 Jul 2026 10:42:21 +0000 Subject: [PATCH] =?UTF-8?q?feat(vagrant):=20v0.15.0=20stone-age=20slice=20?= =?UTF-8?q?=E2=80=94=20saw=5Fplanks=20property-constraint=20recipe=20(log?= =?UTF-8?q?=E2=86=924=20planks,=20cutting=20tool);=20crafting=20output-ali?= =?UTF-8?q?as=20for=20bw-compat=20icon=20resolvers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 +++++- init.lua | 111 ++++++++++++++++++++++++++++++++++++++++++++++++ manifest.module | 4 +- 3 files changed, 124 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9701a0f..5c87289 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,20 @@ 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.14.0 +**Version:** 0.15.0 **Module-ID:** vagrant-skeleton -**Requires:** lib-core.input >=0.4.0, lib-core.camera >=0.3.0, lib-core.render >=0.2.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.2.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.2.0, lib-core.crafting-display >=0.2.0, lib-asset.prototype-subterrain >=0.2.0, lib-asset.prototype-tc-basics >=0.1.0 +**Requires:** lib-core.input >=0.4.0, lib-core.camera >=0.3.0, lib-core.render >=0.2.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.2.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.3.0, lib-core.crafting-display >=0.2.0, lib-asset.prototype-subterrain >=0.2.0, lib-asset.prototype-tc-basics >=0.1.0 **Tags:** test-chamber, puppet, walking-sim, interaction +> **v0.15.0:** First consumer of `lib-core.crafting` v0.3.0 (ADR-0055). New +> stone-age slice: a **Log** (heavy wood item) + a **Sharp Stone** (cutting +> tool) spawn in the world, and the `saw_planks` recipe matches its input by +> **property-constraint** (`composition.wood > 0.5` AND `weight > 5`) instead +> of template-id, requires the sharp-stone as a **non-consumed tool** +> (`affordance.cutting`), and yields **4 planks** (multi-output). A light plank +> (weight 2) is correctly not re-sawable. Existing `rock_pick`/`rock_pile` +> recipes are unchanged (template-id hybrid bw-compat). +> > **v0.14.0:** Bed + Bench + Backpack visuals migrated from the proprietary > Subterrain world-atlas to TC_Basics sprites (`single_bed1`, `bench1`, > `sack1`). Bed + Bench promoted to composition-entities (renderable tag) — diff --git a/init.lua b/init.lua index 990dc69..c1489aa 100644 --- a/init.lua +++ b/init.lua @@ -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 diff --git a/manifest.module b/manifest.module index 1ca6b71..1c8fcf1 100644 --- a/manifest.module +++ b/manifest.module @@ -1,6 +1,6 @@ { "id": "vagrant-skeleton", - "version": "0.14.0", + "version": "0.15.0", "kind": "module", "api": "^0.1", "ci_frames": 60, @@ -26,7 +26,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.2.0"}, + {"id": "lib-core.crafting", "version": "0.3.0"}, {"id": "lib-core.crafting-display", "version": "0.3.0"}, {"id": "lib-asset.prototype-subterrain", "version": "0.2.0"}, {"id": "lib-asset.prototype-blob-geom", "version": "0.1.0"},