Splatter feel pass: melee, homing, multi-ball, clearer MG mode
From Oliver's feedback round: - Racket melee: any swish (tap) in splatter cleaves shamblers within TUNE.meleeRange (1.7m) — a close-range survival tool. Works during a horde too (tap = swish only, still no serve). The racket-zombie boss (Phase 6, not built) is immune by design. - Zombies home in on you: on YOUR half (z > netZ) they now drift toward P.x instead of walking a straight lane; far half stays an aimless shamble. - Multi-ball / rapid fire: splatter is now a shooter — every tap/flick fires immediately (dropped the toss ritual there), and serving while a ball still flies stashes the old one into a new extraBalls array so it keeps arcing and killing (cap 6). Refactored the kill into a shared killZombie() with no ball coupling, used by ball hits, pellets, melee, and extra balls. - Machine-gun mode clarity: confirmed free-walk (drag/arrows move you, auto-assist off) and tap-doesn't-serve during a horde; added a code-drawn "rambo" racket held forward with a muzzle flash over the player, and a splatter-specific serve prompt. REVIEW on your phone — two opinionated calls, both easy to revert: 1. Dropped the toss in splatter (tap = instant quick-serve, pow 0.62). Rapid fire, but no apex-timing quality/pierce. Want the toss back as an option? 2. MG posture is a code-drawn forward racket, not a real body sprite — good enough, or worth a proper p_mg sprite later? Also: each rapid serve still auto-consumes a stocked bomb, so bombs deplete fast now — flag if that feels wrong. Verified via window.T: melee kills a 1.0m zombie and spares a 4.5m one; a near-half zombie drifts x 3→1.9 toward you; firing twice leaves 1 stashed ball + a fresh serve (barrage screenshot); extra balls kill and pierce; free-walk during a horde (x 0→2.7); horde-tap melees without serving; tennis toss-serve and Phase-4 bomb consume still work; no console errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
bf9553bd49
commit
9100bd5a23
5 files changed
+89
-28
No files matched your search
@@ -4,7 +4,7 @@ import { cv, ctx } from './dom.js';
|
||||
import { TUNE, COURT, PLAYER_H, BOY_H, UMP_H, BOUNCE_IDLE, SPRITE_H } from './config.js';
|
||||
import { P, O, ball, score, crowd, G } from './state.js';
|
||||
import { predictLanding } from './gameplay.js';
|
||||
import { zombies, gore, piles, parts, pellets } from './zombies.js';
|
||||
import { zombies, gore, piles, parts, pellets, extraBalls } from './zombies.js';
|
||||
|
||||
// ---------------------------------------------------------------- palette ----
|
||||
// tennis palette -> drained splatter palette; the transition lerps between them
|
||||
@@ -211,6 +211,17 @@ function drawFallbackChar(ch, p, h) { // shown only until sprites load
|
||||
ctx.fillStyle = shorts; ctx.fillRect(x - 2.4 * u, y - 6 * u, 4.8 * u, 3 * u);
|
||||
ctx.fillStyle = skin; ctx.fillRect(x - 1.8 * u, y - 3 * u, 1.4 * u, 3 * u); ctx.fillRect(x + 0.4 * u, y - 3 * u, 1.4 * u, 3 * u);
|
||||
}
|
||||
function drawMgRacket() { // horde: the player braces the racket forward and up-court, machine-gun style
|
||||
const hip = proj(P.x + 0.15, 1.05, P.z);
|
||||
const tip = proj(P.x, 0.95, P.z - 1.35); // pointing up-court, where the pellets stream out
|
||||
ctx.strokeStyle = '#d8d8d8'; ctx.lineWidth = 2;
|
||||
ctx.beginPath(); ctx.moveTo(hip.x, hip.y); ctx.lineTo(tip.x, tip.y); ctx.stroke();
|
||||
ctx.strokeStyle = '#b07030'; ctx.lineWidth = 1.5; // the head, aimed forward
|
||||
ctx.beginPath(); ctx.ellipse(tip.x, tip.y, Math.max(2, tip.s * 0.14), Math.max(3, tip.s * 0.22), 0, 0, 7); ctx.stroke();
|
||||
if (G.horde && G.horde.mgT > 1 / TUNE.mgRate - 0.06) { // muzzle flash right after a shot
|
||||
ctx.fillStyle = '#fff0a0'; ctx.beginPath(); ctx.arc(tip.x, tip.y, Math.max(2, tip.s * 0.12), 0, 7); ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ frame ----
|
||||
export function render() {
|
||||
@@ -293,6 +304,8 @@ export function render() {
|
||||
const by = Math.abs(Math.sin(G.idleT * BOUNCE_IDLE.tempo)) * BOUNCE_IDLE.apex;
|
||||
drawBall(bx, by, sv.z);
|
||||
}
|
||||
// earlier serves still in flight (multi-ball barrage)
|
||||
for (const b of extraBalls) drawBall(b.x, b.y, b.z, false, b.bomb);
|
||||
// the match ball
|
||||
if ((ball.active || G.phase === 'over') && !ball.collected) drawBall(ball.x, ball.y, ball.z, true, ball.bomb);
|
||||
// splatter particles
|
||||
@@ -308,6 +321,7 @@ export function render() {
|
||||
ctx.fillStyle = '#f8e048'; ctx.fillRect((pp.x | 0) - 1, (pp.y | 0) - 1, 2, 2);
|
||||
}
|
||||
if (G.mode !== 'eaten') drawChar(P); // there is no P anymore
|
||||
if (G.horde && G.mode === 'splatter') drawMgRacket(); // rambo racket over the player
|
||||
|
||||
// HUD — scoreboard
|
||||
ctx.fillStyle = 'rgba(12,16,24,0.82)'; ctx.fillRect(6, 6, 132, 30);
|
||||
@@ -336,8 +350,11 @@ export function render() {
|
||||
if (G.phase === 'pretoss' && score.server === 'P' && G.mode !== 'eaten' && !G.horde) {
|
||||
ctx.fillStyle = '#f8e048'; ctx.font = 'bold 8px monospace';
|
||||
const touch = matchMedia('(pointer: coarse)').matches;
|
||||
ctx.fillText(G.serveNum === 2 ? (touch ? 'SECOND SERVE — TAP' : 'SECOND SERVE — SPACE')
|
||||
: (touch ? 'TAP TO TOSS, TAP TO SERVE' : 'SPACE TO TOSS, SPACE TO SERVE'), W / 2, H - 16);
|
||||
const txt = G.mode === 'splatter'
|
||||
? (touch ? 'TAP TO FIRE · FLICK TO AIM · SWISH TO MELEE' : 'SPACE FIRES · ARROWS AIM')
|
||||
: G.serveNum === 2 ? (touch ? 'SECOND SERVE — TAP' : 'SECOND SERVE — SPACE')
|
||||
: (touch ? 'TAP TO TOSS, TAP TO SERVE' : 'SPACE TO TOSS, SPACE TO SERVE');
|
||||
ctx.fillText(txt, W / 2, H - 16);
|
||||
}
|
||||
if (G.flash) {
|
||||
ctx.font = 'bold 14px monospace'; ctx.fillStyle = '#ffffff';
|
||||
|
||||
Reference in new issue
Block a user