Skip to content

Bring the LiveCode Kit + Contraption Builder to professional quality - #4

Merged
SethMorrowSoftware merged 18 commits into
mainfrom
claude/determined-heisenberg-uDx8a
Jun 6, 2026
Merged

Bring the LiveCode Kit + Contraption Builder to professional quality#4
SethMorrowSoftware merged 18 commits into
mainfrom
claude/determined-heisenberg-uDx8a

Conversation

@SethMorrowSoftware

@SethMorrowSoftware SethMorrowSoftware commented Jun 6, 2026

Copy link
Copy Markdown
Owner

Summary

A sweep to bring the xTalk/LiveCode side of box2dxt up to a professional, demo-ready level. The Contraption Builder example now exercises essentially every capability the Kit exposes, and the Kit itself is de-duplicated, hardened, and covered by CI.

Infrastructure & correctness

  • De-duplicate the Kit into the examples via tools/sync-embedded-kit.py (BEGIN/END sentinels keep the embedded copy byte-identical to src/box2dxt-kit.livecodescript).
  • Cross-platform CI (.github/workflows/build.yml): build + C smoke test on Linux/macOS/Windows, a fast "embedded Kit in sync" gate, and binary attach on published releases / manual dispatch.
  • Reserved-word safety pass — xTalk identifiers are case-insensitive, so e.g. pLayers was being read as the reserved word players. Renamed offenders and swept the builder for bare reserved-word locals.
  • Harden b2kReshape (atomic rebuild, preserves the sensor flag) and fix motor re-arm on Run.

New Builder capabilities (all driven by the Kit)

  • Sturdier bridges & chains, smooth rolling hills and a freehand Draw terrain tool (closed ground mass with correct chain winding so objects no longer fall through).
  • Collision layers, sensor trigger zones, the Servo (motor) joint, and world-feel presets (world tuning).
  • Comprehensive part inspector exposing as many options as each type supports — kinematic bodies, sleep threshold, image collision shape, damping, bullet, enable/disable.
  • More pre-built machines: Cannon, Trebuchet, Crane, Wrecking Ball — plus a categorised, scrollable Examples gallery.

This session

  • Per-part sensor toggle — flip any solid part (box/image/ball/capsule/poly) into a non-solid trigger zone from its Collide tab; turns translucent, fires enter/exit, saved/loaded. New Kit command b2kSetSensor.
  • Per-part collision filter (category/mask) — a popup with two rows of eight channel toggles: which channels a part is on and which it collides with. Built on b2kSetCategory / b2kSetMask, re-applied after every reshape, saved/loaded, click-away to dismiss.
  • Inspector tab fix — the Physics tab had outgrown the 10 visible rows, hiding the last settings (Collision layer, Sleep, In-simulation, Sensor). Re-balanced into a renamed Collide tab (collision + sim-state) with launch settings moved to Special, so every setting is reachable; tab widths evened out.
  • Collision settings survive a reshape — changing an image shape, the sensor flag, resizing, or loading now re-applies a part's collision layer and channel filter instead of dropping them.

Testing

  • python3 tools/sync-embedded-kit.py --check passes (embedded Kit in sync).
  • C smoke test green locally and in CI across all three platforms.
  • xTalk side is validated by the author in an OpenXTalk (OXT) environment.

🚧 Draft while the latest Builder additions are exercised in OXT.

https://claude.ai/code/session_01JbLzd79E6fNV5CWqSVcW9r

claude added 18 commits June 6, 2026 02:38
Bridges: span planks now use a new internal 'deckpin' joint — a hinge with
a fixed +/-30 deg flex limit (kBridgeFlex) — so a loaded deck sags but can
never fold back on itself. The limit is applied in connectJoint, so it is
re-derived when a saved bridge reloads (no new serialization needed); the
loader and world-anchor guard accept 'deckpin' alongside 'pin'.

Chains: span links are finer (up to 40, ~tD/18), lighter (density 0.3) and
start with a deeper drape, so a chain settles into a smoother catenary.
Chain links keep the free-pivoting 'pin'.

Both spans still save/load/delete as ordinary parts + joints. GUI-only
changes; the embedded Kit is untouched (sync check passes).
Kit: add b2kAddChain pControl, pPoints[, pLoop] — a smooth static chain
attached to a control (body at the control's centre, local points,
registered like b2kAddBox/Polygon) so it selects, drags, resizes, deletes
and saves as a normal static part, unlike the world-only b2kChain. Re-synced
into both examples.

Contraption builder: the Hill terrain now builds a smooth chain following an
undulating outline instead of a single convex polygon, so a fast body rides
over it without catching on seams. Bump count scales with width (narrow =
dome, wide = rolling). Integrated through the existing terrain paths:
placeTerrain (chain on create), regenTerrain/resizePart/setPartDimension
(rebuild the chain on resize/rotate/load via new rebuildHillChain), drag
(b2kMoveTo moves the centred chain), delete (b2kRemove destroys it), and
save/load (deterministic outline, no new format). Point order puts the solid
side under the surface.

