The body plays the wounds it wears, and the flick finally has a teacher

Score-is-the-body was a picture: the opponent lost an arm and swung exactly as
far, lost its head and missed exactly as often. Two dials now read the same
racketStage the SKINS are picked with, so what you watch it lose is what it
loses — deliberately small (6% reach, 17.5% whiff per stage), texture under the
ladder rather than a second difficulty curve, with a test on that ceiling. An
unhurt body still returns exactly 1 and 1, and the whole thing is gated on
G.match: the gore duel's racket zombie is untouched.

And the placement flick, the mode's one act of skill, was taught nowhere on
touch — the placement ring is a receipt, not a lesson, and you only get one if
you already knew to swipe. The ghost hand teaches it now, in the same register
as PULL DOWN and TAP AS IT ARRIVES, riding the failure it answers: a run of
returns that left with no aim on them. The tap lesson still wins when both
qualify — placement is the wrong thing to teach someone who cannot reach the
ball.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-17 08:21:18 +02:00
1 parent 0c90337f2d
commit c613b6ceba
7 files changed
+147 -21

No files matched your search

+35 -3
View File
@@ -2136,6 +2136,32 @@ function drawHitHint() {
ctx.restore();
}
// ---- ...and the lesson AFTER that one. The tap gets the ball back; the FLICK decides where it
// 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.
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();
}
// The modal: drawn LAST, over everything, and the world behind it is frozen (gameplay.update
// early-returns). Unlike drawCard this is a wall — nothing moves until it is tapped away.
// easeOutBack: overshoots past the target and settles. The arcade snap — nothing in a cabinet
@@ -2703,9 +2729,15 @@ 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 one surviving tennis struggle hint: rides the exact failure it answers
if (G.mode === 'tennis' && G.phase === 'rally' && G.passStreak >= 2 && ball.active && ball.lastHit === 'O')
drawHitHint();
// 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 also runs in the MATCH (G.match plays under splatter's
// permanent interlude, not mode 'tennis') and on touch only: on a keyboard the aim is held keys,
// and 'SWIPE' would be an instruction for 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 && (G.mode === 'tennis' || G.match) && 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
// directly on the crowd. It keeps its 1px jitter; the shadow is what makes it legible anywhere.