[object Object]

← back to Neon Baguette Battle

Weapon loadout: Space=poke, Return=throw baguette, R=rocket, B=bomb

e6c507144a080c7b6bf4432fbfd587f846be5208 · 2026-08-31 14:06:11 -0700 · Steve Abrams

- Generalized projectiles into 3 kinds: baguette (missile), rocket (fast+big, costs meter, nose/fins/flame art), bomb (lobbed arc, explodes with splash damage + boom)
- Per-weapon cooldowns for baguette/bomb; rocket gated on the special meter (bar relabeled ROCKET READY)
- P1 keys: Space/F=poke, G=whack, Return/H=throw baguette, R=rocket, B=bomb (+modifier fallbacks). P2: ,/./ / ; '
- AI throws baguettes/bombs at range, rockets when charged; new boom SFX + French voice lines
- Verified each key spawns the correct projectile (0 errors)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Files touched

Diff

commit e6c507144a080c7b6bf4432fbfd587f846be5208
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Aug 31 14:06:11 2026 -0700

    Weapon loadout: Space=poke, Return=throw baguette, R=rocket, B=bomb
    
    - Generalized projectiles into 3 kinds: baguette (missile), rocket (fast+big, costs meter, nose/fins/flame art), bomb (lobbed arc, explodes with splash damage + boom)
    - Per-weapon cooldowns for baguette/bomb; rocket gated on the special meter (bar relabeled ROCKET READY)
    - P1 keys: Space/F=poke, G=whack, Return/H=throw baguette, R=rocket, B=bomb (+modifier fallbacks). P2: ,/./ / ; '
    - AI throws baguettes/bombs at range, rockets when charged; new boom SFX + French voice lines
    - Verified each key spawns the correct projectile (0 errors)
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
 README.md  |  16 ++++---
 index.html | 155 ++++++++++++++++++++++++++++++++++++++++++++-----------------
 2 files changed, 122 insertions(+), 49 deletions(-)

diff --git a/README.md b/README.md
index 2e3e034..0edc58d 100644
--- a/README.md
+++ b/README.md
@@ -31,9 +31,11 @@ also click a card to choose P1's fighter. `Enter`/`Space` or **START FIGHT** beg
 | Move | `A` / `D` | `←` / `→` |
 | Jump | `W` | `↑` |
 | Block | `S` | `↓` |
-| Poke (fast, weak) | `F` | `,` |
-| Swing (slow, strong, knockback) | `G` | `.` |
-| Crumb Blast (projectile, needs full meter) | `H` | `/` |
+| Poke with baguette (fast) | `Space` / `F` | `,` |
+| Whack (slow, strong) | `G` | `.` |
+| Throw baguette 🥖 (projectile) | `Return` / `H` | `/` |
+| Rocket 🚀 (fast + big, needs full meter) | `R` | `;` |
+| Bomb 💣 (lobbed, explodes) | `B` | `'` |
 | Pause | `P` | |
 | Mute (audio + voice) | `M` | |
 
@@ -45,9 +47,11 @@ otherwise it does a whack. (`J`/`K`/`L` also jab/whack/launch.) In 2-player the
 arrows belong to Player 2.
 
 ## Features
-- **Real combat**: startup/active/recovery attack frames, blocking with chip
-  damage, knockback, hit-stun, a build-up special meter, and a **launched
-  baguette missile** (spinning projectile) once the meter is full.
+- **Real combat + a weapon loadout**: baguette **poke** and **whack** (melee),
+  plus three throwables — **throw baguette** (spinning missile, short cooldown),
+  **bomb** (lobbed, arcs and explodes with splash damage), and **rocket** (fast,
+  big, costs the full special meter). Startup/active/recovery frames, blocking
+  with chip damage, knockback, hit-stun.
 - **CPU opponent** with an approach/attack/block/retreat/blast state machine.
 - **Flying food hazards**: pies (cream splat, big damage), french rolls (fast),
   and croissants arc in from off-screen and hit either fighter.
diff --git a/index.html b/index.html
index dd0d0d8..cefe7ed 100644
--- a/index.html
+++ b/index.html
@@ -110,9 +110,11 @@
           <tr><td>Move</td><td><span class="key">A</span><span class="key">D</span></td></tr>
           <tr><td>Jump</td><td><span class="key">W</span></td></tr>
           <tr><td>Block</td><td><span class="key">S</span></td></tr>
