Compare commits

27 Commits

Author SHA1 Message Date
calic
d862a250c4 crafting v0.5.0 + composition v0.4.0: Quality & Condition (Phase J)
lib-core.composition v0.4.0: un-defer quality/condition as inert numeric
properties (loud-error removed); composition stores them without semantics.

lib-core.crafting v0.5.0: quality-block (multi-contributor product-quality
formula: skill-band with min=requires-floor + named ingredient/tool qualities),
optional slot name, tool wear_per_use (condition decrement + wear report).
affordance stays boolean. Additive to v0.4.0.

vagrant-skeleton v0.22.0: branch quality-band RNG at spawn; stone_hammer
condition=1.0; knap+axe quality-blocks; hammer wears out. Headless-verified 20/20.

Design: meta/docs/design/2026-08-03-crafting-quality-condition-design.md.
Docs synced: crafting-model.md, composition-model.md, libraries.md, READMEs.
2026-08-03 09:14:56 +00:00
calic
f93fef5990 crafting: actor-precondition (requires) + reward (grants) — Skills half of Phase H (ADR-0056)
lib-core.crafting v0.4.0: two optional domain-free recipe fields.
- `requires`: a match-table evaluated against ctx.actor (not container items) — a hard gate. Failing -> {ok=false, error="requires_unmet", unmet={keys}}; fail-closed when ctx.actor is nil. Same predicate machinery as slots; distinct from is_known (discovery vs lock).
- `grants`: property->number reward, returned as `granted` from craft(); the consuming MODULE applies it to the actor (crafting stays container-scoped). Crafting knows no 'skill' vocabulary (ADR-0001).

vagrant-skeleton v0.21.0: player-actor carries skill.knapping (entity property, Phase-A lesson); knap_sharp_stone grants knapping XP, craft_stone_axe requires knapping>=3; module applies granted; skill-locked recipes show 'Skill too low'. Gate->earn->unlock loop headless-verified (axe locked @0 requires_unmet -> 3 knaps -> unlocks).

Quality-band reserved (Phase J): the requires threshold = the future quality floor (no re-authoring). Docs synced: ADR-0056, crafting-model.md (v0.4.0 + Actor-Precondition section), libraries.md, crafting README, vagrant README.
2026-07-31 10:00:27 +00:00
calic
b28afd5290 crafting: retire form, unify on affordance capability axis (ADR-0055 amendment)
Recipe roles now match on a single `affordance.*` axis, required by both consumed `inputs` and non-consumed `tools` (the inputs/tools split carries consumption, not a separate axis). `form` — which conflated geometry, identity and capability — is removed. Its functional members folded into affordances (blade->cutting, shaft->leverage, plus knappable/spinnable/binding); identity members became template-id (chop_tree) or material+weight (log, hammer head).

vagrant-skeleton v0.20.0; no crafting/composition lib bump (they never knew `form`). Verified headless via the tool-harness: 16/16 checks pass — role-flexibility (stick OR branch hafts), weight discrimination, tool-gating (tool not consumed), and anti-recursion guards (sharp_stone not re-knappable, cordage not re-spinnable).
2026-07-31 07:43:08 +00:00
calic
4825d01426 feat(vagrant): v0.19.0 — role-based Stone-Age recipes (match by form/material/weight/affordance, not item id); rock/stick get form roles 2026-07-28 12:22:51 +00:00
calic
60093522f5 feat(vagrant): v0.18.0 — Stone-Age round-out (stone_hammer+knap-gate, chop_tree, break_branch, sharpen_stick) in content pack 2026-07-28 12:09:25 +00:00
calic
e9085c0317 refactor(vagrant): v0.17.0 — extract Stone-Age content to content/stone_age.lua (T1-modder content packs via content facade) 2026-07-28 12:01:45 +00:00
calic
ff9d50937d feat(vagrant): v0.16.0 stone-age tech chain (knap/twist/haft → stone_axe); fix crafting manifest.lib to 0.3.0 2026-07-28 11:38:23 +00:00
calic
8994b24567 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 2026-07-28 10:42:21 +00:00
calic
08413e9c29 feat(crafting): lib-core.crafting v0.3.0 — property-constraint slots + non-consumed tools + multi-output (ADR-0055); vagrant-skeleton debug-drive hook for headless scenario testing 2026-07-28 10:35:57 +00:00
calic
c3290058c1 feat(vagrant): v0.14.0 — TC_Basics first-consumer + UI item icons
ADR-0054 Item-Visual-Identity validated end-to-end.

World-sprite migration off the proprietary Subterrain world-atlas:
  - Bed + Bench promoted to composition-entities with renderable tag;
    rendered via the existing draw_entities path (the hardcoded
    draw_sprite_transform calls for furniture are gone).
  - Backpack sprite source: Subterrain backpack_001 → TC_Basics sack1.
  - Stick visual: blob_rect_stone slot_04_straight → TC_Basics log_pile1.
  - rock_pick crafted-output visual: blob_rect_stone slot_05_tee_open →
    TC_Basics spade.
  - rock + rock_pile + workbench stay on blob_rect_stone (no TC_Basics
    equivalent yet).

Scale resolution is now atlas-level (no more module-side constants):
M.init reads tc_meta.atlas_meta.pixels_per_meter and divides into
PROJECT_PIXELS_PER_METER (128 per puppet shoulder anchor) to derive
tc_scale, applied per-entity via lib-core.render v0.3.0's sprite_
scale property.

UI item icons via ADR-0054 §3 Konsum:
  - Backpack + Workbench-Inventory rows now show item icons via the
    inventory-list-display v0.2.0 default resolver (fallback chain
    icon_atlas → sprite_atlas), fit-to-bounds.
  - Backpack-Crafting + Workbench-Crafting panels get an icon column
    via crafting-display v0.3.0; vagrant supplies a small recipe-
    output → {atlas, uv} lookup (craft_icons) and wires it as
    icon_resolver on both widgets.

