Worms
Gemini 3.6 Flash · composer

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 22,250 bytes · SHA-256 b0725f19b9ba
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Worms Reloaded - Artillery Mayhem</title>
<style>
/* ── css: main-styles ── */
* {
box-sizing: border-box;
user-select: none;
-webkit-user-select: none;
margin: 0;
padding: 0;
}
body, html {
width: 100%;
height: 100%;
overflow: hidden;
background-color: #0d0e15;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: #fff;
}
#game-container {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
}
#gameCanvas {
display: block;
width: 100%;
height: 100%;
background: #000;
cursor: crosshair;
}
/* UI Overlay Layer */
.ui-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.ui-layer button, .ui-layer input, .overlay-screen button, .overlay-screen input {
pointer-events: auto;
}
/* Top HUD Bar */
#top-bar {
position: absolute;
top: 12px;
left: 12px;
right: 12px;
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 15px;
}
#team-bars {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 200px;
}
.team-hud-row {
display: flex;
align-items: center;
gap: 8px;
background: rgba(15, 18, 28, 0.75);
backdrop-filter: blur(8px);
padding: 4px 10px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 12px rgba(0,0,0,0.4);
}
.team-hud-row.active {
border-color: #fff;
box-shadow: 0 0 10px rgba(255,255,255,0.4);
}
.team-hud-name {
font-size: 13px;
font-weight: 700;
width: 110px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.team-hud-hp-bar {
flex: 1;
height: 12px;
background: rgba(255,255,255,0.15);
border-radius: 6px;
overflow: hidden;
position: relative;
}
.team-hud-hp-fill {
height: 100%;
width: 100%;
transition: width 0.3s ease;
border-radius: 6px;
}
.team-hud-hp-text {
font-size: 11px;
font-weight: bold;
width: 45px;
text-align: right;
}
/* Timer */
#timer-box {
width: 64px;
height: 64px;
background: rgba(15, 18, 28, 0.85);
backdrop-filter: blur(8px);
border-radius: 50%;
border: 3px solid #ffcc00;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 15px rgba(255,204,0,0.4);
position: relative;
}
#timer-text {
font-size: 24px;
font-weight: 900;
color: #fff;
}
#timer-box.urgent {
animation: pulse-urgent 0.5s infinite alternate;
border-color: #ff3344;
box-shadow: 0 0 20px rgba(255,51,68,0.8);
}
@keyframes pulse-urgent {
from { transform: scale(1); }
to { transform: scale(1.15); }
}
/* Wind Meter */
#wind-meter {
background: rgba(15, 18, 28, 0.75);
backdrop-filter: blur(8px);
padding: 6px 14px;
border-radius: 10px;
border: 1px solid rgba(255,255,255,0.1);
display: flex;
flex-direction: column;
align-items: center;
width: 160px;
}
.hud-label {
font-size: 10px;
font-weight: 800;
letter-spacing: 1px;
color: #8a9bb8;
}
#wind-bar-container {
width: 100%;
height: 10px;
background: rgba(255,255,255,0.15);
border-radius: 5px;
margin: 4px 0;
position: relative;
overflow: hidden;
}
#wind-center {
position: absolute;
left: 50%;
top: 0;
width: 2px;
height: 100%;
background: #fff;
z-index: 2;
}
#wind-bar {
position: absolute;
top: 0;
height: 100%;
background: linear-gradient(90deg, #00d2ff, #3a7bd5);
width: 0%;
left: 50%;
transition: all 0.2s ease;
}
#wind-text {
font-size: 12px;
font-weight: 800;
}
/* Turn Banner */
#turn-banner {
position: absolute;
top: 90px;
left: 50%;
transform: translateX(-50%);
background: rgba(10, 12, 20, 0.9);
padding: 10px 30px;
border-radius: 30px;
border: 2px solid #ffcc00;
font-size: 20px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 5px 25px rgba(0,0,0,0.6);
animation: banner-pop 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
@keyframes banner-pop {
0% { transform: translate(-50%, -20px) scale(0.8); opacity: 0; }
100% { transform: translate(-50%, 0) scale(1); opacity: 1; }
}
/* Bottom Hotbar & Selector */
#weapon-hotbar {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 12px;
align-items: center;
background: rgba(15, 18, 28, 0.85);
backdrop-filter: blur(10px);
padding: 8px 16px;
border-radius: 14px;
border: 1px solid rgba(255,255,255,0.15);
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
}
#weapon-menu-btn {
background: linear-gradient(135deg, #ff8c00, #ff4500);
border: none;
color: white;
padding: 8px 16px;
border-radius: 8px;
font-weight: bold;
cursor: pointer;
transition: transform 0.1s, box-shadow 0.1s;
}
#weapon-menu-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255, 69, 0, 0.4);
}
#selected-weapon-display {
display: flex;
align-items: center;
gap: 10px;
background: rgba(0,0,0,0.4);
padding: 6px 14px;
border-radius: 8px;
font-weight: bold;
}
#cur-wpn-icon { font-size: 20px; }
#cur-wpn-name { font-size: 15px; color: #fff; }
#cur-wpn-ammo { font-size: 13px; color: #ffcc00; }
/* Weapon Grid Popup */
#weapon-selector {
position: absolute;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
background: rgba(15, 18, 28, 0.95);
backdrop-filter: blur(12px);
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.2);
padding: 16px;
width: 420px;
max-width: 90vw;
box-shadow: 0 10px 40px rgba(0,0,0,0.8);
}
.weapon-grid-header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
font-weight: 800;
color: #8a9bb8;
margin-bottom: 12px;
}
#close-weapon-menu {
background: none;
border: none;
color: #aaa;
font-size: 16px;
cursor: pointer;
}
#weapon-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
}
.wpn-card {
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 10px;
padding: 8px;
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
transition: all 0.15s ease;
}
.wpn-card:hover {
background: rgba(255,255,255,0.15);
border-color: #ffcc00;
transform: translateY(-2px);
}
.wpn-card.active {
background: rgba(255, 204, 0, 0.2);
border-color: #ffcc00;
}
.wpn-card.disabled {
opacity: 0.3;
pointer-events: none;
}
.wpn-icon { font-size: 24px; margin-bottom: 4px; }
.wpn-title { font-size: 11px; font-weight: 700; text-align: center; }
.wpn-ammo { font-size: 10px; color: #ffcc00; margin-top: 2px; }
/* Overlays / Screens */
.overlay-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(8, 10, 16, 0.85);
backdrop-filter: blur(12px);
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
}
.menu-card {
background: linear-gradient(145deg, #161b26, #0e121a);
border: 1px solid rgba(255,255,255,0.15);
border-radius: 20px;
padding: 32px;
width: 480px;
max-width: 90vw;
box-shadow: 0 20px 60px rgba(0,0,0,0.8);
display: flex;
flex-direction: column;
gap: 20px;
}
.title-logo {
font-size: 32px;
font-weight: 900;
text-align: center;
letter-spacing: 2px;
color: #fff;
}
.title-logo span {
color: #ff4500;
display: inline-block;
}
.subtitle {
text-align: center;
font-size: 13px;
color: #8a9bb8;
margin-top: -16px;
}
.menu-section h3 {
font-size: 12px;
font-weight: 800;
color: #8a9bb8;
letter-spacing: 1px;
margin-bottom: 8px;
}
.button-group {
display: flex;
gap: 8px;
}
.map-btn, .type-btn {
flex: 1;
padding: 8px 12px;
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.1);
color: #ccc;
border-radius: 8px;
font-size: 12px;
font-weight: 700;
cursor: pointer;
transition: all 0.15s;
}
.map-btn.active, .type-btn.active {
background: rgba(255, 69, 0, 0.25);
border-color: #ff4500;
color: #fff;
}
#teams-config {
display: flex;
flex-direction: column;
gap: 8px;
}
.team-config-row {
display: flex;
align-items: center;
gap: 8px;
background: rgba(0,0,0,0.3);
padding: 6px 10px;
border-radius: 8px;
}
.team-dot {
width: 12px;
height: 12px;
border-radius: 50%;
}
.team-name-input {
flex: 1;
background: transparent;
border: none;
color: white;
font-size: 13px;
font-weight: bold;
outline: none;
}
.primary-btn {
background: linear-gradient(135deg, #ff4500, #ff8c00);
border: none;
color: white;
padding: 14px;
border-radius: 12px;
font-size: 16px;
font-weight: 900;
letter-spacing: 1px;
cursor: pointer;
transition: transform 0.15s, box-shadow 0.15s;
box-shadow: 0 6px 20px rgba(255, 69, 0, 0.4);
}
.primary-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(255, 69, 0, 0.6);
}
.victory-card {
text-align: center;
align-items: center;
}
#victory-title {
font-size: 36px;
font-weight: 900;
color: #ffcc00;
}
.hidden { display: none !important; }
</style>
</head>
<body>
<div id="game-container">
<canvas id="gameCanvas"></canvas>
<!-- HUD Overlay -->
<div id="hud-overlay" class="ui-layer">
<div id="top-bar">
<div id="team-bars"></div>
<div id="timer-box">
<div id="timer-ring"></div>
<div id="timer-text">45</div>
</div>
<div id="wind-meter">
<div class="hud-label">WIND</div>
<div id="wind-bar-container">
<div id="wind-bar"></div>
<div id="wind-center"></div>
</div>
<div id="wind-text">0</div>
</div>
</div>
<div id="turn-banner" class="hidden">
<span id="turn-team-name">Red Team</span>'s Turn!
</div>
<div id="weapon-hotbar">
<button id="weapon-menu-btn" title="Weapon Menu (Right Click / W)">📦 WEAPONS</button>
<div id="selected-weapon-display">
<span id="cur-wpn-icon">🚀</span>
<span id="cur-wpn-name">Bazooka</span>
<span id="cur-wpn-ammo">∞</span>
</div>
</div>
<!-- Full Weapon Selector Grid -->
<div id="weapon-selector" class="hidden">
<div class="weapon-grid-header">SELECT WEAPON <button id="close-weapon-menu">✕</button></div>
<div id="weapon-grid"></div>
</div>
</div>
<!-- Main Menu Overlay -->
<div id="main-menu" class="overlay-screen">
<div class="menu-card">
<h1 class="title-logo">WORMS <span>RELOADED</span></h1>
<p class="subtitle">Tactical Artillery Mayhem</p>
<div class="menu-section">
<h3>MAP SETTINGS</h3>
<div class="button-group">
<button class="map-btn active" data-map="island">🏝️ Single Island</button>
<button class="map-btn" data-map="two_islands">🏝️🏝️ Two Islands</button>
<button class="map-btn" data-map="cave">🦇 Cave</button>
</div>
</div>
<div class="menu-section">
<h3>TEAMS CONFIGURATION</h3>
<div id="teams-config">
<div class="team-config-row" data-team="0">
<span class="team-dot" style="background:#ff3355;"></span>
<input type="text" value="Red Commandos" class="team-name-input">
<button class="type-btn active" data-type="human">HUMAN</button>
<button class="type-btn" data-type="cpu">CPU (AI)</button>
</div>
<div class="team-config-row" data-team="1">
<span class="team-dot" style="background:#3388ff;"></span>
<input type="text" value="Blue Ninjas" class="team-name-input">
<button class="type-btn" data-type="human">HUMAN</button>
<button class="type-btn active" data-type="cpu">CPU (AI)</button>
</div>
<div class="team-config-row" data-team="2">
<span class="team-dot" style="background:#33cc55;"></span>
<input type="text" value="Green Gunners" class="team-name-input">
<button class="type-btn disabled" data-type="off">OFF</button>
<button class="type-btn" data-type="human">HUMAN</button>
<button class="type-btn" data-type="cpu">CPU</button>
</div>
<div class="team-config-row" data-team="3">
<span class="team-dot" style="background:#ffaa00;"></span>
<input type="text" value="Yellow Bombers" class="team-name-input">
<button class="type-btn disabled" data-type="off">OFF</button>
<button class="type-btn" data-type="human">HUMAN</button>
<button class="type-btn" data-type="cpu">CPU</button>
</div>
</div>
</div>
<button id="start-game-btn" class="primary-btn">START BATTLE 🚀</button>
</div>
</div>
<!-- Game Over Screen -->
<div id="game-over-screen" class="overlay-screen hidden">
<div class="menu-card victory-card">
<h2 id="victory-title">VICTORY!</h2>
<p id="victory-subtitle">Red Commandos wins the match!</p>
<button id="restart-btn" class="primary-btn">PLAY AGAIN 🔄</button>
</div>
</div>
</div>
<script>
'use strict';
// ── class: SoundFX ──
// Web Audio Synthesizer for procedural retro sound effects
class SoundFX {
constructor() {
this.ctx = null;
this.enabled = true;
}
init() {
if (!this.ctx) {
const AudioCtx = window.AudioContext || window.webkitAudioContext;
if (AudioCtx) this.ctx = new AudioCtx();
}
if (this.ctx && this.ctx.state === 'suspended') {
this.ctx.resume();
}
}
playTone(freq, duration, type = 'sine', volume = 0.2) {
if (!this.enabled) return;
this.init();
if (!this.ctx) return;
try {
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = type;
osc.frequency.setValueAtTime(freq, this.ctx.currentTime);
gain.gain.setValueAtTime(volume, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + duration);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start();
osc.stop(this.ctx.currentTime + duration);
} catch(e){}
}
playUiClick() {
this.playTone(800, 0.05, 'square', 0.1);
}
playShoot() {
this.init();
if (!this.ctx) return;
try {
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(400, this.ctx.currentTime);
osc.frequency.exponentialRampToValueAtTime(80, this.ctx.currentTime + 0.2);
gain.gain.setValueAtTime(0.3, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.2);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start();
osc.stop(this.ctx.currentTime + 0.2);
} catch(e){}
}
playExplosion(size = 1.0) {
this.init();
if (!this.ctx) return;
try {
const bufferSize = this.ctx.sampleRate * 0.4 * size;
const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = Math.random() * 2 - 1;
}
const noise = this.ctx.createBufferSource();
noise.buffer = buffer;
const filter = this.ctx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.setValueAtTime(600 / size, this.ctx.currentTime);
filter.frequency.linearRampToValueAtTime(100, this.ctx.currentTime + 0.4 * size);
const gain = this.ctx.createGain();
gain.gain.setValueAtTime(0.5, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.4 * size);
noise.connect(filter);
filter.connect(gain);
gain.connect(this.ctx.destination);
noise.start();
} catch(e){}
}
playJump() {
this.init();
if (!this.ctx) return;
try {
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(150, this.ctx.currentTime);
osc.frequency.exponentialRampToValueAtTime(450, this.ctx.currentTime + 0.15);
gain.gain.setValueAtTime(0.25, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.15);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start();
osc.stop(this.ctx.currentTime + 0.15);
} catch(e){}
}
playSplash() {
this.playTone(180, 0.3, 'triangle', 0.3);
}
playTimerTick(isUrgent = false) {
this.playTone(isUrgent ? 900 : 500, 0.04, 'sine', isUrgent ? 0.2 : 0.08);
}
}
// ── const: audioFX ──
const audioFX = new SoundFX();
// ── const: WEAPONS ──
// Weapons and Utilities configuration
const WEAPONS = [
{
id: 'bazooka',
name: 'Bazooka',
icon: '🚀',
type: 'indirect',
category: 'Weapon',
ammo: Infinity,
radius: 35,
damage: 45,
affectedByWind: true,
bouncy: false,
timer: null,
desc: 'Classic rocket influenced by wind.'
},
{
id: 'grenade',
name: 'Grenade',
icon: '💣',
type: 'indirect',
category: 'Weapon',
ammo: Infinity,
radius: 40,
damage: 50,
affectedByWind: false,
bouncy: true,
fuse: 3.0,
desc: '3-second timed bouncy explosive.'
},
{
id: 'shotgun',
name: 'Shotgun',
icon: '🔫',
type: 'direct',
category: 'Weapon',
ammo: 5,
radius: 18,
damage: 25,
affectedByWind: false,
desc: 'Direct line-of-sight attack. 2 shots per turn.'
},
{
id: 'sniper',
name: 'Sniper Rifle',
icon: '🎯',
type: 'direct',
category: 'Weapon',
ammo: 3,
radius: 12,
damage: 60,
affectedByWind: false,
desc: 'High precision laser bullet with high pushback.'
},
{
id: 'cluster_bomb',
name: 'Cluster Bomb',
icon: '💥',
type: 'indirect',
category: 'Weapon',
ammo: 3,
radius: 25,
damage: 30,
affectedByWind: true,
bouncy: true,
fuse: 2.5,
clusters: 5,
desc: 'Explodes into 5 mini bomblets.'
},
{
id: 'holy_grenade',
name: 'Holy Bomb',
icon: '✨',
type: 'indirect',
category: 'Weapon',
ammo: 1,
radius: 75,
damage: 85,
affectedByWind: false,
bouncy: true,
fuse: 3.0,
desc: 'Devastating high-AoE bomb. HALLELUJAH!'
},
{
id: 'airstrike',
name: 'Air Strike',
icon: '✈️',
type: 'map_alter',
category: 'Weapon',
ammo: 2,
radius: 30,
damage: 35,
bombs: 5,
desc: 'Target map location for heavy carpet bombing.'
},
{
id: 'drill_rocket',
name: 'Drill Rocket',
icon: '🌀',
type: 'map_alter',
category: 'Weapon',
ammo: 2,
radius: 25,
damage: 40,
desc: 'Bores deep into terrain before exploding.'
},
{
id: 'girder',
name: 'Bridge Girder',
icon: '🏗️',
type: 'utility',
category: 'Utility',
ammo: 3,
desc: 'Place a steel girder to cross gaps or block shots.'
},
{
id: 'teleport',
name: 'Teleporter',
icon: '🌀',
type: 'utility',
category: 'Utility',
ammo: 2,
desc: 'Instantly beam active worm to any target location.'
}
];
// ── class: FloatingText ──
// Physics Engine, Particle System, Terrain Debris, Floating Text
class FloatingText {
constructor(x, y, text, color = '#ff3344', scale = 1.0) {
this.x = x;
this.y = y;
this.text = text;
this.color = color;
this.scale = scale;
this.vy = -1.5;
this.alpha = 1.0;
this.life = 1.0;
}
update(dt) {
this.y += this.vy;
this.life -= dt * 0.8;
this.alpha = Math.max(0, this.life);
}
draw(ctx) {
ctx.save();
ctx.globalAlpha = this.alpha;
ctx.fillStyle = this.color;
ctx.strokeStyle = '#000';
ctx.lineWidth = 3;
ctx.font = `900 ${Math.round(18 * this.scale)}px sans-serif`;
ctx.textAlign = 'center';
ctx.strokeText(this.text, this.x, this.y);
ctx.fillText(this.text, this.x, this.y);
ctx.restore();
}
}
// ── class: Particle ──
class Particle {
constructor(x, y, vx, vy, radius, color, life = 1.0, isSmoke = false) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.radius = radius;
this.color = color;
this.life = life;
this.maxLife = life;
this.isSmoke = isSmoke;
}
update(dt, windX = 0) {
this.x += (this.vx + windX * (this.isSmoke ? 0.3 : 0.05)) * dt * 60;
this.y += this.vy * dt * 60;
if (!this.isSmoke) this.vy += 0.2; // Gravity
this.life -= dt;
}
draw(ctx) {
if (this.life <= 0) return;
ctx.save();
const progress = this.life / this.maxLife;
ctx.globalAlpha = Math.max(0, progress);
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius * (this.isSmoke ? (2 - progress) : progress), 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
// ── class: TerrainDebrisChunk ──
class TerrainDebrisChunk {
constructor(x, y, vx, vy, size, color) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.size = size;
this.color = color;
this.rotation = Math.random() * Math.PI * 2;
this.vRot = (Math.random() - 0.5) * 0.2;
this.active = true;
}
update(dt, terrain, windX) {
if (!this.active) return;
this.vy += 0.3; // Gravity
this.vx += windX * 0.02;
this.x += this.vx;
this.y += this.vy;
this.rotation += this.vRot;
// Check collision with water or terrain
if (this.y >= terrain.height - 30) {
this.active = false; // Submerged
return;
}
if (terrain.isSolid(Math.round(this.x), Math.round(this.y))) {
this.vx *= -0.3;
this.vy *= -0.3;
if (Math.hypot(this.vx, this.vy) < 0.5) {
this.active = false;
}
}
}
draw(ctx) {
if (!this.active) return;
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
ctx.fillStyle = this.color;
ctx.fillRect(-this.size/2, -this.size/2, this.size, this.size);
ctx.restore();
}
}
</script>
</body>
</html>