GUI-only builder changes; the embedded Kit matches src (sync check passes).
Add a "Collision layer" setting (Physics tab) to every solid dynamic part:
0 collides with everything (default); layers 1-8 give the part a shared
negative collision group via b2kSetCollisionGroup, so parts on the same
layer pass through each other while still hitting the ground and other
layers. Wired through the inspector (partProps/propGroup/propLabel/
adjustPartProp), a new applyCollisionLayer helper, and save/load
(partSpecial "lyr"/applyPartSpecial). GUI-only; embedded Kit unchanged.
Add a "Sensor" SPECIAL part: a non-solid zone (b2kAddSensor) that parts
pass through, but when a dynamic body ENTERS it (on b2kSensorEnter) it
fires the same signal as a pressure plate — triggerSignal (arm bombs +
toggleAllMotors). Occupancy-tracked (gSensorCount) so it fires once on
0->1 and re-arms when empty; reset on clear and at the start of each Run.
Mirrors the plate across palette/dispatch/register/rebuild/partProps/
help/glyph and adds on b2kSensorExit.

Kit: b2kReshape now also preserves the sensor flag (not just sensor
events), so a resized sensor zone stays non-solid. Re-synced.

GUI changes + one Kit line; embedded Kit matches src (sync check passes).
Add a "Servo" joint tool backed by the Kit's b2kMotorTo. It drives part A
to hold its current position + angle relative to part B, yielding under
load and springing back (unlike a rigid weld) — good for self-righting
parts, soft platforms and return-to-home arms. New connectJoint "servo"
case (requires two parts; offset/angle captured at creation so it survives
save/load via the normal joint path), constants kServoForce/kServoTorque,
and palette/label/glyph/name/help wiring. No inspector props in v1, which
showJointInspector already handles gracefully. GUI-only; Kit unchanged.
Add a "World feel" cycle button to BUILD OPTIONS (beside Gravity) that
tunes the whole world via the Kit's world setters: Default, Bouncy (low
restitution threshold), Floaty (soft contacts + speed cap) and Snappy
(stiff contacts). New gWorldFeel state with cycle/apply/refresh/name
handlers, applied on cycle, re-applied each Run, and saved/loaded in the
"world" line (back-compatible: older saves default). GUI-only; Kit
unchanged. Preset values are a sensible first pass, tunable after testing.
Add a Draw terrain tool: press-drag across the field to sketch a ground
line (points sampled every kDrawStep px with a live preview), and on
release it becomes a smooth one-sided chain via b2kAddChain (open). Points
are normalised left->right so the solid side faces up (bodies rest on top).

New drawing input mode in mouseDown/mouseMove/mouseUp (gDragMode "draw"),
handlers beginDrawTerrain/extendDrawTerrain/finishDrawTerrain, a
placeDrawPlaceholder for load, and reversedLines/ptsPacked helpers. Saves/
loads as its own point list (partSpecial "pts=", packed with "|" since ";"
and TAB are taken; applyPartSpecial rebuilds the chain). Drags via b2kMoveTo,
deletes via b2kRemove. Palette/name/glyph/help wired; Hill help updated to
say "rolling". GUI-only; Kit unchanged.
- terrainPoints (smooth hill): rename bare locals env/bump/th/hx/hy/ih/yBase
  to t-prefixed (tEnv/tBump/tT/tHx/tHy/tIh/tYBase). 'env' is the kind of bare
  name that can collide with an xTalk reserved word (cf. pLayers/players);
  prefixing matches the file convention and removes the risk. Full-file scan
  of all locals/params now shows zero reserved-word collisions.
- ptsPacked: pack a drawn terrain's CURRENT points (the points of the graphic)
  instead of the stale uPoints, so dragging a Draw piece before saving no
  longer reverts it to its original spot on load. Also filter blank lines.

GUI-only; embedded Kit in sync.
- Drawn ground line is now kDrawLineSize (7px) instead of 4, so it reads as
  solid ground and is much easier to click/select (addresses the thin-line
  selection note).
- Add a Cannon recipe: static carriage + barrel (axis-aligned to avoid the
  rotated-static-body render gotcha) and a heavy ball that fires across the
  field on Run via the launch system, into a crate stack. Ball + barrel share
  a collision layer so the ball clears the barrel cleanly — also a showcase of
  the new collision-layer feature. Wired into the Examples menu (panel grown
  to fit) and the recipe dispatch.

