Phase 6: racket zombies (rally mini-boss)
Survive a horde and a racket zombie takes up residence on the far court (variant 'r', one per horde survived) — the tennis rally skill lives on inside zombie mode. It reuses the turned-CPU z_c sprite (already holds a racket) and a slimmed copy of the tennis AI: holds station deep, chases a ball laterally at TUNE.racketSpeed (2.8), and returns your SOFT shots (pow < zSplatQ) as live lastHit:'O' balls with quality capped at racketQMax (0.55) and racketFlub (0.2) whiffs. trySwing gained an optional quality-cap arg (defaulted, so tennis is untouched). It's a mini-boss with TUNE.racketHp (3): only splat-quality balls hurt it (-1 hp, staggers the ball; the killing blow runs the full splatter). Soft balls pass through for it to rally; melee and pellets don't touch it. Its return that gets past your baseline is a point for THEM; you can rally it back with the existing player hit. A red HP bar floats over its head. Verified via window.T: endHorde spawns exactly one (hp 3, z 3.5); three splat hits kill it (2→1→dead); a pow-0.3 ball is returned toward you; an 'O' ball past your baseline scores for THEM; melee spares the boss but cleaves the shambler beside it; tennis still 12/11/12 and a normal shambler is still one-shot; renders with its HP bar; no console errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
bfaafe6727
commit
41a6d2a034
4 files changed
+62
-6
No files matched your search
+16
-2
@@ -78,8 +78,13 @@ function firePellet() {
|
||||
pellets.push({ x: P.x, y: 0.9, z: P.z, vx: Math.sin(a) * s, vz: -Math.cos(a) * s, t: 1.2 });
|
||||
pock(0.25, 1400); // rapid fire tick; if it clips on device, gate to every 2nd pellet
|
||||
}
|
||||
function spawnRacketZombie() { // one takes up residence on the far court each horde you survive
|
||||
zombies.push({ x: (Math.random() * 2 - 1) * 2.5, z: 3.5, v: 0, walkT: Math.random() * 9,
|
||||
variant: 'r', side: 'O', facing: 1, pose: 'idle', poseT: 0, swingT: -1, swingCool: 0, runT: 0, hp: TUNE.racketHp });
|
||||
}
|
||||
function endHorde() {
|
||||
G.horde = null; pellets.length = 0;
|
||||
spawnRacketZombie(); // survive the wave → a racket zombie stays to rally you
|
||||
setupPoint(); // serve loop resumes; any zombies still standing are yours to mop up
|
||||
voice('quiet_dark', 0.5, 0.85);
|
||||
}
|
||||
@@ -127,10 +132,11 @@ export function updateZombies(dt) {
|
||||
const zb = zombies[i];
|
||||
zb.walkT += dt;
|
||||
zb.z += zb.v * dt;
|
||||
if (G.mode === 'splatter' && zb.variant !== 'r' && zb.z > COURT.netZ) // on YOUR half they home in on you
|
||||
if (zb.variant === 'r') { /* the racket boss holds station — its AI owns its x */ }
|
||||
else if (G.mode === 'splatter' && zb.z > COURT.netZ) // on YOUR half they home in on you
|
||||
zb.x += Math.sign(P.x - zb.x) * Math.min(Math.abs(P.x - zb.x), zb.v * 0.7 * dt);
|
||||
else
|
||||
zb.x += Math.sin(zb.walkT * 1.7) * 0.15 * dt; // far half / racket boss: aimless shamble
|
||||
zb.x += Math.sin(zb.walkT * 1.7) * 0.15 * dt; // far half: aimless shamble
|
||||
if (G.mode === 'splatter' && Math.hypot(zb.x - P.x, zb.z - P.z) < 0.7) { // it got you
|
||||
gore.push({ x: P.x, z: P.z, r: 0.9, a: 0.9 });
|
||||
for (let k = 0; k < 30; k++) parts.push({
|
||||
@@ -150,6 +156,14 @@ export function updateZombies(dt) {
|
||||
explode(ball.x, ball.z); ball.active = false; ball.bomb = false;
|
||||
G.phase = 'over'; G.overT = TUNE.splatterOverT; break; // zombies mutated — stop this frame's loop
|
||||
}
|
||||
if (zb.variant === 'r') { // mini-boss: splat-quality wounds it; soft balls it rallies (racketAI)
|
||||
if ((ball.pow || 0) >= TUNE.zSplatQ) {
|
||||
zb.hp--; splat(0.5); pock(0.6, 300);
|
||||
ball.vx *= 0.3; ball.vz *= 0.3; ball.lastHit = 'X'; // spent on the hit
|
||||
if (zb.hp <= 0) { splatter(i); continue; } // killing blow: full gore + point
|
||||
}
|
||||
continue; // soft balls pass through, for the AI to return
|
||||
}
|
||||
if ((ball.pow || 0) >= TUNE.zSplatQ) { splatter(i); continue; }
|
||||
zb.z -= TUNE.zPushback; // weak sitters only stagger them
|
||||
pock(0.6, 300);
|
||||
|
||||
Reference in new issue
Block a user