diff --git a/gameplay.js b/gameplay.js index a6bbace..781a0d6 100644 --- a/gameplay.js +++ b/gameplay.js @@ -5,7 +5,7 @@ import { P, O, ball, score, crowd, G, court, other, deuceSide, setupPoint, sendB import { audio, pock, swish, whoosh, applause, voice, sfx, aww, zombieMusic, zombieDuck, menuDuck, splat, shriek, boom } from './audio.js'; import { axisX, axisZ, takeFlickAim, touchPX, resetInput } from './input.js'; import { W } from './render.js'; // runtime-only cycle (render imports gameplay), same pattern as input/zombies -import { updateZombies, spawnZombie, spawnRacketZombie, spawnMachine, spawnChapterBoss, zombies, gore, explode, meleeSwipe, extras, parts, pellets, shots, playerGore, eatenBleed, resetZombies , firePellet , spawnBarrel , fireBeam , pops, popText } from './zombies.js'; +import { updateZombies, updateParts, spawnZombie, spawnRacketZombie, spawnMachine, spawnChapterBoss, zombies, gore, explode, meleeSwipe, extras, parts, pellets, shots, playerGore, eatenBleed, resetZombies , firePellet , spawnBarrel , fireBeam , pops, popText } from './zombies.js'; // ------------------------------------------------------------- ballistics ---- // Launch ball from (x0,y0,z0) so it arrives at (tx, tz) with horizontal speed s under gravity g. @@ -742,13 +742,7 @@ export function startBossFight(chapter) { const art = bossArt(chapter); G.card = { title: art.name, color: UI.bad, t: TUNE.setCardT }; G.flash = null; - if (!G.seen.boss) { // the rules change: a wall, and a thing that shoots back - G.seen.boss = true; - showModal({ title: 'IT BRINGS A MACHINE', color: UI.bad, - body: ['IT THROWS BALLS AT YOU', 'SWING TO RETURN THEM — OR SWISH TO BAT THEM AWAY', - 'SWIPE SIDEWAYS TO STEP OUT OF THE WAY', - 'A HIT COSTS BLOOD. WIN A DUEL TO HEAL.'] }); - } + // (the four-line machine instruction modal is gone — people figure it out) } // the chapter boss is down: the parked game beat resumes and normal spawning comes back @@ -767,7 +761,7 @@ export function endBossFight() { // long hold — the chapter and the crowd move down to the subs where they belong. // (no 'GAME n NEXT' line: the bar under the scoreboard prints the game number permanently, and a // card that repeats what the HUD already says is the clutter this pass is meant to remove.) - G.card = { title: 'YOU DID IT', color: UI.pick, t: TUNE.bossWinT, + G.card = { title: 'EPIC!', sub1: 'YOU DID IT!', color: UI.pick, t: TUNE.bossWinT, wait: TUNE.bossDeathT }; // ...after the body has finished coming apart ball.active = false; // a duel the handback just started owns the phase; otherwise hold the boss beat, and never @@ -1500,6 +1494,7 @@ export function update(dt) { // --- zombies + transition timeline if (G.mode !== 'tennis') updateZombies(dt); + else updateParts(dt); // tennis has no zombies but its bounces still kick up dust that must settle tickTestMode(dt); // keeps the sandbox stocked and rolls it a barrel now and then if (G.meleeT > 0) G.meleeT -= dt; if (G.rackGlow && (G.rackGlow.t -= dt) <= 0) G.rackGlow = null; // the fresh-pickup pulse fades out diff --git a/index.html b/index.html index 690fefe..309d80c 100644 --- a/index.html +++ b/index.html @@ -398,6 +398,6 @@ - + diff --git a/zombies.js b/zombies.js index 845fa2c..0151fb4 100644 --- a/zombies.js +++ b/zombies.js @@ -897,6 +897,21 @@ function bleed(zb) { } } +// Split out of updateZombies because particles are NOT a zombie feature: the ball's bounce dust +// fires in TENNIS too, and while this loop only ran in splatter, tennis dust was born, never +// ticked, and sat on the clay forever — the "marks where the ball hit" testers saw. +export function updateParts(dt) { + for (let i = parts.length - 1; i >= 0; i--) { + const p = parts[i]; + p.t -= dt; + if (p.smoke) { p.vy = 0.55; p.vx *= 0.97; p.vz *= 0.97; } // smoke rises and spreads; it does not fall + else p.vy -= 14 * dt; + p.x += p.vx * dt; p.y += p.vy * dt; p.z += p.vz * dt; + if (p.y < 0) { p.y = 0; p.vy = 0; p.vx *= 0.5; p.vz *= 0.5; } + if (p.t <= 0) parts.splice(i, 1); + } +} + export function updateZombies(dt) { moanT -= dt; if (moanT <= 0) { moan(); moanT = 2.5 + Math.random() * 4; } @@ -1295,15 +1310,7 @@ export function updateZombies(dt) { if (dead) extras.splice(i, 1); } - for (let i = parts.length - 1; i >= 0; i--) { - const p = parts[i]; - p.t -= dt; - if (p.smoke) { p.vy = 0.55; p.vx *= 0.97; p.vz *= 0.97; } // smoke rises and spreads; it does not fall - else p.vy -= 14 * dt; - p.x += p.vx * dt; p.y += p.vy * dt; p.z += p.vz * dt; - if (p.y < 0) { p.y = 0; p.vy = 0; p.vx *= 0.5; p.vz *= 0.5; } - if (p.t <= 0) parts.splice(i, 1); - } + updateParts(dt); const boy = G.boy; // the boy mops any gore he passes over for (let i = gore.length - 1; i >= 0; i--) {