Skip to content
Merged
41 changes: 41 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,47 @@ The native shim's ABI is tracked separately by `b2Version()` (currently `4`).

### Added

- **Wave 4 (liquids) — SWIM, the Kit's first new player-action since
Wave 2 (statically verified + harness v12; user play-tested in the
platformer).** A new `b2kPlayerAddWater x1,y1,x2,y2` registers a polled
water zone (world state, wiped by `b2kClear`, exactly like the ladder
zones). While the player's centre is submerged the controller SWIMS:
gravity drops to `swimGravity` so you sink slowly, the sink caps at
`swimMaxFall` (far below the air terminal), UP/DOWN swim at `swimSpeed`,
and a JUMP press is a REPEATABLE upward STROKE (`swimJump`) with no ground
gate. A new `swim` state plus a `b2kPlayerAnims` swim slot (a 9th arg,
falling back to the fall pose, so three-arg calls still work) drive the
art. Swim is mutually exclusive with the climb (the tick starts only
one); leaving the zone, a hurt, or teardown restores the saved gravity
scale exactly once. The swim path costs ONE compare per frame when no
water zones exist. Two Opus correctness reviews found no blockers.
- **The platformer's L1 GREEN HILLS gains a HILLTOP POOL** — the swim
showcase, in the level the game is actually play-tested in. A RAISED
swim basin between two earth banks past the crusher alley: the 640-tall
world clamps the camera at its bottom, so a swim pool is a basin held
at the surface, never a sub-ground pit. Hop in, dive for three
underwater coins (the gate needs them), then stroke up + hold-forward
to HOP out the far bank to the flag. New `pfMakeWater` helper; per the
playtest the water was made heavier (`swimGravity` 0.6, `swimMaxFall`
200, a trimmed `swimJump` 300 — `swimJump` alone sets the escape, so it
is the lever for "harder to climb out"). A debug warp (`0` on L1)
reaches the pool for fast iteration.
- **Fixed a pre-existing brick head-bump gap** the pool work surfaced:
the hero's 88px capsule was taller than its ~76px visible character
(128px frame headroom at a 0.75 down-scale), so heads "missed" bricks
by the difference even though the smash fired. The hitbox now matches
the art (feet-aligned, bind offset derived) and the bonk window reads
the real half-height instead of a hardcoded 44 (gotcha 28).
- **Harness v12** adds three swim tests: `stTestSwim` (dive / buoyant cap
/ repeatable stroke / swim-up / gravity-restored-on-exit, every value
printed), `stTestSwimGrounded` (swim while resting on a submerged
floor — the pool-floor case), and `stTestSwimClear` (the level-rebuild
path: `b2kClear` must wipe the zone, or the next level's player is born
swimming in mid-air where the old pool was).
- The micro-game also gained an L3 "THE DEEP" swim level (data verbs
`water` + a `fish` pit-dweller), but it shows a white-world build issue
in its own example code and is set aside pending a focused pass — the
Kit swim itself is sound (it runs clean in the platformer).
- **Platformer SHOWCASE polish round (statically verified; awaiting the
OXT pass).** A pre-Wave-4 pass over the platformer to make it a
polished demo of the kit *as it stands today* — longer, better-spaced
Expand Down
25 changes: 24 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ failure. Run it after **every** `.livecodescript` edit.
pass" and let the user confirm.

