From 553b765846d12b037a233dd6e83de16f3e632ff1 Mon Sep 17 00:00:00 2001 From: Oliver Rieger Date: Mon, 17 Aug 2026 08:45:23 +0200 Subject: [PATCH] A gesture is read in a pause, not in a point MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The flick lesson rode the incoming ball, over the player's shoulder — the one moment he has no attention to spare for reading anything. It moves into the room this game already keeps for teaching gestures: the serve stance, standing still, mimed on the apron by the very demo the slingshot lesson plays. Same clock, same hand, same words slot — one teacher, three lessons, and nothing new drawn over a live rally. The words carry both halves now, SWIPE AS YOU HIT TO AIM: WHEN alone teaches nothing, and WHAT FOR alone gets tried at the wrong moment and read as a gesture that does not work. Co-Authored-By: Claude Opus 5 --- HANDOFF-MATCH.md | 9 ++++++-- index.html | 2 +- src/render.js | 55 ++++++++++++++++++++++++------------------------ 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/HANDOFF-MATCH.md b/HANDOFF-MATCH.md index 8d31790..73fa12d 100644 --- a/HANDOFF-MATCH.md +++ b/HANDOFF-MATCH.md @@ -91,8 +91,13 @@ communicating its own rules. Model A only works if the player can see they are b only way in. 6. ~~**Teaching**~~ **DONE** (2026-08-17). The ghost-hand lesson for the PLACEMENT FLICK, in the - same register as PULL DOWN and TAP AS IT ARRIVES: a dotted track out of the player, the hand - riding it, SWIPE AS YOU HIT, mimed both ways in turn so it never teaches one corner. + same register as PULL DOWN and SWIPE TO DODGE — because it is now literally the same demo: the + apron mime, played in the SERVE STANCE after a beat of standing still, mimed both ways in turn so + it never teaches one corner. **A gesture is read in a pause, not in a point** (Oliver: "it is + displayed mid-play.. please rather show it in one of the pauses"): the first cut rode the + incoming ball, over the shoulder, at the moment the player has the least attention to spare. + The words carry both halves — SWIPE AS YOU HIT TO AIM — because WHEN alone teaches nothing and + WHAT FOR alone gets tried at the wrong moment and read as broken. It rides the failure it answers — `G.flatStreak`, returns that left with no aim on them (`flatCount`, reset by the first aimed one) — and only after `TUNE.flickHintAfter` of them, because hitting flat is a legal shot and not a mistake. **One lesson at a time:** the tap hint diff --git a/index.html b/index.html index dbd9ea2..525b60e 100644 --- a/index.html +++ b/index.html @@ -659,6 +659,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') { - + diff --git a/src/render.js b/src/render.js index 95f85c5..f52bfc3 100644 --- a/src/render.js +++ b/src/render.js @@ -2140,26 +2140,22 @@ function drawHitHint() { // goes, and wrong-footing the body across the net is how points are actually won. It was taught // nowhere on touch: the placement ring shows the result of a flick, which is a receipt, not a // lesson — if you never happen to swipe you never get one, so you never learn there was anything -// to learn. Same ghost hand as every other gesture in this game, over the same shoulder, riding -// the same failure it answers (a run of returns hit flat — doHit resets flatStreak the moment one -// leaves with an aim on it). It mimes BOTH ways in turn or it teaches one corner. +// to learn. +// IT IS TAUGHT IN THE PAUSE, NOT IN THE POINT. It first rode the incoming ball, over the player's +// shoulder — which is the moment he has the least attention to spare for reading anything: "it is +// displayed mid-play.. please rather show it in one of the pauses" (Oliver, 2026-08-17). So it +// moved into the room this game already keeps for teaching gestures: standing still in the serve +// stance, on the apron, in the very demo the slingshot lesson plays. Same clock, same mime, same +// words slot — one teacher, three lessons, and nothing new drawn over live play. +// Both ways in turn, or it teaches one corner, and away from whichever edge he is standing against. function drawFlickHint() { - const cyc = 1.4, t = G.idleT % cyc; - const dir = (((G.idleT / cyc) | 0) % 2) ? -1 : 1; - const slide = Math.min(1, t / 0.8), gone = t > 0.95; // stroke, held a beat, lifted, repeat - if (gone) return; - const p0 = proj(P.x, 0, P.z); - const hx = Math.round(p0.x), hy = Math.round(p0.y) - 52; - ctx.save(); ctx.globalAlpha = 0.9; - ctx.fillStyle = UI.dim; // the track the thumb travels - for (let d = 10; d <= 38; d += 7) ctx.fillRect(hx + dir * d - 1, hy, 2, 2); - drawGhostHand(hx + dir * (10 + 28 * slide), hy, 1); // starts clear of his own sprite - ctx.font = F(10); ctx.textAlign = 'center'; ctx.textBaseline = 'top'; - // ...and the words sit ABOVE the stroke and OPPOSITE it: below is the player's own feet and the - // BASELINE, and 10px type across a bright white line is the one place on this court it cannot be - // read. Clamped off the screen edges too — this rides the player and he stands in the corners. - shadowText('SWIPE AS YOU HIT', Math.max(52, Math.min(W - 52, hx - dir * 22)), hy - 26, UI.warm); - ctx.restore(); + const n = ((G.idleHintT - TUNE.pullHintAfter) / 1.9) | 0; + const px = proj(P.x, 0, P.z).x; + // ...and the words carry BOTH halves, because either one alone is useless: WHEN (as you hit — + // a swipe at any other moment does nothing, and a player who tries one and sees nothing concludes + // the gesture is not there) and WHAT FOR (it aims). One line, the same imperative as PULL DOWN + // and TAP AS IT ARRIVES. + demoSwipe(px < W * 0.3 ? 1 : px > W * 0.7 ? -1 : (n % 2 ? -1 : 1), 0, 'SWIPE AS YOU HIT TO AIM'); } // The modal: drawn LAST, over everything, and the world behind it is frozen (gameplay.update @@ -2729,19 +2725,22 @@ export function render() { if (txt) shadowText(txt, W / 2, METER_Y(), UI.warm); // same slot the draw meter uses if (demo) drawPullHint(); // show the gesture instead of only naming it } - // the tennis struggle hints: both ride the exact failure they answer, and only one at a time — - // teaching the flick to someone who cannot get a racket on the ball is the wrong lesson, so the - // tap always wins. - // THE FLICK LESSON BELONGS TO THE RALLY MODES, which is what rallyFeel() already means: CLASSIC - // and ZOMBIE TENNIS. Not the gore mode, and not gore's OPENER either — that tennis is a prologue - // you play for one game before the court turns, and placement is not the skill it is asking for. + // the tennis struggle hints, each riding the exact failure it answers. + // The TAP lesson stays on the incoming ball: it is about a moment, and a moment can only be + // taught while it is happening. The FLICK lesson is about a gesture, and a gesture is read in a + // PAUSE — so it waits for the serve stance and mimes itself on the apron there, exactly like the + // slingshot's. Nothing new is drawn over a live rally. + // It belongs to the RALLY MODES, which is what rallyFeel() already means: CLASSIC and ZOMBIE + // TENNIS. Not the gore mode, and not gore's OPENER either — that tennis is a prologue you play + // for one game before the court turns, and placement is not the skill it is asking for. // (`G.mode === 'tennis'` covered classic AND that prologue, which is exactly the wrong seam; the // match itself is not mode 'tennis' at all, it plays under splatter's permanent interlude.) // Touch only: on a keyboard the aim is held keys and 'SWIPE' would name a gesture that does not // exist there. - const inRally = G.phase === 'rally' && ball.active && ball.lastHit === 'O'; - if (G.mode === 'tennis' && inRally && G.passStreak >= 2) drawHitHint(); - else if (IS_TOUCH && inRally && rallyFeel() && G.flatStreak >= TUNE.flickHintAfter) + if (G.mode === 'tennis' && G.phase === 'rally' && G.passStreak >= 2 && ball.active && ball.lastHit === 'O') + drawHitHint(); + else if (IS_TOUCH && rallyFeel() && G.phase === 'pretoss' && G.idleHintT > TUNE.pullHintAfter + && G.flatStreak >= TUNE.flickHintAfter) drawFlickHint(); // The free-floating register: no panel, one fixed slot, shouted. PERFECT, GAME N, DOUBLE KILL, // RETURNED, CHANGEOVER, weapon labels — everything that used to be plain white 12px text sitting