From 25f915f236d0929f08c1a656c18bff53cc3a0885 Mon Sep 17 00:00:00 2001 From: Oliver Rieger Date: Mon, 3 Aug 2026 12:09:28 +0200 Subject: [PATCH] Fill the rack yourself, and roll barrels out that fill it The rack had five hardcoded slots where the index WAS the weapon, spelled out in five places: SLOTS, slotStock, WDESC, drawRack's three parallel arrays, deliverTin's w+1, and bare wsel === 1|2|3 / G.weapon === 4 tests across five files. config.WEAPONS is the one table now, G.rack holds up to four {id, n} in the order you GATHERED them, and slot 0 stays BALL. Everything reads through slotId/slotStock/slotEntry, so nothing else knows how the rack is stored. A fifth weapon is always allowed, per Oliver: it rides slot 0 as G.temp until spent, then slot 0 goes back to the infinite ball. A barrel arms on the spot and is spent quickly, so refusing it would mean a barrel you shot did nothing. And a spent weapon LEAVES its slot -- with gathered slots, an exhausted weapon holding one is a quarter of the rack you can never pick up into. (The old rack kept its empties dimmed, which was right when a slot meant "BOMB goes here".) Four new weapons, and they are all the ONE existing pellet loop. firePellet(w) stamps the WEAPONS row onto the round -- r, dmg, hp, pierce, boom, ignite, col, speed, life -- so the beacon cannon, flamethrower, laser and rocket needed no new array and no new update; the break after the first body was the only thing making a pellet single-target. All four go through the same isBoss branch, which is why none can silently no-op on a boss the way three of five once did. Measured: gun/beacon/laser/rocket reach past 16 m, the flamer is a ~6 m cone that ignites. G.mgT stays the mode timer for all of them, so the eighteen !(G.mgT > 0) guards across five files did not have to change. kind:'burst' auto-fires at its own rate for len seconds; kind:'shot' holds the same braced stance and fires one per tap. The barrels reuse the ball machine's shape: a rolling, shootable non-body. All three of the registrations that fail silently are done -- blocksClear (loot must not gate the hand-clear), the contact guard (furniture cannot eat you), and hitR/GORE_MUL/zombieH. It scores nothing. The pink had to become procedural. The chroma key deletes every pixel where red and blue both beat green by 40, so a magenta drum and a magenta laser pistol both came back as hollow outlines -- 19% and 15% solid against 50-56% for the icons in other colours. The drum is grey steel with hazard chevrons and every bit of its pink is the halo drawBarrel paints; the laser went electric blue. Both are solid now (92% and 58%), and the rule is written down in HANDOFF. tinTriples 3 -> 10 as asked, and the four dead TUNE ammo constants are gone -- nothing read them once the table landed, which is a panel value you can tune all day for no effect. Co-Authored-By: Claude Opus 5 --- HANDOFF.md | 46 ++++++++++++ assets/_regen_batches.json | 14 ++++ assets/w_beacon.png | Bin 0 -> 11113 bytes assets/w_flamer.png | Bin 0 -> 6887 bytes assets/w_laser.png | Bin 0 -> 10179 bytes assets/w_rocket.png | Bin 0 -> 14583 bytes assets/z_barrel1.png | Bin 0 -> 25307 bytes assets/z_barrel2.png | Bin 0 -> 25660 bytes config.js | 63 ++++++++++++++-- gameplay.js | 142 ++++++++++++++++++++++++++----------- index.html | 2 +- input.js | 10 ++- main.js | 6 +- render.js | 92 ++++++++++++++++++------ state.js | 22 +++--- zombies.js | 85 ++++++++++++++++++---- 16 files changed, 385 insertions(+), 97 deletions(-) create mode 100644 assets/w_beacon.png create mode 100644 assets/w_flamer.png create mode 100644 assets/w_laser.png create mode 100644 assets/w_rocket.png create mode 100644 assets/z_barrel1.png create mode 100644 assets/z_barrel2.png diff --git a/HANDOFF.md b/HANDOFF.md index 9ad3c41..2b76612 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -438,6 +438,16 @@ Clear the intro first: `document.getElementById('intro')?.remove()`. only when `ref_only` is unused — with `ref_only` set, list the throwaway as an extra sprite (`p_pull0`) and delete the PNG afterwards. +15. **NOTHING IN A SPRITE MAY BE PINK, MAGENTA, VIOLET OR PURPLE.** The chroma key does not merely + match the corner colour, it deletes every pixel where red AND blue both beat green by 40 + (`_key.py`: `min(r, b) > g + 40`, and `_fine.py` de-fringes the key twice). So a "vivid magenta + drum" and a "magenta laser pistol with a violet coil" both came back as hollow outlines — 19% and + 15% solid pixels against 50-56% for the icons drawn in other colours. Measure `alpha > 128` as a + fraction of the box if a sprite looks thin; that is the whole diagnosis. **Pink can only ever be + PROCEDURAL** — the weapon barrel is grey steel with yellow chevrons in its PNG, and every bit of + its pink is the halo `drawBarrel` paints around it, which never goes near the keyer. State the + forbidden colours in the prompt explicitly; "make it pink" is a request the pipeline cannot fill. + Known generator quirks, all still true: - It sometimes ignores the magenta (harmless — the key uses the corner colour). - It draws near-identical animation frames unless you describe strongly contrasted poses. @@ -450,6 +460,42 @@ Known generator quirks, all still true: - **Colour-match any regenerated frame** of an existing pair before shipping: fit a per-channel linear map onto the original's mean and sd, then verify with a mean-difference number. +## Session log: a rack you fill yourself, and barrels that fill it (2026-08-03) + +The second half of the same ask. **The rack no longer has fixed slots.** It used to be five hardcoded +positions where the index WAS the weapon — `SLOTS`, `slotStock`, `WDESC`, `drawRack`'s three parallel +arrays, `deliverTin`'s `w + 1`, and bare `wsel === 1|2|3` / `G.weapon === 4` tests in five files. Now +`config.WEAPONS` is the single table, `G.rack` holds up to four `{id, n}` in the order you GATHERED +them, and slot 0 is BALL. Read it through `slotId` / `slotStock` / `slotEntry` and nothing else needs +to know how it is stored. + +**A fifth weapon is always allowed** (Oliver's rule): it rides slot 0 as `G.temp` until spent, then +slot 0 goes back to being the infinite ball. A barrel arms on the spot and is spent quickly, so +refusing it would mean a barrel you shot did nothing. **A spent weapon LEAVES its slot** — with +gathered slots, an exhausted weapon sitting in one is a quarter of the rack you can never pick up +into. (The old rack kept empty slots dimmed, which was right when a slot meant "BOMB goes here".) + +**Four new weapons, and they are all the one pellet loop.** `firePellet(w)` stamps the WEAPONS row +onto the round — `r`, `dmg`, `hp`, `pierce`, `boom`, `ignite`, `col`, `speed`, `life` — so the beacon +cannon, flamethrower, laser and rocket needed no new array and no new update. The `break` after the +first body was the only thing making a pellet single-target. Every one of them goes through the same +`isBoss` branch, which is why none can silently no-op on a boss the way three of five once did. +Measured: gun/beacon/laser/rocket reach 16 m+, the flamer is a deliberate ~6 m cone that ignites. + +**`G.mgT` stayed the mode timer for all of them**, which is why the eighteen `!(G.mgT > 0)` guards +across five files did not have to change. `kind:'burst'` auto-fires at its own rate for `len` +seconds; `kind:'shot'` (laser 10, rocket 3) holds the same braced stance and fires one per TAP. + +**The barrels** are the ball machine's shape reused: a rolling, shootable non-body. Three +registrations are silent if forgotten and all three are done — `blocksClear` (a barrel must NOT gate +the hand-clear; it is optional loot), the contact-kill guard (furniture cannot eat you), and +`hitR`/`GORE_MUL`/`zombieH`. It scores nothing. + +**The pink is procedural, and it has to be** — see pipeline note 15. A magenta drum and a magenta +laser pistol were both eaten by the chroma key. + +`TUNE.tinTriples` 3 -> 10, as asked. + ## Session log: the roster grows — moving bosses, six of them, three new bodies (2026-08-03) Oliver asked for five things. This is the first half: **enemies first, weapons second**, his call, so diff --git a/assets/_regen_batches.json b/assets/_regen_batches.json index e314e17..d660711 100644 --- a/assets/_regen_batches.json +++ b/assets/_regen_batches.json @@ -754,5 +754,19 @@ "check": "loose", "subject": "The reference shows the SAME boss in four states: undamaged, then three worsening stages of damage. Draw all FOUR of those exact states again, in the same order, same size, same costume, same wounds \u2014 but each one caught MID-THROW: the whole body wound up and twisted, the surviving arm drawn back high over the shoulder about to hurl a tennis ball straight at the viewer, weight on the back foot. The wounds must stay clearly visible in every frame. Output exactly four figures in one row.", "state": true + }, + "barrel": { + "sprites": [ + "z_barrel1", + "z_barrel2" + ], + "ref_only": [ + "z_mach1" + ], + "anchor": 0, + "check": "any", + "newart": true, + "h": 150, + "subject": "The reference is a piece of tennis-court equipment drawn as pixel art, seen from the side. Draw TWO frames of a DIFFERENT object in that same style: a squat STEEL DRUM lying on its side like a barrel about to be rolled, banded with two raised hoops, its curved end-cap facing the viewer. The drum is DARK GUNMETAL GREY steel, heavily dented and streaked with rust, with bold YELLOW AND BLACK hazard chevrons stencilled around the middle band and a thin band of glowing PALE CYAN-WHITE light leaking from the seams between the staves. IMPORTANT: no part of the drum may be pink, magenta, purple or violet \u2014 use only grey steel, rust brown, yellow, black and pale cyan-white. It is a wide low object, clearly wider than it is tall, and it sits ON THE GROUND. The two frames are the same drum rotated part of a turn, so the hoops, chevrons and dents sit at visibly DIFFERENT angles \u2014 frame two is rolled about a third of a turn from frame one. Output exactly two objects in one row, evenly spaced." } } \ No newline at end of file diff --git a/assets/w_beacon.png b/assets/w_beacon.png new file mode 100644 index 0000000000000000000000000000000000000000..4db01afd665d5f7f07cfef27dff299ea13e97fd8 GIT binary patch literal 11113 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 literal 0 HcmV?d00001 diff --git a/assets/w_rocket.png b/assets/w_rocket.png new file mode 100644 index 0000000000000000000000000000000000000000..14084e2dde6fc7389660755cf78be0b90cec0305 GIT binary patch literal 14583 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 literal 0 HcmV?d00001 diff --git a/assets/z_barrel1.png b/assets/z_barrel1.png new file mode 100644 index 0000000000000000000000000000000000000000..a6e3d73b5b6343c1de9ef6a1756ae0a70b648e66 GIT binary patch literal 25307 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 literal 0 HcmV?d00001 diff --git a/config.js b/config.js index f1e5188..34c735c 100644 --- a/config.js +++ b/config.js @@ -211,10 +211,9 @@ export const TUNE = { // on its own so there is always a chance at one. Fires off the next hand-cleared wave. duelFirst: 2, // game after which the first scheduled sudden-death duel is called duelEvery: 3, // games between scheduled duels after the first - bombMax: 3, // bomb balls in a fresh tin - tinTriples: 3, // triple-serves in a fresh tin - tinFlames: 3, // flame balls in a fresh tin - tinGuns: 1, // machine-gun charges in a fresh tin + // How many a fresh tin holds now lives on its WEAPONS row (`tin`), because the rack is no longer + // four fixed types — bombMax/tinTriples/tinFlames/tinGuns lived here and nothing read them after + // the table landed, which is a value you can tune in the panel all day for no effect. wCap: 9, // per-type cap on the courtside rack — tins accumulate, but only so many rackH: 30, // px height of the bottom weapon rack (feel knob: how much court it covers). // 22 logical px is ~30 CSS px on a phone — under half Apple's 44px target, which @@ -231,6 +230,14 @@ export const TUNE = { mgPelletR: 0.5, // m of extra hit radius on a pellet: a fat round. "Bigger ball = more damage." dmgPelletHp: 2, // hp a pellet takes off a body — shamblerHp is 2, so the horde gun one-shots them mgPickupLen: 8, // s the machine-gun tin (4th delivery) auto-fires outside hordes + shotHoldLen: 14, // s you stay braced with a TAP-fired weapon (laser, rocket) before the window + // closes on its own. Running the magazine out ends it earlier, which is the + // normal way; this only stops a forgotten rocket freezing the serve loop. + // ---- the weapon barrel: the only way to get the four weapons that are not on the duel ladder + barrelEvery: 3, // games between barrels being rolled on (like the ball machine's machEvery) + barrelRoll: 2.6, // m/s it trundles across the far court — fast enough to have to be led + barrelHp: 1, // anything pops it: ball, pellet, bomb, flame, swish. It is a target, not a body + barrelH: 1.15, // drawn height (a body is 1.8) fps: 60, // frame cap — 30 halves battery/heat but Oliver felt it; smoothness wins (0 = uncapped) runnerSpeed: 3.2, // runner ('f') speed multiplier over the shamble runnerVMax: 4.2, // hard runner speed cap — the late-game ramp must stay humanly reactable @@ -293,7 +300,10 @@ export const EATEN_FEED_R = 1.15; // m — how tight the feeding ring packs arou // desaturated from the original arcade primaries: they were shouting over the artwork. // (NOT the gore — blood and piles keep their loud reds; only text and interactive UI.) export const UI = { hot: '#d9705f', warm: '#e0cc72', pick: '#cbd06a', cream: '#e8e8dc', - dim: '#8fa0b8', bad: '#a34a42', ball: '#dbe063' }; + dim: '#8fa0b8', bad: '#a34a42', ball: '#dbe063', + // the weapon barrel's glow, and the only pink on the court: nothing else in the + // palette is near it, so "pink means a weapon is in there" needs no explaining + pink: '#ff5cf0' }; // ------------------------------------------------------------------ courts ---- // A court type is pure DATA: adding one is a row here plus its four PNGs, never a change to render(). @@ -408,13 +418,54 @@ export const ZWOUND = { k: ['z_kw', 'z_kw2', 'z_kw3', 'z_kw4'], }; +// ---- THE ARSENAL. One table, keyed by id, replacing the five places the roster used to be spelled +// out (SLOTS, slotStock, WDESC, drawRack's three parallel arrays, and deliverTin's index mapping). +// The rack no longer has a fixed weapon per slot: G.rack holds up to four of these in the order you +// GATHERED them, and slot 0 stays BALL. A weapon is defined entirely by its row. +// kind 'serve' — arms the next serve (the four original tins) +// kind 'burst' — commits to a braced auto-fire window: `len` seconds at `rate` rounds/s +// kind 'shot' — same braced stance, but each TAP fires one and `tin` is the magazine +// The pellet fields (r, dmg, pierce, boom, ignite, col, speed) are stamped onto the round by +// firePellet, so all four new weapons are the ONE existing pellet loop with different numbers. +export const WEAPONS = { + bomb: { label: 'BOMB', icon: 'w_bomb', tin: 3, kind: 'serve', + desc: ['DETONATES ON CONTACT', 'CLEARS A WHOLE CROWD AT ONCE'] }, + triple: { label: 'TRIPLE', icon: 'w_triple', tin: 10, kind: 'serve', // was 3 — Oliver asked for ten + desc: ['ONE SERVE, THREE BALLS', 'FANNED WIDE ACROSS THE LANE'] }, + fire: { label: 'FIRE', icon: 'w_fire', tin: 3, kind: 'serve', + desc: ['KILLS ON ANY TOUCH', 'AND SETS THE OTHERS ALIGHT'] }, + gun: { label: 'GUN', icon: 'w_gun', tin: 1, kind: 'burst', len: 8, rate: 12, + r: 0.5, dmg: 0.34, hp: 2, speed: 26, + desc: ['HOLD THE LINE AND SPRAY', 'A BURST, NOT A SERVE'] }, + // ---- the four that come out of a barrel. Won by shooting one, never delivered. + beacon: { label: 'BEACON', icon: 'w_beacon', tin: 1, kind: 'burst', len: 10, rate: 3, + r: 1.1, dmg: 3, hp: 9, speed: 17, col: '#7fefff', glow: 1, + desc: ['A SLOW BEAT OF BIG', 'GLOWING SHELLS'] }, + flamer: { label: 'FLAMER', icon: 'w_flamer', tin: 1, kind: 'burst', len: 5, rate: 16, + r: 0.8, dmg: 2, hp: 9, speed: 11, life: 0.55, ignite: true, col: '#ffa040', glow: 1, // life 0.55s x 11 m/s = a ~6 m cone + desc: ['A CONE OF FIRE', 'AND THE FIRE SPREADS'] }, + laser: { label: 'LASER', icon: 'w_laser', tin: 10, kind: 'shot', + // NOT magenta: the icon art is chroma-keyed and the key eats every pink/purple + // (min(r,b) > g+40), so a magenta laser came back as a hollow outline. Pink stays + // the BARREL's identity, where it is a procedural glow and never goes near the key. + r: 0.55, dmg: 3, hp: 9, speed: 70, pierce: true, col: '#8ab6ff', glow: 1, + desc: ['TEN SHOTS, AND EACH ONE', 'GOES THROUGH THE WHOLE LANE'] }, + rocket: { label: 'ROCKET', icon: 'w_rocket', tin: 3, kind: 'shot', + r: 0.6, dmg: 3, hp: 9, speed: 22, boom: true, col: '#ffd050', glow: 1, + desc: ['THREE ROCKETS', 'EACH ONE TAKES THE CROWD'] }, +}; +// what a pink barrel can be carrying — the four above that you cannot win any other way +export const BARREL_WEAPONS = ['beacon', 'laser', 'rocket', 'flamer']; +export const RACK_MAX = 4; // gathered weapons the rack holds; a 5th rides slot 0 until it is spent + export const MACH_H = 1.55; // was 1.15 — waist high read as a prop you could ignore. Still furniture // (a body is PLAYER_H 1.88), but big enough to be a thing on the court. // Drawn height of a body on the far side, by variant. render draws to it AND the serve aims at a // fraction of it, so the two cannot drift — a shot at a brute has to arrive at the brute's chest, // not at the height a shambler's chest happens to be. -export const zombieH = zb => zb.variant === 'g' ? 2.7 : zb.variant === 'd' ? 2.15 +export const zombieH = zb => zb.variant === 'o' ? TUNE.barrelH + : zb.variant === 'g' ? 2.7 : zb.variant === 'd' ? 2.15 : zb.variant === 'k' ? 3.2 : zb.variant === 'h' ? 2.4 : zb.variant === 'r' ? 1.85 : zb.variant === 'm' ? MACH_H : zb.variant === 'B' ? 1.8 * (zb.scale || 1.5) : 1.8; diff --git a/gameplay.js b/gameplay.js index 8207e7a..fb08f1e 100644 --- a/gameplay.js +++ b/gameplay.js @@ -1,11 +1,11 @@ // Game logic: ballistics, swings, serves, rules/scoring, AI, and the per-frame update. -import { TUNE, UI, BOSS_ART, APPLAUSE_T, EATEN_BURST, EATEN_SUMMARY, COURT, CONTACT, VOLLEY, ZONE_DEPTH, HOME, P_BOUNDS, TOSS, SHOT_DEPTH, POSE_TIME, BOUNCE_IDLE, zombieH, Z_CHEST } from './config.js'; +import { TUNE, UI, BOSS_ART, APPLAUSE_T, EATEN_BURST, EATEN_SUMMARY, COURT, CONTACT, VOLLEY, ZONE_DEPTH, HOME, P_BOUNDS, TOSS, SHOT_DEPTH, POSE_TIME, BOUNCE_IDLE, zombieH, Z_CHEST , WEAPONS, RACK_MAX } from './config.js'; import { P, O, ball, score, crowd, G, other, deuceSide, setupPoint, sendBoy, resetState } from './state.js'; import { audio, pock, swish, whoosh, applause, voice, sfx, aww, zombieMusic, zombieDuck, menuDuck } 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 } from './zombies.js'; +import { updateZombies, spawnZombie, spawnRacketZombie, spawnMachine, spawnChapterBoss, zombies, gore, explode, meleeSwipe, extras, parts, pellets, shots, playerGore, eatenBleed, resetZombies , firePellet , spawnBarrel } from './zombies.js'; // ------------------------------------------------------------- ballistics ---- // Launch ball from (x0,y0,z0) so it arrives at (tx, tz) with horizontal speed s under gravity g. @@ -263,12 +263,13 @@ function hitServe(sv, quality, aim = null) { // specials for later. Placement still follows the flick — only WHICH ball flies is the // rack's call now. GUN (slot 4) never reaches here; it starts a burst before the serve. const wsel = G.mode === 'splatter' ? G.weapon : 0; - ball.bomb = wsel === 1 && G.bombs > 0; - if (ball.bomb) G.bombs--; - ball.flame = wsel === 3 && G.flames > 0; - if (ball.flame) G.flames--; - if (wsel === 2 && G.triples > 0) { // one serve, three balls: clones fanned ±6° - G.triples--; + const wid = wsel === 0 && !G.temp ? 'ball' : slotId(wsel), armed = slotStock(wsel) > 0; + ball.bomb = wid === 'bomb' && armed; + if (ball.bomb) spendSlot(wsel); + ball.flame = wid === 'fire' && armed; + if (ball.flame) spendSlot(wsel); + if (wid === 'triple' && armed) { // one serve, three balls: clones fanned ±6° + spendSlot(wsel); for (const deg of [-6, 6]) { const r = deg * Math.PI / 180, c = Math.cos(r), sn = Math.sin(r); // g: ball.g — a serve carries up to 2.3x gravity in topspin; clones integrated at flat @@ -278,7 +279,7 @@ function hitServe(sv, quality, aim = null) { vx: ball.vx * c - ball.vz * sn, vz: ball.vx * sn + ball.vz * c }); } } - if (wsel > 0 && slotStock(wsel) <= 0) G.weapon = 0; // that was the last tin — hand back to plain balls + // (spendSlot already hands you back plain balls when the last one goes) // tennis only, same reason as doHit's: a splatter serve lands at a shambler's feet, and the ring // sat there in yellow for 1.3 s saying nothing — right where the aim mark is trying to be read. if (sv.side === 'P' && G.mode === 'tennis') G.outMarker = { x: boxX, z: tzs, out: !inServeBox(boxX, tzs), t: 1.3 }; @@ -376,7 +377,9 @@ function pointTo(side, reason) { // Which bodies gate the hand-clear after a won game. The racket zombie keeps its residence, and the // ball machine is furniture — a standing threat you deal with WHEN YOU CHOOSE. Making either block // progression would turn "decide what to prioritise" into "clear the chore list". -const blocksClear = z => z.variant !== 'r' && z.variant !== 'm'; +// A barrel must NOT gate the hand-clear: it is optional loot, and making it block progression would +// turn "decide whether that is worth a shot" into a chore every time one is on court. +const blocksClear = z => !'rmo'.includes(z.variant); export function splatPoint(side, quiet = false) { if (side === 'P' && ++G.killCount < TUNE.killsPerPoint) return; // K kills bank one point; the rest just splatter @@ -414,6 +417,11 @@ export function splatPoint(side, quiet = false) { if (side === 'P' && G.runGames >= TUNE.machEvery && G.runGames % TUNE.machEvery === 0 && zombies.filter(z => z.variant === 'm').length < TUNE.machMax && !G.bossFight) spawnMachine(); + // ...and a weapon barrel on its own schedule. One at a time: two glowing pink things on court at + // once would out-shout everything the palette uses to say "this one is dangerous". + if (side === 'P' && G.runGames % TUNE.barrelEvery === 0 + && !zombies.some(z => z.variant === 'o') && !G.bossFight) + spawnBarrel(); // The wave does NOT auto-collapse anymore: the survivors stay and must be cleared BY HAND before // play advances — only no NEW shamblers spawn. When the last one is down, proceedAfterClear() // fires (see update()). If the winning shot already emptied the court, proceed at once. @@ -469,41 +477,66 @@ function startChangeover() { // no tins here anymore — weapons are won in the duel, never handed out on a schedule } -// ---- the courtside rack: five slots, PLAIN plus the four tins. Stock lives on G; slot 0 is -// infinite plain balls. selectWeapon is the tap/key target — an empty special can't be picked. -export const SLOTS = ['PLAIN', 'BOMB', 'TRIPLE', 'FIRE', 'GUN']; -export const slotStock = i => [Infinity, G.bombs, G.triples, G.flames, G.guns][i]; +// ---- the courtside rack. Five slots still, but only slot 0 is fixed: it is BALL (infinite), or a +// FIFTH gathered weapon riding it temporarily. Slots 1..4 are whatever you have picked up, in order. +// Everything reads through these three, so nothing else has to know how the rack is stored. +export const slotEntry = i => (i === 0 ? G.temp : G.rack[i - 1]) || null; +export const slotId = i => slotEntry(i)?.id || (i === 0 ? 'ball' : null); +export const slotStock = i => { const e = slotEntry(i); return e ? e.n : (i === 0 ? Infinity : 0); }; export function selectWeapon(i) { if (G.interlude) return; // the sudden-death duel is played under tennis rules — plain balls only - if (i > 0 && slotStock(i) <= 0) return; // dud: no tins of that kind on the rack - G.weapon = i; pock(0.3, 1100); + if (slotStock(i) <= 0) return; // dud: nothing in that slot (slot 0 is only ever empty if a spent + G.weapon = i; pock(0.3, 1100); // temp has just been cleared, and then it is BALL again) +} + +// Grant a weapon and equip it. Tops up what you already carry, else takes a free slot, else rides +// slot 0 as `temp` — Oliver's rule: a fifth is always allowed, because a barrel arms on the spot and +// is spent quickly, so refusing it would mean a barrel you shot did nothing. +export function grantWeapon(id, n = WEAPONS[id].tin) { + const have = G.rack.find(e => e.id === id); + if (have) { have.n = Math.min(TUNE.wCap, have.n + n); G.weapon = G.rack.indexOf(have) + 1; } + else if (G.rack.length < RACK_MAX) { G.rack.push({ id, n }); G.weapon = G.rack.length; } + else if (G.temp?.id === id) { G.temp.n = Math.min(TUNE.wCap, G.temp.n + n); G.weapon = 0; } + else { G.temp = { id, n }; G.weapon = 0; } + return G.weapon; +} + +// Spend one from a slot, and hand you back plain balls when it runs dry. The temp weapon does not +// leave an empty dimmed slot behind the way a rack entry does — slot 0 simply becomes BALL again. +export function spendSlot(i) { + const e = slotEntry(i); + if (!e) return; + if (--e.n > 0) return; + // Spent, so it LEAVES — a slot is for what you are carrying, and an exhausted weapon sitting in + // one is a quarter of the rack you can never pick anything up into. (The old fixed rack kept its + // empty slots dimmed, which was right when the slot meant "BOMB goes here" and is wrong now.) + if (i === 0) G.temp = null; // slot 0 goes back to being the infinite ball + else G.rack.splice(i - 1, 1); + G.weapon = 0; } // the weapon ladder: each delivery opens the next tin in the cycle and ADDS it to the rack // (capped per type at TUNE.wCap) — old stock is kept, so you can bank a bomb for the horde // instead of spending it now. G.weapon picks which tin the next serve throws. -const WDESC = { // what the tin you just won actually does — one line, shown on the pickup modal - BOMB: ['DETONATES ON CONTACT', 'CLEARS A WHOLE CROWD AT ONCE'], - TRIPLE: ['ONE SERVE, THREE BALLS', 'FANNED WIDE ACROSS THE LANE'], - FIRE: ['KILLS ON ANY TOUCH', 'AND SETS THE OTHERS ALIGHT'], - GUN: ['HOLD THE LINE AND SPRAY', 'A BURST, NOT A SERVE'], -}; +const TINS = ['bomb', 'triple', 'fire', 'gun']; // the duel ladder, in the order it hands them out export function deliverTin(healLine = null) { - const w = G.deliveries++ % 4, add = k => Math.min(TUNE.wCap, k); - if (w === 0) G.bombs = add(G.bombs + TUNE.bombMax); - else if (w === 1) G.triples = add(G.triples + TUNE.tinTriples); - else if (w === 2) G.flames = add(G.flames + TUNE.tinFlames); - else G.guns = add(G.guns + TUNE.tinGuns); // machine-gun charges — a burst you pop when you choose, not on arrival - if (G.weapon !== 0) G.weapon = w + 1; // equip the new tin — but never yank you off a deliberate PLAIN (banking) + const id = TINS[G.deliveries++ % TINS.length]; + const wasPlain = G.weapon === 0 && !G.temp; // never yank you off a deliberate PLAIN (banking) + grantWeapon(id); + if (wasPlain) G.weapon = 0; sendBoy(); voice('new_balls', 0.6); aside('ump_new_balls_dark', 2.0); // "...and a new opponent, apparently." - - const name = SLOTS[w + 1]; // healLine folds the trainer's visit INTO this popup. `showModal` deliberately never clobbers an // open modal (a kill cascade can try to open two in one frame), so when both happened the heal // modal opened first and the weapon one was lost entirely — Oliver: "the weapon message is lost". - showModal({ title: name, body: healLine ? [...WDESC[name], '', healLine] : WDESC[name], - icon: ['w_bomb', 'w_triple', 'w_fire', 'w_gun'][w], iconH: 40, color: UI.warm }); + weaponModal(id, healLine); +} + +// The one-per-run "what this thing does" popup, shared by the duel ladder and the barrels. +export function weaponModal(id, healLine = null) { + const w = WEAPONS[id]; + showModal({ title: w.label, body: healLine ? [...w.desc, '', healLine] : w.desc, + icon: w.icon, iconH: 40, color: UI.warm }); } // ---- the interlude: order is restored, one sudden-death point vs the racket zombie. @@ -845,16 +878,39 @@ function racketAI(rz, dt) { } } -// GUN in the rack isn't a ball — committing a charge starts an mgT auto-fire burst (the same -// mode a horde uses). With GUN selected, any serve gesture spends one charge instead of serving. -function tryGunBurst() { - if (G.mode !== 'splatter' || G.weapon !== 4 || G.guns <= 0) return false; - G.guns--; if (G.guns <= 0) G.weapon = 0; // spent the last tin — back to plain balls - G.mgT = TUNE.mgPickupLen; G.mgCool = 0; +// A GUN-like weapon isn't a ball — committing to one braces you at the baseline for a window (the +// same mode a horde uses) and the serve gesture spends it instead of serving. Generalised from +// tryGunBurst: 'burst' weapons auto-fire at their own rate for `len` seconds, 'shot' weapons hold +// the same stance but fire one per TAP until the magazine is out. G.mgT stays the mode timer for +// BOTH, which is why the eighteen `!(G.mgT > 0)` guards across five files did not have to change. +function tryArmGun() { + if (G.mode !== 'splatter') return false; + const id = slotId(G.weapon), w = WEAPONS[id]; + if (!w || (w.kind !== 'burst' && w.kind !== 'shot') || slotStock(G.weapon) <= 0) return false; + const slot = G.weapon; + G.gunId = id; + if (w.kind === 'burst') { G.mgT = w.len ?? TUNE.mgPickupLen; G.gunShots = 0; spendSlot(slot); } + // a 'shot' weapon spends per ROUND, not on commit, so the magazine IS the stock: hold the stance + // for as long as it could take to empty it, and gunShots ends the window early when it runs out. + else { G.gunShots = slotStock(slot); G.mgT = TUNE.shotHoldLen; } + G.mgCool = 0; ball.active = false; G.phase = 'rally'; P.pose = 'idle'; P.poseT = 0; P.swingT = -1; P.z = HOME.P.z; // brace at the baseline for the burst (finger steers x only) - G.flash = { text: 'GUN', t: 1.0 }; + G.flash = { text: w.label, t: 1.0 }; + return true; +} +// a tap while a 'shot' weapon is braced fires ONE round. Returns true if it consumed the tap. +export function tryGunShot() { + const w = WEAPONS[G.gunId]; + if (!(G.mgT > 0) || !w || w.kind !== 'shot' || G.gunShots <= 0) return false; + firePellet(w); + G.gunShots--; + // the magazine lives in the slot it came from, so a half-spent laser you never finish stays half + // spent on the rack rather than silently refilling + const slot = [0, 1, 2, 3, 4].find(i => slotId(i) === G.gunId); + if (slot !== undefined) spendSlot(slot); + if (G.gunShots <= 0) G.mgT = 0; // out of rounds: the window closes, updateZombies resumes serving return true; } @@ -877,7 +933,7 @@ export function dragServe(aim) { if (G.interlude || G.horde || G.mgT > 0 || G.changeover) return; // the duel serves like tennis, not the reticle if (G.phase === 'over') { ball.active = false; setupPoint(); } // mid-beat pull: arm the next shot now if (G.phase !== 'pretoss' && G.phase !== 'toss' && G.phase !== 'rally') return; - if (tryGunBurst()) return; + if (tryArmGun()) return; if (aim.mag < TUNE.pullMin) return; // released without really drawing: cancel, nothing spent // Firing while the last ball still flies is fine again — the brake is the pull itself, not the // rally clock. This is what lets the drag mean ONE thing at all times: a second gesture that @@ -919,6 +975,10 @@ export function keyPressed(code) { swish(0.3); return; } + // A braced 'shot' weapon (laser, rocket) fires ONE round per tap — that is the whole difference + // between it and a burst weapon, and it has to sit above the auto-fire guard below, which would + // otherwise swallow the tap as a swish. + if (tryGunShot()) return; if (G.horde || G.mgT > 0) return; // auto-fire modes: a tap only swishes, never serves if (G.changeover) return; // the breather is mandatory — the melee swish above still works if (G.interlude && G.interlude.introT > 0) return; // let the umpire finish announcing the duel @@ -932,7 +992,7 @@ export function keyPressed(code) { if (flick) { dragServe(flick); return; } // dragServe handles GUN (burst) and the ball check } if (G.phase === 'pretoss' && score.server === 'P') { - if (tryGunBurst()) return; // GUN selected: the serve gesture pops a burst, no ball + if (tryArmGun()) return; // a GUN-like weapon selected: the gesture arms it, no ball tossBall(P); return; // plain tap: the two-stage toss ritual (tennis, and the duel) } if (G.phase === 'toss' && score.server === 'P') { diff --git a/index.html b/index.html index 42ed132..77edb27 100644 --- a/index.html +++ b/index.html @@ -179,6 +179,6 @@ - + diff --git a/input.js b/input.js index 077d91b..3c589b3 100644 --- a/input.js +++ b/input.js @@ -4,9 +4,9 @@ import { cv } from './dom.js'; import { audio, pock } from './audio.js'; -import { TUNE } from './config.js'; +import { TUNE, WEAPONS } from './config.js'; import { G, score } from './state.js'; -import { keyPressed, dragServe, selectWeapon, serveToss, pullQuality, dismissModal } from './gameplay.js'; +import { keyPressed, dragServe, selectWeapon, serveToss, pullQuality, dismissModal, slotId } from './gameplay.js'; import { rackHit } from './render.js'; import { togglePanel } from './panel.js'; @@ -45,10 +45,14 @@ export function takeFlickAim(maxAgeMs = 450) { // (G.aim, drawn by render); release fires the serve there, dragging back home cancels. // EVERY phase of splatter play, so the drag never silently turns into something else. The old // "drag walks the player" mode between shots was invisible state and read as a bug. +// a weapon you UNLOAD (burst or shot) rather than serve — it takes the tap directly, so the +// slingshot reticle must stay out of its way. Was `G.weapon !== 4` back when slot 4 was always GUN. +const isGunLike = () => { const w = WEAPONS[slotId(G.weapon)]; return !!w && (w.kind === 'burst' || w.kind === 'shot'); }; + const canAimServe = () => !G.modal // the world is frozen behind a popup — the thumb that clears it must not draw a bow && G.mode === 'splatter' // every phase of it; 'eaten' is its own mode and never gets here - && score.server === 'P' && G.weapon !== 4 // GUN pops on tap, no reticle + && score.server === 'P' && !isGunLike() // a gun-like weapon pops on tap, no reticle && !G.interlude && !G.horde && !(G.mgT > 0) && !G.changeover; // the duel serves like tennis: no reticle let steerPX = null; // live absolute finger x (-1..1 across the canvas) — machine-gun steering reads it diff --git a/main.js b/main.js index 3327dd2..5cec911 100644 --- a/main.js +++ b/main.js @@ -5,10 +5,10 @@ import { TUNE, COURT, COURTS, UMP_H, UMP_X, EATEN_RESTART } from './config.js'; import { P, O, ball, score, G, setupPoint, selectCourt } from './state.js'; -import { update, restartRun, keyPressed, solveShot, inServeBox, inCourt, forceZombies, splatPoint, deliverTin, selectWeapon, showModal, dismissModal, startDuel, resolveInterlude, flickTarget, startBossFight, endBossFight } from './gameplay.js'; +import { update, restartRun, keyPressed, solveShot, inServeBox, inCourt, forceZombies, splatPoint, deliverTin, selectWeapon, grantWeapon, showModal, dismissModal, startDuel, resolveInterlude, flickTarget, startBossFight, endBossFight } from './gameplay.js'; import { fitViewport, render, proj, RES } from './render.js'; import { voice, ambient, menuMusic, menuDuck } from './audio.js'; -import { zombies, spawnZombie, spawnRacketZombie, spawnMachine, spawnChapterBoss, piles, gore, parts, pellets, extras, shots, explode, hurtPlayer, killPlayer } from './zombies.js'; +import { zombies, spawnZombie, spawnRacketZombie, spawnMachine, spawnChapterBoss, piles, gore, parts, pellets, extras, shots, explode, hurtPlayer, killPlayer , spawnBarrel } from './zombies.js'; import { cv } from './dom.js'; import './input.js'; @@ -184,6 +184,6 @@ cv.addEventListener('pointerdown', e => { requestAnimationFrame(frame); // exposed for in-browser sanity checks and simulated playtests -window.T = { TUNE, ball, P, O, score, G, zombies, spawnZombie, spawnRacketZombie, spawnMachine, spawnChapterBoss, piles, gore, parts, pellets, extras, shots, explode, hurtPlayer, killPlayer, splatPoint, solveShot, inServeBox, inCourt, proj, setupPoint, forceZombies, deliverTin, selectWeapon, showModal, dismissModal, startDuel, resolveInterlude, flickTarget, startBossFight, endBossFight, +window.T = { TUNE, ball, P, O, score, G, zombies, spawnZombie, spawnRacketZombie, spawnMachine, spawnChapterBoss, spawnBarrel, grantWeapon, piles, gore, parts, pellets, extras, shots, explode, hurtPlayer, killPlayer, splatPoint, solveShot, inServeBox, inCourt, proj, setupPoint, forceZombies, deliverTin, selectWeapon, showModal, dismissModal, startDuel, resolveInterlude, flickTarget, startBossFight, endBossFight, step: dt => update(dt), render, press: keyPressed, get phase() { return G.phase; }, set phase(v) { G.phase = v; } }; diff --git a/render.js b/render.js index 8b48ac4..ea89690 100644 --- a/render.js +++ b/render.js @@ -1,9 +1,9 @@ // Camera, viewport, sprites and all drawing. Owns the crowd dots (visual-only state). import { cv, ctx } from './dom.js'; -import { TUNE, UI, EATEN_GRAB, EATEN_BURST, EATEN_SUMMARY, EATEN_RESTART, COURT, P_BOUNDS, PLAYER_H, BOY_H, UMP_H, UMP_X, MACH_H, BOSS_ART, ZWOUND, woundStage, BOUNCE_IDLE, SPRITE_H, zombieH } from './config.js'; +import { TUNE, UI, EATEN_GRAB, EATEN_BURST, EATEN_SUMMARY, EATEN_RESTART, COURT, P_BOUNDS, PLAYER_H, BOY_H, UMP_H, UMP_X, MACH_H, BOSS_ART, ZWOUND, woundStage, BOUNCE_IDLE, SPRITE_H, zombieH , WEAPONS } from './config.js'; import { P, O, ball, score, crowd, G, court } from './state.js'; -import { predictLanding, previewServe } from './gameplay.js'; +import { predictLanding, previewServe , slotId, slotStock } from './gameplay.js'; import { zombies, gore, piles, parts, pellets, extras, shots } from './zombies.js'; // ---------------------------------------------------------------- palette ---- @@ -181,7 +181,7 @@ function drawRunOff() { // ---------------------------------------------------------------- sprites ---- // assets/.png; game falls back to code-drawn figures until sprites load. -const SPRV = 80; // asset cache-buster — bump with main.js?v=N (see the img.src note below) +const SPRV = 81; // asset cache-buster — bump with main.js?v=N (see the img.src note below) const SPR = {}; for (const n of ['p_idle1','p_idle2','p_bounce','p_bounce2','p_toss','p_smash','p_run1','p_run2','p_run3','p_run4','p_swing1','p_swing2','p_die1','p_die2','p_die3','p_die4','p_die5', 'p_pull', // the serve wind-up: the racket dropping behind him while the pull is drawn @@ -215,6 +215,8 @@ for (const n of ['p_idle1','p_idle2','p_bounce','p_bounce2','p_toss','p_smash',' 'z_c3','z_c4', // ...and the turned CPU walks on four frames too 'z_cw1','z_cw2','z_aw','z_bw', // wounded skins for multi-hit enemies (racket boss, brute, and now the classic shamblers) 'w_ball','w_bomb','w_triple','w_fire','w_gun', // weapon-rack icons + 'w_beacon','w_laser','w_rocket','w_flamer', // ...and the four that come out of a barrel + 'z_barrel1','z_barrel2', // the weapon barrel, two frames so it ROLLS 'scoreboard']) { const img = new Image(); img.onload = () => SPR[n] = img; @@ -324,6 +326,36 @@ const ZSTRIDE = { g: TUNE.bruteStride, k: TUNE.rollerStride }; // aimed down-court, and a second frame with the muzzle flashing. Oliver called the first, procedural // version "naively painted" — it was four rects, which is fine for fire but not for furniture the // player has to recognise and prioritise. +// The weapon barrel. The DRUM is a sprite (it is an object, and a procedural one would read as +// "naively painted" the way the machine's first version did); the GLOW is procedural, because light +// is one of the three things that rule explicitly exempts, along with blood and fire. It pulses, so +// it separates itself from the static ads and the crowd behind it at 20 px tall. +function drawBarrel(zb) { + const img = SPR.z_barrel1; + const h = TUNE.barrelH * (img ? img.height / SPRITE_H : 1); + const p = proj(zb.x, h * 0.5, zb.z); + const k = 0.55 + 0.45 * Math.sin((zb.roll || 0) * 3.1); // the pulse rides the roll, not a clock + ctx.save(); + ctx.globalAlpha = 0.20 + k * 0.30; // the halo + ctx.fillStyle = UI.pink; + ctx.beginPath(); ctx.arc(Math.round(p.x), Math.round(p.y), Math.max(3, p.s * 0.30 * (0.8 + k * 0.4)), 0, 7); ctx.fill(); + ctx.globalAlpha = 1; + ctx.restore(); + if (img) { + // two frames alternating off GROUND COVERED is what makes it read as rolling rather than sliding + const n = SPR.z_barrel2 && stepFrame((zb.roll || 0) / 0.5, 2) ? 'z_barrel2' : 'z_barrel1'; + drawStatic(n, zb.x, zb.z, TUNE.barrelH * (SPR[n].height / SPRITE_H)); + } else { // until the PNG lands, a lit drum + ctx.fillStyle = UI.pink; + const w = Math.max(2, p.s * 0.34), hh = Math.max(3, p.s * 0.44); + ctx.fillRect(Math.round(p.x - w / 2), Math.round(p.y - hh / 2), Math.round(w), Math.round(hh)); + } + ctx.globalAlpha = 0.5 + k * 0.5; // the core, brightest at the pulse peak + ctx.fillStyle = '#fff'; + ctx.beginPath(); ctx.arc(Math.round(p.x), Math.round(p.y), Math.max(1, p.s * 0.05), 0, 7); ctx.fill(); + ctx.globalAlpha = 1; +} + function drawMachine(zb) { const firing = zb.windT > 0; // It wears its damage like every other multi-hit body on court: dented at two thirds, split open @@ -348,6 +380,7 @@ function drawMachine(zb) { const p0 = zb => proj(zb.x, 0, zb.z).s; // projected scale, for effects that need pixels not metres function drawZombie(zb) { + if (zb.variant === 'o') return drawBarrel(zb); if (zb.variant === 'm') return drawMachine(zb); const rk = zb.variant === 'r'; const v = rk ? 'c' : zb.variant; // the racket zombie reuses the turned-CPU sprite (already holds a racket) @@ -735,20 +768,25 @@ export function rackHit(nx, ny) { // normalized [0,1] canvas coords -> slot inde } function drawRack() { const g = rackGeom(), pad = 3; - const stock = [Infinity, G.bombs, G.triples, G.flames, G.guns]; - const labels = ['BALL', 'BOMB', 'TRIPLE', 'FIRE', 'GUN']; - const WICONS = ['w_ball', 'w_bomb', 'w_triple', 'w_fire', 'w_gun']; // generated pixel-art icons + // Built from what you are CARRYING, not from three fixed arrays. Slot 0 is BALL unless a fifth + // gathered weapon is riding it (G.temp), and an empty slot is a blank socket rather than a + // permanently dimmed weapon you never had. + const W_ = i => WEAPONS[slotId(i)]; ctx.save(); ctx.lineWidth = 1; for (let i = 0; i < g.n; i++) { - const x = g.x0 + i * g.tw, sel = G.weapon === i, dim = stock[i] <= 0 && i > 0; + const w = W_(i), stockI = slotStock(i), temp = i === 0 && G.temp; + const x = g.x0 + i * g.tw, sel = G.weapon === i, dim = stockI <= 0; + const label = i === 0 && !temp ? 'BALL' : w ? w.label : ''; + const icon = i === 0 && !temp ? 'w_ball' : w ? w.icon : null; // rounded slots: five separate tiles read as five buttons, which is what they are panel(x + 1.5, g.y0 + 1.5, g.tw - 3, g.h - 3, 4, sel ? '#123a24' : '#0a2418', // sunk slot sel ? UI.pick : '#05160e'); // gold frame when live // icon: left, vertically centred, padded off the edge const box = g.h - pad * 2, icx = x + pad + box / 2, icy = g.y0 + g.h / 2; + if (!icon) { ctx.globalAlpha = 1; continue; } // an empty socket: nothing gathered for it yet ctx.globalAlpha = dim ? 0.35 : 1; - const spr = SPR[WICONS[i]]; + const spr = SPR[icon]; if (spr) { const k = Math.min(box / spr.width, box / spr.height), iw = spr.width * k, ih = spr.height * k; ctx.drawImage(spr, icx - iw / 2, icy - ih / 2, iw, ih); @@ -758,11 +796,12 @@ function drawRack() { ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.globalAlpha = dim ? 0.5 : 1; ctx.font = F(8); ctx.fillStyle = sel ? UI.pick : '#e8f0dc'; - ctx.fillText(i === 0 ? '∞' : '×' + stock[i], tx, g.y0 + g.h * 0.33); // ball = ∞, else ×count - ctx.font = F(6, 0); ctx.fillStyle = sel ? '#f2ead6' : '#8fa89a'; - ctx.fillText(labels[i], tx, g.y0 + g.h * 0.72); // weapon label, under its count + ctx.fillText(stockI === Infinity ? '∞' : '×' + stockI, tx, g.y0 + g.h * 0.33); // ball = ∞, else ×count + ctx.font = F(6, 0); ctx.fillStyle = sel ? '#f2ead6' : temp ? UI.pink : '#8fa89a'; + ctx.fillText(label, tx, g.y0 + g.h * 0.72); // weapon label, under its count ctx.globalAlpha = 1; - if (i === 4 && G.mgT > 0) { ctx.fillStyle = UI.pick; ctx.fillRect(x + 2, g.y0 + g.h - 2, (g.tw - 4) * Math.min(1, G.mgT / TUNE.mgPickupLen), 1.5); } // burst countdown + // the burst countdown belongs to whichever slot is holding the armed weapon now, not to slot 4 + if (G.mgT > 0 && slotId(i) === G.gunId) { ctx.fillStyle = UI.pick; ctx.fillRect(x + 2, g.y0 + g.h - 2, (g.tw - 4) * Math.min(1, G.mgT / (WEAPONS[G.gunId]?.len || TUNE.mgPickupLen)), 1.5); } } ctx.restore(); } @@ -1061,7 +1100,8 @@ export function render() { ctx.beginPath(); ctx.arc(pp.x | 0, pp.y | 0, r, 0, 7); ctx.fill(); continue; } - ctx.fillStyle = pt.fire ? (pt.t > 0.12 ? '#ffc030' : '#ff8010') + ctx.fillStyle = pt.pink ? (pt.t > 0.2 ? UI.pink : '#ff9df6') // a barrel bursting: debris, not gore + : pt.fire ? (pt.t > 0.12 ? '#ffc030' : '#ff8010') : pt.dark ? 'rgba(96,14,14,0.9)' : 'rgba(170,22,22,0.9)'; ctx.fillRect(pp.x | 0, pp.y | 0, 2, 2); } @@ -1069,10 +1109,19 @@ export function render() { // they take the perspective like everything else. A fixed 4x4 fillRect stayed exactly as big at // the far baseline as at your feet, which read as square confetti pinned to the screen rather than // as something travelling away from you. Radius is a shade over half the match ball's 0.09. + // A barrel weapon's round carries its own colour and size (see zombies.firePellet), so a beacon + // shell is a fat cyan ball and a laser bolt a thin magenta one — told apart at a glance, which + // matters because they are all the same projectile underneath. 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), r = Math.max(1, pp.s * 0.05); - ctx.fillStyle = UI.ball; ctx.beginPath(); ctx.arc(pp.x, pp.y, r, 0, 7); ctx.fill(); + const sc = (pe.r ?? TUNE.mgPelletR) / TUNE.mgPelletR; + const sh = proj(pe.x, 0, pe.z); drawShadow(sh.x, sh.y, Math.max(1, sh.s * 0.04 * sc), Math.max(1, sh.s * 0.02 * sc), 0.25); + const pp = proj(pe.x, pe.y, pe.z), r = Math.max(1, pp.s * 0.05 * sc); + if (pe.glow) { // the exotic rounds carry their own light, the plain pellet does not + ctx.globalAlpha = 0.35; ctx.fillStyle = pe.col || UI.ball; + ctx.beginPath(); ctx.arc(pp.x, pp.y, r * 2.1, 0, 7); ctx.fill(); + ctx.globalAlpha = 1; + } + ctx.fillStyle = pe.col || UI.ball; ctx.beginPath(); ctx.arc(pp.x, pp.y, r, 0, 7); ctx.fill(); ctx.fillStyle = '#fffbe0'; ctx.beginPath(); ctx.arc(pp.x, pp.y, Math.max(0.5, r * 0.45), 0, 7); ctx.fill(); // hot core: it reads sharp, not soft } // the chapter boss's shockwave: a ring of blood mist blown out through the crowd noise @@ -1199,17 +1248,20 @@ export function render() { 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]; + const selW = WEAPONS[slotId(G.weapon)], sel = selW ? selW.label : 'PLAIN'; + // "is the selected weapon one you UNLOAD rather than serve" — it used to be `G.weapon === 4`, + // which is meaningless once the slots hold whatever you gathered + const gunLike = selW && (selW.kind === 'burst' || selW.kind === 'shot'); // The DUEL is played under tennis rules — no slingshot, no rack, serve to the box (see the // !G.interlude guards in solveShot and the aim reticle). It was still being told to pull back // to aim, which is the one thing that does nothing there. // after a beat of inaction the apron demo takes over, and it says PULL DOWN in its own words — // so this slot shuts up rather than printing the same instruction a second time 40 px below it - const demo = G.mode === 'splatter' && !G.interlude && G.weapon !== 4 && G.idleHintT > TUNE.pullHintAfter; + const demo = G.mode === 'splatter' && !G.interlude && !gunLike && G.idleHintT > TUNE.pullHintAfter; const txt = G.mode === 'splatter' && !G.interlude - ? (G.weapon === 4 ? (touch ? 'TAP TO UNLOAD THE GUN' : 'SPACE UNLOADS THE GUN') + ? (gunLike ? (touch ? `TAP TO UNLOAD THE ${sel}` : `SPACE UNLOADS THE ${sel}`) : G.aim ? 'PULL DOWN: FURTHER = HARDER' - : G.weapon > 0 ? `SERVE FIRES: ${sel} · ${switchHint}` + : slotId(G.weapon) !== 'ball' ? `SERVE FIRES: ${sel} · ${switchHint}` : demo ? '' : touch ? 'PULL DOWN TO AIM · RELEASE TO FIRE' : 'DRAG DOWN TO AIM · SPACE SERVES') : G.serveNum === 2 ? (touch ? 'SECOND SERVE — TAP' : 'SECOND SERVE — SPACE') diff --git a/state.js b/state.js index 70bb977..d64fa62 100644 --- a/state.js +++ b/state.js @@ -45,15 +45,19 @@ export const G = { aiReactT: 9, // time since the player's last hit; CPU reads the shot after TUNE.aiReaction wasServe: false, // the ball in flight is a serve (first bounce must hit the box) splatT: 0, // time survived in splatter mode — drives the difficulty ramp - // the courtside rack: tins ACCUMULATE (capped at TUNE.wCap), and G.weapon picks which one - // the next serve throws. Slot 0 = PLAIN (a normal ball — how you bank your specials). - bombs: 0, // bomb-ball stock — a serve blasts an area (rack slot 1) - triples: 0, // triple-serve stock — a serve fans into three balls (slot 2) - flames: 0, // flame-ball stock — kills on any touch, full pierce (slot 3) - guns: 0, // machine-gun charges — committing one starts an mgT burst (slot 4) - weapon: 0, // selected rack slot: 0 PLAIN, 1 BOMB, 2 TRIPLE, 3 FIRE, 4 GUN - mgT: 0, // s of machine-gun burst left — auto-fire, no serving (spent from G.guns) - mgCool: 0, // s until the next pickup-mode pellet + // The courtside rack. Slots 1-4 are NOT fixed weapon types anymore: they hold what you have + // GATHERED, in pickup order, so which four you are carrying is a run-by-run thing. Stock still + // ACCUMULATES per weapon (capped at TUNE.wCap). Slot 0 stays BALL — infinite, and how you bank a + // special instead of spending it. + rack: [], // up to RACK_MAX { id, n } — the gathered weapons, in the order they arrived + temp: null, // { id, n } — a FIFTH weapon, riding slot 0 until it is spent. A barrel arms + // itself on the spot, so it always has somewhere to go even with a full rack. + weapon: 0, // selected slot: 0 = BALL (or temp, if one is riding it), 1..4 = G.rack[i-1] + mgT: 0, // s of braced auto-fire left — no serving while it runs (gun/beacon/flamer, + // and it doubles as the window a tap-fired laser or rocket is held for) + mgCool: 0, // s until the next pellet in that burst + gunId: null, // which WEAPONS row that burst is firing — rate, radius, damage, colour + gunShots: 0, // rounds left for a kind:'shot' weapon (laser 10, rocket 3); 0 ends the window deliveries: 0, // tins delivered this run — indexes the weapon ladder duelDue: false, // the schedule called a duel — fire it at the next hand-cleared court afterDuel: null, // the game beat the scheduled duel interrupted ('changeover' | 'game'), resumed when it resolves diff --git a/zombies.js b/zombies.js index d0e42cb..404d450 100644 --- a/zombies.js +++ b/zombies.js @@ -3,10 +3,10 @@ // your baseline — one crossing it is a point for THEM. // (zombies <-> gameplay is a runtime-only import cycle, same pattern as input.) -import { TUNE, UI, COURT, VOLLEY, CONTACT, BOSS_ART, woundStage, EATEN_BURST, EATEN_SUMMARY, EATEN_FEED_R } from './config.js'; +import { TUNE, UI, COURT, VOLLEY, CONTACT, BOSS_ART, WEAPONS, BARREL_WEAPONS, woundStage, EATEN_BURST, EATEN_SUMMARY, EATEN_FEED_R } from './config.js'; import { G, P, ball, sendBoy, setupPoint } from './state.js'; import { splat, moan, pock, voice, boom, swish, shriek, aww, sfx } from './audio.js'; -import { splatPoint, cheer, spentBall, resolveInterlude, showModal, endBossFight, aside } from './gameplay.js'; +import { splatPoint, cheer, spentBall, resolveInterlude, showModal, endBossFight, aside, grantWeapon, weaponModal } from './gameplay.js'; export const zombies = []; // {x, z, v, walkT, variant: 'a'|'b'|'c'} export const gore = []; // lawn decals {x, z, r, a} — the flat ground layer @@ -50,6 +50,11 @@ export function spawnZombie(x, z, variant) { if (zb.hp) zb.hpMax = zb.hp; if (variant === 's') zb.shieldT = 0; // chair up; > 0 = seconds of stagger left if ('fgsdehk'.includes(variant) && !G.seen['v' + variant]) { G.seen['v' + variant] = true; aside('ump_dress_code', 0.8); } + if (variant === 'o') { // the weapon barrel: a rolling pink target with a weapon inside it + zb.v = 0; zb.hp = zb.hpMax = TUNE.barrelHp; + zb.wid = BARREL_WEAPONS[Math.floor(Math.random() * BARREL_WEAPONS.length)]; + zb.dir = Math.random() < 0.5 ? -1 : 1; zb.roll = 0; + } if (variant === 'm') { // the rogue ball machine: furniture, not a body. It never SHAMBLES... zb.v = 0; zb.hp = zb.hpMax = TUNE.machHp; zb.fireT = TUNE.machCycle; zb.windT = 0; zb.repoT = TUNE.machRepo * (0.4 + Math.random()); // ...but it wheels itself somewhere else now and then @@ -57,6 +62,18 @@ export function spawnZombie(x, z, variant) { zombies.push(zb); } +// A WEAPON BARREL, rolled across the far court. Shoot it and you get what is inside, armed on the +// spot. It is the only source of the four weapons that are not on the duel ladder, and it is pure +// upside — it never attacks, never scores, and rolls off on its own if you ignore it. +export function spawnBarrel() { + spawnZombie((Math.random() * 2 - 1) * 3.2, 4.5 + Math.random() * 3, 'o'); + if (!G.seen.barrel) { + G.seen.barrel = true; + showModal({ title: 'A GLOWING BARREL', color: UI.pink, + body: ['THERE IS A WEAPON INSIDE IT', 'SHOOT IT AND IT IS YOURS', 'PINK MEANS TAKE THE SHOT'] }); + } +} + // the rogue ball machine, wheeled out onto the far court. It never moves and never scores; it just // shoots at you until you deal with it. export function spawnMachine() { @@ -249,6 +266,7 @@ function pickVariant() { // deliberately NOT one — melee/bomb/fire one-shotting a brute is a settled "power exception". const isBoss = zb => zb.variant === 'r' || zb.variant === 'B'; const hitR = zb => zb.variant === 'g' ? 0.75 : zb.variant === 'd' ? 0.64 : zb.variant === 'r' ? 0.7 + : zb.variant === 'o' ? 0.7 // the barrel: a wide low drum, generous on purpose : zb.variant === 'k' ? 0.88 : zb.variant === 'h' ? 0.68 // the roller man is wider than the brute : zb.variant === 'B' ? 0.55 * (zb.scale || 1) * 1.4 // a chapter boss is a barn door, as it should be : zb.variant === 'm' ? 0.8 // the machine is a wide low box @@ -257,7 +275,22 @@ const hitR = zb => zb.variant === 'g' ? 0.75 : zb.variant === 'd' ? 0.64 : zb.va // core kill: remove the zombie, spawn its gore/pile/particles, score. No ball coupling — // shared by ball hits, machine-gun pellets, melee swipes, and extra (multi-serve) balls. // how absurdly a variant dies: classics splash, the advanced roster DETONATES -const GORE_MUL = { a: 1, b: 1, e: 1, f: 1.8, s: 2.5, h: 2.5, g: 4, k: 5, r: 3, m: 1.5, B: 6 }; // a chapter boss comes apart like nothing else on the court +const GORE_MUL = { a: 1, b: 1, e: 1, f: 1.8, s: 2.5, h: 2.5, g: 4, k: 5, r: 3, m: 1.5, o: 0, B: 6 }; // a chapter boss comes apart like nothing else on the court; a barrel is not meat at all + +// A barrel bursts and hands you what was inside, armed. Pink debris rather than gore — it is a +// container, not a body — and the modal only fires the FIRST time you see each weapon, so the tenth +// rocket does not stop the world to explain itself. +function popBarrel(zb) { + boom(0.7); G.shake = { t: 0.22, mag: 4 }; G.boomFlash = 0.08; + for (let k = 0; k < 30; k++) parts.push({ + x: zb.x, y: 0.7, z: zb.z, + vx: (Math.random() * 2 - 1) * 4, vy: 1.2 + Math.random() * 3.5, vz: (Math.random() * 2 - 1) * 4, + t: 0.35 + Math.random() * 0.5, pink: true }); + grantWeapon(zb.wid); + const w = WEAPONS[zb.wid]; + G.flash = { text: w.label, t: 1.1 }; + if (!G.seen['w' + zb.wid]) { G.seen['w' + zb.wid] = true; weaponModal(zb.wid); } +} // dark smoke: one puff, drifting up and spreading. Not gravity-bound like gore (see the parts loop). function smokePuff(x, y, z, spread = 0.5) { @@ -305,6 +338,7 @@ function killZombie(i, quiet) { const mul = GORE_MUL[zb.variant] || 1; zombies.splice(i, 1); if (zb.variant === 'B') bossDeath(zb); // before endBossFight below, so the card can wait on it + if (zb.variant === 'o') { popBarrel(zb); return; } // a container: it pays out and scores NOTHING if (zb.variant === 'm') { // furniture, not a body: no blood, no mound, no mop boy wreckMachine(zb); if (!G.horde && !G.clearing) splatPoint('P', quiet); @@ -479,10 +513,18 @@ function spawnHordeZombie() { // all from the far baseline, across the court — spawnZombie((Math.random() * 2 - 1) * 5, -1.5 - Math.random(), v); } let mgTick = 0; -function firePellet() { - const a = (Math.random() * 2 - 1) * TUNE.mgSpread * Math.PI / 180, s = 26; // spread cone, straight up-court (-z) - pellets.push({ x: P.x, y: 0.9, z: P.z, vx: Math.sin(a) * s, vz: -Math.cos(a) * s, t: 1.2 }); - if (++mgTick % 2 === 0) pock(0.25, 1400); // rapid fire tick, every 2nd round — at 12+/s every round buzzes +// One round, for every barrel-fed weapon there is. The WEAPONS row is stamped onto the pellet, so +// the loop below serves the machine gun, the beacon cannon, the flamethrower, the laser and the +// rocket without a second array or a second update — the only things that ever differed were a +// radius, a damage number, whether it stops at the first body, and what it does when it lands. +// Called with no argument by the HORDE, which is the plain machine gun. +export function firePellet(w = WEAPONS.gun) { + const spread = w.life ? TUNE.mgSpread * 2.4 : TUNE.mgSpread; // a short-lived round is a CONE, not a line + const a = (Math.random() * 2 - 1) * spread * Math.PI / 180, s = w.speed ?? 26; // straight up-court (-z) + pellets.push({ x: P.x, y: 0.9, z: P.z, vx: Math.sin(a) * s, vz: -Math.cos(a) * s, + t: w.life ?? 1.2, r: w.r ?? TUNE.mgPelletR, dmg: w.dmg ?? TUNE.dmgPellet, hp: w.hp ?? TUNE.dmgPelletHp, + pierce: !!w.pierce, boom: !!w.boom, ignite: w.ignite ? (w.igniteR ?? 1.8) : 0, col: w.col, glow: w.glow }); + if (++mgTick % 2 === 0) pock(0.25, w.boom ? 420 : w.pierce ? 2100 : 1400); // every 2nd round — at 12+/s every round buzzes } export function spawnRacketZombie() { // one takes up residence on the far court — an opponent, of sorts // He stiffens as the run goes on. hpMax is stored because the wound skins and bleed() anchors read @@ -606,7 +648,10 @@ export function updateZombies(dt) { // moved the phase mid-burst (a duel calling setupPoint) froze mgT above zero forever — and // `G.mgT > 0` blocks serving, so the game locked up with the gun racket stuck on screen. G.mgT -= dt; - if (G.phase === 'rally' && (G.mgCool -= dt) <= 0) { firePellet(); G.mgCool = 1 / TUNE.mgRate; } + // whichever gun-like weapon is armed sets the rate and the round (see config.WEAPONS). A + // kind:'shot' weapon has no rate — it fires on the TAP instead (gameplay.tryGunShot). + const gw = WEAPONS[G.gunId] || WEAPONS.gun; + if (G.phase === 'rally' && gw.rate && (G.mgCool -= dt) <= 0) { firePellet(gw); G.mgCool = 1 / gw.rate; } if (G.mgT <= 0) setupPoint(); // ammo dry — the serve loop resumes } if (!G.horde && !G.changeover && !G.interlude && !G.clearing && !G.bossFight) { // breathers, duels, the mop-up after a won game AND a boss fight all get a quiet court (no new spawns) @@ -715,7 +760,14 @@ export function updateZombies(dt) { zb.face = away; continue; } - if (zb.variant === 'm') { // the machine wheels itself around instead of being bolted down + if (zb.variant === 'o') { // the barrel just rolls, turning at the sidelines. It never attacks. + if (!held && G.mode === 'splatter' && !G.interlude) { + zb.x += zb.dir * TUNE.barrelRoll * dt; + zb.roll += TUNE.barrelRoll * dt; // drawn spin, so it reads as ROLLING + if (Math.abs(zb.x) > 4.4) { zb.dir = -Math.sign(zb.x); zb.x = Math.sign(zb.x) * 4.4; } + } + } + else if (zb.variant === 'm') { // the machine wheels itself around instead of being bolted down if (!held && G.mode === 'splatter' && !G.interlude) { if (zb.rollTo !== undefined) { const d = zb.rollTo - zb.x; @@ -739,7 +791,7 @@ export function updateZombies(dt) { // CONTACT IS STILL INSTANT DEATH — settled decision, and the ball machine's chip damage does not // soften it. Two threats that demand two different responses is the point. The machine ('m') is // furniture and cannot eat you. - if (!held && G.mode === 'splatter' && zb.variant !== 'm' + if (!held && G.mode === 'splatter' && !'mo'.includes(zb.variant) // furniture and barrels cannot eat you && Math.hypot(zb.x - P.x, (zb.z - P.z) * 0.75) < TUNE.eatenR + (zb.variant === 'g' ? 0.2 : 0)) // z weighted down: depth is foreshortened, so a head-on shambler eats you when it LOOKS on top of you killPlayer(); if (ball.active && ball.lastHit === 'P' && ball.y < 1.9) { @@ -815,14 +867,19 @@ export function updateZombies(dt) { for (let j = zombies.length - 1; j >= 0; j--) { const zb = zombies[j]; if (!zb) continue; - if (Math.hypot(zb.x - pe.x, zb.z - pe.z) < hitR(zb) + TUNE.mgPelletR) { + if (Math.hypot(zb.x - pe.x, zb.z - pe.z) < hitR(zb) + (pe.r ?? TUNE.mgPelletR)) { // Pellets used to skip the boss entirely, so a GUN burst (and every horde) did literally // nothing to him with no feedback at all. They CHIP him now: dmgPellet is fractional, so // three rounds take a point off and the gun is a slow, committed answer rather than a no-op. - if (isBoss(zb)) { if (wound(zb, TUNE.dmgPellet, false)) killZombie(j, true); else pock(0.45, 240); } - else if ((zb.hp -= TUNE.dmgPelletHp) > 0) pock(0.5, 260); // only brutes soak now; a shambler drops to one round + // EVERY new weapon comes through here, which is why none of them can silently no-op on a + // boss the way three of the five once did. + if (pe.ignite) ignite(pe.x, pe.z, pe.ignite); // the flamer lights the spot, and fire spreads itself + if (isBoss(zb)) { if (wound(zb, pe.dmg ?? TUNE.dmgPellet, false)) killZombie(j, true); else pock(0.45, 240); } + else if ((zb.hp -= (pe.hp ?? TUNE.dmgPelletHp)) > 0) pock(0.5, 260); // only brutes soak now; a shambler drops to one round else killZombie(j, true); - hit = true; break; + if (pe.boom) { explode(pe.x, pe.z); hit = true; break; } // the rocket takes the crowd, not the body + hit = !pe.pierce; // a laser keeps going down the whole lane + if (hit) break; } } if (hit || pe.t <= 0) pellets.splice(i, 1);