-          <tr><td>Jab (baguette)</td><td><span class="key">F</span></td></tr>
-          <tr><td>Whack (baguette)</td><td><span class="key">G</span></td></tr>
-          <tr><td>Launch baguette 🚀</td><td><span class="key">H</span></td></tr>
+          <tr><td>Poke (baguette)</td><td><span class="key">Space</span><span class="key">F</span></td></tr>
+          <tr><td>Whack</td><td><span class="key">G</span></td></tr>
+          <tr><td>Throw baguette 🥖</td><td><span class="key">Return</span><span class="key">H</span></td></tr>
+          <tr><td>Rocket 🚀</td><td><span class="key">R</span></td></tr>
+          <tr><td>Bomb 💣</td><td><span class="key">B</span></td></tr>
         </table>
       </div>
       <div class="ctl p2"><h3>🥖 Player 2</h3>
@@ -120,13 +122,15 @@
           <tr><td>Move</td><td><span class="key">←</span><span class="key">→</span></td></tr>
           <tr><td>Jump</td><td><span class="key">↑</span></td></tr>
           <tr><td>Block</td><td><span class="key">↓</span></td></tr>
-          <tr><td>Jab (baguette)</td><td><span class="key">,</span></td></tr>
-          <tr><td>Whack (baguette)</td><td><span class="key">.</span></td></tr>
-          <tr><td>Launch baguette 🚀</td><td><span class="key">/</span></td></tr>
+          <tr><td>Poke (baguette)</td><td><span class="key">,</span></td></tr>
+          <tr><td>Whack</td><td><span class="key">.</span></td></tr>
+          <tr><td>Throw baguette 🥖</td><td><span class="key">/</span></td></tr>
+          <tr><td>Rocket 🚀</td><td><span class="key">;</span></td></tr>
+          <tr><td>Bomb 💣</td><td><span class="key">'</span></td></tr>
         </table>
       </div>
     </div>
-    <div class="tiny">1-Player also: <span class="key">←</span><span class="key">→</span> move · <span class="key">↑</span> jump · <span class="key">↓</span> block · <span class="key">Space</span> <span class="key">Return</span> <span class="key">⌘</span> <span class="key">⌥</span> <span class="key">Ctrl</span> <span class="key">⌫</span> fire &nbsp;·&nbsp; <span class="key">P</span> pause · <span class="key">M</span> mute</div>
+    <div class="tiny">1-Player also: <span class="key">←</span><span class="key">→</span> move · <span class="key">↑</span> jump · <span class="key">↓</span> block · <span class="key">Space</span> poke · <span class="key">Return</span> throw · <span class="key">R</span> rocket · <span class="key">B</span> bomb &nbsp;·&nbsp; <span class="key">P</span> pause · <span class="key">M</span> mute</div>
   </div>
 
   <!-- CHARACTER SELECT -->
@@ -242,6 +246,7 @@
       block: () => tone(240, .1, 'triangle', .14),
       hurt:  () => { noise(.16,.22); tone(140,.15,'sawtooth',.12,70); },
       blast: () => { tone(660,.25,'sawtooth',.16,120); noise(.2,.16); },
+      boom:  () => { tone(90,.4,'sawtooth',.2,40); noise(.35,.3); },
       jump:  () => tone(300,.12,'sine',.1,540),
       ko:    () => { tone(120,.5,'sawtooth',.2,50); noise(.4,.2); },
       bell:  () => { tone(880,.5,'sine',.12); tone(1320,.5,'sine',.06); },
@@ -286,6 +291,9 @@
       hurt:   () => say(rnd(["Oh non!","Mon dieu!","Sacrebleu!","Ze pain!","Aïe!"]), {pitch:1.25, rate:1.05}),
       block:  () => say(rnd(["Non non non!","Not today!"]), {pitch:1.15}),
       blast:  () => say(rnd(["Baguette missile! Allez!","Fire ze baguette!","Incoming! Ha ha!"]), {rate:0.95, pitch:1.05, force:true}),
+      baguette: () => say(rnd(["Throw ze baguette!","Catch, mon ami!","Voilà!"]), {pitch:1.1, force:true}),
+      rocket:   () => say(rnd(["Rocket! Allez!","Boom!","Fire ze rocket!"]), {rate:0.95, force:true}),
+      bomb:     () => say(rnd(["Bombe!","Boom boom!","Ha! Ze bomb!"]), {pitch:1.15, force:true}),
       round:  () => say("Allez!", {force:true, pitch:1.1}),
       fight:  () => say("Fight, mon ami!", {force:true, pitch:1.15}),
       ko:     () => say("Oh la la! Magnifique!", {force:true, pitch:1.05}),
