← Worms results

Worms

GPT 5.6 Sol · composer

Initial view of Worms

Reviews & generation details

Reviews

No human review yet. Unreviewed does not mean unsuccessful.

Generation context

Imported historical artifact. Its exact original instructions and lineage are not known.

Generation prompt · exact text unavailable

See the current prompt for reference. It may differ from the instructions used to generate this artifact.

Inspect original source 31,414 bytes · SHA-256 b02998ce7531
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pocket Armageddon — Artillery Arena</title>
<style>
/* ── css: base ── */
@import url('https://fonts.googleapis.com/css2?family=Black+Han+Sans&family=Nunito:wght@600;800;900&display=swap');
*{box-sizing:border-box}html,body,#app,#gameShell{margin:0;width:100%;height:100%;overflow:hidden}body{background:#09131f;font-family:Nunito,system-ui,sans-serif;color:#fff;user-select:none}#gameShell{position:relative;background:linear-gradient(#4cc9ff,#c2f0ff)}#game{display:block;width:100%;height:100%;cursor:crosshair}.overlay{position:absolute;inset:0;display:none;align-items:center;justify-content:center;background:radial-gradient(circle at 50% 30%,rgba(20,64,90,.45),rgba(4,12,22,.88));backdrop-filter:blur(7px);z-index:20}.overlay.show{display:flex}.panel{background:linear-gradient(145deg,rgba(19,35,50,.97),rgba(7,17,28,.98));border:1px solid rgba(255,255,255,.16);box-shadow:0 30px 90px #0009, inset 0 1px #ffffff20;border-radius:28px}.hero{width:min(620px,92vw);padding:38px 46px 30px;text-align:center}.logo span{display:block;font-weight:900;letter-spacing:9px;color:#9eeaff;font-size:15px}.logo b{display:block;font:clamp(40px,7vw,66px)/.9 'Black Han Sans',sans-serif;letter-spacing:-2px;background:linear-gradient(#ffe768,#ff8a28);-webkit-background-clip:text;color:transparent;filter:drop-shadow(0 5px #a63820)}.logo em{display:block;margin:14px 0 28px;color:#b6cad6;font-style:normal}.menuGrid{display:grid;grid-template-columns:1fr 1fr;gap:12px;text-align:left}.menuGrid label{font-size:11px;letter-spacing:1.4px;color:#87a9bb;font-weight:900}.menuGrid select{display:block;width:100%;margin-top:5px;padding:12px;border-radius:10px;border:1px solid #ffffff18;background:#122b3b;color:white;font:800 14px Nunito}.check{grid-column:1/-1;display:flex;align-items:center;gap:10px;padding:9px}.check input{accent-color:#ffbd38}.primary{border:0;border-radius:14px;background:linear-gradient(#ffd650,#ff8e27);box-shadow:0 7px 0 #a83d1d,0 13px 24px #0007;color:#41210d;font:900 16px Nunito;letter-spacing:1px;padding:16px 30px;margin:22px 0 10px;cursor:pointer;transition:.15s}.primary:hover{transform:translateY(-2px);filter:brightness(1.1)}.primary:active{transform:translateY(5px);box-shadow:0 2px #a83d1d}.menuFoot{font-size:12px;color:#6f8997}
/* ── css: hud ── */
#topHud{position:absolute;z-index:5;left:20px;right:20px;top:16px;display:flex;align-items:flex-start;justify-content:space-between;pointer-events:none;text-shadow:0 2px 4px #0019}#teamCard,#wind,#timer{background:linear-gradient(#142b3be8,#071723e8);border:1px solid #ffffff22;box-shadow:0 6px 24px #0018,inset 0 1px #ffffff20;border-radius:14px;padding:10px 15px}#teamCard{display:flex;align-items:center;gap:10px;min-width:175px}#teamDot{width:17px;height:17px;border-radius:50%;box-shadow:0 0 15px currentColor}#teamCard b,#teamCard small{display:block}#teamCard b{font-size:14px}#teamCard small{font-size:11px;color:#a8c0ce}#wind{position:absolute;left:50%;transform:translateX(-50%);display:grid;grid-template-columns:auto 100px auto;gap:9px;align-items:center;font-size:10px;letter-spacing:1px}.windTrack{height:5px;background:#ffffff20;border-radius:5px;position:relative}.windTrack i{position:absolute;left:50%;top:-4px;width:12px;height:12px;background:#dff8ff;clip-path:polygon(0 0,100% 50%,0 100%);transform-origin:center}#timer{text-align:center;min-width:74px}#timer span{font-size:9px;display:block;color:#8fb2c5;letter-spacing:2px}#timer b{font:28px/1 'Black Han Sans'}#timer.danger{animation:timerPulse .5s infinite;color:#ff665c}@keyframes timerPulse{50%{transform:scale(1.13);box-shadow:0 0 30px #ff332288}}#weaponBar{position:absolute;z-index:6;right:20px;bottom:48px;display:flex;align-items:center;gap:10px;background:#071723e8;border:1px solid #ffffff20;border-radius:15px;padding:9px 13px;box-shadow:0 8px 30px #0019;pointer-events:none}#weaponBar .icon{font-size:27px}#weaponBar b,#weaponBar small{display:block}#weaponBar small{color:#8db0c2;font-size:10px;letter-spacing:1px}#hintBar{position:absolute;z-index:5;bottom:13px;left:50%;transform:translateX(-50%);display:flex;gap:18px;color:#d9edf5;font-size:11px;font-weight:800;text-shadow:0 2px 4px #000;white-space:nowrap}#toast{position:absolute;z-index:15;left:50%;top:24%;transform:translate(-50%,-20px);opacity:0;font:32px 'Black Han Sans';color:#fff;text-shadow:0 4px #2b3b4a,0 0 20px #000;transition:.25s;pointer-events:none}#toast.show{opacity:1;transform:translate(-50%,0)}#crosshair{display:none;position:absolute;color:#fff;font-size:32px;text-shadow:0 0 8px #000;pointer-events:none}.arsenal{width:min(760px,94vw);padding:25px}.arsenal header{display:flex;justify-content:space-between;align-items:center}.arsenal h2{margin:2px 0 18px}.arsenal small{color:#72d9fa;letter-spacing:2px}.arsenal header button{background:none;border:0;color:#fff;font-size:32px;cursor:pointer}.weaponGrid{display:grid}#weaponGrid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}.weaponChoice{border:1px solid #ffffff18;background:#102838;border-radius:14px;padding:15px 8px;color:#fff;cursor:pointer;text-align:center;transition:.15s}.weaponChoice:hover{transform:translateY(-3px);background:#17425a;border-color:#60d8ff}.weaponChoice i{display:block;font-style:normal;font-size:34px}.weaponChoice b{display:block;font-size:12px}.weaponChoice small{font-size:9px;color:#8faebe}.resultPanel{text-align:center;padding:45px 65px}.resultPanel small{letter-spacing:4px;color:#8fdfff}.resultPanel h1{font:48px 'Black Han Sans';margin:10px} @media(max-width:650px){.menuGrid{grid-template-columns:1fr}.check{grid-column:auto}#weaponGrid{grid-template-columns:repeat(2,1fr)}#hintBar span:nth-child(-n+3){display:none}.hero{padding:28px 22px}#wind{top:65px}.logo b{font-size:42px}}
</style>
</head>
<body>
<div id="gameShell"><canvas id="game"></canvas><div id="topHud"><div id="teamCard"><span id="teamDot"></span><div><b id="teamName">TEAM</b><small id="wormName">Worm</small></div></div><div id="wind"><span>WIND</span><div class="windTrack"><i id="windArrow"></i></div><b id="windValue">0</b></div><div id="timer"><span>TURN</span><b id="timeValue">45</b></div></div><div id="weaponBar"></div><div id="hintBar"><span>← → Move</span><span>↑ ↓ Aim</span><span>SPACE Charge / Fire</span><span>J Jump</span><span>W Weapons</span></div><div id="toast"></div><div id="menu" class="overlay show"><div class="panel hero"><div class="logo"><span>POCKET</span><b>ARMAGEDDON</b><em>tiny worms. huge consequences.</em></div><div class="menuGrid"><label>TEAMS<select id="teamCount"><option value="2">2 teams</option><option value="3">3 teams</option><option value="4">4 teams</option></select></label><label>WORLD<select id="terrainType"><option value="random">Surprise me</option><option value="island">Single island</option><option value="twins">Twin islands</option><option value="cave">Crystal cave</option></select></label><label>TURN TIME<select id="turnLength"><option value="45">45 seconds</option><option value="60">60 seconds</option><option value="30">30 seconds</option></select></label><label class="check"><input id="cpuToggle" type="checkbox" checked=""><span></span>CPU controls rival teams</label></div><button id="startBtn" class="primary">DROP INTO BATTLE</button><div class="menuFoot">Hot-seat ready · toggle CPU off for local multiplayer</div></div></div><div id="weaponPanel" class="overlay"><div class="panel arsenal"><header><div><small>TACTICAL LOADOUT</small><h2>Choose your trouble</h2></div><button id="closeWeapons">×</button></header><div id="weaponGrid"></div></div></div><div id="result" class="overlay"><div class="panel resultPanel"><small>BATTLE COMPLETE</small><h1 id="winnerText">Team wins!</h1><button id="againBtn" class="primary">REMATCH</button></div></div><div id="crosshair">+</div></div>
<script>
'use strict';

