Merge pull request 'Clean release scrub: remove internal fleet references' (#2) from clean-release-scrub into main

Reviewed-on: https://10.244.136.121:3030/matthew/Isle_Slingers/pulls/2
This commit is contained in:
matthew 2026-09-29 14:43:57 +00:00
commit 6de314d5e5
40 changed files with 66 additions and 67 deletions

View file

@ -1,13 +1,12 @@
# BUILD RECORD — RC-0D193E "Build Angry Birds, Report Process" # BUILD RECORD — "Build Angry Birds, Report Process"
- **RC:** RC-0D193E (tier: scout, owner: rc-ops, assigned: Hermie(s))
- **Worker:** Hermie (HANDS worker, Hermes Agent v0.19.1, default profile) - **Worker:** Hermie (HANDS worker, Hermes Agent v0.19.1, default profile)
- **Date:** 2026-08-28 (UTC) - **Date:** 2026-08-28 (UTC)
- **Artifact root:** `/root/rc-0d193e-isle-slingers/` (this git repository) - **Artifact root:** this git repository
- **Result:** shipped — playable browser game + this build record. Test - **Result:** shipped — playable browser game + this build record. Test
suites: **sim 15/15 PASS + page 7/7 PASS** (see §5; post-shipment suites: **sim 15/15 PASS + page 7/7 PASS** (see §5; post-shipment
fixes: bug #11 boot crash, bugs #12-13 physics+camera, bugs #14-15 fixes: bug #11 boot crash, bugs #12-13 physics+camera, bugs #14-15
release+direction — all found by the Commander's playtest). release+direction — all found in playtest).
--- ---
@ -256,7 +255,7 @@ audio → UI → tests. Bugs found by the headless harness and fixed in order:
order against a DOM stub built from the real `index.html` ids and order against a DOM stub built from the real `index.html` ids and
plays a full click session headlessly. plays a full click session headlessly.
12. **Inverted box-pair separation — forts self-destructed (found 12. **Inverted box-pair separation — forts self-destructed (found
post-shipment, Commander's playtest: "objects glitching like crazy, post-shipment, playtest feedback: "objects glitching like crazy,
bouncing around on their own", "unbreakable objects", "can't kill a bouncing around on their own", "unbreakable objects", "can't kill a
pig").** `resolveBoxPair` pushed *resting* blocks **toward** each pig").** `resolveBoxPair` pushed *resting* blocks **toward** each
other: with `s = a.x < c.x ? -1 : 1`, `a.x -= s*ox*0.5; c.x += other: with `s = a.x < c.x ? -1 : 1`, `a.x -= s*ox*0.5; c.x +=
@ -292,7 +291,7 @@ audio → UI → tests. Bugs found by the headless harness and fixed in order:
Arms added: fort rest (test 14) — RED on the original solver Arms added: fort rest (test 14) — RED on the original solver
("L1: structure still moving at idle (max 76.5 px/s)"), GREEN ("L1: structure still moving at idle (max 76.5 px/s)"), GREEN
after. after.
13. **Camera never showed sling and target together (Commander: "cannot 13. **Camera never showed sling and target together (playtest: "cannot
see the entire playing field and have to guess where to sling the see the entire playing field and have to guess where to sling the
birds", sling "not visible anywhere").** Two framings, neither birds", sling "not visible anywhere").** Two framings, neither
sufficient: aiming → camera at (480, 740), a 1280 px view showing sufficient: aiming → camera at (480, 740), a 1280 px view showing
@ -312,7 +311,7 @@ audio → UI → tests. Bugs found by the headless harness and fixed in order:
`e.y - r` while the level data and the integrity test treat `e.y` as `e.y - r` while the level data and the integrity test treat `e.y` as
the center — every mogg dropped 24-38 px on load, one more "objects the center — every mogg dropped 24-38 px on load, one more "objects
moving on their own" moment.) moving on their own" moment.)
14. **Sling never lets go (Commander round 2: "sling never actually lets 14. **Sling never lets go (playtest round 2: "sling never actually lets
go of bird when the bird is shot out").** Purely visual: go of bird when the bird is shot out").** Purely visual:
`drawSling` drew the taut rubber bands toward the active bird `drawSling` drew the taut rubber bands toward the active bird
whenever the bird existed — including while it was *flying* — so the whenever the bird existed — including while it was *flying* — so the
@ -321,7 +320,7 @@ audio → UI → tests. Bugs found by the headless harness and fixed in order:
`bird.state === 'aiming'` (pouch or mid-drag); in flight the bands `bird.state === 'aiming'` (pouch or mid-drag); in flight the bands
hang relaxed (the pre-existing else-branch). Arm 17 (page harness, hang relaxed (the pre-existing else-branch). Arm 17 (page harness,
`lineTo` recording) is RED on 2d9c8f47, GREEN after. `lineTo` recording) is RED on 2d9c8f47, GREEN after.
15. **Birds only launched backwards (Commander round 2: "Sling shot 15. **Birds only launched backwards (playtest round 2: "Sling shot
only shoots backwards and not in the direction of the target").** only shoots backwards and not in the direction of the target").**
Root cause in `main.js toWorld()`: the screen→world inverse was Root cause in `main.js toWorld()`: the screen→world inverse was
`(sx - VW/2) + cam.x` — it added the camera center but never divided `(sx - VW/2) + cam.x` — it added the camera center but never divided
@ -350,7 +349,7 @@ audio → UI → tests. Bugs found by the headless harness and fixed in order:
Process notes: Process notes:
- The headless harness caught **11 bugs pre- and post-shipment on its - The headless harness caught **11 bugs pre- and post-shipment on its
own** (10 in dev + the page boot); the Commander's playtest then found own** (10 in dev + the page boot); playtest then found
**4 more** (#12-15) that the harness *could* have caught — the missing **4 more** (#12-15) that the harness *could* have caught — the missing
invariants were "the world is at rest", "the view shows the play", invariants were "the world is at rest", "the view shows the play",
"the input maps where the player sees", and "the sling lets go". "the input maps where the player sees", and "the sling lets go".
@ -372,7 +371,7 @@ Process notes:
``` ```
# play (any static server, or just open the file): # play (any static server, or just open the file):
cd /root/rc-0d193e-isle-slingers cd isle-slingers # repo root, after cloning
python3 -m http.server 8000 # then open http://localhost:8000/ python3 -m http.server 8000 # then open http://localhost:8000/
# or open index.html directly in a browser # or open index.html directly in a browser
@ -404,10 +403,10 @@ designs are original. The game is named **Isle Slingers**.
## 10. Commit ## 10. Commit
See the repository commit SHA recorded in the RC completion payload. See the repository commit SHA for this build.
Working tree at commit time contains exactly the files listed in §3. Working tree at commit time contains exactly the files listed in §3.
## 11. Addendum — RC-375D84 S-2 modularization (2026-09-28) ## 11. Addendum — modularization pass (2026-09-28)
The flat `js/{core,levels,audio,renderer,main}.js` files described in §3 no The flat `js/{core,levels,audio,renderer,main}.js` files described in §3 no
longer exist. The tree was restructured (behavior-neutral, proven by a longer exist. The tree was restructured (behavior-neutral, proven by a

View file

@ -1,6 +1,6 @@
# Isle Slingers — MODULES.md (master module list) # Isle Slingers — MODULES.md (master module list)
RC-375D84 S-2. Folders = modules, subfolders = submodules. Every module and the modularization pass. Folders = modules, subfolders = submodules. Every module and
submodule carries an `agents.md` describing its surface and invariants. submodule carries an `agents.md` describing its surface and invariants.
Load contract (order is load-bearing) is in `architecture.md` and encoded in Load contract (order is load-bearing) is in `architecture.md` and encoded in
`index.html` + `test/page.test.mjs`. `index.html` + `test/page.test.mjs`.
@ -35,7 +35,7 @@ Load contract (order is load-bearing) is in `architecture.md` and encoded in
| helpers | wood, ice, stone, mogg, grum, king, SLING | — | | helpers | wood, ice, stone, mogg, grum, king, SLING | — |
| data/level-N | one level object each (returns it) | wood/ice/stone/mogg/grum/king, SLING | | data/level-N | one level object each (returns it) | wood/ice/stone/mogg/grum/king, SLING |
## Physics notes (S-3/S-4/S-5, RC-375D84 — landed 2026-09-29) ## Physics notes (S-3/S-4/S-5, the physics + modularization update — landed 2026-09-29)
- **topple** (S-3): blocks carry a rotational DOF (`ang`, `angVel`, `pivoting`). - **topple** (S-3): blocks carry a rotational DOF (`ang`, `angVel`, `pivoting`).
A resting block whose COM is past its support span pivots about the A resting block whose COM is past its support span pivots about the
support-edge corner (full continuous rotation, Q1) and either lands on the support-edge corner (full continuous rotation, Q1) and either lands on the

View file

@ -1,7 +1,7 @@
# Isle Slingers — Architecture # Isle Slingers — Architecture
Original architecture for a fully offline, canvas-rendered slingshot game. Original architecture for a fully offline, canvas-rendered slingshot game.
No frameworks, no build step, no external assets. As of RC-375D84 (S-2) the No frameworks, no build step, no external assets. As of the physics + modularization update (S-2) the
JavaScript is a **modularized tree**: folders are modules, subfolders are JavaScript is a **modularized tree**: folders are modules, subfolders are
submodules, and every module/submodule carries its own `agents.md`. submodules, and every module/submodule carries its own `agents.md`.
@ -52,7 +52,7 @@ app/index.js : the page — input, screens, main loop
The simulation is a deterministic impulse-ish engine: bodies integrate under The simulation is a deterministic impulse-ish engine: bodies integrate under
gravity at fixed 1/120 s, contacts resolve positionally, and damage routes gravity at fixed 1/120 s, contacts resolve positionally, and damage routes
through two funnels (`damageBlock`, `damageEnemy`). What each RC-375D84 step through two funnels (`damageBlock`, `damageEnemy`). What each physics-pass step
added, and where it lives: added, and where it lives:
- **Boards tip and topple (S-3, Q1 = full continuous rotation).** Blocks carry - **Boards tip and topple (S-3, Q1 = full continuous rotation).** Blocks carry
@ -145,9 +145,9 @@ same surface (every new spec was watched RED on the pre-change tree first).
## Conventions for agents working in this tree ## Conventions for agents working in this tree
1. Change a physics rule -> edit the one submodule that owns it (see each 1. Change a physics rule -> edit the one submodule that owns it (see each
`agents.md`); re-run `npm test` (27/27) and the golden-step hash `agents.md`); re-run `npm test` (47/47) and re-check the golden differential
(`/opt/data/s2_golden.mjs` against `/opt/data/core.orig.js`) when touching against the pre-modularization core when touching anything that predates
anything that predates RC-375D84 behavior. this modularization/physics pass.
2. Never re-introduce a flat file for a unit that has a submodule folder. 2. Never re-introduce a flat file for a unit that has a submodule folder.
3. Keep `index.html` script order in lockstep with `test/page.test.mjs`'s 3. Keep `index.html` script order in lockstep with `test/page.test.mjs`'s
`FILES` list — both encode the load contract. `FILES` list — both encode the load contract.

View file

@ -77,7 +77,7 @@
</div> </div>
</div> </div>
<!-- RC-375D84 S-2: modularized tree. Order matters: <!-- Modularized tree. Order matters:
physics submodules (shared scope) -> physics aggregator (ISLE_CORE) physics submodules (shared scope) -> physics aggregator (ISLE_CORE)
-> levels helpers + data (shared scope) -> levels aggregator (ISLE_LEVELS) -> levels helpers + data (shared scope) -> levels aggregator (ISLE_LEVELS)
-> audio -> render -> app --> -> audio -> render -> app -->

View file

@ -1,5 +1,5 @@
# js/app/ — agents.md # js/app/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Self-contained module — the page. Owns DOM, input, screens and the main loop. Self-contained module — the page. Owns DOM, input, screens and the main loop.

View file

@ -1,5 +1,5 @@
# js/audio/ — agents.md # js/audio/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Self-contained module. Exports `ISLE_AUDIO = { AudioEngine }`. Self-contained module. Exports `ISLE_AUDIO = { AudioEngine }`.

View file

@ -1,5 +1,5 @@
# js/levels/ — agents.md # js/levels/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
The levels **module** (aggregator). Shared scope `L`. The levels **module** (aggregator). Shared scope `L`.

View file

@ -1,5 +1,5 @@
# js/levels/data/ — agents.md # js/levels/data/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Level definitions, one file per level (`level-1.js`..`level-5.js`). Level definitions, one file per level (`level-1.js`..`level-5.js`).

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- levels/data/level-1.js (submodule: level-1) /* ISLE SLINGERS -- levels/data/level-1.js (submodule: level-1)
* RC-375D84 S-2: level 1 definition, split from the original js/levels.js. * Modularization pass: level 1 definition, split from the original js/levels.js.
* Installer: returns the level object built from the shared helpers scope L. * Installer: returns the level object built from the shared helpers scope L.
*/ */
(function (root, factory) { (function (root, factory) {

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- levels/data/level-2.js (submodule: level-2) /* ISLE SLINGERS -- levels/data/level-2.js (submodule: level-2)
* RC-375D84 S-2: level 2 definition, split from the original js/levels.js. * Modularization pass: level 2 definition, split from the original js/levels.js.
* Installer: returns the level object built from the shared helpers scope L. * Installer: returns the level object built from the shared helpers scope L.
*/ */
(function (root, factory) { (function (root, factory) {

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- levels/data/level-3.js (submodule: level-3) /* ISLE SLINGERS -- levels/data/level-3.js (submodule: level-3)
* RC-375D84 S-2: level 3 definition, split from the original js/levels.js. * Modularization pass: level 3 definition, split from the original js/levels.js.
* Installer: returns the level object built from the shared helpers scope L. * Installer: returns the level object built from the shared helpers scope L.
*/ */
(function (root, factory) { (function (root, factory) {

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- levels/data/level-4.js (submodule: level-4) /* ISLE SLINGERS -- levels/data/level-4.js (submodule: level-4)
* RC-375D84 S-2: level 4 definition, split from the original js/levels.js. * Modularization pass: level 4 definition, split from the original js/levels.js.
* Installer: returns the level object built from the shared helpers scope L. * Installer: returns the level object built from the shared helpers scope L.
*/ */
(function (root, factory) { (function (root, factory) {

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- levels/data/level-5.js (submodule: level-5) /* ISLE SLINGERS -- levels/data/level-5.js (submodule: level-5)
* RC-375D84 S-2: level 5 definition, split from the original js/levels.js. * Modularization pass: level 5 definition, split from the original js/levels.js.
* Installer: returns the level object built from the shared helpers scope L. * Installer: returns the level object built from the shared helpers scope L.
*/ */
(function (root, factory) { (function (root, factory) {

View file

@ -1,5 +1,5 @@
# js/levels/helpers/ — agents.md # js/levels/helpers/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Installs into `L`: wood, ice, stone (block factories, return center-based Installs into `L`: wood, ice, stone (block factories, return center-based
records), mogg, grum, king (enemy factories) and the SLING anchor {x:260,y:745}. records), mogg, grum, king (enemy factories) and the SLING anchor {x:260,y:745}.

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- levels/helpers/index.js (submodule: helpers) /* ISLE SLINGERS -- levels/helpers/index.js (submodule: helpers)
* RC-375D84 S-2: block/enemy constructor helpers + SLING anchor, split from * Modularization pass: block/enemy constructor helpers + SLING anchor, split from
* the original js/levels.js. Installs into the shared levels scope `L`; * the original js/levels.js. Installs into the shared levels scope `L`;
* js/levels/index.js loads it first, then the level-N data submodules. * js/levels/index.js loads it first, then the level-N data submodules.
*/ */

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- levels/index.js (module: levels -- aggregator / public API) /* ISLE SLINGERS -- levels/index.js (module: levels -- aggregator / public API)
* RC-375D84 S-2 modularization. The original js/levels.js monolith is now: * Modularization pass. The original js/levels.js monolith is now:
* levels/helpers/ - block/enemy constructors + SLING anchor (shared scope L) * levels/helpers/ - block/enemy constructors + SLING anchor (shared scope L)
* levels/data/level-N.js - one level definition each (uses the shared scope) * levels/data/level-N.js - one level definition each (uses the shared scope)
* This aggregator loads the parts in order, builds the LEVELS array, and * This aggregator loads the parts in order, builds the LEVELS array, and

View file

@ -1,5 +1,5 @@
# js/physics/abilities/ — agents.md # js/physics/abilities/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Installs into `P`: launch, triggerAbility. Installs into `P`: launch, triggerAbility.

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- physics/abilities/index.js (submodule: abilities) /* ISLE SLINGERS -- physics/abilities/index.js (submodule: abilities)
* RC-375D84 S-2 modularization (behavior-neutral split of the original * Modularization pass (behavior-neutral split of the original
* js/core.js monolith). Installs its units into ONE shared physics scope `P` * js/core.js monolith). Installs its units into ONE shared physics scope `P`
* that every physics submodule sees -- the same single-scope semantics the * that every physics submodule sees -- the same single-scope semantics the
* monolith had. Must load before js/physics/index.js (the aggregator that * monolith had. Must load before js/physics/index.js (the aggregator that

View file

@ -1,5 +1,5 @@
# js/physics/ — agents.md # js/physics/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
The physics **module** (aggregator). Deterministic, DOM-free. The physics **module** (aggregator). Deterministic, DOM-free.
@ -17,7 +17,7 @@ The physics **module** (aggregator). Deterministic, DOM-free.
(`activeBird`, `trajectory`, `aimVector`, `starsFor`, `birdsRemaining`). (`activeBird`, `trajectory`, `aimVector`, `starsFor`, `birdsRemaining`).
- Invariants: fixed-timestep consumer (tests run 1/120 s); no `Math.random()` - Invariants: fixed-timestep consumer (tests run 1/120 s); no `Math.random()`
in gameplay paths (determinism is a test arm); no DOM access. in gameplay paths (determinism is a test arm); no DOM access.
- New physics behavior lands in the owning submodule, NOT here. RC-375D84 - New physics behavior lands in the owning submodule, NOT here. The physics pass
S-3..S-5 (landed 2026-09-29): topple/ owns board tip-over (Q1 full continuous S-3..S-5 (landed 2026-09-29): topple/ owns board tip-over (Q1 full continuous
rotation), collision/ owns velocity-aware crush + mass-weighted stacks (S-4), rotation), collision/ owns velocity-aware crush + mass-weighted stacks (S-4),
step/ owns the landed-bird state and its integration (S-5). step/ owns the landed-bird state and its integration (S-5).

View file

@ -1,5 +1,5 @@
# js/physics/aim/ — agents.md # js/physics/aim/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Installs into `P`: aimVector, trajectory. Installs into `P`: aimVector, trajectory.

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- physics/aim/index.js (submodule: aim) /* ISLE SLINGERS -- physics/aim/index.js (submodule: aim)
* RC-375D84 S-2 modularization (behavior-neutral split of the original * Modularization pass (behavior-neutral split of the original
* js/core.js monolith). Installs its units into ONE shared physics scope `P` * js/core.js monolith). Installs its units into ONE shared physics scope `P`
* that every physics submodule sees -- the same single-scope semantics the * that every physics submodule sees -- the same single-scope semantics the
* monolith had. Must load before js/physics/index.js (the aggregator that * monolith had. Must load before js/physics/index.js (the aggregator that

View file

@ -1,5 +1,5 @@
# js/physics/camera/ — agents.md # js/physics/camera/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Installs into `P`: updatePopups, updateCameraTarget, decayCamera. Installs into `P`: updatePopups, updateCameraTarget, decayCamera.

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- physics/camera/index.js (submodule: camera) /* ISLE SLINGERS -- physics/camera/index.js (submodule: camera)
* RC-375D84 S-2 modularization (behavior-neutral split of the original * Modularization pass (behavior-neutral split of the original
* js/core.js monolith). Installs its units into ONE shared physics scope `P` * js/core.js monolith). Installs its units into ONE shared physics scope `P`
* that every physics submodule sees -- the same single-scope semantics the * that every physics submodule sees -- the same single-scope semantics the
* monolith had. Must load before js/physics/index.js (the aggregator that * monolith had. Must load before js/physics/index.js (the aggregator that

View file

@ -1,5 +1,5 @@
# js/physics/collision/ — agents.md # js/physics/collision/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Installs into `P`: crushCircleVsBox, collideBirdWorld, collideBirdBlocks, Installs into `P`: crushCircleVsBox, collideBirdWorld, collideBirdBlocks,
collideBirdEnemies, REST_VEL_CLAMP, resolveBoxPair. collideBirdEnemies, REST_VEL_CLAMP, resolveBoxPair.
@ -7,7 +7,7 @@ collideBirdEnemies, REST_VEL_CLAMP, resolveBoxPair.
- Contact resolution for circles (birds, enemies) vs axis-aligned blocks and - Contact resolution for circles (birds, enemies) vs axis-aligned blocks and
block-block pairs. `resolveBoxPair` is the box-box rest/impulse resolver; block-block pairs. `resolveBoxPair` is the box-box rest/impulse resolver;
`crushCircleVsBox` is the fast-falling-onto-structure damage path. `crushCircleVsBox` is the fast-falling-onto-structure damage path.
- RC-375D84 S-4 (landed 2026-09-29): `crushCircleVsBox` is velocity-direction- - Added 2026-09-29: `crushCircleVsBox` is velocity-direction-
aware (the box must be the body plowing into the enemy, `boxAlong > 30`; a aware (the box must be the body plowing into the enemy, `boxAlong > 30`; a
resting box never crushes), carries a per-enemy crush cooldown (0.12 s — one resting box never crushes), carries a per-enemy crush cooldown (0.12 s — one
landing = one crush instance) and an inelastic velocity transfer so boxes do landing = one crush instance) and an inelastic velocity transfer so boxes do

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- physics/collision/index.js (submodule: collision) /* ISLE SLINGERS -- physics/collision/index.js (submodule: collision)
* RC-375D84 S-2 modularization (behavior-neutral split of the original * Modularization pass (behavior-neutral split of the original
* js/core.js monolith). Installs its units into ONE shared physics scope `P` * js/core.js monolith). Installs its units into ONE shared physics scope `P`
* that every physics submodule sees -- the same single-scope semantics the * that every physics submodule sees -- the same single-scope semantics the
* monolith had. Must load before js/physics/index.js (the aggregator that * monolith had. Must load before js/physics/index.js (the aggregator that
@ -32,7 +32,7 @@
ent.vy -= ny * rel * 1.4; ent.vy -= ny * rel * 1.4;
if (isEnemy) { if (isEnemy) {
// crush damage: a fast box hurts what it lands on. // crush damage: a fast box hurts what it lands on.
// S-4 (RC-375D84): the direction gate is velocity-aware -- the BOX // Added in the physics pass: the direction gate is velocity-aware -- the BOX
// must be the body moving into the enemy (boxAlong > 30). The old gate // must be the body moving into the enemy (boxAlong > 30). The old gate
// (ny < -0.3, enemy above the box) only let SIDE plows crush: a block // (ny < -0.3, enemy above the box) only let SIDE plows crush: a block
// dropped STRAIGHT DOWN onto an enemy never registered ("falling // dropped STRAIGHT DOWN onto an enemy never registered ("falling
@ -145,7 +145,7 @@
// per-target hit cooldown so one pass-through = one damage instance // per-target hit cooldown so one pass-through = one damage instance
if (g.time - (e.lastHit || -9) > 0.12) { if (g.time - (e.lastHit || -9) > 0.12) {
var speed = Math.hypot(b.vx, b.vy); var speed = Math.hypot(b.vx, b.vy);
// retuned 2026-09-28 (RC-0D193E, Commander bug #3): the old curve // retuned 2026-09-28 (playtest finding): the old curve
// gave ~0.9 dmg to a soft wren lob — a mogg (hp1) could not be // gave ~0.9 dmg to a soft wren lob — a mogg (hp1) could not be
// killed without a perfect full-power hit. Bumped so a decent // killed without a perfect full-power hit. Bumped so a decent
// medium shot connects; the king (hp3) still survives one // medium shot connects; the king (hp3) still survives one
@ -186,7 +186,7 @@
var relv2 = (c.vy - a.vy) * n2; var relv2 = (c.vy - a.vy) * n2;
if (relv2 < -P.REST_VEL_CLAMP) { if (relv2 < -P.REST_VEL_CLAMP) {
// real impact: inelastic common velocity. // real impact: inelastic common velocity.
// S-4 (RC-375D84): mass-WEIGHTED (was a plain (a.vy+c.vy)/2), so a // Added in the physics pass: mass-WEIGHTED (was a plain (a.vy+c.vy)/2), so a
// heavy block landing on a light plank transfers its real momentum // heavy block landing on a light plank transfers its real momentum
// through the stack (chain-crush). A 22.6-mass stone at v over a // through the stack (chain-crush). A 22.6-mass stone at v over a
// 7.2-mass plank now drives the plank to ~0.76v instead of 0.5v. // 7.2-mass plank now drives the plank to ~0.76v instead of 0.5v.

View file

@ -1,5 +1,5 @@
# js/physics/constants/ — agents.md # js/physics/constants/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Installs into `P`: WORLD, VIEW, MATERIALS, BIRD_TYPES, ENEMY_TYPES, GRAVITY, Installs into `P`: WORLD, VIEW, MATERIALS, BIRD_TYPES, ENEMY_TYPES, GRAVITY,
LAUNCH_POWER, MAX_PULL, MIN_PULL, AIR_DRAG, BIRD_BOUNCE, BIRD_GROUND_FRICTION, LAUNCH_POWER, MAX_PULL, MIN_PULL, AIR_DRAG, BIRD_BOUNCE, BIRD_GROUND_FRICTION,

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- physics/constants/index.js (submodule: constants) /* ISLE SLINGERS -- physics/constants/index.js (submodule: constants)
* RC-375D84 S-2 modularization (behavior-neutral split of the original * Modularization pass (behavior-neutral split of the original
* js/core.js monolith). Installs its units into ONE shared physics scope `P` * js/core.js monolith). Installs its units into ONE shared physics scope `P`
* that every physics submodule sees -- the same single-scope semantics the * that every physics submodule sees -- the same single-scope semantics the
* monolith had. Must load before js/physics/index.js (the aggregator that * monolith had. Must load before js/physics/index.js (the aggregator that
@ -19,7 +19,7 @@
P.MATERIALS = { P.MATERIALS = {
wood: { hp: 1300, density: 1.0, restitution: 0.18, dmgFactor: 0.9, score: 500, maxVel: 520 }, wood: { hp: 1300, density: 1.0, restitution: 0.18, dmgFactor: 0.9, score: 500, maxVel: 520 },
ice: { hp: 480, density: 0.7, restitution: 0.05, dmgFactor: 1.6, score: 800, maxVel: 560 }, ice: { hp: 480, density: 0.7, restitution: 0.05, dmgFactor: 1.6, score: 800, maxVel: 560 },
// stone: retuned 2026-09-28 (RC-0D193E, Commander bug #3 "blocks and pig // stone: retuned 2026-09-28 (playtest finding: "blocks and pig
// bad guys too hard to kill"): at hp 3400 with dmgFactor 0.35 a 70x70 // bad guys too hard to kill"): at hp 3400 with dmgFactor 0.35 a 70x70
// cube took ~11 full-power direct hits — stone walls were functionally // cube took ~11 full-power direct hits — stone walls were functionally
// unbreakable, so every enemy behind one was unkillable. Now ~2-3 hits. // unbreakable, so every enemy behind one was unkillable. Now ~2-3 hits.

View file

@ -1,5 +1,5 @@
# js/physics/damage/ — agents.md # js/physics/damage/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Installs into `P`: damageBlock, damageEnemy. Installs into `P`: damageBlock, damageEnemy.
@ -9,6 +9,6 @@ Installs into `P`: damageBlock, damageEnemy.
here. here.
- The balance spec (`test/balance.test.mjs`) asserts per-material break budgets - The balance spec (`test/balance.test.mjs`) asserts per-material break budgets
and enemy kill budgets under full-power direct hits — that spec is the and enemy kill budgets under full-power direct hits — that spec is the
contract for everything that deals damage. S-4 (RC-375D84, falling-object contract for everything that deals damage. Falling-object
impact damage) routes through these two functions so it inherits the spec. impact damage) routes through these two functions so it inherits the spec.
- Consumes: MATERIALS, ENEMY_TYPES. - Consumes: MATERIALS, ENEMY_TYPES.

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- physics/damage/index.js (submodule: damage) /* ISLE SLINGERS -- physics/damage/index.js (submodule: damage)
* RC-375D84 S-2 modularization (behavior-neutral split of the original * Modularization pass (behavior-neutral split of the original
* js/core.js monolith). Installs its units into ONE shared physics scope `P` * js/core.js monolith). Installs its units into ONE shared physics scope `P`
* that every physics submodule sees -- the same single-scope semantics the * that every physics submodule sees -- the same single-scope semantics the
* monolith had. Must load before js/physics/index.js (the aggregator that * monolith had. Must load before js/physics/index.js (the aggregator that

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- physics/index.js (module: physics -- aggregator / public API) /* ISLE SLINGERS -- physics/index.js (module: physics -- aggregator / public API)
* RC-375D84 S-2 modularization. This is the THIN AGGREGATOR for the physics * Modularization pass. This is the THIN AGGREGATOR for the physics
* module. The original js/core.js monolith is now split into submodules * module. The original js/core.js monolith is now split into submodules
* (physics/constants, -/state, -/aim, -/abilities, -/damage, -/step, * (physics/constants, -/state, -/aim, -/abilities, -/damage, -/step,
* -/collision, -/rules, -/camera); each installs its units into ONE shared * -/collision, -/rules, -/camera); each installs its units into ONE shared

View file

@ -1,10 +1,10 @@
# js/physics/rules/ — agents.md # js/physics/rules/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Installs into `P`: checkEndStates, birdsRemaining, starsFor. Installs into `P`: checkEndStates, birdsRemaining, starsFor.
- Win/lose arbitration (called from step): all enemies dead -> won, no bird - Win/lose arbitration (called from step): all enemies dead -> won, no bird
left in play with enemies alive -> lost. S-5 (RC-375D84, landed 2026-09-29): left in play with enemies alive -> lost. S-5 (added 2026-09-29):
a LANDED bird counts as a bird still in play — do not declare `lost` while a LANDED bird counts as a bird still in play — do not declare `lost` while
one is out in the world (it can still fall, smash and kill). `starsFor(g)` one is out in the world (it can still fall, smash and kill). `starsFor(g)`
maps score to the level's three star thresholds. maps score to the level's three star thresholds.

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- physics/rules/index.js (submodule: rules) /* ISLE SLINGERS -- physics/rules/index.js (submodule: rules)
* RC-375D84 S-2 modularization (behavior-neutral split of the original * Modularization pass (behavior-neutral split of the original
* js/core.js monolith). Installs its units into ONE shared physics scope `P` * js/core.js monolith). Installs its units into ONE shared physics scope `P`
* that every physics submodule sees -- the same single-scope semantics the * that every physics submodule sees -- the same single-scope semantics the
* monolith had. Must load before js/physics/index.js (the aggregator that * monolith had. Must load before js/physics/index.js (the aggregator that

View file

@ -1,5 +1,5 @@
# js/physics/state/ — agents.md # js/physics/state/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Installs into `P`: createGame, makeBird, activeBird. Installs into `P`: createGame, makeBird, activeBird.
@ -8,7 +8,7 @@ Installs into `P`: createGame, makeBird, activeBird.
scorePopups, hadEnemies, camera, status, activeBird, launched, wonAt, lostAt, scorePopups, hadEnemies, camera, status, activeBird, launched, wonAt, lostAt,
stats`) is consumed by render, app and every test — extending is OK, silently stats`) is consumed by render, app and every test — extending is OK, silently
dropping a key is not. dropping a key is not.
- Block/enemy/bird records are plain objects. S-3/S-5 (RC-375D84, landed) - Block/enemy/bird records are plain objects. S-3/S-5 (added)
extended them additively: blocks carry `ang`, `angVel`, `pivoting` extended them additively: blocks carry `ang`, `angVel`, `pivoting`
(topple DOF) and birds gain the `landed` state (a live body under gravity (topple DOF) and birds gain the `landed` state (a live body under gravity
while another bird is in play; see step/topple). Keep further extensions while another bird is in play; see step/topple). Keep further extensions

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- physics/state/index.js (submodule: state) /* ISLE SLINGERS -- physics/state/index.js (submodule: state)
* RC-375D84 S-2 modularization (behavior-neutral split of the original * Modularization pass (behavior-neutral split of the original
* js/core.js monolith). Installs its units into ONE shared physics scope `P` * js/core.js monolith). Installs its units into ONE shared physics scope `P`
* that every physics submodule sees -- the same single-scope semantics the * that every physics submodule sees -- the same single-scope semantics the
* monolith had. Must load before js/physics/index.js (the aggregator that * monolith had. Must load before js/physics/index.js (the aggregator that

View file

@ -1,5 +1,5 @@
# js/physics/step/ — agents.md # js/physics/step/ — agents.md
RC-375D84 S-2. Module map: `MODULES.md`; runtime model: `architecture.md`. Part of the modularization pass. Module map: `MODULES.md`; runtime model: `architecture.md`.
Installs into `P`: step, stepPhysics, spendBird. **The physics hub.** Installs into `P`: step, stepPhysics, spendBird. **The physics hub.**
@ -10,7 +10,7 @@ Installs into `P`: step, stepPhysics, spendBird. **The physics hub.**
BEFORE nothing — but all of its callees must exist in `P` by the time `step` BEFORE nothing — but all of its callees must exist in `P` by the time `step`
runs (they do, because install order is load order). Keep it that way. runs (they do, because install order is load order). Keep it that way.
- `spendBird` retires a bird that is GONE (aged out / off the island). S-5 - `spendBird` retires a bird that is GONE (aged out / off the island). S-5
(RC-375D84, landed 2026-09-29) split the settle branch: a bird that settles (added 2026-09-29) split the settle branch: a bird that settles
while ANOTHER bird is in play becomes `landed` — a live body integrated while ANOTHER bird is in play becomes `landed` — a live body integrated
every frame in `stepPhysics` (real gravity, no lifetime timer) that falls every frame in `stepPhysics` (real gravity, no lifetime timer) that falls
when its support breaks, chain-damages boards, and can kill enemies. The LAST when its support breaks, chain-damages boards, and can kill enemies. The LAST

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- physics/step/index.js (submodule: step) /* ISLE SLINGERS -- physics/step/index.js (submodule: step)
* RC-375D84 S-2 modularization (behavior-neutral split of the original * Modularization pass (behavior-neutral split of the original
* js/core.js monolith). Installs its units into ONE shared physics scope `P` * js/core.js monolith). Installs its units into ONE shared physics scope `P`
* that every physics submodule sees -- the same single-scope semantics the * that every physics submodule sees -- the same single-scope semantics the
* monolith had. Must load before js/physics/index.js (the aggregator that * monolith had. Must load before js/physics/index.js (the aggregator that
@ -61,7 +61,7 @@
// gone for good (off the island or aged out) -> spent, next bird loads // gone for good (off the island or aged out) -> spent, next bird loads
P.spendBird(g, b); P.spendBird(g, b);
} else if (b.settleTimer > P.SETTLE_TIME) { } else if (b.settleTimer > P.SETTLE_TIME) {
// S-5 (RC-375D84, Q2): the "another bird is already active" window. // Landed-bird gravity behavior: the "another bird is already active" window.
// A bird that settles while MORE birds are in play (queued or active) // A bird that settles while MORE birds are in play (queued or active)
// is NOT a corpse: it becomes a LANDED body -- real continuous // is NOT a corpse: it becomes a LANDED body -- real continuous
// gravity, no lifetime timer. It falls if its support breaks, // gravity, no lifetime timer. It falls if its support breaks,

View file

@ -1,5 +1,5 @@
# js/physics/topple/ — agents.md # js/physics/topple/ — agents.md
RC-375D84 S-3 (2026-09-29). Module map: `MODULES.md`; runtime model: `architecture.md`. Added in the physics pass (2026-09-29). Module map: `MODULES.md`; runtime model: `architecture.md`.
Installs into `P`: toppleBlocks, blockSupportSpan, beginTopple, integrateTopple, Installs into `P`: toppleBlocks, blockSupportSpan, beginTopple, integrateTopple,
landTopple, topplePathDamage, settleBack, supportStillAlive. landTopple, topplePathDamage, settleBack, supportStillAlive.

View file

@ -1,5 +1,5 @@
/* ISLE SLINGERS -- physics/topple/index.js (submodule: topple) /* ISLE SLINGERS -- physics/topple/index.js (submodule: topple)
* RC-375D84 S-3. Boards tip and topple end-over-end (Commander Q1: FULL * Board tip/topple physics. Boards tip and topple end-over-end (design call: FULL
* CONTINUOUS ROTATION). A resting block whose centre of mass lies OUTSIDE the * CONTINUOUS ROTATION). A resting block whose centre of mass lies OUTSIDE the
* x-span of whatever supports it (ground, or the top face of a block below) * x-span of whatever supports it (ground, or the top face of a block below)
* begins pivoting about the support-edge corner on the side the COM leans to. * begins pivoting about the support-edge corner on the side the COM leans to.

View file

@ -8,7 +8,7 @@ Self-contained module. Exports `ISLE_RENDERER = { Renderer, VIEW_W, VIEW_H }`.
scenery, popups). It never mutates `state`. scenery, popups). It never mutates `state`.
- VIEW_W/VIEW_H (1280x720) mirror physics constants VIEW — keep them in sync; - VIEW_W/VIEW_H (1280x720) mirror physics constants VIEW — keep them in sync;
the camera lives in state (set by physics/camera), render only applies it. the camera lives in state (set by physics/camera), render only applies it.
- S-3/S-5 (RC-375D84, landed): `drawBlock` rotates about the block COM when - S-3/S-5 (added): `drawBlock` rotates about the block COM when
`bl.ang` is set (no-op at `ang=0`, so un-tipped blocks draw identically to `bl.ang` is set (no-op at `ang=0`, so un-tipped blocks draw identically to
the pre-S-3 output), and `drawBird` renders `landed` birds as live bodies the pre-S-3 output), and `drawBird` renders `landed` birds as live bodies
alongside the `spent` corpses. The DOM-stub test only checks that drawing alongside the `spent` corpses. The DOM-stub test only checks that drawing

View file

@ -2,7 +2,7 @@
"name": "isle-slingers", "name": "isle-slingers",
"version": "3.0.0", "version": "3.0.0",
"private": true, "private": true,
"description": "Isle Slingers — an original browser slingshot physics game (RC-0D193E). No dependencies; opens directly in a browser.", "description": "Isle Slingers — an original browser slingshot physics game. No dependencies; opens directly in a browser.",
"scripts": { "scripts": {
"test": "node test/sim.test.mjs && node test/page.test.mjs && node test/balance.test.mjs && node test/topple.test.mjs && node test/knifeedge.test.mjs && node test/fallingkill.test.mjs && node test/landedbird.test.mjs", "test": "node test/sim.test.mjs && node test/page.test.mjs && node test/balance.test.mjs && node test/topple.test.mjs && node test/knifeedge.test.mjs && node test/fallingkill.test.mjs && node test/landedbird.test.mjs",
"start": "python3 -m http.server 8000" "start": "python3 -m http.server 8000"