@@ -299,16 +307,19 @@
 
   // ---------- Input ----------
   const keys = {};
-  // maps are arrays so an action can have several keys. 'fire' = launch missile if meter full, else whack.
+  // maps are arrays so an action can have several keys.
+  //   poke = baguette jab · swing = whack · baguette = throw baguette · rocket = launch rocket (needs meter) · bomb = lob bomb
   const P1_ATTACK = {
-    poke:  ['f','j'],
-    swing: ['g','k'],
-    blast: ['h','l'],
-    fire:  [' ','enter','backspace','delete','meta','control','alt','shift']   // Space/Return/Delete/⌘/Ctrl/⌥/⇧
+    poke:     ['f','j',' ','shift'],                 // Space = poke with baguette
+    swing:    ['g','k'],                             // whack
+    baguette: ['h','enter','backspace','delete'],    // Return = throw baguette
+    rocket:   ['r','meta','control'],                // R = rocket
+    bomb:     ['b','alt'],                           // B = bomb
   };
   const P1MAP_1P  = { left:['a','arrowleft'], right:['d','arrowright'], jump:['w','arrowup'], block:['s','arrowdown'], ...P1_ATTACK };
   const P1MAP_2P  = { left:['a'], right:['d'], jump:['w'], block:['s'], ...P1_ATTACK };  // arrows belong to P2 in 2P
