Phase 7: the scoreboard is the progression — spawn director, changeovers, sets, permadeath
- Spawn director: gap and court cap scale with games finished (runGames); game 1 is three lonely shamblers, late runs approach zSpawnMin/zMaxCap - Changeover after odd games: 4.5s forced breather, no spawns, no serving (melee still works), boys mop, umpire calls 'Time.' (new time.m4a) - 6 games = a set; set end launches the horde (replaces the splatGames>=2 trigger) - Zombies taking a game sets shameGame (phase 9 withholds the delivery) - Eaten = the run is over: GAME, SET, MASSACRE summary with games/sets survived and best run (localStorage); HUD shows sets in splatter (SETS·GAMES PTS) - spentBall() helper so dead-ball paths can't clobber a changeover's beat - T surface: splatPoint + explode exposed for verify scripts Verified: game-1 cap 3 held 25s; changeover blocks taps+spawns then resumes; 6 games -> set+horde -> racket zombie; runGames=6 tempo ~6 spawns/10s; eaten writes best; tennis regression 11/12 CPU returns; no console errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
25db84ed87
commit
5a7f71fdba
7 files changed
+80
-27
No files matched your search
@@ -335,22 +335,27 @@ export function render() {
|
||||
const ptTxt = i => score.pts[i] >= 3 && score.pts[0] >= 3 && score.pts[1] >= 3
|
||||
? (score.pts[i] === score.pts[1 - i] ? '40' : (score.pts[i] > score.pts[1 - i] ? 'AD' : '40'))
|
||||
: ['0', '15', '30', '40'][Math.min(score.pts[i], 3)];
|
||||
const setsTxt = i => G.mode === 'tennis' ? '' : G.sets[i] + '·'; // splatter scoreboard: SETS·GAMES PTS
|
||||
ctx.fillStyle = score.server === 'P' ? '#f8e048' : '#c8d0dc';
|
||||
ctx.fillText('YOU ' + score.games[0] + ' ' + ptTxt(0), 12, 10);
|
||||
ctx.fillText('YOU ' + setsTxt(0) + score.games[0] + ' ' + ptTxt(0), 12, 10);
|
||||
for (let b = 0; b < G.bombs; b++) { // stocked bomb balls: black pips with an orange fuse rim
|
||||
ctx.fillStyle = '#111'; ctx.beginPath(); ctx.arc(96 + b * 8, 14, 2.6, 0, 7); ctx.fill();
|
||||
ctx.strokeStyle = '#ff8010'; ctx.lineWidth = 0.75; ctx.stroke();
|
||||
}
|
||||
ctx.fillStyle = score.server === 'O' ? '#f8e048' : '#c8d0dc';
|
||||
ctx.fillText((G.mode === 'tennis' ? 'CPU ' : 'THEM ') + score.games[1] + ' ' + ptTxt(1), 12, 22);
|
||||
ctx.fillText((G.mode === 'tennis' ? 'CPU ' : 'THEM ') + setsTxt(1) + score.games[1] + ' ' + ptTxt(1), 12, 22);
|
||||
ctx.textAlign = 'center';
|
||||
if (G.mode === 'eaten') {
|
||||
if (G.mode === 'eaten') { // the run summary — permadeath makes this scoreboard the whole game
|
||||
ctx.fillStyle = 'rgba(10,4,6,0.72)'; ctx.fillRect(0, 0, W, H);
|
||||
ctx.fillStyle = '#c02020'; ctx.font = 'bold 22px monospace';
|
||||
ctx.fillText('EATEN.', W / 2, H / 2 - 30);
|
||||
ctx.fillStyle = '#c02020'; ctx.font = 'bold 16px monospace';
|
||||
ctx.fillText('GAME, SET, MASSACRE.', W / 2, H / 2 - 32);
|
||||
ctx.fillStyle = '#c8d0dc'; ctx.font = 'bold 10px monospace';
|
||||
ctx.fillText('GAMES YOU ' + score.games[0] + ' — THEM ' + score.games[1], W / 2, H / 2);
|
||||
if (G.eatenT > 0.7) { ctx.fillStyle = '#f8e048'; ctx.font = 'bold 8px monospace'; ctx.fillText('TAP TO PLAY AGAIN', W / 2, H / 2 + 20); }
|
||||
ctx.fillText('SURVIVED ' + G.runGames + ' GAMES · ' + G.sets[0] + ' SETS', W / 2, H / 2 - 6);
|
||||
if (G.best) {
|
||||
ctx.fillStyle = '#8fa0b8'; ctx.font = 'bold 8px monospace';
|
||||
ctx.fillText('BEST ' + G.best.games + ' GAMES · ' + G.best.sets + ' SETS', W / 2, H / 2 + 10);
|
||||
}
|
||||
if (G.eatenT > 0.7) { ctx.fillStyle = '#f8e048'; ctx.font = 'bold 8px monospace'; ctx.fillText('TAP TO PLAY AGAIN', W / 2, H / 2 + 28); }
|
||||
}
|
||||
if (G.phase === 'pretoss' && score.server === 'P' && G.mode !== 'eaten' && !G.horde) {
|
||||
ctx.fillStyle = '#f8e048'; ctx.font = 'bold 8px monospace';
|
||||
|
||||
Reference in new issue
Block a user