// ── const: WORLD ──
const WORLD={w:2400,h:1100,water:900,gravity:520};

// ── const: TEAM_DATA ──
const TEAM_DATA=[{name:'CORAL COMMANDOS',color:'#ff5964',dark:'#9f2634'},{name:'MINT MISFITS',color:'#48e0a4',dark:'#187f60'},{name:'GOLD GOBLINS',color:'#ffd34e',dark:'#a96b14'},{name:'VIOLET VANDALS',color:'#bd7cff',dark:'#6738a0'}];

// ── const: WEAPONS ──
const WEAPONS=[
{id:'bazooka',name:'Bazooka',icon:'🚀',cat:'INDIRECT',desc:'Reliable rocket',power:72,radius:68,damage:55,heavy:false},
{id:'grenade',name:'Bouncy Betty',icon:'🍍',cat:'INDIRECT',desc:'3-second fuse',power:58,radius:75,damage:62,fuse:3},
{id:'cluster',name:'Cluster Pop',icon:'🎆',cat:'INDIRECT',desc:'Splits into bomblets',power:58,radius:48,damage:30,cluster:true},
{id:'shotgun',name:'Scattergun',icon:'💥',cat:'DIRECT / INSTANT',desc:'Two close blasts',power:38,radius:35,damage:34,instant:true},
{id:'drill',name:'Mole Missile',icon:'🪱',cat:'MAP-ALTERING',desc:'Burrows through earth',power:64,radius:62,damage:50,drill:true},
{id:'airstrike',name:'Sky Sardines',icon:'🐟',cat:'HIGH-AOE',desc:'Five fish from above',power:44,radius:58,damage:38,airstrike:true},
{id:'teleport',name:'Blink Beacon',icon:'🌀',cat:'UTILITY',desc:'Teleport to cursor',utility:'teleport'},
{id:'bubble',name:'Bubble Shield',icon:'🫧',cat:'UTILITY',desc:'Float & resist damage',utility:'shield'}];

