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.
391 lines
23 KiB
Markdown
391 lines
23 KiB
Markdown
# sporel-module-vagrant-skeleton
|
||
|
||
F.vagrant Tier-1 test-chamber: single player as a 13-bone humanoid puppet,
|
||
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. End-to-end inventory pickup/drop
|
||
demo: pick up a rock into a backpack container, drop it back into the world.
|
||
|
||
**Version:** 0.21.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.4.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.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) -->
|
||
<!-- topology:end -->
|
||
|
||
## Controls
|
||
|
||
- **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: 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
|
||
|
||
- lib-core.puppet v0.4.3 integration:
|
||
- Multi-look-target: body "soft" (slerp=6, lazy) + head "aim" (instant-snap, ±60° clamp)
|
||
- Scale-deformation walk cycle: leg.scl_y cycles +1/0/-1 via keyframe animation
|
||
- Procedural leg orientation: legs face walk direction independent of body rotation
|
||
- Foot-body-orientation ("tactical twist"): feet face body direction, not leg direction
|
||
- `inherit_scale: false` on feet: feet do not Y-flip when leg.scl_y goes negative
|
||
- Per-channel conflict guard: walk animation (scl) + procedural (rot) on same leg bones
|
||
- Camera in `camera.begin()/finish()` block for correct world-space rendering
|
||
- WASD movement → walk_fwd_lower + walk_upper animation pair (speed = 0.45x)
|
||
- Shift+WASD sprint → `puppet.set_play_speed` doubles cadence to 0.9x mid-cycle
|
||
(no animation restart on toggle); player movement speed doubled (120 → 240)
|
||
- 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 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
|
||
|
||
- WASD active → legs swing in walk direction; body slowly rotates to mouse; head snaps to mouse
|
||
- Shift held while moving → 2x player speed; walk cadence doubles (smooth mid-cycle transition)
|
||
- 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 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
|
||
|
||
- `SPOREL_CI=1` → 60-frame perf canary. Emits `vagrant: frame_avg_ms=N`
|
||
and `vagrant: render_frame_ok` (per-frame, from render hook), then exits.
|
||
Used by smoke stages P0-S25a + P0-S25b.
|
||
- `SPOREL_VAGRANT_PHASE1_DRIVE=1` → scripted synthetic WASD (frames 20-40
|
||
move east). Used by P0-S25 puppet-test integration.
|
||
|
||
## References
|
||
|
||
- `superpowers/specs/2026-05-18-puppet-subterrain-model-design.md` (design basis)
|
||
- `docs/adrs/0040-puppet-subterrain-model-pivot.md` (architectural rationale)
|
||
- `architecture/puppet.md` (lib-core.puppet reference)
|
||
- `audit/2026-05-18-puppet-subterrain-model-manual-test.md` (manual verification checklist)
|
||
- ADR-0028 (Launcher-Flow)
|
||
- ADR-0036 (Lib-Override for multi-module reuse)
|
||
- ADR-0037 (Tests-as-Libs)
|
||
- ADR-0038 (API-Doc-Convention)
|
||
|
||
## 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`
|
||
(new v0.4.4 API) — no animation cycle restart.
|
||
- **Backpack interaction**: `lib-core.interaction.register(350, 400, 40,
|
||
"interact", callback)` registered at init. Pressing E within range
|
||
prints log message via `engine.print` with actor distance.
|
||
- New deps: lib-core.interaction ^0.1.0. lib-core.puppet bumped to >=0.4.4.
|
||
|
||
### v0.4.3
|
||
- `inherit_scale: false` on foot_l, foot_r: feet no longer Y-flip when leg.scl_y
|
||
inverts during walk cycle. foot.rest.scl reset to [1,1].
|
||
- foot procedural callback writes foot.rot to cancel leg.rot → foot.world.rot = body.world.rot.
|
||
|
||
### v0.4.2
|
||
- Procedural orientation callback writes leg.rot + foot.rot (per-channel guard allows
|
||
this alongside walk_fwd_lower which writes only leg.scl).
|
||
|
||
### v0.4.1
|
||
- head look-at uses target_local normalization (-π, π]) to fix flick at body south-facing.
|
||
|
||
### v0.4.0
|
||
- Multi-look-target: body tracks "soft", head tracks "aim" (both currently raw mouse).
|
||
- body.look_at_slerp = 6 (lazy rotation); head instant-snap with rot_min/max ±60°.
|
||
- Legs re-parented from root to body (hip-static body-relative positioning).
|
||
- Procedural "leg_foot_orientation" callback: leg faces walk direction,
|
||
foot cancels leg rotation to stay body-aligned.
|
||
- walk_fwd_lower plays at speed = 0.45x for human-feel cadence.
|
||
- Rendering wrapped in camera.begin()/camera.finish() block.
|
||
|
||
### v0.3.3
|
||
- sprite_rot applied to leg bones to compensate for sprite natural orientation.
|
||
|
||
### v0.3.2
|
||
- Persistent look_at_state; fixes rate-limit reset each frame.
|
||
|
||
### v0.3.1
|
||
- look-at atan math corrected: atan(dx, -dy) for sprite-top convention.
|
||
|
||
### v0.3.0
|
||
- Sprite-mode rendering: 13-bone humanoid puppet with multi-channel animation.
|
||
- walk_fwd_lower (lower track): leg.scl_y cycle +1/0/-1 (substrate walk_fwd_lower).
|
||
- walk_upper (upper track): arm-swing animation.
|
||
- idle + idle_lower: upper body sway + leg collapse.
|
||
- Sprite-tilemap with 4 floor variants; hardcoded furniture.
|
||
|
||
### v0.2.0
|
||
- Sprite-mode rendering throughout: 13-bone humanoid puppet via lib-core.puppet v0.2
|
||
with PROTOTYPE asset-pack textures; sprite-tilemap via lib-core.maps v0.1.2 with
|
||
4 floor variants; hardcoded furniture (bed, bench, backpack).
|