Splatter: re-toss mid-rally (keep the two-stage serve), leaner blood
Serve: - Reverts the single-tap auto-serve. Firing a ball still needs the two-stage toss+fire gesture — but a tap mid-rally now RE-ARMS the toss (throws a new ball up) with the previous ball flying on as a ballistic clone, so you can toss the next serve without waiting for the last to leave play. Blood: - Wound bleed cut further: sparse (most frames emit nothing), thinner spread, lower rise, short-lived, capped low — a fine trickle, not a spray. - Repainted z_gw / z_cw1 with clean stumps and almost no painted blood (the torso gash and shirt spatter are gone), so the dynamic trickle is the blood. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
No files matched your search
|
Before Width: | Height: | Size: 248 KiB After Width: | Height: | Size: 266 KiB |
|
Before Width: | Height: | Size: 299 KiB After Width: | Height: | Size: 829 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
@@ -567,15 +567,19 @@ export function keyPressed(code) {
|
||||
if (G.interlude && G.interlude.introT > 0) return; // let the umpire finish announcing the duel
|
||||
// splatter: a tap/flick during the brief 'over' beat re-arms the next serve immediately
|
||||
if (G.mode === 'splatter' && G.phase === 'over') { ball.active = false; setupPoint(); }
|
||||
// splatter rapid serve: a flick fires an aimed quick serve — in pretoss OR mid-rally (dragServe
|
||||
// banishes any live ball so the next launches now). The duel is exempt: it serves like tennis.
|
||||
// splatter serve stays a TWO-STAGE ritual — toss up, THEN fire — but the toss can start any
|
||||
// time, even with a ball still in flight: the old ball flies on as a ballistic clone so the
|
||||
// toss+fire pair chains without waiting for it to leave. A flick is the aimed one-motion
|
||||
// variant (the reticle). The duel is exempt: it serves like tennis.
|
||||
if (G.mode === 'splatter' && !G.interlude && score.server === 'P'
|
||||
&& (G.phase === 'pretoss' || G.phase === 'rally')) {
|
||||
const flick = takeFlickAim();
|
||||
if (flick) { dragServe(flick); return; } // dragServe handles GUN (burst) and the live-ball banish
|
||||
// a bare tap MID-RALLY serves at the auto-target too — pump the screen to keep the balls
|
||||
// coming while the last one's still up. (pretoss keeps its toss ritual, handled below.)
|
||||
if (G.phase === 'rally') { dragServe(null); return; }
|
||||
if (G.phase === 'rally') { // a tap mid-rally re-arms the TOSS (stage one); the next tap fires
|
||||
if (tryGunBurst()) return;
|
||||
if (ball.active) 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 });
|
||||
tossBall(P); return;
|
||||
}
|
||||
}
|
||||
if (G.phase === 'pretoss' && score.server === 'P') {
|
||||
if (tryGunBurst()) return; // GUN selected: the serve gesture pops a burst, no ball
|
||||
|
||||
@@ -155,22 +155,23 @@ export function explode(x, z) {
|
||||
if (G.idleT - G.lastOrderT > 3) { voice('order', 0.35); G.lastOrderT = G.idleT; } // the umpire loses composure
|
||||
}
|
||||
|
||||
// wounded zombies bleed from every open wound — a thin, steady trickle (a spurt from the
|
||||
// neck stump on the headless boss). Anchors are approximate body-height offsets [x, y, rise,
|
||||
// count] (drawStatic never mirrors, so a fixed side stays put); the parts loop's gravity arcs
|
||||
// each drop. Tight spread keeps the streams thin; bounded so it stays subtle.
|
||||
// wounded zombies bleed from every open wound — a fine, sparse trickle (a small spurt from the
|
||||
// neck stump on the headless boss). Anchors are approximate body offsets [x, y, rise]; most
|
||||
// frames a wound drips nothing, and the tight spread keeps each thread thin. Bounded low.
|
||||
function bleed(zb) {
|
||||
if (parts.length > 220) return;
|
||||
if (parts.length > 90) return;
|
||||
let wounds;
|
||||
if (zb.variant === 'g' && zb.hp < TUNE.bruteHp) wounds = [[-0.3, 1.95, 1.8, 1]]; // brute: torn-off arm
|
||||
else if (zb.variant === 'r' && zb.hp <= 1) wounds = [[0.05, 1.5, 3.6, 1], [0.4, 1.15, 1.4, 1]]; // headless: neck spurt + torn arm
|
||||
else if (zb.variant === 'r' && zb.hp === 2) wounds = [[0.4, 1.2, 1.6, 1]]; // racket: torn-off arm
|
||||
if (zb.variant === 'g' && zb.hp < TUNE.bruteHp) wounds = [[-0.3, 1.95, 1.1]]; // brute: torn-off arm
|
||||
else if (zb.variant === 'r' && zb.hp <= 1) wounds = [[0.05, 1.5, 2.4], [0.4, 1.15, 0.9]]; // headless: neck spurt + torn arm
|
||||
else if (zb.variant === 'r' && zb.hp === 2) wounds = [[0.4, 1.2, 1.0]]; // racket: torn-off arm
|
||||
else return;
|
||||
for (const w of wounds)
|
||||
for (let k = 0; k < w[3]; k++) parts.push({
|
||||
x: zb.x + w[0] + (Math.random() * 2 - 1) * 0.05, y: w[1] + (Math.random() * 2 - 1) * 0.04, z: zb.z + (Math.random() * 2 - 1) * 0.05,
|
||||
vx: (Math.random() * 2 - 1) * 0.6, vy: w[2] * (0.6 + Math.random() * 0.6), vz: (Math.random() * 2 - 1) * 0.5,
|
||||
t: 0.4 + Math.random() * 0.35, dark: Math.random() < 0.5 });
|
||||
for (const w of wounds) {
|
||||
if (Math.random() > 0.4) continue; // sparse: most frames this wound emits nothing
|
||||
parts.push({
|
||||
x: zb.x + w[0] + (Math.random() * 2 - 1) * 0.03, y: w[1] + (Math.random() * 2 - 1) * 0.03, z: zb.z + (Math.random() * 2 - 1) * 0.03,
|
||||
vx: (Math.random() * 2 - 1) * 0.3, vy: w[2] * (0.6 + Math.random() * 0.5), vz: (Math.random() * 2 - 1) * 0.28,
|
||||
t: 0.3 + Math.random() * 0.3, dark: Math.random() < 0.55 });
|
||||
}
|
||||
}
|
||||
|
||||
export function updateZombies(dt) {
|
||||
|
||||