// ── let: G ──
let G=null;

// ── let: AUDIO ──
let AUDIO=null;

// ── const: clamp ──
const clamp=(v,a,b)=>Math.max(a,Math.min(b,v));

// ── const: rnd ──
const rnd=(a,b)=>a+Math.random()*(b-a);

// ── const: lerp ──
const lerp=(a,b,t)=>a+(b-a)*t;

// ── function: noise1 ──
function noise1(x,seed){return Math.sin(x*.007+seed)*52+Math.sin(x*.019+seed*2.1)*24+Math.sin(x*.041+1.7)*10;}

// ── function: terrainSolid ──
function terrainSolid(x,y){if(!G||x<0||x>=WORLD.w||y<0||y>=WORLD.h)return false;return G.tctx.getImageData(x|0,y|0,1,1).data[3]>20;}

// ── function: findGround ──
function findGround(x){for(let y=G&&G.type==='cave'?430:80;y<WORLD.water;y+=3)if(terrainSolid(x,y))return y;return WORLD.water-10;}

// ── function: makeTerrain ──
function makeTerrain(type){const c=document.createElement('canvas');c.width=WORLD.w;c.height=WORLD.h;const x=c.getContext('2d',{willReadFrequently:true});const seed=rnd(0,30);x.clearRect(0,0,c.width,c.height);let top=[];for(let px=0;px<WORLD.w;px+=4){let y=560+noise1(px,seed);if(type==='island'){const edge=Math.min(px,WORLD.w-px);y+=Math.max(0,320-edge*.55)}else if(type==='twins'){const gap=Math.abs(px-WORLD.w/2);if(gap<180)y=950-Math.pow(gap/180,2)*80;else y=570+noise1(px,seed)+(px<120||px>2280?230:0)}else if(type==='cave')y=690+noise1(px,seed)*.65;top.push([px,y])}x.beginPath();x.moveTo(0,WORLD.h);for(const p of top)x.lineTo(p[0],p[1]);x.lineTo(WORLD.w,WORLD.h);x.closePath();const grad=x.createLinearGradient(0,450,0,1050);grad.addColorStop(0,'#79d46b');grad.addColorStop(.06,'#3c9c58');grad.addColorStop(.07,'#78543e');grad.addColorStop(.55,'#483a3d');grad.addColorStop(1,'#29313b');x.fillStyle=grad;x.fill();x.save();x.globalAlpha=.18;for(let i=0;i<900;i++){const px=rnd(0,WORLD.w),py=rnd(560,1080);if(x.getImageData(px|0,py|0,1,1).data[3]){x.fillStyle=i%3?'#e5b174':'#182b30';x.fillRect(px,py,rnd(2,8),rnd(2,7))}}x.restore();if(type==='cave'){x.globalCompositeOperation='destination-out';x.beginPath();x.ellipse(1200,570,930,260,0,0,Math.PI*2);x.fill();x.globalCompositeOperation='source-over';x.fillStyle='#304655';for(let i=0;i<45;i++){const px=rnd(280,2120),py=rnd(270,330),len=rnd(25,80);x.beginPath();x.moveTo(px-12,py);x.lineTo(px,py+len);x.lineTo(px+12,py);x.fill()}}G.terrain=c;G.tctx=x;}

// ── function: carve ──
function carve(cx,cy,r){const x=G.tctx;x.save();x.globalCompositeOperation='destination-out';x.beginPath();x.arc(cx,cy,r,0,Math.PI*2);x.fill();x.restore();for(let i=0;i<Math.min(16,r/4);i++)G.debris.push({x:cx+rnd(-r,r),y:cy+rnd(-r*.4,r*.3),vx:rnd(-160,160),vy:rnd(-250,-40),life:rnd(1,2.3),size:rnd(3,9),color:i%2?'#73513d':'#3f894f'});}

// ── function: surfaceSafe ──
function surfaceSafe(x){const y=findGround(x);return y<WORLD.water-35&&!terrainSolid(x,y-35)&&y>150;}

// ── function: pickSpawn ──
function pickSpawn(teamIndex,unitIndex,total){let base=(teamIndex+.5)*WORLD.w/total;let x=base+(unitIndex-1.5)*95;for(let i=0;i<30&&!surfaceSafe(x);i++)x=rnd(100,WORLD.w-100);return {x,y:findGround(x)-18};}

