← Worms results

Worms

seed 2.0 code · full

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 121,560 bytes · SHA-256 3197284e04ac
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WORMS: Tactical Chaos</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
            min-height: 100vh;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            overflow: hidden;
            color: white;
        }

        #gameContainer {
            position: relative;
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        #gameCanvas {
            border: 3px solid #e94560;
            border-radius: 8px;
            box-shadow: 0 0 30px rgba(233, 69, 96, 0.5), inset 0 0 60px rgba(0,0,0,0.3);
        }

        /* Menu Styles */
        .menu-screen {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            background: linear-gradient(180deg, #1a1a2e 0%, #0f3460 100%);
            z-index: 100;
        }

        .menu-screen.hidden {
            display: none;
        }

        .game-title {
            font-size: 72px;
            font-weight: 900;
            background: linear-gradient(45deg, #e94560, #ff6b6b, #feca57, #48dbfb, #ff9ff3);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            text-shadow: 0 0 40px rgba(233, 69, 96, 0.5);
            margin-bottom: 10px;
            animation: titlePulse 2s ease-in-out infinite;
        }

        .game-subtitle {
            font-size: 24px;
            color: #48dbfb;
            margin-bottom: 40px;
            letter-spacing: 8px;
        }

        @keyframes titlePulse {
            0%, 100% { transform: scale(1); }
            50% { transform: scale(1.02); }
        }

        .menu-btn {
            padding: 15px 50px;
            font-size: 20px;
            font-weight: bold;
            border: none;
            border-radius: 30px;
            cursor: pointer;
            margin: 10px;
            transition: all 0.3s ease;
            text-transform: uppercase;
            letter-spacing: 2px;
        }

        .btn-primary {
            background: linear-gradient(45deg, #e94560, #ff6b6b);
            color: white;
            box-shadow: 0 5px 20px rgba(233, 69, 96, 0.4);
        }

        .btn-primary:hover {
            transform: translateY(-3px) scale(1.05);
            box-shadow: 0 8px 30px rgba(233, 69, 96, 0.6);
        }

        .btn-secondary {
            background: linear-gradient(45deg, #48dbfb, #0abde3);
            color: white;
            box-shadow: 0 5px 20px rgba(72, 219, 251, 0.4);
        }

        .btn-secondary:hover {
            transform: translateY(-3px) scale(1.05);
            box-shadow: 0 8px 30px rgba(72, 219, 251, 0.6);
        }

        /* Settings Panel */
        .settings-panel {
            background: rgba(255,255,255,0.1);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            padding: 30px;
            margin: 20px;
            max-width: 500px;
        }

        .setting-group {
            margin-bottom: 20px;
        }

        .setting-group label {
            display: block;
            margin-bottom: 8px;
            font-size: 16px;
            color: #feca57;
        }

        .setting-group select, .setting-group input {
            width: 100%;
            padding: 12px;
            border: none;
            border-radius: 10px;
            background: rgba(255,255,255,0.2);
            color: white;
            font-size: 16px;
            cursor: pointer;
        }

        .setting-group select option {
            background: #1a1a2e;
        }

        /* HUD Styles */
        #hud {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            z-index: 50;
        }

        #hud > * {
            pointer-events: auto;
        }

        .team-indicator {
            position: absolute;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0,0,0,0.7);
            padding: 15px 40px;
            border-radius: 30px;
            border: 2px solid;
            font-size: 24px;
            font-weight: bold;
            backdrop-filter: blur(5px);
        }

        .timer-display {
            position: absolute;
            top: 20px;
            right: 30px;
            background: rgba(0,0,0,0.7);
            padding: 15px 25px;
            border-radius: 15px;
            font-size: 28px;
            font-weight: bold;
            color: #feca57;
            backdrop-filter: blur(5px);
            min-width: 100px;
            text-align: center;
        }

        .timer-display.urgent {
            color: #e94560;
            animation: timerPulse 0.5s ease-in-out infinite;
        }

        @keyframes timerPulse {
            0%, 100% { transform: scale(1); }
            50% { transform: scale(1.15); }
        }

        .health-bar-container {
            position: absolute;
            top: 20px;
            left: 30px;
            background: rgba(0,0,0,0.7);
            padding: 15px;
            border-radius: 15px;
            backdrop-filter: blur(5px);
        }

        .worm-name {
            font-size: 14px;
            margin-bottom: 5px;
        }

        .health-bar {
            width: 200px;
            height: 20px;
            background: #333;
            border-radius: 10px;
            overflow: hidden;
        }

        .health-fill {
            height: 100%;
            background: linear-gradient(90deg, #e94560, #ff6b6b);
            transition: width 0.3s ease;
            border-radius: 10px;
        }

        .health-fill.high { background: linear-gradient(90deg, #1dd1a1, #10ac84); }
        .health-fill.medium { background: linear-gradient(90deg, #feca57, #ff9f43); }
        .health-fill.low { background: linear-gradient(90deg, #e94560, #ee5a24); }

        /* Weapon Panel */
        .weapon-panel {
            position: absolute;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0,0,0,0.8);
            padding: 15px;
            border-radius: 20px;
            backdrop-filter: blur(10px);
            display: flex;
            gap: 10px;
        }

        .weapon-slot {
            width: 60px;
            height: 60px;
            background: rgba(255,255,255,0.1);
            border: 2px solid transparent;
            border-radius: 12px;
            cursor: pointer;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            transition: all 0.2s ease;
            position: relative;
        }

        .weapon-slot:hover {
            background: rgba(255,255,255,0.2);
            transform: translateY(-5px);
        }

        .weapon-slot.selected {
            border-color: #feca57;
            box-shadow: 0 0 20px rgba(254, 202, 87, 0.5);
            transform: translateY(-5px);
        }

        .weapon-icon {
            font-size: 24px;
        }

        .weapon-key {
            position: absolute;
            bottom: 2px;
            right: 4px;
            font-size: 10px;
            color: #888;
        }

        .weapon-name {
            position: absolute;
            bottom: 70px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0,0,0,0.9);
            padding: 8px 15px;
            border-radius: 8px;
            font-size: 12px;
            white-space: nowrap;
            opacity: 0;
            transition: opacity 0.2s;
            pointer-events: none;
        }

        .weapon-slot:hover .weapon-name {
            opacity: 1;
        }

        /* Power Indicator */
        .power-indicator {
            position: absolute;
            bottom: 110px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0,0,0,0.8);
            padding: 10px 20px;
            border-radius: 10px;
            backdrop-filter: blur(5px);
        }

        .power-bar {
            width: 200px;
            height: 15px;
            background: #333;
            border-radius: 8px;
            overflow: hidden;
        }

        .power-fill {
            height: 100%;
            background: linear-gradient(90deg, #48dbfb, #feca57, #e94560);
            width: 0%;
            transition: width 0.05s linear;
        }

        /* Wind Indicator */
        .wind-indicator {
            position: absolute;
            top: 90px;
            right: 30px;
            background: rgba(0,0,0,0.7);
            padding: 10px 15px;
            border-radius: 10px;
            backdrop-filter: blur(5px);
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .wind-arrow {
            font-size: 24px;
            transition: transform 0.3s ease;
        }

        /* Teams Panel */
        .teams-panel {
            position: absolute;
            top: 90px;
            left: 30px;
            background: rgba(0,0,0,0.7);
            padding: 10px;
            border-radius: 10px;
            backdrop-filter: blur(5px);
        }

        .team-entry {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 5px 10px;
            margin: 5px 0;
            border-radius: 5px;
            font-size: 14px;
        }

        .team-entry.active {
            background: rgba(255,255,255,0.2);
        }

        .team-color {
            width: 12px;
            height: 12px;
            border-radius: 50%;
        }

        /* Aim Info */
        .aim-info {
            position: absolute;
            bottom: 110px;
            right: 30px;
            background: rgba(0,0,0,0.7);
            padding: 10px 15px;
            border-radius: 10px;
            backdrop-filter: blur(5px);
            font-size: 14px;
        }

        /* Floating Text */
        .floating-text {
            position: absolute;
            font-weight: bold;
            font-size: 20px;
            pointer-events: none;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
            animation: floatUp 1.5s ease-out forwards;
            z-index: 1000;
        }

        @keyframes floatUp {
            0% { opacity: 1; transform: translateY(0) scale(1); }
            100% { opacity: 0; transform: translateY(-60px) scale(1.5); }
        }

        /* Game Over Screen */
        .game-over-screen {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.9);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 200;
        }

        .game-over-screen.hidden {
            display: none;
        }

        .winner-text {
            font-size: 64px;
            font-weight: 900;
            margin-bottom: 20px;
            animation: winnerPulse 1s ease-in-out infinite;
        }

        @keyframes winnerPulse {
            0%, 100% { transform: scale(1); }
            50% { transform: scale(1.05); }
        }

        /* Controls Help */
        .controls-help {
            position: absolute;
            bottom: 20px;
            left: 20px;
            background: rgba(0,0,0,0.7);
            padding: 10px 15px;
            border-radius: 10px;
            font-size: 12px;
            color: #aaa;
            backdrop-filter: blur(5px);
        }

        /* Pause Menu */
        .pause-menu {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.8);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 150;
        }

        .pause-menu.hidden {
            display: none;
        }
    </style>
</head>
<body>
    <div id="gameContainer">
        <canvas id="gameCanvas"></canvas>
        
        <!-- Main Menu -->
        <div id="mainMenu" class="menu-screen">
            <h1 class="game-title">WORMS</h1>
            <p class="game-subtitle">TACTICAL CHAOS</p>
            
            <div class="settings-panel" id="gameSettings">
                <div class="setting-group">
                    <label>Terrain Type:</label>
                    <select id="terrainType">
                        <option value="single">Single Island</option>
                        <option value="two">Two Islands</option>
                        <option value="cave">Cave</option>
                    </select>
                </div>
                <div class="setting-group">
                    <label>Number of Teams:</label>
                    <select id="teamCount">
                        <option value="2">2 Teams</option>
                        <option value="3">3 Teams</option>
                        <option value="4">4 Teams</option>
                    </select>
                </div>
                <div class="setting-group">
                    <label>CPU Players:</label>
                    <select id="cpuCount">
                        <option value="0">None - All Human</option>
                        <option value="1">1 CPU</option>
                        <option value="2">2 CPU</option>
                        <option value="3">3 CPU</option>
                    </select>
                </div>
                <div class="setting-group">
                    <label>Round Time (seconds):</label>
                    <select id="roundTime">
                        <option value="30">30</option>
                        <option value="45" selected>45</option>
                        <option value="60">60</option>
                    </select>
                </div>
            </div>
            
            <button class="menu-btn btn-primary" id="startBtn">⚔️ START BATTLE</button>
            <button class="menu-btn btn-secondary" id="howToBtn">❓ HOW TO PLAY</button>
        </div>

        <!-- How To Play -->
        <div id="howToMenu" class="menu-screen hidden">
            <h2 style="font-size: 36px; color: #48dbfb; margin-bottom: 30px;">How to Play</h2>
            <div class="settings-panel" style="max-width: 600px; text-align: left;">
                <p style="margin-bottom: 15px;"><strong style="color: #feca57;">Movement:</strong> ← → Arrow keys to move, ↑ to jump</p>
                <p style="margin-bottom: 15px;"><strong style="color: #feca57;">Aim:</strong> Mouse to aim angle</p>
                <p style="margin-bottom: 15px;"><strong style="color: #feca57;">Fire:</strong> Hold SPACE to charge power, release to fire</p>
                <p style="margin-bottom: 15px;"><strong style="color: #feca57;">Weapons:</strong> 1-8 keys to select weapon</p>
                <p style="margin-bottom: 15px;"><strong style="color: #feca57;">Utilities:</strong> Press corresponding key for utilities</p>
                <p style="margin-bottom: 15px;"><strong style="color: #feca57;">Pause:</strong> ESC key</p>
                <p style="margin-bottom: 15px;"><strong style="color: #feca57;">Skip Turn:</strong> Enter key</p>
                <hr style="border-color: #444; margin: 20px 0;">
                <p style="color: #aaa; font-size: 14px;">💡 Wind affects projectile trajectories. Watch the wind indicator!</p>
                <p style="color: #aaa; font-size: 14px;">💡 Falling into water causes instant drowning!</p>
                <p style="color: #aaa; font-size: 14px;">💡 Falling from great heights causes fall damage!</p>
            </div>
            <button class="menu-btn btn-secondary" id="backBtn">← BACK</button>
        </div>

        <!-- HUD -->
        <div id="hud" class="hidden">
            <div class="team-indicator" id="teamIndicator">Team 1's Turn</div>
            
            <div class="timer-display" id="timerDisplay">45</div>
            
            <div class="health-bar-container">
                <div class="worm-name" id="wormName">Worm 1</div>
                <div class="health-bar">
                    <div class="health-fill" id="healthFill" style="width: 100%;"></div>
                </div>
            </div>
            
            <div class="wind-indicator">
                <span>Wind:</span>
                <span class="wind-arrow" id="windArrow">➡️</span>
                <span id="windStrength">0</span>
            </div>
            
            <div class="teams-panel" id="teamsPanel"></div>
            
            <div class="aim-info" id="aimInfo">Angle: 45° | Power: 0%</div>
            
            <div class="power-indicator" id="powerIndicator">
                <div style="text-align: center; margin-bottom: 5px; font-size: 12px;">POWER</div>
                <div class="power-bar">
                    <div class="power-fill" id="powerFill"></div>
                </div>
            </div>
            
            <div class="weapon-panel" id="weaponPanel"></div>
            
            <div class="controls-help">
                ← → Move | ↑ Jump | SPACE Fire | 1-8 Weapons | Enter Skip | ESC Pause
            </div>
        </div>

        <!-- Pause Menu -->
        <div id="pauseMenu" class="pause-menu hidden">
            <h2 style="font-size: 48px; color: #feca57; margin-bottom: 40px;">PAUSED</h2>
            <button class="menu-btn btn-primary" id="resumeBtn">▶️ RESUME</button>
            <button class="menu-btn btn-secondary" id="quitBtn">🏠 QUIT TO MENU</button>
        </div>

        <!-- Game Over Screen -->
        <div id="gameOverScreen" class="game-over-screen hidden">
            <h1 class="winner-text" id="winnerText">TEAM 1 WINS!</h1>
            <p style="font-size: 24px; color: #aaa; margin-bottom: 40px;" id="victoryMessage">Victory!</p>
            <button class="menu-btn btn-primary" id="playAgainBtn">🔄 PLAY AGAIN</button>
            <button class="menu-btn btn-secondary" id="menuBtn">🏠 MAIN MENU</button>
        </div>
    </div>

    <script>
    // =============================================================
    // WORMS: TACTICAL CHAOS - Main Game Engine
    // =============================================================

    // Canvas Setup
    const canvas = document.getElementById('gameCanvas');
    const ctx = canvas.getContext('2d');
    
    // Set canvas size
    function resizeCanvas() {
        canvas.width = Math.min(window.innerWidth - 40, 1200);
        canvas.height = Math.min(window.innerHeight - 40, 700);
    }
    resizeCanvas();
    window.addEventListener('resize', resizeCanvas);

    // =============================================================
    // AUDIO SYSTEM - Sound Effects
    // =============================================================
    class AudioSystem {
        constructor() {
            this.ctx = null;
            this.initialized = false;
        }

        init() {
            if (this.initialized) return;
            try {
                this.ctx = new (window.AudioContext || window.webkitAudioContext)();
                this.initialized = true;
            } catch(e) {
                console.log('Audio not supported');
            }
        }

        playTone(freq, duration, type = 'sine', volume = 0.3) {
            if (!this.ctx) return;
            const osc = this.ctx.createOscillator();
            const gain = this.ctx.createGain();
            osc.connect(gain);
            gain.connect(this.ctx.destination);
            osc.frequency.value = freq;
            osc.type = type;
            gain.gain.setValueAtTime(volume, this.ctx.currentTime);
            gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + duration);
            osc.start();
            osc.stop(this.ctx.currentTime + duration);
        }

        playExplosion(size = 1) {
            this.init();
            // Low frequency rumble
            this.playTone(80 * size, 0.3, 'sawtooth', 0.4 * Math.min(size, 1));
            this.playTone(50 * size, 0.5, 'square', 0.2 * Math.min(size, 1));
            // Noise burst
            setTimeout(() => this.playTone(200, 0.1, 'sawtooth', 0.2), 50);
        }

        playShoot() {
            this.init();
            this.playTone(600, 0.1, 'square', 0.15);
            this.playTone(400, 0.15, 'sawtooth', 0.1);
        }

        playJump() {
            this.init();
            this.playTone(300, 0.1, 'sine', 0.2);
            setTimeout(() => this.playTone(500, 0.1, 'sine', 0.15), 50);
        }

        playStep() {
            this.init();
            this.playTone(100 + Math.random() * 50, 0.05, 'square', 0.1);
        }

        playHit() {
            this.init();
            this.playTone(150, 0.2, 'sawtooth', 0.3);
        }

        playSplash() {
            this.init();
            this.playTone(300, 0.2, 'sine', 0.2);
            this.playTone(200, 0.3, 'sine', 0.15);
        }

        playDing() {
            this.init();
            this.playTone(800, 0.15, 'sine', 0.2);
        }

        playPop() {
            this.init();
            this.playTone(400, 0.08, 'sine', 0.15);
        }

        playSelect() {
            this.init();
            this.playTone(500, 0.05, 'sine', 0.1);
        }

        playClick() {
            this.init();
            this.playTone(600, 0.05, 'sine', 0.15);
        }

        playTimerTick(pitch = 440) {
            this.init();
            this.playTone(pitch, 0.05, 'sine', 0.1);
        }

        playGameOver() {
            this.init();
            [523, 659, 784, 1047].forEach((f, i) => {
                setTimeout(() => this.playTone(f, 0.3, 'sine', 0.3), i * 200);
            });
        }

        playVictory() {
            this.init();
            const notes = [523, 659, 784, 659, 784, 1047];
            notes.forEach((f, i) => {
                setTimeout(() => this.playTone(f, 0.2, 'sine', 0.25), i * 150);
            });
        }
    }

    const audio = new AudioSystem();

    // =============================================================
    // GAME CONSTANTS & SETTINGS
    // =============================================================
    const GRAVITY = 0.15;
    const WORM_WIDTH = 20;
    const WORM_HEIGHT = 24;
    const MOVE_SPEED = 2;
    const JUMP_FORCE = -5;
    const MAX_POWER = 15;
    const WATER_LEVEL_OFFSET = 50;
    const FALL_DAMAGE_THRESHOLD = 100;
    const FALL_DAMAGE_MULTIPLIER = 0.5;

    // Team colors
    const TEAM_COLORS = [
        { primary: '#e94560', secondary: '#ff6b6b', name: 'Red Team' },
        { primary: '#48dbfb', secondary: '#0abde3', name: 'Blue Team' },
        { primary: '#1dd1a1', secondary: '#10ac84', name: 'Green Team' },
        { primary: '#feca57', secondary: '#ff9f43', name: 'Gold Team' }
    ];

    // Weapon definitions
    const WEAPONS = [
        { 
            id: 'bazooka', 
            name: 'Bazooka', 
            icon: '🚀', 
            category: 'indirect',
            damage: 50, 
            radius: 40,
            projectileSpeed: 1,
            affectedByWind: true,
            key: '1'
        },
        { 
            id: 'grenade', 
            name: 'Grenade', 
            icon: '💣', 
            category: 'indirect',
            damage: 45, 
            radius: 35,
            projectileSpeed: 0.8,
            affectedByWind: true,
            bounces: true,
            fuseTime: 3000,
            key: '2'
        },
        { 
            id: 'shotgun', 
            name: 'Shotgun', 
            icon: '🔫', 
            category: 'direct',
            damage: 30, 
            pellets: 5,
            spread: 0.15,
            instant: true,
            range: 400,
            key: '3'
        },
        { 
            id: 'dynamite', 
            name: 'Dynamite', 
            icon: '🧨', 
            category: 'high_aoe',
            damage: 70, 
            radius: 60,
            placeable: true,
            fuseTime: 3000,
            key: '4'
        },
        { 
            id: 'cluster', 
            name: 'Cluster Bomb', 
            icon: '🎯', 
            category: 'high_aoe',
            damage: 25, 
            radius: 25,
            projectileSpeed: 0.9,
            affectedByWind: true,
            clusters: 5,
            clusterDelay: 1000,
            key: '5'
        },
        { 
            id: 'mortar', 
            name: 'Mortar', 
            icon: '💥', 
            category: 'indirect',
            damage: 40, 
            radius: 45,
            projectileSpeed: 0.7,
            affectedByWind: true,
            key: '6'
        }
    ];

    // Utility items
    const UTILITIES = [
        { 
            id: 'ninja_rope', 
            name: 'Ninja Rope', 
            icon: '🪢', 
            key: '7'
        },
        { 
            id: 'parachute', 
            name: 'Parachute', 
            icon: '🪂', 
            key: '8'
        },
        { 
            id: 'teleport', 
            name: 'Teleport', 
            icon: '✨'
        },
        { 
            id: 'first_aid', 
            name: 'First Aid', 
            icon: '💊',
            healAmount: 30
        }
    ];

    // =============================================================
    // GAME STATE
    // =============================================================
    let gameState = {
        phase: 'menu', // menu, playing, paused, gameover
        teams: [],
        currentTeamIndex: 0,
        currentWormIndex: 0,
        turnTime: 45,
        turnTimeRemaining: 45,
        roundTimeSetting: 45,
        wind: { x: 0, y: 0 },
        terrainType: 'single',
        projectiles: [],
        particles: [],
        floatingTexts: [],
        debris: [],
        activeProjectile: null,
        selectedWeapon: WEAPONS[0],
        isCharging: false,
        chargePower: 0,
        chargeDirection: 1,
        aimAngle: -Math.PI / 4,
        aimDirection: 1,
        camera: { x: 0, y: 0, targetX: 0, targetY: 0, zoom: 1, targetZoom: 1 },
        screenShake: { x: 0, y: 0, intensity: 0 },
        impactFreeze: { active: false, duration: 0, maxDuration: 0 },
        lastTick: 0,
        cpuPlayers: [],
        cpuThinking: false
    };

    // Terrain data
    let terrainCanvas, terrainCtx;
    let terrainData = null;

    // Input state
    const keys = {};
    let mouseX = 0, mouseY = 0;
    let mouseDown = false;

    // =============================================================
    // TERRAIN GENERATION
    // =============================================================
    function initTerrain() {
        terrainCanvas = document.createElement('canvas');
        terrainCanvas.width = canvas.width;
        terrainCanvas.height = canvas.height;
        terrainCtx = terrainCanvas.getContext('2d');
        generateTerrain();
    }

    function generateTerrain() {
        const w = terrainCanvas.width;
        const h = terrainCanvas.height;
        
        // Clear to transparent - only actual terrain will be visible to collision
        terrainCtx.clearRect(0, 0, w, h);

        // Generate based on type
        switch(gameState.terrainType) {
            case 'single':
                generateSingleIsland(w, h);
                break;
            case 'two':
                generateTwoIslands(w, h);
                break;
            case 'cave':
                generateCave(w, h);
                break;
        }

        // Water
        const gradient = terrainCtx.createLinearGradient(0, h - WATER_LEVEL_OFFSET, 0, h);
        gradient.addColorStop(0, 'rgba(72, 219, 251, 0.5)');
        gradient.addColorStop(1, 'rgba(10, 189, 227, 0.8)');
        terrainCtx.fillStyle = gradient;
        terrainCtx.fillRect(0, h - WATER_LEVEL_OFFSET, w, WATER_LEVEL_OFFSET);

        // Update terrain data
        updateTerrainData();
    }

    function generateSingleIsland(w, h) {
        // Generate a central island using midpoint displacement
        const groundY = h - 150;
        const points = [];
        const segments = 60;
        
        // Initialize with random height variation
        let heights = [];
        for (let i = 0; i <= segments; i++) {
            heights[i] = 0;
        }
        
        // Midpoint displacement for terrain
        let range = 80;
        for (let level = 1; level <= 6; level++) {
            const step = Math.floor(segments / Math.pow(2, level));
            if (step < 1) break;
            
            for (let i = step; i < segments; i += step * 2) {
                const left = heights[i - step] || 0;
                const right = heights[i + step] || 0;
                heights[i] = (left + right) / 2 + (Math.random() - 0.5) * range;
            }
            range *= 0.6;
        }

        // Create plateau in center
        const centerX = segments / 2;
        const plateauWidth = segments * 0.3;
        for (let i = 0; i <= segments; i++) {
            const dist = Math.abs(i - centerX);
            if (dist < plateauWidth) {
                heights[i] *= 0.2;
            } else if (dist < plateauWidth * 1.5) {
                const t = (dist - plateauWidth) / (plateauWidth * 0.5);
                heights[i] = heights[i] * t + (heights[i] * 0.2) * (1 - t);
            }
        }

        // Build points array
        for (let i = 0; i <= segments; i++) {
            points.push({
                x: (i / segments) * w,
                y: groundY + heights[i]
            });
        }

        // Draw terrain
        const grd = terrainCtx.createLinearGradient(0, 0, 0, h);
        grd.addColorStop(0, '#10ac84');
        grd.addColorStop(0.3, '#1dd1a1');
        grd.addColorStop(0.5, '#27ae60');
        grd.addColorStop(1, '#1e3a5f');

        terrainCtx.beginPath();
        terrainCtx.moveTo(0, h);
        terrainCtx.lineTo(points[0].x, points[0].y);
        
        for (let i = 1; i < points.length; i++) {
            terrainCtx.lineTo(points[i].x, points[i].y);
        }
        
        terrainCtx.lineTo(w, h);
        terrainCtx.closePath();
        terrainCtx.fillStyle = grd;
        terrainCtx.fill();

        // Add some texture
        addTerrainTexture();
    }

    function generateTwoIslands(w, h) {
        const groundY = h - 150;
        const segments = 30;
        
        // Left island
        let heights1 = generateTerrainHeights(segments, 60);
        // Create plateau
        for (let i = 5; i < segments - 5; i++) {
            if (i > segments * 0.3 && i < segments * 0.7) {
                heights1[i] *= 0.15;
            }
        }
        
        // Right island
        let heights2 = generateTerrainHeights(segments, 60);
        for (let i = 5; i < segments - 5; i++) {
            if (i > segments * 0.3 && i < segments * 0.7) {
                heights2[i] *= 0.15;
            }
        }

        const grd = terrainCtx.createLinearGradient(0, 0, 0, h);
        grd.addColorStop(0, '#10ac84');
        grd.addColorStop(0.3, '#1dd1a1');
        grd.addColorStop(0.5, '#27ae60');
        grd.addColorStop(1, '#1e3a5f');

        // Draw left island (left 40%)
        terrainCtx.beginPath();
        const leftWidth = w * 0.4;
        terrainCtx.moveTo(-20, h);
        for (let i = 0; i <= segments; i++) {
            const x = (i / segments) * leftWidth;
            const y = groundY + heights1[i];
            terrainCtx.lineTo(x, y);
        }
        terrainCtx.lineTo(leftWidth, h);
        terrainCtx.closePath();
        terrainCtx.fillStyle = grd;
        terrainCtx.fill();

        // Draw right island (right 40%)
        terrainCtx.beginPath();
        const rightStart = w * 0.6;
        terrainCtx.moveTo(rightStart, h);
        for (let i = 0; i <= segments; i++) {
            const x = rightStart + (i / segments) * (w - rightStart);
            const y = groundY + heights2[i];
            terrainCtx.lineTo(x, y);
        }
        terrainCtx.lineTo(w + 20, h);
        terrainCtx.closePath();
        terrainCtx.fillStyle = grd;
        terrainCtx.fill();

        addTerrainTexture();
    }

    function generateCave(w, h) {
        // Generate ceiling and floor
        const groundY = h - 120;
        const ceilY = 80;
        const segments = 60;

        // Floor heights
        const floorHeights = generateTerrainHeights(segments, 40);
        // Ceiling heights (inverted)
        const ceilHeights = generateTerrainHeights(segments, 40);

        // Create a large flat area in center
        for (let i = segments * 0.2; i < segments * 0.4; i++) floorHeights[i] *= 0.2;
        for (let i = segments * 0.6; i < segments * 0.8; i++) floorHeights[i] *= 0.2;
        for (let i = segments * 0.2; i < segments * 0.8; i++) ceilHeights[i] = Math.max(0, ceilHeights[i] - 20);

        const grd = terrainCtx.createLinearGradient(0, 0, 0, h);
        grd.addColorStop(0, '#5f27cd');
        grd.addColorStop(0.5, '#341f97');
        grd.addColorStop(1, '#1e3a5f');

        // Draw cave walls (left and right)
        terrainCtx.fillStyle = grd;
        terrainCtx.fillRect(0, 0, 30, h);
        terrainCtx.fillRect(w - 30, 0, 30, h);

        // Draw ceiling
        terrainCtx.beginPath();
        terrainCtx.moveTo(30, 0);
        for (let i = 0; i <= segments; i++) {
            const x = 30 + (i / segments) * (w - 60);
            const y = ceilY + ceilHeights[i];
            terrainCtx.lineTo(x, y);
        }
        terrainCtx.lineTo(w - 30, 0);
        terrainCtx.closePath();
        terrainCtx.fill();

        // Draw floor
        terrainCtx.beginPath();
        terrainCtx.moveTo(30, h);
        for (let i = 0; i <= segments; i++) {
            const x = 30 + (i / segments) * (w - 60);
            const y = groundY + floorHeights[i];
            terrainCtx.lineTo(x, y);
        }
        terrainCtx.lineTo(w - 30, h);
        terrainCtx.closePath();
        terrainCtx.fill();

        // Add stalactites to ceiling
        for (let i = 0; i < 10; i++) {
            const sx = 50 + Math.random() * (w - 100);
            const sy = ceilY + 20 + Math.random() * 40;
            const sw = 8 + Math.random() * 12;
            const sh = 15 + Math.random() * 25;
            
            terrainCtx.beginPath();
            terrainCtx.moveTo(sx - sw/2, sy);
            terrainCtx.lineTo(sx, sy + sh);
            terrainCtx.lineTo(sx + sw/2, sy);
            terrainCtx.closePath();
            terrainCtx.fillStyle = '#636e72';
            terrainCtx.fill();
        }

        addTerrainTexture();
    }

    function generateTerrainHeights(segments, range) {
        const heights = [];
        for (let i = 0; i <= segments; i++) heights[i] = 0;
        
        let currentRange = range;
        for (let level = 1; level <= 6; level++) {
            const step = Math.floor(segments / Math.pow(2, level));
            if (step < 1) break;
            
            for (let i = step; i < segments; i += step * 2) {
                const left = heights[Math.max(0, i - step)];
                const right = heights[Math.min(segments, i + step)];
                heights[i] = (left + right) / 2 + (Math.random() - 0.5) * currentRange;
            }
            currentRange *= 0.6;
        }
        return heights;
    }

    function addTerrainTexture() {
        // Add some noise/texture to terrain
        const w = terrainCanvas.width;
        const h = terrainCanvas.height;
        terrainCtx.globalAlpha = 0.3;
        
        for (let i = 0; i < 500; i++) {
            const x = Math.random() * w;
            const y = Math.random() * h;
            
            // Only draw if pixel is part of terrain
            if (isTerrainPixel(Math.floor(x), Math.floor(y))) {
                terrainCtx.fillStyle = `rgba(255,255,255,${Math.random() * 0.1})`;
                terrainCtx.fillRect(x, y, 2, 2);
            }
        }
        terrainCtx.globalAlpha = 1;
    }

    function updateTerrainData() {
        const w = terrainCanvas.width;
        const h = terrainCanvas.height;
        const imgData = terrainCtx.getImageData(0, 0, w, h);
        terrainData = {
            width: w,
            height: h,
            data: new Uint8Array(imgData.data)
        };
    }

    function isTerrainPixel(x, y) {
        if (!terrainData) return false;
        if (x < 0 || x >= terrainData.width || y < 0 || y >= terrainData.height) return false;
        // Don't count water as terrain
        if (y >= canvas.height - WATER_LEVEL_OFFSET) return false;
        const idx = (Math.floor(y) * terrainData.width + Math.floor(x)) * 4;
        // Check alpha channel
        return terrainData.data[idx + 3] > 50;
    }

    function isWaterLevel(y) {
        return y >= canvas.height - WATER_LEVEL_OFFSET;
    }

    // =============================================================
    // TERRAIN DESTRUCTION
    // =============================================================
    function destroyTerrain(cx, cy, radius) {
        // Create a circular hole
        terrainCtx.globalCompositeOperation = 'destination-out';
        terrainCtx.beginPath();
        terrainCtx.arc(cx, cy, radius, 0, Math.PI * 2);
        terrainCtx.fill();
        terrainCtx.globalCompositeOperation = 'source-over';

        // Create debris particles
        const debrisCount = Math.floor(radius / 3);
        for (let i = 0; i < debrisCount; i++) {
            const angle = Math.random() * Math.PI * 2;
            const dist = Math.random() * radius;
            const x = cx + Math.cos(angle) * dist;
            const y = cy + Math.sin(angle) * dist;
            
            if (isTerrainPixel(Math.floor(x), Math.floor(y))) {
                // Check pixel color for debris color
                const idx = (Math.floor(y) * terrainData.width + Math.floor(x)) * 4;
                const r = terrainData.data[idx];
                const g = terrainData.data[idx + 1];
                const b = terrainData.data[idx + 2];

                gameState.debris.push({
                    x: x,
                    y: y,
                    vx: (Math.random() - 0.5) * 5 + (x - cx) * 0.05,
                    vy: (Math.random() - 0.5) * 5 - 2,
                    size: 2 + Math.random() * 4,
                    color: `rgb(${r},${g},${b})`,
                    life: 180 + Math.random() * 120,
                    rotation: Math.random() * Math.PI * 2,
                    rotationSpeed: (Math.random() - 0.5) * 0.2
                });
            }
        }

        updateTerrainData();
    }

    // =============================================================
    // TEAM & WORM MANAGEMENT
    // =============================================================
    class Worm {
        constructor(x, y, teamIndex, wormIndex) {
            this.x = x;
            this.y = y;
            this.vx = 0;
            this.vy = 0;
            this.teamIndex = teamIndex;
            this.wormIndex = wormIndex;
            this.health = 100;
            this.maxHealth = 100;
            this.alive = true;
            this.onGround = false;
            this.facing = 1; // 1 right, -1 left
            this.name = `Worm ${wormIndex + 1}`;
            this.invincible = false;
            this.invincibleTimer = 0;
            this.parachuteActive = false;
            this.lastY = y;
            this.fallDistance = 0;
            this.animFrame = 0;
            this.animTimer = 0;
            this.isMoving = false;
        }

        update(dt) {
            if (!this.alive) return;

            this.animTimer++;
            if (this.animTimer > 10) {
                this.animTimer = 0;
                this.animFrame = (this.animFrame + 1) % 4;
            }

            // Invincibility timer
            if (this.invincible) {
                this.invincibleTimer--;
                if (this.invincibleTimer <= 0) {
                    this.invincible = false;
                }
            }

            // Track fall distance
            if (!this.onGround && this.vy > 0) {
                this.fallDistance += this.vy;
            }

            // Apply gravity
            if (this.parachuteActive && this.vy > 0) {
                this.vy += GRAVITY * 0.1;
                this.vy = Math.min(this.vy, 1.5);
            } else {
                this.vy += GRAVITY;
            }

            // Wind affects airborne worms slightly
            if (!this.onGround) {
                this.vx += gameState.wind.x * 0.002;
            }

            // Friction
            if (this.onGround) {
                this.vx *= 0.8;
                if (Math.abs(this.vx) < 0.1) this.vx = 0;
            } else {
                this.vx *= 0.99;
            }

            // Movement
            if (keys['ArrowLeft'] || keys['KeyA']) {
                if (this.onGround) {
                    this.vx = -MOVE_SPEED;
                    this.isMoving = true;
                }
                this.facing = -1;
            } else if (keys['ArrowRight'] || keys['KeyD']) {
                if (this.onGround) {
                    this.vx = MOVE_SPEED;
                    this.isMoving = true;
                }
                this.facing = 1;
            } else {
                this.isMoving = false;
            }

            // Jump
            if ((keys['ArrowUp'] || keys['KeyW'] || keys['Space']) && this.onGround && !gameState.isCharging) {
                this.vy = JUMP_FORCE;
                this.onGround = false;
                this.parachuteActive = false;
                audio.playJump();
                // Dust particles
                createDustParticles(this.x, this.y + WORM_HEIGHT/2, 5);
            }

            // Update position
            const newX = this.x + this.vx;
            const newY = this.y + this.vy;

            // Collision detection
            this.onGround = false;

            // Check vertical collision (feet first)
            const footY = newY + WORM_HEIGHT / 2;
            const leftFootX = newX - WORM_WIDTH / 2 + 3;
            const rightFootX = newX + WORM_WIDTH / 2 - 3;
            
            if (isTerrainPixel(leftFootX, footY) || isTerrainPixel(rightFootX, footY)) {
                if (this.vy > 0) {
                    // Landing
                    this.y = snapToGround(newX, newY) - WORM_HEIGHT / 2;
                    
                    // Fall damage
                    if (this.fallDistance > FALL_DAMAGE_THRESHOLD) {
                        const damage = Math.floor((this.fallDistance - FALL_DAMAGE_THRESHOLD) * FALL_DAMAGE_MULTIPLIER);
                        this.takeDamage(damage);
                        createFloatingText(this.x, this.y - 30, `-${damage}`, '#e94560');
                        audio.playHit();
                    }
                    
                    this.fallDistance = 0;
                    this.vy = 0;
                    this.onGround = true;
                    this.parachuteActive = false;
                }
            } else {
                this.y = newY;
            }

            // Check head collision
            const headY = newY - WORM_HEIGHT / 2;
            if (isTerrainPixel(newX, headY) && this.vy < 0) {
                this.vy = 0;
            }

            // Horizontal collision
            const bodyY = newY;
            const leftX = newX - WORM_WIDTH / 2;
            const rightX = newX + WORM_WIDTH / 2;
            
            if (isTerrainPixel(leftX, bodyY)) {
                this.vx = 0;
            } else if (isTerrainPixel(rightX, bodyY)) {
                this.vx = 0;
            } else {
                this.x = newX;
            }

            // Step sound when moving on ground
            if (this.isMoving && this.onGround && Math.random() < 0.1) {
                audio.playStep();
                if (Math.random() < 0.3) {
                    createDustParticles(this.x, this.y + WORM_HEIGHT/2, 2);
                }
            }

            // Water check
            if (isWaterLevel(this.y + WORM_HEIGHT / 2)) {
                this.drown();
            }

            // Boundary check
            if (this.x < WORM_WIDTH/2 + 30) this.x = WORM_WIDTH/2 + 30;
            if (this.x > canvas.width - WORM_WIDTH/2 - 30) this.x = canvas.width - WORM_WIDTH/2 - 30;

            // If worm goes too high, kill
            if (this.y < -50) {
                this.takeDamage(50);
            }

            this.lastY = this.y;
        }

        takeDamage(amount, fromExplosion = false) {
            if (this.invincible && !fromExplosion) return;
            
            this.health -= amount;
            if (this.health <= 0) {
                this.health = 0;
                this.die();
            }
            
            // Flash white
            this.invincible = true;
            this.invincibleTimer = 30;
        }

        die() {
            this.alive = false;
            // Death particles
            createExplosionParticles(this.x, this.y, TEAM_COLORS[this.teamIndex].primary, 20);
            audio.playExplosion(0.5);
            createFloatingText(this.x, this.y - 20, 'K.O.!', '#e94560');
        }

        drown() {
            this.alive = false;
            audio.playSplash();
            createSplashParticles(this.x, canvas.height - WATER_LEVEL_OFFSET);
            createFloatingText(this.x, canvas.height - WATER_LEVEL_OFFSET - 30, 'DROWNED!', '#48dbfb');
        }

        draw(ctx, camX, camY, zoom) {
            if (!this.alive) return;

            const sx = (this.x - camX) * zoom + canvas.width / 2;
            const sy = (this.y - camY) * zoom + canvas.height / 2;
            const sw = WORM_WIDTH * zoom;
            const sh = WORM_HEIGHT * zoom;

            ctx.save();
            ctx.translate(sx, sy);
            ctx.scale(this.facing * zoom, zoom);

            // Flash when invincible
            if (this.invincible && Math.floor(this.invincibleTimer / 4) % 2 === 0) {
                ctx.globalAlpha = 0.5;
            }

            const isActive = isCurrentWorm(this);

            // Body
            ctx.fillStyle = isActive ? '#feca57' : TEAM_COLORS[this.teamIndex].primary;
            ctx.beginPath();
            ctx.ellipse(0, 2, 10, 12, 0, 0, Math.PI * 2);
            ctx.fill();

            // Outline
            ctx.strokeStyle = '#1a1a2e';
            ctx.lineWidth = 2;
            ctx.stroke();

            // Eyes
            ctx.fillStyle = 'white';
            ctx.beginPath();
            ctx.ellipse(-3, -4, 4, 4, 0, 0, Math.PI * 2);
            ctx.fill();
            ctx.beginPath();
            ctx.ellipse(5, -4, 4, 4, 0, 0, Math.PI * 2);
            ctx.fill();

            // Pupils
            ctx.fillStyle = '#1a1a2e';
            const lookDir = this.facing;
            ctx.beginPath();
            ctx.arc(-3 + lookDir * 1.5, -4, 2, 0, Math.PI * 2);
            ctx.fill();
            ctx.beginPath();
            ctx.arc(5 + lookDir * 1.5, -4, 2, 0, Math.PI * 2);
            ctx.fill();

            // Mouth
            ctx.strokeStyle = '#1a1a2e';
            ctx.lineWidth = 1.5;
            ctx.beginPath();
            if (isActive && gameState.isCharging) {
                // O face when charging
                ctx.arc(1, 4, 3, 0, Math.PI * 2);
            } else {
                // Smile
                ctx.arc(1, 2, 4, 0.2, Math.PI - 0.2);
            }
            ctx.stroke();

            // Antenna
            ctx.strokeStyle = TEAM_COLORS[this.teamIndex].primary;
            ctx.lineWidth = 2;
            ctx.beginPath();
            ctx.moveTo(0, -10);
            ctx.quadraticCurveTo(5, -18, 3, -20);
            ctx.stroke();

            // Antenna ball
            ctx.fillStyle = isActive ? '#feca57' : TEAM_COLORS[this.teamIndex].secondary;
            ctx.beginPath();
            ctx.arc(3, -20, 3, 0, Math.PI * 2);
            ctx.fill();

            // Parachute
            if (this.parachuteActive) {
                ctx.restore();
                ctx.save();
                ctx.translate(sx, sy);

                ctx.fillStyle = TEAM_COLORS[this.teamIndex].primary;
                ctx.beginPath();
                ctx.moveTo(-20 * zoom, -25 * zoom);
                ctx.quadraticCurveTo(0, -50 * zoom, 20 * zoom, -25 * zoom);
                ctx.lineTo(0, -15 * zoom);
                ctx.closePath();
                ctx.fill();

                ctx.strokeStyle = '#888';
                ctx.lineWidth = 1;
                ctx.beginPath();
                ctx.moveTo(-15 * zoom, -25 * zoom);
                ctx.lineTo(0, -12 * zoom);
                ctx.moveTo(15 * zoom, -25 * zoom);
                ctx.lineTo(0, -12 * zoom);
                ctx.stroke();
            }

            ctx.restore();

            // Health bar (screen space)
            if (this.alive) {
                const barWidth = 30 * zoom;
                const barHeight = 4 * zoom;
                const barY = sy - 20 * zoom;

                ctx.fillStyle = '#333';
                ctx.fillRect(sx - barWidth/2, barY, barWidth, barHeight);

                const hp = this.health / this.maxHealth;
                let hpColor = '#1dd1a1';
                if (hp < 0.3) hpColor = '#e94560';
                else if (hp < 0.6) hpColor = '#feca57';

                ctx.fillStyle = hpColor;
                ctx.fillRect(sx - barWidth/2, barY, barWidth * hp, barHeight);
            }

            // Active indicator
            if (isActive) {
                const bounce = Math.sin(Date.now() / 200) * 3;
                ctx.fillStyle = '#feca57';
                ctx.beginPath();
                ctx.moveTo(sx, sy - 40 * zoom + bounce);
                ctx.lineTo(sx - 8 * zoom, sy - 50 * zoom + bounce);
                ctx.lineTo(sx + 8 * zoom, sy - 50 * zoom + bounce);
                ctx.closePath();
                ctx.fill();
            }
        }
    }

    class Team {
        constructor(index, isCPU = false) {
            this.index = index;
            this.worms = [];
            this.color = TEAM_COLORS[index];
            this.isCPU = isCPU;
            this.aliveWormCount = 4;
        }

        spawnWorms() {
            const positions = [];
            const w = canvas.width;
            const safeY = canvas.height - WATER_LEVEL_OFFSET - 100; // Well above water
            
            // Find all valid spawn regions first
            let spawnRegions = [];
            
            // Scan across map to find valid terrain above water
            const scanStep = 5;
            for (let x = 60; x < w - 60; x += scanStep) {
                const gy = findGroundY(x);
                // Must be well above water
                if (gy < canvas.height - WATER_LEVEL_OFFSET - 50) {
                    spawnRegions.push({ x: x, groundY: gy });
                }
            }
            
            if (spawnRegions.length === 0) {
                // Fallback: just use center
                console.warn('No valid spawn regions found! Using fallback.');
                for (let i = 0; i < 4; i++) {
                    positions.push({ 
                        x: w/2 + (i - 1.5) * 40, 
                        y: canvas.height - WATER_LEVEL_OFFSET - 100 
                    });
                }
            } else {
                // Split terrain into sections based on team count
                const totalRegions = spawnRegions.length;
                const regionsPerTeam = Math.floor(totalRegions / gameState.teams.length);
                const startIdx = this.index * regionsPerTeam;
                const endIdx = Math.min(startIdx + regionsPerTeam, totalRegions);
                
                // Get our team's region
                const myRegions = spawnRegions.slice(startIdx, endIdx);
                
                // Sample 4 positions from our region
                for (let i = 0; i < 4; i++) {
                    // Pick a position with some spread
                    const regionIdx = Math.floor((i / 4) * myRegions.length);
                    if (myRegions[regionIdx]) {
                        // Add some horizontal jitter
                        const jitter = (Math.random() - 0.5) * 30;
                        let tx = myRegions[regionIdx].x + jitter;
                        let ty = myRegions[regionIdx].groundY;
                        
                        // Validate this position
                        const actualY = findGroundY(tx);
                        if (actualY < canvas.height - WATER_LEVEL_OFFSET - 50) {
                            ty = actualY;
                        }
                        
                        positions.push({ 
                            x: Math.max(50, Math.min(w - 50, tx)), 
                            y: ty - WORM_HEIGHT/2 - 5 
                        });
                    } else {
                        // Fallback
                        positions.push({ 
                            x: w/2, 
                            y: safeY 
                        });
                    }
                }
            }

            this.worms = positions.map((pos, i) => new Worm(pos.x, pos.y, this.index, i));
        }

        getAliveWorms() {
            return this.worms.filter(w => w.alive);
        }

        updateAliveCount() {
            this.aliveWormCount = this.getAliveWorms().length;
        }

        isDefeated() {
            return this.aliveWormCount === 0;
        }
    }

    function findGroundY(x) {
        // Search from bottom (just above water) going up to find the terrain surface
        const startY = canvas.height - WATER_LEVEL_OFFSET - 5;
        const endY = 50;
        
        let foundTerrainY = null;
        for (let y = startY; y > endY; y--) {
            if (isTerrainPixel(x, y)) {
                foundTerrainY = y;
            } else if (foundTerrainY !== null) {
                // We were in terrain, now we're out - this is the surface
                return foundTerrainY;
            }
        }
        
        // Fallback: return a safe height
        if (foundTerrainY !== null) return foundTerrainY;
        return canvas.height - WATER_LEVEL_OFFSET - 80;
    }

    function snapToGround(x, y) {
        for (let ty = y; ty < canvas.height - WATER_LEVEL_OFFSET; ty++) {
            if (isTerrainPixel(x, ty)) {
                return ty;
            }
        }
        return y;
    }

    function getCurrentWorm() {
        const team = gameState.teams[gameState.currentTeamIndex];
        if (!team) return null;
        const aliveWorms = team.getAliveWorms();
        if (aliveWorms.length === 0) return null;
        
        // Return the worm at currentWormIndex, cycling if needed
        gameState.currentWormIndex = gameState.currentWormIndex % aliveWorms.length;
        return aliveWorms[gameState.currentWormIndex];
    }

    function isCurrentWorm(worm) {
        const current = getCurrentWorm();
        return current === worm;
    }

    // =============================================================
    // PROJECTILE SYSTEM
    // =============================================================
    class Projectile {
        constructor(x, y, vx, vy, weapon, owner) {
            this.x = x;
            this.y = y;
            this.vx = vx;
            this.vy = vy;
            this.weapon = weapon;
            this.owner = owner;
            this.alive = true;
            this.trail = [];
            this.bounceCount = 0;
            this.maxBounces = 3;
            this.fuseTimer = weapon.fuseTime || 0;
            this.spawnTime = Date.now();
            this.rotation = 0;
            this.rotationSpeed = 0.2;
            this.isCluster = weapon.id === 'cluster';
            this.clusterTimer = weapon.clusterDelay || 0;
            this.hasClustered = false;
        }

        update(dt) {
            if (!this.alive) return;

            // Save trail
            this.trail.push({ x: this.x, y: this.y });
            if (this.trail.length > 50) this.trail.shift();

            // Rotation
            this.rotation += this.rotationSpeed;

            // Fuse check
            if (this.fuseTimer > 0 && Date.now() - this.spawnTime > this.fuseTimer) {
                this.explode();
                return;
            }

            // Cluster bomb check
            if (this.isCluster && !this.hasClustered && Date.now() - this.spawnTime > this.clusterTimer) {
                this.releaseClusters();
                return;
            }

            // Apply gravity
            this.vy += GRAVITY;

            // Apply wind
            if (this.weapon.affectedByWind) {
                this.vx += gameState.wind.x * 0.01;
                this.vy += gameState.wind.y * 0.005;
            }

            const newX = this.x + this.vx;
            const newY = this.y + this.vy;

            // Collision detection
            const hitTerrain = isTerrainPixel(newX, newY);
            const hitWater = isWaterLevel(newY);
            const outOfBounds = newX < 0 || newX > canvas.width || newY < -100;

            if (hitWater) {
                createSplashParticles(this.x, canvas.height - WATER_LEVEL_OFFSET);
                audio.playSplash();
                this.alive = false;
                return;
            }

            if (outOfBounds && newY > canvas.height + 100) {
                this.alive = false;
                return;
            }

            if (hitTerrain) {
                if (this.weapon.bounces && this.bounceCount < this.maxBounces) {
                    // Bounce
                    this.bounceCount++;
                    // Simple normal estimation - check surrounding pixels
                    const upClear = !isTerrainPixel(this.x, this.y - 5);
                    const downClear = !isTerrainPixel(this.x, this.y + 5);
                    const leftClear = !isTerrainPixel(this.x - 5, this.y);
                    const rightClear = !isTerrainPixel(this.x + 5, this.y);

                    if (!upClear) this.vy = Math.abs(this.vy) * 0.6;
                    if (!downClear) this.vy = -Math.abs(this.vy) * 0.6;
                    if (!leftClear) this.vx = Math.abs(this.vx) * 0.6;
                    if (!rightClear) this.vx = -Math.abs(this.vx) * 0.6;

                    // Default vertical bounce if unsure
                    if (upClear && downClear && leftClear && rightClear) {
                        this.vy = -this.vy * 0.6;
                        this.vx *= 0.8;
                    }

                    audio.playPop();
                    createDustParticles(this.x, this.y, 5);
                } else {
                    this.explode();
                    return;
                }
            }

            // Check collision with worms (direct hit)
            if (!this.weapon.instant) {
                for (const team of gameState.teams) {
                    for (const worm of team.worms) {
                        if (!worm.alive) continue;
                        
                        const dx = newX - worm.x;
                        const dy = newY - worm.y;
                        const dist = Math.sqrt(dx * dx + dy * dy);
                        
                        if (dist < 15) {
                            // Direct hit!
                            if (team === gameState.teams[gameState.currentTeamIndex]) {
                                // Self hit
                                createFloatingText(worm.x, worm.y - 30, 'FRIENDLY FIRE!', '#feca57');
                            }
                            this.explode();
                            return;
                        }
                    }
                }
            }

            this.x = newX;
            this.y = newY;
        }

        explode() {
            this.alive = false;
            
            const damage = this.weapon.damage;
            const radius = this.weapon.radius;

            // Visual effects
            createExplosionParticles(this.x, this.y, '#ff6b6b', 40);
            createFireParticles(this.x, this.y, 20);
            createShockwave(this.x, this.y, radius);
            
            // Screen effects
            const shakeIntensity = Math.min(radius / 10, 10);
            addScreenShake(shakeIntensity);
            
            if (radius >= 50) {
                addImpactFreeze(200);
            }

            audio.playExplosion(radius / 40);

            // Terrain destruction
            destroyTerrain(this.x, this.y, radius);

            // Damage worms in radius
            for (const team of gameState.teams) {
                for (const worm of team.worms) {
                    if (!worm.alive) continue;

                    const dx = worm.x - this.x;
                    const dy = worm.y - this.y;
                    const dist = Math.sqrt(dx * dx + dy * dy);

                    if (dist < radius + 20) {
                        // Damage falloff
                        const dmgFactor = 1 - (dist / (radius + 20)) * 0.5;
                        const actualDamage = Math.floor(damage * dmgFactor);
                        
                        worm.takeDamage(actualDamage, true);
                        
                        // Knockback
                        const knockbackForce = (1 - dist / (radius + 30)) * 8;
                        const angle = Math.atan2(dy, dx);
                        worm.vx += Math.cos(angle) * knockbackForce;
                        worm.vy += Math.sin(angle) * knockbackForce - 2;
                        worm.onGround = false;

                        createFloatingText(worm.x, worm.y - 30, `-${actualDamage}`, '#e94560');
                    }
                }
            }
        }

        releaseClusters() {
            this.alive = false;
            this.hasClustered = true;

            const clusterCount = this.weapon.clusters;
            
            for (let i = 0; i < clusterCount; i++) {
                const angle = (i / clusterCount) * Math.PI * 2 + Math.random() * 0.3;
                const speed = 3 + Math.random() * 3;
                
                const clusterWeapon = {
                    ...this.weapon,
                    id: 'cluster_bomblet',
                    name: 'Cluster Bomblet',
                    radius: 20,
                    damage: 20,
                    clusters: 0,
                    affectedByWind: true
                };

                const proj = new Projectile(
                    this.x,
                    this.y,
                    Math.cos(angle) * speed + this.vx * 0.3,
                    Math.sin(angle) * speed + this.vy * 0.2,
                    clusterWeapon,
                    this.owner
                );
                gameState.projectiles.push(proj);
            }

            audio.playPop();
            createExplosionParticles(this.x, this.y, '#feca57', 15);
        }

        draw(ctx, camX, camY, zoom) {
            if (!this.alive) return;

            const toScreen = (wx, wy) => ({
                x: (wx - camX) * zoom + canvas.width / 2,
                y: (wy - camY) * zoom + canvas.height / 2
            });

            // Draw trail
            if (this.trail.length > 1) {
                ctx.beginPath();
                const start = toScreen(this.trail[0].x, this.trail[0].y);
                ctx.moveTo(start.x, start.y);
                
                for (let i = 1; i < this.trail.length; i++) {
                    const p = toScreen(this.trail[i].x, this.trail[i].y);
                    ctx.lineTo(p.x, p.y);
                }
                
                ctx.strokeStyle = 'rgba(254, 202, 87, 0.4)';
                ctx.lineWidth = 2 * zoom;
                ctx.stroke();
            }

            const pos = toScreen(this.x, this.y);
            ctx.save();
            ctx.translate(pos.x, pos.y);
            ctx.rotate(this.rotation);
            ctx.scale(zoom, zoom);

            // Draw projectile based on weapon
            switch(this.weapon.id) {
                case 'bazooka':
                case 'mortar':
                    // Rocket shape
                    ctx.fillStyle = '#555';
                    ctx.beginPath();
                    ctx.ellipse(0, 0, 6, 3, Math.atan2(this.vy, this.vx), 0, Math.PI * 2);
                    ctx.fill();
                    // Exhaust
                    ctx.fillStyle = '#feca57';
                    const exhaustAngle = Math.atan2(-this.vy, -this.vx);
                    ctx.beginPath();
                    ctx.arc(Math.cos(exhaustAngle) * 8, Math.sin(exhaustAngle) * 8, 3, 0, Math.PI * 2);
                    ctx.fill();
                    break;

                case 'grenade':
                case 'cluster':
                    ctx.fillStyle = '#2d3436';
                    ctx.beginPath();
                    ctx.arc(0, 0, 5, 0, Math.PI * 2);
                    ctx.fill();
                    ctx.strokeStyle = '#636e72';
                    ctx.lineWidth = 1;
                    ctx.stroke();
                    // Pin
                    ctx.strokeStyle = '#b2bec3';
                    ctx.lineWidth = 1.5;
                    ctx.beginPath();
                    ctx.moveTo(0, -5);
                    ctx.lineTo(0, -8);
                    ctx.lineTo(2, -10);
                    ctx.stroke();
                    break;

                default:
                    ctx.fillStyle = '#ff6b6b';
                    ctx.beginPath();
                    ctx.arc(0, 0, 4, 0, Math.PI * 2);
                    ctx.fill();
            }

            ctx.restore();
        }
    }

    // =============================================================
    // PARTICLE SYSTEM
    // =============================================================
    function createExplosionParticles(x, y, color, count) {
        for (let i = 0; i < count; i++) {
            const angle = Math.random() * Math.PI * 2;
            const speed = 1 + Math.random() * 8;
            gameState.particles.push({
                x: x,
                y: y,
                vx: Math.cos(angle) * speed,
                vy: Math.sin(angle) * speed,
                size: 2 + Math.random() * 5,
                color: color,
                life: 60 + Math.random() * 60,
                maxLife: 120,
                gravity: 0.1,
                type: 'explosion'
            });
        }
    }

    function createFireParticles(x, y, count) {
        for (let i = 0; i < count; i++) {
            const angle = -Math.PI/2 + (Math.random() - 0.5) * 0.8;
            const speed = 1 + Math.random() * 4;
            gameState.particles.push({
                x: x + (Math.random() - 0.5) * 20,
                y: y + (Math.random() - 0.5) * 20,
                vx: Math.cos(angle) * speed,
                vy: Math.sin(angle) * speed,
                size: 4 + Math.random() * 6,
                life: 40 + Math.random() * 30,
                maxLife: 70,
                gravity: -0.05,
                type: 'fire'
            });
        }
    }

    function createDustParticles(x, y, count) {
        for (let i = 0; i < count; i++) {
            const angle = Math.random() * Math.PI * 2;
            const speed = 0.5 + Math.random() * 2;
            gameState.particles.push({
                x: x,
                y: y,
                vx: Math.cos(angle) * speed,
                vy: Math.sin(angle) * speed - 1,
                size: 2 + Math.random() * 4,
                color: '#b2bec3',
                life: 60 + Math.random() * 60,
                maxLife: 120,
                gravity: 0.02,
                type: 'dust'
            });
        }
    }

    function createSplashParticles(x, y) {
        for (let i = 0; i < 15; i++) {
            const angle = -Math.PI/2 + (Math.random() - 0.5) * 1.5;
            const speed = 2 + Math.random() * 5;
            gameState.particles.push({
                x: x + (Math.random() - 0.5) * 30,
                y: y,
                vx: Math.cos(angle) * speed,
                vy: Math.sin(angle) * speed,
                size: 2 + Math.random() * 4,
                color: '#48dbfb',
                life: 40 + Math.random() * 40,
                maxLife: 80,
                gravity: 0.15,
                type: 'splash'
            });
        }
    }

    function createShockwave(x, y, radius) {
        gameState.particles.push({
            x: x,
            y: y,
            radius: 5,
            maxRadius: radius * 1.5,
            life: 30,
            maxLife: 30,
            type: 'shockwave'
        });
    }

    function createFloatingText(x, y, text, color) {
        gameState.floatingTexts.push({
            x: x,
            y: y,
            text: text,
            color: color,
            life: 90,
            maxLife: 90,
            vy: -1.5
        });
    }

    function updateParticles() {
        // Regular particles
        gameState.particles = gameState.particles.filter(p => {
            p.life--;
            if (p.life <= 0) return false;

            if (p.type === 'shockwave') {
                p.radius += (p.maxRadius - 5) / p.maxLife;
            } else {
                p.vy += p.gravity || 0.1;
                p.x += p.vx;
                p.y += p.vy;
                p.vx *= 0.98;
            }

            return true;
        });

        // Floating texts
        gameState.floatingTexts = gameState.floatingTexts.filter(t => {
            t.life--;
            t.y += t.vy;
            return t.life > 0;
        });

        // Debris
        gameState.debris = gameState.debris.filter(d => {
            d.life--;
            if (d.life <= 0) return false;

            d.vy += GRAVITY;
            d.x += d.vx;
            d.y += d.vy;
            d.rotation += d.rotationSpeed;
            d.vx *= 0.99;

            // Collision with terrain
            if (isTerrainPixel(d.x, d.y) && d.vy > 0) {
                d.vy *= -0.3;
                d.vx *= 0.5;
                if (Math.abs(d.vy) < 0.5) {
                    d.vy = 0;
                    d.vx = 0;
                }
            }

            // Water
            if (isWaterLevel(d.y)) {
                return false;
            }

            return true;
        });
    }

    function drawParticles(ctx, camX, camY, zoom) {
        const toScreen = (wx, wy) => ({
            x: (wx - camX) * zoom + canvas.width / 2,
            y: (wy - camY) * zoom + canvas.height / 2
        });

        // Debris
        for (const d of gameState.debris) {
            const pos = toScreen(d.x, d.y);
            ctx.save();
            ctx.translate(pos.x, pos.y);
            ctx.rotate(d.rotation);
            ctx.fillStyle = d.color;
            ctx.fillRect(-d.size/2 * zoom, -d.size/2 * zoom, d.size * zoom, d.size * zoom);
            ctx.restore();
        }

        // Particles
        for (const p of gameState.particles) {
            if (p.type === 'shockwave') {
                const pos = toScreen(p.x, p.y);
                const alpha = p.life / p.maxLife;
                ctx.strokeStyle = `rgba(255, 255, 255, ${alpha * 0.5})`;
                ctx.lineWidth = 3 * zoom;
                ctx.beginPath();
                ctx.arc(pos.x, pos.y, p.radius * zoom, 0, Math.PI * 2);
                ctx.stroke();
            } else {
                const pos = toScreen(p.x, p.y);
                const alpha = p.life / p.maxLife;
                let color = p.color;

                if (p.type === 'fire') {
                    // Fire color gradient
                    const t = 1 - p.life / p.maxLife;
                    if (t < 0.3) color = '#feca57';
                    else if (t < 0.6) color = '#ff9f43';
                    else color = '#ee5a24';
                }

                ctx.globalAlpha = alpha;
                ctx.fillStyle = color;
                ctx.beginPath();
                ctx.arc(pos.x, pos.y, p.size * zoom / 2, 0, Math.PI * 2);
                ctx.fill();
                ctx.globalAlpha = 1;
            }
        }

        // Floating texts
        for (const t of gameState.floatingTexts) {
            const pos = toScreen(t.x, t.y);
            const alpha = t.life / t.maxLife;
            const scale = 1 + (1 - alpha) * 0.5;

            ctx.save();
            ctx.globalAlpha = alpha;
            ctx.font = `bold ${20 * scale}px 'Segoe UI', sans-serif`;
            ctx.fillStyle = t.color;
            ctx.textAlign = 'center';
            ctx.strokeStyle = 'rgba(0,0,0,0.8)';
            ctx.lineWidth = 3;
            ctx.strokeText(t.text, pos.x, pos.y);
            ctx.fillText(t.text, pos.x, pos.y);
            ctx.restore();
        }
    }

    // =============================================================
    // CAMERA & SCREEN EFFECTS
    // =============================================================
    function updateCamera() {
        const worm = getCurrentWorm();
        
        if (worm && worm.alive) {
            // Focus on active worm
            gameState.camera.targetX = worm.x;
            gameState.camera.targetY = worm.y;
        } else if (gameState.activeProjectile) {
            // Follow projectile
            gameState.camera.targetX = gameState.activeProjectile.x;
            gameState.camera.targetY = gameState.activeProjectile.y;
        }

        // Smooth camera movement
        gameState.camera.x += (gameState.camera.targetX - gameState.camera.x) * 0.08;
        gameState.camera.y += (gameState.camera.targetY - gameState.camera.y) * 0.08;

        // Clamp camera
        const marginX = canvas.width / 2 - 50;
        const marginY = canvas.height / 2 - 50;
        
        gameState.camera.x = Math.max(marginX, Math.min(canvas.width - marginX, gameState.camera.x));
        gameState.camera.y = Math.max(marginY, Math.min(canvas.height - marginY, gameState.camera.y));

        // Screen shake decay
        if (gameState.screenShake.intensity > 0) {
            gameState.screenShake.x = (Math.random() - 0.5) * gameState.screenShake.intensity * 2;
            gameState.screenShake.y = (Math.random() - 0.5) * gameState.screenShake.intensity * 2;
            gameState.screenShake.intensity *= 0.92;
            if (gameState.screenShake.intensity < 0.1) {
                gameState.screenShake.intensity = 0;
                gameState.screenShake.x = 0;
                gameState.screenShake.y = 0;
            }
        }

        // Impact freeze
        if (gameState.impactFreeze.active) {
            gameState.impactFreeze.duration--;
            if (gameState.impactFreeze.duration <= 0) {
                gameState.impactFreeze.active = false;
            }
        }
    }

    function addScreenShake(intensity) {
        gameState.screenShake.intensity = Math.max(gameState.screenShake.intensity, intensity);
    }

    function addImpactFreeze(duration) {
        gameState.impactFreeze.active = true;
        gameState.impactFreeze.duration = duration;
        gameState.impactFreeze.maxDuration = duration;
    }

    // =============================================================
    // AI SYSTEM
    // =============================================================
    class CPUPlayer {
        constructor(teamIndex) {
            this.teamIndex = teamIndex;
            this.state = 'idle'; // idle, aiming, charging, firing, waiting
            this.aimTimer = 0;
            this.chargeTimer = 0;
            this.waitTimer = 0;
            this.targetAngle = 0;
            this.targetPower = 0;
            this.currentWorm = null;
            this.targetWorm = null;
        }

        startTurn() {
            this.state = 'thinking';
            this.aimTimer = 0;
            this.chargeTimer = 0;
            this.waitTimer = 0;
            this.currentWorm = getCurrentWorm();
            
            gameState.cpuThinking = true;

            // Risk assessment - check if in danger
            this.assessRisks();
            
            // Choose best target
            this.chooseTarget();
            
            // Calculate optimal aim
            this.calculateAim();

            // Decide if we should move first
            const shouldMove = this.decideToMove();
            
            if (shouldMove) {
                this.state = 'moving';
                this.moveTimer = 60;
            } else {
                this.state = 'aiming';
            }
        }

        assessRisks() {
            if (!this.currentWorm) return;

            const worm = this.currentWorm;
            
            // Check water proximity
            const distToWater = (canvas.height - WATER_LEVEL_OFFSET) - worm.y;
            if (distToWater < 60) {
                // Danger close to water - mark as high risk
                this.waterRisk = true;
            } else {
                this.waterRisk = false;
            }

            // Check edge proximity
            if (worm.x < 80 || worm.x > canvas.width - 80) {
                this.edgeRisk = true;
            } else {
                this.edgeRisk = false;
            }
        }

        decideToMove() {
            if (!this.currentWorm) return false;

            const worm = this.currentWorm;

            // If near water, definitely move
            if (this.waterRisk) return true;
            if (this.edgeRisk && Math.random() < 0.7) return true;

            // Check if there's good cover nearby (we don't have cover system, so skip)
            
            // Random chance to reposition for better angle
            if (this.targetWorm) {
                const dx = this.targetWorm.x - worm.x;
                const dist = Math.abs(dx);
                
                // If too close, back away
                if (dist < 100 && Math.random() < 0.6) return true;
                
                // If too far, maybe move closer (for direct weapons)
                if (dist > 400 && gameState.selectedWeapon.category === 'direct' && Math.random() < 0.5) return true;
            }

            return Math.random() < 0.2;
        }

        chooseTarget() {
            let bestTarget = null;
            let bestScore = -Infinity;

            for (let ti = 0; ti < gameState.teams.length; ti++) {
                if (ti === this.teamIndex) continue;
                
                const team = gameState.teams[ti];
                for (const worm of team.worms) {
                    if (!worm.alive) continue;

                    // Score target
                    let score = 0;
                    
                    // Low health = high priority
                    score += (100 - worm.health) * 2;
                    
                    // Proximity (closer = easier to hit)
                    const dist = this.currentWorm ? Math.abs(worm.x - this.currentWorm.x) : 300;
                    score += Math.max(0, (500 - dist) / 10);

                    // Isolation (grouped targets better for AoE)
                    let nearbyAllies = 0;
                    for (const other of team.worms) {
                        if (other === worm || !other.alive) continue;
                        const od = Math.abs(other.x - worm.x);
                        if (od < 80) nearbyAllies++;
                    }
                    score += nearbyAllies * 20;

                    if (score > bestScore) {
                        bestScore = score;
                        bestTarget = worm;
                    }
                }
            }

            this.targetWorm = bestTarget;
            
            // Choose weapon based on target
            this.chooseWeapon();
        }

        chooseWeapon() {
            if (!this.targetWorm) {
                gameState.selectedWeapon = WEAPONS[0];
                return;
            }

            const dist = Math.abs(this.targetWorm.x - this.currentWorm.x);

            // If clustered targets, use cluster or grenade
            if (this.hasClusteredTargets() && Math.random() < 0.6) {
                gameState.selectedWeapon = WEAPONS.find(w => w.id === 'cluster') || WEAPONS[0];
            }
            // If close, use shotgun
            else if (dist < 200 && Math.random() < 0.5) {
                gameState.selectedWeapon = WEAPONS.find(w => w.id === 'shotgun') || WEAPONS[0];
            }
            // If far, use mortar or bazooka
            else if (dist > 400 && Math.random() < 0.4) {
                gameState.selectedWeapon = WEAPONS.find(w => w.id === 'mortar') || WEAPONS[0];
            }
            // Default
            else {
                const options = ['bazooka', 'grenade'];
                const choice = options[Math.floor(Math.random() * options.length)];
                gameState.selectedWeapon = WEAPONS.find(w => w.id === choice) || WEAPONS[0];
            }

            updateWeaponPanel();
        }

        hasClusteredTargets() {
            for (let ti = 0; ti < gameState.teams.length; ti++) {
                if (ti === this.teamIndex) continue;
                const team = gameState.teams[ti];
                const alive = team.getAliveWorms();
                
                for (let i = 0; i < alive.length; i++) {
                    for (let j = i + 1; j < alive.length; j++) {
                        const dist = Math.abs(alive[i].x - alive[j].x);
                        if (dist < 80) return true;
                    }
                }
            }
            return false;
        }

        calculateAim() {
            if (!this.currentWorm || !this.targetWorm) {
                this.targetAngle = -Math.PI / 4;
                this.targetPower = 0.5;
                return;
            }

            const worm = this.currentWorm;
            const target = this.targetWorm;

            const dx = target.x - worm.x;
            const dy = target.y - worm.y;

            // Set facing
            this.currentWorm.facing = dx >= 0 ? 1 : -1;

            // Predictive aiming with simple physics estimation
            // Adjust for wind
            const windAdjustment = gameState.wind.x * 0.05;
            
            // Solve ballistic equation approximately
            // We'll try different angles and pick the best one
            const baseAngle = Math.atan2(dy, Math.abs(dx));
            
            // For indirect fire, use a parabolic arc
            let bestAngle = baseAngle;
            let bestError = Infinity;
            let bestPower = 0.6;

            // Monte Carlo approach - try a few combinations
            for (let test = 0; test < 20; test++) {
                const testAngle = -Math.PI/3 + (test / 20) * Math.PI/2;
                const testPower = 0.4 + (test % 5) * 0.15;

                const error = this.simulateTrajectory(
                    worm.x, worm.y,
                    testAngle,
                    testPower * MAX_POWER * (gameState.selectedWeapon.projectileSpeed || 1),
                    target.x, target.y
                );

                if (error < bestError) {
                    bestError = error;
                    bestAngle = testAngle;
                    bestPower = testPower;
                }
            }

            // Add some randomness (imperfect aim)
            const accuracy = 0.85; // 85% accuracy
            const randomAngleOffset = (Math.random() - 0.5) * 0.3 * (1 - accuracy);
            const randomPowerOffset = (Math.random() - 0.5) * 0.2 * (1 - accuracy);

            this.targetAngle = bestAngle + randomAngleOffset;
            this.targetPower = Math.max(0.1, Math.min(1, bestPower + randomPowerOffset));

            // Adjust angle for facing
            if (dx < 0) {
                this.targetAngle = Math.PI - this.targetAngle;
            }
        }

        simulateTrajectory(startX, startY, angle, power, targetX, targetY) {
            let x = startX;
            let y = startY;
            let vx = Math.cos(angle) * power;
            let vy = Math.sin(angle) * power;

            let minError = Infinity;

            for (let step = 0; step < 500; step++) {
                vy += GRAVITY;
                if (gameState.selectedWeapon.affectedByWind) {
                    vx += gameState.wind.x * 0.01;
                }
                x += vx;
                y += vy;

                const error = Math.sqrt((x - targetX) ** 2 + (y - targetY) ** 2);
                minError = Math.min(minError, error);

                if (isTerrainPixel(x, y) || isWaterLevel(y) || y > canvas.height + 100) {
                    break;
                }
            }

            return minError;
        }

        update() {
            if (this.state === 'idle' || !gameState.cpuThinking) return;

            const worm = this.currentWorm;
            if (!worm || !worm.alive) {
                this.state = 'idle';
                gameState.cpuThinking = false;
                return;
            }

            switch(this.state) {
                case 'thinking':
                    this.waitTimer++;
                    if (this.waitTimer > 30) {
                        this.waitTimer = 0;
                        this.state = 'aiming';
                        audio.playDing();
                    }
                    break;

                case 'moving':
                    this.moveTimer--;
                    
                    if (this.waterRisk) {
                        // Move away from water - move upward if possible, or horizontally
                        const moveDir = worm.y > canvas.height/2 ? -1 : 1;
                        // Just jump to try to get higher ground
                        if (worm.onGround && this.moveTimer % 30 === 0) {
                            worm.vy = JUMP_FORCE;
                            worm.onGround = false;
                            audio.playJump();
                        }
                        // Move horizontally toward center
                        worm.vx = worm.x < canvas.width/2 ? MOVE_SPEED : -MOVE_SPEED;
                        worm.facing = worm.vx >= 0 ? 1 : -1;
                    } else if (this.edgeRisk) {
                        // Move toward center
                        const centerX = canvas.width / 2;
                        worm.vx = worm.x < centerX ? MOVE_SPEED : -MOVE_SPEED;
                        worm.facing = worm.vx >= 0 ? 1 : -1;
                    } else {
                        // Move toward a better position relative to target
                        if (this.targetWorm) {
                            const dx = this.targetWorm.x - worm.x;
                            // If too close, back up
                            if (Math.abs(dx) < 100) {
                                worm.vx = dx > 0 ? -MOVE_SPEED : MOVE_SPEED;
                            } else {
                                worm.vx = dx > 0 ? MOVE_SPEED * 0.5 : -MOVE_SPEED * 0.5;
                            }
                            worm.facing = dx >= 0 ? 1 : -1;
                        }
                    }

                    if (this.moveTimer <= 0) {
                        worm.vx = 0;
                        this.calculateAim();
                        this.state = 'aiming';
                        this.aimTimer = 0;
                    }
                    break;

                case 'aiming':
                    // Smoothly interpolate aim
                    this.aimTimer++;
                    
                    // Update game state aim
                    const aimLerp = Math.min(1, this.aimTimer / 60);
                    gameState.aimAngle = gameState.aimAngle + (this.targetAngle - gameState.aimAngle) * 0.05;

                    if (this.aimTimer > 80) {
                        this.state = 'charging';
                        this.chargeTimer = 0;
                        gameState.isCharging = true;
                        gameState.chargePower = 0;
                    }
                    break;

                case 'charging':
                    // Charge up to target power
                    this.chargeTimer++;
                    
                    if (gameState.chargePower < this.targetPower) {
                        gameState.chargePower += 0.015;
                        if (gameState.chargePower > 1) gameState.chargePower = 1;
                    }

                    // Hold at target power briefly, then fire
                    const targetReached = gameState.chargePower >= this.targetPower * 0.95;
                    
                    if (targetReached && this.chargeTimer > 30) {
                        this.state = 'firing';
                        this.fireWeapon();
                    }
                    break;

                case 'firing':
                    this.waitTimer++;
                    
                    // Wait for projectile to finish
                    const hasActiveProjectiles = gameState.projectiles.some(p => p.alive);
                    
                    if (!hasActiveProjectiles && this.waitTimer > 60) {
                        this.state = 'idle';
                        gameState.cpuThinking = false;
                        endTurn();
                    }
                    break;
            }
        }

        fireWeapon() {
            const worm = this.currentWorm;
            if (!worm) return;

            const weapon = gameState.selectedWeapon;
            const power = gameState.chargePower * MAX_POWER * (weapon.projectileSpeed || 1);

            if (weapon.instant) {
                fireInstantWeapon(worm, weapon);
            } else if (weapon.placeable) {
                placeWeapon(worm, weapon);
            } else {
                const vx = Math.cos(gameState.aimAngle) * power;
                const vy = Math.sin(gameState.aimAngle) * power;

                const proj = new Projectile(worm.x, worm.y - 5, vx, vy, weapon, worm);
                gameState.projectiles.push(proj);
                gameState.activeProjectile = proj;
            }

            gameState.isCharging = false;
            gameState.chargePower = 0;
            audio.playShoot();
        }
    }

    // =============================================================
    // WEAPON FIRING
    // =============================================================
    function fireInstantWeapon(worm, weapon) {
        if (weapon.id === 'shotgun') {
            const baseAngle = gameState.aimAngle;
            
            for (let i = 0; i < weapon.pellets; i++) {
                const spread = (i - (weapon.pellets - 1) / 2) * weapon.spread;
                const angle = baseAngle + spread + (Math.random() - 0.5) * 0.05;
                
                // Raycast
                let hitX = worm.x;
                let hitY = worm.y;
                let hitWorm = null;
                const range = weapon.range;

                for (let d = 0; d < range; d += 3) {
                    const tx = worm.x + Math.cos(angle) * d;
                    const ty = worm.y - 5 + Math.sin(angle) * d;

                    if (isTerrainPixel(tx, ty) || isWaterLevel(ty)) {
                        hitX = tx;
                        hitY = ty;
                        break;
                    }

                    // Check worm hit
                    for (const team of gameState.teams) {
                        for (const w of team.worms) {
                            if (!w.alive) continue;
                            const dx = tx - w.x;
                            const dy = ty - w.y;
                            if (Math.sqrt(dx*dx + dy*dy) < 12) {
                                hitX = tx;
                                hitY = ty;
                                hitWorm = w;
                                d = range;
                                break;
                            }
                        }
                    }
                }

                // Trail particle
                createShotgunTrail(worm.x, worm.y - 5, hitX, hitY);

                if (hitWorm) {
                    const dmg = Math.floor(weapon.damage / weapon.pellets);
                    hitWorm.takeDamage(dmg);
                    createFloatingText(hitWorm.x, hitWorm.y - 30, `-${dmg}`, '#e94560');
                    createExplosionParticles(hitX, hitY, '#feca57', 5);
                } else {
                    createDustParticles(hitX, hitY, 3);
                }
            }

            audio.playShoot();
            addScreenShake(2);
        }
    }

    function createShotgunTrail(x1, y1, x2, y2) {
        const dist = Math.sqrt((x2-x1)**2 + (y2-y1)**2);
        const steps = Math.floor(dist / 10);
        
        for (let i = 0; i < steps; i++) {
            const t = i / steps;
            const x = x1 + (x2 - x1) * t;
            const y = y1 + (y2 - y1) * t;
            
            gameState.particles.push({
                x: x,
                y: y,
                vx: (Math.random() - 0.5) * 0.5,
                vy: (Math.random() - 0.5) * 0.5,
                size: 2 + Math.random() * 2,
                color: '#feca57',
                life: 10 + Math.random() * 10,
                maxLife: 20,
                gravity: 0,
                type: 'spark'
            });
        }
    }

    function placeWeapon(worm, weapon) {
        // Place at worm's feet
        const weaponData = {
            ...weapon,
            x: worm.x,
            y: worm.y + WORM_HEIGHT/2,
            vx: 0,
            vy: 0,
            fuseTimer: weapon.fuseTime,
            spawnTime: Date.now(),
            placed: true
        };

        // Treat dynamite as a projectile with fuse
        const proj = new Projectile(
            worm.x,
            worm.y + WORM_HEIGHT/2,
            0, 0.5,
            weapon,
            worm
        );
        gameState.projectiles.push(proj);

        createFloatingText(worm.x, worm.y - 30, weapon.name + '!', '#feca57');
        audio.playPop();
    }

    // =============================================================
    // TRAJECTORY PREDICTION
    // =============================================================
    function calculateTrajectoryPoints(startX, startY, angle, power, weapon, maxPoints = 100) {
        const points = [];
        let x = startX;
        let y = startY;
        let vx = Math.cos(angle) * power * (weapon.projectileSpeed || 1);
        let vy = Math.sin(angle) * power * (weapon.projectileSpeed || 1);

        for (let i = 0; i < maxPoints; i++) {
            points.push({ x, y });

            vy += GRAVITY;
            if (weapon.affectedByWind) {
                vx += gameState.wind.x * 0.01;
            }
            x += vx;
            y += vy;

            if (isTerrainPixel(x, y) || isWaterLevel(y) || y > canvas.height + 50 || x < -50 || x > canvas.width + 50) {
                points.push({ x, y });
                break;
            }
        }

        return points;
    }

    // =============================================================
    // TURN MANAGEMENT
    // =============================================================
    function startTurn() {
        // Check for game over first
        if (checkGameOver()) return;

        // Skip defeated teams
        while (gameState.teams[gameState.currentTeamIndex].isDefeated()) {
            gameState.currentTeamIndex = (gameState.currentTeamIndex + 1) % gameState.teams.length;
        }

        const team = gameState.teams[gameState.currentTeamIndex];
        const aliveWorms = team.getAliveWorms();
        
        // Cycle to next worm
        gameState.currentWormIndex = (gameState.currentWormIndex + 1) % aliveWorms.length;
        
        const worm = getCurrentWorm();

        // Reset turn state
        gameState.turnTimeRemaining = gameState.roundTimeSetting;
        gameState.isCharging = false;
        gameState.chargePower = 0;
        gameState.activeProjectile = null;

        // Update HUD
        updateHUD();
        updateTeamsPanel();

        // Set camera
        if (worm) {
            gameState.camera.targetX = worm.x;
            gameState.camera.targetY = worm.y;
        }

        // Announce turn
        const teamColor = TEAM_COLORS[gameState.currentTeamIndex];
        const indicator = document.getElementById('teamIndicator');
        indicator.textContent = team.isCPU ? `CPU - ${teamColor.name}'s Turn` : `${teamColor.name}'s Turn`;
        indicator.style.borderColor = teamColor.primary;

        createFloatingText(canvas.width/2, 150, team.isCPU ? 'CPU TURN' : 'YOUR TURN!', teamColor.primary);
        audio.playDing();

        // Start AI if CPU team
        if (team.isCPU) {
            const cpu = gameState.cpuPlayers.find(c => c.teamIndex === gameState.currentTeamIndex);
            if (cpu) {
                cpu.startTurn();
            }
        }
    }

    function endTurn() {
        // Check for worms under terrain and push them out
        for (const team of gameState.teams) {
            for (const worm of team.worms) {
                if (!worm.alive) continue;
                
                // If stuck in terrain, nudge them
                if (isTerrainPixel(worm.x, worm.y)) {
                    // Try to move up
                    while (worm.y > 0 && isTerrainPixel(worm.x, worm.y)) {
                        worm.y -= 1;
                    }
                }
                
                // Also check feet - if floating, let them fall
                worm.onGround = isTerrainPixel(worm.x - 3, worm.y + WORM_HEIGHT/2) || 
                               isTerrainPixel(worm.x + 3, worm.y + WORM_HEIGHT/2);
            }
        }

        // Update alive counts
        for (const team of gameState.teams) {
            team.updateAliveCount();
        }

        // Check game over
        if (checkGameOver()) return;

        // Change wind slightly each turn
        const windChange = (Math.random() - 0.5) * 2;
        gameState.wind.x = Math.max(-5, Math.min(5, gameState.wind.x + windChange));
        updateWindIndicator();

        // Next team
        gameState.currentTeamIndex = (gameState.currentTeamIndex + 1) % gameState.teams.length;
        
        startTurn();
    }

    function checkGameOver() {
        const aliveTeams = gameState.teams.filter(t => !t.isDefeated());
        
        if (aliveTeams.length <= 1) {
            // Game over!
            gameState.phase = 'gameover';
            
            const winner = aliveTeams[0];
            const color = TEAM_COLORS[winner.index];
            
            document.getElementById('winnerText').textContent = `${color.name.toUpperCase()} WINS!`;
            document.getElementById('winnerText').style.color = color.primary;
            document.getElementById('victoryMessage').textContent = winner.isCPU ? 'CPU Victory!' : 'Congratulations!';
            
            document.getElementById('hud').classList.add('hidden');
            document.getElementById('gameOverScreen').classList.remove('hidden');
            
            audio.playVictory();
            return true;
        }
        
        return false;
    }

    // =============================================================
    // UI UPDATES
    // =============================================================
    function updateHUD() {
        const worm = getCurrentWorm();
        if (!worm) return;

        document.getElementById('wormName').textContent = worm.name;
        
        const hpPercent = (worm.health / worm.maxHealth) * 100;
        const healthFill = document.getElementById('healthFill');
        healthFill.style.width = `${hpPercent}%`;
        
        healthFill.className = 'health-fill';
        if (hpPercent > 60) healthFill.classList.add('high');
        else if (hpPercent > 30) healthFill.classList.add('medium');
        else healthFill.classList.add('low');
    }

    function updateTimerDisplay() {
        const el = document.getElementById('timerDisplay');
        const time = Math.ceil(gameState.turnTimeRemaining);
        el.textContent = time;

        if (time <= 10) {
            el.classList.add('urgent');
            // Tick sound
            if (Math.floor(gameState.turnTimeRemaining * 5) !== Math.floor((gameState.turnTimeRemaining + 0.016) * 5)) {
                const pitch = 440 + (10 - time) * 50;
                audio.playTimerTick(pitch);
            }
        } else {
            el.classList.remove('urgent');
        }
    }

    function updateWindIndicator() {
        const arrow = document.getElementById('windArrow');
        const strength = document.getElementById('windStrength');
        
        const w = gameState.wind.x;
        strength.textContent = Math.abs(w).toFixed(1);
        
        if (Math.abs(w) < 0.5) {
            arrow.textContent = '➖';
        } else if (w > 0) {
            arrow.textContent = '➡️';
        } else {
            arrow.textContent = '⬅️';
        }
    }

    function updateTeamsPanel() {
        const panel = document.getElementById('teamsPanel');
        panel.innerHTML = '';

        for (let i = 0; i < gameState.teams.length; i++) {
            const team = gameState.teams[i];
            const color = TEAM_COLORS[i];
            const alive = team.getAliveWorms().length;
            
            const entry = document.createElement('div');
            entry.className = 'team-entry' + (i === gameState.currentTeamIndex ? ' active' : '');
            
            entry.innerHTML = `
                <div class="team-color" style="background: ${color.primary}"></div>
                <span>${color.name}${team.isCPU ? ' (CPU)' : ''}: ${alive}/4 alive</span>
            `;
            
            panel.appendChild(entry);
        }
    }

    function updateWeaponPanel() {
        const panel = document.getElementById('weaponPanel');
        panel.innerHTML = '';

        for (const weapon of WEAPONS) {
            const slot = document.createElement('div');
            slot.className = 'weapon-slot' + (gameState.selectedWeapon.id === weapon.id ? ' selected' : '');
            
            let catColor = '#888';
            if (weapon.category === 'indirect') catColor = '#48dbfb';
            else if (weapon.category === 'direct') catColor = '#1dd1a1';
            else if (weapon.category === 'high_aoe') catColor = '#e94560';

            slot.innerHTML = `
                <div class="weapon-icon">${weapon.icon}</div>
                <div class="weapon-key">${weapon.key}</div>
                <div class="weapon-name">${weapon.name}</div>
            `;
            slot.style.borderLeft = `3px solid ${catColor}`;

            slot.addEventListener('click', () => {
                gameState.selectedWeapon = weapon;
                updateWeaponPanel();
                audio.playSelect();
            });

            panel.appendChild(slot);
        }
    }

    function updateAimInfo() {
        const angleDeg = Math.round(gameState.aimAngle * 180 / Math.PI);
        const powerPercent = Math.round(gameState.chargePower * 100);
        
        document.getElementById('aimInfo').textContent = 
            `Angle: ${angleDeg}° | Power: ${powerPercent}%`;
        
        document.getElementById('powerFill').style.width = `${powerPercent}%`;
    }

    // =============================================================
    // INPUT HANDLING
    // =============================================================
    document.addEventListener('keydown', (e) => {
        keys[e.code] = true;
        audio.init();

        if (gameState.phase === 'playing') {
            const currentTeam = gameState.teams[gameState.currentTeamIndex];
            const isPlayerTurn = currentTeam && !currentTeam.isCPU;

            // Weapon selection
            const weaponKeys = { Digit1: 0, Digit2: 1, Digit3: 2, Digit4: 3, Digit5: 4, Digit6: 5 };
            if (weaponKeys[e.code] !== undefined && isPlayerTurn) {
                const idx = weaponKeys[e.code];
                if (idx < WEAPONS.length) {
                    gameState.selectedWeapon = WEAPONS[idx];
                    updateWeaponPanel();
                    audio.playSelect();
                }
            }

            // Start charging
            if (e.code === 'Space' && isPlayerTurn && !gameState.isCharging && !gameState.projectiles.some(p => p.alive)) {
                const worm = getCurrentWorm();
                if (worm && worm.onGround) {
                    gameState.isCharging = true;
                    gameState.chargePower = 0;
                    gameState.chargeDirection = 1;
                }
            }

            // Skip turn
            if (e.code === 'Enter' && isPlayerTurn && !gameState.projectiles.some(p => p.alive)) {
                endTurn();
            }

            // Pause
            if (e.code === 'Escape') {
                gameState.phase = 'paused';
                document.getElementById('pauseMenu').classList.remove('hidden');
            }
        }
    });

    document.addEventListener('keyup', (e) => {
        keys[e.code] = false;

        if (gameState.phase === 'playing') {
            const currentTeam = gameState.teams[gameState.currentTeamIndex];
            const isPlayerTurn = currentTeam && !currentTeam.isCPU;

            // Fire weapon
            if (e.code === 'Space' && gameState.isCharging && isPlayerTurn) {
                fireChargedWeapon();
            }
        }
    });

    document.addEventListener('mousemove', (e) => {
        const rect = canvas.getBoundingClientRect();
        mouseX = e.clientX - rect.left;
        mouseY = e.clientY - rect.top;

        if (gameState.phase === 'playing') {
            const worm = getCurrentWorm();
            if (!worm) return;

            // Convert mouse to world coordinates
            const worldMouseX = (mouseX - canvas.width/2) / gameState.camera.zoom + gameState.camera.x;
            const worldMouseY = (mouseY - canvas.height/2) / gameState.camera.zoom + gameState.camera.y;

            // Calculate angle from worm to mouse
            const dx = worldMouseX - worm.x;
            const dy = worldMouseY - worm.y;
            
            gameState.aimAngle = Math.atan2(dy, dx);
            
            // Update facing
            if (dx > 0) worm.facing = 1;
            else worm.facing = -1;
        }
    });

    document.addEventListener('mousedown', () => { mouseDown = true; });
    document.addEventListener('mouseup', () => { mouseDown = false; });

    function fireChargedWeapon() {
        const worm = getCurrentWorm();
        if (!worm) return;

        const weapon = gameState.selectedWeapon;
        const power = gameState.chargePower * MAX_POWER;

        if (weapon.instant) {
            fireInstantWeapon(worm, weapon);
        } else if (weapon.placeable) {
            placeWeapon(worm, weapon);
        } else {
            const vx = Math.cos(gameState.aimAngle) * power * (weapon.projectileSpeed || 1);
            const vy = Math.sin(gameState.aimAngle) * power * (weapon.projectileSpeed || 1);

            const proj = new Projectile(worm.x, worm.y - 5, vx, vy, weapon, worm);
            gameState.projectiles.push(proj);
            gameState.activeProjectile = proj;
        }

        gameState.isCharging = false;
        gameState.chargePower = 0;
        audio.playShoot();
    }

    // =============================================================
    // MENU BUTTONS
    // =============================================================
    document.getElementById('startBtn').addEventListener('click', () => {
        audio.init();
        audio.playClick();
        startNewGame();
    });

    document.getElementById('howToBtn').addEventListener('click', () => {
        audio.playClick();
        document.getElementById('mainMenu').classList.add('hidden');
        document.getElementById('howToMenu').classList.remove('hidden');
    });

    document.getElementById('backBtn').addEventListener('click', () => {
        audio.playClick();
        document.getElementById('howToMenu').classList.add('hidden');
        document.getElementById('mainMenu').classList.remove('hidden');
    });

    document.getElementById('resumeBtn').addEventListener('click', () => {
        audio.playClick();
        gameState.phase = 'playing';
        document.getElementById('pauseMenu').classList.add('hidden');
    });

    document.getElementById('quitBtn').addEventListener('click', () => {
        audio.playClick();
        returnToMenu();
    });

    document.getElementById('playAgainBtn').addEventListener('click', () => {
        audio.playClick();
        startNewGame();
    });

    document.getElementById('menuBtn').addEventListener('click', () => {
        audio.playClick();
        returnToMenu();
    });

    function returnToMenu() {
        gameState.phase = 'menu';
        document.getElementById('pauseMenu').classList.add('hidden');
        document.getElementById('hud').classList.add('hidden');
        document.getElementById('gameOverScreen').classList.add('hidden');
        document.getElementById('mainMenu').classList.remove('hidden');
    }

    // =============================================================
    // GAME INITIALIZATION
    // =============================================================
    function startNewGame() {
        // Read settings
        gameState.terrainType = document.getElementById('terrainType').value;
        const teamCount = parseInt(document.getElementById('teamCount').value);
        const cpuCount = parseInt(document.getElementById('cpuCount').value);
        gameState.roundTimeSetting = parseInt(document.getElementById('roundTime').value);

        // Init terrain
        initTerrain();

        // Create teams first
        gameState.teams = [];
        gameState.cpuPlayers = [];

        for (let i = 0; i < teamCount; i++) {
            const isCPU = i >= (teamCount - cpuCount);
            gameState.teams.push(new Team(i, isCPU));
            if (isCPU) {
                gameState.cpuPlayers.push(new CPUPlayer(i));
            }
        }

        // NOW spawn worms - gameState.teams.length is correct
        for (const team of gameState.teams) {
            team.spawnWorms();
        }

        // Init wind
        gameState.wind = { x: (Math.random() - 0.5) * 6, y: 0 };

        // Reset game state
        gameState.currentTeamIndex = 0;
        gameState.currentWormIndex = 0;
        gameState.projectiles = [];
        gameState.particles = [];
        gameState.floatingTexts = [];
        gameState.debris = [];
        gameState.activeProjectile = null;
        gameState.selectedWeapon = WEAPONS[0];
        gameState.isCharging = false;
        gameState.chargePower = 0;
        gameState.screenShake = { x: 0, y: 0, intensity: 0 };
        gameState.impactFreeze = { active: false, duration: 0, maxDuration: 0 };
        gameState.cpuThinking = false;

        // Reset camera
        const firstWorm = gameState.teams[0].worms[0];
        gameState.camera = {
            x: firstWorm.x,
            y: firstWorm.y,
            targetX: firstWorm.x,
            targetY: firstWorm.y,
            zoom: 1,
            targetZoom: 1
        };

        // Update UI
        updateWeaponPanel();
        updateTeamsPanel();
        updateWindIndicator();

        // Show HUD, hide menu
        document.getElementById('mainMenu').classList.add('hidden');
        document.getElementById('gameOverScreen').classList.add('hidden');
        document.getElementById('hud').classList.remove('hidden');

        // Start game
        gameState.phase = 'playing';
        startTurn();
    }

    // =============================================================
    // MAIN GAME LOOP
    // =============================================================
    let lastTime = 0;
    let turnTimerAccum = 0;

    function gameLoop(timestamp) {
        const dt = Math.min((timestamp - lastTime) / 1000, 0.05);
        lastTime = timestamp;

        // Update
        if (gameState.phase === 'playing') {
            // Skip update during impact freeze
            if (!gameState.impactFreeze.active) {
                // Turn timer
                const currentTeam = gameState.teams[gameState.currentTeamIndex];
                const isPlayerTurn = currentTeam && !currentTeam.isCPU;
                const hasActiveProjectiles = gameState.projectiles.some(p => p.alive);

                if (isPlayerTurn && !hasActiveProjectiles && !gameState.cpuThinking) {
                    turnTimerAccum += dt;
                    if (turnTimerAccum >= 1) {
                        turnTimerAccum -= 1;
                        gameState.turnTimeRemaining -= 1;
                        
                        if (gameState.turnTimeRemaining <= 0) {
                            // Time's up!
                            if (gameState.isCharging) {
                                fireChargedWeapon();
                            } else {
                                endTurn();
                            }
                        }
                    }
                }

                // Update charge power
                if (gameState.isCharging) {
                    gameState.chargePower += 0.01;
                    if (gameState.chargePower > 1) gameState.chargePower = 0;
                }

                // Update current worm
                const worm = getCurrentWorm();
                if (worm && worm.alive) {
                    worm.update(dt);
                }

                // Update all worms (for physics, falling, etc.)
                for (const team of gameState.teams) {
                    for (const w of team.worms) {
                        if (w !== worm && w.alive) {
                            w.update(dt);
                        }
                    }
                }

                // Update projectiles
                for (const proj of gameState.projectiles) {
                    proj.update(dt);
                }

                // Update AI
                for (const cpu of gameState.cpuPlayers) {
                    if (gameState.currentTeamIndex === cpu.teamIndex) {
                        cpu.update();
                    }
                }

                // Update particles
                updateParticles();

                // Update camera
                updateCamera();

                // Update HUD
                updateHUD();
                updateTimerDisplay();
                updateAimInfo();
            }
        }

        // Render
        render();

        requestAnimationFrame(gameLoop);
    }

    // =============================================================
    // RENDERING
    // =============================================================
    function render() {
        // Clear
        ctx.fillStyle = '#1a1a2e';
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        if (gameState.phase === 'menu' || gameState.phase === 'paused' || gameState.phase === 'gameover') {
            // Still draw terrain if game was started
            if (terrainCanvas) {
                ctx.drawImage(terrainCanvas, 0, 0);
            }
            return;
        }

        const cam = gameState.camera;
        const shake = gameState.screenShake;

        // Apply screen shake
        ctx.save();
        ctx.translate(shake.x, shake.y);

        // World to screen conversion
        const toScreen = (wx, wy) => ({
            x: (wx - cam.x) * cam.zoom + canvas.width / 2,
            y: (wy - cam.y) * cam.zoom + canvas.height / 2
        });

        // Draw sky gradient
        const skyGrd = ctx.createLinearGradient(0, 0, 0, canvas.height);
        
        if (gameState.terrainType === 'cave') {
            skyGrd.addColorStop(0, '#2d3436');
            skyGrd.addColorStop(1, '#1a1a2e');
        } else {
            skyGrd.addColorStop(0, '#0984e3');
            skyGrd.addColorStop(0.5, '#74b9ff');
            skyGrd.addColorStop(1, '#a29bfe');
        }
        ctx.fillStyle = skyGrd;
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // Draw background clouds
        ctx.fillStyle = 'rgba(255,255,255,0.1)';
        const cloudOffset = (Date.now() / 100) % (canvas.width + 200);
        drawCloud(ctx, cloudOffset - 100, 80, 60);
        drawCloud(ctx, (cloudOffset + 400) % (canvas.width + 200) - 100, 120, 40);

        // Draw terrain
        ctx.drawImage(terrainCanvas, 0, 0);

        // Draw water animation
        const waterY = canvas.height - WATER_LEVEL_OFFSET;
        ctx.fillStyle = 'rgba(72, 219, 251, 0.3)';
        for (let x = 0; x < canvas.width; x += 20) {
            const waveY = Math.sin((x + Date.now() / 200) * 0.05) * 3;
            ctx.fillRect(x, waterY + waveY, 20, WATER_LEVEL_OFFSET);
        }

        // Draw aiming trajectory
        const worm = getCurrentWorm();
        if (worm && worm.alive) {
            const currentTeam = gameState.teams[gameState.currentTeamIndex];
            const isPlayerTurn = currentTeam && !currentTeam.isCPU;
            const hasActiveProjectiles = gameState.projectiles.some(p => p.alive);

            if (isPlayerTurn && !hasActiveProjectiles && !gameState.cpuThinking) {
                const power = gameState.isCharging ? 
                    gameState.chargePower * MAX_POWER : 
                    0.5 * MAX_POWER;

                const trajPoints = calculateTrajectoryPoints(
                    worm.x,
                    worm.y - 5,
                    gameState.aimAngle,
                    power,
                    gameState.selectedWeapon
                );

                if (trajPoints.length > 1) {
                    ctx.beginPath();
                    const start = toScreen(trajPoints[0].x, trajPoints[0].y);
                    ctx.moveTo(start.x, start.y);

                    for (let i = 1; i < trajPoints.length; i++) {
                        const p = toScreen(trajPoints[i].x, trajPoints[i].y);
                        ctx.lineTo(p.x, p.y);
                    }

                    ctx.strokeStyle = gameState.isCharging ? 
                        'rgba(254, 202, 87, 0.8)' : 
                        'rgba(255, 255, 255, 0.3)';
                    ctx.setLineDash(gameState.isCharging ? [] : [5, 5]);
                    ctx.lineWidth = 2;
                    ctx.stroke();
                    ctx.setLineDash([]);

                    // Draw dots along trajectory
                    for (let i = 5; i < trajPoints.length; i += 10) {
                        const p = toScreen(trajPoints[i].x, trajPoints[i].y);
                        ctx.fillStyle = 'rgba(254, 202, 87, 0.6)';
                        ctx.beginPath();
                        ctx.arc(p.x, p.y, 3, 0, Math.PI * 2);
                        ctx.fill();
                    }
                }
            }
        }

        // Draw particles
        drawParticles(ctx, cam.x, cam.y, cam.zoom);

        // Draw worms
        for (const team of gameState.teams) {
            for (const w of team.worms) {
                w.draw(ctx, cam.x, cam.y, cam.zoom);
            }
        }

        // Draw projectiles
        for (const proj of gameState.projectiles) {
            proj.draw(ctx, cam.x, cam.y, cam.zoom);
        }

        // Vignette effect
        const vignette = ctx.createRadialGradient(
            canvas.width/2, canvas.height/2, canvas.height * 0.3,
            canvas.width/2, canvas.height/2, canvas.height * 0.8
        );
        vignette.addColorStop(0, 'rgba(0,0,0,0)');
        vignette.addColorStop(1, 'rgba(0,0,0,0.4)');
        ctx.fillStyle = vignette;
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        ctx.restore();
    }

    function drawCloud(ctx, x, y, size) {
        ctx.beginPath();
        ctx.arc(x, y, size * 0.5, 0, Math.PI * 2);
        ctx.arc(x + size * 0.4, y - size * 0.1, size * 0.4, 0, Math.PI * 2);
        ctx.arc(x + size * 0.8, y, size * 0.45, 0, Math.PI * 2);
        ctx.arc(x + size * 0.3, y + size * 0.2, size * 0.35, 0, Math.PI * 2);
        ctx.fill();
    }

    // =============================================================
    // START
    // =============================================================
    requestAnimationFrame(gameLoop);

    </script>
</body>
</html>
<!-- agent-meta {"model":"bytedance-seed/seed-2.0-code","provider":"openrouter","persona":"full","sessionId":"c054efcb-ce10-478f-b55f-e3dfdd4c4cc7","tokensIn":1939034,"tokensOut":52150,"tokensTotal":1991184,"cost":1.1259669999999997,"turns":30,"toolCalls":29,"failedToolCalls":0,"timestamp":"2026-08-15T22:06:48.550Z"} -->