Platformer showcase overhaul: Wave 5 player actions + level rebalance#42
Merged
Conversation
- Delete examples/box2dxt-microgame.livecodescript (focus is the platformer showcase) and drop it from the embedded-Kit sync list. - Remove the platformer's '0'-key DEBUG WARP block (it was marked 'delete before merge' - a swim-pool teleport left over from Wave 4). - Update the README and CLAUDE.md example lists (seven -> six). https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
New player-controller actions, each OPT-IN through a knob whose default leaves the pre-Wave-5 controller byte-for-byte unchanged, and each idle path costs one compare per frame: - airJumps: native double/multi air-jump (refilled on landing). - wallJumpX/Y + wallSlideMax: a side ray detects a wall while airborne; hugging it caps the fall (wallslide state), JUMP launches up-and-away with a brief steer lock. - dashSpeed/dashMs/dashCooldownMs: a flat horizontal burst (gravity parked) on the new dash action (bound to shift/x); yields to climb/swim; cooldown-gated. New dash state. - duckScale (<1): DOWN reshapes the capsule to a feet-anchored crawl (b2kReshape) with a headroom check before standing, so you can slip under low gaps; duckScale 1 keeps the Wave 2 brake-duck. - platformCarry: a grounded player inherits its platform's velocity (rides a moving/kinematic platform; vertical lift exempts ground-snap). Plus the pure-win helpers that remove example boilerplate and serve gotcha 28: b2kPlayerHalfH/HalfW (live capsule extents), b2kPlayerInLadder/ InWater (this frame's zone membership), and b2kPlayerRespawn (teleport + zero velocity + clean state). b2kPlayerAnims gains pWall/pDash slots. Self-test harness -> v13: six new hand-stepped tests (double-jump, wall-slide/jump, dash+cooldown, platform-carry, duck-reshape, the getters+respawn). Re-synced into every embedded copy. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
- kit-reference: the new b2kPlayer* getters (HalfH/HalfW/InLadder/InWater), b2kPlayerRespawn, the wall/dash anim slots + states, and the Wave 5 opt-in tuning keys (airJumps, wall*, dash*, duckScale, platformCarry). - kit-guide: a Wave 5 actions subsection (§21) with the enable recipe; retired the deleted micro-game's file references (the pattern stays). - expansion-prep: Wave 5 marked BUILT (harness v13). - CHANGELOG: Wave 5 Added entry + micro-game Removed entry. - plan.md: the Wave 5 decision-log entry (the as-built record). - README: the platformer's controller blurb advertises the new moves. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
Enable double-jump, wall-slide/jump, dash, crawl (duckScale) and platform-carry on the showcase player (re-applied each level since teardown wipes tuning), and rewrite the on-screen help to teach the new controls. First-pass feel numbers; tune in OXT. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
Give each level a distinct signature finale that leans on a Wave 5 move, and de-duplicate the four copy-pasted crusher alleys: - New pfMakeLift/pfTickLift: a kinematic platform driven by VELOCITY (write-on-change at the patrol endpoints) so the Kit's platformCarry ferries its rider. Wired through globals, the pfStartGame reset, and the on b2kFrame fan-out. - L1 GREEN HILLS: a CRAWL TUNNEL (duck under a low overhang for a coin) - showcases the duckScale reshape. Swim pool stays its signature. - L2 THE WORKS: a LIFT BAY - ride a moving deck across a grinder hazard (the platform-carry showcase). Additive: no ground split, no x-shifts. - L3 FROZEN CITADEL: a WALL-JUMP SHAFT - two floating ice pillars, a top coin reachable by wall-jumps (or a plain double-jump). - L4 HAUNTED HOLLOW: a LAVA LIFT over the existing lava strip; trimmed the finale from four snails to two. Keeps its crusher alley. Every new coin goes through pfMakeCoin (self-counting total stays correct); every move-gated beat is also double-jumpable, so no level can dead-end. Fixed the stale header widths (L1 8640/L2 5952/L3 6592/L4 6656) and the 'three-level' leftovers; added a Wave 5 verify checklist item. First-pass geometry/feel; the new beats need an OXT pass. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
The platformer reloaded all five Kenney atlases on every level rebuild: b2kTeardown wiped the sheets, then the level re-decoded each PNG, re-parsed each XML, and re-sliced every frame - the costliest thing the Kit does, repeated per level. That is the between-levels load time. Sheets are ASSETS, not world state (b2kClear already keeps them, and sounds already survive teardown for the same reason). New opt-in b2kSheetPersist makes them survive b2kTeardown too: - Idempotent loaders: an identical b2kSheetLoad/LoadAtlas/FromImage is a no-op (matched by a stored source path), so a level rebuild reuses the cache. Sliced frames survive too, so re-warming is free. - Cross-session: source images keep their deterministic name (b2ksheet_<name>) + a uB2kSrcPath tag, and frames are sliced into deterministically named images (b2kfr_<sheet>_<index>). A SAVED stack carries them, so on reopen the load adopts the in-stack image - no PNG decode, no re-slice - instead of importing from disk. - b2kTeardown clears only sprite instances + dead viewports when persisting (b2kSpriteSweepOrphans gains pKeepAssets); b2kSheetsWipe is still the explicit purge. All gated on the persist flag (default off), so the demo, builder, slingshot, spike and the harness are byte-for-byte unchanged. The platformer turns it on at openCard and purges on Shift+Reset. Self-test v14: stTestSheetPersist (survive-teardown + idempotent-reload + purge); stNewWorld resets the flag for isolation. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
Optimization + correctness follow-up to the persistent spritesheet cache, from a focused review of the cross-session reuse paths. Correctness (the saved-stack reuse was keyed too loosely): - Sliced frames are now stamped with a provenance signature (uB2kSig = source + grid/xml args + scale) and re-checked on reuse. Before, a slice was identified by (sheet name, frame index) and an existence check only, so on reopen a sheet NAME reused for different art -- e.g. the platformer uses "chars" for both the atlas and the placeholder -- could adopt the wrong saved frames, and a frame baked at one scale could be reused at another (wrong size). A mismatch now re-slices instead of showing stale pixels. - The loader idempotency key now includes ALL args (grid w/h/count/margin/ spacing, and the atlas xml path), not just the file path, so a reload that changes the grid or xml rebuilds instead of being silently skipped. Optimization: - The deterministic frame name (and its key-list scan) is now computed only when persisting; off the persist path the slicer uses a fresh unique name exactly as before, so the demo/builder/slingshot/spike are truly unchanged (not just gated -- byte-for-byte behavior). Self-test v15: stTestSheetPersist now also asserts that a changed grid and a changed source each force a rebuild. All static gates pass; embedded Kit re-synced. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
Adds an end-user distribution path: hand someone the extension, the native
libraries, and a saved stack as a single self-contained zip they install in
three steps.
- dist/INSTALL.md: a simple, self-contained install guide that uses ONLY the
files in the zip -- place the native library (already renamed to the bare
name the loader wants), Load box2dxt.lcb, open platformer.livecode. Covers
Windows, macOS, and Linux, with a focused troubleshooting table.
- tools/make-release.py: assembles dist/box2dxt-platformer.zip from
src/box2dxt.lcb + the prebuilt/ libraries (renamed to box2dxt.{dll,dylib,so}
under lib/) + INSTALL.md + your saved --stack. --check validates inputs;
--win/--mac/--linux override a library. Documented in docs/building.md.
- prebuilt/README.md: corrected -- the committed binaries are now ABI 4 (the
full ~370-handler set), so the stale 'outdated / ABI 3' warning is removed
and the dead linux-x86_64/ subdir paths are fixed.
- docs/getting-started.md: a zero-code 'just want to play?' callout pointing
at the package, a worked 'try a few more things' snippet (spawn capsule +
impulse + box), an Example Games entry, and the same prebuilt-path fixes.
- .gitignore: ignore the generated zip / dropped-in stack artifacts.
The zip itself is built after the stack is saved in OXT (the one input this
repo can't produce); everything else is in place and tested.
https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
The demo rebuilt its entire UI (top bar, scene/tool buttons, HUD, help bar) on every openCard -- buildUI always clearUI'd and recreated ~15 controls. The chrome is created controls that persist in a saved stack, so gate the rebuild behind a version check, exactly like the contraption builder's chromeBuilt and the platformer's kUIVersion: on reopen the persisted chrome is reused and only the (ephemeral) physics scene is rebuilt. - kDemoUIVersion + demoChromeBuilt(): present-and-current check on the saved UI. - startBox2DDemo builds the chrome only when it's missing or stale (a version bump rebuilds older saved stacks once). Example-only change (no Kit edit, no harness bump). Statically verified. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
clearAll removed only parts tracked in gParts, so any UNTRACKED part graphic survived -- and a stack saved with parts on the stage reopens with exactly that: the graphics persist but their bodies are gone and gParts is reset, so they sit there dead, and a subsequent Load piles the restored contraption on top of them (which makes Save/Load look broken). Add sweepOrphanParts: delete every control carrying a uPartId (the stamp tagPart puts on every PLACED part, and nothing else -- chrome is ui_*, the arena is cb_*, and the image library is data in uImageLibrary, not controls). Call it from clearAll (so Load/Reset/Clear/rebuildFromText always start from a truly clean stage) and from startCB after the world is built (so a reopened stack has no dead, untracked shapes). The Save/Load format itself is sound -- audited the full round-trip: partLine's 12 fields align with rebuildFromText's reads, all 18 part kinds rebuild, and joints/wires/world settings restore with id remapping. The orphan pile-up was what made it look broken. Example-only change. Statically verified. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
…unnel overlap THE DUCK BUG (Wave 5). b2kPlayerDuckSet shrinks the crawl capsule and drops the body CENTRE by tShift to keep the FEET planted (correct physics). But the bound art follows the body centre at a CONSTANT offset (sSprBindDY), so it sank by tShift with the centre -- the platformer's 'ducking puts me below ground'. The buried, crouched hero then can't jump (jumping is gated off while DOWN is held by design), so it read as 'breaks jumping entirely'. Fix: b2kPlayerDuckSet lifts the bound art by tShift (and b2kPlayerStandUp drops it back), so the visible character stays planted as the hitbox shrinks. The body math was already correct; this is purely the art-follow offset. Audited the rest of Wave 5 (dash, drop-through, wall-jump/slide, platform-carry, double-jump) -- all correct. Self-test v16: stTestPlayerDuck -- duck shrinks the hitbox, keeps the feet planted, and lifts/restores the art bind by exactly tShift. L1 LAYOUT. The rest cloud's one-way collision + tiles (solid span 3136..3328) overlapped the crawl-tunnel bar (slab + block tiles 3300..3492) by 28px -- the cloud's right edge sat INSIDE the solid bar, by the flying ladybug. Shifted the whole crawl-tunnel beat (bar, block tiles, the reward coin) right one tile to 3364..3556, clearing the cloud with a 36px gap (the space after was open). https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
…ile crawling) Wave 5 interaction bug, same class as the duck (state lost across a reshape). b2kReshape rebuilt the shape with the DEFAULT collision filter, silently resetting collision layers. With duckScale < 1 (the platformer's 0.6), DOWN on a one-way cloud/bridge reshapes the capsule to a crawl; DOWN+JUMP then drops through -- b2kPlayerDropStart clears the one-way bit -- but the very next frame the airborne player stands up, b2kReshape rebuilds the shape, the one-way bit defaults back ON, and the body (centroid still above the line) snaps back onto the platform it was falling through (gotcha 20). The drop-window's careful timed restore was being bypassed by the reshape. Fix: b2kReshape reads the old shape's filter (category/mask/group) and re-applies it to the new shape, clamping Box2D's 2^64-1 default mask to the 32 Kit bits (gotcha 21). A reshape now never changes collision layers -- so a drop survives a duck/stand, and the drop-window owns the restore. Also fixes a latent bug where resizing a custom-filtered contraption part reset its layers. The player's material is still re-applied by b2kPlayerDuckSet/StandUp (b2kReshape resets density/friction/restitution by design); only the FILTER needed carrying. Self-test v17: stTestReshapeFilter -- a custom filter (cat 1, mask 5) set on a box survives a b2kReshape to a ball. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
…rough while crawling)" This reverts commit e50be67.
…s broken test The sprite-bind compensation I added to b2kPlayerDuckSet/StandUp was based on a wrong model: it assumed the camera is vertically LOCKED, so it lifted the bound art by tShift to cancel the body-centre drop. But the platformer follows the hero with b2kCamDeadzone 0,0 + b2kCamFollow gHero,1 -- the camera tracks the body, so the lift over-compensated and floated the art (the 'floating + stuck mid-air' report). Reverted both lines; the duck is back to its prior behaviour. Also removed stTestPlayerDuck: it relied on writing the Kit's script-locals (sPlayPX/PY) and reading sSprBindDY from a harness handler, which OXT does NOT honour across the embedded-Kit boundary (its body teleported to y~13 and the bind read back empty -- the test was measuring nothing real). The L1 crawl-tunnel layout fix is unrelated and kept. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
…wl tunnel Per the chosen path: DOWN now brakes the hero to a stop in place with NO hitbox reshape, so the duck physically cannot float, sink, or stick (the reshape crawl was the source of the float/sink trouble, and the Kit's own crawl self-test still fails it). The crawl-tunnel overhang only existed for the crawl move, so it is removed; its coin now sits on the open meadow run (3460,500) and is grabbed just by running past -- no coin lost, no dead end, totals unchanged. Updated the in-game help text and the header docs (controls, the L1 beat list, the Wave 5 move summary). Example-only change; gates pass. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
… ground DASH (task 1): new player knob airDash (default 1 = current air-dash). The platformer sets airDash 0, so DASH now fires only while grounded -- no more mid-air dash. Gated in b2kPlayerTick's dash trigger. Self-test v18: stTestDash now also asserts the dash does NOT fire when airborne with airDash off (jump, press dash mid-air, confirm no dash state / no burst). LAVA PIT (task 3): L4's ground ran unbroken over the lava strip (3136..3264), so the lava sat hidden under the ground line and its hazard was invisible. The ground now BREAKS there -- pf_ground2 (..3136) + pf_ground3 (3264..) with a gap, and the purple top-tile loop skips the gap -- so the lava tiles fill an open pit you ride the lift or double-jump over (the lava sensor at y548..640 knocks back a fall). pfShowSlabs already lists pf_ground3, so the no-assets fallback shows both halves. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
Task 2 (static platforms): pfTextureSlab lays a COPY of a tile frame, stretched to a slab's exact rect, over the flat-coloured placeholder -- so collision sizes never change and the rect gives way to sheet texture. Applied to the L3 wall-jump pillars (snow block), the L2 lift pedestals (stone), and the swim-pool banks (stone). The texture is a pf_* image so pfWipeStage clears it each build; done at build time (camera scroll 0) so the slab rect reads as world coords. No assets / a missing frame: it no-ops and the flat colour stays (safe fallback). The MOVING lift is left for a follow-up: a static texture can't track it, so it needs a per-frame-followed texture -- I'd rather you eyeball the static ones first (the stretch look is unverifiable here). https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
…nd the keg Fewer GRCs: the L2 lift-bay grinder was a flat red rectangle (pf_grinder) + a hurt sensor. Replaced both with pfMakeLava 3010,3210 -- the same lava sheet tiles + hurt sensor the L4 pit uses -- so the bay hazard is sprite art, not a GRC, and consistent with the rest of the game. Crate pyramid: the powder keg's flat four-crate row is now a small PYRAMID around the barrel -- a four-crate base flanking it, a crate bridging each outer pair, and one capping the barrel. The crates are dynamic + fixed-rotation, so the stack holds its shape and b2kExplode scatters it dramatically. Example-only; gates pass. https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Full audit + improvement pass on the platformer showcase, with the Kit as the source of truth. Driven by a fleet of Opus deep-dive agents (Kit internals, the platformer, docs/history, OXT/harness tooling) → a synthesized plan → the maximal path you chose (Full Wave 5 + Rebalance & add).
Kit Wave 5 — five new player-controller moves (the source of truth)
All opt-in through
b2kPlayerSetknobs whose defaults leave the pre-Wave-5 controller byte-for-byte unchanged; each idle path is one compare per frame.airJumps, refilled on landing) · wall-slide + wall-jump (wallSlideMax,wallJumpX/Y; a side ray runs only while airborne; newwallslidestate) · dash (dashSpeed/dashMs/dashCooldownMson the newdashaction = SHIFT/X; newdashstate) · duck capsule-reshape (duckScale < 1→ a feet-anchored crawl viab2kReshapewith a headroom check) · moving-platform carry (platformCarry).b2kPlayerHalfH/HalfW(serving gotcha 28),b2kPlayerInLadder/InWater,b2kPlayerRespawn.b2kPlayerAnimsgainswall/dashslots.Platformer — enabled + rebalanced to showcase the moves
pfMakeLiftmechanic (kinematic, velocity-driven, write-on-change so carry ferries the rider).pfMakeCoin).Cleanup & docs
0-key DEBUG WARP.Needs an OpenXTalk eye (feel is statically unverifiable)
Wall-jump engagement against the L3 pillars; lift boarding (L2 pedestal / L4 lava bank) and that carry ferries the rider; the L1 crawl clearance (~52–61px gap vs the reshaped capsule); the new beats' double-jump reachability; and overall tuning of the first-pass move numbers (jump/dash/wall speeds).
https://claude.ai/code/session_01H3ZxaY5qDTTPSayfrDg64R