← back to Games Agentabrams
Refine Baguette Brawl: combo system + damage scaling, impact juice, dizzy stars, smarter AI (re-inlined build)
4eb380db0ca07a032a235e3b963fb9458f4431c2 · 2026-08-31 13:16:50 -0700 · Steve Abrams
Files touched
M games/baguette-brawl/BaguetteBrawl.aaM games/baguette-brawl/index.html
Diff
commit 4eb380db0ca07a032a235e3b963fb9458f4431c2
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Aug 31 13:16:50 2026 -0700
Refine Baguette Brawl: combo system + damage scaling, impact juice, dizzy stars, smarter AI (re-inlined build)
---
games/baguette-brawl/BaguetteBrawl.aa | 81 +++++++++++++++++++++++++++++------
games/baguette-brawl/index.html | 81 +++++++++++++++++++++++++++++------
2 files changed, 134 insertions(+), 28 deletions(-)
diff --git a/games/baguette-brawl/BaguetteBrawl.aa b/games/baguette-brawl/BaguetteBrawl.aa
index e8ac114..2ef86e1 100644
--- a/games/baguette-brawl/BaguetteBrawl.aa
+++ b/games/baguette-brawl/BaguetteBrawl.aa
@@ -310,13 +310,14 @@ function makeFighter(x, facing, cfg){
swing:0, walkPhase:0,
color:cfg.color, beret:cfg.beret, name:cfg.name,
isAI:cfg.isAI||false, ctrl:cfg.ctrl,
- hitFlash:0, wins:0, _t:0, _think:0
+ hitFlash:0, wins:0, _t:0, _think:0, combo:0, comboT:0
};
}
let f1, f2, mode=1, difficulty='normal', state='menu';
let roundNum=1, roundTime=60, timeLeft=60, roundPhase='intro';
let freeze=0, shake=0, particles=[], clouds=[], stars=[], koTimer=null;
+let flash=0, flashCol='#ffffff', zoom=0, comboShow={n:0,x:W/2,t:0};
let showAnnounce='', announceT=0, countdownT=0, lastCount=99;
const AI_TUNE = {
@@ -342,14 +343,14 @@ function startGame(m){
function resetFighter(f,x,facing){
Object.assign(f,{x,y:GROUND,vx:0,vy:0,hp:100,meter:f.meter*0.5|0,
state:'idle',atk:null,atkT:0,atkDur:0,atkHit:false,hurtT:0,blockT:0,
- blocking:false,swing:0,facing,hitFlash:0,onGround:true});
+ blocking:false,swing:0,facing,hitFlash:0,onGround:true,combo:0,comboT:0});
}
function startRound(){
clearTimeout(koTimer); koTimer=null;
resEl.classList.add('hide'); menuEl.classList.add('hide'); pauseEl.classList.add('hide');
resetFighter(f1,300,1);
resetFighter(f2,660,-1);
- timeLeft=roundTime; particles=[]; freeze=0; shake=0;
+ timeLeft=roundTime; particles=[]; freeze=0; shake=0; flash=0; zoom=0; comboShow={n:0,x:W/2,t:0};
showAnnounce=`MANCHE ${roundNum}`; announceT=1.6; countdownT=3.2; lastCount=99;
roundPhase='intro';
state='fight';
@@ -443,6 +444,7 @@ function updateFighter(f, other, dt){
if(f.state==='hurt'){ f.hurtT-=dt; if(f.hurtT<=0) f.state='idle'; }
if(f.hitFlash>0) f.hitFlash-=dt;
if(f.blockT>0 && !f.blocking) f.blockT-=dt*.5;
+ if(f.comboT>0){ f.comboT-=dt; if(f.comboT<=0) f.combo=0; } // combo window lapses → reset
}
function swingCurve(p){
@@ -461,23 +463,34 @@ function landHit(attacker, target, a){
if(blocked){
dmg*=0.18; kb*=.5;
addMeter(target,4); // reward defense a little
+ attacker.combo=0; attacker.comboT=0; // string was guarded — pressure resets
sfx.block();
spawnParticles(target.x+target.facing*-20, target.y-70, 6, '#ffe7b0', 3);
shake=Math.max(shake,4); target.blockT=.2;
} else {
+ // combo scaling — each extra hit in a string does progressively less (curbs juggles)
+ attacker.combo++; attacker.comboT=1.1;
+ const scale = attacker.combo<=1 ? 1 : Math.max(0.5, 1-(attacker.combo-1)*0.12);
+ dmg = Math.round(dmg*scale);
+ if(attacker.combo>=2){ comboShow={n:attacker.combo, x:target.x, t:0.9}; }
a.unblock ? sfx.crush() : sfx.hit();
addMeter(target, a.unblock?0:6);
target.state='hurt'; target.hurtT=a.stun;
spawnParticles(target.x, target.y-70, a.unblock?26:16, attacker.color, a.unblock?9:6);
- spawnParticles(target.x, target.y-80, 8, '#fff3dd', 5);
+ spawnBurst(target.x, target.y-72, a.unblock?12:7, '#fff3dd', a.unblock?11:8, attacker.facing);
target.hitFlash=.18;
shake=Math.max(shake, a.dmg>10?11:6);
freeze=a.unblock?.11:(a.dmg>10?.07:.035);
+ if(a.unblock){ flash=Math.max(flash,.55); flashCol='#ffe7a0'; zoom=Math.max(zoom,.06); }
+ else if(a.dmg>10){ zoom=Math.max(zoom,.035); }
}
target.hp=Math.max(0,target.hp-dmg);
target.vx = attacker.facing*kb;
if(!blocked && a.dmg>10){ target.vy=-4-(a.unblock?3:0); target.onGround=false; }
- if(target.hp<=0){ target.state='ko'; target.vx=attacker.facing*5; target.vy=-8; target.onGround=false; sfx.ko(); }
+ if(target.hp<=0){
+ target.state='ko'; target.vx=attacker.facing*5; target.vy=-8; target.onGround=false;
+ flash=Math.max(flash,.7); flashCol='#ffffff'; zoom=Math.max(zoom,.08); sfx.ko();
+ }
}
// ---------------------------------------------------------------------------
@@ -492,15 +505,22 @@ function aiControl(f, o, dt){
let mv=0;
if(f.state==='attack'||f.state==='hurt'){ f.vx*=.85; return; }
+ const nearWall = f.x<130 || f.x>W-130;
+
+ // the Crust Crusher is UNBLOCKABLE — don't guard it, RETREAT out of its reach
+ if(o.state==='attack' && o.atk==='super' && dist<170){
+ if(f.onGround && Math.random()<T.blockCh) { f.vy=-12.5; f.onGround=false; } // hop away
+ f.vx=-dir*T.speed; return;
+ }
- // anti-air: opponent airborne & close → block or heavy up-swing
- if(!o.onGround && dist<120 && Math.random()<T.blockCh*0.5){ f.blocking=true; f.vx=0; return; }
+ // anti-air: opponent airborne & closing → guard
+ if(!o.onGround && o.vy>-2 && dist<120 && Math.random()<T.blockCh*0.6){ f.blocking=true; f.vx=0; return; }
- // block when opponent commits an attack in range
- if(o.state==='attack' && dist<130 && Math.random()<T.blockCh*0.18){ f.blocking=true; f.vx=0; return; }
+ // block a committed grounded heavy/jab in range (per-frame prob → reliable over startup)
+ if(o.state==='attack' && o.atk!=='super' && dist<130 && Math.random()<T.blockCh*0.22){ f.blocking=true; f.vx=0; return; }
- // fire super when charged & in range
- if(f.meter>=100 && dist<110 && Math.random()<T.superCh*0.1){ tryAttack(f,'super'); return; }
+ // fire super decisively: opponent stunned, or simply in range (scaled by difficulty)
+ if(f.meter>=100 && dist<115 && (o.state==='hurt' || Math.random()<T.superCh*0.14)){ tryAttack(f,'super'); return; }
const ideal=90;
if(dist>ideal+30) mv=dir;
@@ -519,7 +539,9 @@ function aiControl(f, o, dt){
// punish a whiffed opponent attack: dash in
if(o.state==='attack' && o.atkHit===false && dist<160 && dist>90 && Math.random()<T.whiff*0.1){ mv=dir; }
- if(f.onGround && Math.random()<T.jumpCh && dist<180){ f.vy=-13; f.onGround=false; }
+ // cornered & pressured → hop over instead of walking into the wall
+ if(nearWall && dist<150 && f.onGround && Math.random()<0.04){ f.vy=-13; f.onGround=false; mv=dir; }
+ else if(f.onGround && Math.random()<T.jumpCh && dist<180){ f.vy=-13; f.onGround=false; }
f.vx=mv*T.speed;
}
@@ -532,6 +554,13 @@ function spawnParticles(x,y,n,color,spd){
particles.push({x,y,vx:Math.cos(ang)*s,vy:Math.sin(ang)*s-2,life:1,color,r:Math.random()*3+1.5});
}
}
+// directional spark cone — sprays in the knockback direction (dir = attacker.facing)
+function spawnBurst(x,y,n,color,spd,dir){
+ for(let i=0;i<n;i++){
+ const ang=(-0.5+Math.random())*0.9, s=Math.random()*spd+2;
+ particles.push({x,y,vx:dir*Math.cos(ang)*s,vy:Math.sin(ang)*s-1.5,life:1,color,r:Math.random()*2.5+1.2});
+ }
+}
function updateParticles(dt){
for(let i=particles.length-1;i>=0;i--){
const p=particles[i];
@@ -626,9 +655,10 @@ function drawScene(){
ctx.strokeStyle='rgba(0,0,0,.18)'; ctx.stroke(); }
}
function draw(){
- drawScene();
ctx.save();
+ if(zoom>0){ const z=1+zoom; ctx.translate(W/2,H/2); ctx.scale(z,z); ctx.translate(-W/2,-H/2); } // impact punch-in
if(shake>0){ ctx.translate((Math.random()-.5)*shake,(Math.random()-.5)*shake); }
+ drawScene();
if(f1&&f2){
drawShadow(f1); drawShadow(f2);
// draw the fighter that's further back (higher on screen / smaller x-depth) first
@@ -639,10 +669,22 @@ function draw(){
ctx.globalAlpha=1;
ctx.restore();
+ if(flash>0){ ctx.save(); ctx.globalAlpha=Math.min(.6,flash); ctx.fillStyle=flashCol; ctx.fillRect(0,0,W,H); ctx.restore(); }
if(f1&&f2) drawHUD();
+ if(comboShow.t>0) drawCombo();
if(announceT>0) drawAnnounce();
if(countdownT>0 && countdownT<3.2 && roundPhase==='intro') drawCountdown();
}
+function drawCombo(){
+ ctx.save();
+ ctx.globalAlpha=Math.min(1,comboShow.t*2);
+ const rise=(0.9-comboShow.t)*26;
+ ctx.textAlign='center'; ctx.textBaseline='middle';
+ ctx.fillStyle='#ffe7a0'; ctx.font="italic 800 30px Georgia,serif";
+ ctx.shadowColor='rgba(0,0,0,.6)'; ctx.shadowBlur=10;
+ ctx.fillText(`COMBO ×${comboShow.n}`, Math.max(90,Math.min(W-90,comboShow.x)), 150-rise);
+ ctx.restore();
+}
function drawShadow(f){
const sc=f.onGround?1:0.6;
@@ -727,10 +769,17 @@ function drawFighter(f){
ctx.beginPath(); ctx.arc(21,eyY-1,1.1,0,7); ctx.fill();
}
ctx.strokeStyle='#5c3a1a'; ctx.lineWidth=2; ctx.beginPath();
- if(f.state==='attack'){ ctx.arc(14,eyY+13,4,0,Math.PI); }
+ if(f.state==='hurt'){ ctx.arc(14,eyY+13,3.2,Math.PI,2*Math.PI); } // "ow" mouth
+ else if(f.state==='attack'){ ctx.arc(14,eyY+13,4,0,Math.PI); }
else { ctx.moveTo(9,eyY+12); ctx.quadraticCurveTo(14,eyY+15,20,eyY+12); }
ctx.stroke();
+ // dizzy stars circling the head while stunned
+ if(f.state==='hurt'){
+ const t=performance.now()/220; ctx.fillStyle='#ffe7a0';
+ for(let i=0;i<3;i++){ const aa=t+i*2.094; spark(14+Math.cos(aa)*16, -bh-6+Math.sin(aa)*5, 3.4); }
+ }
+
// arm + weapon baguette
ctx.save();
ctx.translate(16,-bh/2-4);
@@ -763,6 +812,7 @@ function drawFighter(f){
ctx.restore();
}
function xeye(x,y){ ctx.beginPath(); ctx.moveTo(x-3,y-3); ctx.lineTo(x+3,y+3); ctx.moveTo(x+3,y-3); ctx.lineTo(x-3,y+3); ctx.stroke(); }
+function spark(x,y,r){ ctx.beginPath(); ctx.moveTo(x,y-r); ctx.lineTo(x+r*.3,y-r*.3); ctx.lineTo(x+r,y); ctx.lineTo(x+r*.3,y+r*.3); ctx.lineTo(x,y+r); ctx.lineTo(x-r*.3,y+r*.3); ctx.lineTo(x-r,y); ctx.lineTo(x-r*.3,y-r*.3); ctx.closePath(); ctx.fill(); }
function roundedLoaf(cx,topY,w,h){
const r=w/2; ctx.beginPath();
ctx.moveTo(cx-r,topY+r); ctx.arc(cx,topY+r,r,Math.PI,0);
@@ -878,6 +928,9 @@ function loop(now){
}
updateParticles(dt);
if(shake>0) shake=Math.max(0,shake-dt*40);
+ if(flash>0) flash=Math.max(0,flash-dt*2.2);
+ if(zoom>0) zoom =Math.max(0,zoom -dt*0.34);
+ if(comboShow.t>0) comboShow.t-=dt;
}
}
diff --git a/games/baguette-brawl/index.html b/games/baguette-brawl/index.html
index e8ac114..2ef86e1 100644
--- a/games/baguette-brawl/index.html
+++ b/games/baguette-brawl/index.html
@@ -310,13 +310,14 @@ function makeFighter(x, facing, cfg){
swing:0, walkPhase:0,
color:cfg.color, beret:cfg.beret, name:cfg.name,
isAI:cfg.isAI||false, ctrl:cfg.ctrl,
- hitFlash:0, wins:0, _t:0, _think:0
+ hitFlash:0, wins:0, _t:0, _think:0, combo:0, comboT:0
};
}
let f1, f2, mode=1, difficulty='normal', state='menu';
let roundNum=1, roundTime=60, timeLeft=60, roundPhase='intro';
let freeze=0, shake=0, particles=[], clouds=[], stars=[], koTimer=null;
+let flash=0, flashCol='#ffffff', zoom=0, comboShow={n:0,x:W/2,t:0};
let showAnnounce='', announceT=0, countdownT=0, lastCount=99;
const AI_TUNE = {
@@ -342,14 +343,14 @@ function startGame(m){
function resetFighter(f,x,facing){
Object.assign(f,{x,y:GROUND,vx:0,vy:0,hp:100,meter:f.meter*0.5|0,
state:'idle',atk:null,atkT:0,atkDur:0,atkHit:false,hurtT:0,blockT:0,
- blocking:false,swing:0,facing,hitFlash:0,onGround:true});
+ blocking:false,swing:0,facing,hitFlash:0,onGround:true,combo:0,comboT:0});
}
function startRound(){
clearTimeout(koTimer); koTimer=null;
resEl.classList.add('hide'); menuEl.classList.add('hide'); pauseEl.classList.add('hide');
resetFighter(f1,300,1);
resetFighter(f2,660,-1);
- timeLeft=roundTime; particles=[]; freeze=0; shake=0;
+ timeLeft=roundTime; particles=[]; freeze=0; shake=0; flash=0; zoom=0; comboShow={n:0,x:W/2,t:0};
showAnnounce=`MANCHE ${roundNum}`; announceT=1.6; countdownT=3.2; lastCount=99;
roundPhase='intro';
state='fight';
@@ -443,6 +444,7 @@ function updateFighter(f, other, dt){
if(f.state==='hurt'){ f.hurtT-=dt; if(f.hurtT<=0) f.state='idle'; }
if(f.hitFlash>0) f.hitFlash-=dt;
if(f.blockT>0 && !f.blocking) f.blockT-=dt*.5;
+ if(f.comboT>0){ f.comboT-=dt; if(f.comboT<=0) f.combo=0; } // combo window lapses → reset
}
function swingCurve(p){
@@ -461,23 +463,34 @@ function landHit(attacker, target, a){
if(blocked){
dmg*=0.18; kb*=.5;
addMeter(target,4); // reward defense a little
+ attacker.combo=0; attacker.comboT=0; // string was guarded — pressure resets
sfx.block();
spawnParticles(target.x+target.facing*-20, target.y-70, 6, '#ffe7b0', 3);
shake=Math.max(shake,4); target.blockT=.2;
} else {
+ // combo scaling — each extra hit in a string does progressively less (curbs juggles)
+ attacker.combo++; attacker.comboT=1.1;
+ const scale = attacker.combo<=1 ? 1 : Math.max(0.5, 1-(attacker.combo-1)*0.12);
+ dmg = Math.round(dmg*scale);
+ if(attacker.combo>=2){ comboShow={n:attacker.combo, x:target.x, t:0.9}; }
a.unblock ? sfx.crush() : sfx.hit();
addMeter(target, a.unblock?0:6);
target.state='hurt'; target.hurtT=a.stun;
spawnParticles(target.x, target.y-70, a.unblock?26:16, attacker.color, a.unblock?9:6);
- spawnParticles(target.x, target.y-80, 8, '#fff3dd', 5);
+ spawnBurst(target.x, target.y-72, a.unblock?12:7, '#fff3dd', a.unblock?11:8, attacker.facing);
target.hitFlash=.18;
shake=Math.max(shake, a.dmg>10?11:6);
freeze=a.unblock?.11:(a.dmg>10?.07:.035);
+ if(a.unblock){ flash=Math.max(flash,.55); flashCol='#ffe7a0'; zoom=Math.max(zoom,.06); }
+ else if(a.dmg>10){ zoom=Math.max(zoom,.035); }
}
target.hp=Math.max(0,target.hp-dmg);
target.vx = attacker.facing*kb;
if(!blocked && a.dmg>10){ target.vy=-4-(a.unblock?3:0); target.onGround=false; }
- if(target.hp<=0){ target.state='ko'; target.vx=attacker.facing*5; target.vy=-8; target.onGround=false; sfx.ko(); }
+ if(target.hp<=0){
+ target.state='ko'; target.vx=attacker.facing*5; target.vy=-8; target.onGround=false;
+ flash=Math.max(flash,.7); flashCol='#ffffff'; zoom=Math.max(zoom,.08); sfx.ko();
+ }
}
// ---------------------------------------------------------------------------
@@ -492,15 +505,22 @@ function aiControl(f, o, dt){
let mv=0;
if(f.state==='attack'||f.state==='hurt'){ f.vx*=.85; return; }
+ const nearWall = f.x<130 || f.x>W-130;
+
+ // the Crust Crusher is UNBLOCKABLE — don't guard it, RETREAT out of its reach
+ if(o.state==='attack' && o.atk==='super' && dist<170){
+ if(f.onGround && Math.random()<T.blockCh) { f.vy=-12.5; f.onGround=false; } // hop away
+ f.vx=-dir*T.speed; return;
+ }
- // anti-air: opponent airborne & close → block or heavy up-swing
- if(!o.onGround && dist<120 && Math.random()<T.blockCh*0.5){ f.blocking=true; f.vx=0; return; }
+ // anti-air: opponent airborne & closing → guard
+ if(!o.onGround && o.vy>-2 && dist<120 && Math.random()<T.blockCh*0.6){ f.blocking=true; f.vx=0; return; }
- // block when opponent commits an attack in range
- if(o.state==='attack' && dist<130 && Math.random()<T.blockCh*0.18){ f.blocking=true; f.vx=0; return; }
+ // block a committed grounded heavy/jab in range (per-frame prob → reliable over startup)
+ if(o.state==='attack' && o.atk!=='super' && dist<130 && Math.random()<T.blockCh*0.22){ f.blocking=true; f.vx=0; return; }
- // fire super when charged & in range
- if(f.meter>=100 && dist<110 && Math.random()<T.superCh*0.1){ tryAttack(f,'super'); return; }
+ // fire super decisively: opponent stunned, or simply in range (scaled by difficulty)
+ if(f.meter>=100 && dist<115 && (o.state==='hurt' || Math.random()<T.superCh*0.14)){ tryAttack(f,'super'); return; }
const ideal=90;
if(dist>ideal+30) mv=dir;
@@ -519,7 +539,9 @@ function aiControl(f, o, dt){
// punish a whiffed opponent attack: dash in
if(o.state==='attack' && o.atkHit===false && dist<160 && dist>90 && Math.random()<T.whiff*0.1){ mv=dir; }
- if(f.onGround && Math.random()<T.jumpCh && dist<180){ f.vy=-13; f.onGround=false; }
+ // cornered & pressured → hop over instead of walking into the wall
+ if(nearWall && dist<150 && f.onGround && Math.random()<0.04){ f.vy=-13; f.onGround=false; mv=dir; }
+ else if(f.onGround && Math.random()<T.jumpCh && dist<180){ f.vy=-13; f.onGround=false; }
f.vx=mv*T.speed;
}
@@ -532,6 +554,13 @@ function spawnParticles(x,y,n,color,spd){
particles.push({x,y,vx:Math.cos(ang)*s,vy:Math.sin(ang)*s-2,life:1,color,r:Math.random()*3+1.5});
}
}
+// directional spark cone — sprays in the knockback direction (dir = attacker.facing)
+function spawnBurst(x,y,n,color,spd,dir){
+ for(let i=0;i<n;i++){
+ const ang=(-0.5+Math.random())*0.9, s=Math.random()*spd+2;
+ particles.push({x,y,vx:dir*Math.cos(ang)*s,vy:Math.sin(ang)*s-1.5,life:1,color,r:Math.random()*2.5+1.2});
+ }
+}
function updateParticles(dt){
for(let i=particles.length-1;i>=0;i--){
const p=particles[i];
@@ -626,9 +655,10 @@ function drawScene(){
ctx.strokeStyle='rgba(0,0,0,.18)'; ctx.stroke(); }
}
function draw(){
- drawScene();
ctx.save();
+ if(zoom>0){ const z=1+zoom; ctx.translate(W/2,H/2); ctx.scale(z,z); ctx.translate(-W/2,-H/2); } // impact punch-in
if(shake>0){ ctx.translate((Math.random()-.5)*shake,(Math.random()-.5)*shake); }
+ drawScene();
if(f1&&f2){
drawShadow(f1); drawShadow(f2);
// draw the fighter that's further back (higher on screen / smaller x-depth) first
@@ -639,10 +669,22 @@ function draw(){
ctx.globalAlpha=1;
ctx.restore();
+ if(flash>0){ ctx.save(); ctx.globalAlpha=Math.min(.6,flash); ctx.fillStyle=flashCol; ctx.fillRect(0,0,W,H); ctx.restore(); }
if(f1&&f2) drawHUD();
+ if(comboShow.t>0) drawCombo();
if(announceT>0) drawAnnounce();
if(countdownT>0 && countdownT<3.2 && roundPhase==='intro') drawCountdown();
}
+function drawCombo(){
+ ctx.save();
+ ctx.globalAlpha=Math.min(1,comboShow.t*2);
+ const rise=(0.9-comboShow.t)*26;
+ ctx.textAlign='center'; ctx.textBaseline='middle';
+ ctx.fillStyle='#ffe7a0'; ctx.font="italic 800 30px Georgia,serif";
+ ctx.shadowColor='rgba(0,0,0,.6)'; ctx.shadowBlur=10;
+ ctx.fillText(`COMBO ×${comboShow.n}`, Math.max(90,Math.min(W-90,comboShow.x)), 150-rise);
+ ctx.restore();
+}
function drawShadow(f){
const sc=f.onGround?1:0.6;
@@ -727,10 +769,17 @@ function drawFighter(f){
ctx.beginPath(); ctx.arc(21,eyY-1,1.1,0,7); ctx.fill();
}
ctx.strokeStyle='#5c3a1a'; ctx.lineWidth=2; ctx.beginPath();
- if(f.state==='attack'){ ctx.arc(14,eyY+13,4,0,Math.PI); }
+ if(f.state==='hurt'){ ctx.arc(14,eyY+13,3.2,Math.PI,2*Math.PI); } // "ow" mouth
+ else if(f.state==='attack'){ ctx.arc(14,eyY+13,4,0,Math.PI); }
else { ctx.moveTo(9,eyY+12); ctx.quadraticCurveTo(14,eyY+15,20,eyY+12); }
ctx.stroke();
+ // dizzy stars circling the head while stunned
+ if(f.state==='hurt'){
+ const t=performance.now()/220; ctx.fillStyle='#ffe7a0';
+ for(let i=0;i<3;i++){ const aa=t+i*2.094; spark(14+Math.cos(aa)*16, -bh-6+Math.sin(aa)*5, 3.4); }
+ }
+
// arm + weapon baguette
ctx.save();
ctx.translate(16,-bh/2-4);
@@ -763,6 +812,7 @@ function drawFighter(f){
ctx.restore();
}
function xeye(x,y){ ctx.beginPath(); ctx.moveTo(x-3,y-3); ctx.lineTo(x+3,y+3); ctx.moveTo(x+3,y-3); ctx.lineTo(x-3,y+3); ctx.stroke(); }
+function spark(x,y,r){ ctx.beginPath(); ctx.moveTo(x,y-r); ctx.lineTo(x+r*.3,y-r*.3); ctx.lineTo(x+r,y); ctx.lineTo(x+r*.3,y+r*.3); ctx.lineTo(x,y+r); ctx.lineTo(x-r*.3,y+r*.3); ctx.lineTo(x-r,y); ctx.lineTo(x-r*.3,y-r*.3); ctx.closePath(); ctx.fill(); }
function roundedLoaf(cx,topY,w,h){
const r=w/2; ctx.beginPath();
ctx.moveTo(cx-r,topY+r); ctx.arc(cx,topY+r,r,Math.PI,0);
@@ -878,6 +928,9 @@ function loop(now){
}
updateParticles(dt);
if(shake>0) shake=Math.max(0,shake-dt*40);
+ if(flash>0) flash=Math.max(0,flash-dt*2.2);
+ if(zoom>0) zoom =Math.max(0,zoom -dt*0.34);
+ if(comboShow.t>0) comboShow.t-=dt;
}
}
← 63c4a52 Add Baguette Brawl to the arcade (games/baguette-brawl, sing
·
back to Games Agentabrams
·
Baguette Brawl: add Training mode (re-inlined build) 0b14433 →