// ── function: sfx ──
function sfx(type,level=1){try{if(!AUDIO)AUDIO=new (AudioContext||webkitAudioContext)();const a=AUDIO,t=a.currentTime,o=a.createOscillator(),g=a.createGain();o.connect(g);g.connect(a.destination);let f=type==='fire'?150:type==='boom'?55:type==='jump'?330:type==='tick'?700:type==='menu'?520:220;o.type=type==='boom'?'sawtooth':'sine';o.frequency.setValueAtTime(f,t);o.frequency.exponentialRampToValueAtTime(Math.max(35,f*(type==='fire'?2.2:.65)),t+.12);g.gain.setValueAtTime(.0001,t);g.gain.exponentialRampToValueAtTime(.09*level,t+.01);g.gain.exponentialRampToValueAtTime(.0001,t+(type==='boom'?.45:.12));o.start();o.stop(t+.5)}catch(e){}}

// ── function: toast ──
function toast(txt,ms=1400){const e=document.querySelector('#toast');e.textContent=txt;e.classList.add('show');clearTimeout(G.toastT);G.toastT=setTimeout(()=>e.classList.remove('show'),ms)}

// ── function: particles ──
function particles(x,y,color,count=20,speed=250){for(let i=0;i<count;i++){const a=rnd(0,Math.PI*2),v=rnd(speed*.2,speed);G.particles.push({x,y,vx:Math.cos(a)*v,vy:Math.sin(a)*v-rnd(0,80),life:rnd(.35,1.15),max:1,size:rnd(2,7),color})}}

// ── function: floating ──
function floating(x,y,text,color='#fff'){G.floaters.push({x,y,text,color,life:1.3})}

// ── function: activeUnit ──
function activeUnit(){return G.teams[G.turn.team].units[G.turn.unit]}

// ── function: aliveUnits ──
function aliveUnits(){return G.teams.flatMap(t=>t.units).filter(u=>u.hp>0)}

// ── function: nextTurn ──
function nextTurn(){if(G.over)return;G.projectiles=[];G.turn.busy=false;G.turn.team=(G.turn.team+1)%G.teams.length;if(G.turn.team===0)G.turn.unit=(G.turn.unit+1)%4;let attempts=0;while(activeUnit().hp<=0&&attempts++<30){G.turn.team=(G.turn.team+1)%G.teams.length;if(G.turn.team===0)G.turn.unit=(G.turn.unit+1)%4}G.wind=rnd(-110,110);G.turn.time=G.turn.limit;G.turn.started=performance.now();G.turn.shots=0;G.charge=0;G.charging=false;const u=activeUnit();G.camera.target=u;updateHud();toast(`${G.teams[G.turn.team].name} — ${u.name}`,1000);checkWinner();if(!G.over&&G.teams[G.turn.team].cpu)setTimeout(cpuPlan,700)}

// ── function: checkWinner ──
function checkWinner(){const living=G.teams.filter(t=>t.units.some(u=>u.hp>0));if(living.length<=1&&G.started){G.over=true;document.querySelector('#winnerText').textContent=living[0]?`${living[0].name} WIN!`:'EVERYBODY LOST!';document.querySelector('#result').classList.add('show')}}

// ── function: damageUnit ──
function damageUnit(u,amount,cx,cy){if(u.hp<=0)return;const shield=u.shield>0?.55:1;amount=Math.round(amount*shield);u.hp=Math.max(0,u.hp-amount);const dx=u.x-cx,dy=u.y-cy,d=Math.hypot(dx,dy)||1;u.vx+=dx/d*amount*4;u.vy+=dy/d*amount*4-amount*2;floating(u.x,u.y-30,`-${amount}`,amount>35?'#ff705c':'#fff');particles(u.x,u.y,'#ffcf78',10,120);if(!u.hp)floating(u.x,u.y-45,'K.O.!','#ffea56')}

// ── function: explode ──
function explode(x,y,r,damage,heavy=false){carve(x,y,r);particles(x,y,'#ffdb69',45,420);particles(x,y,'#ff6045',30,300);sfx('boom',heavy?1.5:1);G.shake=Math.max(G.shake,heavy?18:9);if(heavy)G.freeze=.075;for(const u of aliveUnits()){const d=Math.hypot(u.x-x,u.y-y);if(d<r*1.65)damageUnit(u,damage*(1-d/(r*1.7)),x,y)}G.camera.zoomTarget=heavy?.72:.86;setTimeout(()=>{if(G)G.camera.zoomTarget=1},450)}

// ── function: endAction ──
function endAction(delay=1500){G.turn.busy=true;clearTimeout(G.endTimer);G.endTimer=setTimeout(()=>{if(G&&!G.over)nextTurn()},delay)}

