Hide the racket, and light the beam all the way to its edge

The mg racket is gone, not moved. It was a procedural racket drawn over a sprite
that already holds one: straight up-court it projected onto the player's back as
a floating oval, and offset to the side it just stuck out.

The laser's far edge was pinned to the middle of the ad strip two commits ago and
nothing appeared to change, twice. The geometry was always right -- each wedge
layer faded to alpha 0 along its length, so the beam went invisible before it
reached its own far corners and the VISIBLE end sat well short of the pinned one.
The far stop is lit now, so the two are the same line. Verified by recomputing
the projection: far corners at y 113.8, the ads' middle, against 110.2 unpinned.

It also falls off with distance now, brightest at the strings and thinning down
the court -- but never to zero, which is the trap above.

Fixes a third instance of the empties-under-the-iterator bug: a rocket blast can
bank the last kill of a game, and the duel or chapter boss that follows clears
pellets[] mid-loop. shots, pellets, extras and the body loop all carry the guard.

Also records that two consecutive render() calls do not produce the same frame --
render mutates camera smoothing as a side effect -- so frame-diffing to find where
something was drawn measures the camera drift instead. It claimed the beam reached
16 px above where its own geometry could go.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-03 15:20:33 +02:00
1 parent 6e88fd5bb9
commit da9cca2f23
4 files changed
+48 -24

No files matched your search

+28 -1
View File
@@ -385,10 +385,31 @@ them missed would hand out free ammo. The soak asserts `gunId` is never set with
`mx || mz || assist`, and finger-steering zeroes `mx`/`mz` — so a player sliding left and right under
auto-fire still counted as standing still and never ran. `mgMoving` is in that expression now.
**The oval on his back was the mg racket.** `drawMgRacket` put its head at `(P.x, 0.95, P.z - 1.35)`,
**The mg racket is GONE.** It was a procedural racket drawn over a sprite that already holds one:
straight up-court it projected onto the player's back as a floating oval, and offset to the side it
just stuck out. Oliver: "can we please just hide it?" The pellets streaming up-court are the
feedback. `drawMgRacket` is deleted, not commented out.
**(Previously) the oval on his back was the mg racket.** `drawMgRacket` put its head at `(P.x, 0.95, P.z - 1.35)`,
which at this camera projects almost exactly onto the player sprite. Offset by `P.facing` it reads as
a racket held out beside him.
**A third array had the empties-under-the-iterator bug.** A rocket blast can bank the last kill of a
game, and the beat that follows (a duel, a chapter boss) does `pellets.length = 0` — so the pellet
loop's next index read undefined and the frame threw. `shots`, `pellets`, `extras` and the body loop
all carry the same `if (!x) continue` guard now. **Any array iterated in `updateZombies` that can
contain something whose death advances the game needs it.**
**A gradient that ends at alpha 0 hides the thing you are aligning.** The laser's far corners were
pinned correctly from the first attempt, but each wedge layer faded to fully transparent along its
length — so the beam became invisible before it reached the pinned edge, the VISIBLE end sat well
short of it, and moving the geometry appeared to do nothing twice in a row. The far stop is lit
(0.62 of the layer's alpha) now, so the visible edge and the pinned edge are the same line.
**The beam falls off with distance** — brightest at the strings, thinning down the court, because
that is what a beam travelling away from you does. The far stop is 0.32 of the layer's alpha: enough
depth to read, never zero, so the visible end stays the pinned end.
**The laser's far edge is pinned in SCREEN space.** Running out to `beamLen` on the ground landed it
on the ads' TOP line; it should die at their middle. `c3.y = c4.y = proj(0,0,-6).y - AD_H / 2` — the
x's keep their perspective, the y is pinned. That holds in both orientations and leaves `beamLen`
@@ -420,6 +441,12 @@ Drive the game deterministically via `window.T` with `javascript_tool`. The Brow
context reports `document.visibilityState === 'hidden'`, so RAF is paused; halt it anyway with
`window.requestAnimationFrame = () => 0` and step with `T.step(1/60)`. `T.press('Space')` = tap.
Clear the intro first: `document.getElementById('intro')?.remove()`.
- **Two consecutive `render()` calls do NOT produce the same frame**, so "draw it, change one thing,
draw again, diff the pixels" silently measures nothing. `render()` mutates camera state as a side
effect (`aimCam += ((G.aim ? 1 : 0) - aimCam) * 0.15`), so the whole scene shifts slightly between
grabs and the diff lights up everywhere — it reported a laser beam extending 16 px above where its
own geometry could possibly reach. To check WHERE something lands, recompute the projection
directly (`T.proj(...)`) and compare numbers; keep pixel-diffing for "did anything at all change".
- **The module-refresh list MUST include the VERSIONED entry point.** `index.html` loads
`main.js?v=N`, and `fetch('main.js', {cache:'reload'})` does not bust that URL — so main.js alone
stays stale through a reload while every other module is fresh. Read the src off the page: