Splatter: Angry-Birds slingshot serve — pull back to toss, dotted-line aim, release fires

The touch serve drag now inverts: pull toward yourself to launch up-court.
The first pull past the deadzone starts the real toss animation (serveToss),
dragServe fires straight from the toss phase without banishing its own fresh
toss, and a held aim keeps the toss airborne instead of dropping. Render shows
an Angry-Birds dotted trajectory racket->target (red = zombie locked). Two-stage
tap serve, mid-rally re-toss/banish, and keyboard serve+arrow-aim unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 4.8 committed 2026-07-17 22:56:22 +02:00
1 parent 6df0ddb00f
commit 013fe8152d
3 files changed
+31 -16

No files matched your search

+10 -4
View File
@@ -570,14 +570,20 @@ function tryGunBurst() {
return true;
}
// release of the two-stage aim: an instant serve to the reticle (quality fixed — placement
// slingshot serve, stage one: the pull-back starts the toss animation. The ball rises while
// you aim (dotted line, render.js); the release (dragServe) fires it. Only from a fresh pretoss.
export function serveToss() {
if (score.server === 'P' && G.phase === 'pretoss' && !ball.active) tossBall(P);
}
// release of the slingshot aim: an instant serve to the reticle (quality fixed — placement
// is this serve's power; the toss ritual keeps the PERFECT-timing power ceiling)
export function dragServe(aim) {
if (G.mode !== 'splatter' || score.server !== 'P') return;
if (G.interlude || G.horde || G.mgT > 0 || G.changeover) return; // the duel serves like tennis, not the reticle
if (G.phase !== 'pretoss' && G.phase !== 'rally') return; // rally too: fire again while a ball's still up
if (G.phase !== 'pretoss' && G.phase !== 'rally' && G.phase !== 'toss') return; // toss = our own slingshot wind-up
if (tryGunBurst()) return;
if (ball.active) // rapid fire: the ball in flight keeps going as a ballistic clone so the next serve can launch now
if (ball.active && G.phase !== 'toss') // rapid fire: banish the live rally ball as a ballistic clone — but NOT our fresh toss
extras.push({ x: ball.x, y: ball.y, z: ball.z, vx: ball.vx, vy: ball.vy, vz: ball.vz, pow: ball.pow || 0.5, t: 2.5 });
ball.active = true; ball.collected = false; ball.bounces = 0; ball.firstLand = null;
ball.x = P.x + 0.35; ball.z = P.z; ball.y = 2.2; ball.g = TUNE.gravity;
@@ -819,7 +825,7 @@ export function update(dt) {
((ball.z < -2 && ball.vz < 0) || (ball.z > 25.5 && ball.vz > 0) || Math.abs(ball.x) > 7.5)))) {
spentBall();
}
if (G.phase === 'toss' && ball.y <= 1.4 && ball.vy < 0) { // uncaught toss → retoss
if (G.phase === 'toss' && ball.y <= 1.4 && ball.vy < 0 && !G.aim) { // uncaught toss → retoss (a held slingshot aim keeps the toss up)
ball.active = false; G.phase = 'pretoss';
if (score.server === 'O') G.aiServeT = 0.8;
}