GUI-only; embedded Kit in sync.
The freehand Draw terrain fell through / vanished: an open chain wound
left-to-right puts the solid side DOWN (Box2D: solid = right of travel; the
smoke test's ground runs right-to-left for solid-up), so bodies passed
through, and the thin open line was easy to miss.

Draw is now a filled, CLOSED ground mass: the drawn contour down to the
arena floor, laid out bottom-right / contour right-to-left / bottom-left so
the loop's solid side faces out and up. It renders as solid ground, is easy
to click, and collides reliably. Load restores it as a closed loop too.

The smooth Hill shared the same backwards loop winding (untested); its
outline is now reversed (reversedLines) so its solid side also faces out.

GUI-only; embedded Kit in sync. New locals are t-prefixed (no reserved-word
clashes); valid LC syntax throughout.
Three more one-click recipes, following the established pattern (place
parts -> connect joints -> set densities/launch -> renderBuild):

- Trebuchet: a long arm hinged off-centre, a heavy counterweight welded to
  the short end, a ball on the long end; gravity flings it on Run.
- Crane: a static mast + a motorised hinge jib (26 deg/s) with a load hung
  on a rope so it trails and sways.
- Wrecking Ball: a static gantry, a heavy roped ball given a sideways launch
  on Run to swing into a brick wall.

Careful with LC gotchas: static scenery is axis-aligned (exact static
render), parts are built before joints, nothing is teleported after
jointing. All locals are t-prefixed (no reserved-word clashes). Wired into
the Examples menu (panel grown) + dispatch. GUI-only; embedded Kit in sync.

Note: the flat Examples list is now near its practical limit (15 entries) —
a categorised, scrollable gallery is the next step for adding more.
… more)

Expose more of box2dxt per object so the demo covers the full surface:

- Body type is now a 3-way cycle dynamic -> static -> KINEMATIC. A kinematic
  part ignores gravity/collisions and moves at its launch velocity — i.e. a
  moving platform. propLabel/adjustProp updated; applyPartSpecial restores
  static AND kinematic (was static-only).
- Sleep threshold: every dynamic kind gets a "Sleeps below: N px/s" stepper
  (b2kSetSleepThreshold), saved/loaded as "slp".
- Imported images can choose their collision shape (box/ball/capsule) via a
  new "imgshape" property + uShape; a new partShape() helper routes resize/
  reshape through it so the choice survives resizing; saved/loaded as "ishp".
- Filled-out thin inspectors: Draw ground gets a colour; Anchors get bounce
  and grip (friction).

propGroup files the new keys under the right tabs (imgshape=shape, sleep=
physics). All keys are string literals; new helper/props are t-/u-prefixed.
Full-file scan: no reserved-word clashes. GUI-only; embedded Kit in sync.
- Examples menu rebuilt as a two-column gallery grouped into categories
  (Launchers / Machines / Chain Reactions / Toys & Tests) with headers, via
  a new recipeCategory() layout helper. Fits the card without scrolling and
  scales to more machines. kUIVersion bumped 3 -> 4 so the chrome rebuilds
  (buildUI clears old chrome first, so no duplicates).
- "Active" (in-simulation) toggle on every dynamic part -> b2kDisable/
  b2kEnable, saved/loaded as "act".

GUI-only; embedded Kit in sync. Full-file scan: no reserved-word clashes.
…shape

Add a 'sensor' inspector toggle to solid parts (box/image/ball/capsule/
poly) that flips them between solid and a non-solid trigger zone. A flagged
part fires the same b2kSensorEnter/Exit signal as the dedicated Sensor and
shows translucent so it reads as a ghost zone. Saved/restored via the 'sen'
special key, with a single reshape that also covers the image collision
shape.

Add b2kSetSensor to the Kit so the flag can be toggled on an existing part
(the shape is rebuilt with b2kReshape, which re-applies the sensor state).

Re-apply the collision layer after every b2kReshape (sensor/image-shape
toggle, resize, dimension edit, load) — a reshape builds a fresh shape with
a default filter, which previously dropped a part's collision layer.
…ettings

The Physics tab had grown to 14 settings but the inspector renders at most
10 rows, so Collision layer, Sleep, In-simulation and the new Sensor toggle
were silently cut off. Re-balance the tabs: Physics keeps the 10 material/
dynamics settings, a renamed Collide tab gathers the collision and
simulation-state settings (layer, sensor, active, sleep), and the launch
settings move to Special. Every setting a part offers is now reachable.

Bump kUIVersion so an older saved stack rebuilds its chrome with the new
tab button.
Add a Collision filter to every solid part, opened from its Collide tab: a
popup with two rows of eight channel toggles — the channels the part is on
(category) and the channels it collides with (mask). Two parts collide only
when each is on a channel the other collides with, so sets of parts can be
made to ignore one another while still resting on the ground. Built on the
Kit's b2kSetCategory / b2kSetMask, re-applied after every reshape (via a new
reapplyCollision helper), saved and loaded, and dismissable by clicking away.

The default (uncustomised) filter is left untouched so ordinary parts keep
hitting everything, and the panel defaults a part to colliding with all
channels so it stays on the floor unless deliberately changed.
Give the four inspector tabs even widths so the 7-character labels (Physics,
Collide, Special) never clip, and order the Collide tab so the collision
settings (layer, sensor, filter) lead, with the simulation-state ones
(in-simulation, sleep) after.
@SethMorrowSoftware
SethMorrowSoftware marked this pull request as ready for review June 6, 2026 15:17
@SethMorrowSoftware
SethMorrowSoftware merged commit f65d76b into main Jun 6, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants