Two-stage serve aim: hold + drag shows the reticle, release fires
Flick-to-aim was aim-by-faith — you learned where you aimed after the ball was gone. Now, at a serveable pretoss, touching and dragging shows a LIVE reticle (same mapping: direction + length = landing point); release serves there. The magnet is finally visible: within zSnap the ring turns red and rides the locked zombie (plus a marker over its head) — you see the lock BEFORE you commit the tin. - Context locked at touchstart: a movement drag started mid-rally never morphs into a serve when the ball dies (sub-300ms releases still count as the rapid flick re-serve — the Phase 1 cadence survives) - Drag back home = cancel: no serve, the tin stays in stock - Aimed (drag) serves commit specials at fixed 0.62 quality; the tap-toss ritual keeps the PERFECT-timing power ceiling and the stock — placement vs power is now a real choice - Rally / horde / machine gun untouched: drag steers, flick swings - Serve prompt: 'TAP TO SERVE - HOLD + DRAG TO AIM' / 'AIMED SERVES FIRE THE BOMB - TAP KEEPS IT' Verified with synthetic touch sequences: drag arms the reticle without serving and it tracks the thumb; release serves at the marker with the bomb committed; cancel keeps the tin; rally-started drags never morph; locked reticle renders red on the zombie (screenshot); tennis 12/12; no console errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2f1419b1f2
commit
8cfe9fdbcd
4 files changed
+46
-9
No files matched your search
@@ -4,7 +4,8 @@
|
||||
|
||||
import { cv } from './dom.js';
|
||||
import { audio } from './audio.js';
|
||||
import { keyPressed } from './gameplay.js';
|
||||
import { G, score } from './state.js';
|
||||
import { keyPressed, dragServe } from './gameplay.js';
|
||||
import { togglePanel } from './panel.js';
|
||||
|
||||
const keys = {};
|
||||
@@ -36,10 +37,17 @@ export function takeFlickAim(maxAgeMs = 450) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// the two-stage serve: a drag that STARTS at a serveable pretoss aims a visible reticle
|
||||
// (G.aim, drawn by render); release fires the serve there, dragging back home cancels.
|
||||
const canAimServe = () =>
|
||||
G.mode === 'splatter' && G.phase === 'pretoss' && score.server === 'P'
|
||||
&& !G.horde && !(G.mgT > 0) && !G.changeover;
|
||||
|
||||
cv.addEventListener('touchstart', e => {
|
||||
e.preventDefault(); audio();
|
||||
for (const t of e.changedTouches) {
|
||||
if (!stick) stick = { id: t.identifier, x0: t.clientX, y0: t.clientY, t0: performance.now(), moved: false };
|
||||
if (!stick) stick = { id: t.identifier, x0: t.clientX, y0: t.clientY, t0: performance.now(),
|
||||
moved: false, aimable: canAimServe() }; // context locked at touch start — a movement drag never morphs into a serve
|
||||
else keyPressed('Space');
|
||||
}
|
||||
}, { passive: false });
|
||||
@@ -49,8 +57,12 @@ cv.addEventListener('touchmove', e => {
|
||||
if (!stick) return;
|
||||
for (const t of e.changedTouches) {
|
||||
if (t.identifier !== stick.id) continue;
|
||||
const dx = t.clientX - stick.x0, dy = t.clientY - stick.y0;
|
||||
if (Math.hypot(dx, dy) > DEAD) stick.moved = true;
|
||||
const dx = t.clientX - stick.x0, dy = t.clientY - stick.y0, dist = Math.hypot(dx, dy);
|
||||
if (dist > DEAD) stick.moved = true;
|
||||
if (stick.aimable) { // aiming, not steering: the reticle tracks the thumb
|
||||
G.aim = dist > DEAD ? { fx: dx / dist, fy: dy / dist, mag: dist } : null;
|
||||
continue;
|
||||
}
|
||||
touchAxis.x = Math.abs(dx) > DEAD ? Math.sign(dx) : 0;
|
||||
touchAxis.z = Math.abs(dy) > DEAD ? Math.sign(dy) : 0;
|
||||
}
|
||||
@@ -62,7 +74,12 @@ const endTouch = e => {
|
||||
if (!stick || t.identifier !== stick.id) continue;
|
||||
const dt = performance.now() - stick.t0;
|
||||
const dx = t.clientX - stick.x0, dy = t.clientY - stick.y0, dist = Math.hypot(dx, dy);
|
||||
if (!stick.moved && dt < 250) keyPressed('Space'); // tap
|
||||
if (stick.aimable && stick.moved) { // two-stage release
|
||||
const aim = G.aim; G.aim = null;
|
||||
if (aim && dist > DEAD * 1.5) dragServe(aim); // on target — serve
|
||||
// else: dragged back home — cancel, the tin stays in stock
|
||||
}
|
||||
else if (!stick.moved && dt < 250) keyPressed('Space'); // tap
|
||||
else if (dt < 300 && dist > 28) { // flick = aimed swing
|
||||
flickAim = { ax: Math.abs(dx) > dist * 0.38 ? Math.sign(dx) : 0,
|
||||
az: Math.abs(dy) > dist * 0.38 ? Math.sign(dy) : 0,
|
||||
|
||||
Reference in new issue
Block a user