Worms
GPT 5.6 terra · typescript

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 18,491 bytes · SHA-256 f343bed6c651
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Wormfall — Tactical Artillery</title>
<style>
/* ── css: main ── */
@import url('https://fonts.googleapis.com/css2?family=Fredoka+One&family=Nunito:wght@700;800;900&display=swap');*{box-sizing:border-box}html,body,#app,#game{margin:0;width:100%;height:100%;overflow:hidden;background:#07172d;font-family:Nunito,Arial,sans-serif;color:#fff}canvas{position:absolute;inset:0;width:100%;height:100%;cursor:crosshair}#hud{position:absolute;inset:0;pointer-events:none}.topbar{display:flex;align-items:flex-start;justify-content:space-between;padding:18px 28px;text-shadow:0 2px 3px #001}.brand,.logo{font-family:'Fredoka One',Arial;font-size:28px;letter-spacing:1px;color:#f4fcff}.brand span,.logo span{color:#ffcb4b}.brand i{color:#72e9e1;font-style:normal;font-size:20px}#turnCard{position:absolute;left:50%;transform:translateX(-50%);text-align:center;min-width:240px;background:#092446cc;border:1px solid #78d8e688;border-radius:14px;padding:7px 14px 9px;box-shadow:0 8px 22px #0018}.turnLabel{font-size:9px;letter-spacing:2px;color:#76dce8}.turnLabel,#wind span{font-weight:900}.turnLabel+#turnName{font-size:14px;font-weight:900;color:#ffcf4e;letter-spacing:.5px}#timer{position:absolute;right:-40px;top:6px;width:36px;height:36px;border-radius:50%;border:2px solid #ffcf4e;background:#0a2547;display:grid;place-items:center;font-weight:900;font-size:16px}.timerTrack{height:4px;background:#0a1930;border-radius:5px;margin-top:6px;overflow:hidden}#timerFill{height:100%;background:linear-gradient(90deg,#62e1d5,#ffcb4b);width:100%;transition:width .2s}#wind{display:flex;gap:8px;align-items:center;background:#092446aa;border:1px solid #78d8e655;border-radius:12px;padding:7px 11px;color:#a5eaf0;font-size:11px}#wind b{font-size:21px;color:#76e6ed;display:inline-block}#wind strong{color:#fff;font-size:13px}#unitPanel{position:absolute;left:22px;top:88px;display:flex;gap:7px;flex-direction:column}.unitCard{width:160px;border-radius:9px;background:#092446c9;border:1px solid #ffffff22;padding:6px 9px;display:flex;align-items:center;gap:8px;font-size:11px}.pip{width:11px;height:11px;border-radius:50%;background:var(--c);box-shadow:0 0 8px var(--c)}.hp{margin-left:auto;color:#fff;font-weight:900}.hp small{color:#8db4ca}#weaponBar{position:absolute;bottom:24px;left:50%;transform:translateX(-50%);display:flex;gap:7px;pointer-events:auto}.weapon{width:82px;height:62px;border-radius:12px;border:1px solid #ffffff33;background:#092646e8;color:#c8edf1;display:flex;flex-direction:column;justify-content:center;align-items:center;cursor:pointer;font-family:inherit;font-weight:900;font-size:10px;transition:.15s;box-shadow:0 5px 12px #0018}.weapon:hover,.weapon.active{transform:translateY(-5px);background:#16466a;border-color:#ffcf4e;box-shadow:0 0 0 2px #ffcf4e55}.weapon .icon{font-size:24px;line-height:25px}.weapon em{font-style:normal;color:#ffcf4e;font-size:9px;margin-top:2px}#hint{position:absolute;bottom:6px;width:100%;text-align:center;font-size:10px;color:#8bb9ca;letter-spacing:.6px}#start{position:absolute;inset:0;z-index:3;display:flex;flex-direction:column;align-items:center;justify-content:center;background:radial-gradient(circle at 50% 45%,#164978cc,#051125e8);text-align:center}.logo{font-size:78px;text-shadow:0 6px 0 #08213b,0 12px 26px #0008}.logo span{color:#ffc844}#start p{letter-spacing:4px;font-size:12px;color:#8be4e9;margin:5px 0 30px}.modeRow{display:flex;gap:8px;margin-bottom:17px}button{font-family:inherit;font-weight:900;color:#bdebf0;background:#0c3155;border:1px solid #58a9bd;border-radius:9px;padding:10px 15px;cursor:pointer;transition:.15s;font-size:11px}button:hover,button.selected{background:#1e5776;color:#fff;border-color:#ffca48;transform:translateY(-2px)}#launch{font-size:17px;color:#09213a;background:#ffca48;border:0;padding:14px 24px;box-shadow:0 5px 0 #b27619}.micro{margin-top:22px;color:#7da8c4;font-size:10px;letter-spacing:2px}#toast{position:absolute;top:135px;left:50%;transform:translateX(-50%);font-family:'Fredoka One';font-size:25px;color:#fff4ca;text-shadow:0 3px 2px #472a19;opacity:0;transition:.25s}@media(max-width:700px){.logo{font-size:54px}.weapon{width:57px;height:53px;font-size:8px}.topbar{padding:12px}.brand{font-size:22px}#unitPanel{left:8px;top:71px}.unitCard{width:125px}.unitCard:nth-child(n+4){display:none}}
</style>
</head>
<body>
<div id="game"><canvas id="canvas"></canvas><div id="hud"><div class="topbar"><div class="brand"><span>WORM</span>FALL <i>✦</i></div><div id="turnCard"><div class="turnLabel">ACTIVE TURN</div><div id="turnName">CRIMSON CORSAIRS</div><div id="timer">30</div><div class="timerTrack"><div id="timerFill"></div></div></div><div id="wind"><span>WIND</span><b id="windArrow">➜</b><strong id="windValue">2.4</strong></div></div><div id="unitPanel"></div><div id="weaponBar"></div><div id="hint">A / D MOVE · W JUMP · ← → AIM · SPACE FIRE</div></div><div id="start"><div class="logo"><span>WORM</span>FALL</div><p>TACTICAL ARTILLERY IN THE WILD BLUE YONDER</p><div class="modeRow"><button data-mode="islands">TWO ISLANDS</button><button data-mode="island" class="selected">SINGLE ISLAND</button><button data-mode="cave">CAVERN</button></div><button id="launch">BEGIN SKIRMISH <b>↗</b></button><div class="micro">2 TEAMS · 4 WORMS EACH · HOT SEAT + CPU</div></div><div id="toast"></div></div>
<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('#canvas'), ctx = canvas.getContext('2d');
const start = document.querySelector('#start'), launch = document.querySelector('#launch'), turnName = document.querySelector('#turnName'), timerEl = document.querySelector('#timer'), fill = document.querySelector('#timerFill'), windEl = document.querySelector('#windValue'), windArrow = document.querySelector('#windArrow'), weaponBar = document.querySelector('#weaponBar'), units = document.querySelector('#unitPanel'), toast = document.querySelector('#toast');
let W = innerWidth, H = innerHeight, dpr = 1, running = false, terrain, mask, worms = [], shots = [], particles = [], turn = 0, time = 30, last = 0, wind = 0, weapon = 0, aim = -55, power = 0.76, shake = 0, freeze = 0, camX = 0, camY = 0, zoom = 1, aiWait = 0, mode = 'island';
const teams = [{ name: 'CRIMSON CORSAIRS', color: '#ff5d62', cpu: false }, { name: 'TEAL TERRORS', color: '#42d7ca', cpu: true }];
const weapons = [['🍌', 'BANANA', 'INDIRECT', 38], ['🚀', 'BAZOOKA', 'INDIRECT', 31], ['⚡', 'ZAPPER', 'INSTANT', 18], ['💣', 'MEGA BOMB', 'HIGH AoE', 62], ['🪃', 'BOOMER', 'CURVE', 28], ['🧊', 'ICE CUBE', 'UTILITY', 10]];
function resize() { dpr = devicePixelRatio; canvas.width = W = innerWidth * dpr; canvas.height = H = innerHeight * dpr; canvas.style.width = innerWidth + 'px'; canvas.style.height = innerHeight + 'px'; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); W = innerWidth; H = innerHeight; }
addEventListener('resize', resize);
resize();
function setupTerrain() { const c = document.createElement('canvas'); c.width = W; c.height = H; const x = c.getContext('2d'); x.clearRect(0, 0, W, H); x.fillStyle = '#163d35'; if (mode === 'cave') {
x.fillRect(0, H * .65, W, H);
x.fillRect(0, 0, W, H * .16);
for (let i = 0; i < 13; i++) {
x.beginPath();
x.arc(i * W / 12, H * .18, 90 + Math.random() * 70, 0, Math.PI);
x.fill();
}
}
else {
const islands = mode === 'islands' ? 2 : 1;
for (let q = 0; q < islands; q++) {
let cx = (q + .5) * W / islands, base = H * (.69 + q * .03);
x.beginPath();
x.moveTo(cx - W / (islands * 2), H);
for (let px = cx - W / (islands * 2); px <= cx + W / (islands * 2); px += 12) {
let y = base + Math.sin(px * .018 + q) * 25 + Math.sin(px * .052) * 13;
x.lineTo(px, y);
}
x.lineTo(cx + W / (islands * 2), H);
x.closePath();
x.fill();
}
} terrain = x.getImageData(0, 0, W, H); mask = new Uint8Array(W * H); for (let i = 0; i < mask.length; i++)
mask[i] = terrain.data[i * 4 + 3] > 0 ? 1 : 0; }
function solid(x, y) { let ix = x | 0, iy = y | 0; return ix >= 0 && ix < W && iy >= 0 && iy < H && mask[iy * W + ix] === 1; }
function groundY(x) { for (let y = 0; y < H; y += 2)
if (solid(x, y))
return y; return H - 20; }
function spawn() { worms = []; for (let t = 0; t < 2; t++)
for (let i = 0; i < 4; i++) {
let x = (t ? (.68 + i * .065) : (.13 + i * .065)) * W;
if (mode === 'islands')
x = (t ? (.64 + i * .055) : (.15 + i * .055)) * W;
worms.push({ x, y: groundY(x) - 13, vx: 0, vy: 0, team: t, hp: 100, alive: true, name: ['Midge', 'Bubbles', 'Grub', 'Nibbles'][i], ground: true });
} }
function ui() { weaponBar.innerHTML = weapons.map((w, i) => `<button class="weapon ${i === weapon ? 'active' : ''}" data-w="${i}"><span class="icon">${w[0]}</span>${w[1]}<em>${w[2]}</em></button>`).join(''); weaponBar.querySelectorAll('button').forEach(b => b.addEventListener('click', () => { weapon = +b.dataset.w; ui(); beep(430, .04); })); units.innerHTML = worms.filter(w => w.alive).map(w => `<div class="unitCard"><i class="pip" style="--c:${teams[w.team].color}"></i>${w.name}<span class="hp">${w.hp}<small> HP</small></span></div>`).join(''); }
function begin() { start.style.display = 'none'; running = true; setupTerrain(); spawn(); turn = 0; time = 30; wind = (Math.random() - .5) * 1.2; ui(); toastText('ROUND 1 — FIRE AT WILL'); last = performance.now(); requestAnimationFrame(loop); }
function beep(freq, dur) { try {
let a = new AudioContext(), o = a.createOscillator(), g = a.createGain();
o.frequency.value = freq;
g.gain.value = .04;
o.connect(g);
g.connect(a.destination);
o.start();
g.gain.exponentialRampToValueAtTime(.001, a.currentTime + dur);
o.stop(a.currentTime + dur);
}
catch { } }
function toastText(s) { toast.textContent = s; toast.style.opacity = '1'; setTimeout(() => toast.style.opacity = '0', 1400); }
document.querySelectorAll('[data-mode]').forEach(b => b.addEventListener('click', () => { mode = b.dataset.mode; document.querySelectorAll('[data-mode]').forEach(x => x.classList.remove('selected')); b.classList.add('selected'); beep(380, .04); }));
launch.onclick = begin;
function active() { let candidates = worms.filter(w => w.alive && w.team === turn); return candidates.length ? candidates[Math.floor(Date.now() / 999999) % candidates.length] : worms.find(w => w.alive); }
const keys = new Set();
addEventListener('keydown', e => { keys.add(e.key.toLowerCase()); if (e.key === ' ') {
e.preventDefault();
if (running && !teams[turn].cpu)
fire();
} if (e.key >= '1' && e.key <= '6') {
weapon = +e.key - 1;
ui();
} });
addEventListener('keyup', e => keys.delete(e.key.toLowerCase()));
function fire() { let w = active(); if (!w || shots.length)
return; let dir = Math.cos(aim * Math.PI / 180), up = Math.sin(aim * Math.PI / 180); if (weapon === 2) {
zap(w);
endTurn();
return;
} shots.push({ x: w.x + dir * 12, y: w.y + up * 12, vx: dir * (8 + power * 8), vy: up * (8 + power * 8), kind: weapon, life: 0 }); beep(weapon === 3 ? 80 : 230, .09); }
function zap(w) { let dx = Math.cos(aim * Math.PI / 180), dy = Math.sin(aim * Math.PI / 180), hit; for (let d = 0; d < 700; d += 5) {
let x = w.x + dx * d, y = w.y + dy * d;
if (solid(x, y))
break;
hit = worms.find(o => o.alive && o.team !== w.team && Math.hypot(o.x - x, o.y - y) < 12);
if (hit)
break;
} if (hit) {
hurt(hit, 18);
explosion(hit.x, hit.y, 18, '#92f5ff');
}
else
explosion(w.x + dx * 350, w.y + dy * 350, 5, '#92f5ff'); }
function explosion(x, y, r, col) { for (let yy = Math.max(0, y - r) | 0; yy < Math.min(H, y + r); yy++)
for (let xx = Math.max(0, x - r) | 0; xx < Math.min(W, x + r); xx++)
if ((xx - x) ** 2 + (yy - y) ** 2 < r * r) {
mask[yy * W + xx] = 0;
terrain.data[(yy * W + xx) * 4 + 3] = 0;
} worms.forEach(w => { let d = Math.hypot(w.x - x, w.y - y); if (w.alive && d < r * 2.4) {
let dam = Math.max(2, Math.round((1 - d / (r * 2.4)) * 55));
hurt(w, dam);
w.vx += (w.x - x) / (d + 1) * r * .22;
w.vy -= r * .22;
} }); for (let i = 0; i < r * 3; i++)
particles.push({ x, y, vx: (Math.random() - .5) * r * .35, vy: (Math.random() - .8) * r * .3, life: 25 + Math.random() * 25, color: i % 3 ? '#e5a85a' : '#fff0a1', size: 2 + Math.random() * 4 }); shake = Math.max(shake, r * .18); if (r > 40)
freeze = 5; beep(70, .12); }
function hurt(w, n) { w.hp -= n; particles.push({ x: w.x, y: w.y - 20, vx: 0, vy: -1, life: 55, color: '#fff4c5', size: 0 }); toastText('-' + n + ' HP'); if (w.hp <= 0) {
w.alive = false;
explosion(w.x, w.y, 18, '#fff');
} ui(); }
function endTurn() { setTimeout(() => { turn = (turn + 1) % 2; time = 30; wind = (Math.random() - .5) * 1.4; toastText(teams[turn].name + ' TURN'); ui(); }, 500); }
function update(dt) { if (freeze) {
freeze--;
return;
} let w = active(); if (!w)
return; if (!teams[turn].cpu) {
if (keys.has('a'))
w.vx -= .22;
if (keys.has('d'))
w.vx += .22;
if (keys.has('w') && w.ground) {
w.vy = -7;
w.ground = false;
beep(300, .05);
}
if (keys.has('arrowleft'))
aim -= 1;
if (keys.has('arrowright'))
aim += 1;
if (keys.has('arrowup'))
power = Math.min(1, power + .01);
if (keys.has('arrowdown'))
power = Math.max(.25, power - .01);
}
else {
aiWait += dt;
if (aiWait > 1.5) {
let targets = worms.filter(o => o.alive && o.team !== turn);
let target = targets.sort((a, b) => Math.abs(a.x - w.x) - Math.abs(b.x - w.x))[0];
aim = target.x > w.x ? -48 : -132;
power = Math.min(1, Math.abs(target.x - w.x) / 600 + .45);
weapon = Math.random() < .28 ? 3 : 1;
fire();
aiWait = 0;
}
} time -= dt; if (time <= 0 && !shots.length)
endTurn(); worms.forEach(o => { if (!o.alive)
return; o.vy += .24; o.vx *= .93; let nx = o.x + o.vx, ny = o.y + o.vy; o.ground = false; if (solid(nx, ny + 11)) {
while (solid(nx, ny + 11))
ny -= 1;
o.vy = 0;
o.ground = true;
}
else if (solid(nx + (o.vx > 0 ? 7 : -7), ny)) {
o.vx = 0;
}
else
o.x = nx; o.y = ny; if (o.y > H - 5) {
hurt(o, 100);
o.alive = false;
} }); shots = shots.filter(s => { s.life++; s.vx += wind * .012; s.vy += .16; if (s.kind === 4)
s.vy -= .045 * Math.sin(s.life * .1); s.x += s.vx; s.y += s.vy; if (s.x < 0 || s.x > W || s.y > H || s.life > 240) {
explosion(s.x, s.y, weapons[s.kind][3], '#f7c658');
endTurn();
return false;
} if (solid(s.x, s.y)) {
explosion(s.x, s.y, weapons[s.kind][3], '#f7c658');
endTurn();
return false;
} return true; }); particles = particles.filter(p => { p.x += p.vx; p.y += p.vy; p.vy += .12; p.life--; return p.life > 0; }); }
function draw() { ctx.clearRect(0, 0, W, H); let grad = ctx.createLinearGradient(0, 0, 0, H); grad.addColorStop(0, '#0a315c'); grad.addColorStop(.7, '#176483'); grad.addColorStop(1, '#7db6aa'); ctx.fillStyle = grad; ctx.fillRect(0, 0, W, H); ctx.fillStyle = '#ffffff18'; for (let i = 0; i < 30; i++) {
ctx.beginPath();
ctx.arc((i * 83) % W, (i * 47) % 300, 1.5, 0, 7);
ctx.fill();
} ctx.putImageData(terrain, 0, 0); ctx.fillStyle = '#255b45'; ctx.globalAlpha = .6; ctx.fillRect(0, H * .88, W, H * .12); ctx.globalAlpha = 1; worms.forEach(w => { if (!w.alive)
return; ctx.save(); ctx.translate(w.x, w.y); ctx.fillStyle = '#0005'; ctx.beginPath(); ctx.ellipse(0, 12, 12, 4, 0, 0, 7); ctx.fill(); ctx.fillStyle = teams[w.team].color; ctx.beginPath(); ctx.ellipse(0, 0, 11, 13, 0, 0, 7); ctx.fill(); ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(4, -4, 4, 0, 7); ctx.fill(); ctx.fillStyle = '#10233a'; ctx.beginPath(); ctx.arc(5, -4, 1.5, 0, 7); ctx.fill(); ctx.restore(); ctx.fillStyle = '#081f34cc'; ctx.fillRect(w.x - 15, w.y - 23, 30, 4); ctx.fillStyle = teams[w.team].color; ctx.fillRect(w.x - 15, w.y - 23, 30 * Math.max(0, w.hp) / 100, 4); }); let a = active(); if (a && !teams[turn].cpu) {
ctx.save();
ctx.strokeStyle = '#fff6a9aa';
ctx.setLineDash([4, 5]);
ctx.beginPath();
let vx = Math.cos(aim * Math.PI / 180) * (8 + power * 8), vy = Math.sin(aim * Math.PI / 180) * (8 + power * 8);
for (let t = 0; t < 35; t++) {
let x = a.x + vx * t, y = a.y + vy * t + .08 * t * t;
if (t === 0)
ctx.moveTo(x, y);
else
ctx.lineTo(x, y);
}
ctx.stroke();
ctx.restore();
} shots.forEach(s => { ctx.fillStyle = s.kind === 3 ? '#ff7a42' : '#ffd65a'; ctx.beginPath(); ctx.arc(s.x, s.y, s.kind === 3 ? 8 : 5, 0, 7); ctx.fill(); }); particles.forEach(p => { ctx.globalAlpha = Math.min(1, p.life / 18); ctx.fillStyle = p.color; if (p.size > 0)
ctx.fillRect(p.x, p.y, p.size, p.size); ctx.globalAlpha = 1; }); }
function loop(now) { let dt = Math.min(.05, (now - last) / 1000); last = now; if (running) {
update(dt);
draw();
let t = Math.max(0, Math.ceil(time));
timerEl.textContent = t + '';
fill.style.width = (time / 30 * 100) + '%';
timerEl.style.transform = `scale(${time < 8 ? 1.18 : 1})`;
windEl.textContent = Math.abs(wind * 10).toFixed(1);
windArrow.style.transform = `scaleX(${wind < 0 ? -1 : 1})`;
requestAnimationFrame(loop);
} }
};
// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"openai/gpt-5.6-terra","provider":"openrouter","persona":"typescript","sessionId":"937cec1f-f366-420e-a2ff-93f2803b193d","tokensIn":135932,"tokensOut":6525,"tokensTotal":142457,"turns":16,"toolCalls":15,"failedToolCalls":1,"timestamp":"2026-07-31T18:05:54.663Z"} -->