// ── function: fireWeapon ──
function fireWeapon(power=1,targetX=null,targetY=null){if(G.turn.busy)return;const u=activeUnit(),w=WEAPONS.find(q=>q.id===G.weapon);const ang=u.aim,dir=u.dir;if(w.utility==='shield'){u.shield=3;toast('BUBBLE SHIELD!');particles(u.x,u.y,'#82e8ff',25,130);endAction(900);return}if(w.utility==='teleport'){const tx=targetX??u.x+dir*260,ty=findGround(clamp(targetX??u.x+dir*260,30,WORLD.w-30))-18;particles(u.x,u.y,'#9a87ff',30);u.x=clamp(tx,20,WORLD.w-20);u.y=ty;particles(u.x,u.y,'#9a87ff',30);endAction(900);return}if(w.airstrike){const tx=targetX??u.x+dir*450;for(let i=0;i<5;i++)setTimeout(()=>spawnProjectile(tx+(i-2)*65,80,rnd(-25,25),280,w,.8),i*140);endAction(2800);return}if(w.instant){const range=380,ex=u.x+Math.cos(ang)*dir*range,ey=u.y+Math.sin(ang)*range;let hit=null,hd=range;for(const v of aliveUnits())if(v!==u){const d=Math.hypot(v.x-ex,v.y-ey);if(d<55&&Math.hypot(v.x-u.x,v.y-u.y)<hd){hit=v;hd=Math.hypot(v.x-u.x,v.y-u.y)}}const ix=hit?hit.x:ex,iy=hit?hit.y:ey;particles(ix,iy,'#ffe091',22,230);carve(ix,iy,w.radius);if(hit)damageUnit(hit,w.damage,u.x,u.y);sfx('fire');endAction(1100);return}const speed=220+power*520;spawnProjectile(u.x+dir*22,u.y-10,Math.cos(ang)*dir*speed,Math.sin(ang)*speed,w,1);sfx('fire');G.turn.busy=true}

// ── function: spawnProjectile ──
function spawnProjectile(x,y,vx,vy,w,scale=1){G.projectiles.push({x,y,vx,vy,w,life:0,fuse:w.fuse||8,bounces:0,scale})}

// ── function: clusterBurst ──
function clusterBurst(p){for(let i=0;i<6;i++)spawnProjectile(p.x,p.y,rnd(-230,230),rnd(-300,-100),{...WEAPONS[0],radius:34,damage:22},.65);particles(p.x,p.y,'#caff70',20)}

// ── function: update ──
function update(dt){if(!G.started||G.over)return;if(G.freeze>0){G.freeze-=dt;return}const u=activeUnit();if(!G.turn.busy&&!G.teams[G.turn.team].cpu){let move=(G.keys.ArrowRight?1:0)-(G.keys.ArrowLeft?1:0);if(move){u.dir=move;const ahead=u.x+move*18,gy=findGround(ahead);if(gy-u.y<28&&gy<WORLD.water){u.vx+=move*340*dt;u.energy=Math.max(0,u.energy-dt)}}if(G.keys.ArrowUp)u.aim-=1.25*dt;if(G.keys.ArrowDown)u.aim+=1.25*dt;u.aim=clamp(u.aim,-Math.PI*.92,-.08)}
for(const team of G.teams)for(const q of team.units){if(q.hp<=0)continue;q.shield=Math.max(0,q.shield-dt);q.vy+=WORLD.gravity*dt;if(q.y>WORLD.water-15){q.vy-=WORLD.gravity*1.22*dt;q.vx*=.985;q.air-=dt;if(q.air<=0){q.drownTick-=dt;if(q.drownTick<=0){damageUnit(q,8,q.x,q.y+40);q.drownTick=1}}}else q.air=Math.min(5,q.air+dt*2);let nx=q.x+q.vx*dt,ny=q.y+q.vy*dt;if(terrainSolid(nx,ny+18)){if(q.vy>300)damageUnit(q,(q.vy-280)*.07,q.x,q.y+25);ny=findGround(nx)-18;q.vy=0;q.vx*=Math.pow(.08,dt);q.grounded=true}else q.grounded=false;if(terrainSolid(nx+Math.sign(q.vx)*13,ny))q.vx*=-.15;else q.x=clamp(nx,8,WORLD.w-8);q.y=ny;if(q.y>WORLD.h+60){q.hp=0;floating(q.x,WORLD.water-20,'DROWNED!','#72e9ff')}}
for(let i=G.projectiles.length-1;i>=0;i--){const p=G.projectiles[i];p.life+=dt;p.fuse-=dt;p.vx+=G.wind*dt*(p.w.drill?.2:1);p.vy+=WORLD.gravity*dt;p.x+=p.vx*dt;p.y+=p.vy*dt;const hit=terrainSolid(p.x,p.y)||p.y>WORLD.water+35||p.x<0||p.x>WORLD.w;if(p.w.drill&&terrainSolid(p.x,p.y)&&p.life<1.8){carve(p.x,p.y,13);p.vx*=.985;p.vy*=.985;particles(p.x,p.y,'#b9976b',2,70);continue}if(p.w.fuse&&hit&&p.fuse>0&&p.bounces<6){p.vy=-Math.abs(p.vy)*.62;p.vx*=.78;p.y-=5;p.bounces++;sfx('menu',.25);continue}if(p.w.cluster&&p.life>1.15){clusterBurst(p);G.projectiles.splice(i,1);continue}if(hit||p.fuse<=0||p.life>9){explode(p.x,p.y,p.w.radius*p.scale,p.w.damage*p.scale,p.w.id==='airstrike'||p.w.drill);G.projectiles.splice(i,1);if(!G.projectiles.length)endAction(1700)}}
for(let i=G.particles.length-1;i>=0;i--){const p=G.particles[i];p.life-=dt;p.vy+=360*dt;p.x+=p.vx*dt;p.y+=p.vy*dt;p.vx*=.99;if(p.life<=0)G.particles.splice(i,1)}for(let i=G.debris.length-1;i>=0;i--){const d=G.debris[i];d.life-=dt;d.vy+=WORLD.gravity*dt;d.x+=d.vx*dt;d.y+=d.vy*dt;if(terrainSolid(d.x,d.y+d.size)){d.vy*=-.2;d.vx*=.4;d.life-=.3}if(d.life<=0)G.debris.splice(i,1)}for(let i=G.floaters.length-1;i>=0;i--){G.floaters[i].life-=dt;G.floaters[i].y-=28*dt;if(G.floaters[i].life<=0)G.floaters.splice(i,1)}
if(!G.turn.busy){G.turn.time-=dt;if(G.turn.time<=0){toast('TIME!');nextTurn()}if(Math.ceil(G.turn.time)<=10&&Math.ceil(G.turn.time)!==G.lastTick){G.lastTick=Math.ceil(G.turn.time);sfx('tick',.45)}}if(G.projectiles[0])G.camera.target=G.projectiles[0];else G.camera.target=activeUnit();G.camera.x=lerp(G.camera.x,G.camera.target.x,.055);G.camera.y=lerp(G.camera.y,G.camera.target.y,.055);G.camera.zoom=lerp(G.camera.zoom,G.camera.zoomTarget,.04);G.shake*=.88;updateHud()}

