diff --git a/README.md b/README.md index 091d76f..0b14620 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.18.0 +**Version:** 0.19.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.19.0:** **Role-based recipes.** Items carry a `form` role (shaft / cord / +> fiber / lump / blade / …); Stone-Age recipes now match by ROLE + material + +> weight + affordance, not by item id — so **any item that fills a role qualifies** +> (a haft = `{form="shaft"}` → stick OR branch). Verified: stick and branch both +> haft a hammer/axe; a log is rejected as a hammer head (not a lump); weight +> discriminates (a light stick can't be "broken", a heavy branch can't be +> "sharpened"). Template-id matching stays available (see rock_pick/rock_pile) — +> one of several modes, not the default. +> > **v0.18.0:** Stone-Age round-out (content pack) — `craft_stone_hammer` > (rock+stick+cordage → hammering tool), which now **gates `knap_sharp_stone`** > (was bare-handed); `chop_tree` (tree + chopping axe → 2 logs, closing the diff --git a/content/stone_age.lua b/content/stone_age.lua index 6d65ba7..9296d6b 100644 --- a/content/stone_age.lua +++ b/content/stone_age.lua @@ -1,145 +1,143 @@ -- ===================================================================== -- content/stone_age.lua — T1-modder content pack: Stone-Age items + recipes. -- --- PURE CONTENT. No engine wiring, no sprite/atlas plumbing — just the --- authoring API `c` passed in by the module (see init.lua's content facade): --- c.T{ id, name, desc, weight, vol, material={...}, affordance={...}, +-- PURE CONTENT via the authoring API `c` (see init.lua's content facade): +-- c.T{ id, name, desc, weight, vol, form=, material={...}, affordance={...}, -- atlas="tc"|"stone", uv="", scale? } -- define an item -- c.R{ ...crafting.define_recipe def... } -- define a recipe -- c.S{ template, x, y } -- place item in world --- `c.T` auto-registers the crafting-panel icon from the item's own sprite. -- --- A T1 modder adds Stone-Age content by editing THIS file only — never --- init.lua. Drop a sibling `content/.lua` + list it in init.lua's --- CONTENT_PACKS to add a new pack. +-- ROLE-BASED RECIPES (the intent): a recipe slot asks for the ROLE it needs, +-- and ANY item that fills that role qualifies — not a specific item id. Roles +-- are expressed as property-constraints (crafting v0.3 / ADR-0055): +-- * `form` — physical role: shaft / cord / fiber / lump / blade / … +-- * `composition.*`— material makeup (wood / stone / fiber …) +-- * `weight` — size discriminator (light stick vs heavy branch) +-- * `affordance.*` — functional capability of a TOOL (cutting/hammering/…) +-- e.g. a "haft" = { form="shaft" } → any stick OR branch OR pole works. -- --- Self-bootstrapping graph: --- rock --knap--> sharp_stone --+--(tool)--> saw_planks (log -> 4 planks) --- +--(head)--> stone_axe --- plant_fiber x3 --twist--> cordage --(binding)--> stone_axe --- stick --------------------------(haft)--------> stone_axe +-- Template-id matching ({ template="rock" }) is ALSO valid — it is just the +-- trivial one-property constraint — and is used where a recipe genuinely wants +-- one specific item (see rock_pick/rock_pile in init.lua). Role-based is the +-- default here; template-id is one of the options, not the norm. -- ===================================================================== return function(c) - -- ---- items ------------------------------------------------------- + -- ---- items (each tagged with its ROLE via `form`) ---------------- + -- NOTE: `rock` (form=lump) and `stick` (form=shaft) are BASE items defined + -- in init.lua; this pack's role-based recipes match them by form. -- World-gathered - c.T{ id = "log", name = "Log", - desc = "A heavy wooden log. Saw it into planks with a cutting edge.", - weight = 20, vol = 0.05, material = { wood = 1.0 }, + c.T{ id = "branch", name = "Branch", desc = "A heavy springy branch. Snap it into sticks.", + weight = 1.5, vol = 0.006, form = "shaft", material = { wood = 1.0 }, + atlas = "tc", uv = "log_pile1", scale = 0.85 } + + c.T{ id = "log", name = "Log", desc = "A heavy wooden log. Saw it into planks.", + weight = 20, vol = 0.05, form = "log", material = { wood = 1.0 }, atlas = "tc", uv = "log_pile1" } - c.T{ id = "sharp_stone", name = "Sharp Stone", - desc = "A knapped stone with a cutting edge. A tool — not consumed by sawing.", - weight = 1, vol = 0.0004, material = { stone = 1.0 }, - affordance = { cutting = true }, atlas = "tc", uv = "spade" } + c.T{ id = "tree", name = "Tree", desc = "A standing tree. Chop it down for logs.", + weight = 400, vol = 1.0, form = "tree", material = { wood = 1.0 }, + atlas = "tc", uv = "log_pile1", scale = 1.6 } - c.T{ id = "plant_fiber", name = "Plant Fiber", - desc = "A strand of tough plant fiber. Twist three into cordage.", - weight = 0.05, vol = 0.0002, material = { fiber = 1.0 }, + c.T{ id = "plant_fiber", name = "Plant Fiber", desc = "Tough plant fiber. Twist several into cordage.", + weight = 0.05, vol = 0.0002, form = "fiber", material = { fiber = 1.0 }, atlas = "tc", uv = "sack1", scale = 0.6 } - -- Crafted outputs - c.T{ id = "plank", name = "Plank", - desc = "A sawn wooden plank.", - weight = 2, vol = 0.004, material = { wood = 1.0 }, -- light: not re-sawable (<5) - atlas = "tc", uv = "bench1" } + -- Crafted + c.T{ id = "sharp_stone", name = "Sharp Stone", desc = "A knapped stone with a cutting edge.", + weight = 1, vol = 0.0004, form = "blade", material = { stone = 1.0 }, + affordance = { cutting = true }, atlas = "tc", uv = "spade" } - c.T{ id = "cordage", name = "Cordage", - desc = "A length of twisted plant-fiber cord.", - weight = 0.1, vol = 0.0003, material = { fiber = 1.0 }, + c.T{ id = "cordage", name = "Cordage", desc = "Twisted plant-fiber cord. A binding.", + weight = 0.1, vol = 0.0003, form = "cord", material = { fiber = 1.0 }, atlas = "tc", uv = "sack1" } - c.T{ id = "stone_axe", name = "Stone Axe", - desc = "A hafted stone axe. Chops wood.", - weight = 1.8, vol = 0.0012, + c.T{ id = "plank", name = "Plank", desc = "A sawn wooden plank.", + weight = 2, vol = 0.004, form = "plank", material = { wood = 1.0 }, + atlas = "tc", uv = "bench1" } + + c.T{ id = "stone_axe", name = "Stone Axe", desc = "A hafted stone axe. Chops wood.", + weight = 1.8, vol = 0.0012, form = "tool", material = { stone = 0.6, wood = 0.3, fiber = 0.1 }, affordance = { chopping = true }, atlas = "tc", uv = "spade" } - -- World-gathered (round-out) - c.T{ id = "branch", name = "Branch", - desc = "A long springy branch. Snap it into sticks.", - weight = 1.5, vol = 0.006, material = { wood = 1.0 }, - atlas = "tc", uv = "log_pile1", scale = 0.85 } - - c.T{ id = "tree", name = "Tree", - desc = "A standing tree. Chop it down for logs.", - weight = 400, vol = 1.0, material = { wood = 1.0 }, - atlas = "tc", uv = "log_pile1", scale = 1.6 } - - -- Crafted tools - c.T{ id = "stone_hammer", name = "Stone Hammer", - desc = "A stone lashed to a haft. Pounds and knaps.", - weight = 1.5, vol = 0.001, + c.T{ id = "stone_hammer", name = "Stone Hammer", desc = "A stone lashed to a haft. Pounds and knaps.", + weight = 1.5, vol = 0.001, form = "tool", material = { stone = 0.6, wood = 0.3, fiber = 0.1 }, affordance = { hammering = true }, atlas = "tc", uv = "spade" } - c.T{ id = "sharpened_stick", name = "Sharpened Stick", - desc = "A stick whittled to a point. A crude spear.", - weight = 0.3, vol = 0.0005, material = { wood = 1.0 }, + c.T{ id = "sharpened_stick", name = "Sharpened Stick", desc = "A stick whittled to a point. A crude spear.", + weight = 0.3, vol = 0.0005, form = "shaft", material = { wood = 1.0 }, affordance = { piercing = true }, atlas = "tc", uv = "log_pile1", scale = 0.9 } - -- ---- recipes ----------------------------------------------------- - -- saw_planks: input matched by MATERIAL (mostly wood AND heavy), a light - -- plank fails weight>5. Needs a cutting TOOL (not consumed). ×4 output. + -- ---- recipes (match by ROLE, not by item id) -------------------- + -- knap: any hard stone LUMP + a HAMMERING tool -> a sharp blade. + c.R{ id = "knap_sharp_stone", + inputs = { { match = { form = "lump" }, count = 1 } }, + tools = { { match = { ["affordance.hammering"] = true } } }, + outputs = { { template = "sharp_stone", count = 1 } }, + name = "Knap Sharp Stone", + description = "Strike a stone with a hammerstone to flake off a sharp edge." } + + -- cordage: three FIBERS (any fiber source) -> cord. + c.R{ id = "twist_cordage", + inputs = { { match = { form = "fiber" }, count = 3 } }, + outputs = { { template = "cordage", count = 1 } }, + name = "Twist Cordage", + description = "Twist three plant fibers into a length of cordage." } + + -- hammer: a LUMP head + a SHAFT haft + a CORD binding. + c.R{ id = "craft_stone_hammer", + inputs = { + { match = { form = "lump" }, count = 1 }, -- head + { match = { form = "shaft" }, count = 1 }, -- haft (stick OR branch) + { match = { form = "cord" }, count = 1 }, -- binding + }, + outputs = { { template = "stone_hammer", count = 1 } }, + name = "Haft Stone Hammer", + description = "Lash a stone to a haft for a pounding tool (enables knapping)." } + + -- axe: a BLADE head + a SHAFT haft + a CORD binding. + c.R{ id = "craft_stone_axe", + inputs = { + { match = { form = "blade" }, count = 1 }, -- head (a sharp stone) + { match = { form = "shaft" }, count = 1 }, -- haft + { match = { form = "cord" }, count = 1 }, -- binding + }, + outputs = { { template = "stone_axe", count = 1 } }, + name = "Haft Stone Axe", + description = "Bind a sharp stone to a haft with cordage." } + + -- break: a HEAVY shaft (branch, not a light stick) -> two sticks. + c.R{ id = "break_branch", + inputs = { { match = { form = "shaft", weight = ">1" }, count = 1 } }, + outputs = { { template = "stick", count = 2 } }, + name = "Break Branch", + description = "Snap a heavy branch over your knee into two sticks." } + + -- sharpen: a LIGHT shaft (a stick) + a CUTTING tool -> a pointed stick. + c.R{ id = "sharpen_stick", + inputs = { { match = { form = "shaft", weight = "<1" }, count = 1 } }, + tools = { { match = { ["affordance.cutting"] = true } } }, + outputs = { { template = "sharpened_stick", count = 1 } }, + name = "Sharpen Stick", + description = "Whittle a stick to a point with a cutting edge." } + + -- chop: a TREE + a CHOPPING tool -> two logs (closes axe->log->plank). + c.R{ id = "chop_tree", + inputs = { { match = { form = "tree" }, count = 1 } }, + tools = { { match = { ["affordance.chopping"] = true } } }, + outputs = { { template = "log", count = 2 } }, + name = "Chop Tree", + description = "Fell a tree with a chopping edge — yields two logs." } + + -- saw: any heavy WOOD (matched by material+weight, not form) + a CUTTING + -- tool -> four planks. A light plank (weight 2) is correctly not re-sawable. c.R{ 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)." } - - c.R{ id = "knap_sharp_stone", - inputs = { { template = "rock", count = 1 } }, - tools = { { match = { ["affordance.hammering"] = true } } }, - outputs = { { template = "sharp_stone", count = 1 } }, - name = "Knap Sharp Stone", - description = "Strike a rock with a hammerstone to flake off a sharp edge." } - - c.R{ 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." } - - c.R{ 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." } - - -- Round-out recipes - c.R{ id = "craft_stone_hammer", - inputs = { - { template = "rock", count = 1 }, - { template = "stick", count = 1 }, - { template = "cordage", count = 1 }, - }, - outputs = { { template = "stone_hammer", count = 1 } }, - name = "Haft Stone Hammer", - description = "Lash a stone to a stick for a pounding tool (enables knapping)." } - - c.R{ id = "break_branch", - inputs = { { template = "branch", count = 1 } }, - outputs = { { template = "stick", count = 2 } }, -- multi-output from one branch - name = "Break Branch", - description = "Snap a branch over your knee into two sticks." } - - c.R{ id = "chop_tree", - inputs = { { template = "tree", count = 1 } }, - tools = { { match = { ["affordance.chopping"] = true } } }, - outputs = { { template = "log", count = 2 } }, -- closes the loop: axe -> logs -> planks - name = "Chop Tree", - description = "Fell a tree with a chopping edge — yields two logs." } - - c.R{ id = "sharpen_stick", - inputs = { { template = "stick", count = 1 } }, - tools = { { match = { ["affordance.cutting"] = true } } }, - outputs = { { template = "sharpened_stick", count = 1 } }, - name = "Sharpen Stick", - description = "Whittle a stick to a point with a cutting edge." } + description = "Saw a heavy wooden log into four planks (needs a cutting edge)." } -- ---- world spawns (backpack sits at 350,400) --------------------- c.S{ template = "log", x = 110, y = 400 } diff --git a/init.lua b/init.lua index 26f2055..2f211ae 100644 --- a/init.lua +++ b/init.lua @@ -181,6 +181,7 @@ composition.define_template{ description = "A heavy stone. Could be useful for crafting.", weight = 2.5, -- kg (real units) volume = 0.001, -- L (real units) + form = "lump", -- role: a knappable/poundable stone -- Material composition (composition-model.md §4 — 0-1 platonic; -- 1.0 = pure stone). Phase E (property-driven recipes) reads this. ["composition.stone"] = 1.0, @@ -204,6 +205,7 @@ composition.define_template{ description = "A wooden stick. Useful for crafting tool handles.", weight = 0.3, -- kg volume = 0.0005, -- L + form = "shaft", -- role: a light rigid shaft (handle) ["composition.wood"] = 1.0, sprite_atlas = "", sprite_uv = {x = 0, y = 0, w = 1, h = 1}, @@ -642,6 +644,7 @@ function M.init(ctx) position = { x = 0, y = 0 }, } if a.scale then props.sprite_scale = a.scale * (d.scale or 1) end + if d.form then props.form = d.form end -- role tag: shaft / cord / fiber / lump / blade / … for m, v in pairs(d.material or {}) do props["composition." .. m] = v end for k, v in pairs(d.affordance or {}) do props["affordance." .. k] = v end composition.define_template{ id = d.id, properties = props, tags = { "renderable", "item" } } diff --git a/manifest.module b/manifest.module index 2cc7383..03b246d 100644 --- a/manifest.module +++ b/manifest.module @@ -1,6 +1,6 @@ { "id": "vagrant-skeleton", - "version": "0.18.0", + "version": "0.19.0", "kind": "module", "api": "^0.1", "ci_frames": 60,