← Worms results

Worms

GPT 5.6 Luna Pro · typescript

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 24,087 bytes · SHA-256 2341fbdbc2ac
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WILDWORMS // Siege of the Shifting Isles</title>
<style>
/* ── css: base ── */
@import url('https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@400;600;700;800;900&family=Space+Mono:wght@400;700&display=swap');*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;overflow:hidden;background:#07131d;color:#edf7f5;font-family:'Barlow Condensed',sans-serif}#game{position:relative;width:100vw;height:100vh;overflow:hidden;background:#081822}#world{position:absolute;inset:0;width:100%;height:100%;display:block}#vignette{position:absolute;inset:0;pointer-events:none;background:radial-gradient(ellipse at center,transparent 42%,rgba(1,7,13,.45) 100%),linear-gradient(180deg,rgba(1,7,13,.15),transparent 30%,rgba(1,7,13,.35));mix-blend-mode:multiply}.topbar{height:78px;padding:18px 28px;display:flex;align-items:center;justify-content:space-between;background:linear-gradient(180deg,rgba(5,17,26,.95),rgba(5,17,26,.72),transparent);position:relative;z-index:2}.brand{display:flex;gap:11px;align-items:center;letter-spacing:1px}.brand-mark{font-size:27px;color:#67e6c1;text-shadow:0 0 18px #67e6c1}.brand b{display:block;font-weight:900;font-size:20px;line-height:18px}.brand small,.wind small,.turn small{font-family:'Space Mono';font-size:8px;letter-spacing:1.3px;color:#82a1a8}.wind,.turn{display:flex;align-items:center;gap:11px}.wind-icon{color:#f6c85f;font-size:27px}.wind strong{font-family:'Space Mono';color:#f6c85f;font-size:14px}.turn{display:block;text-align:right;min-width:145px}.turn small{color:#67e6c1}.turn strong{font-family:'Space Mono';display:block;font-size:22px;color:#fff}.timer-track{height:3px;width:145px;background:#273b43;margin-top:3px}.timer-track i{display:block;width:100%;height:100%;background:#67e6c1;box-shadow:0 0 10px #67e6c1}.status-row{position:absolute;top:84px;left:28px;right:28px;display:flex;justify-content:space-between;align-items:center;z-index:2}.team-card{display:flex;align-items:center;gap:10px;background:rgba(8,23,32,.77);border:1px solid rgba(147,180,183,.16);padding:9px 11px;min-width:190px;opacity:.7}.team-card.active{opacity:1;border-color:#67e6c1;box-shadow:0 0 22px #67e6c122}.team-dot{width:9px;height:9px;border-radius:50%;background:#67e6c1;box-shadow:0 0 10px #67e6c1}.coral .team-dot{background:#ff796f;box-shadow:0 0 10px #ff796f}.team-card b{display:block;font-size:12px;letter-spacing:1px}.team-card small{color:#92aeb1;font-family:'Space Mono';font-size:8px}.team-card em{font-family:'Space Mono';font-style:normal;margin-left:auto;color:#67e6c1}.coral em{color:#ff796f}.terrain-chip{font-family:'Space Mono';font-size:10px;letter-spacing:1px;color:#c4d9d5}.terrain-chip span{color:#f6c85f}.terrain-chip small{color:#5f7f83;margin:0 8px}.weapon-dock{position:absolute;z-index:3;left:50%;bottom:31px;transform:translateX(-50%);display:flex;align-items:center;gap:6px;padding:10px 12px;background:rgba(5,17,25,.9);border:1px solid rgba(120,170,167,.24);box-shadow:0 12px 38px #0008}.weapon-title{width:130px;padding:2px 10px;border-right:1px solid #29424a}.weapon-title small,.weapon-title span{display:block;font-family:'Space Mono';font-size:8px;color:#7c9da0;letter-spacing:1px}.weapon-title strong{display:block;color:#f6c85f;font-size:16px;letter-spacing:1px}.weapon{width:62px;height:57px;background:#132c35;color:#b3cbca;border:1px solid #294952;cursor:pointer;transition:.18s;display:grid;place-items:center;position:relative}.weapon:hover{transform:translateY(-3px);background:#1b3d46}.weapon.selected{border-color:#f6c85f;background:#31433b;color:#fff;box-shadow:inset 0 -3px #f6c85f}.weapon i{font-style:normal;font-size:21px;color:#67e6c1}.weapon b{font-size:10px;letter-spacing:1px}.weapon small{position:absolute;right:5px;top:3px;color:#f6c85f;font-family:'Space Mono';font-size:8px}.weapon.utility i{color:#f6c85f}.power{width:150px;padding-left:13px}.power span,.power b{font-family:'Space Mono';font-size:8px;color:#88a8aa}.power b{float:right;color:#f6c85f}.powerbar{height:7px;background:#263c42;margin-top:7px}.powerbar i{display:block;width:72%;height:100%;background:linear-gradient(90deg,#67e6c1,#f6c85f);box-shadow:0 0 12px #67e6c1}.hint{position:absolute;bottom:9px;left:50%;transform:translateX(-50%);font-family:'Space Mono';font-size:8px;color:#789397;letter-spacing:1px;white-space:nowrap}.hint span{color:#d4e6df;background:#29434a;padding:3px 5px;margin:0 4px}.toast{position:absolute;left:50%;top:40%;transform:translate(-50%,-50%);font-size:28px;font-weight:900;color:#fff;text-shadow:0 3px #15343d;opacity:0;transition:.3s;pointer-events:none}.toast small{color:#f6c85f}.toast.show{opacity:1;transform:translate(-50%,-75%)}.modal{position:absolute;inset:0;margin:auto;width:490px;height:280px;z-index:10;text-align:center;padding:30px;background:linear-gradient(145deg,#122d38ee,#071923f2);border:1px solid #67e6c166;box-shadow:0 20px 100px #000b}.eyebrow{font-family:'Space Mono';font-size:9px;letter-spacing:2px;color:#67e6c1}.modal h1{font-size:64px;line-height:1;margin:16px 0 5px;font-weight:900;letter-spacing:3px}.modal h1 span{color:#f6c85f}.modal p{margin:0 0 23px;color:#a5c0bc;font-size:17px}.modal button{background:#67e6c1;color:#082029;border:0;padding:14px 25px;font:900 14px 'Barlow Condensed';letter-spacing:1px;cursor:pointer;box-shadow:0 6px 0 #318f7e}.modal button:active{transform:translateY(3px);box-shadow:0 3px 0 #318f7e}.modal-meta{display:flex;justify-content:center;gap:16px;margin-top:25px;font:8px 'Space Mono';color:#789397}.pause{position:absolute;inset:0;margin:auto;width:220px;height:70px;text-align:center;background:#071923dd;padding:17px;z-index:9;border:1px solid #67e6c1;font-weight:900;font-size:25px;letter-spacing:3px}.pause small{display:block;font:8px 'Space Mono';color:#8ba6a6;letter-spacing:1px}@media(max-width:700px){.status-row{left:10px;right:10px}.team-card{min-width:145px}.team-card b{font-size:9px}.terrain-chip{display:none}.weapon-dock{bottom:28px;gap:3px}.weapon-title,.power{display:none}.weapon{width:48px;height:50px}.hint{font-size:7px}.modal{width:90%;height:auto}.modal h1{font-size:48px}}
/* ── css: motion ── */
@keyframes pulse{50%{opacity:.6;transform:scale(1.04)}}@keyframes floatup{to{transform:translateY(-35px);opacity:0}}.active .team-dot{animation:pulse 1.2s infinite}.toast.show{animation:floatup 1.2s ease-out forwards}
</style>
</head>
<body>
<main id="game"><canvas id="world" aria-label="Wildworms battlefield"></canvas><div id="vignette"></div><section id="hud"><header class="topbar"><div class="brand"><span class="brand-mark">✦</span><div><b>WILDWORMS</b><small>SIEGE OF THE SHIFTING ISLES</small></div></div><div class="wind"><span class="wind-icon">➳</span><div><small>GLOBAL WIND</small><strong id="windValue">→ 12</strong></div></div><div class="turn"><small id="turnLabel">BLUE TURN · 01</small><strong id="timer">18.4</strong><div class="timer-track"><i id="timerFill"></i></div></div></header><div class="status-row"><div class="team-card blue active"><span class="team-dot"></span><div><b>AZURE ALCHEMISTS</b><small id="blueStatus">WORM 01 · 100 HP</small></div><em>4</em></div><div class="terrain-chip"><span>◈</span> SHIFTING ISLES <small>•</small> ROUND <b id="roundNo">01</b></div><div class="team-card coral"><span class="team-dot"></span><div><b>EMBER BANDITS</b><small id="redStatus">WORM 01 · 100 HP</small></div><em>4</em></div></div><div class="weapon-dock"><div class="weapon-title"><small>ARSENAL</small><strong id="weaponName">AURORA ARC</strong><span id="weaponType">INDIRECT · PARABOLIC</span></div><button class="weapon selected" data-weapon="0"><i>➳</i><b>ARC</b><small>∞</small></button><button class="weapon" data-weapon="1"><i>✦</i><b>BOOM</b><small>3</small></button><button class="weapon" data-weapon="2"><i>⌁</i><b>RAY</b><small>∞</small></button><button class="weapon" data-weapon="3"><i>◉</i><b>DRILL</b><small>2</small></button><button class="weapon" data-weapon="4"><i>✺</i><b>METEOR</b><small>1</small></button><button class="weapon utility" data-weapon="5"><i>⚑</i><b>GRAPPLE</b><small>2</small></button><div class="power"><span>POWER</span><div class="powerbar"><i id="powerFill"></i></div><b id="powerText">72%</b></div></div><div class="hint"><span>SPACE</span> FIRE / JUMP <span>← →</span> AIM <span>↑ ↓</span> POWER <span>TAB</span> CHANGE WEAPON</div><div id="toast" class="toast">DIRECT HIT! <small>+28</small></div></section><div id="startCard" class="modal"><div class="eyebrow">TACTICAL ARCADE · HOT-SEAT / CPU</div><h1>WILD<span>WORMS</span></h1><p>Make the island move. Leave nothing standing.</p><button id="startBtn">DROP INTO BATTLE <span>→</span></button><div class="modal-meta"><span>◈ 4v4 SKIRMISH</span><span>◈ WIND: ACTIVE</span><span>◈ TERRAIN: TWIN ISLES</span></div></div><div id="pause" class="pause" hidden="">PAUSED <small>Press P to return</small></div></main>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {};
function __require(id) {
  if (__cache[id]) return __cache[id].exports;
  var module = __cache[id] = { exports: {} };
  var factory = __mods[id];
  if (!factory) throw new Error("Module not found: " + id);
  factory(module.exports, function (spec) {
    var target = (__map[id] && __map[id][spec]) || spec;
    return __require(target);
  }, module);
  return module.exports;
}