// ── function: drawCloud ──
function drawCloud(ctx,x,y,s){ctx.fillStyle='#fff9';ctx.beginPath();ctx.arc(x,y,s*.32,0,7);ctx.arc(x+s*.32,y-s*.1,s*.25,0,7);ctx.arc(x+s*.58,y,s*.36,0,7);ctx.fill()}

// ── function: drawUnit ──
function drawUnit(ctx,u,active){ctx.save();ctx.translate(u.x,u.y);if(u.shield>0){ctx.strokeStyle='#80eaffaa';ctx.lineWidth=4;ctx.beginPath();ctx.arc(0,-3,25+Math.sin(performance.now()*.006)*2,0,7);ctx.stroke()}ctx.fillStyle='rgba(0,0,0,.2)';ctx.beginPath();ctx.ellipse(0,17,16,4,0,0,7);ctx.fill();ctx.fillStyle=u.team.color;ctx.beginPath();ctx.ellipse(0,0,14,18,0,0,7);ctx.fill();ctx.fillStyle='#fff';ctx.beginPath();ctx.ellipse(-5,-5,5,7,0,0,7);ctx.ellipse(5,-5,5,7,0,0,7);ctx.fill();ctx.fillStyle='#17212a';ctx.beginPath();ctx.arc(-4+u.dir*1.5,-4,2,0,7);ctx.arc(6+u.dir*1.5,-4,2,0,7);ctx.fill();ctx.strokeStyle='#572832';ctx.lineWidth=2;ctx.beginPath();ctx.arc(0,3,5,.15,Math.PI-.15);ctx.stroke();if(active){ctx.strokeStyle='#fff';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(Math.cos(u.aim)*u.dir*18,Math.sin(u.aim)*18);ctx.lineTo(Math.cos(u.aim)*u.dir*31,Math.sin(u.aim)*31);ctx.stroke()}ctx.restore();ctx.font='900 10px Nunito';ctx.textAlign='center';ctx.fillStyle='#09202ccc';ctx.fillRect(u.x-22,u.y-33,44,7);ctx.fillStyle=u.hp>35?'#69e66e':'#ff5b55';ctx.fillRect(u.x-22,u.y-33,44*u.hp/100,7);ctx.fillStyle='#fff';ctx.fillText(Math.ceil(u.hp),u.x,u.y-36)}

// ── function: drawTrajectory ──
function drawTrajectory(ctx,u){const w=WEAPONS.find(x=>x.id===G.weapon);if(G.turn.busy||w.instant||w.utility||w.airstrike)return;let x=u.x+u.dir*22,y=u.y-10,v=220+Math.max(.2,G.charge)*520,vx=Math.cos(u.aim)*u.dir*v,vy=Math.sin(u.aim)*v;ctx.fillStyle='#fff9';for(let i=0;i<42;i++){vx+=G.wind*.045;vy+=WORLD.gravity*.045;x+=vx*.045;y+=vy*.045;if(i%3===0){ctx.globalAlpha=1-i/50;ctx.beginPath();ctx.arc(x,y,2.5,0,7);ctx.fill()}if(terrainSolid(x,y))break}ctx.globalAlpha=1}

