Shape the cone by hand, and give the playground its knobs back

Oliver's tuned numbers become the defaults: beamY 0.95, beamLen 23, beamW0 0.08,
beamW1 3.1, beamOriginLift 0.44, beamBorder 0.18.

Three knobs earned their keep on the way there.

beamLen is the DRAWN length now, not only the kill range. The drawn length was
solved every frame so the far end landed on the middle of the ad strip, which
made this a control that visibly did nothing. One meaning beats one clever
behaviour -- and it pairs with beamY, since a lower plane meets the ads sooner.

beamOriginLift replaces beamOriginY: the apex is a lift ABOVE the plane rather
than an absolute height, so lowering beamY brings the near end down with it
instead of leaving it hanging at chest height with the beam sloping away.

beamBorder exposes the edge width, which was hardcoded.

And the playground is back, deliberately NOT paused. The first version froze the
world so the picture would hold still, and then nothing redrew, so every value
changed did nothing -- a still frame you cannot change is worth nothing. The
bodies are held still individually and spawning is off instead.

Also: a boolean living in TUNE has to be tested with >= 0.5. The panel steps
every key by 0.02, so one nudge set beamBand to 0.02 -- still truthy -- and the
renderer switched variants under him mid-tune.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-03 17:58:51 +02:00
1 parent ac1d5fbccd
commit 99774ab4be
6 files changed
+125 -62

No files matched your search

+40 -1
View File
@@ -200,9 +200,48 @@ cv.addEventListener('pointerdown', e => {
});
// ---- A POSED SCENE for tuning a shot: `T.laserScene()`, or load with #laser. Six bodies of
// different types at known depths, the laser armed, a pull held.
// IT DOES NOT PAUSE, deliberately. The first version froze the world so the picture would hold
// still — and then every TUNE value you changed did nothing visible, because nothing redrew. The
// bodies are held still individually (v = 0) and spawning is off instead, so the picture is stable
// while the render loop keeps running and an edit shows on the very next frame.
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; // nothing new wanders into the shot
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; // held still: the scene 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)
return 'posed and LIVE — change TUNE (T panel or console) and it redraws. T.laserFire() to shoot it.';
}
// fire the posed shot, then pose it again so you can keep tuning
function laserFire() {
G.aim = null; score.server = 'P'; setupPoint();
keyPressed('Space');
return 'fired — T.laserScene() to re-pose';
}
// 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, 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, laserScene, laserFire, 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; } };