Make the plane solid, and take the posed scene back out

The cone is fully opaque now: solid colour, deep blue at the edges and near-white
down the core, dimming as it runs away. Depth is carried by BRIGHTNESS instead of
by alpha, so it still reads as receding without anything showing through.

That settles an argument the translucent version could not win. Its opacity and
how well you could tell above-the-plane from below-the-plane were the same dial
pulling opposite ways: readable enough to show the split and it veiled the whole
cone, subtle enough to look right and there was nothing to see. Solid, a body
above the plane is drawn on top of it and a body below is hidden behind it. The
trade is that you cannot see the crowd through it, which is the thing to judge on
the phone.

The `alpha` parameter went with it rather than being left in the signature lying
about what it does.

laserScene is gone. T.pause(true) stays -- it freezes the world but keeps drawing,
which is worth having whenever a still is what you need to look at.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-03 17:21:21 +02:00
1 parent 44419d9ce1
commit 601aa934ad
4 files changed
+43 -81

No files matched your search

+1 -35
View File
@@ -200,43 +200,9 @@ cv.addEventListener('pointerdown', e => {
});
// ---- A POSED SCENE for looking at the laser. `T.laserScene()` in the console, or load the page
// with #laser. It drops you into splatter with a spread of bodies at known depths, the laser armed
// and a pull held, renders it, and PAUSES the world so devtools edits (TUNE.beamY, beamW1, the
// colours in drawBeamWedge) can be judged against a still picture.
function laserScene() {
const intro = document.getElementById('intro');
if (intro) intro.hidden = true;
paused = false;
if (G.mode === 'tennis') forceZombies();
for (let i = 0; i < 600; i++) update(1 / 60); // ride the transition out into splatter
G.modal = null; G.horde = null; G.interlude = false; G.changeover = 0; G.clearing = false;
G.mgT = 0; G.gunId = null; G.beam = null; G.bossFight = 0; G.hp = G.hpMax = TUNE.playerHp;
zombies.length = 0; pellets.length = 0; parts.length = 0; gore.length = 0; piles.length = 0;
score.server = 'P'; setupPoint();
P.x = 0;
TUNE.zMax = 0; // no new spawns wandering into the shot
// one of everything, at four depths, straddling the lane so the cut lands at different heights
for (const [x, z, v] of [[0, 20, 'a'], [0.75, 16, 'b'], [-0.85, 12, 'k'],
[0.85, 8.5, 'g'], [-0.75, 5.5, 'h'], [0, 3, 'e']]) {
spawnZombie(x, z, v);
zombies[zombies.length - 1].v = 0; // hold still: this is a photograph
}
G.rack.length = 0; G.temp = null;
G.weapon = grantWeapon('laser');
G.modal = null;
G.aim = { fx: 0, fy: -1, mag: 100 }; // a full pull straight up-court (fy is NEGATIVE)
update(1 / 60); P.x = 0;
render();
paused = true;
return 'laser scene posed and PAUSED. T.pause(false) to run again, T.render() to redraw by hand.';
}
// a plain timer, not the load event: this module runs as a module, so `load` may already have gone
if (location.hash === '#laser') setTimeout(laserScene, 600);
requestAnimationFrame(frame);
// exposed for in-browser sanity checks and simulated playtests
window.T = { TUNE, ball, P, O, score, G, zombies, laserScene, pause: v => { paused = v !== false; }, spawnZombie, spawnRacketZombie, spawnMachine, spawnChapterBoss, spawnBarrel, grantWeapon, toggleTestMode, piles, gore, parts, pellets, extras, shots, explode, hurtPlayer, killPlayer, splatPoint, solveShot, inServeBox, inCourt, proj, setupPoint, forceZombies, deliverTin, selectWeapon, showModal, dismissModal, startDuel, resolveInterlude, flickTarget, startBossFight, endBossFight,
window.T = { TUNE, ball, P, O, score, G, zombies, pause: v => { paused = v !== false; }, spawnZombie, spawnRacketZombie, spawnMachine, spawnChapterBoss, spawnBarrel, grantWeapon, toggleTestMode, piles, gore, parts, pellets, extras, shots, explode, hurtPlayer, killPlayer, splatPoint, solveShot, inServeBox, inCourt, proj, setupPoint, forceZombies, deliverTin, selectWeapon, showModal, dismissModal, startDuel, resolveInterlude, flickTarget, startBossFight, endBossFight,
step: dt => update(dt), render, press: keyPressed,
get phase() { return G.phase; }, set phase(v) { G.phase = v; } };