Deps cascaded: lib-core.render 0.2.0 → 0.3.0, lib-core.inventory-
list-display 0.1.1 → 0.2.0, lib-core.crafting-display 0.2.0 → 0.3.0,
lib-asset.prototype-tc-basics 0.1.0 → 0.3.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-21 11:45:24 +02:00
Axel Meyer
088e14aeb2 chore: bump lib-core.panel dep to 0.4.0
Engine resolver does exact strcmp on dep versions, so consumer manifests
must pin the new lib-core.panel 0.4.0 to keep load-time resolution green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-15 03:05:25 +02:00
Axel Meyer
d69d2bd95d chore: bump lib-core.panel dep to 0.3.0 2026-06-15 02:26:04 +02:00
Axel Meyer
1fa0628c83 feat: v0.13.0 Workbench multi-panel via panel v0.2.0
D.2-revisit closes the Workbench Gate-2 walkthrough: right-click
on Workbench now opens workbench_crafting (left-half layout-slot)
+ workbench_inv (right-half) simultaneously, replacing the v0.12.0
auto-switch workaround. Both panels visible at the same time.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-15 02:01:39 +02:00
Axel Meyer
5cd42caaa7 chore: bump lib-core.panel dep to 0.2.0
Track lib-core.panel v0.2.0 (multi-active panels with layout-slots).
No behavioral change in this consumer — the v0.1.1 call sites remain
compatible via the panel framework's bw-compat shim.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-15 01:46:43 +02:00
Axel Meyer
644bfe98fd feat: v0.12.0 Workbench-Integration
Workbench-Entity bei (350, 380) als Crafting-Context-Erweiterung.
Right-Click oeffnet Workbench-Crafting-Panel + Workbench-Inventory.
Crafting nutzt Multi-Source-Locale {backpack, workbench} mit sink
= workbench; Take-Action verschiebt vom Workbench-Inventory zum
Backpack. C-Key Backpack-Crafting bleibt unveraendert via Bw-
Compat-Shim in crafting v0.2.0.

Deps: crafting 0.1.0 -> 0.2.0, crafting-display 0.1.0 -> 0.2.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-14 20:04:04 +02:00
Axel Meyer
f16c8954d9 fix: notify channel hygiene + craft-output sprites
- Drop "craft" tag from Inspect-Recipe payload; multi-line detail
  posts now route only to the inspect/detail channel, not the toast
  events channel.
- Wire rock_pick + rock_pile templates to blob_rect_stone atlas
  slots (slot_05_tee_open + slot_13_solid) so crafted items render
  in inventory + on drop instead of magenta fallback.
- Drop dead "crafting" tag from events filter (no post emitted it).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-14 14:09:17 +02:00
Axel Meyer
31333111c0 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>
2026-06-14 13:57:19 +02:00
Axel Meyer
0658e87066 chore: bump lib-core.composition dep to 0.3.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-14 11:57:40 +02:00
Axel Meyer
e310494d39 fix(vagrant): ESC closes open panel before quitting to launcher
Previously ESC unconditionally switched to the launcher, leaving no way
to dismiss an open inspect-detail (or any panel-widget opened via
notify.post without a toggle key). Now ESC closes the active panel
first; only when no panel is open does it quit to the launcher.
2026-06-14 02:36:40 +02:00
Axel Meyer
99f3583e91 chore: bump lib-core.panel dep to 0.1.1 2026-06-14 02:22:51 +02:00
Axel Meyer
6b4c698984 feat(vagrant): notification system — Inspect modal, pickup-float, toast events, game-log
- manifest: bump to v0.10.0; add deps lib-core.notify, lib-core.notify-display,
  lib-core.world-overlay (all v0.1.0)
- Inspect action migrated from engine.print to notify.post{tags={"inspect"}, data={...}};
  routes to modal detail-panel via notify-display detail-channel
- Pickup callback: world_overlay.spawn fires "+1 <name>" float anchored on player
  (yellow, rise_px=24, ttl=1.2s) + notify.post pickup event-toast/log entry
- Drop action (right-click menu): notify.post{tags={"drop","inventory"}} added
  after existing relocate+close_menu logic
- Setup block: 3 channels (inspect/events/log), 2 panel widgets
  (inspect-detail/game-log), 3 attach_mode calls; L bound to game-log panel
- M.update: notify_display.update + world_overlay.update after panel.update pause-guard
- M.draw: camera_handle constructed after camera.finish(); world_overlay.render between
  camera.finish and panel.render; notify_display.render at end (toasts on top)
- README: version 0.10.0, Requires expanded, Controls section updated, changelog entry added
2026-06-14 02:05:35 +02:00
Axel Meyer
f66ffc7f12 feat(vagrant): rock template gains player-facing properties
Rock template now declares name="Rock", a description, weight (kg),
volume (L), and composition.stone=1.0. The module-side label_resolver
override is removed — display lib's default reads name now.

Inspect action prints a structured block (name, description, weight,
volume, composition.<material>, reg_id) to the console. Future
notification system will route this to an in-game text panel; for now
the console output makes Inspect useful instead of silent.
2026-06-14 00:26:29 +02:00
Axel Meyer
60eac65bce feat(vagrant): Tab-Toggle inventory panel + right-click context menu
- lib-core.panel and lib-core.inventory-list-display added to deps (v0.1.0 each)
- backpack-widget created in M.init with label-resolver returning "Rock"
- Drop action registered: removes from backpack, relocates to player position,
  re-registers pickup trigger