-  const P2MAP     = { left:['arrowleft'], right:['arrowright'], jump:['arrowup'], block:['arrowdown'], poke:[','], swing:['.'], blast:['/'] };
+  const P2MAP     = { left:['arrowleft'], right:['arrowright'], jump:['arrowup'], block:['arrowdown'],
+                      poke:[','], swing:['.'], baguette:['/'], rocket:[';'], bomb:["'"] };
   let   p1map     = P1MAP_1P;
   addEventListener('keydown', e => {
     const k = e.key.toLowerCase();
@@ -375,6 +386,7 @@
       this.animT = Math.random()*10;
       this.aiTimer = 0; this.aiIntent = 'idle';
       this.armSwing = 0;                // visual arm extension 0..1
+      this.cd = { baguette:0, bomb:0 }; // per-weapon cooldowns (rocket uses the meter)
     }
     get top(){ return this.y - this.h; }
     get cx(){ return this.x; }
@@ -395,12 +407,15 @@
 
     startAttack(type){
       if (this.attack || this.state==='hurt' || this.state==='ko' || !this.onGround) return;
-      if (type === 'blast'){
-        if (this.meter < 100) return;
-        this.meter = 0;
-        spawnBlast(this);
-        Audio.blast(); Voice.blast();
-        this.attack = { type:'blast', phase:'recover', t:0, recover:16 };
+      const THROW = { baguette:14, rocket:16, bomb:15 };   // recover frames per thrown weapon
+      if (type in THROW){
+        if (type === 'rocket'){ if (this.meter < 100) return; this.meter = 0; }  // rocket costs the meter
+        else { if (this.cd[type] > 0) return; this.cd[type] = (type==='baguette' ? 48 : 90); } // cooldown
+        spawnProjectile(this, type);
+        if (type==='bomb'){ Audio.swing(); Voice.bomb(); }
+        else if (type==='rocket'){ Audio.blast(); Voice.rocket(); }
+        else { Audio.blast(); Voice.baguette(); }
+        this.attack = { type, phase:'recover', t:0, recover:THROW[type] };
         this.state = 'poke'; this.stateTime = 0; this.armSwing = 1;
         return;
       }
@@ -453,10 +468,11 @@
       if (down(map.block) && this.onGround && !this.attack){ this.state = 'block'; }
       else if (this.state==='block'){ this.state = 'idle'; }
       if (hit(map.jump) && this.onGround && !this.attack){ this.vy = -16; this.onGround = false; Audio.jump(); }
-      if (hit(map.poke))  this.startAttack('poke');
-      if (hit(map.swing)) this.startAttack('swing');
-      if (hit(map.blast)) this.startAttack('blast');
-      if (hit(map.fire))  this.startAttack(this.meter >= 100 ? 'blast' : 'swing');  // Space / ⌘ / Ctrl
+      if (hit(map.poke))     this.startAttack('poke');
+      if (hit(map.swing))    this.startAttack('swing');
+      if (hit(map.baguette)) this.startAttack('baguette');
+      if (hit(map.rocket))   this.startAttack('rocket');
+      if (hit(map.bomb))     this.startAttack('bomb');
     }
 
     controlAI(foe){
@@ -498,10 +514,11 @@
           break;
         case 'attack':
           if (adist < 100) this.startAttack(Math.random()<0.55?'poke':'swing');
+          else if (adist < 300 && Math.random()<0.4) this.startAttack(Math.random()<0.6?'baguette':'bomb');
           else if (!this.attack) this.vx = this.facing * speed;
           break;
         case 'blast':
-          if (this.meter>=100) this.startAttack('blast');
+          if (this.meter>=100) this.startAttack('rocket'); else this.startAttack('baguette');
           this.aiIntent='approach'; break;
       }
     }
@@ -509,11 +526,13 @@
     update(foe){
       this.animT += 0.16;
       if (this.invuln>0) this.invuln--;
+      if (this.cd.baguette>0) this.cd.baguette--;
+      if (this.cd.bomb>0) this.cd.bomb--;
       // attack phase machine
       if (this.attack){
         this.attack.t++;
         const a = this.attack;
-        if (a.type==='blast'){
+        if (a.type==='baguette' || a.type==='rocket' || a.type==='bomb'){
           if (a.t >= a.recover){ this.attack=null; if(this.state!=='hurt') this.state='idle'; }
         } else {
           if (a.phase==='startup' && a.t>=a.startup){ a.phase='active'; a.t=0; }
@@ -624,7 +643,7 @@
       if (this.state==='swing'){                 // overhead whack that sweeps down
         bang = -2.1 + armExt*2.7;
         hx = sx + 6 + Math.cos(bang)*6; hy = sy + 2 + Math.sin(bang)*6;
-      } else if (this.state==='poke' || (this.attack&&this.attack.type==='blast')){
+      } else if (this.state==='poke' || (this.attack && (this.attack.type==='baguette'||this.attack.type==='rocket'||this.attack.type==='bomb'))){
         hx = sx + 12 + armExt*26; hy = sy + 8; bang = -0.12;   // thrust / throw
       } else if (this.state==='block'){
         hx = sx - 2; hy = sy + 4; bang = -1.45;                // raised guard
@@ -633,7 +652,7 @@
       }
       g.strokeStyle=shirt; g.lineWidth=9; g.lineCap='round';
       g.beginPath(); g.moveTo(sx, sy); g.lineTo(hx, hy); g.stroke();
-      const hideBag = this.attack && this.attack.type==='blast';   // baguette is airborne as a missile
+      const hideBag = this.attack && this.attack.type==='baguette';   // baguette left the hand as a missile
       if (!hideBag) drawBaguetteWeapon(g, hx, hy, bang, 46, true);
       g.fillStyle=skin; g.beginPath(); g.arc(hx, hy, 5, 0, Math.PI*2); g.fill();
 
@@ -703,6 +722,28 @@
     g.restore();
   }
 
+  // rocket drawn pointing +x (caller flips by facing)
+  function drawRocket(g, r){
+    g.fillStyle='#e6e6ee'; roundRectPath(g, -r, -8, r*1.7, 16, 6); g.fill();
+    g.lineWidth=1.5; g.strokeStyle='rgba(0,0,0,.25)'; roundRectPath(g, -r, -8, r*1.7, 16, 6); g.stroke();
+    g.fillStyle='#ff4d4d'; g.beginPath(); g.moveTo(r*0.7,-8); g.lineTo(r*0.7+18,0); g.lineTo(r*0.7,8); g.closePath(); g.fill(); // nose
+    g.fillStyle='#4d9dff';
+    g.beginPath(); g.moveTo(-r,-8); g.lineTo(-r-9,-15); g.lineTo(-r+7,-8); g.closePath(); g.fill();   // fins
+    g.beginPath(); g.moveTo(-r, 8); g.lineTo(-r-9, 15); g.lineTo(-r+7, 8); g.closePath(); g.fill();
+    g.fillStyle='#22e0ff'; g.beginPath(); g.arc(r*0.05,0,4,0,Math.PI*2); g.fill();                    // window
+  }
+  // bomb drawn spinning; t drives the fuse-spark flicker
+  function drawBomb(g, r, t){
+    g.shadowColor='rgba(0,0,0,.5)'; g.shadowBlur=6;
+    g.fillStyle='#141414'; g.beginPath(); g.arc(0,0,r*0.72,0,Math.PI*2); g.fill();
+    g.shadowBlur=0;
+    g.fillStyle='rgba(255,255,255,.18)'; g.beginPath(); g.arc(-r*0.22,-r*0.22,r*0.22,0,Math.PI*2); g.fill();
+    g.fillStyle='#5a4a2a'; g.fillRect(-3,-r*0.82,6,5);                                                 // fuse cap
+    g.strokeStyle='#8a5a2a'; g.lineWidth=2; g.beginPath(); g.moveTo(0,-r*0.82); g.quadraticCurveTo(7,-r-2,12,-r*0.72); g.stroke();
+    g.fillStyle=(Math.floor(t/3)%2)?'#ffd166':'#ff6a3d'; g.shadowColor='#ffb347'; g.shadowBlur=8;
+    g.beginPath(); g.arc(12,-r*0.72,3.2,0,Math.PI*2); g.fill(); g.shadowBlur=0;
+  }
+
   function roundRectPath(g,x,y,w,h,r){
     r = Math.max(0, Math.min(r, w/2, h/2));    // never negative (thin cobble rows)
     g.beginPath();
@@ -716,9 +757,15 @@
 
   // ---------- Projectiles & particles ----------
   let blasts = [], sparks = [], hazards = [], hazardTimer = 140, shake = 0;
-  function spawnBlast(owner){
-    blasts.push({ x: owner.x + owner.facing*owner.w*0.5, y: owner.y - owner.h*0.62,
-                  vx: owner.facing*19, r: 34, owner, life: 90, t:0, rot: 0 });  // fast + big
+  function spawnProjectile(owner, kind){
+    const f = owner.facing, base = { owner, kind, t:0, rot:0, hit:false };
+    if (kind === 'rocket'){
+      blasts.push({ ...base, x: owner.x + f*owner.w*0.5, y: owner.y - owner.h*0.55, vx: f*26, vy:0, grav:0, r:30, dmg:22, kb:18, life:80 });
+    } else if (kind === 'bomb'){
+      blasts.push({ ...base, x: owner.x + f*owner.w*0.4, y: owner.y - owner.h*0.9, vx: f*9, vy:-11, grav:0.45, spin:f*0.3, r:20, dmg:14, kb:14, life:220 });
+    } else { // baguette missile (fast + big)
+      blasts.push({ ...base, x: owner.x + f*owner.w*0.5, y: owner.y - owner.h*0.62, vx: f*19, vy:0, grav:0, r:34, dmg:16, kb:16, life:90 });
+    }
   }
   function spawnSpark(x,y,color,n){
     for (let i=0;i<n;i++){
@@ -728,29 +775,51 @@
   }
   function updateProjectiles(f1,f2){
     for (const b of blasts){
-      b.t++; b.x += b.vx; b.rot += 0.7*Math.sign(b.vx); b.life--;
+      b.t++; b.x += b.vx;
+      if (b.grav){ b.vy += b.grav; b.y += b.vy; }
+      b.rot += (b.spin != null ? b.spin : 0.7*Math.sign(b.vx));
+      b.life--;
       const foe = b.owner === f1 ? f2 : f1;
       const hb = foe.hurtbox();
-      if (b.x+b.r > hb.x && b.x-b.r < hb.x+hb.w && b.y+b.r>hb.y && b.y-b.r<hb.y+hb.h){
-        if (foe.takeHit(18 * b.owner.powF, 16, Math.sign(b.vx))) { b.life = 0; spawnSpark(b.x,b.y,'#ffb347',18); }
+      const overlap = (b.x+b.r > hb.x && b.x-b.r < hb.x+hb.w && b.y+b.r>hb.y && b.y-b.r<hb.y+hb.h);
+      if (b.kind === 'bomb'){
+        if (overlap || b.y >= GROUND-1 || b.life<=0){ explodeBomb(b, foe); b.life=0; }
+      } else {
+        if (overlap){ if (foe.takeHit(b.dmg * b.owner.powF, b.kb, Math.sign(b.vx))){ b.life=0; spawnSpark(b.x,b.y,'#ffb347',18); } }
+        if (b.x < ARENA_L-60 || b.x > ARENA_R+60) b.life = 0;
       }
-      if (b.x < ARENA_L-40 || b.x > ARENA_R+40) b.life = 0;
     }
     blasts = blasts.filter(b => b.life>0);
     for (const s of sparks){ s.x+=s.vx; s.y+=s.vy; s.vy+=0.25; s.life--; }
     sparks = sparks.filter(s => s.life>0);
   }
+  function explodeBomb(b, foe){
+    spawnSpark(b.x, b.y, '#ffd166', 26); spawnSpark(b.x, b.y, '#ff6a3d', 18); spawnSpark(b.x, b.y, '#ffffff', 8);
+    shake = Math.min(22, shake + 11); Audio.boom();
+    const d = Math.hypot(foe.x - b.x, (foe.y - foe.h*0.5) - b.y);
+    if (d < 96) foe.takeHit(b.dmg * b.owner.powF * (1 - d/150), 15, Math.sign(foe.x - b.x || b.vx));
+  }
   function drawProjectiles(g){
     for (const b of blasts){
       const c = b.owner.colors;
       g.save();
-      // motion trail
-      g.fillStyle = c.glow;
-      for (let i=1;i<=6;i++){ g.globalAlpha = 0.30/i; g.beginPath(); g.arc(b.x-b.vx*i*0.9, b.y, 12-i, 0, Math.PI*2); g.fill(); }
-      // the big spinning baguette missile
-      g.globalAlpha = 1;
-      g.translate(b.x, b.y); g.rotate(b.rot); g.scale(1.5, 1.5);
-      drawBaguetteWeapon(g, -33, 0, 0, 66, true);   // centered on flight path
+      if (b.kind === 'rocket'){
+        g.fillStyle = '#ff9a3d';
+        for (let i=1;i<=6;i++){ g.globalAlpha=0.32/i; g.beginPath(); g.arc(b.x-b.vx*i*0.7, b.y, 11-i, 0, Math.PI*2); g.fill(); }
+        g.globalAlpha=1; g.translate(b.x, b.y); g.scale(Math.sign(b.vx), 1);
+        drawRocket(g, b.r);
+      } else if (b.kind === 'bomb'){
+        g.translate(b.x, b.y);
+        g.fillStyle='rgba(130,130,140,.4)';
+        for (let i=1;i<=4;i++){ g.globalAlpha=0.32/i; g.beginPath(); g.arc(-b.vx*i*0.6, -b.vy*i*0.3, 7-i, 0, Math.PI*2); g.fill(); }
+        g.globalAlpha=1; g.rotate(b.rot);
+        drawBomb(g, b.r, b.t);
+      } else {  // baguette missile
+        g.fillStyle = c.glow;
+        for (let i=1;i<=6;i++){ g.globalAlpha=0.30/i; g.beginPath(); g.arc(b.x-b.vx*i*0.9, b.y, 12-i, 0, Math.PI*2); g.fill(); }
+        g.globalAlpha=1; g.translate(b.x, b.y); g.rotate(b.rot); g.scale(1.5, 1.5);
+        drawBaguetteWeapon(g, -33, 0, 0, 66, true);
+      }
       g.restore();
     }
     for (const s of sparks){
@@ -1022,7 +1091,7 @@
     if (val>=100){ g.shadowColor=c.glow; g.shadowBlur=10; }
     g.fillRect(bx,y,bw,h); g.shadowBlur=0;
     if (val>=100){ g.fillStyle=c.accent; g.font='700 9px Georgia'; g.textAlign=flip?'right':'left';
-      g.fillText('BAGUETTE MISSILE READY', flip?x+w:x, y+18); g.textAlign='left'; }
+      g.fillText('ROCKET READY', flip?x+w:x, y+18); g.textAlign='left'; }
   }
   function drawPips(g,x,y,n,flip){
     for (let i=0;i<2;i++){
@@ -1304,7 +1373,7 @@
 
   buildRoster();
   // tiny test hook (only when the URL ends in #debug) — no effect on normal play
-  if (location.hash === '#debug') window.__bb = { get f1(){ return f1; }, get f2(){ return f2; }, get state(){ return state; } };
+  if (location.hash === '#debug') window.__bb = { get f1(){ return f1; }, get f2(){ return f2; }, get state(){ return state; }, get blasts(){ return blasts; } };
   // La Marseillaise on load — try immediately; browsers gate autoplay, so also fire on the first interaction
   Audio.startAnthem();
   const kickAnthem = () => Audio.startAnthem();

← a39d05a Broaden P1 controls: arrows move; Space/Return/Cmd/Option/Ct  ·  back to Neon Baguette Battle  ·  Apply graphic-designer review fixes #1-5 5e37c3a →