// ── function: render ──
function render(){const c=G.canvas,ctx=G.ctx,dpr=G.dpr,w=c.width/dpr,h=c.height/dpr;ctx.setTransform(dpr,0,0,dpr,0,0);const sky=ctx.createLinearGradient(0,0,0,h);sky.addColorStop(0,'#55c9fa');sky.addColorStop(.68,'#bdeeff');sky.addColorStop(1,'#dff8ff');ctx.fillStyle=sky;ctx.fillRect(0,0,w,h);for(let i=0;i<8;i++)drawCloud(ctx,((i*331-G.camera.x*.08)% (w+300))-100,80+(i%3)*78,90+(i%2)*35);const z=G.camera.zoom,ox=w/2-G.camera.x*z+rnd(-G.shake,G.shake),oy=h/2-G.camera.y*z+rnd(-G.shake,G.shake);ctx.save();ctx.translate(ox,oy);ctx.scale(z,z);ctx.drawImage(G.terrain,0,0);ctx.fillStyle='rgba(36,153,213,.62)';ctx.fillRect(0,WORLD.water,WORLD.w,WORLD.h-WORLD.water);ctx.strokeStyle='#b7f4ff';ctx.lineWidth=5;ctx.beginPath();for(let x=0;x<WORLD.w;x+=18)ctx.lineTo(x,WORLD.water+Math.sin(x*.025+performance.now()*.003)*5);ctx.stroke();drawTrajectory(ctx,activeUnit());for(const t of G.teams)for(const u of t.units)if(u.hp>0)drawUnit(ctx,u,u===activeUnit()&&!G.turn.busy);for(const p of G.projectiles){ctx.save();ctx.translate(p.x,p.y);ctx.rotate(Math.atan2(p.vy,p.vx));ctx.font=`${24*p.scale}px serif`;ctx.textAlign='center';ctx.fillText(p.w.id==='airstrike'?'🐟':p.w.id==='grenade'?'🍍':p.w.id==='cluster'?'🎆':p.w.drill?'🪱':'🚀',0,5);ctx.restore();particlesTrail(ctx,p)}for(const p of G.particles){ctx.globalAlpha=clamp(p.life,0,1);ctx.fillStyle=p.color;ctx.beginPath();ctx.arc(p.x,p.y,p.size*clamp(p.life*2,0,1),0,7);ctx.fill()}ctx.globalAlpha=1;for(const d of G.debris){ctx.fillStyle=d.color;ctx.fillRect(d.x,d.y,d.size,d.size)}for(const f of G.floaters){ctx.globalAlpha=clamp(f.life,0,1);ctx.fillStyle=f.color;ctx.font='900 18px Nunito';ctx.textAlign='center';ctx.fillText(f.text,f.x,f.y)}ctx.globalAlpha=1;ctx.restore();if(G.charging){ctx.fillStyle='#071723dd';ctx.fillRect(w/2-122,h-100,244,22);ctx.fillStyle='#ffb832';ctx.fillRect(w/2-118,h-96,236*G.charge,14);ctx.fillStyle='#fff';ctx.font='900 11px Nunito';ctx.textAlign='center';ctx.fillText(`POWER ${Math.round(G.charge*100)}%`,w/2,h-106)}requestAnimationFrame(loop)}

// ── function: particlesTrail ──
function particlesTrail(ctx,p){ctx.fillStyle='#fff5';ctx.beginPath();ctx.arc(p.x-p.vx*.025,p.y-p.vy*.025,rnd(2,5),0,7);ctx.fill()}

// ── function: loop ──
function loop(now){if(!G.last)G.last=now;let dt=Math.min(.033,(now-G.last)/1000);G.last=now;update(dt);render()}

// ── function: updateHud ──
function updateHud(){if(!G||!G.started)return;const t=G.teams[G.turn.team],u=activeUnit(),w=WEAPONS.find(x=>x.id===G.weapon);document.querySelector('#teamName').textContent=t.name;document.querySelector('#wormName').textContent=`${u.name} · ${Math.ceil(u.hp)} HP`;const dot=document.querySelector('#teamDot');dot.style.background=t.color;dot.style.color=t.color;document.querySelector('#timeValue').textContent=Math.max(0,Math.ceil(G.turn.time));document.querySelector('#timer').classList.toggle('danger',G.turn.time<=10);document.querySelector('#windValue').textContent=Math.abs(Math.round(G.wind));const a=document.querySelector('#windArrow');a.style.transform=`translateX(${clamp(Math.abs(G.wind)*.32,0,40)}px) rotate(${G.wind<0?180:0}deg)`;document.querySelector('#weaponBar').innerHTML=`<span class="icon">${w.icon}</span><div><small>${w.cat}</small><b>${w.name}</b></div>`}

// ── function: buildWeapons ──
function buildWeapons(){document.querySelector('#weaponGrid').innerHTML=WEAPONS.map(w=>`<button class="weaponChoice" data-id="${w.id}"><i>${w.icon}</i><b>${w.name}</b><small>${w.cat}<br>${w.desc}</small></button>`).join('');document.querySelectorAll('.weaponChoice').forEach(b=>b.onclick=()=>{G.weapon=b.dataset.id;document.querySelector('#weaponPanel').classList.remove('show');sfx('menu');updateHud()})}

// ── function: startGame ──
function startGame(){const count=+document.querySelector('#teamCount').value,cpu=document.querySelector('#cpuToggle').checked;let type=document.querySelector('#terrainType').value;if(type==='random')type=['island','twins','cave'][Math.floor(Math.random()*3)];const canvas=document.querySelector('#game'),dpr=Math.min(1.5,devicePixelRatio||1);G={canvas,ctx:canvas.getContext('2d'),dpr,type,teams:[],particles:[],debris:[],floaters:[],projectiles:[],keys:{},camera:{x:WORLD.w/2,y:550,zoom:.8,zoomTarget:1,target:null},turn:{team:0,unit:0,time:+document.querySelector('#turnLength').value,limit:+document.querySelector('#turnLength').value,busy:false,shots:0},weapon:'bazooka',wind:rnd(-100,100),charge:0,charging:false,shake:0,freeze:0,last:0,lastTick:99,started:true,over:false,audio:null};resize();makeTerrain(type);for(let ti=0;ti<count;ti++){const team={...TEAM_DATA[ti],cpu:cpu&&ti>0,units:[]};for(let ui=0;ui<4;ui++){const p=pickSpawn(ti,ui,count);team.units.push({x:p.x,y:p.y,vx:0,vy:0,hp:100,team,name:['BERT','NIBS','WIGGLE','CAPTAIN'][ui],dir:ti%2?-1:1,aim:-.65,grounded:true,energy:1,air:5,drownTick:1,shield:0})}G.teams.push(team)}document.querySelector('#menu').classList.remove('show');G.camera.target=activeUnit();buildWeapons();updateHud();toast(type==='cave'?'CRYSTAL CAVE':type==='twins'?'TWIN TROUBLE':'THE BIG ISLAND',1600);requestAnimationFrame(loop)}