- Inspect action registered: prints reg_id via engine.print
- M.update: panel.update(dt) + is_pausing() early-return added after
  quit_to_launcher check
- interaction.update wrapped in panel.is_open() guard to block world triggers
  while panel is visible
- M.draw: panel.render() added at z-top after CI traces
- Q-drop (LIFO quick-drop via Q key) unchanged and parallel to panel Drop action
2026-06-13 23:42:55 +02:00
Axel Meyer
e38d396b87 feat(vagrant): backpack as inventory container — end-to-end pickup/drop demo
- Add container={kind="list"} to the backpack template so inventory.add
  accepts it as a valid list-container
- Remove old backpack proximity-trigger (print-only callback, pre-B.5);
  the rock's register_pickup is now the sole interaction trigger
- Emit unconditional inventory inspection trace after every add/remove:
  backpack count + item reg-ids for user-facing demo feedback
- Add CI-gated tree-check traces: tree_check_after_pickup=ok and
  tree_check_after_drop=ok verify composition-tree child placement
- Bump version 0.7.3 -> 0.8.0 across init.lua header, manifest.module,
  and README
- README: rewrite Controls section into full demo walkthrough; add Demo
  Walkthrough section; update Demonstrates + Interactions sections;
  add v0.8.0 CHANGELOG entry

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 19:00:12 +02:00
Axel Meyer
f874813498 feat(vagrant): spawn rock entity with pickup trigger
- Rock template (stack_mode=individual, tags=renderable+item) defined
  alongside backpack and vagrant_player templates.
- state.rock entity created in M.init using slot_00_isolated UV from
  the blob_rect_stone atlas (lib-asset.prototype-blob-geom); position
  (270, 400) — 80px left of the backpack.
- register_pickup(state.rock) wires an interaction trigger; existing
  render.draw_entities{tag="renderable"} draws the rock with no
  special-case render code.
- CI trace: engine.print("vagrant: rock_spawned") emitted once on init.
- Version bump 0.7.2 -> 0.7.3.
2026-06-13 18:27:25 +02:00
Axel Meyer
f1e27d8183 feat(vagrant): pickup/drop glue + lib-core.inventory-list dep
- Add lib-core.inventory-list v0.1.0 to manifest deps; bump module
  version 0.7.1 -> 0.7.2
- Import inventory = require("lib-core.inventory-list") in init.lua
- Add Item-Template-Convention comment block after vagrant_player
  template: documents stack_mode/tags/sprite/position contract that
  B.4 (rock) and B.5 (backpack-as-container) will fulfill
- Add register_pickup(item) factory: reads item position, registers
  interaction trigger; callback calls inventory.add + unregisters
  trigger on pickup
- Bind "drop" action to Q; drop handler in M.update pops last item
  from backpack via inventory.remove (renderable tag restored by
  library), repositions item at player pos +30px, re-registers pickup
- Update README Controls section: document E (pickup) and Q (drop)
  with note that end-to-end use requires B.4 + B.5
2026-06-13 18:18:52 +02:00
Axel Meyer
342adf64a1 chore: bump lib-core.composition dep to 0.2.0 2026-06-13 14:07:46 +02:00
4 changed files with 1540 additions and 75 deletions

293
README.md
View File

