From 96ccb410d357bdee53e2bc7efc1af32e127350f7 Mon Sep 17 00:00:00 2001 From: Axel Meyer Date: Mon, 18 May 2026 18:34:42 +0200 Subject: [PATCH] =?UTF-8?q?feat(puppet):=20v0.3.0=20=E2=80=94=20idle-retur?= =?UTF-8?q?n=20locomotion=20+=20version=20bump?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - apply_locomotion idle-return: after (now - last_move_time) > idle_return_delay, the most-displaced planted leg steps back to its current hip position (over idle_step_duration). Group-stagger via bumping last_move_time forward by idle_return_stagger. - M.move_to tracks p.last_move_time when position actually changes. - README v0.3.0 changelog entry. --- README.md | 17 ++++++++++++++++- init.lua | 36 ++++++++++++++++++++++++++++++++++++ manifest.lib | 2 +- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7fde3a0..5dcb3ab 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ tracks, rest pose, keyframe animations, procedural animations (Lua-side), and look-at constraint. No footplant IK in v0.1 (deferred). -**Version:** 0.2.0 +**Version:** 0.3.0 **Lib-ID:** lib-core.puppet **Requires:** (none — pure Lua + engine.render.*) **Tags:** animation, skeleton, puppet, character @@ -125,6 +125,21 @@ ownership at write time. ## CHANGELOG +### v0.3.0 +- BoneState multi-channel (rot/pos/scl) with world.scl_x/y and world.rot +- Cascaded world-transform with scale propagation (compute_world) +- Procedural locomotion layer via rig.locomotion block (apply_locomotion): + step-trigger, step-placement, step-height, leg-aim + leg-stretch +- Idle-return logic in apply_locomotion: after (now - last_move_time) > + idle_return_delay, the most-displaced planted leg steps back to its hip + position over idle_step_duration; group-stagger via idle_return_stagger +- M.move_to tracks p.last_move_time when position actually changes +- Schema changes: rest.rot/scl required; legacy rest.angle rejected with + clear error message (Substrate-Parity v0.3.0 schema) +- Bone-rig top-level scale field removed; per-frame world state replaces it +- New APIs: bone_world_scale, set_foot_sprite_reach_test_only, + foot_state_test_only, bone_animated_test_only + ### v0.2.0 - Rig-format extension (texture, z_order, scale, anchor, asset_pack fields on bones; rig.bones_z_sorted for render-order) diff --git a/init.lua b/init.lua index 7b063ee..ecb6056 100644 --- a/init.lua +++ b/init.lua @@ -438,6 +438,7 @@ function M.move_to(handle, x, y) p.facing = math.atan(dy, dx) p.last_move_x = x p.last_move_y = y + p.last_move_time = engine.time.now() end p.x = x p.y = y @@ -731,6 +732,41 @@ local function apply_locomotion(p, dt) ::continue:: end + + if (now - p.last_move_time) > loco.idle_return_delay then + local any_stepping = false + for i, _ in ipairs(loco.legs) do + if p.foot_state[i].state ~= "planted" then any_stepping = true; break end + end + if not any_stepping then + local best_i, best_dist = nil, 0 + for i, leg_def in ipairs(loco.legs) do + local fs = p.foot_state[i] + if fs.state == "planted" then + local hip_wx = p.bone_state[leg_def.leg].world.x + local hip_wy = p.bone_state[leg_def.leg].world.y + local d = math.sqrt((hip_wx - fs.anchor_x)^2 + (hip_wy - fs.anchor_y)^2) + if d > best_dist then + best_dist = d + best_i = i + end + end + end + if best_i and best_dist > 1 then + local fs = p.foot_state[best_i] + local hip_wx = p.bone_state[loco.legs[best_i].leg].world.x + local hip_wy = p.bone_state[loco.legs[best_i].leg].world.y + fs.state = "stepping" + fs.step_from_x = fs.cur_x + fs.step_from_y = fs.cur_y + fs.step_to_x = hip_wx + fs.step_to_y = hip_wy + fs.step_timer = 0 + fs.step_total = loco.idle_step_duration + p.last_move_time = now - loco.idle_return_delay + loco.idle_return_stagger + end + end + end end -- ==================================================================== diff --git a/manifest.lib b/manifest.lib index 94685a6..574b750 100644 --- a/manifest.lib +++ b/manifest.lib @@ -1,6 +1,6 @@ { "id": "lib-core.puppet", - "version": "0.2.0", + "version": "0.3.0", "api_min": "0.1", "deps": [] }