The lob is the flick you already have, and a game stops being a death
Three from Oliver. THE LOB. He picked option (c) from the written comparison — "no additional gesture to learn" — and corrected the premise: the zombie does come to the net (racketAI chases to the far service line, the classic CPU to z 9.5). So the forward flick keeps its one meaning, "put it deep behind him", and the game picks the trajectory that actually does that: a smash while he is back, over his head once he is in. oppAtNet() is the whole switch, and it reads the BODY, not O — in the match O is the invisible tennis body and only moves between points, so it would have answered "on his mark" all rally. lobArm 7.0 m is measured, not taste: a lob peaking at lobApex passes a man 7 m from the net at almost exactly CONTACT.maxY, so the shot arms exactly as far out as it can still clear him. The pace is derived from the height, so the same arc comes off the baseline and off mid-court, and SHOT_DEPTH.lob is deeper than deep — at the ordinary deep target the ball passed 2.05 m over him at the edge of the band, which is inside his reach. PACE, SEEN FROM THE STRIKER. "the opponent sees the ball really fast - the player barely sees the speed increase." Measured: the ball is the same ball on both phones, the camera is not — it compresses your own ball into the distance and expands theirs as it arrives. Pace untouched; three channels added on the striker's side: the ghost streak now reads pace (3-8 ghosts, was 3 at every speed), the crack is pitched by it, and a fast ball kicks the camera. The serve's own ladder is 49.2 plain / 50.8-52.4 hard, because serveArc's loft margin takes 10% of the +9 back — widening that is his call, and PROJECT.md records the knob and the risk. THE DEATH MATCH. "each player has 8 blood points but the player explodes for the winner already at 0:40." Two clocks: a game is four points, a life is eight. A won game is only a game now (flash, serve, heal); the burst moved to the death itself, on the dead message, where both phones agree. The summary waits 1.4 s for the gore, because update() freezes the world on G.done and would have hung a still frame of an explosion behind the trophy screen. And bleedO tracks THEIR blood off the pos stream instead of our points-this-game, which reset every game and ignored the heal. 4 new tests, 113 pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6e07dc74bb
commit
4f4534e272
9 files changed
+355
-38
No files matched your search
+19
-10
@@ -2567,23 +2567,32 @@ export function render() {
|
||||
const by = Math.abs(Math.sin(G.idleT * BOUNCE_IDLE.tempo)) * (mine ? bounceApex() : (oTour().oApex ?? BOUNCE_IDLE.oApex));
|
||||
drawBall(bx, by, sv.z);
|
||||
}
|
||||
// hot-ball ghost streak: a fast rally ball drags three fading ghosts behind it. It gated on
|
||||
// pow > 0.8 alone, which in practice only the gore slingshot ever reached — tennis tap swings
|
||||
// live below it, so both tennis modes played without the streak (Oliver's sidequest). PACE now
|
||||
// qualifies a ball too: a flat serve or a clean winner streaks in every mode, a lob stays clean.
|
||||
// hot-ball ghost streak: a fast ball drags fading ghosts behind it. It gated on pow > 0.8 alone,
|
||||
// which in practice only the gore slingshot ever reached — tennis tap swings live below it, so
|
||||
// both tennis modes played without the streak (Oliver's sidequest). PACE now qualifies a ball
|
||||
// too: a flat serve or a clean winner streaks in every mode, a lob stays clean.
|
||||
// ...AND THE STREAK IS HOW LONG THE BALL IS FAST (Oliver, 2026-08-22, after the multiplayer test:
|
||||
// "when smashing the opponent sees the ball really fast — the player barely sees the speed
|
||||
// increase"). It was three ghosts at every pace, so it said "fast/not fast" and nothing else —
|
||||
// while the CAMERA works against the striker, compressing his own ball into the distance as the
|
||||
// same shot expands toward the man receiving it. Length reads as pace on both phones now, and it
|
||||
// is the striker's only cue that the ball he just hit is quick. Sampled every frame, so a fast
|
||||
// ball's ghosts are ALSO further apart: pace shows as reach and as spacing at once.
|
||||
const bspd = Math.hypot(ball.vx, ball.vz);
|
||||
const hot = ball.active && G.phase === 'rally' && !ball.collected
|
||||
&& ((ball.pow || 0) > 0.8 || Math.hypot(ball.vx, ball.vz) > TUNE.trailSpeed);
|
||||
if (hot) { ballHist.push({ x: ball.x, y: ball.y, z: ball.z }); if (ballHist.length > 7) ballHist.shift(); }
|
||||
&& ((ball.pow || 0) > 0.8 || bspd > TUNE.trailSpeed);
|
||||
const ghosts = Math.max(3, Math.min(8, Math.round((bspd - TUNE.trailSpeed) / 6)));
|
||||
if (hot) { ballHist.push({ x: ball.x, y: ball.y, z: ball.z }); if (ballHist.length > 10) ballHist.shift(); }
|
||||
else if (ballHist.length) ballHist.length = 0;
|
||||
if (hot) {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const h = ballHist[ballHist.length - 2 - i * 2]; if (!h) break;
|
||||
for (let i = 0; i < ghosts; i++) {
|
||||
const h = ballHist[ballHist.length - 2 - i]; if (!h) break;
|
||||
const p = proj(h.x, h.y, h.z);
|
||||
// 0.26 was tuned against the gore mode's dark rot palette; on the bright tennis lawn it
|
||||
// vanished entirely — "i see no ball trails" (Oliver, in the tournament)
|
||||
ctx.globalAlpha = 0.38 - i * 0.11;
|
||||
ctx.globalAlpha = 0.4 * (1 - i / ghosts);
|
||||
ctx.fillStyle = UI.ball;
|
||||
ctx.beginPath(); ctx.arc(p.x, p.y, Math.max(1.5, p.s * 0.09 * (0.9 - i * 0.18)), 0, 7); ctx.fill();
|
||||
ctx.beginPath(); ctx.arc(p.x, p.y, Math.max(1.5, p.s * 0.09 * (0.95 - 0.6 * i / ghosts)), 0, 7); ctx.fill();
|
||||
}
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user