Let the horde gun keep up with the horde

The run ended on the second wave every time: the gun fired 6 flat rounds a
second, hit one body each, and every shambler soaked one — while hordeGrow
made each wave longer and denser. Doubled the rate and scaled it with the
wave (14/s at one set banked, 21/s at four), gave a round 0.5m of extra
reach, a wider cone, and enough damage to drop a shambler outright. Raised
the spawn rate to match, so surviving a wave still leaves a standing crowd
on your half — that pressure is the point of it.

Also: he no longer carries a second racket at his hip while holding one
forward (the run frames are the racket-less art), the umpire stopped talking
over himself and saying "remain seated" twice, and a timed freeze now shows
what it is counting down to instead of reading as dead controls.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-07-31 08:07:35 +02:00
1 parent 571cd4c399
commit e904dfefa6
5 files changed
+47 -13

No files matched your search

+19 -3
View File
@@ -416,6 +416,10 @@ function drawChar(ch) {
if (ch.pose === 'swing') name = pre + (ch.poseT < 0.15 ? 'swing1' : 'swing2');
if (ch.pose === 'serve') name = pre + (G.phase === 'toss' ? 'toss' : 'smash');
if (ch.pose === 'run') p.y -= Math.floor(ch.runT * 18) % 2; // 1px run bob
// Auto-fire: the idle sprite carries a racket at his hip AND drawMgRacket puts one in his hands —
// two rackets. The run frames are the racket-less art, so he jogs on the spot instead.
if (ch.side === 'P' && ch.pose === 'idle' && G.mode === 'splatter' && (G.horde || G.mgT > 0))
name = pre + (Math.floor(G.idleT * 6) % 2 ? 'run1' : 'run2');
const img = SPR[name];
drawShadow(p.x, p.y, hpx * 0.22, hpx * 0.06);
if (img) {
@@ -450,7 +454,8 @@ function drawMgRacket() { // horde: the player braces the racket forward and up-
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();
const cool = G.horde ? G.horde.mgT : G.mgCool; // horde or GUN-tin pickup — same racket
if (cool > 1 / TUNE.mgRate - 0.06) { // muzzle flash right after a shot
const rate = G.horde ? G.horde.mgRate || TUNE.mgRate : TUNE.mgRate;
if (cool > 1 / rate - 0.03) { // 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();
}
}
@@ -805,7 +810,7 @@ export function render() {
for (const pe of pellets) {
const sh = proj(pe.x, 0, pe.z); drawShadow(sh.x, sh.y, Math.max(1, sh.s * 0.04), Math.max(1, sh.s * 0.02), 0.25);
const pp = proj(pe.x, pe.y, pe.z);
ctx.fillStyle = UI.warm; ctx.fillRect((pp.x | 0) - 1, (pp.y | 0) - 1, 2, 2);
ctx.fillStyle = UI.warm; ctx.fillRect((pp.x | 0) - 2, (pp.y | 0) - 2, 4, 4); // fat round — it hits like one
}
// death, in three stages. A: you are still there, thrashing and going red as they crowd in.
// B (from EATEN_BURST): the sprite is gone and you ARE the gore — playerGore() drops the mound.
@@ -905,7 +910,18 @@ export function render() {
if (G.eatenT > EATEN_RESTART) { ctx.fillStyle = UI.warm; ctx.font = F(8); ctx.fillText('TAP TO PLAY AGAIN', W / 2, H / 2 + 26); }
ctx.restore();
}
if (G.phase === 'pretoss' && score.server === 'P' && G.mode !== 'eaten' && !G.horde && !(G.mgT > 0) && !G.aim) { // while pulling, the draw meter owns this slot
// Timed freezes swallow every input until they run out, which reads as the game having hung
// ("sometimes the controls just feel frozen"). Show the clock instead of hiding it. A modal is
// excluded on purpose — that one waits for YOU, and says so.
const waitT = G.modal || G.mode === 'eaten' ? 0
: Math.max(G.changeover > 0 ? G.changeover : 0,
G.phase === 'over' ? G.overT : 0,
G.interlude ? G.interlude.introT : 0);
if (waitT > 0.5) {
ctx.font = F(7, 0); ctx.fillStyle = hexA(UI.warm, 0.8);
ctx.fillText('RESUMING IN ' + Math.ceil(waitT), W / 2, 78); // under the flash line, clear of the court and the man
}
if (G.phase === 'pretoss' && score.server === 'P' && G.mode !== 'eaten' && !G.horde && !(G.mgT > 0) && !G.aim && waitT <= 0.5) { // while pulling, the draw meter owns this slot
ctx.fillStyle = UI.warm; ctx.font = F(8);
const touch = IS_TOUCH, switchHint = touch ? 'TAP THE RACK TO SWITCH' : '1-5 TO SWITCH';
const sel = ['PLAIN', 'BOMB', 'TRIPLE', 'FIRE', 'GUN'][G.weapon];