// ── module: src/main.ts ──
__mods["src/main.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const canvas = document.querySelector('#world');
const ctx = canvas.getContext('2d');
const startCard = document.querySelector('#startCard');
const startBtn = document.querySelector('#startBtn');
const pause = document.querySelector('#pause');
const timerEl = document.querySelector('#timer');
const timerFill = document.querySelector('#timerFill');
const powerFill = document.querySelector('#powerFill');
const powerText = document.querySelector('#powerText');
const toast = document.querySelector('#toast');
const weaponName = document.querySelector('#weaponName');
const weaponType = document.querySelector('#weaponType');
const windValue = document.querySelector('#windValue');
const turnLabel = document.querySelector('#turnLabel');
const roundNo = document.querySelector('#roundNo');
const blueStatus = document.querySelector('#blueStatus');
const redStatus = document.querySelector('#redStatus');
const weapons = [
    { name: 'AURORA ARC', type: 'INDIRECT · PARABOLIC', color: '#67e6c1', damage: 42, radius: 34, ammo: 99 },
    { name: 'BOOMERANG BOMB', type: 'INDIRECT · BOUNCES', color: '#ff796f', damage: 70, radius: 64, ammo: 3 },
    { name: 'PRISM RAY', type: 'DIRECT · INSTANT-HIT', color: '#b6f7ff', damage: 34, radius: 10, ammo: 99, direct: true },
    { name: 'TUNNEL DRILL', type: 'MAP-ALTERING · BURROWS', color: '#f6c85f', damage: 48, radius: 38, ammo: 2 },
    { name: 'SKY METEOR', type: 'HIGH-AOE · MAP-ALTERING', color: '#ff9d62', damage: 95, radius: 92, ammo: 1 },
    { name: 'GRAPPLE GADGET', type: 'UTILITY · SWING / CLIMB', color: '#f6c85f', damage: 0, radius: 0, ammo: 2, utility: true }
];
let W = innerWidth, H = innerHeight, scale = 1, camX = 0, camY = 0, targetCamX = 0, targetCamY = 0;
let terrain = [];
let terrainType = 'TWIN ISLES';
let worms = [];
let particles = [];
let texts = [];
let active = 0, weaponIndex = 0, power = .72, angle = -.72, wind = 12, timeLeft = 18.4, round = 1, playing = false, paused = false, charging = false, last = 0, shake = 0;
const keys = new Set();
function resize() { W = canvas.width = innerWidth; H = canvas.height = innerHeight; generateTerrain(); }
addEventListener('resize', resize);
function groundY(x) { const i = Math.max(0, Math.min(terrain.length - 1, Math.floor(x))); return terrain[i] ?? H * .7; }
function generateTerrain() { terrain = []; const base = H * .72; const twin = W * .5; const shape = Math.floor(Math.random() * 3); terrainType = shape === 0 ? 'CAVE' : shape === 1 ? 'SINGLE ISLAND' : 'TWIN ISLES'; for (let x = 0; x < W; x++) {
    let y = base;
    const n = Math.sin(x * .008) * 26 + Math.sin(x * .021 + 2) * 13 + Math.sin(x * .055) * 5;
    if (shape === 0)
        y = base - 42 + n + Math.sin(x * .002) * 24;
    if (shape === 1) {
        const d = Math.abs(x - W / 2) / (W * .43);
        y = base + (d < 1 ? d * d * 120 : 110) + n * .5;
    }
    if (shape === 2) {
        const d = Math.min(Math.abs(x - W * .28), Math.abs(x - W * .72)) / (W * .25);
        y = base + (d < 1 ? d * d * 80 : 90) + n * .6;
    }
    terrain.push(y);
} }
function reset() { worms = []; for (let t = 0; t < 2; t++)
    for (let i = 0; i < 4; i++) {
        const x = t === 0 ? W * (.16 + i * .08) : W * (.84 - i * .08);
        worms.push({ x, y: groundY(x) - 20, vx: 0, vy: 0, hp: 100, team: t === 0 ? 'blue' : 'red', index: i, grounded: true, alive: true });
    } active = 0; round = 1; timeLeft = 18.4; wind = Math.round(Math.random() * 25 - 8); weaponIndex = 0; angle = -.72; power = .72; particles = []; texts = []; generateTerrain(); updateWeapon(); updateHud(); }
function burst(x, y, color, count = 18) { for (let i = 0; i < count; i++) {
    const a = Math.random() * Math.PI * 2, s = 30 + Math.random() * 150;
    particles.push({ x, y, vx: Math.cos(a) * s, vy: Math.sin(a) * s, life: .5 + Math.random() * .8, max: 1, color, size: 2 + Math.random() * 4 });
} }
function crater(x, y, r) { for (let i = Math.max(0, Math.floor(x - r)); i < Math.min(W, Math.ceil(x + r)); i++) {
    const dx = i - x;
    const cut = Math.sqrt(Math.max(0, r * r - dx * dx)) * .9;
    terrain[i] = Math.min(H + 30, Math.max(groundY(i), y - cut));
} burst(x, y, '#d88b55', 32); shake = Math.max(shake, r / 16); }
function damageAt(x, y, r, dmg) { worms.forEach(w => { if (!w.alive)
    return; const d = Math.hypot(w.x - x, w.y - y); if (d < r + 13) {
    const hit = Math.round(dmg * (1 - d / (r + 13)));
    w.hp = Math.max(0, w.hp - hit);
    texts.push({ x: w.x, y: w.y - 22, text: `-${hit}`, color: w.team === 'blue' ? '#67e6c1' : '#ff796f', life: 1 });
    burst(w.x, w.y, '#fff', 8);
    if (w.hp <= 0) {
        w.alive = false;
        burst(w.x, w.y, w.team === 'blue' ? '#67e6c1' : '#ff796f', 28);
    }
} }); }
function explode(x, y, w) { crater(x, y, w.radius); damageAt(x, y, w.radius, w.damage); toast.textContent = w.name + '  IMPACT!'; toast.classList.remove('show'); void toast.offsetWidth; toast.classList.add('show'); setTimeout(() => toast.classList.remove('show'), 950); }
function fire() { if (!playing || paused)
    return; const w = weapons[weaponIndex], me = worms[active]; if (!me.alive)
    return; if (w.ammo <= 0 && !w.utility)
    return; charging = false; if (w.utility) {
    me.vx += Math.cos(angle) * 380;
    me.vy += Math.sin(angle) * 380;
    w.ammo--;
    endTurn();
    return;
} if (!w.direct) {
    const vx = Math.cos(angle) * power * 470, vy = Math.sin(angle) * power * 470;
    projectiles.push({ x: me.x, y: me.y - 16, vx, vy, life: 4, w });
}
else {
    let x = me.x, y = me.y - 12;
    const dx = Math.cos(angle), dy = Math.sin(angle);
    for (let i = 0; i < 900; i += 5) {
        x += dx * 5;
        y += dy * 5;
        if (y > groundY(x)) {
            explode(x, y, w);
            break;
        }
    }
} if (w.ammo < 90)
    w.ammo--; }
const projectiles = [];
function endTurn() { active = (active + 1) % worms.length; while (!worms[active].alive)
    active = (active + 1) % worms.length; timeLeft = 18.4; round++; wind = Math.max(-28, Math.min(28, wind + Math.round(Math.random() * 8 - 4))); angle = worms[active].team === 'blue' ? -0.72 : -2.42; power = .72; updateHud(); if (worms[active].team === 'red')
    setTimeout(cpuTurn, 500); }
function cpuTurn() { if (!playing || paused || worms[active].team !== 'red')
    return; const me = worms[active]; const targets = worms.filter(w => w.team === 'blue' && w.alive).sort((a, b) => a.hp - b.hp); const target = targets[0]; if (!target)
    return; angle = Math.atan2(target.y - me.y, target.x - me.x) - .35; power = .65; weaponIndex = round % 5 === 0 ? 4 : round % 3 === 0 ? 1 : 0; updateWeapon(); setTimeout(fire, 700); }
function updateWeapon() { const w = weapons[weaponIndex]; weaponName.textContent = w.name; weaponType.textContent = w.type; document.querySelectorAll('.weapon').forEach((e, i) => e.classList.toggle('selected', i === weaponIndex)); }
function updateHud() { const me = worms[active]; turnLabel.textContent = `${me?.team === 'blue' ? 'BLUE' : 'EMBER'} TURN · ${String(round).padStart(2, '0')}`; timerEl.textContent = timeLeft.toFixed(1); timerFill.style.width = `${timeLeft / 18.4 * 100}%`; timerFill.style.background = timeLeft < 5 ? '#ff796f' : '#67e6c1'; windValue.textContent = (wind >= 0 ? '→ ' : '← ') + Math.abs(wind); roundNo.textContent = String(round).padStart(2, '0'); blueStatus.textContent = `WORM ${String((worms.find(w => w.team === 'blue' && w.alive)?.index ?? 0) + 1).padStart(2, '0')} · ${worms.filter(w => w.team === 'blue' && w.alive).reduce((a, w) => a + w.hp, 0)} HP`; redStatus.textContent = `WORM ${String((worms.find(w => w.team === 'red' && w.alive)?.index ?? 0) + 1).padStart(2, '0')} · ${worms.filter(w => w.team === 'red' && w.alive).reduce((a, w) => a + w.hp, 0)} HP`; }
function update(dt) { if (!playing || paused)
    return; timeLeft -= dt; if (timeLeft <= 0) {
    endTurn();
    return;
} const me = worms[active]; if (!charging) {
    if (keys.has('ArrowLeft'))
        me.vx -= 160 * dt;
    if (keys.has('ArrowRight'))
        me.vx += 160 * dt;
    if (keys.has('ArrowUp'))
        angle -= .9 * dt;
    if (keys.has('ArrowDown'))
        angle += .9 * dt;
    if (keys.has(' ') && me.grounded) {
        me.vy = -330;
        me.grounded = false;
        burst(me.x, groundY(me.x), '#d6a66b', 8);
    }
} me.vx *= .92; me.vy += 560 * dt; me.x += me.vx * dt; me.y += me.vy * dt; me.x = Math.max(8, Math.min(W - 8, me.x)); const waterLevel = H * .86; if (me.y > waterLevel) {
    me.vy *= .82;
    me.vx *= .96;
    if (Math.random() < dt * .8) {
        me.hp = Math.max(0, me.hp - 2);
        texts.push({ x: me.x, y: me.y - 22, text: 'DROWNING', color: '#67d9ff', life: .7 });
    }
    if (me.hp <= 0)
        me.alive = false;
} const gy = groundY(me.x); if (me.y > gy - 16) {
    if (me.vy > 420) {
        me.hp = Math.max(0, me.hp - Math.round(me.vy / 25));
        texts.push({ x: me.x, y: me.y - 25, text: 'HARD LANDING', color: '#f6c85f', life: 1 });
    }
    me.y = gy - 16;
    me.vy = 0;
    me.grounded = true;
} projectiles.forEach((p, i) => { p.vx += (wind * 2.2) * dt; p.vy += 560 * dt; p.x += p.vx * dt; p.y += p.vy * dt; p.life -= dt; if (p.y > groundY(p.x) || p.life < 0) {
    explode(p.x, p.y, p.w);
    projectiles.splice(i, 1);
    endTurn();
} }); particles.forEach(p => { p.x += p.vx * dt; p.y += p.vy * dt; p.vy += 400 * dt; p.life -= dt; }); particles = particles.filter(p => p.life > 0); texts.forEach(t => { t.y -= 35 * dt; t.life -= dt; }); texts = texts.filter(t => t.life > 0); targetCamX = me.x - W / 2; targetCamY = me.y - H * .54; camX += (targetCamX - camX) * dt * 3; camY += (targetCamY - camY) * dt * 3; shake *= .9; updateHud(); }
function draw() { ctx.save(); const sx = (Math.random() - .5) * shake, sy = (Math.random() - .5) * shake; ctx.translate(sx, sy); ctx.fillStyle = '#091b2a'; ctx.fillRect(0, 0, W, H); const sky = ctx.createLinearGradient(0, 0, 0, H); sky.addColorStop(0, '#102c4c'); sky.addColorStop(.55, '#17435a'); sky.addColorStop(1, '#214c57'); ctx.fillStyle = sky; ctx.fillRect(0, 0, W, H); ctx.fillStyle = '#ffd37a'; ctx.globalAlpha = .85; ctx.beginPath(); ctx.arc(W * .78, H * .2, 45, 0, 7); ctx.fill(); ctx.globalAlpha = 1; for (let i = 0; i < 3; i++) {
    ctx.fillStyle = `rgba(125,211,197,${.08 + i * .03})`;
    ctx.beginPath();
    ctx.ellipse(W * (.2 + i * .35), H * (.27 + i * .06), 180, 25, 0, 0, 7);
    ctx.fill();
} ctx.translate(-camX, -camY); ctx.fillStyle = 'rgba(58,178,205,.24)'; ctx.fillRect(0, H * .86, W, H * .2); ctx.strokeStyle = 'rgba(151,235,226,.45)'; ctx.lineWidth = 2; ctx.beginPath(); for (let x = 0; x < W; x += 18) {
    const yy = H * .86 + Math.sin(x * .035 + performance.now() * .002) * 2;
    x ? ctx.lineTo(x, yy) : ctx.moveTo(x, yy);
} ctx.stroke(); ctx.fillStyle = '#9e5f43'; ctx.beginPath(); ctx.moveTo(0, H + 80); terrain.forEach((y, x) => ctx.lineTo(x, y)); ctx.lineTo(W, H + 80); ctx.closePath(); ctx.fill(); ctx.fillStyle = '#54b89d'; ctx.beginPath(); terrain.forEach((y, x) => { if (x === 0)
    ctx.moveTo(x, y);
else
    ctx.lineTo(x, y); }); ctx.lineTo(W, terrain[W - 1] + 8); ctx.lineTo(0, terrain[0] + 8); ctx.closePath(); ctx.fill(); ctx.strokeStyle = '#b5edb4'; ctx.lineWidth = 2; ctx.beginPath(); terrain.forEach((y, x) => x ? ctx.lineTo(x, y) : ctx.moveTo(x, y)); ctx.stroke(); const aim = worms[active]; if (playing && aim && aim.alive && projectiles.length === 0) {
    ctx.save();
    ctx.setLineDash([4, 8]);
    ctx.strokeStyle = weapons[weaponIndex].color;
    ctx.globalAlpha = .45;
    ctx.lineWidth = 2;
    ctx.beginPath();
    let ax = aim.x, ay = aim.y - 16, avx = Math.cos(angle) * power * 470, avy = Math.sin(angle) * power * 470;
    ctx.moveTo(ax, ay);
    for (let k = 0; k < 34; k++) {
        avx += wind * 2.2 * .08;
        avy += 560 * .08;
        ax += avx * .08;
        ay += avy * .08;
        ctx.lineTo(ax, ay);
        if (ay > groundY(ax))
            break;
    }
    ctx.stroke();
    ctx.restore();
} worms.forEach(w => { if (!w.alive)
    return; ctx.save(); ctx.translate(w.x, w.y); ctx.fillStyle = w.team === 'blue' ? '#67e6c1' : '#ff796f'; ctx.shadowBlur = 12; ctx.shadowColor = ctx.fillStyle; ctx.beginPath(); ctx.ellipse(0, 0, 15, 11, 0, 0, 7); ctx.fill(); ctx.shadowBlur = 0; ctx.fillStyle = '#f3fbeb'; ctx.beginPath(); ctx.arc(-5, -9, 5, 0, 7); ctx.arc(6, -9, 5, 0, 7); ctx.fill(); ctx.fillStyle = '#15313a'; ctx.beginPath(); ctx.arc(-4, -9, 2, 0, 7); ctx.arc(7, -9, 2, 0, 7); ctx.fill(); ctx.strokeStyle = '#f6c85f'; ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo(0, -10); ctx.lineTo(Math.cos(angle) * 25, Math.sin(angle) * 25 - 10); ctx.stroke(); ctx.restore(); }); projectiles.forEach(p => { ctx.fillStyle = p.w.color; ctx.shadowBlur = 16; ctx.shadowColor = p.w.color; ctx.beginPath(); ctx.arc(p.x, p.y, 6, 0, 7); ctx.fill(); ctx.shadowBlur = 0; }); particles.forEach(p => { ctx.globalAlpha = Math.max(0, p.life / p.max); ctx.fillStyle = p.color; ctx.fillRect(p.x, p.y, p.size, p.size); }); texts.forEach(t => { ctx.globalAlpha = t.life; ctx.fillStyle = t.color; ctx.font = '900 16px Barlow Condensed'; ctx.fillText(t.text, t.x, t.y); }); ctx.restore(); requestAnimationFrame(draw); }
function start() { resize(); reset(); playing = true; startCard.style.display = 'none'; last = performance.now(); requestAnimationFrame(loop); }
function loop(t) { const dt = Math.min(.035, (t - last) / 1000); last = t; update(dt); requestAnimationFrame(loop); }
startBtn.addEventListener('click', start);
document.querySelectorAll('.weapon').forEach((e, i) => e.addEventListener('click', () => { weaponIndex = i; updateWeapon(); }));
addEventListener('keydown', e => { keys.add(e.key); if (e.key === 'Tab') {
    e.preventDefault();
    weaponIndex = (weaponIndex + 1) % weapons.length;
    updateWeapon();
} if (e.key === ' ') {
    e.preventDefault();
    if (playing)
        fire();
} if (e.key === 'p') {
    paused = !paused;
    pause.hidden = !paused;
} });
addEventListener('keyup', e => keys.delete(e.key));
canvas.addEventListener('pointerdown', () => { if (playing)
    fire(); });
resize();
reset();
draw();
};

// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"openai/gpt-5.6-luna-pro","provider":"openrouter","persona":"typescript","sessionId":"189ba6f2-d2b7-4968-86f2-7c982691b43d","tokensIn":448388,"tokensOut":26583,"tokensTotal":474971,"turns":13,"toolCalls":19,"failedToolCalls":0,"timestamp":"2026-07-31T03:17:04.338Z"} -->