**The self-test harness** (`examples/box2dxt-selftest.livecodescript`) is the runtime safety net:
~113 deterministic assertions (currently **v10**) driving the real Kit (paused world +
~125 deterministic assertions (currently **v12**) driving the real Kit (paused world +
`b2kStepOnce` hand-stepping + `b2kInputInject` scripted keys). The workflow for every **Kit**
change: (1) add/extend an assertion
that captures the new behavior, (2) **bump `kStHarnessV`** (the report header prints it, so a
Expand Down Expand Up @@ -226,6 +226,17 @@ OXT's compiler is **stricter than LiveCode's**. These are the recurring footguns
27. **`the result` is consumed by the NEXT command** — capture it into a
local immediately after every `b2kSpawn*`/maker call before calling
anything else (several past bugs were a stale `the result`).
28. **A physics hitbox taller than the VISIBLE sprite makes the head bump
things it never visually touches.** A capsule sized to the sprite FRAME
(not the character within it) tops out above the drawn head when the art
is bottom-aligned with frame headroom (Kenney's 128px characters at a
down-scale): the invisible "hat" hits the brick/ceiling while the visible
head stops short with a gap — even though the contact and the head-bump
poll both fire. Size the hitbox to the VISIBLE art, feet-aligned (derive
the bind offset from it), and have any head-reach logic read the body's
real half-height (a build-time global), never a hardcoded constant. The
platformer's brick-smash gap (round 7) was exactly this: an 88px capsule
over a ~76px visible character.

## The single-threaded performance playbook

Expand Down Expand Up @@ -301,6 +312,18 @@ The rules, each earned by a measured regression:
- **Ladders:** run the zone a little above a platform at the ladder's
top (walk-off + DOWN grabs it); zones are world state (`b2kClear`
wipes them).
- **Liquids / SWIM (Wave 4):** a swim pool CANNOT be a sub-ground pit —
`b2kCamBounds` clamps the camera at the world's bottom edge, so anything
below the ground line is off-screen. A swimmable pool is a RAISED basin
between two banks (or the whole ground raised, as the micro-game did):
hop in, dive for the underwater coins, stroke up + hold-forward to HOP
out the far bank. Water zones (`b2kPlayerAddWater`) are world state,
wiped by `b2kClear` like ladders. **Tuning:** `swimGravity` sets only the
between-stroke SINK; the single-stroke escape height is `swimJump` ALONE
(the stroke sets velocity directly, then full air-gravity governs the
apex once you break the surface). To make climbing out HARDER, lower
`swimJump` — raising `swimGravity` only makes you sink faster between
strokes.
- **Hazard mercy patterns:** a riser never rises under the hero's feet
(the piranha); proximity hazards give a sprint-speed telegraph
(mimic wake ≥110px); unkillable hazards follow "the saw rule" (skip
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ Box2D v3.1.0 (fetched by CMake)
two-level platformer (menu → levels → win screen, embedded art,
synthesized sound), the
[platformer showcase](examples/box2dxt-platformer.livecodescript) is the
Game Kit pushed hard — player controller, scrolling camera, spritesheets,
coin puzzles — and the
Game Kit pushed hard — player controller (run/jump/climb/duck/swim),
scrolling camera, spritesheets, coin puzzles, a hilltop swim pool — and the
[slingshot](examples/box2dxt-slingshot.livecodescript) is pure physics
joy: catapult cannonballs into toppling towers, angry-birds style
(three levels, ballistic aim preview, zero assets). And the
Expand Down
134 changes: 131 additions & 3 deletions docs/expansion-prep.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ that keep the expansion as reliable as the engine underneath it.
| Wave 2 | **COMPLETE — user-verified 2026-06-13** (player actions I, harness v10; see §9) |
| Wave 3 | **BUILT — statically verified 2026-06-13** (bestiary I + HAUNTED HOLLOW; see §10) |
| Showcase polish | **BUILT — statically verified 2026-06-13** (pre-Wave-4: longer/re-spaced levels, the kit's first JOINT mechanics — rope bridge + boulder + barrel; a prototyped wrecking ball was cut as un-sprite-able — and four variety species; all example-side, zero Kit change, no harness bump) |
| Next | **Wave 4** (liquids: swim zones + lava + pit dwellers + collapsing bridge) — see §7 |
| Wave 4 | **SWIM user play-tested in the platformer 2026-06-14** (harness **v12**, two Opus reviews clean; see §11). The Kit gained `b2kPlayerAddWater` + a buoyant `swim` mode/state/anim; the platformer's L1 GREEN HILLS gained a **HILLTOP POOL** (a raised-bank basin — the swim showcase, where it's tested), tuned heavier and with the hero hitbox fixed to match the art (gotcha 28), all per the user's OXT pass. DONE: swim zones, pit-dwellers (the micro-game `fish`, debut), lava (already in platformer L4). CARRY-OVER: the collapsing-bridge trap, and the micro-game's L3 "THE DEEP" (built but shows an example-side white-world build issue — set aside) |
| Next | **Wave 5 — player actions II** (wall-slide/jump, dash, double-jump; capsule reshape on duck) — see §7 and §12. Loose ends: the collapsing bridge + the micro-game L3 fix |
| Companions | [plan.md](../plan.md) (history/decision log) · [game-engine-spec.md](game-engine-spec.md) (module design) |

---
Expand Down Expand Up @@ -210,8 +211,9 @@ to Kit API (`b2kFoe…`).
shape-def filter already covers chain creation).
3. **Wave 3 — bestiary I:** shelled (kickable!), ghost, bat, mimic,
pipe plant, crusher-with-faces — into a platformer "haunted" section.
4. **Wave 4 — liquids:** swim zones + lava + pit dwellers + collapsing
bridge; a water level in the micro-game (level 3).
4. **Wave 4 — liquids:** swim zones (done — the platformer's hilltop pool)
+ lava (already in L4) + pit dwellers (the micro-game fish) + the
collapsing bridge (carry-over). As-built record in §11.
5. **Wave 5 — player actions II:** wall-slide/jump, dash, double-jump
powerup (boxItem delivers it), platform carry.
6. **Wave 6 — bestiary II + promotion:** chaser, lunger, spider, saws;
Expand Down Expand Up @@ -453,3 +455,129 @@ knockback; only pits/kill-plane respawn.
3. No regression: L1-L3 + micro-game still complete (spacing pass
verified at the same time).
4. Docs ride along: CHANGELOG, plan.md decision log, this section.

## 11. Wave 4 — liquids (swim) — as built (2026-06-14)

The first new player-action since Wave 2, built as a faithful parallel to
the ladder/climb system and **user play-tested in the platformer**.

### 11.1 The swim feature (Kit)

- **`b2kPlayerAddWater x1,y1,x2,y2`** — a polled water zone (flat
`sPlayWat*` arrays, parallel to `sPlayLad*`); world state, wiped by
`b2kClear`/`b2kPlayerForget`, surviving an attach — exactly like ladders.
- **`swim` mode** — while the centre is in a zone: gravity scales to
`swimGravity` (the body's own scale is saved and restored exactly once,
like the climb), the sink caps at `swimMaxFall`, UP/DOWN drive vy at
`swimSpeed`, and a JUMP press is a *repeatable* upward STROKE of
`swimJump` (no grounded/coyote/buffer gate). The `swim` state overrides
the grounded/airborne machine and clears `sPlayAir`, so surfacing is not
a phantom land. A 9th `pSwim` arg on `b2kPlayerAnims` falls back to fall.
- **Mutual exclusion with climb** — both park gravity via *separate* saves,
so the start gates check BOTH flags and two saves never fight. The tick
costs ONE compare/frame when no water zones exist.
- Knobs (`swimSpeed`/`swimJump`/`swimGravity`/`swimMaxFall`) cached in
`b2kPlayerTuneCache`. Two Opus correctness reviews (the Kit change; the
micro-game + harness) — no blockers.

### 11.2 The layout law: a swim pool is a RAISED basin

`b2kCamBounds` clamps the camera at the world's bottom edge, so a sub-ground
pit is off-screen. A swimmable pool is therefore a **raised basin between
two banks** (the platformer) or the whole ground raised (the micro-game):
hop in, dive for the underwater coins (which FORCE the swim via the
coin-gate), then stroke up + hold-forward to hop out the far bank.

### 11.3 Playtest tuning + the swimGravity/swimJump lesson

The pool felt too floaty (you could pop straight out), so it was made
heavier: `swimGravity` 0.6, `swimMaxFall` 200, `swimJump` 300. The lesson
worth keeping: **`swimGravity` sets only the between-stroke SINK; the
single-stroke escape height is `swimJump` ALONE** (the stroke writes
velocity directly, then full air-gravity governs the apex once you break the
surface). The lever for "harder to climb out" is `swimJump`, not gravity.

### 11.4 The brick head-bump fix (gotcha 28)

The pool work surfaced a pre-existing regression: the hero's 88px capsule
was taller than its ~76px visible character (128px frame headroom at a 0.75
down-scale), so the invisible "hat" hit bricks while the visible head sat
~12px low (the bonk still fired). Fixed by sizing the hitbox to the art
(`tH` 88→76, `tDY` derived to keep the feet planted) and reading the body's
real half-height (`gHeroHalfH`) in the bonk window — see gotcha 28.

### 11.5 Harness v12

- `stTestSwim` — dive / buoyant cap / repeatable stroke / swim-up /
gravity-restored-on-exit, every value printed.
- `stTestSwimGrounded` — swim while resting on a submerged floor (the
pool-floor case): still `swim`, a stroke lifts off with no grounded gate.
- `stTestSwimClear` — the level-rebuild path: `b2kClear` must wipe the zone,
or the next level's player is born swimming where the old pool was.

### 11.6 Status / loose ends

- The platformer's L1 hilltop pool is the user-tested swim showcase; a
`0`-key debug warp (delete-before-merge sentinel) reaches it fast.
- The micro-game's L3 "THE DEEP" (data verbs `water` + `fish`) is built but
shows an example-side **white-world build issue** (set aside — the Kit
swim is sound). To revisit: the L3 build path in `mgBuild` / the new verbs.
- The **collapsing-bridge** trap (the last named Wave 4 mechanic) is carried
to the loose-ends list.

## 12. Wave 5 design — player actions II (prepared 2026-06-14)

The next wave: four moves that extend the controller. All are KIT changes
(harness bumps), each a parallel to an existing tick path — reuse the
start-gate discipline that keeps swim/climb from fighting.

### 12.1 Wall-slide / wall-jump

- **Detect:** a side probe (mirror the ground ray) reports a wall contact on
the facing side while airborne and pressing toward it — a new `sPlayOnWall`
(+ side), polled like grounding.
- **Slide:** while wall-pressed and falling, cap the descent at a new
`wallSlideMax` (a velocity assert, like the swim sink).
- **Jump:** a JUMP press while sliding launches up-and-away (`wallJumpX`/
`wallJumpY`), consuming the press, with a brief control-lock so the
away-velocity is not instantly cancelled (the knockback hand-off pattern).
- **State `wallslide`** + a 10th `b2kPlayerAnims` slot (falls back to fall).

### 12.2 Dash

- A direction + a new "dash" action (bound like "jump") gives a fast
horizontal burst (`dashSpeed`) for `dashMs`, then decays, gated by
`dashCooldownMs`; air-dash optional (one per airtime). **State `dash`** —
a velocity assert for its window (gotcha 17: it must keep moving). A dash
into water/ladder yields to swim/climb (those modes win, as over walk).

### 12.3 Double-jump (powerup-delivered)

- The "?-box" (boxItem) delivers a double-jump charge. An airborne
`b2kPlayerJump` gated on a charge count (`sPlayAirJumps`, reset on
landing) — reuses the gate-free `b2kPlayerJump`, so it is mostly
bookkeeping + the pickup. A pickup, not a permanent knob.

### 12.4 Duck capsule reshape (deferred from Wave 2 by design)

- Wave 2's duck brakes but keeps the hitbox; Wave 5 shrinks the capsule to
crawl under low gaps. The hard part is gotcha 28's cousin — a
**bottom-anchored** reshape (keep the FEET planted as the capsule
shortens) — and restoring it only when a ceiling probe finds headroom.
New `duckHeight` knob.

### 12.5 Harness plan

One self-diagnosing test per move: wall-slide caps the fall + wall-jump
launches away + leaves the state; dash bursts to `dashSpeed`, decays, and
respects the cooldown; double-jump fires once airborne and re-arms on
landing; duck-reshape shrinks the probed half-height and restores only under
headroom. Bump `kStHarnessV` per the rule.

### 12.6 Exit criteria

1. Harness all-pass (new states + the existing suite).
2. Each move feels right in the platformer (the test bed) and composes with
swim/climb/duck/knockback without fighting (the start-gate discipline).
3. Docs ride along (CHANGELOG, plan.md, this section).
4. No regression: every existing level still completes.
14 changes: 7 additions & 7 deletions docs/game-engine-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,9 +361,9 @@ Input module and writes `b2kSetVelocity` x / preserves y.
| `b2kPlayerMake pX, pY, pW, pH [,pSheet]` → control | One call: sprite (or plain capsule graphic if no sheet), capsule body, controller defaults, input armed. Reports the control. |
| `b2kPlayerAttach pCtrl` | Adopt an existing control/sprite as the player (capsule body added if it has none). |
| `b2kPlayerSet pKey, pValue` / `b2kPlayerGet(pKey)` | Tuning knobs (table below). |
| `b2kPlayerAnims pIdle, pRun, pJump, pFall [,pLand]` | Map controller states to sheet animations; auto-`FlipH` from facing. |
| `b2kPlayerAnims pIdle, pRun, pJump, pFall [,pLand] [,pDuck] [,pClimb] [,pHurt] [,pSwim]` | Map controller states to sheet animations; auto-`FlipH` from facing. (Wave 2-4 slots are optional fallbacks — see kit-reference.) |
| `b2kPlayerOnGround()` → bool | Grounded this frame (post-tick). |
| `b2kPlayerState()` → word | `idle` / `run` / `jump` / `fall` (+ `land` transition tick). |
| `b2kPlayerState()` → word | `idle` / `run` / `jump` / `fall` / `duck` / `climb` / `hurt` / `swim` (+ `land` transition tick). |
| `b2kPlayerFacing()` → 1 / -1 | Last horizontal intent. |
| `b2kPlayerJump [pSpeed]` | Programmatic jump (springs, double-jump powerups) — respects the same state machine. |
| `b2kPlayerControl pFlag` | Enable/disable input→motion (cutscenes; physics continues). |
Expand Down Expand Up @@ -396,11 +396,11 @@ automatically when `b2kPlayerAnims` is set.
segments are one-sided exactly the way platformers need — a capsule rises
through from below and lands on top (`b2kChain`/`b2kSmoothGround`, top surface
listed right-to-left; plain `b2kWall` segments are two-sided and cannot do
this). **Explicitly deferred** (designed-for, not in v1): moving-platform
velocity carry (v1 relies on friction), player-initiated drop-through
(pressing down to fall through a ledge — needs a brief collision-mask window),
wall-jump/slide, swim zones, multiple simultaneous players (state is
per-control already; only the input bindings are global).
this). **Explicitly deferred** (designed-for, not in v1) — several have
since landed: **drop-through** and **ladders** in Wave 2, **SWIM zones** in
Wave 4. Still ahead: moving-platform velocity carry (v1 relies on friction),
**wall-jump/slide** and **dash** (Wave 5), multiple simultaneous players
(state is per-control already; only the input bindings are global).

## 7. Module: Camera

Expand Down
Loading
Loading