// ── function: resize ──
function resize(){if(!G)return;const r=G.canvas.getBoundingClientRect();G.canvas.width=r.width*G.dpr;G.canvas.height=r.height*G.dpr}

// ── function: jump ──
function jump(){if(!G.started||G.turn.busy||G.teams[G.turn.team].cpu)return;const u=activeUnit();if(u.grounded){u.vy=-315;u.vx+=u.dir*105;sfx('jump');particles(u.x,u.y+16,'#d9bd8d',10,80)}}

// ── function: screenToWorld ──
function screenToWorld(clientX,clientY){const r=G.canvas.getBoundingClientRect(),w=r.width,h=r.height,z=G.camera.zoom;return{x:(clientX-r.left-w/2)/z+G.camera.x,y:(clientY-r.top-h/2)/z+G.camera.y}}

// ── function: cpuPlan ──
function cpuPlan(){if(G.over||!G.teams[G.turn.team].cpu)return;const u=activeUnit(),enemies=aliveUnits().filter(v=>v.team!==u.team);if(!enemies.length)return;let best=null,score=-1e9;for(const e of enemies){const near=enemies.filter(q=>Math.hypot(q.x-e.x,q.y-e.y)<130).length;const hazard=(e.y>WORLD.water-120?22:0)+(e.x<80||e.x>WORLD.w-80?15:0);const value=(100-e.hp)*.45+near*24+hazard-Math.hypot(e.x-u.x,e.y-u.y)*.012;if(value>score){score=value;best=e}}const cliff=!surfaceSafe(u.x+u.dir*45),selfRisk=u.y>WORLD.water-110||cliff;if(selfRisk&&Math.random()<.4){G.weapon='teleport';const safeXs=[];for(let x=100;x<WORLD.w-100;x+=100)if(surfaceSafe(x))safeXs.push(x);const tx=safeXs[Math.floor(Math.random()*safeXs.length)]||u.x;updateHud();setTimeout(()=>fireWeapon(1,tx),650);return}const clustered=enemies.filter(e=>Math.hypot(e.x-best.x,e.y-best.y)<150).length>=2;G.weapon=clustered&&Math.random()<.5?'airstrike':Math.random()<.2?'cluster':'bazooka';updateHud();if(G.weapon==='airstrike'){setTimeout(()=>fireWeapon(1,best.x,best.y),700);return}u.dir=best.x>u.x?1:-1;const dx=Math.abs(best.x-u.x),dy=best.y-u.y;let power=clamp(dx/720+.28,.36,1);let speed=220+power*520;let angle=-clamp(.42+dx/1400-dy/1800,.35,1.15);u.aim=angle;G.charge=power;setTimeout(()=>{if(G&&!G.over)fireWeapon(power,best.x,best.y)},800)}

// ── function: wire ──
function wire(){document.querySelector('#startBtn').onclick=()=>{sfx('menu');startGame()};document.querySelector('#againBtn').onclick=()=>location.reload();document.querySelector('#closeWeapons').onclick=()=>document.querySelector('#weaponPanel').classList.remove('show');window.addEventListener('resize',resize);window.addEventListener('keydown',e=>{if(!G||!G.started)return;G.keys[e.code]=true;if(['Space','ArrowUp','ArrowDown','ArrowLeft','ArrowRight'].includes(e.code))e.preventDefault();if(e.code==='KeyW'&&!G.turn.busy&&!G.teams[G.turn.team].cpu)document.querySelector('#weaponPanel').classList.add('show');if(e.code==='KeyJ')jump();if(e.code==='Space'&&!G.charging&&!G.turn.busy&&!G.teams[G.turn.team].cpu){const w=WEAPONS.find(x=>x.id===G.weapon);if(w.utility||w.airstrike){toast('CLICK A TARGET');G.targeting=true}else{G.charging=true;G.charge=.08}}});window.addEventListener('keyup',e=>{if(!G)return;G.keys[e.code]=false;if(e.code==='Space'&&G.charging){G.charging=false;fireWeapon(G.charge)}});document.querySelector('#game').addEventListener('pointerdown',e=>{if(!G||!G.targeting)return;const p=screenToWorld(e.clientX,e.clientY);G.targeting=false;fireWeapon(1,p.x,p.y)});buildWeapons();setInterval(()=>{if(G&&G.charging)G.charge=clamp(G.charge+.018,0,1)},30);document.querySelectorAll('button,select').forEach(e=>e.addEventListener('pointerenter',()=>{if(G)sfx('menu',.15)}))}

// ── main ──
wire();
</script>
</body>
</html>
<!-- agent-meta {"model":"openai/gpt-5.6-sol","provider":"openrouter","persona":"composer","sessionId":"9bf2aee4-dbb4-427a-b479-fad8a1c2292c","tokensIn":334105,"tokensOut":14261,"tokensTotal":348366,"turns":21,"toolCalls":26,"failedToolCalls":0,"timestamp":"2026-07-14T16:40:58.452Z"} -->