@@ -5,13 +5,91 @@ WASD movement, camera-chase, multi-target look-at (body lazy-tracks mouse via
slerp, head snaps to mouse with ±60° clamp). Demonstrates the Subterrain-style
puppet control model: leg orientation decoupled from body via procedural
callback, foot-body-orientation ("tactical twist"), scale-deformation walk
cycle, and inheritance-decoupled foot sprites.
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.4.4
**Version:** 0.22.0
**Module-ID:** vagrant-skeleton
**Requires:** lib-core.input >=0.4.0, lib-core.camera >=0.3.0, lib-core.render >=0.1.0, lib-core.maps >=0.1.2, lib-core.puppet >=0.4.4, lib-core.interaction >=0.1.0, lib-asset.prototype-subterrain >=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.4.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.5.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.22.0:** **Quality & Condition (crafting v0.5.0 / Phase J).** Items now carry
> a `quality` (build-grade) and tools a `condition` (wear). A storm-broken `branch`
> rolls its `quality` in [0.5,1.0] at spawn (producer sets quality; the RNG lives in
> the module, never Core). `knap_sharp_stone` computes the edge's quality from
> skill-band + rock grade + hammer `quality×condition`, and **wears** the hammer 0.2
> per knap (5 knaps → a fully-worn hammer stops qualifying — the recipe asks for
> `condition > 0`). `craft_stone_axe`'s quality reads skill (floor = the `requires`
> gate 3) + head + haft, so a mediocre branch caps the axe even at max skill.
> `affordance.*` stays boolean; quality/condition are orthogonal scalars. Headless-
> verified (20/20): formula values, hammer wear-out, mediocre-haft cap, neutral-1.0
> default for un-graded items.
>
> **v0.21.0:** **Skill-gating (crafting v0.4.0 / ADR-0056).** Recipes can now gate
> on and reward the ACTOR: the player-actor carries `skill.knapping` (an entity
> property), `knap_sharp_stone` **grants** knapping-XP, and `craft_stone_axe`
> **requires** `skill.knapping>=3`. The module applies the craft's returned
> `granted` to the actor. Demonstrates the gate→earn→unlock loop (gate downstream,
> grant upstream). Headless-verified: axe locked at skill 0 (`requires_unmet`) →
> 3 knaps → axe unlocks. Skill-locked recipes show "Skill too low" instead of a
> generic failure.
>
> **v0.20.0:** **`form` retired → `affordance` is the one capability axis**
> (ADR-0055 amendment). The `form` role-tag mixed geometry, identity and
> capability; it is gone. Its functional members folded into `affordance.*`
> (`blade→cutting`, `shaft→leverage`, plus `knappable` / `spinnable` /
> `binding`); identity members became template-id (`chop_tree` = `{template=
> "tree"}`) or material+mass (`log`/hammer-head). The SAME affordance vocabulary
> is now required by both consumed `inputs` and non-consumed `tools`. Role-
> flexibility preserved: a haft = `{["affordance.leverage"]=true}` → stick OR
> branch; weight still discriminates (light stick sharpens, heavy branch breaks).
>
> **v0.19.0:** **Role-based recipes** (superseded by v0.20.0's axis rename).
> Introduced role-matching for Stone-Age recipes so any item filling a role
> qualifies, not a specific item id — originally via a `form` tag, since folded
> into `affordance`. Template-id matching stays available (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
> axe→log→plank loop); `break_branch` (branch → 2 sticks, multi-output);
> `sharpen_stick` (stick + cutting → sharpened_stick). Tool-gating verified
> (knap/chop fail without the right affordance).
>
> **v0.17.0:** **T1-modder content packs.** Stone-Age items + recipes + world-
> spawns moved OUT of `init.lua` into `content/stone_age.lua` — pure declarative
> data (`c.T{}` item, `c.R{}` recipe, `c.S{}` spawn), loaded via a content facade
> in `init.lua` (`engine.module.dir_of` + `dofile`, the sanctioned multi-file
> pattern). `c.T` auto-registers each craftable's crafting-panel icon from its
> sprite. A T1 modder now adds/edits Stone-Age content by touching only the pack
> file, never engine wiring. Behaviour is unchanged (regression-verified); also
> fixes crafted `sharp_stone` rendering (was a "?" placeholder before).
>
> **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
> **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) —
> the hardcoded `draw_sprite_transform` calls for furniture are gone. First
> concrete consumer of ADR-0054 Item-Visual-Identity. The Subterrain dep
> remains for the puppet sprite parts (`subterrain_player` alias).
## Topology
<!-- topology:start (auto-generated; do not edit) -->
@@ -22,8 +100,70 @@ cycle, and inheritance-decoupled foot sprites.
- **W A S D** — direct movement (4-way, normalized for diagonals)
- **Shift + WASD** — sprint (2x player speed + 2x walk-anim cadence)
- **Mouse** — body + head look-at targets (same position, different smoothing)
- **E** — interact with nearest proximity-trigger in range
- **ESC** — return to launcher
- **E** — interact: if within range of the rock or stick, picks it up into the backpack
- **Q** — quick-drop (LIFO): places the most-recently-added backpack item at player position (+30px right)
- **Tab** — toggle inventory panel (Backpack contents)
- **C** — toggle Backpack-Crafting panel (lists known recipes; availability dimmed by inventory)
- **Right-Click on Workbench** (within range) — opens Workbench-Crafting (left-half) + Workbench-Inventory (right-half) panels side-by-side (panel v0.2.0 multi-active)
- **Right-Click on item row** (inventory panel): context menu with **Drop** / **Inspect** (Backpack) or **Take** / **Inspect** (Workbench)
- **Right-Click on recipe row** (crafting panel): context menu with **Craft** / **Inspect**
- **L** — toggle game-log (chronological list of all events)
- **Right-Click on item row → Inspect** — opens a modal Item Details panel (instead of console-only output)
- **Pickup (E)** — shows "+1 <name>" floating above the player + toast at top-right + log entry
- **Drop (right-click on item → Drop, or Q for LIFO quick-drop)** — toast at top-right + log entry
- **Craft (right-click on recipe → Craft)** — consumes inputs from current locale, adds output, posts a success/failure toast
- **Take (right-click on Workbench-item → Take)** — moves item from Workbench → Backpack + toast
- **ESC** — close any open panel; if no panel is open, return to launcher
## Demo Walkthrough
The world contains five notable entities at startup:
1. **Player** — spawns at (320, 240), controlled via WASD + mouse.
2. **Backpack** — a visible sprite at (350, 400). Acts as a container (kind="list").
Not directly interactable; it receives items via inventory.add.
3. **Rock** — a standalone stone tile at (270, 400), 80px to the left of the backpack.
Tagged "renderable" + "item"; starts visible on the ground.
4. **Stick** — a placeholder pickup-item at (190, 400), 160px to the left of the backpack.
Re-uses the `slot_04_straight` blob_rect_stone tile for its visual (Phase C-Q10
pragmatic resolution — echtes Stick-Sprite folgt mit dem ersten atlas-baker-decals-Slice).
5. **Workbench** — a crafting bench at (350, 380), just above the backpack. Container
(kind="list"); placeholder visual re-uses the `slot_07_tee_full` blob_rect_stone tile.
Right-Click within range opens Workbench-Crafting + Workbench-Inventory panels.
**Pickup flow:**
- Walk toward the rock (it sits to the left of the backpack).
- Press **E** within 40px of the rock.
- The rock moves into the backpack via `inventory.add`; its "renderable" tag is
removed so it no longer appears in the world.
- The console prints: `vagrant: backpack count=1 items=[<reg_id>]`
**Drop flow:**
- Press **Q** at any time when the backpack has items.
- The most-recently-added item is removed from the backpack (`inventory.remove`)
and placed at the player position +30px to the right. Its "renderable" tag
is restored so it reappears in the world.
- A new pickup trigger is registered at the drop position.
- The console prints: `vagrant: backpack count=0 items=[]`
**Multiple pickups:** Each E → Q → E cycle moves the same rock in and out.
Only one rock is spawned per session.
**Workbench flow (Phase D v0.2 + v0.13.0 multi-panel):**
- Pick up rock + stick into the backpack (E on each).
- Walk to the Workbench (just above the Backpack at (350, 380)) and
right-click within 40px range.
- Both panels open simultaneously, side-by-side: Workbench-Crafting in
the **left-half** layout-slot, Workbench-Inventory in the **right-half**
slot (panel v0.2.0 multi-active + layout-slots).
- Right-click on a recipe row (e.g. **Stone Pick**) → context menu with
**Craft** + **Inspect**. Click **Craft** → crafted item lands in
**Workbench-Inventory** (NOT Backpack); it appears in the right-half
panel immediately.
- Right-click on the crafted item in Workbench-Inventory → **Take** moves
it to the Backpack; a "Took <item>" toast appears.
- Crafting inputs are aggregated from `{backpack, workbench}` (drain order:
backpack first, then workbench).
## Demonstrates
@@ -41,8 +181,10 @@ cycle, and inheritance-decoupled foot sprites.
- Idle state → idle + idle_lower animations
- Camera-chase via lib-core.camera target-provider
- Sprite-tilemap via lib-core.maps
- lib-core.interaction proximity trigger on the backpack sprite at (350, 400),
range 40px, "interact" action bound to E — fires log on dispatch
- lib-core.interaction proximity trigger on the rock at (270, 400), range 40px,
"interact" action bound to E — fires inventory.add on dispatch
- lib-core.inventory-list: backpack is a container (kind="list"); rock is an
individual-stack item; add/remove toggle renderable tag
## Interactions
@@ -51,8 +193,10 @@ cycle, and inheritance-decoupled foot sprites.
- WASD released → idle_lower collapses legs (scl=0); upper body returns to idle sway
- Mouse moved → body lazy-rotates via slerp (look_at_slerp: 6); head snaps within ±60° of body
- Walking east while body faces north → legs perpendicular to body, hips body-relative, feet face north
- E pressed within 40px of the backpack sprite → fires `"interact"` action;
callback logs `vagrant: interact with backpack (distance=X.X)`
- E pressed within 40px of the rock → fires `"interact"` action; rock moves into backpack;
console prints `vagrant: backpack count=N items=[...]`
- Q pressed with items in backpack → most-recent item drops at player position; pickup trigger
re-registered at drop location; console prints updated backpack state
## CI Hooks
@@ -75,6 +219,137 @@ cycle, and inheritance-decoupled foot sprites.
## CHANGELOG
### v0.13.0 — Workbench Multi-Panel via lib-core.panel v0.2.0
- Right-click on Workbench now opens BOTH panels simultaneously:
`workbench_crafting` (layout="left-half") + `workbench_inv`
(layout="right-half").
- Replaces the v0.12.0 auto-switch workaround that was forced by
panel v0.1.1's single-active limitation. The Workbench Gate-2
walkthrough per `2026-06-14-phase-D-workbench-design.md` §7 now
runs cleanly.
- Deps: lib-core.panel 0.1.1 → 0.2.0 (multi-active + layout-slots).
### v0.12.0 — Workbench
- **New: Workbench entity** in the world at (350, 380) — `composition.define_template`
with `container = {kind="list"}`, tagged `renderable`, `container`, `workbench`,
`interactable`. Placeholder visual re-uses the `slot_07_tee_full`
blob_rect_stone tile (visually distinct from rock/stick/rock_pick/rock_pile).
- **New: Right-Click on Workbench** opens two panels:
- **Workbench Crafting** — recipes are still listed by `lib-core.crafting`,
but availability + craft-action use a **multi-source locale**
`{sources={backpack, workbench}, sink=workbench}` (crafting v0.2.0 API).
Inputs are drained greedy in source-array order (backpack first, then
workbench). Output lands in the Workbench, not the Backpack.
- **Workbench Inventory** — workbench contents with **Take** + **Inspect**
actions on right-click. Take moves the item to the Backpack and fires
a `transfer` event-toast.
- **Still works**: C-key Backpack-Crafting (basic recipes still craftable
with Backpack-only via the bw-compat shim in crafting v0.2.0). The
Phase-C C-key callsite passes `state.backpack` as a bare entity-handle;
crafting v0.2.0's resolve_locale wraps that as `{sources={backpack},
sink=backpack}` transparently.
- **Caveat (panel-lib v0.1 single-active)**: `lib-core.panel` v0.1.1
enforces one active widget. Opening both Workbench panels means only
the second (`workbench_inv`) is visible until the player closes it (X);
closing it reveals `workbench_crafting`. Proper side-by-side stacking
is a future panel-lib slice. **Resolved in v0.13.0** via panel v0.2.0
multi-active + layout-slots.
- **Deps**: crafting 0.1.0 → 0.2.0, crafting-display 0.1.0 → 0.2.0.
- **Notify events-Channel**: filter um `"transfer"` erweitert, damit
Take-Action-Toasts im events-channel landen.
- **Input binding**: `"use"` bound to `mouse_right` (mirrors the lib-core.input
v0.3.0 unified key+mouse binding).
### v0.11.0 — crafting integration
- **New: stick pickup-item** neben rock am Boden (Position 190,400 — 160px links
vom Backpack). Re-uses `slot_04_straight` aus blob_rect_stone als Visual-
Placeholder (Phase C-Q10).
- **New: 2 Recipes** via `lib-core.crafting`:
- **Stone Pick** = 1 rock + 1 stick → `rock_pick`
- **Rock Pile** = 3 rocks → `rock_pile`
- **New: C-Taste** öffnet ein Crafting-Panel via `lib-core.crafting-display`.
Recipe-Zeilen sind dimmed, wenn inputs im Backpack fehlen.
- **New: Right-Click auf Recipe-Row** → Context-Menu mit **Craft** + **Inspect**.
Craft konsumiert inputs aus dem Backpack und legt das Output-Template als
neues item-Entity in den Backpack. Inspect routet recipe details (name,
description, inputs, output) zum notify-detail-channel.
- **New: 3 item-templates** — `stick`, `rock_pick`, `rock_pile` — jedes mit
player-facing properties (name, description, weight, volume,
composition.<material>). Placeholder atlas-slots: stick=slot_04_straight,
rock_pick=slot_05_tee_open, rock_pile=slot_13_solid.
- **Deps**: +lib-core.crafting 0.1.0, +lib-core.crafting-display 0.1.0.
composition bleibt auf 0.3.0 (gebumpt in Phase C.0).
- **Notify events-Channel**: filter um `"craft"` erweitert, damit Craft-
Erfolgs- und Fehler-Toasts im events-channel landen.
### v0.10.0 — notification system integration
- **Inspect modal**: Inspect action replaced with `notify.post{tags={"inspect"}, ...}` routing
to a modal detail-panel (`notify-display` detail-channel). Item name, description, weight,
volume, and material composition are passed as structured data.
Inspect no longer calls engine.print directly; the notify lib's engine-log mirror routes the event to engine.log for backward-compat with dev tools.
- **Pickup world-float**: after `inventory.add`, a `world_overlay.spawn` fires a "+1 Rock"
floating text anchored to the player (yellow, rises 24px, fades in 1.2s). A `notify.post`
simultaneously emits a pickup event-toast and log entry.
- **Drop toast**: the Drop action (both right-click menu and Q quick-drop) emits a
`notify.post{tags={"drop","inventory"}}` event-toast after the item is relocated.
- **Game-log panel**: a catch-all log channel (`filter={}`) captures every pickup, drop, and
inspect event chronologically. **L** toggles the panel.
- **New deps**: lib-core.notify v0.1.0, lib-core.notify-display v0.1.0,
lib-core.world-overlay v0.1.0.
- **Render order**: camera.finish() → world_overlay.render(camera_handle) →
panel.render() → notify_display.render() (toasts on top of all UI).
### v0.9.1 — item-template player-facing properties
- **Rock template enriched**: `name`, `description`, `weight` (kg),
`volume` (L), and `composition.stone = 1.0` properties added. Other item
templates can follow the same convention (optional with fallbacks).
- **Inspect prints structured info**: `name`, `description`, `weight`,
`volume`, and a `composition: <material>=<value>` line; `reg_id`
parenthetical at the end. Future notification system will route this
to an in-game text panel.
- **Module-side label_resolver override removed**: `inventory-list-display`
v0.1.1's `default_label_resolver` reads the `name` property; the
hardcoded `"Rock"` override is obsolete.
### v0.9.0 — inventory panel UI
- **Panel overlay**: `lib-core.panel` + `lib-core.inventory-list-display` wired in.
Tab key toggles a panel overlay showing backpack contents.
- **Context menu actions**: right-clicking an item row opens a context menu with
**Drop** (re-parents item to world at player position, re-registers pickup trigger)
and **Inspect** (prints `reg_id` to console).
- **Q-drop unchanged**: LIFO quick-drop via Q remains available in parallel to the
panel-based drop action.
- **Interaction guard**: `interaction.update` is suppressed while the panel is open
so world triggers don't fire during menu navigation.
### v0.8.0 — end-to-end inventory demo functional
- **Backpack container**: `composition.define_template{id="backpack"}` now includes
`container={kind="list"}`. The composition v0.2 validator accepts this; the backpack
can now receive items via `inventory.add`.
- **End-to-end pickup/drop**: E on the rock calls `inventory.add(state.backpack, rock)`;
inventory-list removes the "renderable" tag so the rock disappears from the world.
Q calls `inventory.remove`, restores "renderable", relocates item to player position
+30px right, and re-registers the pickup trigger. Full loop functional.
- **Inventory inspection trace**: after every add or remove, `engine.print` emits
`vagrant: backpack count=N items=[...]` (unconditional; user-facing demo feedback).
- **Tree-check traces (CI-gated)**: after pickup, `vagrant: tree_check_after_pickup=ok`
verifies item appears as `item.*` child of the backpack in the composition tree.
After drop, `vagrant: tree_check_after_drop=ok` verifies no `item.*` children remain.
- **Backpack interaction.register removed**: the old print-only proximity trigger on the
backpack sprite has been deleted. The rock's pickup trigger is the only interaction
trigger registered.
### v0.7.3
- **Rock entity**: `composition.define_template{id="rock"}` added (stack_mode=individual,
tags=renderable+item). One rock entity spawned at (270, 400) — 80px left of the backpack.
Uses `slot_00_isolated` UV from the `blob_rect_stone` atlas (lib-asset.prototype-blob-geom).
`register_pickup(state.rock)` wires an interaction trigger so pressing E near the rock
fires the pickup callback. `render.draw_entities{tag="renderable"}` (existing Phase A
render path) draws the rock without any special-case render code.
### v0.4.4
- **Sprint** (Shift+WASD): doubles `WALK_SPEED` (120 → 240) and walk-anim
`speed` (0.45 → 0.9). Mid-walk sprint toggle uses `puppet.set_play_speed`

208
content/stone_age.lua Normal file
View File

@@ -0,0 +1,208 @@
-- =====================================================================
-- content/stone_age.lua — T1-modder content pack: Stone-Age items + recipes.
--
-- PURE CONTENT via the authoring API `c` (see init.lua's content facade):
-- c.T{ id, name, desc, weight, vol, material={...}, affordance={...},
-- atlas="tc"|"stone", uv="<sprite-key>", scale? } -- define an item
-- c.R{ ...crafting.define_recipe def... } -- define a recipe
-- c.S{ template, x, y } -- place item in world
--
-- ROLE-BASED RECIPES (the intent): a recipe slot asks for the CAPABILITY it
-- needs, and ANY item that offers that capability qualifies — not a specific
-- item id. Capabilities are expressed as property-constraints (crafting v0.3 /
-- ADR-0055 + its 2026-07-31 amendment). Two orthogonal axes carry a recipe:
-- * `affordance.*` — what an item OFFERS / can DO / what role it fills:
-- cutting / hammering / chopping / leverage / binding /
-- spinnable / knappable / piercing … (open vocabulary)
-- * `composition.*`— material makeup (wood / stone / fiber …)
-- * `weight` — mass discriminator (light stick vs heavy branch)
-- e.g. a "haft" = { ["affordance.leverage"]=true } → any stick OR branch OR
-- pole works. The SAME affordance vocabulary is required by both consumed
-- `inputs` and non-consumed `tools`; the inputs/tools split (not a separate
-- axis) says whether the item is used up.
--
-- ACTOR-SIDE (crafting v0.4 / ADR-0056): a recipe may also gate on / reward the
-- ACTOR, not the ingredients:
-- * `requires = { ["skill.knapping"]=">=3" }` — hard gate vs ctx.actor
-- * `grants = { ["skill.knapping"]=1 }` — XP given on successful craft
-- Same predicate machine; the module applies `grants` to the actor.
--
-- `form` was RETIRED (ADR-0055 amendment): a physical-shape axis mixed
-- geometry, identity and capability. Its functional members became
-- affordances; its identity members (a "tree") stay template-id.
--
-- Template-id matching ({ template="tree" }) 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, and chop_tree below).
-- Role-based is the default; template-id is one of the options, not the norm.
-- =====================================================================
return function(c)
-- ---- items (each tagged with the CAPABILITIES it offers) ---------
-- NOTE: `rock` (affordance.knappable) and `stick` (affordance.leverage,
-- weight 0.3) are BASE items defined in init.lua; this pack's role-based
-- recipes match them by affordance the same as pack items.
-- World-gathered
c.T{ id = "branch", name = "Branch", desc = "A heavy springy branch. Snap it into sticks.",
weight = 1.5, vol = 0.006, material = { wood = 1.0 },
affordance = { leverage = true }, -- a haft candidate, but heavy
-- Phase J (design 2026-08-03 §5): a storm-broken branch is of variable
-- grade — the world rolls its `quality` in [0.5,1.0] at spawn. Used as a
-- haft, this variance flows into the axe's quality (craft_stone_axe).
quality_band = { min = 0.5, max = 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, material = { wood = 1.0 }, -- raw heavy wood; matched by material+weight
atlas = "tc", uv = "log_pile1" }
c.T{ id = "tree", name = "Tree", desc = "A standing tree. Chop it down for logs.",
weight = 400, vol = 1.0, material = { wood = 1.0 }, -- one concrete item → matched by template-id
atlas = "tc", uv = "log_pile1", scale = 1.6 }
c.T{ id = "plant_fiber", name = "Plant Fiber", desc = "Tough plant fiber. Twist several into cordage.",
weight = 0.05, vol = 0.0002, material = { fiber = 1.0 },
affordance = { spinnable = true }, -- can be twisted; cord itself is NOT spinnable
atlas = "tc", uv = "sack1", scale = 0.6 }
-- Crafted
c.T{ id = "sharp_stone", name = "Sharp Stone", desc = "A knapped stone with a cutting edge.",
weight = 1, vol = 0.0004, material = { stone = 1.0 },
affordance = { cutting = true }, -- "blade" role = affords cutting
atlas = "tc", uv = "spade" }
c.T{ id = "cordage", name = "Cordage", desc = "Twisted plant-fiber cord. A binding.",
weight = 0.1, vol = 0.0003, material = { fiber = 1.0 },
affordance = { binding = true }, -- "cord" role = affords binding (NOT spinnable → no re-twist)
atlas = "tc", uv = "sack1" }
c.T{ id = "plank", name = "Plank", desc = "A sawn wooden plank.",
weight = 2, vol = 0.004, material = { wood = 1.0 }, -- output only; light wood (not re-sawable)
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,
material = { stone = 0.6, wood = 0.3, fiber = 0.1 },
affordance = { chopping = true }, atlas = "tc", uv = "spade" }
c.T{ id = "stone_hammer", name = "Stone Hammer", desc = "A stone lashed to a haft. Pounds and knaps.",
weight = 1.5, vol = 0.001,
material = { stone = 0.6, wood = 0.3, fiber = 0.1 },
-- Phase J: a tool tracks `condition` (fresh = 1.0). Each knap wears it
-- (knap_sharp_stone.tools.wear_per_use); at condition 0 it stops
-- qualifying (the recipe's tool match asks for condition > 0).
condition = 1.0,
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 },
affordance = { piercing = true }, atlas = "tc", uv = "log_pile1", scale = 0.9 }
-- ---- recipes (match by CAPABILITY, not by item id) --------------
-- knap: any KNAPPABLE stone + a HAMMERING tool -> a cutting edge.
-- (rock is knappable; sharp_stone is NOT → no self-recursion.)
-- Skill-ungated but GRANTS knapping XP (ADR-0056) — the accessible "grind"
-- recipe that bootstraps the gated craft_stone_axe below.
c.R{ id = "knap_sharp_stone",
inputs = { { match = { ["affordance.knappable"] = true }, count = 1, name = "stone" } },
-- Phase J: the hammer must have condition > 0, and wears 0.2 per knap →
-- a fresh hammer lasts 5 knaps, then stops qualifying (missing_tools).
tools = { { match = { ["affordance.hammering"] = true, condition = ">0" },
name = "hammer", wear_per_use = 0.2 } },
outputs = { { template = "sharp_stone", count = 1 } },
grants = { ["skill.knapping"] = 1 },
-- Product quality = skill (0..5 band, min 0 since ungated) + stone grade
-- + hammer grade×condition. A worn hammer makes worse edges.
quality = { contributors = {
{ skill = "skill.knapping", max = 5, weight = 0.3 },
{ ingredient = "stone", weight = 0.4 },
{ tool = "hammer", weight = 0.3 },
} },
name = "Knap Sharp Stone",
description = "Strike a stone with a hammerstone to flake off a sharp edge." }
-- cordage: three SPINNABLE fibers (any fiber source) -> cord.
-- (cordage is binding-but-not-spinnable → you can't re-twist cord.)
c.R{ id = "twist_cordage",
inputs = { { match = { ["affordance.spinnable"] = true }, count = 3 } },
outputs = { { template = "cordage", count = 1 } },
name = "Twist Cordage",
description = "Twist three plant fibers into a length of cordage." }
-- hammer: a heavy STONE head + a LEVERAGE haft + a BINDING.
-- head matched the primitive way (material+mass); haft/binding by affordance —
-- one recipe, both matching styles, one mechanism (ADR-0055).
c.R{ id = "craft_stone_hammer",
inputs = {
{ match = { ["composition.stone"] = ">0.5", weight = ">1" }, count = 1 }, -- head (a stone chunk)
{ match = { ["affordance.leverage"] = true }, count = 1 }, -- haft (stick OR branch)
{ match = { ["affordance.binding"] = true }, 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 CUTTING head + a LEVERAGE haft + a BINDING.
-- Skill-GATED (ADR-0056): needs knapping >=3, earned by knap_sharp_stone.
-- Demonstrates the gate+earn+unlock loop (gate downstream, grant upstream).
c.R{ id = "craft_stone_axe",
inputs = {
{ match = { ["affordance.cutting"] = true }, count = 1, name = "head" }, -- a sharp stone
{ match = { ["affordance.leverage"] = true }, count = 1, name = "haft" }, -- stick OR branch
{ match = { ["affordance.binding"] = true }, count = 1 }, -- binding
},
requires = { ["skill.knapping"] = ">=3" },
-- Product quality = skill (band min 3 = the requires-floor, no re-author,
-- max 5) + head grade (the knapped edge) + haft grade (raw-branch variance).
-- A mediocre branch caps the axe even at max skill.
quality = { contributors = {
{ skill = "skill.knapping", max = 5, weight = 0.4 },
{ ingredient = "head", weight = 0.3 },
{ ingredient = "haft", weight = 0.3 },
} },
outputs = { { template = "stone_axe", count = 1 } },
name = "Haft Stone Axe",
description = "Bind a sharp stone to a haft with cordage. (Needs knapping skill 3.)" }
-- break: a HEAVY leverage-thing (branch, not a light stick) -> two sticks.
c.R{ id = "break_branch",
inputs = { { match = { ["affordance.leverage"] = true, 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 leverage-thing (a stick) + a CUTTING tool -> pointed stick.
c.R{ id = "sharpen_stick",
inputs = { { match = { ["affordance.leverage"] = true, 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: THE tree (genuine single item → template-id) + a CHOPPING tool -> logs.
-- (Multiple tree species would switch this to an affordance/material match.)
c.R{ id = "chop_tree",
inputs = { { match = { template = "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+mass) + a CUTTING tool -> 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 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 }
c.S{ template = "sharp_stone", x = 110, y = 330 }
for i = 1, 3 do
c.S{ template = "plant_fiber", x = 50, y = 300 + (i - 1) * 28 }
end
c.S{ template = "branch", x = 50, y = 250 }
c.S{ template = "branch", x = 85, y = 250 }
c.S{ template = "tree", x = 200, y = 190 }
end

1085
init.lua

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,35 @@
{
"id": "vagrant-skeleton",
"version": "0.7.1",
"version": "0.22.0",
"kind": "module",
"api": "^0.1",
"ci_frames": 60,
"entry_point": "init.lua",
"asset_aliases": {
"subterrain_player": "lib-asset.prototype-subterrain",
"subterrain_world": "lib-asset.prototype-subterrain",
"blob_testbench": "lib-asset.prototype-blob-geom",
"blob_rect_stone": "lib-asset.prototype-blob-geom"
"blob_rect_stone": "lib-asset.prototype-blob-geom",
"tc_basics": "lib-asset.prototype-tc-basics"
},
"deps": [
{"id": "lib-core.input", "version": "0.4.0"},
{"id": "lib-core.camera", "version": "0.3.0"},
{"id": "lib-core.render", "version": "0.2.0"},
{"id": "lib-core.composition", "version": "0.1.0"},
{"id": "lib-core.render", "version": "0.3.0"},
{"id": "lib-core.composition", "version": "0.4.0"},
{"id": "lib-core.actor", "version": "0.1.0"},
{"id": "lib-core.maps","version":"0.5.7"},
{"id": "lib-core.panel", "version": "0.4.0"},
{"id": "lib-core.inventory-list-display", "version": "0.2.0"},
{"id": "lib-core.notify", "version": "0.1.0"},
{"id": "lib-core.notify-display", "version": "0.1.0"},
{"id": "lib-core.world-overlay", "version": "0.1.0"},
{"id": "lib-core.maps", "version": "0.5.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.5.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"}
{"id": "lib-asset.prototype-blob-geom", "version": "0.1.0"},
{"id": "lib-asset.prototype-tc-basics", "version": "0.3.0"}
]
}