From ff9d50937d05c58ab397fb36ce97c0f84e579110 Mon Sep 17 00:00:00 2001 From: calic Date: Tue, 28 Jul 2026 11:38:23 +0000 Subject: [PATCH] =?UTF-8?q?feat(vagrant):=20v0.16.0=20stone-age=20tech=20c?= =?UTF-8?q?hain=20(knap/twist/haft=20=E2=86=92=20stone=5Faxe);=20fix=20cra?= =?UTF-8?q?fting=20manifest.lib=20to=200.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 ++++- init.lua | 115 ++++++++++++++++++++++++++++++++++++++++++++++-- manifest.module | 2 +- 3 files changed, 122 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5c87289..bb94359 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,19 @@ 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.15.0 +**Version:** 0.16.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.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.16.0:** Stone-Age tech chain — a self-bootstrapping crafting graph on +> the v0.3 API. `knap_sharp_stone` (rock → sharp_stone, bare-handed) yields a +> cutting tool that feeds BOTH `saw_planks` (as the non-consumed tool) AND +> `craft_stone_axe`. `twist_cordage` (3× plant_fiber → cordage) + the axe haft +> (`sharp_stone + stick + cordage → stone_axe`, carries `affordance.chopping`). +> Three plant fibers spawn in the world. Validated headless end-to-end incl. the +> knap→saw and knap→haft connections. +> > **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 diff --git a/init.lua b/init.lua index c1489aa..0e24735 100644 --- a/init.lua +++ b/init.lua @@ -251,6 +251,23 @@ composition.define_template{ tags = {"renderable", "item"}, } +-- `plant_fiber` is a light gatherable; three twist into cordage (binding). +composition.define_template{ + id = "plant_fiber", + properties = { + stack_mode = "individual", + name = "Plant Fiber", + description = "A strand of tough plant fiber. Twist several into cordage.", + weight = 0.05, + volume = 0.0002, + ["composition.fiber"] = 1.0, + 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 @@ -718,6 +735,48 @@ function M.init(ctx) tags = {"renderable", "item"}, } + -- cordage: crafted from 3 plant_fiber; a binding material for hafting. + local cordage_uv = tc_sprites.sack1 + composition.define_template{ + id = "cordage", + properties = { + stack_mode = "individual", + name = "Cordage", + description = "A length of twisted plant-fiber cord.", + weight = 0.1, + volume = 0.0003, + ["composition.fiber"] = 1.0, + sprite_atlas = tc_atlas_png, + sprite_uv = {x = cordage_uv.x, y = cordage_uv.y, w = cordage_uv.w, h = cordage_uv.h}, + sprite_scale = tc_scale, + position = {x = 0, y = 0}, + }, + tags = {"renderable", "item"}, + } + + -- stone_axe: hafted tool (sharp_stone head + stick haft + cordage binding). + -- Carries affordance.chopping for future wood-felling recipes. + local axe_uv = tc_sprites.spade + composition.define_template{ + id = "stone_axe", + properties = { + stack_mode = "individual", + name = "Stone Axe", + description = "A hafted stone axe. Chops wood.", + weight = 1.8, + volume = 0.0012, + ["composition.stone"] = 0.6, + ["composition.wood"] = 0.3, + ["composition.fiber"] = 0.1, + ["affordance.chopping"] = true, + sprite_atlas = tc_atlas_png, + sprite_uv = {x = axe_uv.x, y = axe_uv.y, w = axe_uv.w, h = axe_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 @@ -813,6 +872,23 @@ function M.init(ctx) }, } register_pickup(state.sharp_stone) + + -- Three plant fibers (twist into cordage → haft a stone axe). + state.fibers = {} + local fiber_uv = tc_sprites.sack1 + for i = 1, 3 do + local f = composition.create{ + template = "plant_fiber", + properties = { + sprite_atlas = tc_atlas_png, + sprite_uv = {x = fiber_uv.x, y = fiber_uv.y, w = fiber_uv.w, h = fiber_uv.h}, + sprite_scale = tc_scale * 0.6, + position = {x = LOG_X - 60, y = LOG_Y - 100 + (i - 1) * 28}, + }, + } + register_pickup(f) + state.fibers[i] = f + end if CI_FRAME_PERF then engine.print("vagrant: stoneage_spawned") end -- Workbench spawn (Phase D v0.2). Placed at (350, 380) — 20px above @@ -866,6 +942,36 @@ function M.init(ctx) name = "Saw Planks", description = "Saw a wooden log into four planks (needs a cutting edge).", } + -- ── Stone-Age tech chain (crafting v0.3) ────────────────────────────── + -- Self-bootstrapping graph: knap a rock into a sharp_stone (bare hands), + -- which is then the cutting TOOL for saw_planks AND the head for a + -- stone_axe. Twist plant fibers into cordage, then haft axe = head + stick + -- + cordage. Tier-0 steps need no tool (the bootstrap entry points). + crafting.define_recipe{ + id = "knap_sharp_stone", + inputs = { {template = "rock", count = 1} }, + outputs = { {template = "sharp_stone", count = 1} }, + name = "Knap Sharp Stone", + description = "Strike a rock to flake off a sharp-edged stone.", + } + crafting.define_recipe{ + id = "twist_cordage", + inputs = { {template = "plant_fiber", count = 3} }, + outputs = { {template = "cordage", count = 1} }, + name = "Twist Cordage", + description = "Twist three plant fibers into a length of cordage.", + } + crafting.define_recipe{ + id = "craft_stone_axe", + inputs = { + {template = "sharp_stone", count = 1}, -- head (consumed) + {template = "stick", count = 1}, -- haft + {template = "cordage", count = 1}, -- binding + }, + outputs = { {template = "stone_axe", count = 1} }, + name = "Haft Stone Axe", + description = "Bind a sharp stone to a stick with cordage.", + } -- v0.14.0: per-recipe icon resolver for the Crafting + Workbench-Crafting -- widgets. Maps recipe.output.template → {atlas, uv} for the icon @@ -874,9 +980,12 @@ function M.init(ctx) -- domain-free), so the module-side that DEFINES the templates is the -- canonical place to know each output's sprite. 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 }, + 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 }, + sharp_stone = { atlas = stone_atlas_png, uv = stone_uvs.slot_13_solid }, + cordage = { atlas = tc_atlas_png, uv = tc_sprites.sack1 }, + stone_axe = { atlas = tc_atlas_png, uv = tc_sprites.spade }, } 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 1c8fcf1..79fb238 100644 --- a/manifest.module +++ b/manifest.module @@ -1,6 +1,6 @@ { "id": "vagrant-skeleton", - "version": "0.15.0", + "version": "0.16.0", "kind": "module", "api": "^0.1", "ci_frames": 60,