From 929d9d92df9ecdd179c0263f072fa08ac0be6fdd Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 13 Jun 2026 05:24:18 +0000 Subject: [PATCH 1/6] Third expansion: snails liberally, chained thwomps back, longer levels; boulder slides all the way User: longer classic levels with more clouds/coins/enemies/switches; snails used liberally; bring the old chained-weight thwomps back; and the L3 ice boulder should SLIDE ALL THE WAY in its direction (no teleport-reset). - BOULDER: lowered friction (coasts far) and moved its reset line off-screen-left (3050 -> 2300, past the run and below the camera edge while the hero is in it), so a fresh boulder arrives from the source instead of one teleporting back in place. The slide path is clear (the snow cloud is a one-way chain above; the saw is bodiless; the patrol slime sits left of 2300 so the re-park beats it there). - A fourth act on every level, finales shifted whole again (L1 +768 tail, L2 +640, L3 +512, L4 +512): the classic chained-weight THWOMPS return in all four levels (with ride-the-head coins), SNAILS go liberal (L4 now has four), plus more one-way clouds, coins, and decor. - New widths L1 6336 / L2 4672 / L3 5312 / L4 5376. Enemy indices unique per level; flags within bounds. Docs synced. Still zero Kit changes. https://claude.ai/code/session_012A6Dqdr3DPCeC3oWZohjHi --- CHANGELOG.md | 24 ++- examples/box2dxt-platformer.livecodescript | 206 ++++++++++++++------- plan.md | 1 + 3 files changed, 152 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5be270a..0d95a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,14 +66,22 @@ The native shim's ABI is tracked separately by `b2Version()` (currently `4`). (spike-type — hurts from every side). L4's PIRANHA row is now twice as long (four burrows on staggered timers). - **Longer, re-spaced levels** (the layout law: widen before squeezing - a beat in), grown across two passes to L1 5568, L2 4032, L3 4800, - L4 4864. Existing verified beats are preserved in place; each level's - walled-door / steps finale shifts as a whole. New classic acts: a L1 - "meadow gauntlet" (a one-way cloud over two slimes + a darting fly); a - L2 second machine bay (a third chained crusher + an always-on sweeping - saw); a L3 second snow cloud + a final glacier slime; a L4 "bowling - lane" (a second snail to shell-and-kick + a slime to bowl over). More - decor and a live `awake N/M` body count on the HUD. + a beat in), grown across three passes to L1 6336, L2 4672, L3 5312, + L4 5376. Existing verified beats are preserved in place; each level's + walled-door / steps finale shifts as a whole. Classic acts stacked on: + L1 a "meadow gauntlet" + a "homeward run" (chained thwomp + snail under + a cloud); L2 second and third machine bays (chained crushers + an + always-on saw + a snail); L3 a second snow cloud + a glacier slime, + snail and thwomp; L4 a "bowling lane" plus a snail-heavy finale. + **Snails are now used liberally** (L4 carries four), the **classic + chained-weight thwomps are back** in every level alongside L4's faced + crushers, and there are more clouds, coins, and decor throughout. A + live `awake N/M` body count on the HUD. + - **The L3 ice boulder slides ALL THE WAY** in its direction now (per + the user: "it is an ice block/boulder") - lower friction so it coasts + far, and its reset line moved off-screen-left (past the run, below the + camera edge) so a fresh one comes from the source rather than the old + one teleporting back in place. - **User review fixes:** the SNAIL faced backwards relative to travel (gotcha 26 — its sheet art is mirrored vs the slimes'); a per-row `gSlimeFlip` polarity column inverts only its flip. The barrel's diff --git a/examples/box2dxt-platformer.livecodescript b/examples/box2dxt-platformer.livecodescript index 88f9db2..e4bce06 100644 --- a/examples/box2dxt-platformer.livecodescript +++ b/examples/box2dxt-platformer.livecodescript @@ -30,7 +30,7 @@ -- CURRENT level · ESC pauses · M mutes the synthesized sound -- -- THE FOUR LEVELS (every beat holds a coin; the flag advances): --- LEVEL 1 GREEN HILLS (5568px) - movement + the toys: the +-- LEVEL 1 GREEN HILLS (6336px) - movement + the toys: the -- SPRINGBOARD mid-meadow (sky coin above; a 42px hop for -- non-bouncers), the BONK ROW (headbutt ?-boxes, SMASH bricks -- into debris), the one-way BRIDGE over a spike slime, the @@ -38,7 +38,7 @@ -- SPIKE PIT, a fast MOUSE, a flying ladybug - then the -- finale: a sagging ROPE BRIDGE (hinged planks) over a chasm -- and a ladybug on the far meadow to the flag. --- LEVEL 2 THE WORKS (4032px) - the machines: drag the +-- LEVEL 2 THE WORKS (4672px) - the machines: drag the -- CRATE onto the yellow BUTTON to open the gate (coin in -- the gateway), the Wave 2 LADDER up to its bonus ledge, -- the red CHECKPOINT, the saw LEVER (STAND at it to power @@ -49,7 +49,7 @@ -- and the WALLED DOOR - a stone -- curtain to the ceiling, so the stone steps, the coins and -- the FLAG behind it are reachable ONLY through the door. --- LEVEL 3 FROZEN CITADEL (4800px) - everything at once, +-- LEVEL 3 FROZEN CITADEL (5312px) - everything at once, -- on ICE (~15% acceleration: momentum rules): a ladybug, -- spring over the first spiked pit, a bonk row, the first -- sweeping saw, a second spiked pit, a checkpoint, a thwomp @@ -57,7 +57,7 @@ -- saw under a snow cloud) with a ROLLING BOULDER that slides -- the ice head-on (leap it), the red walled door, snow -- steps, the final flag. --- LEVEL 4 HAUNTED HOLLOW (4864px) - WAVE 3's bestiary in +-- LEVEL 4 HAUNTED HOLLOW (5376px) - WAVE 3's bestiary in -- the purple biome: a MIMIC field (grass blocks that do -- not belong - they wake and lunge), the SNAIL (stomp it -- into a SHELL, kick the shell, bowl the slime down the @@ -1489,7 +1489,7 @@ command pfMakeBoulder pX, pY, pResetX, pVX put the result into tRef if tRef is empty then exit pfMakeBoulder b2kSetDensity tRef, 3.0 - b2kSetFriction tRef, 0.35 + b2kSetFriction tRef, 0.18 -- low grip: it COASTS far on the ice b2kSetBounce tRef, 0.08 b2kNoCollide tRef, gHero b2kSetStatic tRef -- parked until the timer releases it @@ -1569,7 +1569,7 @@ command pfMakeWoodCrate pX, pY end pfMakeWoodCrate -- ===================================================================== --- LEVEL 1 - "GREEN HILLS" (5568px). Movement and the Wave 1 toys: +-- LEVEL 1 - "GREEN HILLS" (6336px). Movement and the Wave 1 toys: -- springboard + sky coin, the bonk row, the one-way bridge over the -- spike slime, the slope mound, two one-way clouds, the spike pit, a -- skittering MOUSE and a flying ladybug, then the showcase finale: a @@ -1579,10 +1579,10 @@ end pfMakeWoodCrate command pfL1Scene local tX put "GREEN HILLS" into gLevelName - pfBounds 5568 + pfBounds 6336 pfSlab "pf_ground1", 0, 576, 2560, 640 pfSlab "pf_ground2", 2752, 576, 3968, 640 - pfSlab "pf_ground3", 4288, 576, 5568, 640 -- the far meadow + the sky-island gauntlet + pfSlab "pf_ground3", 4288, 576, 6336, 640 -- the far meadow + the sky-island gauntlet + the homeward run if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_grass_block_top") then repeat with tX = 0 to 2496 step 64 if tX >= 1376 and tX <= 1696 then @@ -1595,7 +1595,7 @@ command pfL1Scene repeat with tX = 2752 to 3904 step 64 pfTile "terrain_grass_block_top", tX, 576 end repeat - repeat with tX = 4288 to 5504 step 64 + repeat with tX = 4288 to 6272 step 64 pfTile "terrain_grass_block_top", tX, 576 end repeat pfShowSlabs false @@ -1719,7 +1719,24 @@ command pfL1Scene end if if gAssetsOK is true and b2kSheetHasFrame("tiles", "bush") then pfTile "bush", 5160, 512 - pfTile "mushroom_red", 5440, 512 + pfTile "mushroom_red", 5560, 512 + pfTile "bush", 5980, 512 + pfTile "mushroom_brown", 6200, 512 + end if + -- the HOMEWARD RUN (a fourth act): a chained thwomp + a snail (in the + -- cast) under a final one-way cloud (ghost-padded a tile past the art) + b2kSmoothGround "6296,448" & cr & "6232,448" & cr & "6040,448" & cr & "5976,448" + if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_grass_cloud_left") then + pfTile "terrain_grass_cloud_left", 6040, 448 + pfTile "terrain_grass_cloud_middle", 6104, 448 + pfTile "terrain_grass_cloud_right", 6168, 448 + else + create graphic "pf_cloudledgeG" + set the style of it to "line" + set the points of it to "6040,448" & cr & "6232,448" + set the lineSize of it to 4 + set the foregroundColor of it to "70,190,110" + b2kCamAdopt the long id of graphic "pf_cloudledgeG" end if if gToysOK is true then -- the spring mid-meadow: bouncing happens in open air, well away @@ -1750,7 +1767,10 @@ command pfL1Cast pfMakeCoin 4832, 392 -- up on the gauntlet's one-way cloud pfMakeCoin 5040, 500 -- the first gauntlet slime's beat pfMakeCoin 5232, 500 -- under the darting fly - pfMakeCoin 5360, 500 -- the last slime's beat, before the flag + pfMakeCoin 5360, 500 -- the last gauntlet slime's beat + pfMakeCoin 5700, 184 -- ride the chained thwomp's head up to it + pfMakeCoin 5900, 500 -- the homeward snail's beat (shell it, kick it) + pfMakeCoin 6136, 392 -- up on the final one-way cloud, by the flag if gToysOK is true then pfMakeCoin 240, 230 -- the sky coin (spring up) -- the bee tours the mound (harmless); the fly guards the cloud coin; -- a flying LADYBUG patrols the second-act meadow (new aerial variety) @@ -1798,15 +1818,19 @@ command pfL1Cast pfAddMover tRef, 5232, 452, 80, 720, 30, 480, 34, 40, true end if end if + -- the HOMEWARD RUN: the classic chained-weight THWOMP returns (ride its + -- resting head up for the high coin), and a SNAIL to shell-and-kick + pfMakeThwomp 1, 5700 + pfMakeSnail 9, 5900, 5840, 5960 -- a CHECKPOINT just before the rope bridge: a slip into the chasm is -- then a quick retry at the brink, never a replay of the whole level pfMakeCheckpoint 3856 if gToysOK is true then pfMakeDebrisPool -- this level has bricks - pfMakeGoal 5500, 544 + pfMakeGoal 6280, 544 end pfL1Cast -- ===================================================================== --- LEVEL 2 - "THE WORKS" (4032px). The machines: crate + button gate, +-- LEVEL 2 - "THE WORKS" (4672px). The machines: crate + button gate, -- the Wave 2 ladder to its bonus ledge, the stand-to-flip saw lever, -- both saws, a crawling WORM, chained thwomps, a breather cloud with its -- bee, a SECOND machine bay (another chained crusher + an always-on @@ -1817,26 +1841,26 @@ end pfL1Cast command pfL2Scene local tX, tRef put "THE WORKS" into gLevelName - pfBounds 4032 - pfSlab "pf_ground1", 0, 576, 4032, 640 - pfSlab "pf_plat1", 3648, 512, 3840, 576 - pfSlab "pf_plat2", 3840, 448, 3968, 576 + pfBounds 4672 + pfSlab "pf_ground1", 0, 576, 4672, 640 + pfSlab "pf_plat1", 4288, 512, 4480, 576 + pfSlab "pf_plat2", 4480, 448, 4608, 576 if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_grass_block_top") then - repeat with tX = 0 to 3968 step 64 - if tX >= 3648 then + repeat with tX = 0 to 4608 step 64 + if tX >= 4288 then -- the finale plays in STONE past the walled door pfTile "terrain_stone_block_top", tX, 576 else pfTile "terrain_grass_block_top", tX, 576 end if end repeat - pfTile "terrain_stone_block_top_left", 3648, 512 - pfTile "terrain_stone_block_top", 3712, 512 - pfTile "terrain_stone_block_top_right", 3776, 512 - pfTile "terrain_stone_block_top", 3840, 448 - pfTile "terrain_stone_block_top", 3904, 448 - pfTile "terrain_stone_block_center", 3840, 512 - pfTile "terrain_stone_block_center", 3904, 512 + pfTile "terrain_stone_block_top_left", 4288, 512 + pfTile "terrain_stone_block_top", 4352, 512 + pfTile "terrain_stone_block_top_right", 4416, 512 + pfTile "terrain_stone_block_top", 4480, 448 + pfTile "terrain_stone_block_top", 4544, 448 + pfTile "terrain_stone_block_center", 4480, 512 + pfTile "terrain_stone_block_center", 4544, 512 pfShowSlabs false else pfShowSlabs true @@ -1911,9 +1935,27 @@ command pfL2Scene pfTile "sign", 2400, 512 pfTile "bush", 3160, 512 end if + -- a THIRD machine run: a snail + another chained crusher (in the cast) + -- under a one-way cloud (ghost-padded a tile past the art each side) + b2kSmoothGround "3956,448" & cr & "3892,448" & cr & "3700,448" & cr & "3636,448" + if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_grass_cloud_left") then + pfTile "terrain_grass_cloud_left", 3700, 448 + pfTile "terrain_grass_cloud_middle", 3764, 448 + pfTile "terrain_grass_cloud_right", 3828, 448 + else + create graphic "pf_cloudledgeH" + set the style of it to "line" + set the points of it to "3700,448" & cr & "3892,448" + set the lineSize of it to 4 + set the foregroundColor of it to "70,190,110" + b2kCamAdopt the long id of graphic "pf_cloudledgeH" + end if + if gAssetsOK is true and b2kSheetHasFrame("tiles", "bush") then + pfTile "bush", 3400, 512 + end if if gToysOK is true then pfMakeLever 1120 -- stand here to power the FIRST sweep saw down - pfMakeKeyDoor 1740, 500, 3424, "yellow" + pfMakeKeyDoor 1740, 500, 4064, "yellow" end if end pfL2Scene @@ -1926,9 +1968,12 @@ command pfL2Cast pfMakeCoin 2210, 392 -- on the breather cloud (the bee patrols it) pfMakeCoin 2680, 500 -- past the second bay's chained crusher pfMakeCoin 2880, 448 -- above the second sweeping saw: time the hop - pfMakeCoin 3780, 448 -- over the first stone step - pfMakeCoin 3854, 392 -- over the top step, by the flag - if gToysOK is true then pfMakeCoin 3472, 510 -- inside the door passage + pfMakeCoin 3300, 500 -- the third bay's snail (shell it, kick it) + pfMakeCoin 3600, 184 -- ride the third chained crusher's head up + pfMakeCoin 3796, 392 -- up on the third bay's one-way cloud + pfMakeCoin 4420, 448 -- over the first stone step + pfMakeCoin 4494, 392 -- over the top step, by the flag + if gToysOK is true then pfMakeCoin 4112, 510 -- inside the door passage -- the bee patrols the breather cloud if gAssetsOK is true and b2kSheetHasFrame("foes", "bee_a") then b2kSpriteNew "foes", "bee_a", 2210, 310 @@ -1971,13 +2016,16 @@ command pfL2Cast put tRef into gSawSpr end if end if - pfMakeSlime 1, "normal", 3734, 3684, 3820, 512 + pfMakeSlime 1, "normal", 4374, 4324, 4460, 512 -- a slow WORM crawls the run before the breather cloud (new variety; -- native foes art, stomps like any walker) pfMakeCritter 2, "normal", 1990, 1920, 2060, 576, 38, "worm_normal_move_a", "wormwalk", empty, 22, 26, "210,120,170", -2 + -- the THIRD bay's snail (snails liberally now) and a third chained crusher + pfMakeSnail 3, 3300, 3240, 3360 pfMakeThwomp 1, 1640 pfMakeThwomp 2, 1840 pfMakeThwomp 3, 2560 -- the second bay's chained crusher + pfMakeThwomp 4, 3600 -- the third bay's chained crusher -- a SECOND sweeping saw, ALWAYS on (the lever powers only the first): -- a bodiless mover hurting by proximity, sweeping the second bay floor if gAssetsOK is true and b2kSheetHasFrame("foes", "saw_a") then @@ -1989,11 +2037,11 @@ command pfL2Cast end if end if pfMakeCheckpoint 1000 - pfMakeGoal 3924, 416 + pfMakeGoal 4564, 416 end pfL2Cast -- ===================================================================== --- LEVEL 3 - "FROZEN CITADEL" (4800px). Everything at once, on ICE: +-- LEVEL 3 - "FROZEN CITADEL" (5312px). Everything at once, on ICE: -- snow ground with quarter-strength player acceleration (momentum!), a -- ladybug, the spring arcs over the first spiked pit, a bonk row, the -- sweeping saw, a second spiked pit, a thwomp guarding the RED key, @@ -2003,12 +2051,12 @@ end pfL2Cast command pfL3Scene local tX put "FROZEN CITADEL (icy!)" into gLevelName - pfBounds 4800 + pfBounds 5312 pfSlab "pf_ground1", 0, 576, 768, 640 pfSlab "pf_ground2", 960, 576, 1792, 640 - pfSlab "pf_ground3", 1984, 576, 4800, 640 - pfSlab "pf_plat1", 4128, 512, 4320, 576 - pfSlab "pf_plat2", 4320, 448, 4448, 576 + pfSlab "pf_ground3", 1984, 576, 5312, 640 + pfSlab "pf_plat1", 4640, 512, 4832, 576 + pfSlab "pf_plat2", 4832, 448, 4960, 576 if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_snow_block_top") then repeat with tX = 0 to 704 step 64 pfTile "terrain_snow_block_top", tX, 576 @@ -2016,16 +2064,16 @@ command pfL3Scene repeat with tX = 960 to 1728 step 64 pfTile "terrain_snow_block_top", tX, 576 end repeat - repeat with tX = 1984 to 4736 step 64 + repeat with tX = 1984 to 5248 step 64 pfTile "terrain_snow_block_top", tX, 576 end repeat - pfTile "terrain_snow_block_top_left", 4128, 512 - pfTile "terrain_snow_block_top", 4192, 512 - pfTile "terrain_snow_block_top_right", 4256, 512 - pfTile "terrain_snow_block_top", 4320, 448 - pfTile "terrain_snow_block_top", 4384, 448 - pfTile "terrain_snow_block_center", 4320, 512 - pfTile "terrain_snow_block_center", 4384, 512 + pfTile "terrain_snow_block_top_left", 4640, 512 + pfTile "terrain_snow_block_top", 4704, 512 + pfTile "terrain_snow_block_top_right", 4768, 512 + pfTile "terrain_snow_block_top", 4832, 448 + pfTile "terrain_snow_block_top", 4896, 448 + pfTile "terrain_snow_block_center", 4832, 512 + pfTile "terrain_snow_block_center", 4896, 512 pfShowSlabs false else pfShowSlabs true @@ -2071,7 +2119,7 @@ command pfL3Scene pfMakeBonk "box", 1296 pfMakeBonk "brick", 1360 pfMakeBonk "box", 1424 - pfMakeKeyDoor 2268, 500, 3968, "red" + pfMakeKeyDoor 2268, 500, 4480, "red" end if end pfL3Scene @@ -2092,11 +2140,13 @@ command pfL3Cast pfMakeCoin 3250, 500 -- in the boulder run: snatch it between slides pfMakeCoin 3640, 500 -- the final glacier slime's beat pfMakeCoin 3796, 392 -- up on the second snow cloud - pfMakeCoin 4258, 448 -- over the first snow step - pfMakeCoin 4353, 392 -- over the top step, by the flag + pfMakeCoin 4040, 500 -- the glacier snail's beat (shell it, kick it) + pfMakeCoin 4280, 184 -- ride the chained thwomp's head up to it + pfMakeCoin 4770, 448 -- over the first snow step + pfMakeCoin 4865, 392 -- over the top step, by the flag if gToysOK is true then pfMakeCoin 864, 300 -- over the first pit: ride the spring's arc - pfMakeCoin 4016, 510 -- inside the red door's passage + pfMakeCoin 4528, 510 -- inside the red door's passage end if if gAssetsOK is true and b2kSheetHasFrame("foes", "saw_a") then b2kSpriteNew "foes", "saw_a", 1640, 548 @@ -2121,21 +2171,27 @@ command pfL3Cast -- a LADYBUG ambles the early ice (new variety; native foes art) pfMakeCritter 2, "normal", 1100, 1010, 1180, 576, 64, "ladybug_walk_a", "ladywalk", empty, 22, 30, "200,70,70", -4 pfMakeThwomp 1, 2208 -- it guards the red key - -- the glacier's ROLLING BOULDER: it slides the icy flat HEAD-ON at the - -- climbing hero, who must leap it (jumping lifts him clear of the poll). - -- It passes through him (b2kNoCollide) and recycles forever, never - -- reaching the kill floor. Built in the CAST so the hero exists. - pfMakeBoulder 3450, 549, 3050, -260 + -- the glacier's ROLLING ICE BOULDER: it slides the icy flat HEAD-ON at + -- the climbing hero, who must leap it (jumping lifts him clear of the + -- poll). It passes through him (b2kNoCollide) and SLIDES ALL THE WAY + -- off-screen left (reset line 2300, well past the run and below the + -- camera's left edge) before a fresh one comes from the source - no + -- teleport-in-place. Built in the CAST so the hero exists. + pfMakeBoulder 3450, 549, 2300, -320 -- a final glacier slime under the second snow cloud, clear to the right -- of the boulder's home so the parked stone never jostles it pfMakeSlime 3, "normal", 3640, 3580, 3700, 576 + -- the citadel's final stretch: a SNAIL and the classic chained THWOMP + -- back, guarding the run to the red door + pfMakeSnail 4, 4040, 3980, 4100 + pfMakeThwomp 2, 4280 pfMakeCheckpoint 2048 if gToysOK is true then pfMakeDebrisPool -- this level has bricks - pfMakeGoal 4408, 416 + pfMakeGoal 4920, 416 end pfL3Cast -- ===================================================================== --- LEVEL 4 - "HAUNTED HOLLOW" (4864px). Wave 3's bestiary in the purple +-- LEVEL 4 - "HAUNTED HOLLOW" (5376px). Wave 3's bestiary in the purple -- biome: a MIMIC field (grass blocks that do not belong), the SNAIL -- whose kicked shell bowls a slime over, the BAT overhang, a pit, a long -- FOUR-burrow PIRANHA row, the GHOST stalking the back half, a pair of @@ -2147,27 +2203,27 @@ end pfL3Cast command pfL4Scene local tX, tH, tHi put "HAUNTED HOLLOW (spooky!)" into gLevelName - pfBounds 4864 + pfBounds 5376 pfSlab "pf_ground1", 0, 576, 1856, 640 - pfSlab "pf_ground2", 2048, 576, 4864, 640 - pfSlab "pf_plat1", 4448, 512, 4640, 576 - pfSlab "pf_plat2", 4640, 448, 4768, 576 + pfSlab "pf_ground2", 2048, 576, 5376, 640 + pfSlab "pf_plat1", 4960, 512, 5152, 576 + pfSlab "pf_plat2", 5152, 448, 5280, 576 -- the bat overhang: a stone bar the bats roost under pfSlab "pf_batbar", 1472, 256, 1856, 320 if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_purple_block_top") then repeat with tX = 0 to 1792 step 64 pfTile "terrain_purple_block_top", tX, 576 end repeat - repeat with tX = 2048 to 4800 step 64 + repeat with tX = 2048 to 5312 step 64 pfTile "terrain_purple_block_top", tX, 576 end repeat - pfTile "terrain_purple_block_top_left", 4448, 512 - pfTile "terrain_purple_block_top", 4512, 512 - pfTile "terrain_purple_block_top_right", 4576, 512 - pfTile "terrain_purple_block_top", 4640, 448 - pfTile "terrain_purple_block_top", 4704, 448 - pfTile "terrain_purple_block_center", 4640, 512 - pfTile "terrain_purple_block_center", 4704, 512 + pfTile "terrain_purple_block_top_left", 4960, 512 + pfTile "terrain_purple_block_top", 5024, 512 + pfTile "terrain_purple_block_top_right", 5088, 512 + pfTile "terrain_purple_block_top", 5152, 448 + pfTile "terrain_purple_block_top", 5216, 448 + pfTile "terrain_purple_block_center", 5152, 512 + pfTile "terrain_purple_block_center", 5216, 512 pfTile "terrain_purple_horizontal_left", 1472, 256 pfTile "terrain_purple_horizontal_middle", 1536, 256 pfTile "terrain_purple_horizontal_middle", 1600, 256 @@ -2228,8 +2284,11 @@ command pfL4Cast pfMakeCoin 3892, 500 -- past the powder keg (the lure: mind the blast) pfMakeCoin 4000, 500 -- the second snail's beat (shell it, kick it!) pfMakeCoin 4220, 500 -- the slime its bowled shell can flatten - pfMakeCoin 4544, 448 -- over the first purple step - pfMakeCoin 4672, 392 -- over the top step, by the flag + pfMakeCoin 4400, 500 -- a THIRD snail's beat + pfMakeCoin 4600, 184 -- ride the chained crusher's head up to it + pfMakeCoin 4780, 500 -- a FOURTH snail's beat + pfMakeCoin 5056, 448 -- over the first purple step + pfMakeCoin 5184, 392 -- over the top step, by the flag pfMakeSnail 1, 980, 880, 1080 pfMakeSlime 2, "normal", 1255, 1180, 1330, 576 -- split patrol bands: two flying BODIES sharing one band would @@ -2254,8 +2313,13 @@ command pfL4Cast -- and a slime its sliding shell flattens - the haunted level escalates pfMakeSnail 8, 4000, 3940, 4060 pfMakeSlime 9, "normal", 4220, 4160, 4280, 576 + -- the haunted FINALE: snails used liberally (two more) around the + -- classic chained-weight THWOMP coming back among the faced crushers + pfMakeSnail 10, 4400, 4340, 4460 + pfMakeThwomp 3, 4600 + pfMakeSnail 11, 4780, 4720, 4840 pfMakeCheckpoint 2160 - pfMakeGoal 4732, 416 + pfMakeGoal 5244, 416 end pfL4Cast diff --git a/plan.md b/plan.md index 70382ae..77fb71f 100644 --- a/plan.md +++ b/plan.md @@ -317,3 +317,4 @@ user-confirmed in OXT before the next begins. | 2026-06-13 | **Platformer SHOWCASE polish round (pre-Wave-4; user direction: "show off the kit as it stands today, classic platformers in mind, more sprites + enemies").** A deep-dive survey found the kit's biggest DARK subsystem is JOINTS/dynamics (the demo used 90 of the kit's handlers but zero joints), so the polish draws its four marquee mechanisms from there — all **example-side, ZERO Kit changes, no harness bump** (rule 2), matching how Waves 1 and 3 shipped. Calls: (1) **only hazards the player TIMES/AVOIDS, never rides** — the controller has no platform-carry until a later wave, so a ridable moving platform would slide the player off (verified in `b2kPlayerTick`); a wrecking ball (hinge+motor), a sagging rope bridge (the canonical revolute-joint bridge, hinged planks pinned both ends), a sliding boulder, and an exploding barrel all sidestep that. (2) **Verdicts POLLED, bodies `b2kNoCollide` with the hero** (wrecking ball, boulder) so a solid shove never fights the controller's vx-assert/ground-snap; the pendulum head is found flip-proof at `pivot + 2*(centre - pivot)` (no angle read). (3) The boulder **recycles on a cycle, re-parked the instant it passes its reset line** so it never reaches the kill floor and never needs a respawn; a signed release velocity covers both a flat icy SLIDE (L3) and a ramp roll. (4) New enemy SPECIES (mouse/worm/ladybug/fire slime) **join the slime family** as ordinary patrollers via a new `pfMakeCritter` + two table columns (`gSlimeSpeed`, `gSlimeFlat`) — the variety-not-archetypes choice keeps frog/spider/barnacle for their planned Wave 6. (5) Levels **extended and re-spaced per the layout law** (L1 4800 / L2 3712 / L3 4416 / L4 4288); each walled-door/steps finale shifts as a WHOLE so the proven beats survive, and a new L1 checkpoint at the bridge brink makes a fall a retry, not a replay. Perf: every new tick idles at one compare and shares the one hero snapshot; the HUD now prints `awake N/M`. `b2kTeardown` destroys the world, so the first-ever joints clean up with it. Statically verified; awaiting the OXT pass. | User direction; this commit | | 2026-06-13 | **Showcase round, user review pass: the wrecking ball is CUT; the boulder gets a sprite face; the piranha row doubles.** The user's call surfaced the SPRITE-ONLY visuals policy (expansion-prep §62) sharply: "the cannonball needs to go — if we cannot use sprites for it, then remove it." A swinging arm/chain ROTATES, and sprites do not (gotcha 23), so the wrecking ball could only ever be a plain graphic — it was removed (maker, tick, globals, the L2 demolition bay now an open breather, which also serves "some things are too close together"). The boulder, by contrast, is ROUND — rotation-agnostic — so it earns a real sprite face (`rock` over the invisible ball, bound so it tracks roll + re-park); the rope-bridge planks stay graphics by necessity (a sagging deck must rotate) but read as wood. L4's PIRANHA row went from two burrows to FOUR (the user: "twice as long"), and the lava/crushers/fire-slime/barrel/steps shifted +192 (tile-aligned) to give the longer row clear air. Lesson reaffirmed: **sprite-only is a hard line for VISIBLE game art — if a mechanic's art must rotate and no sprite fits, cut the mechanic rather than ship a plain graphic.** | User review; this commit | | 2026-06-13 | **Showcase round 2: levels expanded further with classic-platformer acts; two precise sprite/facing fixes.** User: "expand the levels a bit further following all our best practices; get rid of the brown boxes around the bomb (use the crate sprite); everything else should use the provided sprites; reverse the snail (it faces backwards travelling forwards)." Done: (1) the SNAIL flip — a per-row `gSlimeFlip` polarity column XORed into all four `pfTickSlimes` flips; only the snail is `true` (its sheet art mirrors the slimes'), so slimes/mice/worms/ladybugs are byte-identical and only the snail inverts (gotcha 26). (2) The barrel's WOODPILE is now `pfMakeWoodCrate` — the `block_empty` crate sprite over an invisible FIXED-ROTATION spawned box (the L2 gate-crate pattern), so a blast slides the crates without the sprite-cannot-rotate problem (gotcha 23) — no more brown rectangles. (3) Each level gained a classic act, finales shifted as a WHOLE by a tile-aligned offset (L1 +768 tail, L2 +320, L3 +384, L4 +384): L1 a meadow gauntlet (one-way cloud + two slimes + a fly), L2 a second machine bay (a 3rd chained crusher + an ALWAYS-on saw — not on `gSawMov`, so the lever can't disable it), L3 a second snow cloud + a final glacier slime placed clear of the boulder's parked home, L4 a bowling lane (a 2nd snail + a slime to bowl). New widths L1 5568 / L2 4032 / L3 4800 / L4 4864; enemy indices stay unique per level; every new beat on solid ground within bounds. Still zero Kit changes. | User review; this commit | +| 2026-06-13 | **Showcase round 3: a third expansion + the ice-boulder physics fix.** User: "expand a little further — more clouds, coins, enemies, switches, gates; snails liberally; the OLD chained thwomps back; mimic iconic classics; and the ice boulder should SLIDE ALL THE WAY, not reset (it is an ice block)." Done: (1) BOULDER — lowered its friction to 0.18 so it coasts, and moved its reset line off-screen-left (3050 → 2300, past the run and below the camera's left edge while the hero is in it), so a fresh one arrives from the source instead of a single block teleporting back in place; the slide path is clear (the snow cloud is a one-way chain above; the saw is bodiless; the L3 patrol slime sits left of 2300 so the re-park beats it there). (2) A fourth act on each level, finales shifted whole again (L1 +768 tail, L2 +640, L3 +512, L4 +512): the classic CHAINED-WEIGHT THWOMPS return in every level (with "ride the head" coins), SNAILS go liberal (L4 now carries four), more one-way clouds + coins + decor. New widths L1 6336 / L2 4672 / L3 5312 / L4 5376. Enemy indices still unique per level; flags within bounds. Note: the single-instance toys (gate/lever/spring/key-door) stay one-per-level by design — "more gates/switches" was met by carrying the door beat into more levels and stacking machine bays, not by generalising those mechanics (which would be a Kit-shaped change). Still zero Kit changes. | User review; this commit | From db65cfb8dbf67fc0678a7d36faa698012e2ace19 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 13 Jun 2026 13:49:16 +0000 Subject: [PATCH 2/6] Fix: thwomp ride-coins were embedded in the block (gate-locking bug) Reported by the user: the four new "ride the thwomp head" coins were placed at y184, but pfMakeThwomp's body spans y170..230 (centered at 200), so the coin sat INSIDE the block - below the hero riding on top - hidden and unreachable, which would prevent the collect-every-coin gate from opening. Moved all four (L1 5700, L2 3600, L3 4280, L4 4600) to y110: above the block top (170) so never embedded, and within the hero's body as the rising head crests, so it is grabbed by the RIDE itself (no precise hop) - the gate opens reliably. (The original L2 ride coin at y64 is unchanged.) https://claude.ai/code/session_012A6Dqdr3DPCeC3oWZohjHi --- examples/box2dxt-platformer.livecodescript | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/box2dxt-platformer.livecodescript b/examples/box2dxt-platformer.livecodescript index e4bce06..6cf6092 100644 --- a/examples/box2dxt-platformer.livecodescript +++ b/examples/box2dxt-platformer.livecodescript @@ -1768,7 +1768,7 @@ command pfL1Cast pfMakeCoin 5040, 500 -- the first gauntlet slime's beat pfMakeCoin 5232, 500 -- under the darting fly pfMakeCoin 5360, 500 -- the last gauntlet slime's beat - pfMakeCoin 5700, 184 -- ride the chained thwomp's head up to it + pfMakeCoin 5700, 110 -- ride the chained thwomp's head up to grab it (clear of the block) pfMakeCoin 5900, 500 -- the homeward snail's beat (shell it, kick it) pfMakeCoin 6136, 392 -- up on the final one-way cloud, by the flag if gToysOK is true then pfMakeCoin 240, 230 -- the sky coin (spring up) @@ -1969,7 +1969,7 @@ command pfL2Cast pfMakeCoin 2680, 500 -- past the second bay's chained crusher pfMakeCoin 2880, 448 -- above the second sweeping saw: time the hop pfMakeCoin 3300, 500 -- the third bay's snail (shell it, kick it) - pfMakeCoin 3600, 184 -- ride the third chained crusher's head up + pfMakeCoin 3600, 110 -- ride the third chained crusher's head up to grab it pfMakeCoin 3796, 392 -- up on the third bay's one-way cloud pfMakeCoin 4420, 448 -- over the first stone step pfMakeCoin 4494, 392 -- over the top step, by the flag @@ -2141,7 +2141,7 @@ command pfL3Cast pfMakeCoin 3640, 500 -- the final glacier slime's beat pfMakeCoin 3796, 392 -- up on the second snow cloud pfMakeCoin 4040, 500 -- the glacier snail's beat (shell it, kick it) - pfMakeCoin 4280, 184 -- ride the chained thwomp's head up to it + pfMakeCoin 4280, 110 -- ride the chained thwomp's head up to grab it (clear of the block) pfMakeCoin 4770, 448 -- over the first snow step pfMakeCoin 4865, 392 -- over the top step, by the flag if gToysOK is true then @@ -2285,7 +2285,7 @@ command pfL4Cast pfMakeCoin 4000, 500 -- the second snail's beat (shell it, kick it!) pfMakeCoin 4220, 500 -- the slime its bowled shell can flatten pfMakeCoin 4400, 500 -- a THIRD snail's beat - pfMakeCoin 4600, 184 -- ride the chained crusher's head up to it + pfMakeCoin 4600, 110 -- ride the chained crusher's head up to grab it (clear of the block) pfMakeCoin 4780, 500 -- a FOURTH snail's beat pfMakeCoin 5056, 448 -- over the first purple step pfMakeCoin 5184, 392 -- over the top step, by the flag From bfc84cc437e83ed159711a3935dc7ad9bbecf305 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 13 Jun 2026 17:42:03 +0000 Subject: [PATCH 3/6] Final layout pass: move thwomp coins onto the critical path (gate-locking fix) User found an L3 coin they couldn't reach. The new thwomps' reward coins sat at the crusher's perch height (embedded, then lifted just above it), reachable only by the obscure "hop onto the resting crusher and ride it up" beat - which a player avoiding the hazard never finds, so a REQUIRED collect-every-coin could lock the goal-flag gate. Fix: all four new thwomp coins (L1 5700, L2 3600, L3 4280, L4 4600) moved to y500, directly UNDER each crusher on the critical path. The crusher is too tall to jump over, so the hero must pass beneath it to progress and grabs the coin by passing (timing the crush) - the gate is guaranteed to open. A full coin-reachability audit (every coin vs each level's slabs / clouds / pits / step-plats) confirmed every other coin is reachable via its mechanism (spring arcs, the rope-bridge deck, jump-over-pit arcs, the mound plateau, and L2's original verified ride coin at 1840,64). Static checker passes; zero Kit changes. https://claude.ai/code/session_012A6Dqdr3DPCeC3oWZohjHi --- examples/box2dxt-platformer.livecodescript | 8 ++++---- plan.md | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/box2dxt-platformer.livecodescript b/examples/box2dxt-platformer.livecodescript index 6cf6092..5a9e72f 100644 --- a/examples/box2dxt-platformer.livecodescript +++ b/examples/box2dxt-platformer.livecodescript @@ -1768,7 +1768,7 @@ command pfL1Cast pfMakeCoin 5040, 500 -- the first gauntlet slime's beat pfMakeCoin 5232, 500 -- under the darting fly pfMakeCoin 5360, 500 -- the last gauntlet slime's beat - pfMakeCoin 5700, 110 -- ride the chained thwomp's head up to grab it (clear of the block) + pfMakeCoin 5700, 500 -- beneath the chained thwomp: grab it as you dash under pfMakeCoin 5900, 500 -- the homeward snail's beat (shell it, kick it) pfMakeCoin 6136, 392 -- up on the final one-way cloud, by the flag if gToysOK is true then pfMakeCoin 240, 230 -- the sky coin (spring up) @@ -1969,7 +1969,7 @@ command pfL2Cast pfMakeCoin 2680, 500 -- past the second bay's chained crusher pfMakeCoin 2880, 448 -- above the second sweeping saw: time the hop pfMakeCoin 3300, 500 -- the third bay's snail (shell it, kick it) - pfMakeCoin 3600, 110 -- ride the third chained crusher's head up to grab it + pfMakeCoin 3600, 500 -- beneath the third chained crusher: grab it as you dash under pfMakeCoin 3796, 392 -- up on the third bay's one-way cloud pfMakeCoin 4420, 448 -- over the first stone step pfMakeCoin 4494, 392 -- over the top step, by the flag @@ -2141,7 +2141,7 @@ command pfL3Cast pfMakeCoin 3640, 500 -- the final glacier slime's beat pfMakeCoin 3796, 392 -- up on the second snow cloud pfMakeCoin 4040, 500 -- the glacier snail's beat (shell it, kick it) - pfMakeCoin 4280, 110 -- ride the chained thwomp's head up to grab it (clear of the block) + pfMakeCoin 4280, 500 -- beneath the chained thwomp: grab it as you dash under pfMakeCoin 4770, 448 -- over the first snow step pfMakeCoin 4865, 392 -- over the top step, by the flag if gToysOK is true then @@ -2285,7 +2285,7 @@ command pfL4Cast pfMakeCoin 4000, 500 -- the second snail's beat (shell it, kick it!) pfMakeCoin 4220, 500 -- the slime its bowled shell can flatten pfMakeCoin 4400, 500 -- a THIRD snail's beat - pfMakeCoin 4600, 110 -- ride the chained crusher's head up to grab it (clear of the block) + pfMakeCoin 4600, 500 -- beneath the chained crusher: grab it as you dash under pfMakeCoin 4780, 500 -- a FOURTH snail's beat pfMakeCoin 5056, 448 -- over the first purple step pfMakeCoin 5184, 392 -- over the top step, by the flag diff --git a/plan.md b/plan.md index 77fb71f..b545679 100644 --- a/plan.md +++ b/plan.md @@ -318,3 +318,4 @@ user-confirmed in OXT before the next begins. | 2026-06-13 | **Showcase round, user review pass: the wrecking ball is CUT; the boulder gets a sprite face; the piranha row doubles.** The user's call surfaced the SPRITE-ONLY visuals policy (expansion-prep §62) sharply: "the cannonball needs to go — if we cannot use sprites for it, then remove it." A swinging arm/chain ROTATES, and sprites do not (gotcha 23), so the wrecking ball could only ever be a plain graphic — it was removed (maker, tick, globals, the L2 demolition bay now an open breather, which also serves "some things are too close together"). The boulder, by contrast, is ROUND — rotation-agnostic — so it earns a real sprite face (`rock` over the invisible ball, bound so it tracks roll + re-park); the rope-bridge planks stay graphics by necessity (a sagging deck must rotate) but read as wood. L4's PIRANHA row went from two burrows to FOUR (the user: "twice as long"), and the lava/crushers/fire-slime/barrel/steps shifted +192 (tile-aligned) to give the longer row clear air. Lesson reaffirmed: **sprite-only is a hard line for VISIBLE game art — if a mechanic's art must rotate and no sprite fits, cut the mechanic rather than ship a plain graphic.** | User review; this commit | | 2026-06-13 | **Showcase round 2: levels expanded further with classic-platformer acts; two precise sprite/facing fixes.** User: "expand the levels a bit further following all our best practices; get rid of the brown boxes around the bomb (use the crate sprite); everything else should use the provided sprites; reverse the snail (it faces backwards travelling forwards)." Done: (1) the SNAIL flip — a per-row `gSlimeFlip` polarity column XORed into all four `pfTickSlimes` flips; only the snail is `true` (its sheet art mirrors the slimes'), so slimes/mice/worms/ladybugs are byte-identical and only the snail inverts (gotcha 26). (2) The barrel's WOODPILE is now `pfMakeWoodCrate` — the `block_empty` crate sprite over an invisible FIXED-ROTATION spawned box (the L2 gate-crate pattern), so a blast slides the crates without the sprite-cannot-rotate problem (gotcha 23) — no more brown rectangles. (3) Each level gained a classic act, finales shifted as a WHOLE by a tile-aligned offset (L1 +768 tail, L2 +320, L3 +384, L4 +384): L1 a meadow gauntlet (one-way cloud + two slimes + a fly), L2 a second machine bay (a 3rd chained crusher + an ALWAYS-on saw — not on `gSawMov`, so the lever can't disable it), L3 a second snow cloud + a final glacier slime placed clear of the boulder's parked home, L4 a bowling lane (a 2nd snail + a slime to bowl). New widths L1 5568 / L2 4032 / L3 4800 / L4 4864; enemy indices stay unique per level; every new beat on solid ground within bounds. Still zero Kit changes. | User review; this commit | | 2026-06-13 | **Showcase round 3: a third expansion + the ice-boulder physics fix.** User: "expand a little further — more clouds, coins, enemies, switches, gates; snails liberally; the OLD chained thwomps back; mimic iconic classics; and the ice boulder should SLIDE ALL THE WAY, not reset (it is an ice block)." Done: (1) BOULDER — lowered its friction to 0.18 so it coasts, and moved its reset line off-screen-left (3050 → 2300, past the run and below the camera's left edge while the hero is in it), so a fresh one arrives from the source instead of a single block teleporting back in place; the slide path is clear (the snow cloud is a one-way chain above; the saw is bodiless; the L3 patrol slime sits left of 2300 so the re-park beats it there). (2) A fourth act on each level, finales shifted whole again (L1 +768 tail, L2 +640, L3 +512, L4 +512): the classic CHAINED-WEIGHT THWOMPS return in every level (with "ride the head" coins), SNAILS go liberal (L4 now carries four), more one-way clouds + coins + decor. New widths L1 6336 / L2 4672 / L3 5312 / L4 5376. Enemy indices still unique per level; flags within bounds. Note: the single-instance toys (gate/lever/spring/key-door) stay one-per-level by design — "more gates/switches" was met by carrying the door beat into more levels and stacking machine bays, not by generalising those mechanics (which would be a Kit-shaped change). Still zero Kit changes. | User review; this commit | +| 2026-06-13 | **Final layout pass: the thwomp "ride-the-head" coins were a gate-locking trap; moved on-path.** User found an L3 coin they couldn't find. Root cause: the new thwomps' reward coins were placed at the perch height (first embedded in the 60px block at y184, then lifted to y110 above it) and were reachable only by the obscure "hop onto the resting crusher and ride it up" beat - which a player avoiding the hazard never discovers, so a REQUIRED (collect-every-coin) coin could lock the goal-flag gate. Fix: all four new thwomp coins (L1 5700, L2 3600, L3 4280, L4 4600) moved to **y500, directly under each crusher on the critical path** - the crusher is too tall to jump over, so the hero MUST pass beneath it to progress and grabs the coin by passing (timing the crush), guaranteeing the gate can open. A full coin-reachability audit (every coin vs the slabs/clouds/pits/plats per level) confirmed every other coin is reachable via its mechanism (spring arcs, the rope-bridge deck, jump-over-pit arcs, the mound plateau, L2's original verified ride coin at 1840,64). Lesson: **a REQUIRED coin must sit on the critical path or an unskippable beat - never behind an optional, discover-it-yourself trick.** Still zero Kit changes. | User review; this commit | From 506a6a8738559a54f08dcf3ae57d983143b9ade3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 13 Jun 2026 17:51:33 +0000 Subject: [PATCH 4/6] Polish: open up L4's haunted finale spacing to the 100px layout law The final completability review (all four levels confirmed finishable, every coin reachable, no softlocks) flagged one minor: L4's finale (slime -> snail -> crusher -> snail) had a ~60px-clear-air pinch, under the ~100px layout law. Nudged the third snail +40 (4400 -> 4440); every gap in that run is now >=100px, with no cascade onto the steps/flag. Static checker passes; zero Kit changes. https://claude.ai/code/session_012A6Dqdr3DPCeC3oWZohjHi --- examples/box2dxt-platformer.livecodescript | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/box2dxt-platformer.livecodescript b/examples/box2dxt-platformer.livecodescript index 5a9e72f..ec9017c 100644 --- a/examples/box2dxt-platformer.livecodescript +++ b/examples/box2dxt-platformer.livecodescript @@ -2284,7 +2284,7 @@ command pfL4Cast pfMakeCoin 3892, 500 -- past the powder keg (the lure: mind the blast) pfMakeCoin 4000, 500 -- the second snail's beat (shell it, kick it!) pfMakeCoin 4220, 500 -- the slime its bowled shell can flatten - pfMakeCoin 4400, 500 -- a THIRD snail's beat + pfMakeCoin 4440, 500 -- a THIRD snail's beat pfMakeCoin 4600, 500 -- beneath the chained crusher: grab it as you dash under pfMakeCoin 4780, 500 -- a FOURTH snail's beat pfMakeCoin 5056, 448 -- over the first purple step @@ -2315,7 +2315,7 @@ command pfL4Cast pfMakeSlime 9, "normal", 4220, 4160, 4280, 576 -- the haunted FINALE: snails used liberally (two more) around the -- classic chained-weight THWOMP coming back among the faced crushers - pfMakeSnail 10, 4400, 4340, 4460 + pfMakeSnail 10, 4440, 4380, 4500 pfMakeThwomp 3, 4600 pfMakeSnail 11, 4780, 4720, 4840 pfMakeCheckpoint 2160 From 2cd58931b65c4af4e9929894a46690edc736611c Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 13 Jun 2026 18:24:54 +0000 Subject: [PATCH 5/6] Showcase round 4: crusher alley + cloud hop closes every level Mirror L1's GREEN CRUSHER ALLEY across L2/L3/L4: a row of four tile-block thwomps the hero dashes BENEATH, then a two-cloud hop to the flag. - New pTileFace param on pfMakeThwomp draws a plain tile-sprite crusher (no mood-face swaps; same armed/drop/rest/rise/re-arm cycle), so the alley never blocks the path for good. Biome-matched blocks: green (L2 grass), blue (L3 ice), red (L4 haunted). - Finales (walled door + steps + flag + bounds) shifted right as whole units by +1280 (tile-aligned): L2 4672->5952, L3 5312->6592, L4 5376->6656. Coins at under-crusher midpoints (y500) and atop each cloud (y392); a new snail in L2 and L3 (snails liberally). - Cloud chains ghost-padded a tile past the art each side (solid span == art span); door-gated coins reachable (keys are early on the path); enemy indices unique per gBlock/gSlime/gPlant namespace per level. Opus correctness/completability review found no blockers across index uniqueness, coin reachability, ghost-padding, spacing, finale consistency, patrol safety, and compile. Static checker clean; zero Kit changes (harness v10 holds). Awaiting the OXT pass. https://claude.ai/code/session_012A6Dqdr3DPCeC3oWZohjHi --- CHANGELOG.md | 15 +- examples/box2dxt-platformer.livecodescript | 280 ++++++++++++++++----- plan.md | 1 + 3 files changed, 235 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d95a31..5ef8e96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,8 +66,8 @@ The native shim's ABI is tracked separately by `b2Version()` (currently `4`). (spike-type — hurts from every side). L4's PIRANHA row is now twice as long (four burrows on staggered timers). - **Longer, re-spaced levels** (the layout law: widen before squeezing - a beat in), grown across three passes to L1 6336, L2 4672, L3 5312, - L4 5376. Existing verified beats are preserved in place; each level's + a beat in), grown across several passes to L1 7552, L2 5952, L3 6592, + L4 6656. Existing verified beats are preserved in place; each level's walled-door / steps finale shifts as a whole. Classic acts stacked on: L1 a "meadow gauntlet" + a "homeward run" (chained thwomp + snail under a cloud); L2 second and third machine bays (chained crushers + an @@ -77,6 +77,17 @@ The native shim's ABI is tracked separately by `b2Version()` (currently `4`). chained-weight thwomps are back** in every level alongside L4's faced crushers, and there are more clouds, coins, and decor throughout. A live `awake N/M` body count on the HUD. + - **A marquee CRUSHER ALLEY + cloud hop closes every level** (the latest + pass: "show off the engine with as much pizzazz as possible"). Each + level's homeward stretch is now a row of FOUR tile-block thwomps the + hero times a dash BENEATH, biome-matched so the mechanic reads at a + glance — **green** blocks (L1, L2 grass), **blue** (L3 ice), **red** + (L4 haunted) — followed by a two-cloud HOP to the flag with coins up + top. Powered by a new `pTileFace` parameter on `pfMakeThwomp` (a plain + tile sprite, no mood-face swaps; the same drop/rest/rise/re-arm cycle, + so the alley never blocks the path for good). The four levels each grew + ~1280px for the new gauntlets; the walled-door/steps finales shifted as + whole units; coins and their totals self-count as the level builds. - **The L3 ice boulder slides ALL THE WAY** in its direction now (per the user: "it is an ice block/boulder") - lower friction so it coasts far, and its reset line moved off-screen-left (past the run, below the diff --git a/examples/box2dxt-platformer.livecodescript b/examples/box2dxt-platformer.livecodescript index ec9017c..2c8e983 100644 --- a/examples/box2dxt-platformer.livecodescript +++ b/examples/box2dxt-platformer.livecodescript @@ -958,7 +958,10 @@ end pfMakeCritter -- kinematic rise back UP to perch height. pFaced true = the Wave 3 -- CRUSHER look: it wears the block_idle/fall/rest FACES (the art that -- was this machine's fallback all along) instead of the chained weight. -command pfMakeThwomp pIdx, pX, pFaced +-- pTileFace (optional) overrides BOTH with a plain tiles sprite (e.g. +-- "block_green" for the grass-biome crusher rows) - a solid block that +-- drops, no mood-face swaps. +command pfMakeThwomp pIdx, pX, pFaced, pTileFace local tName, tBody put "pf_blockBody" & pIdx into tName create graphic tName @@ -973,7 +976,12 @@ command pfMakeThwomp pIdx, pX, pFaced put empty into gBlockChainA[pIdx] put empty into gBlockChainB[pIdx] put false into gBlockFace[pIdx] - if pFaced is true and gAssetsOK is true and b2kSheetHasFrame("foes", "block_idle") then + if pTileFace is not empty and gAssetsOK is true and b2kSheetHasFrame("tiles", pTileFace) then + -- a plain tile block (block_green for the green crusher rows): it + -- drops and rises with no mood-face swap (gBlockFace stays false) + b2kSpriteNew "tiles", pTileFace, pX, 200 + put the result into gBlockSpr[pIdx] + else if pFaced is true and gAssetsOK is true and b2kSheetHasFrame("foes", "block_idle") then b2kSpriteNew "foes", "block_idle", pX, 200 put the result into gBlockSpr[pIdx] put true into gBlockFace[pIdx] -- pfTickThwomps swaps the moods @@ -1579,10 +1587,10 @@ end pfMakeWoodCrate command pfL1Scene local tX put "GREEN HILLS" into gLevelName - pfBounds 6336 + pfBounds 7552 pfSlab "pf_ground1", 0, 576, 2560, 640 pfSlab "pf_ground2", 2752, 576, 3968, 640 - pfSlab "pf_ground3", 4288, 576, 6336, 640 -- the far meadow + the sky-island gauntlet + the homeward run + pfSlab "pf_ground3", 4288, 576, 7552, 640 -- the far meadow, the gauntlet, the homeward run + the GREEN CRUSHER alley if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_grass_block_top") then repeat with tX = 0 to 2496 step 64 if tX >= 1376 and tX <= 1696 then @@ -1595,7 +1603,7 @@ command pfL1Scene repeat with tX = 2752 to 3904 step 64 pfTile "terrain_grass_block_top", tX, 576 end repeat - repeat with tX = 4288 to 6272 step 64 + repeat with tX = 4288 to 7488 step 64 pfTile "terrain_grass_block_top", tX, 576 end repeat pfShowSlabs false @@ -1738,6 +1746,30 @@ command pfL1Scene set the foregroundColor of it to "70,190,110" b2kCamAdopt the long id of graphic "pf_cloudledgeG" end if + -- the GREEN CRUSHER ALLEY (the showcase gauntlet): a row of green-block + -- thwomps (in the cast) you run beneath, then a two-cloud HOP to the + -- flag. Both clouds ghost-padded a tile past the art each side. + b2kSmoothGround "7312,448" & cr & "7248,448" & cr & "7056,448" & cr & "6992,448" + b2kSmoothGround "7532,448" & cr & "7468,448" & cr & "7276,448" & cr & "7212,448" + if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_grass_cloud_left") then + pfTile "terrain_grass_cloud_left", 7056, 448 + pfTile "terrain_grass_cloud_middle", 7120, 448 + pfTile "terrain_grass_cloud_right", 7184, 448 + pfTile "terrain_grass_cloud_left", 7276, 448 + pfTile "terrain_grass_cloud_middle", 7340, 448 + pfTile "terrain_grass_cloud_right", 7404, 448 + else + create graphic "pf_cloudledgeI" + set the style of it to "line" + set the points of it to "7056,448" & cr & "7468,448" + set the lineSize of it to 4 + set the foregroundColor of it to "70,190,110" + b2kCamAdopt the long id of graphic "pf_cloudledgeI" + end if + if gAssetsOK is true and b2kSheetHasFrame("tiles", "bush") then + pfTile "bush", 6320, 512 + pfTile "mushroom_red", 7488, 512 + end if if gToysOK is true then -- the spring mid-meadow: bouncing happens in open air, well away -- from the left corner (flush-to-wall put the bounce against the @@ -1770,7 +1802,12 @@ command pfL1Cast pfMakeCoin 5360, 500 -- the last gauntlet slime's beat pfMakeCoin 5700, 500 -- beneath the chained thwomp: grab it as you dash under pfMakeCoin 5900, 500 -- the homeward snail's beat (shell it, kick it) - pfMakeCoin 6136, 392 -- up on the final one-way cloud, by the flag + pfMakeCoin 6136, 392 -- up on the homeward-run cloud + pfMakeCoin 6530, 500 -- threading the GREEN CRUSHER alley: grab between drops + pfMakeCoin 6710, 500 -- ...and between the next pair of crushers + pfMakeCoin 6890, 500 -- ...and the last gap + pfMakeCoin 7120, 392 -- up on the first hop cloud + pfMakeCoin 7340, 392 -- up on the second hop cloud, by the flag if gToysOK is true then pfMakeCoin 240, 230 -- the sky coin (spring up) -- the bee tours the mound (harmless); the fly guards the cloud coin; -- a flying LADYBUG patrols the second-act meadow (new aerial variety) @@ -1818,15 +1855,23 @@ command pfL1Cast pfAddMover tRef, 5232, 452, 80, 720, 30, 480, 34, 40, true end if end if - -- the HOMEWARD RUN: the classic chained-weight THWOMP returns (ride its - -- resting head up for the high coin), and a SNAIL to shell-and-kick + -- the HOMEWARD RUN: the classic chained-weight THWOMP returns (dash + -- under it for the coin), and a SNAIL to shell-and-kick pfMakeThwomp 1, 5700 pfMakeSnail 9, 5900, 5840, 5960 + -- the GREEN CRUSHER ALLEY (the grass biome's marquee gauntlet): a row + -- of four green-block thwomps you time your run beneath - each drops as + -- you near it, so keep moving and snatch the coins in the gaps. They + -- rest STATIC then rise + re-arm, never blocking the alley for good. + pfMakeThwomp 2, 6440, false, "block_green" + pfMakeThwomp 3, 6620, false, "block_green" + pfMakeThwomp 4, 6800, false, "block_green" + pfMakeThwomp 5, 6980, false, "block_green" -- a CHECKPOINT just before the rope bridge: a slip into the chasm is -- then a quick retry at the brink, never a replay of the whole level pfMakeCheckpoint 3856 if gToysOK is true then pfMakeDebrisPool -- this level has bricks - pfMakeGoal 6280, 544 + pfMakeGoal 7540, 544 end pfL1Cast -- ===================================================================== @@ -1841,26 +1886,26 @@ end pfL1Cast command pfL2Scene local tX, tRef put "THE WORKS" into gLevelName - pfBounds 4672 - pfSlab "pf_ground1", 0, 576, 4672, 640 - pfSlab "pf_plat1", 4288, 512, 4480, 576 - pfSlab "pf_plat2", 4480, 448, 4608, 576 + pfBounds 5952 + pfSlab "pf_ground1", 0, 576, 5952, 640 + pfSlab "pf_plat1", 5568, 512, 5760, 576 + pfSlab "pf_plat2", 5760, 448, 5888, 576 if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_grass_block_top") then - repeat with tX = 0 to 4608 step 64 - if tX >= 4288 then + repeat with tX = 0 to 5888 step 64 + if tX >= 5568 then -- the finale plays in STONE past the walled door pfTile "terrain_stone_block_top", tX, 576 else pfTile "terrain_grass_block_top", tX, 576 end if end repeat - pfTile "terrain_stone_block_top_left", 4288, 512 - pfTile "terrain_stone_block_top", 4352, 512 - pfTile "terrain_stone_block_top_right", 4416, 512 - pfTile "terrain_stone_block_top", 4480, 448 - pfTile "terrain_stone_block_top", 4544, 448 - pfTile "terrain_stone_block_center", 4480, 512 - pfTile "terrain_stone_block_center", 4544, 512 + pfTile "terrain_stone_block_top_left", 5568, 512 + pfTile "terrain_stone_block_top", 5632, 512 + pfTile "terrain_stone_block_top_right", 5696, 512 + pfTile "terrain_stone_block_top", 5760, 448 + pfTile "terrain_stone_block_top", 5824, 448 + pfTile "terrain_stone_block_center", 5760, 512 + pfTile "terrain_stone_block_center", 5824, 512 pfShowSlabs false else pfShowSlabs true @@ -1953,9 +1998,33 @@ command pfL2Scene if gAssetsOK is true and b2kSheetHasFrame("tiles", "bush") then pfTile "bush", 3400, 512 end if + -- the GREEN CRUSHER ALLEY (the works' marquee gauntlet): a row of green- + -- block thwomps (in the cast) you run beneath, then a two-cloud HOP to the + -- walled door. Both clouds ghost-padded a tile past the art each side. + b2kSmoothGround "5096,448" & cr & "5032,448" & cr & "4840,448" & cr & "4776,448" + b2kSmoothGround "5316,448" & cr & "5252,448" & cr & "5060,448" & cr & "4996,448" + if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_grass_cloud_left") then + pfTile "terrain_grass_cloud_left", 4840, 448 + pfTile "terrain_grass_cloud_middle", 4904, 448 + pfTile "terrain_grass_cloud_right", 4968, 448 + pfTile "terrain_grass_cloud_left", 5060, 448 + pfTile "terrain_grass_cloud_middle", 5124, 448 + pfTile "terrain_grass_cloud_right", 5188, 448 + else + create graphic "pf_cloudledgeJ" + set the style of it to "line" + set the points of it to "4840,448" & cr & "5252,448" + set the lineSize of it to 4 + set the foregroundColor of it to "70,190,110" + b2kCamAdopt the long id of graphic "pf_cloudledgeJ" + end if + if gAssetsOK is true and b2kSheetHasFrame("tiles", "bush") then + pfTile "sign", 3980, 512 -- a way-marker into the crusher alley + pfTile "bush", 5400, 512 + end if if gToysOK is true then pfMakeLever 1120 -- stand here to power the FIRST sweep saw down - pfMakeKeyDoor 1740, 500, 4064, "yellow" + pfMakeKeyDoor 1740, 500, 5344, "yellow" end if end pfL2Scene @@ -1971,9 +2040,15 @@ command pfL2Cast pfMakeCoin 3300, 500 -- the third bay's snail (shell it, kick it) pfMakeCoin 3600, 500 -- beneath the third chained crusher: grab it as you dash under pfMakeCoin 3796, 392 -- up on the third bay's one-way cloud - pfMakeCoin 4420, 448 -- over the first stone step - pfMakeCoin 4494, 392 -- over the top step, by the flag - if gToysOK is true then pfMakeCoin 4112, 510 -- inside the door passage + pfMakeCoin 3950, 500 -- the fourth bay's snail (shell it, kick it) + pfMakeCoin 4230, 500 -- threading the GREEN CRUSHER alley: grab between drops + pfMakeCoin 4410, 500 -- ...and between the next pair of crushers + pfMakeCoin 4590, 500 -- ...and the last gap + pfMakeCoin 4904, 392 -- up on the first hop cloud + pfMakeCoin 5124, 392 -- up on the second hop cloud, before the door + pfMakeCoin 5700, 448 -- over the first stone step + pfMakeCoin 5774, 392 -- over the top step, by the flag + if gToysOK is true then pfMakeCoin 5392, 510 -- inside the door passage -- the bee patrols the breather cloud if gAssetsOK is true and b2kSheetHasFrame("foes", "bee_a") then b2kSpriteNew "foes", "bee_a", 2210, 310 @@ -2036,8 +2111,18 @@ command pfL2Cast pfAddMover tRef, 2880, 548, 92, 1050, 0, 1, 38, 42, false end if end if + -- the GREEN CRUSHER ALLEY (the works' marquee gauntlet): a row of four + -- green-block thwomps you time your run beneath - each drops as you near + -- it, so keep moving and snatch the coins in the gaps. They rest STATIC + -- then rise + re-arm, never blocking the alley for good. A fourth-bay + -- SNAIL guards the approach (snails liberally now). + pfMakeSnail 4, 3950, 3910, 3990 + pfMakeThwomp 5, 4140, false, "block_green" + pfMakeThwomp 6, 4320, false, "block_green" + pfMakeThwomp 7, 4500, false, "block_green" + pfMakeThwomp 8, 4680, false, "block_green" pfMakeCheckpoint 1000 - pfMakeGoal 4564, 416 + pfMakeGoal 5844, 416 end pfL2Cast -- ===================================================================== @@ -2051,12 +2136,12 @@ end pfL2Cast command pfL3Scene local tX put "FROZEN CITADEL (icy!)" into gLevelName - pfBounds 5312 + pfBounds 6592 pfSlab "pf_ground1", 0, 576, 768, 640 pfSlab "pf_ground2", 960, 576, 1792, 640 - pfSlab "pf_ground3", 1984, 576, 5312, 640 - pfSlab "pf_plat1", 4640, 512, 4832, 576 - pfSlab "pf_plat2", 4832, 448, 4960, 576 + pfSlab "pf_ground3", 1984, 576, 6592, 640 + pfSlab "pf_plat1", 5920, 512, 6112, 576 + pfSlab "pf_plat2", 6112, 448, 6240, 576 if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_snow_block_top") then repeat with tX = 0 to 704 step 64 pfTile "terrain_snow_block_top", tX, 576 @@ -2064,16 +2149,16 @@ command pfL3Scene repeat with tX = 960 to 1728 step 64 pfTile "terrain_snow_block_top", tX, 576 end repeat - repeat with tX = 1984 to 5248 step 64 + repeat with tX = 1984 to 6528 step 64 pfTile "terrain_snow_block_top", tX, 576 end repeat - pfTile "terrain_snow_block_top_left", 4640, 512 - pfTile "terrain_snow_block_top", 4704, 512 - pfTile "terrain_snow_block_top_right", 4768, 512 - pfTile "terrain_snow_block_top", 4832, 448 - pfTile "terrain_snow_block_top", 4896, 448 - pfTile "terrain_snow_block_center", 4832, 512 - pfTile "terrain_snow_block_center", 4896, 512 + pfTile "terrain_snow_block_top_left", 5920, 512 + pfTile "terrain_snow_block_top", 5984, 512 + pfTile "terrain_snow_block_top_right", 6048, 512 + pfTile "terrain_snow_block_top", 6112, 448 + pfTile "terrain_snow_block_top", 6176, 448 + pfTile "terrain_snow_block_center", 6112, 512 + pfTile "terrain_snow_block_center", 6176, 512 pfShowSlabs false else pfShowSlabs true @@ -2113,13 +2198,36 @@ command pfL3Scene set the foregroundColor of it to "200,220,240" b2kCamAdopt the long id of graphic "pf_cloudledgeF" end if + -- the BLUE CRUSHER ALLEY (the citadel's marquee gauntlet): a row of ice- + -- block thwomps (in the cast) you run beneath, then a two-cloud HOP to the + -- red door. Both clouds ghost-padded a tile past the art each side. + b2kSmoothGround "5516,448" & cr & "5452,448" & cr & "5260,448" & cr & "5196,448" + b2kSmoothGround "5736,448" & cr & "5672,448" & cr & "5480,448" & cr & "5416,448" + if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_snow_cloud_left") then + pfTile "terrain_snow_cloud_left", 5260, 448 + pfTile "terrain_snow_cloud_middle", 5324, 448 + pfTile "terrain_snow_cloud_right", 5388, 448 + pfTile "terrain_snow_cloud_left", 5480, 448 + pfTile "terrain_snow_cloud_middle", 5544, 448 + pfTile "terrain_snow_cloud_right", 5608, 448 + else + create graphic "pf_cloudledgeK" + set the style of it to "line" + set the points of it to "5260,448" & cr & "5672,448" + set the lineSize of it to 4 + set the foregroundColor of it to "200,220,240" + b2kCamAdopt the long id of graphic "pf_cloudledgeK" + end if + if gAssetsOK is true and b2kSheetHasFrame("tiles", "sign") then + pfTile "sign", 4440, 512 -- a way-marker into the crusher alley + end if if gToysOK is true then pfMakeSpring 700 -- arcs you clean over the first pit pfMakeBonk "brick", 1232 pfMakeBonk "box", 1296 pfMakeBonk "brick", 1360 pfMakeBonk "box", 1424 - pfMakeKeyDoor 2268, 500, 4480, "red" + pfMakeKeyDoor 2268, 500, 5760, "red" end if end pfL3Scene @@ -2142,11 +2250,17 @@ command pfL3Cast pfMakeCoin 3796, 392 -- up on the second snow cloud pfMakeCoin 4040, 500 -- the glacier snail's beat (shell it, kick it) pfMakeCoin 4280, 500 -- beneath the chained thwomp: grab it as you dash under - pfMakeCoin 4770, 448 -- over the first snow step - pfMakeCoin 4865, 392 -- over the top step, by the flag + pfMakeCoin 4420, 500 -- the citadel snail's beat (shell it, kick it) + pfMakeCoin 4650, 500 -- threading the BLUE CRUSHER alley: grab between drops + pfMakeCoin 4830, 500 -- ...and between the next pair of crushers + pfMakeCoin 5010, 500 -- ...and the last gap + pfMakeCoin 5324, 392 -- up on the first hop cloud + pfMakeCoin 5544, 392 -- up on the second hop cloud, before the door + pfMakeCoin 6050, 448 -- over the first snow step + pfMakeCoin 6145, 392 -- over the top step, by the flag if gToysOK is true then pfMakeCoin 864, 300 -- over the first pit: ride the spring's arc - pfMakeCoin 4528, 510 -- inside the red door's passage + pfMakeCoin 5808, 510 -- inside the red door's passage end if if gAssetsOK is true and b2kSheetHasFrame("foes", "saw_a") then b2kSpriteNew "foes", "saw_a", 1640, 548 @@ -2185,9 +2299,18 @@ command pfL3Cast -- back, guarding the run to the red door pfMakeSnail 4, 4040, 3980, 4100 pfMakeThwomp 2, 4280 + -- the BLUE CRUSHER ALLEY (the citadel's marquee gauntlet): a row of four + -- ice-block thwomps you time your run beneath - each drops as you near it, + -- so keep moving and snatch the coins in the gaps. A glacier SNAIL guards + -- the approach (snails liberally). + pfMakeSnail 5, 4420, 4390, 4450 + pfMakeThwomp 3, 4560, false, "block_blue" + pfMakeThwomp 4, 4740, false, "block_blue" + pfMakeThwomp 5, 4920, false, "block_blue" + pfMakeThwomp 6, 5100, false, "block_blue" pfMakeCheckpoint 2048 if gToysOK is true then pfMakeDebrisPool -- this level has bricks - pfMakeGoal 4920, 416 + pfMakeGoal 6200, 416 end pfL3Cast -- ===================================================================== @@ -2203,27 +2326,27 @@ end pfL3Cast command pfL4Scene local tX, tH, tHi put "HAUNTED HOLLOW (spooky!)" into gLevelName - pfBounds 5376 + pfBounds 6656 pfSlab "pf_ground1", 0, 576, 1856, 640 - pfSlab "pf_ground2", 2048, 576, 5376, 640 - pfSlab "pf_plat1", 4960, 512, 5152, 576 - pfSlab "pf_plat2", 5152, 448, 5280, 576 + pfSlab "pf_ground2", 2048, 576, 6656, 640 + pfSlab "pf_plat1", 6240, 512, 6432, 576 + pfSlab "pf_plat2", 6432, 448, 6560, 576 -- the bat overhang: a stone bar the bats roost under pfSlab "pf_batbar", 1472, 256, 1856, 320 if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_purple_block_top") then repeat with tX = 0 to 1792 step 64 pfTile "terrain_purple_block_top", tX, 576 end repeat - repeat with tX = 2048 to 5312 step 64 + repeat with tX = 2048 to 6592 step 64 pfTile "terrain_purple_block_top", tX, 576 end repeat - pfTile "terrain_purple_block_top_left", 4960, 512 - pfTile "terrain_purple_block_top", 5024, 512 - pfTile "terrain_purple_block_top_right", 5088, 512 - pfTile "terrain_purple_block_top", 5152, 448 - pfTile "terrain_purple_block_top", 5216, 448 - pfTile "terrain_purple_block_center", 5152, 512 - pfTile "terrain_purple_block_center", 5216, 512 + pfTile "terrain_purple_block_top_left", 6240, 512 + pfTile "terrain_purple_block_top", 6304, 512 + pfTile "terrain_purple_block_top_right", 6368, 512 + pfTile "terrain_purple_block_top", 6432, 448 + pfTile "terrain_purple_block_top", 6496, 448 + pfTile "terrain_purple_block_center", 6432, 512 + pfTile "terrain_purple_block_center", 6496, 512 pfTile "terrain_purple_horizontal_left", 1472, 256 pfTile "terrain_purple_horizontal_middle", 1536, 256 pfTile "terrain_purple_horizontal_middle", 1600, 256 @@ -2267,6 +2390,32 @@ command pfL4Scene pfMakeWoodCrate 3688, 554 pfMakeWoodCrate 3778, 554 pfMakeWoodCrate 3824, 554 + -- the RED CRUSHER ALLEY (the hollow's marquee gauntlet): a row of danger- + -- block thwomps (in the cast) you run beneath, then a two-cloud HOP to the + -- purple steps. Both clouds ghost-padded a tile past the art each side. + b2kSmoothGround "5956,448" & cr & "5892,448" & cr & "5700,448" & cr & "5636,448" + b2kSmoothGround "6176,448" & cr & "6112,448" & cr & "5920,448" & cr & "5856,448" + if gAssetsOK is true and b2kSheetHasFrame("tiles", "terrain_purple_cloud_left") then + pfTile "terrain_purple_cloud_left", 5700, 448 + pfTile "terrain_purple_cloud_middle", 5764, 448 + pfTile "terrain_purple_cloud_right", 5828, 448 + pfTile "terrain_purple_cloud_left", 5920, 448 + pfTile "terrain_purple_cloud_middle", 5984, 448 + pfTile "terrain_purple_cloud_right", 6048, 448 + else + create graphic "pf_cloudledgeL" + set the style of it to "line" + set the points of it to "5700,448" & cr & "6112,448" + set the lineSize of it to 4 + set the foregroundColor of it to "150,110,190" + b2kCamAdopt the long id of graphic "pf_cloudledgeL" + end if + if gAssetsOK is true and b2kSheetHasFrame("tiles", "sign") then + pfTile "sign", 4900, 512 -- a way-marker into the crusher alley + end if + if gAssetsOK is true and b2kSheetHasFrame("tiles", "grass_purple") then + pfTile "grass_purple", 5640, 512 + end if end pfL4Scene command pfL4Cast @@ -2287,8 +2436,13 @@ command pfL4Cast pfMakeCoin 4440, 500 -- a THIRD snail's beat pfMakeCoin 4600, 500 -- beneath the chained crusher: grab it as you dash under pfMakeCoin 4780, 500 -- a FOURTH snail's beat - pfMakeCoin 5056, 448 -- over the first purple step - pfMakeCoin 5184, 392 -- over the top step, by the flag + pfMakeCoin 5090, 500 -- threading the RED CRUSHER alley: grab between drops + pfMakeCoin 5270, 500 -- ...and between the next pair of crushers + pfMakeCoin 5450, 500 -- ...and the last gap + pfMakeCoin 5764, 392 -- up on the first hop cloud + pfMakeCoin 5984, 392 -- up on the second hop cloud, by the steps + pfMakeCoin 6336, 448 -- over the first purple step + pfMakeCoin 6464, 392 -- over the top step, by the flag pfMakeSnail 1, 980, 880, 1080 pfMakeSlime 2, "normal", 1255, 1180, 1330, 576 -- split patrol bands: two flying BODIES sharing one band would @@ -2318,8 +2472,16 @@ command pfL4Cast pfMakeSnail 10, 4440, 4380, 4500 pfMakeThwomp 3, 4600 pfMakeSnail 11, 4780, 4720, 4840 + -- the RED CRUSHER ALLEY (the hollow's marquee gauntlet): a row of four + -- danger-block thwomps you time your run beneath - each drops as you near + -- it, so keep moving and snatch the coins in the gaps. The haunted level's + -- last test before the flag. + pfMakeThwomp 4, 5000, false, "block_red" + pfMakeThwomp 5, 5180, false, "block_red" + pfMakeThwomp 6, 5360, false, "block_red" + pfMakeThwomp 7, 5540, false, "block_red" pfMakeCheckpoint 2160 - pfMakeGoal 5244, 416 + pfMakeGoal 6524, 416 end pfL4Cast diff --git a/plan.md b/plan.md index b545679..e558cdd 100644 --- a/plan.md +++ b/plan.md @@ -319,3 +319,4 @@ user-confirmed in OXT before the next begins. | 2026-06-13 | **Showcase round 2: levels expanded further with classic-platformer acts; two precise sprite/facing fixes.** User: "expand the levels a bit further following all our best practices; get rid of the brown boxes around the bomb (use the crate sprite); everything else should use the provided sprites; reverse the snail (it faces backwards travelling forwards)." Done: (1) the SNAIL flip — a per-row `gSlimeFlip` polarity column XORed into all four `pfTickSlimes` flips; only the snail is `true` (its sheet art mirrors the slimes'), so slimes/mice/worms/ladybugs are byte-identical and only the snail inverts (gotcha 26). (2) The barrel's WOODPILE is now `pfMakeWoodCrate` — the `block_empty` crate sprite over an invisible FIXED-ROTATION spawned box (the L2 gate-crate pattern), so a blast slides the crates without the sprite-cannot-rotate problem (gotcha 23) — no more brown rectangles. (3) Each level gained a classic act, finales shifted as a WHOLE by a tile-aligned offset (L1 +768 tail, L2 +320, L3 +384, L4 +384): L1 a meadow gauntlet (one-way cloud + two slimes + a fly), L2 a second machine bay (a 3rd chained crusher + an ALWAYS-on saw — not on `gSawMov`, so the lever can't disable it), L3 a second snow cloud + a final glacier slime placed clear of the boulder's parked home, L4 a bowling lane (a 2nd snail + a slime to bowl). New widths L1 5568 / L2 4032 / L3 4800 / L4 4864; enemy indices stay unique per level; every new beat on solid ground within bounds. Still zero Kit changes. | User review; this commit | | 2026-06-13 | **Showcase round 3: a third expansion + the ice-boulder physics fix.** User: "expand a little further — more clouds, coins, enemies, switches, gates; snails liberally; the OLD chained thwomps back; mimic iconic classics; and the ice boulder should SLIDE ALL THE WAY, not reset (it is an ice block)." Done: (1) BOULDER — lowered its friction to 0.18 so it coasts, and moved its reset line off-screen-left (3050 → 2300, past the run and below the camera's left edge while the hero is in it), so a fresh one arrives from the source instead of a single block teleporting back in place; the slide path is clear (the snow cloud is a one-way chain above; the saw is bodiless; the L3 patrol slime sits left of 2300 so the re-park beats it there). (2) A fourth act on each level, finales shifted whole again (L1 +768 tail, L2 +640, L3 +512, L4 +512): the classic CHAINED-WEIGHT THWOMPS return in every level (with "ride the head" coins), SNAILS go liberal (L4 now carries four), more one-way clouds + coins + decor. New widths L1 6336 / L2 4672 / L3 5312 / L4 5376. Enemy indices still unique per level; flags within bounds. Note: the single-instance toys (gate/lever/spring/key-door) stay one-per-level by design — "more gates/switches" was met by carrying the door beat into more levels and stacking machine bays, not by generalising those mechanics (which would be a Kit-shaped change). Still zero Kit changes. | User review; this commit | | 2026-06-13 | **Final layout pass: the thwomp "ride-the-head" coins were a gate-locking trap; moved on-path.** User found an L3 coin they couldn't find. Root cause: the new thwomps' reward coins were placed at the perch height (first embedded in the 60px block at y184, then lifted to y110 above it) and were reachable only by the obscure "hop onto the resting crusher and ride it up" beat - which a player avoiding the hazard never discovers, so a REQUIRED (collect-every-coin) coin could lock the goal-flag gate. Fix: all four new thwomp coins (L1 5700, L2 3600, L3 4280, L4 4600) moved to **y500, directly under each crusher on the critical path** - the crusher is too tall to jump over, so the hero MUST pass beneath it to progress and grabs the coin by passing (timing the crush), guaranteeing the gate can open. A full coin-reachability audit (every coin vs the slabs/clouds/pits/plats per level) confirmed every other coin is reachable via its mechanism (spring arcs, the rope-bridge deck, jump-over-pit arcs, the mound plateau, L2's original verified ride coin at 1840,64). Lesson: **a REQUIRED coin must sit on the critical path or an unskippable beat - never behind an optional, discover-it-yourself trick.** Still zero Kit changes. | User review; this commit | +| 2026-06-13 | **Showcase round 4: a marquee CRUSHER ALLEY + cloud hop closes every level.** User: "make these longer - add a row of green/grass thwomps to run under/over, more clouds, longer levels in the same style; show off the engine with as much pizzazz as possible." A new `pTileFace` param on `pfMakeThwomp` makes a plain TILE-SPRITE crusher (no mood-face swaps; the same armed->drop->rest->rise->re-arm cycle), so a ROW of four becomes a timing gauntlet the hero dashes BENEATH (the 42px trigger << 180px spacing means only one drops at a time; coins sit at the 90px midpoints, y500). One alley per level, biome-matched so the mechanic reads at a glance: GREEN blocks (L1 last turn, + L2 grass), BLUE (L3 ice), RED (L4 haunted), each followed by a two-cloud HOP to the flag (clouds at y448 - a 154px jump clears the 128px - coins up top at y392), the clouds one-way chains ghost-padded a tile past the art each side (solid span == art span per the ghost rule). Finales (walled door + steps + flag + bounds) shifted RIGHT as whole units by +1280 (tile-aligned): L2 4672->5952, L3 5312->6592, L4 5376->6656 (L1 was already 7552). New snails in L2 (gSlime 4) and L3 (gSlime 5) keep snails liberal; new crushers are gBlock 5-8 (L2), 3-6 (L3), 4-7 (L4) - unique per namespace per level. Door-gated coins stay reachable (the keys are early on the main path, before each alley). Flags within plat2; coins/totals self-count as built. Static checker clean; awaiting the OXT pass. Still zero Kit changes (harness v10 holds). | User review; this commit | From d7b19ec8d62ffd6b240c5e3a70b234b379ef1239 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 13 Jun 2026 18:36:07 +0000 Subject: [PATCH 6/6] Per-frame optimization pass: skip FFI for armed thwomps, cache HUD reads An Opus audit of on b2kFrame + all pfTick* against the kit's performance playbook found one regression (introduced by the crusher alleys) and three free wins, all example-side. - pfTickThwomps read b2kPosition over the FFI for EVERY block every frame, even armed ones (static bodies resting at a fixed perch). With the new alleys at ~7-8 blocks/level and only 0-1 ever in motion, that was ~7 wasted FFI round-trips + string splits per frame. Now each perch x is cached (gBlockX) at make/re-arm, the armed->falling trigger uses the cached x + the shared hero snapshot (perch y is invariantly 200), and b2kPosition is read only for the 0-1 blocks in motion. Identical trigger. - shellslide tick reads b2kVelocity once (was twice). - HUD reuses the snapshotted gHeroState and a single camera-scroll read. Every other per-frame tick was already at the playbook's standard. Gotcha scan clean. Example-only, no harness bump (v10 holds). Static checker clean; awaiting the OXT pass. https://claude.ai/code/session_012A6Dqdr3DPCeC3oWZohjHi --- CHANGELOG.md | 13 +++++++ examples/box2dxt-platformer.livecodescript | 43 +++++++++++++--------- plan.md | 1 + 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ef8e96..2686111 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,19 @@ The native shim's ABI is tracked separately by `b2Version()` (currently `4`). so the alley never blocks the path for good). The four levels each grew ~1280px for the new gauntlets; the walled-door/steps finales shifted as whole units; coins and their totals self-count as the level builds. + - **A per-frame optimization pass** against the kit's performance + playbook (FFI round-trips are the second-biggest per-frame cost). The + new crusher rows exposed that `pfTickThwomps` read each block's + position over the FFI *every frame even while ARMED* — a static body + resting at a fixed perch that cannot move until triggered — so it now + caches each perch x (`gBlockX`) and gates the armed→falling trigger on + that + the shared hero snapshot, paying `b2kPosition` only for the 0–1 + blocks actually in motion (≈8 → ≈1 FFI/frame, identical trigger). Plus + the sliding-shell tick reads its velocity once (not twice) and the HUD + reuses the snapshotted player state and a single camera-scroll read. An + Opus audit confirmed every other per-frame tick was already at the + playbook's standard (O(1) idle gates, hoisted clocks, shared snapshot, + change-gated writes, sleep-friendly). - **The L3 ice boulder slides ALL THE WAY** in its direction now (per the user: "it is an ice block/boulder") - lower friction so it coasts far, and its reset line moved off-screen-left (past the run, below the diff --git a/examples/box2dxt-platformer.livecodescript b/examples/box2dxt-platformer.livecodescript index 2c8e983..31ebcf4 100644 --- a/examples/box2dxt-platformer.livecodescript +++ b/examples/box2dxt-platformer.livecodescript @@ -198,7 +198,7 @@ local gPlateX, gGateUpY, gGateDownY, gDoorX, gDoorWord, gCheckX local gSlimeN, gSlimeB, gSlimeSpr, gSlimeKind, gSlimeDir local gSlimeMin, gSlimeMax, gSlimeGoneAt local gSlimeT -- Wave 3: per-row timer (mimic hop cooldowns) -local gBlockN, gBlockB, gBlockSpr, gBlockState, gBlockT +local gBlockN, gBlockB, gBlockSpr, gBlockState, gBlockT, gBlockX local gMovN, gMovSpr, gMovX, gMovY, gMovAX, gMovPX, gMovAY, gMovPY local gMovHurtW, gMovHurtH, gMovFlip -- Wave 3 (bestiary I): the piranha burrows, the ghost, the spooks sheet @@ -537,6 +537,7 @@ command pfStartGame put empty into gBlockSpr put empty into gBlockState put empty into gBlockT + put empty into gBlockX put 0 into gMovN put empty into gMovSpr put empty into gMovX @@ -1011,6 +1012,7 @@ command pfMakeThwomp pIdx, pX, pFaced, pTileFace if the visible of graphic tName is true then b2kCamAdopt tBody if pIdx > gBlockN then put pIdx into gBlockN put tBody into gBlockB[pIdx] + put pX into gBlockX[pIdx] -- perch x cached: an armed block never moves put "armed" into gBlockState[pIdx] put 0 into gBlockT[pIdx] end pfMakeThwomp @@ -2543,7 +2545,7 @@ end pfWipeStage -- The game tick -- ===================================================================== on b2kFrame - local tHud, tPos + local tHud, tPos, tHS if gHero is empty then exit b2kFrame -- THE HERO SNAPSHOT, once per frame: position and controller state -- feed the kill plane, edge failsafe, sound cues and every pf tick @@ -2616,10 +2618,11 @@ on b2kFrame else put (the milliseconds - gRunStart) div 1000 into tSecs end if - put "L" & gLevel & " " & gCoins & "/" & gCoinsTotal & " " & format("%d:%02d", tSecs div 60, tSecs mod 60) & " falls " & gFalls & " hits " & gOuches & " lands " & gLands & " | " & b2kPlayerState() & "/" & b2kSpriteAnim(gHeroSpr) & " " & round(b2kFrameMS() * 10) / 10 & " ms awake " & b2kAwakeBodyCount() & "/" & b2kBodyCount() into tHud + put "L" & gLevel & " " & gCoins & "/" & gCoinsTotal & " " & format("%d:%02d", tSecs div 60, tSecs mod 60) & " falls " & gFalls & " hits " & gOuches & " lands " & gLands & " | " & gHeroState & "/" & b2kSpriteAnim(gHeroSpr) & " " & round(b2kFrameMS() * 10) / 10 & " ms awake " & b2kAwakeBodyCount() & "/" & b2kBodyCount() into tHud if gHasKey is true then put " [KEY]" after tHud if gCamOK is true then - put " | view " & the hScroll of b2kCamGroup() & "-" & (the hScroll of b2kCamGroup() + 1024) after tHud + put the hScroll of b2kCamGroup() into tHS -- read the scroll once + put " | view " & tHS & "-" & (tHS + 1024) after tHud end if if not b2kIsAwake(gHero) then put " [ASLEEP?!]" after tHud -- must never show if b2kSoundStatus() is not empty then put " [audio: " & b2kSoundStatus() & "]" after tHud @@ -2709,7 +2712,7 @@ end pfTickGate -- shell; bat -> batfly; mimic -> mimiclive. (The stomp/kick/hurt -- VERDICTS live in b2kContact - this tick is pure steering.) command pfTickSlimes - local i, j, tX, tY, tPos, tBody, tVX, tMS + local i, j, tX, tY, tPos, tBody, tVX, tMS, tVel set the itemDelimiter to comma put the milliseconds into tMS repeat with i = 1 to gSlimeN @@ -2741,10 +2744,11 @@ command pfTickSlimes -- the bowling ball: a per-frame velocity ASSERT (the -- player-controller pattern) keeps the slide at speed; a -- wall hit shows as collapsed vx -> reverse, keep rolling - put item 1 of b2kVelocity(tBody) into tVX + put b2kVelocity(tBody) into tVel -- read once, reuse for the vy pass-through + put item 1 of tVel into tVX if gSlimeDir[i] > 0 and tVX < 60 then put -520 into gSlimeDir[i] if gSlimeDir[i] < 0 and tVX > -60 then put 520 into gSlimeDir[i] - b2kSetVelocity tBody, gSlimeDir[i], item 2 of b2kVelocity(tBody) + b2kSetVelocity tBody, gSlimeDir[i], item 2 of tVel if gSlimeSpr[i] is not empty then b2kSpriteFlipH gSlimeSpr[i], ((gSlimeDir[i] < 0) is not gSlimeFlip[i]) -- bowl over any ground foe it reaches (shells are immune -- to each other; this loop only runs while one slides) @@ -2915,21 +2919,25 @@ command pfTickThwomps repeat with i = 1 to gBlockN put gBlockB[i] into tBody if tBody is empty then next repeat + if gBlockState[i] is "armed" then + -- a STATIC block resting at a fixed perch (x cached, y always 200): + -- trigger on the cached x + the hero snapshot, NO per-frame FFI. + -- Only the 0-1 blocks actually in motion below pay b2kPosition. + if abs(tHX - gBlockX[i]) < 42 and tHY > 200 then + put "falling" into gBlockState[i] + b2kSetDynamic tBody + b2kSetGravityScale tBody, 1.8 -- slam, not float + -- (face swaps only exist on the block_* fallback art; the + -- chained weight is one frame and the chain stays behind) + if gBlockFace[i] is true then b2kSpriteSetFrame gBlockSpr[i], "block_fall" + end if + next repeat + end if put b2kPosition(tBody) into tPos if tPos is empty then next repeat put item 1 of tPos into tBX put item 2 of tPos into tBY switch gBlockState[i] - case "armed" - if abs(tHX - tBX) < 42 and tHY > tBY then - put "falling" into gBlockState[i] - b2kSetDynamic tBody - b2kSetGravityScale tBody, 1.8 -- slam, not float - -- (face swaps only exist on the block_* fallback art; the - -- chained weight is one frame and the chain stays behind) - if gBlockFace[i] is true then b2kSpriteSetFrame gBlockSpr[i], "block_fall" - end if - break case "falling" pfBlockUndersideCheck tHX, tHY, tBX, tBY if abs(item 2 of b2kVelocity(tBody)) < 4 and tBY > 320 then @@ -2960,6 +2968,7 @@ command pfTickThwomps b2kSetStatic tBody b2kMoveTo tBody, tBX, 200 b2kSpriteMoveTo tBody, tBX, 200 -- statics are not body-synced + put tBX into gBlockX[i] -- keep the cached perch honest after any drift -- re-seat the chain under the weight (defensive: the rise -- is vertical, but any drift would betray the chain) if gBlockChainA[i] is not empty then b2kSpriteMoveTo gBlockChainA[i], tBX, 76 diff --git a/plan.md b/plan.md index e558cdd..28db42b 100644 --- a/plan.md +++ b/plan.md @@ -320,3 +320,4 @@ user-confirmed in OXT before the next begins. | 2026-06-13 | **Showcase round 3: a third expansion + the ice-boulder physics fix.** User: "expand a little further — more clouds, coins, enemies, switches, gates; snails liberally; the OLD chained thwomps back; mimic iconic classics; and the ice boulder should SLIDE ALL THE WAY, not reset (it is an ice block)." Done: (1) BOULDER — lowered its friction to 0.18 so it coasts, and moved its reset line off-screen-left (3050 → 2300, past the run and below the camera's left edge while the hero is in it), so a fresh one arrives from the source instead of a single block teleporting back in place; the slide path is clear (the snow cloud is a one-way chain above; the saw is bodiless; the L3 patrol slime sits left of 2300 so the re-park beats it there). (2) A fourth act on each level, finales shifted whole again (L1 +768 tail, L2 +640, L3 +512, L4 +512): the classic CHAINED-WEIGHT THWOMPS return in every level (with "ride the head" coins), SNAILS go liberal (L4 now carries four), more one-way clouds + coins + decor. New widths L1 6336 / L2 4672 / L3 5312 / L4 5376. Enemy indices still unique per level; flags within bounds. Note: the single-instance toys (gate/lever/spring/key-door) stay one-per-level by design — "more gates/switches" was met by carrying the door beat into more levels and stacking machine bays, not by generalising those mechanics (which would be a Kit-shaped change). Still zero Kit changes. | User review; this commit | | 2026-06-13 | **Final layout pass: the thwomp "ride-the-head" coins were a gate-locking trap; moved on-path.** User found an L3 coin they couldn't find. Root cause: the new thwomps' reward coins were placed at the perch height (first embedded in the 60px block at y184, then lifted to y110 above it) and were reachable only by the obscure "hop onto the resting crusher and ride it up" beat - which a player avoiding the hazard never discovers, so a REQUIRED (collect-every-coin) coin could lock the goal-flag gate. Fix: all four new thwomp coins (L1 5700, L2 3600, L3 4280, L4 4600) moved to **y500, directly under each crusher on the critical path** - the crusher is too tall to jump over, so the hero MUST pass beneath it to progress and grabs the coin by passing (timing the crush), guaranteeing the gate can open. A full coin-reachability audit (every coin vs the slabs/clouds/pits/plats per level) confirmed every other coin is reachable via its mechanism (spring arcs, the rope-bridge deck, jump-over-pit arcs, the mound plateau, L2's original verified ride coin at 1840,64). Lesson: **a REQUIRED coin must sit on the critical path or an unskippable beat - never behind an optional, discover-it-yourself trick.** Still zero Kit changes. | User review; this commit | | 2026-06-13 | **Showcase round 4: a marquee CRUSHER ALLEY + cloud hop closes every level.** User: "make these longer - add a row of green/grass thwomps to run under/over, more clouds, longer levels in the same style; show off the engine with as much pizzazz as possible." A new `pTileFace` param on `pfMakeThwomp` makes a plain TILE-SPRITE crusher (no mood-face swaps; the same armed->drop->rest->rise->re-arm cycle), so a ROW of four becomes a timing gauntlet the hero dashes BENEATH (the 42px trigger << 180px spacing means only one drops at a time; coins sit at the 90px midpoints, y500). One alley per level, biome-matched so the mechanic reads at a glance: GREEN blocks (L1 last turn, + L2 grass), BLUE (L3 ice), RED (L4 haunted), each followed by a two-cloud HOP to the flag (clouds at y448 - a 154px jump clears the 128px - coins up top at y392), the clouds one-way chains ghost-padded a tile past the art each side (solid span == art span per the ghost rule). Finales (walled door + steps + flag + bounds) shifted RIGHT as whole units by +1280 (tile-aligned): L2 4672->5952, L3 5312->6592, L4 5376->6656 (L1 was already 7552). New snails in L2 (gSlime 4) and L3 (gSlime 5) keep snails liberal; new crushers are gBlock 5-8 (L2), 3-6 (L3), 4-7 (L4) - unique per namespace per level. Door-gated coins stay reachable (the keys are early on the main path, before each alley). Flags within plat2; coins/totals self-count as built. Static checker clean; awaiting the OXT pass. Still zero Kit changes (harness v10 holds). | User review; this commit | +| 2026-06-13 | **Showcase round 4b: a per-frame optimization pass (user: "as fully optimized to the current kit/library as possible").** An Opus audit of `on b2kFrame` + all 14 `pfTick*` against the perf playbook (cost order: interpreter ops > FFI round-trips > property-set redraws) found one real regression and three free wins, all example-side. (1) HIGH - `pfTickThwomps` did an FFI `b2kPosition` + comma-split for EVERY block every frame, and the new crusher alleys had doubled the block count to ~7-8/level while only 0-1 are ever in motion. Fix: cache each block's perch x at make + re-arm (new `gBlockX[]`), gate the armed->falling trigger on the cached x + the hero snapshot (perch y is invariantly 200, so `tHY > tBY` becomes `tHY > 200`), and read the live position ONLY in the in-motion states - cutting thwomp FFI from ~8/frame to ~0-1/frame with byte-identical trigger semantics. (2) The `shellslide` tick read `b2kVelocity` twice (vx test + vy pass-through) -> read once into a local. (3) The HUD reused the already-snapshotted `gHeroState` instead of re-calling `b2kPlayerState()` and hoisted `the hScroll of b2kCamGroup()` (read twice) into one local - both 4 Hz so minor, but free. The audit confirmed every other tick already optimal (O(1) idle gates via `gXxxN is 0`/`is empty`, hoisted clocks, the shared snapshot, change-gated property/velocity writes, sleeping bodies left asleep) and the gotcha scan clean (no smart quotes; no `local` nested in a block; no per-frame velocity write to a resting body; no two velocity-asserting bodies sharing a band; no stale `the result`). Example-only, so NO harness bump (v10 holds; the harness drives the Kit, not the example's `pf*` ticks). Static checker clean; awaiting the OXT pass. | User direction; this commit |