The per-frame sweep over physics, character, vehicles and nav found three fact makers that made a record per fact: contacts and splashes, the character's and the vehicles'. Each keeps a pool now, as wildlife's and npc's do (q_unheld). phys_sink, asked on every removal, built two new lists each time; it filters into a kept spare pair and swaps. ludic.nav's paths and crowds already allocate nothing, and none of the four builds a string per call. What remains at load, reset or a world swap is not per frame. vehicle_feed takes its state mut. vehicles_test: 1000 fed facts, drained turn by turn, use at most 4 records. All packages 421.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The package keeps the rules and hands them over each step: speed and heading, whether a jump
leaves, CHAR_STEP and the boots' slope limit. The walker (Jolt CharacterVirtual) does gravity,
landing, steps, slopes and following the ground down. Too steep is ground too steep facing the way
the body wanted to go; stepping off an edge is not. ludic.physics: phys_walker_move (follow the body
if something else moved it, then step), the slope limit, the jump always honoured. The character's
tests run on a real Jolt walker over the same fake world: 15 pass, a knee-high boulder now a step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ludic migrate state packages <every example program> packages/ludic.lab/example/plate.ludic
1804 vars into 126 states, 64 into lets; 23498 edits in 460 files
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>