🎮ArcadeLab

Army Tower Defense

by MysticScout59
3745 lines126.4 KB
▶ Play
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Army Tower Defense</title>
<style>
  @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Share+Tech+Mono&display=swap');

  :root {
    --army-green: #3a5a1c;
    --dark-green: #1e2e0e;
    --olive: #6b7c3a;
    --tan: #c8a96e;
    --sand: #e8d5a3;
    --danger: #ff3a1a;
    --gold: #ffd700;
    --sky: #4a7ab5;
    --hud-bg: rgba(10,18,5,0.92);
    --border-col: #4a6a20;
  }

  * { box-sizing: border-box; margin: 0; padding: 0; }

  body {
    background: var(--dark-green);
    font-family: 'Share Tech Mono', monospace;
    color: var(--sand);
    overflow: hidden;
    height: 100vh;
    display: flex;
    flex-direction: column;
  }

  #header {
    background: var(--hud-bg);
    border-bottom: 2px solid var(--border-col);
    padding: 6px 12px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-shrink: 0;
  }

  #header h1 {
    font-family: 'Orbitron', sans-serif;
    font-size: 18px;
    color: var(--gold);
    letter-spacing: 3px;
    text-shadow: 0 0 10px rgba(255,215,0,0.4);
  }

  .stat-group { display: flex; gap: 20px; align-items: center; }

  .stat {
    display: flex; align-items: center; gap: 6px;
    font-size: 13px; font-family: 'Orbitron', sans-serif;
  }

  .stat-label { color: var(--olive); font-size: 10px; letter-spacing: 1px; }
  .stat-value { color: var(--gold); font-size: 16px; font-weight: 700; }
  .stat-value.lives { color: #ff6b6b; }
  .stat-value.cash { color: #7fff7f; }

  #main {
    display: flex;
    flex: 1;
    overflow: hidden;
    gap: 0;
  }

  #game-area {
    position: relative;
    flex: 1;
  }

  #gameCanvas {
    display: block;
    width: 100%;
    height: 100%;
  }

  #sidebar {
    width: 180px;
    background: var(--hud-bg);
    border-left: 2px solid var(--border-col);
    display: flex;
    flex-direction: column;
    padding: 8px;
    gap: 6px;
    overflow-y: auto;
    flex-shrink: 0;
  }

  #sidebar h3 {
    font-family: 'Orbitron', sans-serif;
    font-size: 11px;
    color: var(--olive);
    letter-spacing: 2px;
    border-bottom: 1px solid var(--border-col);
    padding-bottom: 4px;
    margin-bottom: 2px;
  }

  .tower-btn {
    background: linear-gradient(135deg, #1a2a08, #2a4010);
    border: 1px solid var(--border-col);
    border-radius: 4px;
    padding: 7px 8px;
    cursor: pointer;
    text-align: left;
    transition: all 0.15s;
    position: relative;
    overflow: hidden;
  }

  .tower-btn:hover { border-color: var(--gold); background: linear-gradient(135deg, #2a3a10, #3a5018); }
  .tower-btn.selected { border-color: var(--gold); box-shadow: 0 0 8px rgba(255,215,0,0.4); background: linear-gradient(135deg, #2a4010, #4a6a1a); }
  .tower-btn.cant-afford { opacity: 0.45; cursor: not-allowed; }

  .tower-btn .t-name {
    font-family: 'Orbitron', sans-serif;
    font-size: 11px;
    color: var(--sand);
    display: flex; align-items: center; gap: 5px;
  }

  .tower-btn .t-cost { color: #7fff7f; font-size: 11px; margin-top: 2px; }
  .tower-btn .t-desc { color: #888; font-size: 9px; margin-top: 2px; line-height: 1.3; }

  .tower-icon { font-size: 16px; }

  #sell-btn {
    background: linear-gradient(135deg, #3a0808, #5a1010);
    border: 1px solid #7a2020;
    border-radius: 4px;
    padding: 6px;
    cursor: pointer;
    font-family: 'Orbitron', sans-serif;
    font-size: 10px;
    color: #ff9999;
    text-align: center;
    transition: all 0.15s;
    display: none;
  }
  #sell-btn:hover { border-color: var(--danger); }
  #sell-btn.visible { display: block; }

  #wave-btn {
    background: linear-gradient(135deg, #1a3a6a, #0a1a3a);
    border: 2px solid #3a6aaa;
    border-radius: 4px;
    padding: 8px;
    cursor: pointer;
    font-family: 'Orbitron', sans-serif;
    font-size: 10px;
    color: #7ab5ff;
    text-align: center;
    letter-spacing: 1px;
    transition: all 0.15s;
    margin-top: 4px;
  }
  #wave-btn:hover { border-color: var(--gold); color: var(--gold); }
  #wave-btn:disabled { opacity: 0.4; cursor: not-allowed; }

  #msg-log {
    font-size: 9px;
    color: #667755;
    line-height: 1.6;
    max-height: 80px;
    overflow-y: auto;
    border-top: 1px solid var(--border-col);
    padding-top: 4px;
    margin-top: auto;
  }

  #msg-log .msg-line { margin-bottom: 1px; }
  #msg-log .msg-line.warn { color: #ffaa44; }
  #msg-log .msg-line.good { color: #88ff88; }

  #overlay {
    position: absolute;
    inset: 0;
    background: rgba(0,0,0,0.8);
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    z-index: 100;
    font-family: 'Orbitron', sans-serif;
  }

  #overlay h2 {
    font-size: 36px;
    color: var(--gold);
    text-shadow: 0 0 20px rgba(255,215,0,0.6);
    letter-spacing: 4px;
    margin-bottom: 12px;
  }

  #overlay p { color: var(--sand); font-size: 14px; margin-bottom: 6px; text-align: center; }

  #overlay button {
    margin-top: 20px;
    padding: 12px 30px;
    font-family: 'Orbitron', sans-serif;
    font-size: 14px;
    background: var(--army-green);
    color: var(--gold);
    border: 2px solid var(--gold);
    border-radius: 4px;
    cursor: pointer;
    letter-spacing: 2px;
    transition: all 0.2s;
  }
  #overlay button:hover { background: var(--olive); box-shadow: 0 0 15px rgba(255,215,0,0.4); }

  #tooltip {
    position: absolute;
    background: var(--hud-bg);
    border: 1px solid var(--border-col);
    border-radius: 4px;
    padding: 6px 10px;
    font-size: 10px;
    pointer-events: none;
    z-index: 50;
    display: none;
    max-width: 140px;
    line-height: 1.5;
  }

  /* DEV PANEL */
  #dev-panel {
    display: none;
    position: fixed;
    top: 48px;
    left: 50%;
    transform: translateX(-50%);
    background: rgba(5,10,3,0.97);
    border: 2px solid #ffdd00;
    border-radius: 6px;
    padding: 10px;
    z-index: 999;
    min-width: 280px;
    font-family: 'Share Tech Mono', monospace;
    font-size: 12px;
    color: var(--sand);
    box-shadow: 0 0 30px rgba(255,221,0,0.3);
  }
  #dev-panel.visible { display: block; }
  #dev-header {
    font-family: 'Orbitron', sans-serif;
    font-size: 13px;
    color: #ffdd00;
    letter-spacing: 2px;
    margin-bottom: 10px;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  #dev-close {
    cursor: pointer;
    color: #ff6666;
    font-size: 16px;
    line-height: 1;
  }
  .dev-section {
    margin-bottom: 8px;
    border-top: 1px solid #2a4a10;
    padding-top: 8px;
  }
  .dev-label {
    font-size: 9px;
    color: var(--olive);
    letter-spacing: 1px;
    margin-bottom: 5px;
  }
  .dev-row {
    display: flex;
    gap: 6px;
    align-items: center;
  }
  .dev-row button {
    background: #1a3a0a;
    border: 1px solid var(--border-col);
    border-radius: 3px;
    color: var(--sand);
    font-family: 'Share Tech Mono', monospace;
    font-size: 11px;
    padding: 4px 8px;
    cursor: pointer;
    transition: all 0.1s;
    flex: 1;
  }
  .dev-row button:hover { border-color: #ffdd00; color: #ffdd00; }
  .dev-row button.active { background: #3a1a0a; border-color: #ff4400; color: #ff4400; }
  .dev-row input, .dev-row select {
    background: #0a1a05;
    border: 1px solid var(--border-col);
    border-radius: 3px;
    color: var(--sand);
    font-family: 'Share Tech Mono', monospace;
    font-size: 11px;
    padding: 4px 6px;
    flex: 1;
  }
  #dev-tower-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    max-height: 120px;
    overflow-y: auto;
    padding: 4px;
    background: #0a120a;
    border: 1px solid var(--border-col);
    border-radius: 3px;
  }
  .dev-tower-chip {
    background: #1a3a0a;
    border: 1px solid var(--border-col);
    border-radius: 3px;
    padding: 3px 7px;
    font-size: 10px;
    cursor: pointer;
    color: var(--sand);
    white-space: nowrap;
    transition: all 0.1s;
  }
  .dev-tower-chip:hover { border-color: #ffdd00; color: #ffdd00; }
  .dev-tower-chip.active { background: #2a5a0a; border-color: #44ff44; color: #44ff44; }
</style>
</head>
<body>

<div id="header">
  <h1 id="title-btn" style="cursor:default;user-select:none;">⚔️ ARMY DEFENSE</h1>
  <div class="stat-group">
    <div class="stat">
      <div>
        <div class="stat-label">WAVE</div>
        <div class="stat-value" id="wave-display">1</div>
      </div>
    </div>
    <div class="stat">
      <div>
        <div class="stat-label">LIVES</div>
        <div class="stat-value lives" id="lives-display">8</div>
      </div>
    </div>
    <div class="stat">
      <div>
        <div class="stat-label">FUNDS</div>
        <div class="stat-value cash" id="cash-display">$1500</div>
      </div>
    </div>
    <div class="stat">
      <div>
        <div class="stat-label">SCORE</div>
        <div class="stat-value" id="score-display">0</div>
      </div>
    </div>
    <div class="stat">
      <div>
        <div class="stat-label">KILLS</div>
        <div class="stat-value" id="kills-display">0</div>
      </div>
    </div>
  </div>
</div>

<!-- DEV PANEL -->
<div id="dev-panel">
  <div id="dev-header">🛠 DEV PANEL <span id="dev-close" onclick="toggleDevPanel()">✕</span></div>
  <div class="dev-section">
    <div class="dev-label">JUMP TO WAVE</div>
    <div class="dev-row">
      <input id="dev-wave-input" type="number" min="1" max="20" value="1" />
      <button onclick="devJumpWave()">GO</button>
    </div>
  </div>
  <div class="dev-section">
    <div class="dev-label">ADD CASH</div>
    <div class="dev-row">
      <button onclick="devAddCash(500)">+$500</button>
      <button onclick="devAddCash(2000)">+$2K</button>
      <button onclick="devAddCash(10000)">+$10K</button>
    </div>
  </div>
  <div class="dev-section">
    <div class="dev-label">SPAWN ENEMY</div>
    <div class="dev-row">
      <select id="dev-enemy-select">
        <option value="infantry">Infantry</option>
        <option value="jeep">Jeep</option>
        <option value="humvee">Humvee</option>
        <option value="motorcycle">Motorcycle</option>
        <option value="apc">APC</option>
        <option value="ifv">IFV</option>
        <option value="tank">Tank</option>
        <option value="heavytank">Heavy Tank</option>
        <option value="spg">SPG</option>
        <option value="supplytruck">Supply Truck</option>
        <option value="engineer">Engineer</option>
        <option value="bombtruck">Bomb Truck</option>
        <option value="plane">Plane</option>
        <option value="helicopter">Helicopter</option>
        <option value="gunship">Gunship</option>
        <option value="drone">Drone</option>
        <option value="paratrooper">Paratrooper</option>
      </select>
      <button onclick="devSpawnEnemy()">SPAWN</button>
    </div>
  </div>
  <div class="dev-section">
    <div class="dev-label">GOD MODE</div>
    <div class="dev-row">
      <button id="dev-god-btn" onclick="devToggleGod()">OFF</button>
      <button onclick="devMaxLives()">FULL LIVES</button>
    </div>
  </div>
  <div class="dev-section">
    <div class="dev-label">FREE PLACE TOWER — select then click map</div>
    <div id="dev-tower-bar">
      <!-- populated by JS -->
    </div>
    <div class="dev-row" style="margin-top:5px;">
      <button onclick="devCancelPlace()" id="dev-cancel-btn" style="display:none;">✕ CANCEL PLACE</button>
    </div>
  </div>
  <div class="dev-section">
    <div class="dev-label">PLACE ALL TOWERS</div>
    <div class="dev-row">
      <button onclick="devPlaceAllTowers()">AUTO PLACE</button>
      <button onclick="devClearTowers()">CLEAR ALL</button>
    </div>
  </div>
</div>

<div id="main">
  <div id="game-area">
    <canvas id="gameCanvas"></canvas>
    <div id="overlay">
      <h2>🎖️ ARMY DEFENSE</h2>
      <button onclick="startGame()">▶ DEPLOY TROOPS</button>
    </div>
    <div id="tooltip"></div>
  </div>

  <div id="sidebar" style="display:none;">
    <button id="wave-btn" onclick="startWave()">▶ START WAVE 1</button>
    <button id="sell-btn" onclick="sellSelected()">💰 SELL TOWER</button>
    <h3>🏗 TOWERS</h3>
    <div id="tower-buttons"></div>

    <div id="msg-log"></div>
  </div>
</div>

<script>
// ===================== SOUND ENGINE =====================
let audioCtx = null;

function getAudio() {
  if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
  return audioCtx;
}

function playSound(fn) {
  try { fn(getAudio()); } catch(e) {}
}

// --- WEAPON FIRE SOUNDS ---

function sndM2Browning() {
  // Rapid sharp crack — machine gun burst
  playSound(ac => {
    const buf = ac.createBuffer(1, ac.sampleRate * 0.12, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i = 0; i < d.length; i++) {
      const t = i / ac.sampleRate;
      d[i] = (Math.random() * 2 - 1) * Math.exp(-t * 60)
            + Math.sin(t * 1800 * Math.PI * 2) * Math.exp(-t * 90) * 0.4;
    }
    const src = ac.createBufferSource();
    src.buffer = buf;
    const g = ac.createGain(); g.gain.setValueAtTime(0.35, ac.currentTime);
    src.connect(g); g.connect(ac.destination);
    src.start();
  });
}

function sndMortarFire() {
  // Deep hollow thump — mortar launch
  playSound(ac => {
    const osc = ac.createOscillator();
    const g = ac.createGain();
    osc.type = 'sine';
    osc.frequency.setValueAtTime(90, ac.currentTime);
    osc.frequency.exponentialRampToValueAtTime(30, ac.currentTime + 0.25);
    g.gain.setValueAtTime(0.6, ac.currentTime);
    g.gain.exponentialRampToValueAtTime(0.001, ac.currentTime + 0.3);
    // Add noise layer for the whomp
    const buf = ac.createBuffer(1, ac.sampleRate * 0.15, ac.sampleRate);
    const nd = buf.getChannelData(0);
    for (let i = 0; i < nd.length; i++) nd[i] = (Math.random()*2-1) * Math.exp(-(i/ac.sampleRate)*25);
    const ns = ac.createBufferSource(); ns.buffer = buf;
    const ng = ac.createGain(); ng.gain.setValueAtTime(0.3, ac.currentTime);
    osc.connect(g); g.connect(ac.destination);
    ns.connect(ng); ng.connect(ac.destination);
    osc.start(); osc.stop(ac.currentTime + 0.3);
    ns.start();
  });
}

function sndTOWFire() {
  // Rocket whoosh + ignition hiss
  playSound(ac => {
    const buf = ac.createBuffer(1, ac.sampleRate * 0.45, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i = 0; i < d.length; i++) {
      const t = i / ac.sampleRate;
      const noise = Math.random() * 2 - 1;
      const env = t < 0.05 ? t / 0.05 : Math.exp(-(t - 0.05) * 4);
      d[i] = noise * env * 0.8
            + Math.sin(t * 200 * Math.PI * 2) * Math.exp(-t * 8) * 0.3;
    }
    const src = ac.createBufferSource(); src.buffer = buf;
    const g = ac.createGain(); g.gain.setValueAtTime(0.5, ac.currentTime);
    const filter = ac.createBiquadFilter();
    filter.type = 'bandpass'; filter.frequency.value = 600; filter.Q.value = 0.8;
    src.connect(filter); filter.connect(g); g.connect(ac.destination);
    src.start();
  });
}

function sndEMPFire() {
  // Electric zap buzz
  playSound(ac => {
    const osc1 = ac.createOscillator();
    const osc2 = ac.createOscillator();
    const g = ac.createGain();
    osc1.type = 'sawtooth'; osc1.frequency.setValueAtTime(220, ac.currentTime);
    osc1.frequency.linearRampToValueAtTime(880, ac.currentTime + 0.08);
    osc1.frequency.linearRampToValueAtTime(110, ac.currentTime + 0.22);
    osc2.type = 'square'; osc2.frequency.setValueAtTime(440, ac.currentTime);
    osc2.frequency.linearRampToValueAtTime(1760, ac.currentTime + 0.1);
    g.gain.setValueAtTime(0.18, ac.currentTime);
    g.gain.exponentialRampToValueAtTime(0.001, ac.currentTime + 0.25);
    osc1.connect(g); osc2.connect(g); g.connect(ac.destination);
    osc1.start(); osc1.stop(ac.currentTime + 0.25);
    osc2.start(); osc2.stop(ac.currentTime + 0.25);
  });
}

function sndPatriotFire() {
  // Loud boom + tail scream — SAM launch
  playSound(ac => {
    // Launch boom
    const buf = ac.createBuffer(1, ac.sampleRate * 0.5, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i = 0; i < d.length; i++) {
      const t = i / ac.sampleRate;
      d[i] = (Math.random()*2-1) * Math.exp(-t * 12) * 0.9
            + Math.sin(t * 60 * Math.PI*2) * Math.exp(-t*10) * 0.5;
    }
    const src = ac.createBufferSource(); src.buffer = buf;
    // Missile scream osc
    const osc = ac.createOscillator();
    osc.type = 'sawtooth';
    osc.frequency.setValueAtTime(800, ac.currentTime + 0.05);
    osc.frequency.exponentialRampToValueAtTime(2400, ac.currentTime + 0.4);
    const og = ac.createGain();
    og.gain.setValueAtTime(0, ac.currentTime);
    og.gain.linearRampToValueAtTime(0.12, ac.currentTime + 0.06);
    og.gain.exponentialRampToValueAtTime(0.001, ac.currentTime + 0.45);
    const g = ac.createGain(); g.gain.setValueAtTime(0.55, ac.currentTime);
    src.connect(g); g.connect(ac.destination);
    osc.connect(og); og.connect(ac.destination);
    src.start(); osc.start(); osc.stop(ac.currentTime + 0.5);
  });
}

// --- ENEMY KILL / DEATH SOUNDS ---

function sndKillInfantry() {
  // Small pop + short grunt
  playSound(ac => {
    const buf = ac.createBuffer(1, ac.sampleRate * 0.18, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i = 0; i < d.length; i++) {
      const t = i / ac.sampleRate;
      d[i] = (Math.random()*2-1) * Math.exp(-t * 45) * 0.5
            + Math.sin(t * 320 * Math.PI*2) * Math.exp(-t*30) * 0.25;
    }
    const src = ac.createBufferSource(); src.buffer = buf;
    const g = ac.createGain(); g.gain.setValueAtTime(0.4, ac.currentTime);
    src.connect(g); g.connect(ac.destination); src.start();
  });
}

function sndKillJeep() {
  // Crunching metal bang
  playSound(ac => {
    const buf = ac.createBuffer(1, ac.sampleRate * 0.3, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i = 0; i < d.length; i++) {
      const t = i / ac.sampleRate;
      d[i] = (Math.random()*2-1) * Math.exp(-t * 18) * 0.8
            + Math.sin(t * 140 * Math.PI*2) * Math.exp(-t*22) * 0.35;
    }
    const src = ac.createBufferSource(); src.buffer = buf;
    const filter = ac.createBiquadFilter();
    filter.type = 'lowpass'; filter.frequency.value = 1800;
    const g = ac.createGain(); g.gain.setValueAtTime(0.55, ac.currentTime);
    src.connect(filter); filter.connect(g); g.connect(ac.destination); src.start();
  });
}

function sndKillTank() {
  // Massive deep BOOM with rumble
  playSound(ac => {
    const buf = ac.createBuffer(1, ac.sampleRate * 0.8, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i = 0; i < d.length; i++) {
      const t = i / ac.sampleRate;
      d[i] = (Math.random()*2-1) * Math.exp(-t * 6) * 1.0
            + Math.sin(t * 45 * Math.PI*2) * Math.exp(-t * 5) * 0.7
            + Math.sin(t * 80 * Math.PI*2) * Math.exp(-t * 8) * 0.4;
    }
    const src = ac.createBufferSource(); src.buffer = buf;
    const filter = ac.createBiquadFilter();
    filter.type = 'lowpass'; filter.frequency.value = 900;
    const g = ac.createGain(); g.gain.setValueAtTime(0.8, ac.currentTime);
    src.connect(filter); filter.connect(g); g.connect(ac.destination); src.start();
  });
}

function sndKillAPC() {
  // Metal crunch + mid explosion
  playSound(ac => {
    const buf = ac.createBuffer(1, ac.sampleRate * 0.5, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i = 0; i < d.length; i++) {
      const t = i / ac.sampleRate;
      d[i] = (Math.random()*2-1) * Math.exp(-t * 10) * 0.85
            + Math.sin(t * 70 * Math.PI*2) * Math.exp(-t*9) * 0.45
            + (Math.random()*2-1) * Math.exp(-t*30) * 0.3;
    }
    const src = ac.createBufferSource(); src.buffer = buf;
    const filter = ac.createBiquadFilter();
    filter.type = 'lowpass'; filter.frequency.value = 1400;
    const g = ac.createGain(); g.gain.setValueAtTime(0.65, ac.currentTime);
    src.connect(filter); filter.connect(g); g.connect(ac.destination); src.start();
  });
}

function sndKillPlane() {
  // Explosion + high pitched metallic crash + descending whine
  playSound(ac => {
    // Explosion
    const buf = ac.createBuffer(1, ac.sampleRate * 0.6, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i = 0; i < d.length; i++) {
      const t = i / ac.sampleRate;
      d[i] = (Math.random()*2-1) * Math.exp(-t*9) * 0.7
            + Math.sin(t * 110 * Math.PI*2) * Math.exp(-t*12) * 0.3;
    }
    const src = ac.createBufferSource(); src.buffer = buf;
    const g = ac.createGain(); g.gain.setValueAtTime(0.5, ac.currentTime);
    // Descending engine whine
    const osc = ac.createOscillator();
    osc.type = 'sawtooth';
    osc.frequency.setValueAtTime(1800, ac.currentTime);
    osc.frequency.exponentialRampToValueAtTime(80, ac.currentTime + 0.9);
    const og = ac.createGain();
    og.gain.setValueAtTime(0.15, ac.currentTime);
    og.gain.exponentialRampToValueAtTime(0.001, ac.currentTime + 0.9);
    src.connect(g); g.connect(ac.destination);
    osc.connect(og); og.connect(ac.destination);
    src.start(); osc.start(); osc.stop(ac.currentTime + 0.9);
  });
}

function sndKillHelicopter() {
  // Explosion + slowing rotor chop
  playSound(ac => {
    const buf = ac.createBuffer(1, ac.sampleRate * 0.55, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i = 0; i < d.length; i++) {
      const t = i / ac.sampleRate;
      d[i] = (Math.random()*2-1) * Math.exp(-t*8) * 0.75
            + Math.sin(t * 65 * Math.PI*2) * Math.exp(-t*7) * 0.4;
    }
    const src = ac.createBufferSource(); src.buffer = buf;
    const g = ac.createGain(); g.gain.setValueAtTime(0.6, ac.currentTime);
    // Slowing blade chop
    const osc = ac.createOscillator();
    osc.type = 'square';
    osc.frequency.setValueAtTime(22, ac.currentTime);
    osc.frequency.exponentialRampToValueAtTime(4, ac.currentTime + 1.1);
    const og = ac.createGain();
    og.gain.setValueAtTime(0.18, ac.currentTime);
    og.gain.exponentialRampToValueAtTime(0.001, ac.currentTime + 1.1);
    src.connect(g); g.connect(ac.destination);
    osc.connect(og); og.connect(ac.destination);
    src.start(); osc.start(); osc.stop(ac.currentTime + 1.1);
  });
}

const FIRE_SOUNDS = {
  machinegun:   sndM2Browning,
  mortar:       sndMortarFire,
  rocket:       sndTOWFire,
  emp:          sndEMPFire,
  antiair:      sndPatriotFire,
  sniper: ac => playSound(ac2 => {
    const buf = ac2.createBuffer(1, ac2.sampleRate*0.18, ac2.sampleRate);
    const d = buf.getChannelData(0);
    for (let i=0;i<d.length;i++){const t=i/ac2.sampleRate;d[i]=(Math.random()*2-1)*Math.exp(-t*80)*0.6+Math.sin(t*2400*Math.PI*2)*Math.exp(-t*120)*0.3;}
    const src=ac2.createBufferSource();src.buffer=buf;
    const g=ac2.createGain();g.gain.setValueAtTime(0.5,ac2.currentTime);
    src.connect(g);g.connect(ac2.destination);src.start();
  }),
  flamethrower: () => playSound(ac => {
    const buf = ac.createBuffer(1, ac.sampleRate*0.12, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i=0;i<d.length;i++){const t=i/ac.sampleRate;d[i]=(Math.random()*2-1)*Math.exp(-t*15)*0.5;}
    const src=ac.createBufferSource();src.buffer=buf;
    const f=ac.createBiquadFilter();f.type='bandpass';f.frequency.value=800;f.Q.value=0.5;
    const g=ac.createGain();g.gain.setValueAtTime(0.25,ac.currentTime);
    src.connect(f);f.connect(g);g.connect(ac.destination);src.start();
  }),
  howitzer: () => playSound(ac => {
    const buf = ac.createBuffer(1, ac.sampleRate*1.0, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i=0;i<d.length;i++){const t=i/ac.sampleRate;d[i]=(Math.random()*2-1)*Math.exp(-t*4)*1.0+Math.sin(t*35*Math.PI*2)*Math.exp(-t*3.5)*0.8;}
    const src=ac.createBufferSource();src.buffer=buf;
    const f=ac.createBiquadFilter();f.type='lowpass';f.frequency.value=700;
    const g=ac.createGain();g.gain.setValueAtTime(0.9,ac.currentTime);
    src.connect(f);f.connect(g);g.connect(ac.destination);src.start();
  }),
  mlrs: sndTOWFire,
  napalm: () => playSound(ac => {
    // Whoosh + ignition crackle
    const buf = ac.createBuffer(1, ac.sampleRate*0.4, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i=0;i<d.length;i++){const t=i/ac.sampleRate;d[i]=(Math.random()*2-1)*Math.exp(-t*6)*0.6+Math.sin(t*180*Math.PI*2)*Math.exp(-t*12)*0.3;}
    const src=ac.createBufferSource();src.buffer=buf;
    const f=ac.createBiquadFilter();f.type='bandpass';f.frequency.value=600;f.Q.value=0.4;
    const g=ac.createGain();g.gain.setValueAtTime(0.35,ac.currentTime);
    src.connect(f);f.connect(g);g.connect(ac.destination);src.start();
  }),
  chaingun: () => playSound(ac => {
    // Very rapid metallic rattle
    const buf = ac.createBuffer(1, ac.sampleRate*0.04, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i=0;i<d.length;i++){const t=i/ac.sampleRate;d[i]=(Math.random()*2-1)*Math.exp(-t*120)*0.5+Math.sin(t*2200*Math.PI*2)*Math.exp(-t*150)*0.2;}
    const src=ac.createBufferSource();src.buffer=buf;
    const g=ac.createGain();g.gain.setValueAtTime(0.2,ac.currentTime);
    src.connect(g);g.connect(ac.destination);src.start();
  }),
  cluster: () => playSound(ac => {
    // Thunk launch + scatter pops
    const buf = ac.createBuffer(1, ac.sampleRate*0.35, ac.sampleRate);
    const d = buf.getChannelData(0);
    for (let i=0;i<d.length;i++){const t=i/ac.sampleRate;d[i]=(Math.random()*2-1)*Math.exp(-t*14)*0.75+Math.sin(t*75*Math.PI*2)*Math.exp(-t*10)*0.4;}
    const src=ac.createBufferSource();src.buffer=buf;
    const f=ac.createBiquadFilter();f.type='lowpass';f.frequency.value=1200;
    const g=ac.createGain();g.gain.setValueAtTime(0.55,ac.currentTime);
    src.connect(f);f.connect(g);g.connect(ac.destination);src.start();
  }),
};

const KILL_SOUNDS = {
  infantry:    sndKillInfantry,
  jeep:        sndKillJeep,
  tank:        sndKillTank,
  apc:         sndKillAPC,
  plane:       sndKillPlane,
  helicopter:  sndKillHelicopter,
  motorcycle:  sndKillInfantry,   // small pop
  supplytruck: sndKillTank,       // big boom
  humvee:      sndKillJeep,       // metal crunch
  heavytank:   sndKillTank,       // massive boom
  spg:         sndKillTank,       // big boom
  ifv:         sndKillAPC,        // mid explosion
  gunship:     sndKillHelicopter, // rotors winding down
  drone:       sndKillPlane,      // descending whine
  paratrooper: sndKillPlane,      // plane explosion
  engineer:    sndKillInfantry,   // small pop
  bombtruck:   sndKillTank,       // massive boom
};

// ===================== DEV PANEL =====================
let devFreePlaceTower = null;

function devBuildTowerBar() {
  const bar = document.getElementById('dev-tower-bar');
  if (!bar) return;
  bar.innerHTML = '';
  TOWER_TYPES.forEach(t => {
    const chip = document.createElement('div');
    chip.className = 'dev-tower-chip';
    chip.id = 'dev-chip-' + t.id;
    chip.textContent = t.name;
    chip.onclick = () => devSelectFreeTower(t.id);
    bar.appendChild(chip);
  });
}

function devSelectFreeTower(id) {
  devFreePlaceTower = id;
  document.querySelectorAll('.dev-tower-chip').forEach(c => c.classList.remove('active'));
  document.getElementById('dev-chip-' + id)?.classList.add('active');
  selectedTowerType = null;
  document.querySelectorAll('.tower-btn').forEach(b => b.classList.remove('selected'));
  document.getElementById('dev-cancel-btn').style.display = 'block';
  logMsg(`🛠 DEV: Click map to place ${TOWER_TYPES.find(t=>t.id===id)?.name} free`, 'good');
}

function devCancelPlace() {
  devFreePlaceTower = null;
  document.querySelectorAll('.dev-tower-chip').forEach(c => c.classList.remove('active'));
  document.getElementById('dev-cancel-btn').style.display = 'none';
}


let devClickCount = 0, devClickTimer = null;
let devGodMode = false;

document.addEventListener('DOMContentLoaded', () => {
  document.getElementById('title-btn').addEventListener('click', () => {
    devClickCount++;
    if (devClickTimer) clearTimeout(devClickTimer);
    devClickTimer = setTimeout(() => { devClickCount = 0; }, 600);
    if (devClickCount >= 3) {
      devClickCount = 0;
      toggleDevPanel();
    }
  });
});

function toggleDevPanel() {
  document.getElementById('dev-panel').classList.toggle('visible');
}

function devJumpWave() {
  if (!gameRunning) { logMsg('Start the game first!', 'warn'); return; }
  const target = parseInt(document.getElementById('dev-wave-input').value) || 1;
  wave = target - 1;
  waveActive = false;
  waveEndTimer = 0;
  enemies = [];
  projectiles = [];
  spawnQueue = [];
  const wbtn = document.getElementById('wave-btn');
  wbtn.disabled = false;
  wbtn.textContent = `▶ START WAVE ${target}`;
  document.getElementById('wave-display').textContent = target;
  logMsg(`🛠 DEV: Jumped to wave ${target}`, 'good');
  updateHUD();
}

function devAddCash(amount) {
  if (!gameRunning) { logMsg('Start the game first!', 'warn'); return; }
  cash += amount;
  logMsg(`🛠 DEV: +$${amount} added`, 'good');
  updateHUD();
}

function devSpawnEnemy() {
  if (!gameRunning) { logMsg('Start the game first!', 'warn'); return; }
  const type = document.getElementById('dev-enemy-select').value;
  const base = ENEMY_TYPES[type];
  if (!base) return;
  spawnEnemy(type, base.hp, base.hp);
  logMsg(`🛠 DEV: Spawned ${base.name}`, 'good');
}

function devToggleGod() {
  devGodMode = !devGodMode;
  const btn = document.getElementById('dev-god-btn');
  btn.textContent = devGodMode ? 'ON' : 'OFF';
  btn.classList.toggle('active', devGodMode);
  logMsg(`🛠 DEV: God mode ${devGodMode ? 'ON' : 'OFF'}`, 'good');
}

function devMaxLives() {
  lives = 8;
  logMsg('🛠 DEV: Lives restored', 'good');
  updateHUD();
}

function devPlaceAllTowers() {
  if (!gameRunning) { logMsg('Start the game first!', 'warn'); return; }
  // Place one of each tower type in a grid away from the path
  const startX = canvasW * 0.1, startY = canvasH * 0.05;
  let col = 0, row = 0;
  TOWER_TYPES.forEach(tdef => {
    const x = startX + col * 55;
    const y = startY + row * 55;
    if (!onPath(x, y)) {
      towers.push({ x, y, type: tdef, cooldown: 0, target: null, angle: -Math.PI/2, disabled: 0, id: Math.random() });
    }
    col++;
    if (col > 4) { col = 0; row++; }
  });
  logMsg('🛠 DEV: All towers placed', 'good');
}

function devClearTowers() {
  towers = [];
  selectedTower = null;
  document.getElementById('sell-btn').classList.remove('visible');
  logMsg('🛠 DEV: All towers cleared', 'warn');
}


const GROUND = ['infantry','jeep','tank','apc','motorcycle','supplytruck','humvee','heavytank','spg','ifv','engineer','bombtruck'];
const AIR    = ['plane','helicopter','gunship','drone','paratrooper'];
const ALL    = [...GROUND, ...AIR];

const TOWER_TYPES = [
  {
    id: 'machinegun', name: 'M2 Browning', icon: '🔫',
    cost: 500, damage: 4, range: 80, fireRate: 22, color: '#7ab542',
    desc: '.50 cal — fast fire, shreds infantry', targets: GROUND,
    projectileColor: '#ffff44', projectileSize: 3, splash: 0
  },
  {
    id: 'minefield', name: 'M15 Minefield', icon: '💥',
    cost: 800, damage: 60, range: 40, fireRate: 999, color: '#6a5a2a',
    desc: 'Places mines — detonates on contact', targets: GROUND,
    projectileColor: '#ff4400', projectileSize: 8, splash: 40, mine: true
  },
  {
    id: 'mortar', name: 'M120 Mortar', icon: '💣',
    cost: 1000, damage: 18, range: 100, fireRate: 90, color: '#8a7a30',
    desc: 'Slow fire, splash damage', targets: GROUND,
    projectileColor: '#ff8800', projectileSize: 6, splash: 30
  },
  {
    id: 'flamethrower', name: 'M2 Flamethrower', icon: '🔥',
    cost: 1200, damage: 3, range: 55, fireRate: 4, color: '#cc4400',
    desc: 'Short range, continuous fire — melts infantry', targets: GROUND,
    projectileColor: '#ff6600', projectileSize: 5, splash: 18, flame: true
  },
  {
    id: 'radar', name: 'Radar Tower', icon: '📡',
    cost: 1500, damage: 0, range: 100, fireRate: 999, color: '#2a6a8a',
    desc: 'Boosts range of all nearby towers by 40%', targets: [],
    projectileColor: '#44ffff', projectileSize: 0, splash: 0, radarBoost: true
  },
  {
    id: 'napalm', name: 'Napalm Tower', icon: '🔥',
    cost: 1600, damage: 12, range: 105, fireRate: 600, color: '#cc5500',
    desc: 'Drops napalm — burns a road zone for 5 sec', targets: GROUND,
    projectileColor: '#ff6600', projectileSize: 7, splash: 0, napalm: true
  },
  {
    id: 'sniper', name: 'M107 Sniper', icon: '🎯',
    cost: 1800, damage: 120, range: 220, fireRate: 5000, color: '#8a6a2a',
    desc: 'Extreme range, one-shot power, slow reload', targets: GROUND,
    projectileColor: '#ffffff', projectileSize: 2, splash: 0
  },
  {
    id: 'medic', name: 'Medic Bunker', icon: '🏥',
    cost: 2000, damage: 0, range: 0, fireRate: 999, color: '#2a8a2a',
    desc: 'Slowly restores 1 life every 30 seconds', targets: [],
    projectileColor: '#44ff44', projectileSize: 0, splash: 0, medic: true
  },
  {
    id: 'chaingun', name: 'M168 Chain Gun', icon: '🔫',
    cost: 2200, damage: 6, range: 100, fireRate: 5, color: '#5a8a3a',
    desc: 'Insane fire rate — click to toggle ON/OFF', targets: ALL,
    projectileColor: '#ffff88', projectileSize: 2, splash: 0, chaingun: true
  },
  {
    id: 'emp', name: 'JAMMER-9 EMP', icon: '⚡',
    cost: 2500, damage: 6, range: 95, fireRate: 65, color: '#4a8abf',
    desc: 'Electromagnetic — slows all ground', targets: GROUND,
    projectileColor: '#44ddff', projectileSize: 5, splash: 0, slow: 0.45
  },
  {
    id: 'antiair', name: 'Patriot SAM', icon: '✈️',
    cost: 2750, damage: 28, range: 120, fireRate: 55, color: '#9a4abf',
    desc: 'Surface-to-air — only hits aircraft!', targets: AIR,
    projectileColor: '#ff44ff', projectileSize: 4, splash: 0
  },
  {
    id: 'cluster', name: 'Cluster Bomb', icon: '💥',
    cost: 2800, damage: 30, range: 140, fireRate: 180, color: '#8a3a5a',
    desc: 'One shell splits into 8 bomblets mid-air', targets: GROUND,
    projectileColor: '#ffaa00', projectileSize: 6, splash: 0, cluster: true
  },
  {
    id: 'rocket', name: 'TOW Missile', icon: '🚀',
    cost: 3000, damage: 40, range: 110, fireRate: 110, color: '#c84a1a',
    desc: 'Anti-armor guided missile', targets: GROUND,
    projectileColor: '#ff4400', projectileSize: 5, splash: 15
  },
  {
    id: 'mlrs', name: 'MLRS Battery', icon: '🚀',
    cost: 3500, damage: 25, range: 150, fireRate: 120, color: '#5a3a8a',
    desc: 'Fires 6-rocket salvo — great vs clusters', targets: ALL,
    projectileColor: '#ff8800', projectileSize: 4, splash: 20, salvo: 6
  },
  {
    id: 'howitzer', name: 'M109 Howitzer', icon: '💣',
    cost: 4000, damage: 90, range: 180, fireRate: 1000, color: '#7a5a1a',
    desc: 'Massive artillery — huge splash, hits all ground', targets: GROUND,
    projectileColor: '#ffaa00', projectileSize: 8, splash: 70
  },
];

const ENEMY_TYPES = {
  infantry: {
    name: 'Infantry', hp: 60, speed: 0.7, reward: 25,
    color: '#558844', size: 16, flying: false, wave: 1, armor: 0
  },
  jeep: {
    name: 'Jeep', hp: 120, speed: 1.0, reward: 50,
    color: '#8a7a40', size: 18, flying: false, wave: 1, armor: 0
  },
  tank: {
    name: 'Tank', hp: 1500, speed: 0.4, reward: 300,
    color: '#4a6a2a', size: 20, flying: false, wave: 3, armor: 10
  },
  apc: {
    name: 'APC', hp: 250, speed: 0.6, reward: 100,
    color: '#6a7a3a', size: 19, flying: false, wave: 4, armor: 5
  },
  plane: {
    name: 'Bomber', hp: 180, speed: 1.3, reward: 125,
    color: '#4a7ab5', size: 18, flying: true, wave: 5, armor: 0
  },
  helicopter: {
    name: 'Chopper', hp: 300, speed: 0.9, reward: 175,
    color: '#2a4a8a', size: 18, flying: true, wave: 6, armor: 0
  },
  // --- NEW ENEMIES ---
  motorcycle: {
    name: 'Motorcycle Scout', hp: 40, speed: 2.2, reward: 35,
    color: '#8a6a20', size: 13, flying: false, wave: 2, armor: 0
  },
  supplytruck: {
    name: 'Supply Truck', hp: 500, speed: 0.35, reward: 400,
    color: '#7a5a30', size: 22, flying: false, wave: 3, armor: 0,
    bonusCash: 300  // extra cash on kill
  },
  humvee: {
    name: 'Humvee', hp: 160, speed: 1.4, reward: 60,
    color: '#6a7a2a', size: 16, flying: false, wave: 2, armor: 2
  },
  heavytank: {
    name: 'Heavy Tank', hp: 5000, speed: 0.28, reward: 600,
    color: '#2a4a1a', size: 24, flying: false, wave: 5, armor: 25
  },
  spg: {
    name: 'SPG', hp: 380, speed: 0.5, reward: 200,
    color: '#5a5a2a', size: 20, flying: false, wave: 4, armor: 8,
    shootsBack: true, shootCooldown: 8000, disableDuration: 8000, shootRange: 160
  },
  ifv: {
    name: 'IFV', hp: 300, speed: 0.8, reward: 150,
    color: '#4a6a3a', size: 19, flying: false, wave: 4, armor: 6,
    spawnsInfantry: 3
  },
  gunship: {
    name: 'Gunship', hp: 600, speed: 1.6, reward: 350,
    color: '#1a2a6a', size: 20, flying: true, wave: 6, armor: 8,
    shootsBack: true, shootCooldown: 8000, disableDuration: 8000, shootRange: 200
  },
  drone: {
    name: 'Stealth Drone', hp: 120, speed: 1.8, reward: 150,
    color: '#4a4a4a', size: 14, flying: true, wave: 5, armor: 0,
    stealth: true  // half range vs towers without radar
  },
  paratrooper: {
    name: 'Paratroopers', hp: 80, speed: 0, reward: 0,
    color: '#3a5a1a', size: 15, flying: true, wave: 5, armor: 0,
    isTransport: true  // drops infantry, then dies
  },
  engineer: {
    name: 'Engineer', hp: 80, speed: 0.65, reward: 75,
    color: '#8a6a1a', size: 14, flying: false, wave: 5, armor: 0,
    repairsNearby: true, repairRate: 8, repairRange: 60
  },
  bombtruck: {
    name: 'Bomb Truck', hp: 800, speed: 0.3, reward: 250,
    color: '#5a2a1a', size: 22, flying: false, wave: 6, armor: 0,
    baseDamage: 3  // deals 3 lives on reaching base
  }
};

// ===================== STATE =====================
let canvas, ctx;
let canvasW, canvasH;
let path = [];
let towers = [];
let enemies = [];
let projectiles = [];
let particles = [];
let floaties = [];

let lives = 8;
let cash = 200;
let score = 0;
let kills = 0;
let wave = 0;
let waveActive = false;
let waveEndTimer = 0;
let napalmZones = []; // {x, y, radius, damage, life, maxLife}
let selectedTowerType = null;
let selectedTower = null;
let gameRunning = false;
let spawnQueue = [];
let spawnTimer = 0;
let animFrame;

// ===================== PATH DEFINITION =====================
function buildPath(w, h) {
  // Fixed winding path across the map
  const pts = [
    {x: 0,   y: h*0.2},
    {x: w*0.15, y: h*0.2},
    {x: w*0.15, y: h*0.7},
    {x: w*0.35, y: h*0.7},
    {x: w*0.35, y: h*0.25},
    {x: w*0.55, y: h*0.25},
    {x: w*0.55, y: h*0.75},
    {x: w*0.75, y: h*0.75},
    {x: w*0.75, y: h*0.35},
    {x: w*0.92, y: h*0.35},
    {x: w,     y: h*0.35}
  ];
  return pts;
}

function interpolatePath(pts, totalLen) {
  let segs = [];
  for (let i=0; i<pts.length-1; i++) {
    let dx = pts[i+1].x - pts[i].x, dy = pts[i+1].y - pts[i].y;
    segs.push({x1: pts[i].x, y1: pts[i].y, x2: pts[i+1].x, y2: pts[i+1].y, len: Math.sqrt(dx*dx+dy*dy)});
  }
  return segs;
}

// ===================== WAVE CONFIG =====================
function buildWave(waveNum) {
  let queue = [];
  const w = waveNum;
  let cursor = 0; // running time cursor with random gaps

  function addEnemy(type, baseDelay) {
    const jitter = 2000 + Math.random() * 3000; // 2–5 sec random gap
    cursor = Math.max(cursor, baseDelay) + jitter;
    queue.push({type, delay: cursor});
  }

  // Infantry — random batches of 1-5
  let infantryCount = 15 + w*8;
  let spawned = 0;
  while (spawned < infantryCount) {
    const batchSize = Math.min(Math.ceil(Math.random()*4), infantryCount - spawned);
    // Tight spacing within a batch (0.3–0.6 sec apart)
    for (let b = 0; b < batchSize; b++) {
      const withinBatchDelay = b * (300 + Math.random()*300);
      queue.push({type:'infantry', delay: cursor + withinBatchDelay});
    }
    spawned += batchSize;
    // Big random gap between batches (2–5 sec)
    cursor += 2000 + Math.random()*3000;
  }

  // Jeeps from wave 2
  if (w >= 2) {
    cursor = 0;
    let jeepCount = 5 + Math.floor(w*3);
    for (let i=0; i<jeepCount; i++) addEnemy('jeep', 800);
  }

  // APCs from wave 4
  if (w >= 4) {
    cursor = 0;
    let apcCount = 3 + Math.floor((w-3)*3);
    for (let i=0; i<apcCount; i++) addEnemy('apc', 1800);
  }

  // Tanks from wave 3
  if (w >= 3) {
    cursor = 0;
    let tankCount = 2 + Math.floor((w-2)*2);
    for (let i=0; i<tankCount; i++) addEnemy('tank', 2400);
  }

  // Planes from wave 5
  if (w >= 5) {
    cursor = 0;
    let planeCount = 4 + Math.floor((w-4)*3);
    for (let i=0; i<planeCount; i++) addEnemy('plane', 1600);
  }

  // Motorcycles from wave 2 — fast scouts
  if (w >= 2) {
    cursor = 0;
    let motoCount = 3 + Math.floor(w*2);
    for (let i=0; i<motoCount; i++) addEnemy('motorcycle', 500);
  }

  // Humvees from wave 2
  if (w >= 2) {
    cursor = 0;
    let humveeCount = 3 + Math.floor(w*2);
    for (let i=0; i<humveeCount; i++) addEnemy('humvee', 700);
  }

  // Supply trucks from wave 3 — slow cash pinatas
  if (w >= 3) {
    cursor = 0;
    let truckCount = 1 + Math.floor((w-2));
    for (let i=0; i<truckCount; i++) addEnemy('supplytruck', 1200);
  }

  // SPGs from wave 4 — shoots back!
  if (w >= 4) {
    cursor = 0;
    let spgCount = 1 + Math.floor((w-3));
    for (let i=0; i<spgCount; i++) addEnemy('spg', 1800);
  }

  // IFVs from wave 4 — spawns infantry on death
  if (w >= 4) {
    cursor = 0;
    let ifvCount = 2 + Math.floor((w-3)*1.5);
    for (let i=0; i<ifvCount; i++) addEnemy('ifv', 2000);
  }

  // Heavy tanks from wave 5 — boss units
  if (w >= 5) {
    cursor = 0;
    let heavyCount = 1 + Math.floor((w-4));
    for (let i=0; i<heavyCount; i++) addEnemy('heavytank', 3000);
  }

  // Engineers from wave 5 — repairs nearby tanks
  if (w >= 5) {
    cursor = 0;
    let engCount = 2 + Math.floor((w-4));
    for (let i=0; i<engCount; i++) addEnemy('engineer', 2200);
  }

  // Stealth drones from wave 5
  if (w >= 5) {
    cursor = 0;
    let droneCount = 2 + Math.floor((w-4)*2);
    for (let i=0; i<droneCount; i++) addEnemy('drone', 1400);
  }

  // Paratroopers from wave 5 — air transport that drops infantry
  if (w >= 5) {
    cursor = 0;
    let paraCount = 1 + Math.floor((w-4));
    for (let i=0; i<paraCount; i++) addEnemy('paratrooper', 1600);
  }

  // Gunships from wave 6 — tougher choppers
  if (w >= 6) {
    cursor = 0;
    let gunCount = 2 + Math.floor((w-5));
    for (let i=0; i<gunCount; i++) addEnemy('gunship', 2600);
  }

  // Helicopters from wave 6
  if (w >= 6) {
    cursor = 0;
    let chopCount = 3 + Math.floor((w-5)*2);
    for (let i=0; i<chopCount; i++) addEnemy('helicopter', 2800);
  }

  // Bomb trucks from wave 6 — 3 lives if they reach base
  if (w >= 6) {
    cursor = 0;
    let bombCount = 1 + Math.floor((w-5));
    for (let i=0; i<bombCount; i++) addEnemy('bombtruck', 3500);
  }

  // Scale HP per wave
  queue = queue.map(e => {
    let base = {...ENEMY_TYPES[e.type]};
    let scaledHp = Math.floor(base.hp * (1 + (w-1)*0.18));
    return {...e, hp: scaledHp, maxHp: scaledHp};
  });

  return queue;
}

// ===================== INIT =====================
function initCanvas() {
  canvas = document.getElementById('gameCanvas');
  const area = document.getElementById('game-area');
  canvasW = area.clientWidth;
  canvasH = area.clientHeight;
  canvas.width = canvasW;
  canvas.height = canvasH;
  ctx = canvas.getContext('2d');
  path = buildPath(canvasW, canvasH);
}

function startGame() {
  document.getElementById('overlay').style.display = 'none';
  document.getElementById('sidebar').style.display = 'flex';
  gameRunning = true;
  lives = 8; cash = 9000000000; score = 0; kills = 0; wave = 0; waveEndTimer = 0;
  towers = []; enemies = []; projectiles = []; particles = []; floaties = []; napalmZones = [];
  selectedTower = null; selectedTowerType = null;
  updateHUD();
  buildTowerButtons();
  devBuildTowerBar();
  if (animFrame) cancelAnimationFrame(animFrame);
  gameLoop();
}

function buildTowerButtons() {
  const container = document.getElementById('tower-buttons');
  container.innerHTML = '';
  TOWER_TYPES.forEach(t => {
    const btn = document.createElement('div');
    btn.className = 'tower-btn';
    btn.id = 'tbtn-'+t.id;

    // Mini canvas preview
    const previewId = 'preview-'+t.id;
    btn.innerHTML = `
      <div style="display:flex;align-items:center;gap:7px;">
        <canvas id="${previewId}" width="38" height="38" style="border-radius:3px;background:#1a2a08;flex-shrink:0;"></canvas>
        <div>
          <div class="t-name">${t.name}</div>
          <div class="t-cost">💰 $${t.cost}</div>
          <div class="t-desc">${t.desc}</div>
        </div>
      </div>
    `;
    btn.onclick = () => selectTowerType(t.id);
    container.appendChild(btn);

    // Draw the preview after appending to DOM
    requestAnimationFrame(() => {
      const pc = document.getElementById(previewId);
      if (!pc) return;
      const pctx = pc.getContext('2d');
      pctx.save();
      pctx.translate(19, 22);
      // Sandbags
      drawSandbagsOn(pctx, 0, 5);
      // Tower drawing using the shared draw functions but on pctx
      const prevCtx = ctx;
      ctx = pctx;
      switch(t.id) {
        case 'machinegun':   drawM2Browning(0); break;
        case 'mortar':       drawMortarTower(0); break;
        case 'rocket':       drawTOWMissile(0); break;
        case 'emp':          drawEMPTower(0); break;
        case 'antiair':      drawPatriotSAM(0); break;
        case 'sniper':       drawSniper(0); break;
        case 'flamethrower': drawFlamethrower(0); break;
        case 'minefield':    drawMinefield(0); break;
        case 'howitzer':     drawHowitzer(0); break;
        case 'mlrs':         drawMLRS(0); break;
        case 'radar':        drawRadar(0); break;
        case 'medic':        drawMedic(0); break;
        case 'smoke':        break; // removed
        case 'apache':       break; // removed
        case 'napalm':       drawNapalmTower(0); break;
        case 'chaingun':     drawChainGun(0, null); break;
        case 'cluster':      drawClusterLauncher(0); break;
      }
      ctx = prevCtx;
      pctx.restore();
    });
  });
}

// ===================== HUD =====================
function updateHUD() {
  document.getElementById('lives-display').textContent = lives;
  document.getElementById('cash-display').textContent = '$'+cash;
  document.getElementById('score-display').textContent = score;
  document.getElementById('kills-display').textContent = kills;
  document.getElementById('wave-display').textContent = wave || '-';
  // Update affordability
  TOWER_TYPES.forEach(t => {
    const btn = document.getElementById('tbtn-'+t.id);
    if (!btn) return;
    btn.classList.toggle('cant-afford', cash < t.cost);
  });
}

let msgLines = [];
function logMsg(text, type='') {
  msgLines.unshift({text, type});
  if (msgLines.length > 8) msgLines.pop();
  const log = document.getElementById('msg-log');
  log.innerHTML = msgLines.map(m => `<div class="msg-line ${m.type}">${m.text}</div>`).join('');
}

// ===================== TOWER SELECTION & PLACEMENT =====================
function selectTowerType(id) {
  selectedTowerType = id;
  selectedTower = null;
  document.getElementById('sell-btn').classList.remove('visible');
  document.querySelectorAll('.tower-btn').forEach(b => b.classList.remove('selected'));
  document.getElementById('tbtn-'+id)?.classList.add('selected');
}

function selectPlacedTower(tower) {
  selectedTower = tower;
  selectedTowerType = null;
  document.querySelectorAll('.tower-btn').forEach(b => b.classList.remove('selected'));
  document.getElementById('sell-btn').classList.add('visible');
}

function sellSelected() {
  if (!selectedTower) return;
  const refund = Math.floor(selectedTower.type.cost * 0.6);
  cash += refund;
  towers = towers.filter(t => t !== selectedTower);
  selectedTower = null;
  document.getElementById('sell-btn').classList.remove('visible');
  logMsg(`Tower sold for $${refund}`, 'good');
  updateHUD();
}

// ===================== CANVAS CLICK =====================
canvas && canvas.addEventListener('click', handleClick);

// ---- Apache airstrike state ----
let apacheMode = false; // true when player has selected an apache beacon and is picking a strike point

function handleClick(e) {
  if (!gameRunning) return;
  const rect = canvas.getBoundingClientRect();
  const scaleX = canvasW / rect.width;
  const scaleY = canvasH / rect.height;
  const mx = (e.clientX - rect.left) * scaleX;
  const my = (e.clientY - rect.top) * scaleY;

  // Dev free-place mode — place tower at click point for free
  if (devFreePlaceTower) {
    const tdef = TOWER_TYPES.find(t => t.id === devFreePlaceTower);
    if (tdef) {
      towers.push({
        x: mx, y: my,
        type: tdef,
        cooldown: tdef.medic ? 1800 : 0,
        target: null,
        angle: -Math.PI/2,
        disabled: 0,
        id: Math.random()
      });
      logMsg(`🛠 DEV: ${tdef.name} placed free`, 'good');
    }
    return;
  }

  // Check if clicking an existing tower
  for (let t of towers) {
    if (Math.hypot(t.x - mx, t.y - my) < 20) {
      // Chain gun: clicking toggles it ON/OFF
      if (t.type.chaingun) {
        t.toggled = !t.toggled;
        const state = t.toggled ? 'OFF' : 'ON';
        logMsg(`Chain Gun turned ${state}`, t.toggled ? 'warn' : 'good');
        return;
      }
      selectPlacedTower(t);
      return;
    }
  }

  // Place new tower
  if (selectedTowerType) {
    const tdef = TOWER_TYPES.find(t => t.id === selectedTowerType);
    if (!tdef) return;
    if (cash < tdef.cost) { logMsg('Not enough funds!', 'warn'); return; }
    if (tdef.radar || tdef.medic) {
      // Support towers can go anywhere (no road restriction)
    } else if (onPath(mx, my)) {
      logMsg('Cannot place on the road!', 'warn'); return;
    }

    towers.push({
      x: mx, y: my,
      type: tdef,
      cooldown: tdef.medic ? 1800 : 0,
      target: null,
      angle: -Math.PI/2,
      id: Math.random()
    });
    cash -= tdef.cost;
    logMsg(`${tdef.name} deployed`, 'good');
    updateHUD();
  } else {
    selectedTower = null;
    document.getElementById('sell-btn').classList.remove('visible');
  }
}

function launchApacheStrike(tx, ty) {
  // Apache flies in from left, strafes across target, exits right
  // Spawn a burst of projectiles along a horizontal line through target
  playSound(ac => {
    // Rotor sound + rocket burst
    const osc = ac.createOscillator(); osc.type = 'sawtooth';
    osc.frequency.setValueAtTime(28, ac.currentTime);
    const og = ac.createGain(); og.gain.setValueAtTime(0.2, ac.currentTime);
    og.gain.exponentialRampToValueAtTime(0.001, ac.currentTime + 1.5);
    osc.connect(og); og.connect(ac.destination);
    osc.start(); osc.stop(ac.currentTime + 1.5);
    // Rocket burst
    for (let i = 0; i < 8; i++) {
      setTimeout(() => {
        playSound(ac2 => {
          const buf = ac2.createBuffer(1, ac2.sampleRate*0.2, ac2.sampleRate);
          const d = buf.getChannelData(0);
          for (let j=0;j<d.length;j++){const t2=j/ac2.sampleRate;d[j]=(Math.random()*2-1)*Math.exp(-t2*20)*0.7+Math.sin(t2*80*Math.PI*2)*Math.exp(-t2*15)*0.3;}
          const src=ac2.createBufferSource();src.buffer=buf;
          const g=ac2.createGain();g.gain.setValueAtTime(0.45,ac2.currentTime);
          src.connect(g);g.connect(ac2.destination);src.start();
        });
      }, i * 120);
    }
  });

  // Strafe — 8 rockets hitting in a line around target
  for (let i = 0; i < 8; i++) {
    setTimeout(() => {
      if (!gameRunning) return;
      const sx = tx + (Math.random()-0.5)*80;
      const sy = ty + (Math.random()-0.5)*60;
      // Damage all enemies near each rocket impact
      for (let en of enemies) {
        const d = Math.hypot(en.x - sx, en.y - sy);
        if (d < 50) dealDamage(en, 80 * (1 - d/50));
      }
      spawnExplosion(sx, sy, '#ff6600');
    }, i * 150);
  }
}

function onPath(x, y) {
  for (let i = 0; i < path.length-1; i++) {
    let p1 = path[i], p2 = path[i+1];
    let dx = p2.x-p1.x, dy = p2.y-p1.y;
    let len = Math.sqrt(dx*dx+dy*dy);
    if (len === 0) continue;
    let t = ((x-p1.x)*dx + (y-p1.y)*dy) / (len*len);
    t = Math.max(0, Math.min(1, t));
    let cx = p1.x + t*dx, cy = p1.y + t*dy;
    if (Math.hypot(x-cx, y-cy) < 28) return true;
  }
  return false;
}

// ===================== WAVE START =====================
function startWave() {
  if (waveActive) return;
  wave++;
  waveActive = true;
  spawnQueue = buildWave(wave);
  spawnTimer = 0;
  document.getElementById('wave-btn').disabled = true;
  document.getElementById('wave-btn').textContent = `⚔ WAVE ${wave} IN PROGRESS...`;
  document.getElementById('wave-display').textContent = wave;
  logMsg(`=== WAVE ${wave} STARTED! ===`, 'warn');
}

// ===================== ENEMY SPAWNING & MOVEMENT =====================

function makePlaneWaypoints(w, h) {
  // Generate a snaking aerial path loosely following the map
  // but curving above the road with sweeping turns
  const pts = [];
  pts.push({x: -60, y: h * (0.1 + Math.random()*0.3)});
  pts.push({x: w*0.18 + (Math.random()-0.5)*80, y: h*(0.15 + Math.random()*0.25)});
  pts.push({x: w*0.32 + (Math.random()-0.5)*80, y: h*(0.55 + Math.random()*0.2)});
  pts.push({x: w*0.5  + (Math.random()-0.5)*80, y: h*(0.1  + Math.random()*0.25)});
  pts.push({x: w*0.65 + (Math.random()-0.5)*80, y: h*(0.55 + Math.random()*0.2)});
  pts.push({x: w*0.82 + (Math.random()-0.5)*60, y: h*(0.2  + Math.random()*0.2)});
  pts.push({x: w + 60, y: h*(0.2 + Math.random()*0.2)});
  return pts;
}

function spawnEnemy(type, hp, maxHp) {
  const base = ENEMY_TYPES[type];

  // No lane offset — perpendicular offset breaks at sharp 90° path corners
  const laneOffset = 0;

  // Flying enemies get their own curving aerial waypoints
  const isAerial = base.flying;
  const planeWpts = isAerial ? makePlaneWaypoints(canvasW, canvasH) : null;

  enemies.push({
    type,
    hp,
    maxHp,
    speed: base.speed * (1 + (wave-1)*0.04),
    reward: base.reward,
    bonusCash: base.bonusCash || 0,
    baseDamage: base.baseDamage || 1,
    color: base.color,
    size: base.size,
    flying: base.flying,
    armor: base.armor || 0,
    laneOffset: 0,
    pathIdx: 0,
    pathPct: 0,
    x: planeWpts ? planeWpts[0].x : path[0].x,
    y: planeWpts ? planeWpts[0].y : path[0].y,
    angle: 0,
    slow: 0,
    id: Math.random(),
    // special behaviors
    stealth: base.stealth || false,
    shootsBack: base.shootsBack || false,
    shootTimer: base.shootCooldown ? Math.random() * base.shootCooldown : 0,
    shootCooldown: base.shootCooldown || 0,
    shootRange: base.shootRange || 150,
    disableDuration: base.disableDuration || 0,
    spawnsInfantry: base.spawnsInfantry || 0,
    isTransport: base.isTransport || false,
    transportDropped: false,
    repairsNearby: base.repairsNearby || false,
    repairRate: base.repairRate || 0,
    repairRange: base.repairRange || 0,
    repairTimer: 0,
    lastAttackerId: null, // tracks which tower last hit this enemy
    // aerial path
    planeWpts,
    planeWptIdx: 0,
    planePct: 0,
  });
}

function updateEnemies(dt) {
  for (let e of enemies) {
    let spd = e.speed * (e.slow > 0 ? 0.45 : 1);
    e.slow = Math.max(0, e.slow - dt);

    if (e.planeWpts) {
      // ---- PLANE: follow its own curving waypoints ----
      const wpts = e.planeWpts;
      let moved = spd * dt * 0.08;
      while (moved > 0 && e.planeWptIdx < wpts.length - 1) {
        const p1 = wpts[e.planeWptIdx], p2 = wpts[e.planeWptIdx + 1];
        const dx = p2.x - p1.x, dy = p2.y - p1.y;
        const segLen = Math.sqrt(dx*dx + dy*dy);
        const remaining = segLen * (1 - e.planePct);
        if (moved <= remaining) {
          e.planePct += moved / segLen;
          e.x = p1.x + dx * e.planePct;
          e.y = p1.y + dy * e.planePct;
          // Smoothly rotate toward direction of travel
          const targetAngle = Math.atan2(dy, dx);
          let da = targetAngle - e.angle;
          while (da > Math.PI) da -= Math.PI*2;
          while (da < -Math.PI) da += Math.PI*2;
          e.angle += da * 0.08; // smooth turning
          moved = 0;
        } else {
          moved -= remaining;
          e.planeWptIdx++;
          e.planePct = 0;
        }
      }
      // Reached end of aerial path
      if (e.planeWptIdx >= wpts.length - 1) {
        e.hp = -1;
        lives--;
        logMsg(`Enemy reached base! -1 life`, 'warn');
        updateHUD();
        if (lives <= 0) { gameOver(); return; }
      }
    } else {
      // ---- GROUND / HELICOPTER: follow road path ----
      let moved = spd * dt * 0.06;
      while (moved > 0 && e.pathIdx < path.length-1) {
        let p1 = path[e.pathIdx], p2 = path[e.pathIdx+1];
        let dx = p2.x-p1.x, dy = p2.y-p1.y;
        let segLen = Math.sqrt(dx*dx+dy*dy);
        let remaining = segLen * (1 - e.pathPct);
        if (moved <= remaining) {
          e.pathPct += moved / segLen;
          // Apply lane offset perpendicular to road direction
          const angle = Math.atan2(dy, dx);
          const px = p1.x + dx*e.pathPct;
          const py = p1.y + dy*e.pathPct;
          const perp = angle + Math.PI/2;
          e.x = px + Math.cos(perp) * e.laneOffset;
          e.y = py + Math.sin(perp) * e.laneOffset;
          e.angle = angle;
          moved = 0;
        } else {
          moved -= remaining;
          e.pathIdx++;
          e.pathPct = 0;
          if (e.pathIdx < path.length-1) {
            e.x = path[e.pathIdx].x;
            e.y = path[e.pathIdx].y;
          }
        }
      }

      // Reached end
      if (e.pathIdx >= path.length-1) {
        e.hp = -1;
        const dmg = e.baseDamage || 1;
        lives -= dmg;
        if (dmg > 1) logMsg(`💣 Bomb Truck exploded! -${dmg} lives!`, 'warn');
        else logMsg(`Enemy reached base! -1 life`, 'warn');
        updateHUD();
        if (lives <= 0) { gameOver(); return; }
      }
    }

    // ---- SPECIAL BEHAVIORS ----

    // SPG / Gunship: shoots back at the tower that last attacked it
    if (e.shootsBack && towers.length > 0) {
      e.shootTimer -= dt;
      if (e.shootTimer <= 0) {
        // Find the attacker tower — only if it's within shoot range
        let target = null;
        if (e.lastAttackerId) {
          const attacker = towers.find(t => t.id === e.lastAttackerId);
          if (attacker && Math.hypot(attacker.x - e.x, attacker.y - e.y) <= e.shootRange) {
            target = attacker;
          }
        }
        // Fallback: nearest tower within range
        if (!target) {
          let nearDist = Infinity;
          for (let t of towers) {
            const d = Math.hypot(t.x - e.x, t.y - e.y);
            if (d <= e.shootRange && d < nearDist) { nearDist = d; target = t; }
          }
        }
        if (target) {
          e.shootTimer = e.shootCooldown;
          target.disabled = e.disableDuration;
          spawnExplosion(target.x, target.y, '#ff4400');
          logMsg(`⚠ ${e.type === 'gunship' ? 'Gunship' : 'SPG'} disabled a tower!`, 'warn');
          playSound(ac => {
            const buf = ac.createBuffer(1, ac.sampleRate*0.4, ac.sampleRate);
            const d = buf.getChannelData(0);
            for (let i=0;i<d.length;i++){const t2=i/ac.sampleRate;d[i]=(Math.random()*2-1)*Math.exp(-t2*8)*0.8+Math.sin(t2*50*Math.PI*2)*Math.exp(-t2*6)*0.5;}
            const src=ac.createBufferSource();src.buffer=buf;
            const g=ac.createGain();g.gain.setValueAtTime(0.6,ac.currentTime);
            src.connect(g);g.connect(ac.destination);src.start();
          });
        } else {
          e.shootTimer = e.shootCooldown; // no target in range, reset timer
        }
      }
    }

    // Engineer: repair nearby ground enemies
    if (e.repairsNearby) {
      e.repairTimer -= dt;
      if (e.repairTimer <= 0) {
        e.repairTimer = 30;
        for (let other of enemies) {
          if (other === e || other.flying) continue;
          if (Math.hypot(other.x - e.x, other.y - e.y) < e.repairRange) {
            other.hp = Math.min(other.maxHp, other.hp + e.repairRate);
          }
        }
      }
    }

    // Paratrooper transport: drop infantry at midpoint
    if (e.isTransport && !e.transportDropped) {
      const progress = (e.planeWptIdx || 0) + (e.planePct || 0);
      if (progress > 2) { // drop around the middle of the path
        e.transportDropped = true;
        e.hp = -1; // transport disappears after dropping
        // Spawn 4 infantry on the road near current position
        const dropX = e.x, dropY = e.y;
        for (let pi = 0; pi < 4; pi++) {
          const inf = {
            type: 'infantry',
            hp: 60, maxHp: 60,
            speed: ENEMY_TYPES.infantry.speed,
            reward: 15,
            bonusCash: 0, baseDamage: 1,
            color: ENEMY_TYPES.infantry.color,
            size: ENEMY_TYPES.infantry.size,
            flying: false, armor: 0, laneOffset: 0,
            pathIdx: Math.max(0, Math.floor(path.length * 0.3)),
            pathPct: Math.random() * 0.2,
            x: dropX + (Math.random()-0.5)*40,
            y: dropY + (Math.random()-0.5)*40,
            angle: 0, slow: 0,
            id: Math.random(),
            stealth: false, shootsBack: false, shootTimer: 0,
            shootCooldown: 0, disableDuration: 0,
            spawnsInfantry: 0, isTransport: false, transportDropped: false,
            repairsNearby: false, repairRate: 0, repairRange: 0, repairTimer: 0,
            planeWpts: null, planeWptIdx: 0, planePct: 0,
          };
          enemies.push(inf);
        }
        logMsg('☁ Paratroopers dropped!', 'warn');
      }
    }
  }
  enemies = enemies.filter(e => e.hp > 0);
}

// ===================== TOWER SHOOTING =====================
function updateTowers(dt) {
  // First pass: collect radar-boosted tower IDs
  const boostedTowers = new Set();
  for (let radar of towers.filter(t => t.type.radarBoost)) {
    for (let t of towers) {
      if (t !== radar && Math.hypot(t.x - radar.x, t.y - radar.y) < radar.type.range) {
        boostedTowers.add(t.id);
      }
    }
  }

  // Medic bunker: restore 1 life every 30s
  for (let t of towers.filter(t => t.type.medic)) {
    t.cooldown -= dt;
    if (t.cooldown <= 0) {
      t.cooldown = 1800; // ~30 seconds at 60fps
      if (lives < 8) {
        lives++;
        logMsg('Medic restored 1 life! ❤️', 'good');
        updateHUD();
      }
    }
  }

  // Apache: handled via click — no auto-fire
  // Radar: passive — no fire

  for (let t of towers) {
    if (t.type.radarBoost || t.type.medic) continue;
    if (t.type.targets.length === 0) continue;

    // Disabled tower (hit by SPG) — count down
    if (t.disabled > 0) { t.disabled -= dt; continue; }

    t.cooldown -= dt;
    if (t.cooldown > 0) continue;

    // Check if any radar tower covers this enemy's position (for stealth)
    const hasRadarNearEnemy = (enemy) =>
      towers.some(r => r.type.radarBoost && Math.hypot(r.x - enemy.x, r.y - enemy.y) < r.type.range * 1.5);

    const effectiveRange = t.type.range * (boostedTowers.has(t.id) ? 1.4 : 1);

    // Minefield: auto-detonate nearest enemy in range
    if (t.type.mine) {
      let closest = null, closestDist = Infinity;
      for (let e of enemies) {
        if (!t.type.targets.includes(e.type)) continue;
        let d = Math.hypot(e.x - t.x, e.y - t.y);
        if (d < effectiveRange && d < closestDist) { closestDist = d; closest = e; }
      }
      if (closest) {
        t.cooldown = t.type.fireRate;
        // Mine explosion — damage all in splash radius
        for (let e of enemies) {
          if (Math.hypot(e.x - closest.x, e.y - closest.y) < t.type.splash) {
            dealDamage(e, t.type.damage);
          }
        }
        spawnExplosion(closest.x, closest.y, '#ff8800');
        playSound(ac => {
          const buf = ac.createBuffer(1, ac.sampleRate*0.5, ac.sampleRate);
          const d = buf.getChannelData(0);
          for (let i=0;i<d.length;i++){const t2=i/ac.sampleRate;d[i]=(Math.random()*2-1)*Math.exp(-t2*10)*0.9+Math.sin(t2*55*Math.PI*2)*Math.exp(-t2*8)*0.5;}
          const src=ac.createBufferSource();src.buffer=buf;
          const g=ac.createGain();g.gain.setValueAtTime(0.7,ac.currentTime);
          src.connect(g);g.connect(ac.destination);src.start();
        });
      }
      continue;
    }

    // Find target (farthest along path within range)
    let tgt = null, farthest = -1;
    for (let e of enemies) {
      if (!t.type.targets.includes(e.type)) continue;
      // Stealth drones: 50% range penalty without radar coverage near the drone
      const rangeVsThis = (e.stealth && !hasRadarNearEnemy(e)) ? effectiveRange * 0.5 : effectiveRange;
      let dist = Math.hypot(e.x - t.x, e.y - t.y);
      if (dist <= rangeVsThis) {
        let prog = (e.planeWptIdx || 0) + (e.planePct || 0) + (e.pathIdx || 0) + (e.pathPct || 0);
        if (prog > farthest) { farthest = prog; tgt = e; }
      }
    }

    if (tgt) {
      t.cooldown = t.type.fireRate;
      t.angle = Math.atan2(tgt.y - t.y, tgt.x - t.x);

      // Napalm: drop a burn zone at target location
      if (t.type.napalm) {
        FIRE_SOUNDS['napalm']?.();
        napalmZones.push({
          x: tgt.x, y: tgt.y,
          radius: 38,
          damage: t.type.damage,
          life: 5000,   // 5 seconds
          maxLife: 5000,
          id: Math.random()
        });
        spawnExplosion(tgt.x, tgt.y, '#ff6600');

      // Cluster: fires one shell that splits into 8 bomblets
      } else if (t.type.cluster) {
        FIRE_SOUNDS['cluster']?.();
        // Main shell travels to target then splits
        const shellX = tgt.x, shellY = tgt.y;
        projectiles.push({
          x: t.x, y: t.y,
          tx: shellX, ty: shellY,
          targetId: tgt.id,
          towerId: t.id,
          speed: 5,
          damage: 0, // no direct damage — splits on arrival
          color: '#ffaa00',
          size: 8,
          splash: 0,
          slow: 0,
          towerType: 'cluster',
          cluster: true,
          clusterDamage: t.type.damage,
          id: Math.random()
        });

      // MLRS: fire salvo of 6 rockets with slight spread
      } else if (t.type.salvo) {
        FIRE_SOUNDS['rocket']?.();
        for (let s = 0; s < t.type.salvo; s++) {
          const delay = s * 80;
          setTimeout(() => {
            if (!gameRunning) return;
            const spread = (Math.random()-0.5) * 30;
            projectiles.push({
              x: t.x, y: t.y,
              tx: tgt.x + spread, ty: tgt.y + spread,
              targetId: tgt.id,
              speed: 7,
              damage: t.type.damage,
              color: t.type.projectileColor,
              size: t.type.projectileSize,
              splash: t.type.splash,
              slow: 0,
              towerType: t.type.id,
              id: Math.random()
            });
          }, delay);
        }
      } else {
        fireProjectile(t, tgt);
      }
    }
  }
}

function fireProjectile(tower, target) {
  FIRE_SOUNDS[tower.type.id]?.();
  projectiles.push({
    x: tower.x, y: tower.y,
    tx: target.x, ty: target.y,
    targetId: target.id,
    towerId: tower.id,  // track which tower fired this
    speed: 6,
    damage: tower.type.damage,
    color: tower.type.projectileColor,
    size: tower.type.projectileSize,
    splash: tower.type.splash || 0,
    slow: tower.type.slow || 0,
    towerType: tower.type.id,
    id: Math.random()
  });
}

function updateProjectiles(dt) {
  for (let p of projectiles) {
    // Track target
    let tgt = enemies.find(e => e.id === p.targetId);

    // If target is gone and no splash, discard the projectile
    if (!tgt && p.splash <= 0) { p.dead = true; continue; }

    if (tgt) { p.tx = tgt.x; p.ty = tgt.y; }

    let dx = p.tx - p.x, dy = p.ty - p.y;
    let dist = Math.sqrt(dx*dx+dy*dy);

    if (dist < p.speed * 1.5) {
      // Hit!
      if (p.cluster) {
        // Split into 8 bomblets in a spread
        for (let b = 0; b < 8; b++) {
          const angle = (b / 8) * Math.PI * 2;
          const spread = 35 + Math.random() * 25;
          const bx = p.tx + Math.cos(angle) * spread;
          const by = p.ty + Math.sin(angle) * spread;
          // Damage enemies near each bomblet
          for (let e of enemies) {
            if (Math.hypot(e.x - bx, e.y - by) < 28) {
              dealDamage(e, p.clusterDamage, p.towerId);
            }
          }
          spawnExplosion(bx, by, '#ffaa00');
        }
        spawnExplosion(p.tx, p.ty, '#ff6600');
        FIRE_SOUNDS['cluster']?.();
      } else if (p.splash > 0) {
        for (let e of enemies) {
          if (Math.hypot(e.x-p.tx, e.y-p.ty) < p.splash) {
            dealDamage(e, p.damage * (1 - Math.hypot(e.x-p.tx,e.y-p.ty)/p.splash * 0.5), p.towerId);
          }
        }
        spawnExplosion(p.tx, p.ty, '#ff8800');
      } else if (tgt) {
        dealDamage(tgt, p.damage, p.towerId);
        if (p.slow) tgt.slow = 60;
      }
      p.dead = true;
    } else {
      p.x += (dx/dist)*p.speed;
      p.y += (dy/dist)*p.speed;
    }
  }
  projectiles = projectiles.filter(p => !p.dead);
}

function dealDamage(enemy, dmg, attackerTowerId = null) {
  if (enemy.hp <= 0) return;
  // Record which tower last hit this enemy (for shoot-back)
  if (attackerTowerId) enemy.lastAttackerId = attackerTowerId;
  let actualDmg = devGodMode ? enemy.hp : Math.max(1, dmg - (enemy.armor || 0));
  enemy.hp -= actualDmg;
  if (enemy.hp <= 0) {
    const totalReward = enemy.reward + (enemy.bonusCash || 0);
    cash += totalReward;
    score += totalReward * 10;
    kills++;
    KILL_SOUNDS[enemy.type]?.();
    if (enemy.bonusCash) logMsg(`💰 Supply truck! +$${totalReward}`, 'good');
    floaties.push({x: enemy.x, y: enemy.y, text: '+$'+totalReward, life: 60, color: '#7fff7f'});
    spawnExplosion(enemy.x, enemy.y, enemy.color);
    // IFV: spawn infantry on death
    if (enemy.spawnsInfantry > 0) {
      for (let i = 0; i < enemy.spawnsInfantry; i++) {
        setTimeout(() => {
          if (!gameRunning) return;
          enemies.push({
            type: 'infantry', hp: 60, maxHp: 60,
            speed: ENEMY_TYPES.infantry.speed,
            reward: 10, bonusCash: 0, baseDamage: 1,
            color: ENEMY_TYPES.infantry.color,
            size: ENEMY_TYPES.infantry.size,
            flying: false, armor: 0, laneOffset: 0,
            pathIdx: enemy.pathIdx, pathPct: enemy.pathPct,
            x: enemy.x + (Math.random()-0.5)*20,
            y: enemy.y + (Math.random()-0.5)*20,
            angle: 0, slow: 0, id: Math.random(),
            stealth: false, shootsBack: false, shootTimer: 0,
            shootCooldown: 0, disableDuration: 0,
            spawnsInfantry: 0, isTransport: false, transportDropped: false,
            repairsNearby: false, repairRate: 0, repairRange: 0, repairTimer: 0,
            planeWpts: null, planeWptIdx: 0, planePct: 0,
          });
        }, i * 300);
      }
      logMsg('IFV destroyed — infantry bailing out!', 'warn');
    }
    updateHUD();
  }
}

// ===================== PARTICLES =====================
function spawnExplosion(x, y, color) {
  for (let i=0; i<8; i++) {
    let angle = Math.random()*Math.PI*2;
    let spd = 1+Math.random()*3;
    particles.push({x,y,vx:Math.cos(angle)*spd,vy:Math.sin(angle)*spd,life:25,color,size:2+Math.random()*3});
  }
}

function updateParticles(dt) {
  for (let p of particles) {
    p.x+=p.vx; p.y+=p.vy; p.life-=dt*0.05; p.vy+=0.08;
  }
  particles = particles.filter(p => p.life > 0);

  for (let f of floaties) {
    f.y -= 0.5; f.life -= dt*0.05;
  }
  floaties = floaties.filter(f => f.life > 0);
}

function updateNapalm(dt) {
  for (let z of napalmZones) {
    z.life -= dt;
    // Damage enemies in zone every 300ms
    if (!z.damageTimer) z.damageTimer = 0;
    z.damageTimer -= dt;
    if (z.damageTimer <= 0) {
      z.damageTimer = 300;
      for (let e of enemies) {
        if (!e.flying && Math.hypot(e.x - z.x, e.y - z.y) < z.radius) {
          dealDamage(e, z.damage);
        }
      }
      // Occasional fire particle
      for (let i = 0; i < 3; i++) {
        const angle = Math.random() * Math.PI * 2;
        const dist = Math.random() * z.radius;
        particles.push({
          x: z.x + Math.cos(angle)*dist,
          y: z.y + Math.sin(angle)*dist,
          vx: (Math.random()-0.5)*1.5,
          vy: -1 - Math.random()*2,
          life: 20 + Math.random()*15,
          color: Math.random() > 0.5 ? '#ff6600' : '#ffaa00',
          size: 3 + Math.random()*4
        });
      }
    }
  }
  napalmZones = napalmZones.filter(z => z.life > 0);
}


function updateSpawning(dt) {
  if (!waveActive) return;
  spawnTimer += dt;
  while (spawnQueue.length > 0 && spawnTimer >= spawnQueue[0].delay) {
    let e = spawnQueue.shift();
    spawnEnemy(e.type, e.hp, e.maxHp);
  }
  // Only declare wave complete when queue is empty AND no enemies remain
  // AND a 2 second grace period has passed (catches IFV/paratrooper spawns)
  if (spawnQueue.length === 0 && enemies.length === 0) {
    if (!waveEndTimer) waveEndTimer = 0;
    waveEndTimer += dt;
    if (waveEndTimer >= 2000) {
      waveEndTimer = 0;
      waveActive = false;
      logMsg(`Wave ${wave} complete! 🎖`, 'good');
      const wbtn = document.getElementById('wave-btn');
      wbtn.disabled = false;
      wbtn.textContent = `▶ START WAVE ${wave+1}`;
    }
  } else {
    waveEndTimer = 0;
    // Show live enemy count on the button so player knows wave is still going
    const remaining = spawnQueue.length + enemies.length;
    document.getElementById('wave-btn').textContent = `⚔ WAVE ${wave} — ${remaining} left`;
  }
}

// ===================== DRAW =====================
function draw() {
  ctx.clearRect(0, 0, canvasW, canvasH);

  // Background terrain
  drawTerrain();

  // Path
  drawPath();

  // Range indicator for selected tower
  if (selectedTower) {
    ctx.beginPath();
    ctx.arc(selectedTower.x, selectedTower.y, selectedTower.type.range, 0, Math.PI*2);
    ctx.strokeStyle = 'rgba(255,215,0,0.4)';
    ctx.lineWidth = 1.5;
    ctx.stroke();
    ctx.fillStyle = 'rgba(255,215,0,0.06)';
    ctx.fill();
  }

  // Preview range for placing
  if (selectedTowerType) {
    // Already shown on hover via mousemove
  }

  // Napalm burn zones
  for (let z of napalmZones) {
    const alpha = (z.life / z.maxLife) * 0.6;
    const pulse = Math.sin(Date.now() * 0.01) * 0.1;
    ctx.beginPath();
    ctx.arc(z.x, z.y, z.radius, 0, Math.PI*2);
    ctx.fillStyle = `rgba(255,80,0,${alpha + pulse})`;
    ctx.fill();
    ctx.strokeStyle = `rgba(255,160,0,${alpha + 0.2})`;
    ctx.lineWidth = 2;
    ctx.stroke();
  }

  // Towers
  for (let t of towers) drawTower(t);

  // Enemies
  for (let e of enemies) drawEnemy(e);

  // Projectiles
  for (let p of projectiles) drawProjectile(p);

  // Particles
  for (let p of particles) {
    ctx.globalAlpha = p.life/25;
    ctx.fillStyle = p.color;
    ctx.beginPath();
    ctx.arc(p.x, p.y, p.size, 0, Math.PI*2);
    ctx.fill();
  }
  ctx.globalAlpha = 1;

  // Floaties
  for (let f of floaties) {
    ctx.globalAlpha = f.life/60;
    ctx.fillStyle = f.color;
    ctx.font = 'bold 12px Share Tech Mono';
    ctx.fillText(f.text, f.x-14, f.y);
  }
  ctx.globalAlpha = 1;

  // Base indicator
  drawBase();
}

function drawTerrain() {
  // Grass gradient
  const grd = ctx.createLinearGradient(0,0,canvasW,canvasH);
  grd.addColorStop(0,'#2a4a12');
  grd.addColorStop(0.5,'#1e3a0e');
  grd.addColorStop(1,'#2a4a12');
  ctx.fillStyle = grd;
  ctx.fillRect(0,0,canvasW,canvasH);

  // Texture dots
  ctx.fillStyle = 'rgba(60,90,20,0.18)';
  for (let i=0; i<200; i++) {
    let x = (i*137)%canvasW, y = (i*211)%canvasH;
    ctx.fillRect(x,y,3,3);
  }
}

function drawPath() {
  // Road shadow
  ctx.beginPath();
  ctx.moveTo(path[0].x, path[0].y);
  for (let i=1;i<path.length;i++) ctx.lineTo(path[i].x,path[i].y);
  ctx.strokeStyle = 'rgba(0,0,0,0.4)';
  ctx.lineWidth = 38;
  ctx.lineJoin = 'round';
  ctx.stroke();

  // Road surface
  ctx.beginPath();
  ctx.moveTo(path[0].x, path[0].y);
  for (let i=1;i<path.length;i++) ctx.lineTo(path[i].x,path[i].y);
  ctx.strokeStyle = '#8a7a50';
  ctx.lineWidth = 32;
  ctx.lineJoin = 'round';
  ctx.stroke();

  // Road lines
  ctx.beginPath();
  ctx.moveTo(path[0].x, path[0].y);
  for (let i=1;i<path.length;i++) ctx.lineTo(path[i].x,path[i].y);
  ctx.strokeStyle = 'rgba(200,180,100,0.3)';
  ctx.lineWidth = 2;
  ctx.setLineDash([20,15]);
  ctx.stroke();
  ctx.setLineDash([]);

  // Arrow indicators
  for (let i=0; i<path.length-1; i++) {
    let p1=path[i], p2=path[i+1];
    let mx=(p1.x+p2.x)/2, my=(p1.y+p2.y)/2;
    let angle=Math.atan2(p2.y-p1.y, p2.x-p1.x);
    ctx.save();
    ctx.translate(mx,my);
    ctx.rotate(angle);
    ctx.fillStyle='rgba(200,180,100,0.25)';
    ctx.beginPath();
    ctx.moveTo(8,0); ctx.lineTo(-8,-6); ctx.lineTo(-8,6);
    ctx.closePath(); ctx.fill();
    ctx.restore();
  }
}

function drawTower(t) {
  const selected = selectedTower === t;
  const x = t.x, y = t.y;
  const angle = t.angle || -Math.PI/2;

  ctx.save();
  ctx.translate(x, y);

  // Drop shadow
  ctx.fillStyle = 'rgba(0,0,0,0.25)';
  ctx.beginPath();
  ctx.ellipse(3, 14, 13, 5, 0, 0, Math.PI*2);
  ctx.fill();

  // Sandbag base (all towers share this)
  drawSandbagsOn(ctx, 0, 6);

  // Draw tower-specific body + barrel
  switch(t.type.id) {
    case 'machinegun':   drawM2Browning(angle); break;
    case 'mortar':       drawMortarTower(angle); break;
    case 'rocket':       drawTOWMissile(angle); break;
    case 'emp':          drawEMPTower(angle); break;
    case 'antiair':      drawPatriotSAM(angle); break;
    case 'sniper':       drawSniper(angle); break;
    case 'flamethrower': drawFlamethrower(angle); break;
    case 'minefield':    drawMinefield(angle); break;
    case 'howitzer':     drawHowitzer(angle); break;
    case 'mlrs':         drawMLRS(angle); break;
    case 'radar':        drawRadar(angle); break;
    case 'medic':        drawMedic(angle); break;
    case 'napalm':       drawNapalmTower(angle); break;
    case 'chaingun':     drawChainGun(angle, t); break;
    case 'cluster':      drawClusterLauncher(angle); break;
  }

  // Selection ring
  if (selected) {
    ctx.strokeStyle = '#ffd700';
    ctx.lineWidth = 2;
    ctx.setLineDash([4,4]);
    ctx.beginPath();
    ctx.arc(0, 0, 18, 0, Math.PI*2);
    ctx.stroke();
    ctx.setLineDash([]);
  }

  ctx.restore();
}

function drawSandbagsOn(pctx, cx, cy) {
  const positions = [[-10,cy],[-4,cy],[2,cy],[8,cy],[-7,cy-4],[5,cy-4]];
  for (let [bx,by] of positions) {
    pctx.fillStyle = '#9a7a30';
    pctx.beginPath();
    pctx.ellipse(bx, by, 5, 3.5, 0, 0, Math.PI*2);
    pctx.fill();
    pctx.strokeStyle = '#6a5020';
    pctx.lineWidth = 0.8;
    pctx.stroke();
  }
}


function drawSniper(angle) {
  // Camo netting over platform
  ctx.fillStyle = '#3a5a1a';
  ctx.fillRect(-10, -4, 20, 10);
  ctx.fillStyle = '#2a4a10';
  for (let i=0;i<5;i++) ctx.fillRect(-8+i*4, -4, 2, 10);
  // Prone shooter silhouette
  ctx.fillStyle = '#4a6a22';
  ctx.beginPath(); ctx.ellipse(-3, -2, 8, 4, 0, 0, Math.PI*2); ctx.fill();
  // Head
  ctx.fillStyle = '#c8a070'; ctx.beginPath(); ctx.arc(-9, -4, 4, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = '#2a4a12'; ctx.beginPath(); ctx.arc(-9, -6, 4, Math.PI, 0); ctx.fill();
  // Sniper barrel — very long, rotates
  ctx.save(); ctx.rotate(angle);
  ctx.fillStyle = '#111'; ctx.fillRect(0, -1.5, 26, 3);
  ctx.fillStyle = '#333'; ctx.fillRect(-3, -3, 7, 6);
  // Scope
  ctx.fillStyle = '#444'; ctx.fillRect(6, -4, 8, 2);
  // Suppressor
  ctx.fillStyle = '#1a1a1a'; ctx.beginPath(); ctx.arc(26, 0, 2.5, 0, Math.PI*2); ctx.fill();
  ctx.restore();
}

function drawFlamethrower(angle) {
  // Tank on back
  ctx.fillStyle = '#8a3a1a';
  ctx.beginPath(); ctx.roundRect(-6, -8, 12, 14, 3); ctx.fill();
  ctx.fillStyle = '#cc4a1a';
  ctx.beginPath(); ctx.roundRect(-4, -6, 8, 10, 2); ctx.fill();
  // Soldier body
  ctx.fillStyle = '#4a6a22';
  ctx.beginPath(); ctx.ellipse(2, -1, 6, 7, 0, 0, Math.PI*2); ctx.fill();
  // Nozzle arm
  ctx.save(); ctx.rotate(angle);
  ctx.fillStyle = '#555'; ctx.fillRect(0, -2, 14, 4);
  ctx.fillStyle = '#333'; ctx.fillRect(12, -3, 4, 6);
  // Flame tip
  ctx.fillStyle = 'rgba(255,120,0,0.8)'; ctx.beginPath(); ctx.arc(17, 0, 4, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = 'rgba(255,200,0,0.6)'; ctx.beginPath(); ctx.arc(17, 0, 2, 0, Math.PI*2); ctx.fill();
  ctx.restore();
}

function drawMinefield(angle) {
  // Warning sign post
  ctx.fillStyle = '#8a7a30';
  ctx.fillRect(-1, -14, 2, 14);
  ctx.fillStyle = '#ffdd00';
  ctx.fillRect(-7, -16, 14, 8);
  ctx.fillStyle = '#cc0000';
  ctx.font = 'bold 7px monospace';
  ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
  ctx.fillText('!', 0, -12);
  // Mines on ground (3 visible bumps)
  for (let [mx2,my2] of [[-8,4],[0,2],[8,5]]) {
    ctx.fillStyle = '#2a2a1a';
    ctx.beginPath(); ctx.ellipse(mx2, my2, 6, 4, 0, 0, Math.PI*2); ctx.fill();
    ctx.fillStyle = '#444';
    ctx.beginPath(); ctx.arc(mx2, my2-1, 2, 0, Math.PI*2); ctx.fill();
  }
}

function drawHowitzer(angle) {
  // Heavy base platform
  ctx.fillStyle = '#3a2a0a';
  ctx.fillRect(-14, 2, 28, 10);
  // Wheels
  for (let wx of [-9, 9]) {
    ctx.fillStyle = '#1a1a0a'; ctx.beginPath(); ctx.arc(wx, 11, 5, 0, Math.PI*2); ctx.fill();
    ctx.fillStyle = '#333'; ctx.beginPath(); ctx.arc(wx, 11, 2.5, 0, Math.PI*2); ctx.fill();
  }
  // Shield plate
  ctx.fillStyle = '#4a3a1a';
  ctx.beginPath(); ctx.roundRect(-10, -8, 20, 14, 2); ctx.fill();
  // Barrel assembly — rotates, very thick
  ctx.save(); ctx.rotate(angle);
  ctx.fillStyle = '#2a2010'; ctx.fillRect(-2, -5, 28, 10);
  ctx.fillStyle = '#3a3020'; ctx.fillRect(-5, -6, 10, 12);
  // Muzzle brake (wide)
  ctx.fillStyle = '#1a1510'; ctx.fillRect(24, -7, 6, 14);
  ctx.fillStyle = '#0a0a05';
  for (let hy of [-4,0,4]) ctx.fillRect(26, hy-1, 4, 2);
  ctx.restore();
}

function drawMLRS(angle) {
  // Vehicle cab
  ctx.fillStyle = '#4a5a2a';
  ctx.beginPath(); ctx.roundRect(-12, -2, 10, 10, 2); ctx.fill();
  ctx.fillStyle = 'rgba(150,210,255,0.5)';
  ctx.fillRect(-11, -1, 8, 5);
  // Chassis
  ctx.fillStyle = '#3a4a1a'; ctx.fillRect(-12, 6, 24, 6);
  // Wheels
  for (let wx of [-8, 0, 8]) {
    ctx.fillStyle = '#1a1a0a'; ctx.beginPath(); ctx.arc(wx, 12, 4, 0, Math.PI*2); ctx.fill();
    ctx.fillStyle = '#444'; ctx.beginPath(); ctx.arc(wx, 12, 2, 0, Math.PI*2); ctx.fill();
  }
  // Rocket pod array — rotates
  ctx.save(); ctx.rotate(angle);
  ctx.fillStyle = '#2a3a1a'; ctx.beginPath(); ctx.roundRect(-2, -8, 22, 16, 3); ctx.fill();
  // 6 rocket tubes
  const tubeColors = ['#1a2a0a','#2a3a1a'];
  for (let row=0;row<2;row++) for (let col=0;col<3;col++) {
    ctx.fillStyle = tubeColors[(row+col)%2];
    ctx.beginPath(); ctx.ellipse(2+col*6, -5+row*10, 2.5, 2.5, 0, 0, Math.PI*2); ctx.fill();
    ctx.fillStyle = '#0a0a0a'; ctx.beginPath(); ctx.arc(2+col*6, -5+row*10, 1.2, 0, Math.PI*2); ctx.fill();
  }
  ctx.restore();
}

function drawRadar(angle) {
  // Tower mast
  ctx.fillStyle = '#2a4a6a';
  ctx.fillRect(-3, -18, 6, 22);
  // Crossbrace
  ctx.strokeStyle = '#1a3a5a'; ctx.lineWidth = 2;
  ctx.beginPath(); ctx.moveTo(-3,-4); ctx.lineTo(3,-12); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(3,-4); ctx.lineTo(-3,-12); ctx.stroke();
  // Rotating dish
  ctx.save(); ctx.rotate(angle * 0.3 + Date.now()*0.002);
  ctx.fillStyle = '#3a6a9a';
  ctx.beginPath(); ctx.ellipse(0, -18, 12, 4, 0, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = '#2a5a8a';
  ctx.beginPath(); ctx.ellipse(0, -18, 12, 4, 0, 0, Math.PI); ctx.fill();
  // Dish struts
  ctx.strokeStyle = '#4a8abb'; ctx.lineWidth = 1.5;
  for (let a=0;a<3;a++) {
    ctx.beginPath(); ctx.moveTo(0,-18);
    ctx.lineTo(Math.cos(a*Math.PI/1.5)*10, -18+Math.sin(a*Math.PI/1.5)*3); ctx.stroke();
  }
  ctx.restore();
  // Pulse ring (visual effect)
  ctx.strokeStyle = 'rgba(68,220,255,0.3)';
  ctx.lineWidth = 1;
  const pulse = (Date.now() % 1500) / 1500;
  ctx.beginPath(); ctx.arc(0, -10, pulse * 30, 0, Math.PI*2); ctx.stroke();
}

function drawMedic(angle) {
  // Bunker base
  ctx.fillStyle = '#3a3a2a';
  ctx.beginPath(); ctx.roundRect(-12, 0, 24, 12, 3); ctx.fill();
  // Sandbag front
  for (let bx of [-8,-2,4]) {
    ctx.fillStyle = '#8a7030'; ctx.beginPath(); ctx.ellipse(bx, 1, 5, 3.5, 0, 0, Math.PI*2); ctx.fill();
  }
  // Bunker wall
  ctx.fillStyle = '#5a5a4a';
  ctx.beginPath(); ctx.roundRect(-10, -10, 20, 14, 4); ctx.fill();
  // Red cross
  ctx.fillStyle = '#cc2222';
  ctx.fillRect(-2, -9, 4, 12);
  ctx.fillRect(-6, -5, 12, 4);
  // White cross highlight
  ctx.fillStyle = '#ee4444';
  ctx.fillRect(-1, -8, 2, 10);
  ctx.fillRect(-5, -4, 10, 2);
  // Window slit
  ctx.fillStyle = '#222'; ctx.fillRect(-8, -4, 6, 3);
}

function drawNapalmTower(angle) {
  // Fuel tank base — red danger cylinder
  ctx.fillStyle = '#8a1a0a';
  ctx.beginPath(); ctx.roundRect(-8, -2, 16, 14, 3); ctx.fill();
  ctx.fillStyle = '#aa2a10';
  ctx.beginPath(); ctx.roundRect(-6, 0, 12, 10, 2); ctx.fill();
  // Warning stripes
  ctx.fillStyle = '#ffaa00';
  ctx.fillRect(-6, 2, 12, 2);
  ctx.fillRect(-6, 6, 12, 2);
  // Operator
  ctx.fillStyle = '#4a6a22';
  ctx.beginPath(); ctx.ellipse(3, -4, 6, 7, 0, 0, Math.PI*2); ctx.fill();
  // Launch arm — rotates
  ctx.save(); ctx.rotate(angle);
  ctx.fillStyle = '#cc3300'; ctx.fillRect(0, -3, 16, 6);
  ctx.fillStyle = '#ff4400'; ctx.fillRect(14, -4, 4, 8);
  // Flame nozzle
  ctx.fillStyle = 'rgba(255,100,0,0.8)'; ctx.beginPath(); ctx.arc(20, 0, 5, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = 'rgba(255,200,0,0.7)'; ctx.beginPath(); ctx.arc(20, 0, 2.5, 0, Math.PI*2); ctx.fill();
  ctx.restore();
  // Fire flicker
  const flicker = Math.sin(Date.now()*0.02)*0.3+0.7;
  ctx.fillStyle = `rgba(255,80,0,${flicker*0.4})`;
  ctx.beginPath(); ctx.arc(0, -6, 8, 0, Math.PI*2); ctx.fill();
}

function drawChainGun(angle, tower) {
  // Dim when toggled off
  if (tower && tower.toggled) ctx.globalAlpha = 0.5;
  // Mount base
  ctx.fillStyle = '#3a5a20'; ctx.fillRect(-10, 0, 20, 10);
  ctx.fillStyle = '#2a4a15'; ctx.fillRect(-8, -4, 16, 8);
  // Operator
  ctx.fillStyle = '#4a6a22';
  ctx.beginPath(); ctx.ellipse(-4, -6, 5, 6, 0, 0, Math.PI*2); ctx.fill();
  // Helmet
  ctx.fillStyle = '#2a4a12'; ctx.beginPath(); ctx.arc(-4, -10, 5, Math.PI, 0); ctx.fill();
  // Rotating barrel assembly
  ctx.save(); ctx.rotate(angle);
  // Barrel housing
  ctx.fillStyle = '#333'; ctx.fillRect(-3, -5, 10, 10);
  // 6 rotating barrels (Vulcan style)
  const spin = tower ? (Date.now() * (tower.overheated || tower.toggled ? 0 : 0.015)) : 0;
  for (let b = 0; b < 6; b++) {
    const ba = spin + b * Math.PI / 3;
    const bx = Math.cos(ba) * 3.5;
    const by = Math.sin(ba) * 3.5;
    ctx.fillStyle = '#555';
    ctx.beginPath(); ctx.arc(bx + 12, by, 1.8, 0, Math.PI*2); ctx.fill();
    ctx.fillStyle = '#777'; ctx.fillRect(3 + bx*0.5, by - 0.8, 10, 1.6);
  }
  // Muzzle
  ctx.fillStyle = '#111'; ctx.fillRect(18, -3, 4, 6);
  ctx.restore();
  // ON/OFF indicator dot
  if (tower) {
    ctx.globalAlpha = 1;
    ctx.fillStyle = tower.toggled ? '#ff3333' : '#33ff33';
    ctx.beginPath(); ctx.arc(8, -12, 3, 0, Math.PI*2); ctx.fill();
    ctx.font = 'bold 6px monospace'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
    ctx.fillStyle = '#000';
    ctx.fillText(tower.toggled ? 'OFF' : 'ON', 8, -12);
  }
  ctx.globalAlpha = 1;
}

function drawClusterLauncher(angle) {
  // Launcher body on tripod
  ctx.strokeStyle = '#4a4a2a'; ctx.lineWidth = 2.5;
  ctx.beginPath(); ctx.moveTo(-9, 8); ctx.lineTo(0, -2); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(9, 8); ctx.lineTo(0, -2); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(0, 10); ctx.lineTo(0, -2); ctx.stroke();
  // Operator
  ctx.fillStyle = '#4a6a22';
  ctx.beginPath(); ctx.ellipse(-6, -2, 5, 6, -0.2, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = '#2a4a12'; ctx.beginPath(); ctx.arc(-6, -7, 4, Math.PI, 0); ctx.fill();
  // Barrel — rotates
  ctx.save(); ctx.rotate(angle);
  // Main tube
  ctx.fillStyle = '#5a3a6a'; ctx.beginPath(); ctx.roundRect(-2, -5, 22, 10, 3); ctx.fill();
  // Shell in chamber
  ctx.fillStyle = '#ffaa00'; ctx.beginPath(); ctx.ellipse(8, 0, 5, 3.5, 0, 0, Math.PI*2); ctx.fill();
  // Muzzle
  ctx.fillStyle = '#2a1a3a'; ctx.fillRect(18, -6, 5, 12);
  // Bomblet indicators (8 dots on shell)
  ctx.fillStyle = '#ff4400';
  for (let i=0;i<8;i++) {
    const a = i * Math.PI/4;
    ctx.beginPath(); ctx.arc(8 + Math.cos(a)*2.5, Math.sin(a)*2.5, 1, 0, Math.PI*2); ctx.fill();
  }
  ctx.restore();
}

function drawM2Browning(angle) {
  ctx.fillStyle = '#4a6a2a';
  ctx.beginPath();
  ctx.ellipse(0, -2, 6, 7, 0, 0, Math.PI*2);
  ctx.fill();
  // Helmet
  ctx.fillStyle = '#3a5a1a';
  ctx.beginPath();
  ctx.arc(0, -8, 5, Math.PI, 0);
  ctx.fill();
  ctx.fillStyle = '#4a6a2a';
  ctx.fillRect(-5, -9, 10, 3);
  // Gun barrel — rotates
  ctx.save();
  ctx.rotate(angle);
  ctx.fillStyle = '#222';
  ctx.fillRect(0, -2, 18, 3);
  ctx.fillStyle = '#333';
  ctx.fillRect(-4, -4, 8, 6);
  // Muzzle flash slot
  ctx.fillStyle = '#111';
  ctx.fillRect(16, -1, 4, 1);
  ctx.restore();
}

function drawMortarTower(angle) {
  // Soldier crouching body
  ctx.fillStyle = '#4a6a2a';
  ctx.beginPath();
  ctx.ellipse(0, -1, 7, 6, 0, 0, Math.PI*2);
  ctx.fill();
  // Helmet
  ctx.fillStyle = '#3a5a1a';
  ctx.beginPath();
  ctx.arc(0, -7, 5, Math.PI, 0);
  ctx.fill();
  ctx.fillStyle = '#4a6a2a';
  ctx.fillRect(-5, -8, 10, 3);
  // Mortar tube — fixed at steep angle (mortars fire up)
  ctx.save();
  ctx.rotate(-Math.PI * 0.65);
  ctx.fillStyle = '#555';
  ctx.fillRect(-2, -18, 5, 18);
  // Tube end
  ctx.fillStyle = '#333';
  ctx.beginPath();
  ctx.arc(0.5, -18, 3.5, 0, Math.PI*2);
  ctx.fill();
  // Base plate
  ctx.fillStyle = '#444';
  ctx.fillRect(-6, -1, 12, 3);
  ctx.restore();
}

function drawTOWMissile(angle) {
  // Tripod base
  ctx.strokeStyle = '#555';
  ctx.lineWidth = 2;
  ctx.beginPath(); ctx.moveTo(-8, 4); ctx.lineTo(0, -2); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(8, 4); ctx.lineTo(0, -2); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(0, 6); ctx.lineTo(0, -2); ctx.stroke();
  // Operator body
  ctx.fillStyle = '#4a6a2a';
  ctx.beginPath();
  ctx.ellipse(-5, -3, 5, 6, -0.3, 0, Math.PI*2);
  ctx.fill();
  // Launcher tube — rotates
  ctx.save();
  ctx.rotate(angle);
  // Launch tube body
  ctx.fillStyle = '#5a4a30';
  ctx.fillRect(-2, -4, 20, 7);
  // Missile inside
  ctx.fillStyle = '#cc3300';
  ctx.fillRect(4, -2, 12, 4);
  // Nose cone
  ctx.fillStyle = '#ff4400';
  ctx.beginPath();
  ctx.moveTo(16, -2); ctx.lineTo(20, 0); ctx.lineTo(16, 2);
  ctx.closePath(); ctx.fill();
  // Wire guidance reel
  ctx.fillStyle = '#888';
  ctx.beginPath();
  ctx.arc(-2, 0, 4, 0, Math.PI*2);
  ctx.fill();
  ctx.restore();
}

function drawEMPTower(angle) {
  // Generator box base
  ctx.fillStyle = '#2a4a7a';
  ctx.fillRect(-9, -4, 18, 12);
  ctx.fillStyle = '#3a5a9a';
  ctx.fillRect(-7, -2, 14, 8);
  // Vents
  ctx.fillStyle = '#1a3a6a';
  for (let i=0; i<3; i++) {
    ctx.fillRect(-5 + i*4, 0, 2, 5);
  }
  // Dish / emitter — rotates
  ctx.save();
  ctx.rotate(angle);
  // Arm
  ctx.fillStyle = '#446688';
  ctx.fillRect(0, -2, 12, 4);
  // Dish head
  ctx.fillStyle = '#5588bb';
  ctx.beginPath();
  ctx.arc(12, 0, 5, -Math.PI*0.6, Math.PI*0.6);
  ctx.lineTo(12, 0);
  ctx.closePath();
  ctx.fill();
  // Energy glow
  ctx.fillStyle = 'rgba(68,180,255,0.5)';
  ctx.beginPath();
  ctx.arc(14, 0, 3, 0, Math.PI*2);
  ctx.fill();
  ctx.restore();
}

function drawPatriotSAM(angle) {
  // Launch vehicle base
  ctx.fillStyle = '#4a4a3a';
  ctx.fillRect(-12, 2, 24, 8);
  // Wheels
  ctx.fillStyle = '#222';
  for (let wx of [-8, 0, 8]) {
    ctx.beginPath();
    ctx.arc(wx, 10, 4, 0, Math.PI*2);
    ctx.fill();
    ctx.fillStyle = '#444';
    ctx.beginPath();
    ctx.arc(wx, 10, 2, 0, Math.PI*2);
    ctx.fill();
    ctx.fillStyle = '#222';
  }
  // Launcher arm — rotates
  ctx.save();
  ctx.rotate(angle);
  // Arm
  ctx.fillStyle = '#5a5a3a';
  ctx.fillRect(-2, -3, 14, 6);
  // Missile tubes (x2)
  ctx.fillStyle = '#3a3a2a';
  ctx.fillRect(4, -6, 12, 5);
  ctx.fillRect(4, 1, 12, 5);
  // Missile tips
  ctx.fillStyle = '#cc2200';
  ctx.beginPath();
  ctx.moveTo(16, -6); ctx.lineTo(20, -3.5); ctx.lineTo(16, -1); ctx.closePath(); ctx.fill();
  ctx.beginPath();
  ctx.moveTo(16, 1); ctx.lineTo(20, 3.5); ctx.lineTo(16, 6); ctx.closePath(); ctx.fill();
  ctx.restore();
}

function drawEnemy(e) {
  const r = e.size;
  let drawY = e.flying ? e.y - 14 : e.y;
  const slowTint = e.slow > 0;

  // Flying shadow on ground
  if (e.flying) {
    ctx.fillStyle = 'rgba(0,0,0,0.15)';
    ctx.beginPath();
    ctx.ellipse(e.x, e.y + 2, r * 1.2, 4, 0, 0, Math.PI*2);
    ctx.fill();
  }

  ctx.save();
  ctx.translate(e.x, drawY);

  // All flying units rotate to face direction of travel
  if (e.flying && e.angle !== undefined) {
    ctx.rotate(e.angle);
  }

  if (slowTint) { ctx.filter = 'hue-rotate(180deg) brightness(1.3)'; }

  switch(e.type) {
    case 'infantry':    drawInfantrySoldier(r); break;
    case 'jeep':        drawJeep(r); break;
    case 'tank':        drawTank(r); break;
    case 'apc':         drawAPC(r); break;
    case 'plane':       drawPlane(r); break;
    case 'helicopter':  drawHelicopter(r); break;
    case 'motorcycle':  drawMotorcycle(r); break;
    case 'supplytruck': drawSupplyTruck(r); break;
    case 'humvee':      drawHumvee(r); break;
    case 'heavytank':   drawHeavyTank(r); break;
    case 'spg':         drawSPG(r, e); break;
    case 'ifv':         drawIFV(r); break;
    case 'gunship':     drawGunship(r); break;
    case 'drone':       drawDrone(r, e); break;
    case 'paratrooper': drawParatrooper(r); break;
    case 'engineer':    drawEngineer(r); break;
    case 'bombtruck':   drawBombTruck(r); break;
  }

  ctx.filter = 'none';
  ctx.restore();

  // HP bar (always above unit, unrotated)
  let hpPct = e.hp / e.maxHp;
  let barW = r * 2.4;
  ctx.fillStyle = '#111';
  ctx.fillRect(e.x - barW/2, drawY - r - 8, barW, 4);
  ctx.fillStyle = hpPct > 0.5 ? '#44ff44' : hpPct > 0.25 ? '#ffaa00' : '#ff3333';
  ctx.fillRect(e.x - barW/2, drawY - r - 8, barW * hpPct, 4);
}

function drawInfantrySoldier(r) {
  const s = r * 1.4; // scale up
  // Back leg
  ctx.fillStyle = '#2a4a12';
  ctx.fillRect(s*0.1, s*0.3, s*0.28, s*0.65);
  // Boot back
  ctx.fillStyle = '#1a1208';
  ctx.fillRect(s*0.05, s*0.88, s*0.38, s*0.22);
  // Front leg
  ctx.fillStyle = '#3a5a1a';
  ctx.fillRect(-s*0.28, s*0.3, s*0.28, s*0.65);
  // Boot front
  ctx.fillStyle = '#1a1208';
  ctx.fillRect(-s*0.38, s*0.88, s*0.42, s*0.22);
  // Torso / flak jacket
  ctx.fillStyle = '#4a6a22';
  ctx.beginPath();
  ctx.roundRect(-s*0.38, -s*0.22, s*0.76, s*0.58, s*0.08);
  ctx.fill();
  // Camo patches on jacket
  ctx.fillStyle = '#2a4010';
  ctx.fillRect(-s*0.28, -s*0.1, s*0.18, s*0.14);
  ctx.fillRect(s*0.04, s*0.05, s*0.14, s*0.16);
  ctx.fillRect(-s*0.1, s*0.18, s*0.2, s*0.1);
  // Belt/webbing
  ctx.fillStyle = '#8a7a30';
  ctx.fillRect(-s*0.38, s*0.2, s*0.76, s*0.1);
  // Left arm extended holding rifle
  ctx.fillStyle = '#4a6a22';
  ctx.fillRect(-s*0.6, -s*0.18, s*0.28, s*0.22);
  // Right arm
  ctx.fillStyle = '#4a6a22';
  ctx.fillRect(s*0.32, -s*0.12, s*0.22, s*0.22);
  // Rifle body
  ctx.fillStyle = '#1a1008';
  ctx.fillRect(-s*0.72, -s*0.34, s*0.16, s*0.72);
  // Rifle stock
  ctx.fillStyle = '#3a2010';
  ctx.fillRect(-s*0.66, s*0.22, s*0.22, s*0.16);
  // Rifle barrel
  ctx.fillStyle = '#0a0808';
  ctx.fillRect(-s*0.76, -s*0.46, s*0.1, s*0.18);
  // Neck
  ctx.fillStyle = '#c8a070';
  ctx.fillRect(-s*0.1, -s*0.34, s*0.2, s*0.16);
  // Head
  ctx.fillStyle = '#c8a070';
  ctx.beginPath();
  ctx.ellipse(0, -s*0.52, s*0.26, s*0.24, 0, 0, Math.PI*2);
  ctx.fill();
  // Helmet dome
  ctx.fillStyle = '#3a5a1a';
  ctx.beginPath();
  ctx.ellipse(0, -s*0.58, s*0.3, s*0.22, 0, 0, Math.PI*2);
  ctx.fill();
  // Helmet brim
  ctx.fillStyle = '#2a4a12';
  ctx.fillRect(-s*0.32, -s*0.5, s*0.64, s*0.1);
  // Eyes
  ctx.fillStyle = '#1a1208';
  ctx.fillRect(-s*0.1, -s*0.54, s*0.07, s*0.06);
  ctx.fillRect(s*0.04, -s*0.54, s*0.07, s*0.06);
}

function drawJeep(r) {
  const s = r * 1.1;
  // Drop shadow
  ctx.fillStyle = 'rgba(0,0,0,0.2)';
  ctx.beginPath();
  ctx.ellipse(0, s*0.85, s*1.1, s*0.2, 0, 0, Math.PI*2);
  ctx.fill();
  // Chassis / undercarriage
  ctx.fillStyle = '#2a3a10';
  ctx.fillRect(-s*1.0, s*0.3, s*2.0, s*0.4);
  // Rear wheel
  ctx.fillStyle = '#111';
  ctx.beginPath(); ctx.arc(-s*0.68, s*0.62, s*0.32, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = '#444';
  ctx.beginPath(); ctx.arc(-s*0.68, s*0.62, s*0.18, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = '#777';
  ctx.beginPath(); ctx.arc(-s*0.68, s*0.62, s*0.07, 0, Math.PI*2); ctx.fill();
  // Front wheel
  ctx.fillStyle = '#111';
  ctx.beginPath(); ctx.arc(s*0.68, s*0.62, s*0.32, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = '#444';
  ctx.beginPath(); ctx.arc(s*0.68, s*0.62, s*0.18, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = '#777';
  ctx.beginPath(); ctx.arc(s*0.68, s*0.62, s*0.07, 0, Math.PI*2); ctx.fill();
  // Main body
  ctx.fillStyle = '#5a7a28';
  ctx.beginPath();
  ctx.roundRect(-s*0.92, -s*0.28, s*1.84, s*0.72, s*0.08);
  ctx.fill();
  // Hood (front)
  ctx.fillStyle = '#4a6a20';
  ctx.beginPath();
  ctx.roundRect(s*0.5, -s*0.18, s*0.48, s*0.55, s*0.06);
  ctx.fill();
  // Hood vents
  ctx.fillStyle = '#3a5a18';
  for (let i=0; i<3; i++) {
    ctx.fillRect(s*0.56 + i*s*0.1, -s*0.06, s*0.06, s*0.28);
  }
  // Windshield frame
  ctx.fillStyle = '#2a3a10';
  ctx.fillRect(-s*0.12, -s*0.28, s*0.68, s*0.08);
  ctx.fillRect(-s*0.12, s*0.18, s*0.62, s*0.07);
  // Windshield glass
  ctx.fillStyle = 'rgba(150,220,255,0.55)';
  ctx.fillRect(-s*0.1, -s*0.22, s*0.58, s*0.42);
  // Windshield reflection
  ctx.fillStyle = 'rgba(255,255,255,0.2)';
  ctx.fillRect(-s*0.06, -s*0.18, s*0.16, s*0.3);
  // Driver head
  ctx.fillStyle = '#c8a070';
  ctx.beginPath(); ctx.arc(s*0.08, -s*0.32, s*0.18, 0, Math.PI*2); ctx.fill();
  // Helmet
  ctx.fillStyle = '#3a5a1a';
  ctx.beginPath(); ctx.arc(s*0.08, -s*0.36, s*0.2, Math.PI, 0); ctx.fill();
  // Spare tire on back
  ctx.fillStyle = '#1a1a10';
  ctx.beginPath(); ctx.arc(-s*0.82, s*0.08, s*0.24, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = '#333';
  ctx.beginPath(); ctx.arc(-s*0.82, s*0.08, s*0.14, 0, Math.PI*2); ctx.fill();
  // Headlights
  ctx.fillStyle = '#ffffaa';
  ctx.beginPath(); ctx.ellipse(s*0.94, -s*0.05, s*0.07, s*0.1, 0, 0, Math.PI*2); ctx.fill();
  // Exhaust pipe
  ctx.fillStyle = '#555';
  ctx.fillRect(-s*0.95, s*0.1, s*0.08, s*0.28);
}

function drawTank(r) {
  const s = r * 1.2;
  // Ground shadow
  ctx.fillStyle = 'rgba(0,0,0,0.2)';
  ctx.beginPath();
  ctx.ellipse(0, s*0.82, s*1.1, s*0.18, 0, 0, Math.PI*2);
  ctx.fill();
  // Track housings (outer)
  ctx.fillStyle = '#1a1a0e';
  ctx.beginPath();
  ctx.roundRect(-s*1.05, -s*0.22, s*2.1, s*0.95, s*0.18);
  ctx.fill();
  // Track links
  ctx.fillStyle = '#2e2e1e';
  for (let i = -s*0.95; i < s*0.95; i += s*0.14) {
    ctx.fillRect(i, -s*0.2, s*0.1, s*0.9);
  }
  // Track top highlight
  ctx.fillStyle = '#3a3a28';
  ctx.fillRect(-s*1.0, -s*0.22, s*2.0, s*0.12);
  // Hull body
  ctx.fillStyle = '#4e6e2a';
  ctx.beginPath();
  ctx.roundRect(-s*0.88, -s*0.44, s*1.76, s*0.72, s*0.06);
  ctx.fill();
  // Hull armor panel lines
  ctx.strokeStyle = '#3a5a1a';
  ctx.lineWidth = 1.5;
  ctx.beginPath(); ctx.moveTo(-s*0.88, -s*0.1); ctx.lineTo(s*0.88, -s*0.1); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(-s*0.4, -s*0.44); ctx.lineTo(-s*0.4, s*0.28); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(s*0.4, -s*0.44); ctx.lineTo(s*0.4, s*0.28); ctx.stroke();
  // Turret base ring
  ctx.fillStyle = '#2e4e18';
  ctx.beginPath();
  ctx.ellipse(s*0.08, -s*0.42, s*0.62, s*0.2, 0, 0, Math.PI*2);
  ctx.fill();
  // Turret body
  ctx.fillStyle = '#3e5e20';
  ctx.beginPath();
  ctx.roundRect(-s*0.42, -s*0.78, s*1.0, s*0.42, s*0.1);
  ctx.fill();
  // Turret side slope
  ctx.fillStyle = '#4a6a28';
  ctx.beginPath();
  ctx.moveTo(-s*0.42, -s*0.38);
  ctx.lineTo(-s*0.55, -s*0.62);
  ctx.lineTo(s*0.58, -s*0.62);
  ctx.lineTo(s*0.58, -s*0.38);
  ctx.closePath(); ctx.fill();
  // Main barrel
  ctx.fillStyle = '#1e2e0e';
  ctx.beginPath();
  ctx.roundRect(s*0.38, -s*0.64, s*0.95, s*0.18, s*0.04);
  ctx.fill();
  // Barrel muzzle brake
  ctx.fillStyle = '#111';
  ctx.fillRect(s*1.26, -s*0.68, s*0.1, s*0.26);
  // Gunner hatch
  ctx.fillStyle = '#2a4a18';
  ctx.beginPath();
  ctx.ellipse(s*0.08, -s*0.7, s*0.2, s*0.14, 0, 0, Math.PI*2);
  ctx.fill();
  ctx.fillStyle = '#1a3a10';
  ctx.beginPath();
  ctx.arc(s*0.08, -s*0.7, s*0.08, 0, Math.PI*2);
  ctx.fill();
  // Commander's MG on top
  ctx.fillStyle = '#222';
  ctx.fillRect(s*0.2, -s*0.82, s*0.08, s*0.2);
  ctx.fillRect(s*0.18, -s*0.84, s*0.28, s*0.08);
  // Antenna
  ctx.strokeStyle = '#2a2a1a';
  ctx.lineWidth = 1.5;
  ctx.beginPath(); ctx.moveTo(-s*0.3, -s*0.76); ctx.lineTo(-s*0.22, -s*1.18); ctx.stroke();
}

function drawAPC(r) {
  const s = r * 1.1;
  // Shadow
  ctx.fillStyle = 'rgba(0,0,0,0.18)';
  ctx.beginPath();
  ctx.ellipse(0, s*0.8, s*1.0, s*0.16, 0, 0, Math.PI*2);
  ctx.fill();
  // Track housings
  ctx.fillStyle = '#1e1e12';
  ctx.beginPath();
  ctx.roundRect(-s*0.98, -s*0.1, s*1.96, s*0.82, s*0.15);
  ctx.fill();
  // Track links
  ctx.fillStyle = '#2a2a18';
  for (let i = -s*0.9; i < s*0.9; i += s*0.13) {
    ctx.fillRect(i, -s*0.08, s*0.09, s*0.78);
  }
  // Main armored hull
  ctx.fillStyle = '#5a7a30';
  ctx.beginPath();
  ctx.roundRect(-s*0.88, -s*0.68, s*1.76, s*0.82, s*0.1);
  ctx.fill();
  // Sloped front armor
  ctx.fillStyle = '#4a6a24';
  ctx.beginPath();
  ctx.moveTo(s*0.62, -s*0.68);
  ctx.lineTo(s*0.88, -s*0.38);
  ctx.lineTo(s*0.88, s*0.14);
  ctx.lineTo(s*0.62, s*0.14);
  ctx.closePath(); ctx.fill();
  // Armor panel seams
  ctx.strokeStyle = '#3a5a20';
  ctx.lineWidth = 1.5;
  ctx.beginPath(); ctx.moveTo(-s*0.88, -s*0.18); ctx.lineTo(s*0.62, -s*0.18); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(-s*0.2, -s*0.68); ctx.lineTo(-s*0.2, s*0.14); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(s*0.22, -s*0.68); ctx.lineTo(s*0.22, -s*0.18); ctx.stroke();
  // Viewport windows (armored slits)
  ctx.fillStyle = 'rgba(100,180,220,0.5)';
  ctx.fillRect(s*0.64, -s*0.52, s*0.18, s*0.12);
  ctx.fillRect(s*0.64, -s*0.32, s*0.18, s*0.12);
  // Roof hatch
  ctx.fillStyle = '#3a5a1a';
  ctx.beginPath();
  ctx.ellipse(-s*0.18, -s*0.72, s*0.28, s*0.18, 0, 0, Math.PI*2);
  ctx.fill();
  // Pintle-mounted MG
  ctx.fillStyle = '#1a1a10';
  ctx.fillRect(-s*0.1, -s*0.96, s*0.1, s*0.32);
  ctx.fillRect(-s*0.22, -s*0.98, s*0.34, s*0.1);
  // Rear door
  ctx.fillStyle = '#3a5a20';
  ctx.fillRect(-s*0.88, -s*0.55, s*0.12, s*0.6);
  ctx.strokeStyle = '#2a4a18';
  ctx.lineWidth = 1;
  ctx.beginPath(); ctx.moveTo(-s*0.88, -s*0.24); ctx.lineTo(-s*0.76, -s*0.24); ctx.stroke();
  // Headlights
  ctx.fillStyle = '#ffff88';
  ctx.beginPath(); ctx.ellipse(s*0.86, -s*0.52, s*0.06, s*0.1, 0, 0, Math.PI*2); ctx.fill();
  ctx.beginPath(); ctx.ellipse(s*0.86, -s*0.28, s*0.06, s*0.1, 0, 0, Math.PI*2); ctx.fill();
  // Antenna
  ctx.strokeStyle = '#2a2a10';
  ctx.lineWidth = 1.5;
  ctx.beginPath(); ctx.moveTo(-s*0.55, -s*0.68); ctx.lineTo(-s*0.44, -s*1.14); ctx.stroke();
}

function drawPlane(r) {
  const s = r * 1.3;
  // Engine exhaust glow
  ctx.fillStyle = 'rgba(255,140,0,0.25)';
  ctx.beginPath();
  ctx.ellipse(-s*1.32, 0, s*0.22, s*0.16, 0, 0, Math.PI*2);
  ctx.fill();
  // Swept wings (back)
  ctx.fillStyle = '#3a5a8a';
  ctx.beginPath();
  ctx.moveTo(s*0.1, s*0.06);
  ctx.lineTo(-s*0.55, s*1.1);
  ctx.lineTo(-s*0.8, s*1.08);
  ctx.lineTo(-s*0.3, s*0.06);
  ctx.closePath(); ctx.fill();
  ctx.beginPath();
  ctx.moveTo(s*0.1, -s*0.06);
  ctx.lineTo(-s*0.55, -s*1.1);
  ctx.lineTo(-s*0.8, -s*1.08);
  ctx.lineTo(-s*0.3, -s*0.06);
  ctx.closePath(); ctx.fill();
  // Wing markings
  ctx.fillStyle = '#2a4a7a';
  ctx.beginPath();
  ctx.moveTo(-s*0.2, s*0.3);
  ctx.lineTo(-s*0.55, s*0.85);
  ctx.lineTo(-s*0.62, s*0.82);
  ctx.lineTo(-s*0.28, s*0.3);
  ctx.closePath(); ctx.fill();
  ctx.beginPath();
  ctx.moveTo(-s*0.2, -s*0.3);
  ctx.lineTo(-s*0.55, -s*0.85);
  ctx.lineTo(-s*0.62, -s*0.82);
  ctx.lineTo(-s*0.28, -s*0.3);
  ctx.closePath(); ctx.fill();
  // Fuselage
  ctx.fillStyle = '#4a6aaa';
  ctx.beginPath();
  ctx.ellipse(0, 0, s*1.28, s*0.32, 0, 0, Math.PI*2);
  ctx.fill();
  // Fuselage belly shade
  ctx.fillStyle = '#3a5a9a';
  ctx.beginPath();
  ctx.ellipse(0, s*0.1, s*1.1, s*0.2, 0, 0, Math.PI*2);
  ctx.fill();
  // Tail fins (vertical)
  ctx.fillStyle = '#2a4a8a';
  ctx.beginPath();
  ctx.moveTo(-s*0.95, 0);
  ctx.lineTo(-s*1.28, -s*0.55);
  ctx.lineTo(-s*1.22, -s*0.55);
  ctx.lineTo(-s*0.72, 0);
  ctx.closePath(); ctx.fill();
  // Horizontal stabilizers
  ctx.fillStyle = '#3a5a9a';
  ctx.beginPath();
  ctx.moveTo(-s*0.88, s*0.04);
  ctx.lineTo(-s*1.15, s*0.42);
  ctx.lineTo(-s*1.22, s*0.4);
  ctx.lineTo(-s*0.95, s*0.04);
  ctx.closePath(); ctx.fill();
  ctx.beginPath();
  ctx.moveTo(-s*0.88, -s*0.04);
  ctx.lineTo(-s*1.15, -s*0.42);
  ctx.lineTo(-s*1.22, -s*0.4);
  ctx.lineTo(-s*0.95, -s*0.04);
  ctx.closePath(); ctx.fill();
  // Engine intakes
  ctx.fillStyle = '#1a2a5a';
  ctx.beginPath();
  ctx.ellipse(-s*0.18, s*0.28, s*0.12, s*0.08, 0.4, 0, Math.PI*2); ctx.fill();
  ctx.beginPath();
  ctx.ellipse(-s*0.18, -s*0.28, s*0.12, s*0.08, -0.4, 0, Math.PI*2); ctx.fill();
  // Exhaust nozzle
  ctx.fillStyle = '#1a1a2a';
  ctx.beginPath();
  ctx.ellipse(-s*1.28, 0, s*0.14, s*0.18, 0, 0, Math.PI*2); ctx.fill();
  // Nose cone
  ctx.fillStyle = '#5a7abb';
  ctx.beginPath();
  ctx.moveTo(s*1.28, 0);
  ctx.bezierCurveTo(s*1.1, -s*0.12, s*0.82, -s*0.2, s*0.72, -s*0.2);
  ctx.lineTo(s*0.72, s*0.2);
  ctx.bezierCurveTo(s*0.82, s*0.2, s*1.1, s*0.12, s*1.28, 0);
  ctx.fill();
  // Cockpit canopy
  ctx.fillStyle = 'rgba(140,210,255,0.7)';
  ctx.beginPath();
  ctx.ellipse(s*0.52, -s*0.06, s*0.3, s*0.2, -0.2, 0, Math.PI*2);
  ctx.fill();
  // Canopy frame
  ctx.strokeStyle = '#2a4a8a';
  ctx.lineWidth = 1.5;
  ctx.beginPath();
  ctx.ellipse(s*0.52, -s*0.06, s*0.3, s*0.2, -0.2, 0, Math.PI*2);
  ctx.stroke();
  // Bombs/payload under wings
  ctx.fillStyle = '#3a3a2a';
  ctx.beginPath(); ctx.ellipse(-s*0.38, s*0.5, s*0.18, s*0.07, 0.3, 0, Math.PI*2); ctx.fill();
  ctx.beginPath(); ctx.ellipse(-s*0.38, -s*0.5, s*0.18, s*0.07, -0.3, 0, Math.PI*2); ctx.fill();
}

function drawHelicopter(r) {
  const s = r * 1.2;
  const time = Date.now() * 0.01;
  // Ground shadow (moving)
  ctx.fillStyle = 'rgba(0,0,0,0.12)';
  ctx.beginPath();
  ctx.ellipse(0, s*0.78, s*1.2, s*0.18, 0, 0, Math.PI*2);
  ctx.fill();
  // Tail boom
  ctx.fillStyle = '#1e3a52';
  ctx.beginPath();
  ctx.moveTo(-s*0.32, -s*0.06);
  ctx.lineTo(-s*1.2, -s*0.06);
  ctx.lineTo(-s*1.22, s*0.1);
  ctx.lineTo(-s*0.32, s*0.12);
  ctx.closePath(); ctx.fill();
  // Tail fin (vertical)
  ctx.fillStyle = '#162e42';
  ctx.beginPath();
  ctx.moveTo(-s*1.08, -s*0.06);
  ctx.lineTo(-s*1.28, -s*0.42);
  ctx.lineTo(-s*1.18, -s*0.42);
  ctx.lineTo(-s*0.98, -s*0.06);
  ctx.closePath(); ctx.fill();
  // Tail rotor (spinning)
  ctx.strokeStyle = '#8888aa';
  ctx.lineWidth = 2.5;
  for (let i=0; i<2; i++) {
    const a = time*2.5 + i * Math.PI;
    ctx.beginPath();
    ctx.moveTo(-s*1.2, Math.cos(a)*s*0.06);
    ctx.lineTo(-s*1.2, Math.cos(a)*s*0.38);
    ctx.stroke();
  }
  // Skid struts
  ctx.strokeStyle = '#1a2e3e';
  ctx.lineWidth = 2;
  ctx.beginPath(); ctx.moveTo(-s*0.38, s*0.35); ctx.lineTo(-s*0.52, s*0.65); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(s*0.28, s*0.35); ctx.lineTo(s*0.38, s*0.65); ctx.stroke();
  // Skid bars
  ctx.strokeStyle = '#1e3448';
  ctx.lineWidth = 3;
  ctx.beginPath(); ctx.moveTo(-s*0.62, s*0.65); ctx.lineTo(s*0.52, s*0.65); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(-s*0.58, s*0.72); ctx.lineTo(s*0.48, s*0.72); ctx.stroke();
  // Main body
  ctx.fillStyle = '#2e4e6e';
  ctx.beginPath();
  ctx.ellipse(0, 0, s*0.82, s*0.5, 0, 0, Math.PI*2);
  ctx.fill();
  // Body belly plate
  ctx.fillStyle = '#243e58';
  ctx.beginPath();
  ctx.ellipse(s*0.05, s*0.2, s*0.68, s*0.28, 0, 0, Math.PI*2);
  ctx.fill();
  // Engine housing (top)
  ctx.fillStyle = '#1e3448';
  ctx.beginPath();
  ctx.roundRect(-s*0.28, -s*0.52, s*0.56, s*0.22, s*0.06);
  ctx.fill();
  // Main rotor mast
  ctx.fillStyle = '#333';
  ctx.fillRect(-s*0.06, -s*0.62, s*0.12, s*0.14);
  // Main rotor blades (3 blades, animated)
  ctx.strokeStyle = '#666688';
  ctx.lineWidth = 3.5;
  for (let i=0; i<3; i++) {
    const a = time + (i * Math.PI * 2 / 3);
    const bx = Math.cos(a) * s*1.45;
    const by = Math.sin(a) * s*0.28;
    ctx.beginPath();
    ctx.moveTo(Math.cos(a)*s*0.06, Math.sin(a)*s*0.02);
    ctx.quadraticCurveTo(bx*0.5, by*0.5 - s*0.04, bx, by);
    ctx.stroke();
  }
  // Rotor hub
  ctx.fillStyle = '#444';
  ctx.beginPath(); ctx.arc(0, -s*0.58, s*0.07, 0, Math.PI*2); ctx.fill();
  // Cockpit bubble
  ctx.fillStyle = 'rgba(140,210,255,0.65)';
  ctx.beginPath();
  ctx.ellipse(s*0.36, -s*0.06, s*0.42, s*0.36, -0.15, 0, Math.PI*2);
  ctx.fill();
  // Cockpit frame
  ctx.strokeStyle = '#1a2e42';
  ctx.lineWidth = 1.5;
  ctx.beginPath();
  ctx.ellipse(s*0.36, -s*0.06, s*0.42, s*0.36, -0.15, 0, Math.PI*2);
  ctx.stroke();
  // Cockpit divider bar
  ctx.beginPath(); ctx.moveTo(s*0.36, -s*0.4); ctx.lineTo(s*0.28, s*0.28); ctx.stroke();
  // Pilot silhouette
  ctx.fillStyle = '#1a2830';
  ctx.beginPath(); ctx.ellipse(s*0.24, -s*0.1, s*0.12, s*0.14, 0, 0, Math.PI*2); ctx.fill();
  ctx.beginPath(); ctx.ellipse(s*0.22, -s*0.22, s*0.1, s*0.1, 0, 0, Math.PI*2); ctx.fill();
  // Weapon pylons
  ctx.fillStyle = '#1e3040';
  ctx.fillRect(-s*0.45, s*0.2, s*0.18, s*0.1);
  ctx.fillRect(s*0.04, s*0.2, s*0.18, s*0.1);
  // Rocket pods
  ctx.fillStyle = '#2a3a2a';
  ctx.beginPath(); ctx.ellipse(-s*0.36, s*0.3, s*0.2, s*0.08, 0, 0, Math.PI*2); ctx.fill();
  ctx.beginPath(); ctx.ellipse(s*0.13, s*0.3, s*0.2, s*0.08, 0, 0, Math.PI*2); ctx.fill();
}

function drawMotorcycle(r) {
  const s = r * 1.0;
  // Wheels
  ctx.fillStyle = '#111';
  ctx.beginPath(); ctx.arc(-s*0.6, s*0.4, s*0.35, 0, Math.PI*2); ctx.fill();
  ctx.beginPath(); ctx.arc(s*0.6, s*0.4, s*0.35, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = '#444';
  ctx.beginPath(); ctx.arc(-s*0.6, s*0.4, s*0.18, 0, Math.PI*2); ctx.fill();
  ctx.beginPath(); ctx.arc(s*0.6, s*0.4, s*0.18, 0, Math.PI*2); ctx.fill();
  // Frame
  ctx.strokeStyle = '#6a5a20'; ctx.lineWidth = 3;
  ctx.beginPath(); ctx.moveTo(-s*0.6, s*0.1); ctx.lineTo(0, -s*0.2); ctx.lineTo(s*0.6, s*0.1); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(0, -s*0.2); ctx.lineTo(0, s*0.1); ctx.stroke();
  // Handlebar
  ctx.strokeStyle = '#888'; ctx.lineWidth = 2;
  ctx.beginPath(); ctx.moveTo(s*0.4, -s*0.3); ctx.lineTo(s*0.7, -s*0.2); ctx.stroke();
  // Rider body
  ctx.fillStyle = '#4a6a22';
  ctx.beginPath(); ctx.ellipse(s*0.05, -s*0.35, s*0.22, s*0.28, -0.3, 0, Math.PI*2); ctx.fill();
  // Helmet
  ctx.fillStyle = '#1a1a1a';
  ctx.beginPath(); ctx.arc(s*0.15, -s*0.58, s*0.2, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = 'rgba(100,180,255,0.6)';
  ctx.beginPath(); ctx.arc(s*0.22, -s*0.55, s*0.1, 0, Math.PI*2); ctx.fill();
}

function drawSupplyTruck(r) {
  const s = r * 1.1;
  // Wheels (6 — big truck)
  ctx.fillStyle = '#111';
  for (let wx of [-s*0.75, 0, s*0.75]) {
    ctx.beginPath(); ctx.arc(wx, s*0.6, s*0.3, 0, Math.PI*2); ctx.fill();
    ctx.fillStyle = '#555'; ctx.beginPath(); ctx.arc(wx, s*0.6, s*0.14, 0, Math.PI*2); ctx.fill();
    ctx.fillStyle = '#111';
  }
  // Chassis
  ctx.fillStyle = '#5a4a20'; ctx.fillRect(-s*0.95, s*0.1, s*1.9, s*0.55);
  // Cargo box (big)
  ctx.fillStyle = '#7a6a30';
  ctx.beginPath(); ctx.roundRect(-s*0.88, -s*0.7, s*1.76, s*0.88, s*0.06); ctx.fill();
  // Cargo markings
  ctx.strokeStyle = '#4a3a10'; ctx.lineWidth = 2;
  ctx.beginPath(); ctx.moveTo(-s*0.88, -s*0.1); ctx.lineTo(s*0.88, -s*0.1); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(0, -s*0.7); ctx.lineTo(0, -s*0.1); ctx.stroke();
  // Supply star
  ctx.fillStyle = '#ffdd00'; ctx.font = `bold ${s*0.4}px monospace`;
  ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
  ctx.fillText('$', 0, -s*0.38);
  // Cab
  ctx.fillStyle = '#6a5a28'; ctx.beginPath(); ctx.roundRect(s*0.5, -s*0.42, s*0.42, s*0.55, s*0.06); ctx.fill();
  ctx.fillStyle = 'rgba(150,210,255,0.5)'; ctx.fillRect(s*0.54, -s*0.36, s*0.34, s*0.28);
}

function drawHumvee(r) {
  const s = r * 1.0;
  // Wheels
  ctx.fillStyle = '#111';
  for (let [wx,wy] of [[-s*0.7,s*0.45],[s*0.7,s*0.45],[-s*0.7,-s*0.05],[s*0.7,-s*0.05]]) {
    ctx.beginPath(); ctx.arc(wx, wy, s*0.3, 0, Math.PI*2); ctx.fill();
    ctx.fillStyle = '#555'; ctx.beginPath(); ctx.arc(wx, wy, s*0.14, 0, Math.PI*2); ctx.fill();
    ctx.fillStyle = '#111';
  }
  // Body — boxy military
  ctx.fillStyle = '#6a7a28';
  ctx.beginPath(); ctx.roundRect(-s*0.82, -s*0.45, s*1.64, s*0.72, s*0.1); ctx.fill();
  // Roof turret ring
  ctx.fillStyle = '#3a4a18'; ctx.beginPath(); ctx.arc(0, -s*0.4, s*0.22, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = '#222'; ctx.fillRect(-s*0.04, -s*0.72, s*0.08, s*0.35);
  // Windows
  ctx.fillStyle = 'rgba(140,200,255,0.55)';
  ctx.fillRect(-s*0.55, -s*0.38, s*0.42, s*0.26);
  ctx.fillRect(s*0.12, -s*0.38, s*0.42, s*0.26);
  // Grill
  ctx.fillStyle = '#2a3a10';
  for (let i=0;i<3;i++) ctx.fillRect(s*0.65, -s*0.28+i*s*0.14, s*0.2, s*0.08);
}

function drawHeavyTank(r) {
  const s = r * 1.3;
  // Extra wide tracks
  ctx.fillStyle = '#111';
  ctx.beginPath(); ctx.roundRect(-s*1.1, -s*0.28, s*2.2, s*1.05, s*0.2); ctx.fill();
  ctx.fillStyle = '#2a2a18';
  for (let i = -s*1.0; i < s*1.0; i += s*0.15) ctx.fillRect(i, -s*0.26, s*0.11, s*1.0);
  ctx.fillStyle = '#3a3a28'; ctx.fillRect(-s*1.05, -s*0.28, s*2.1, s*0.14);
  // Heavy hull with sloped armor
  ctx.fillStyle = '#3a5a20';
  ctx.beginPath();
  ctx.moveTo(-s*0.95, s*0.72); ctx.lineTo(-s*0.95, -s*0.55);
  ctx.lineTo(-s*0.7, -s*0.72); ctx.lineTo(s*0.7, -s*0.72);
  ctx.lineTo(s*0.95, -s*0.55); ctx.lineTo(s*0.95, s*0.72);
  ctx.closePath(); ctx.fill();
  // Armour panels
  ctx.strokeStyle = '#2a4a18'; ctx.lineWidth = 2;
  ctx.beginPath(); ctx.moveTo(-s*0.95, -s*0.1); ctx.lineTo(s*0.95, -s*0.1); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(-s*0.4, -s*0.72); ctx.lineTo(-s*0.4, s*0.72); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(s*0.4, -s*0.72); ctx.lineTo(s*0.4, s*0.72); ctx.stroke();
  // Massive turret
  ctx.fillStyle = '#2a4a18';
  ctx.beginPath(); ctx.roundRect(-s*0.55, -s*1.05, s*1.3, s*0.55, s*0.1); ctx.fill();
  ctx.beginPath(); ctx.ellipse(s*0.12, -s*0.55, s*0.65, s*0.22, 0, 0, Math.PI*2); ctx.fill();
  // Very long barrel
  ctx.fillStyle = '#111'; ctx.fillRect(s*0.38, -s*0.82, s*1.2, s*0.22);
  ctx.fillStyle = '#0a0a0a'; ctx.fillRect(s*1.5, -s*0.9, s*0.12, s*0.38);
  // Commanders hatch
  ctx.fillStyle = '#1a3a10'; ctx.beginPath(); ctx.ellipse(s*0.05, -s*0.92, s*0.22, s*0.15, 0, 0, Math.PI*2); ctx.fill();
  // Extra armor skirts
  ctx.fillStyle = '#2a4a18'; ctx.fillRect(-s*1.1, s*0.42, s*2.2, s*0.2);
}

function drawSPG(r, e) {
  const s = r * 1.1;
  // Tracks
  ctx.fillStyle = '#1a1a0e'; ctx.beginPath(); ctx.roundRect(-s, -s*0.2, s*2, s*0.95, s*0.18); ctx.fill();
  ctx.fillStyle = '#2a2a18';
  for (let i=-s*0.92;i<s*0.92;i+=s*0.14) ctx.fillRect(i, -s*0.18, s*0.1, s*0.9);
  // Hull
  ctx.fillStyle = '#5a5a28'; ctx.beginPath(); ctx.roundRect(-s*0.88, -s*0.55, s*1.76, s*0.65, s*0.08); ctx.fill();
  // Gun shield
  ctx.fillStyle = '#4a4a20';
  ctx.beginPath(); ctx.moveTo(-s*0.3, -s*0.55); ctx.lineTo(-s*0.5, -s*0.9); ctx.lineTo(s*0.5, -s*0.9); ctx.lineTo(s*0.3, -s*0.55); ctx.closePath(); ctx.fill();
  // Big howitzer barrel
  ctx.fillStyle = '#222'; ctx.fillRect(s*0.1, -s*0.78, s*1.1, s*0.22);
  ctx.fillStyle = '#111'; ctx.fillRect(s*1.14, -s*0.84, s*0.1, s*0.34);
  // Disabled flash
  if (e.disabled > 0) {
    ctx.fillStyle = `rgba(255,80,0,${0.4 + Math.sin(Date.now()*0.02)*0.4})`;
    ctx.beginPath(); ctx.arc(0, -s*0.3, s*0.5, 0, Math.PI*2); ctx.fill();
  }
}

function drawIFV(r) {
  const s = r * 1.05;
  // Tracks
  ctx.fillStyle = '#1e1e12'; ctx.beginPath(); ctx.roundRect(-s*0.95, -s*0.12, s*1.9, s*0.85, s*0.15); ctx.fill();
  ctx.fillStyle = '#2a2a18';
  for (let i=-s*0.88;i<s*0.88;i+=s*0.13) ctx.fillRect(i, -s*0.1, s*0.09, s*0.82);
  // Hull
  ctx.fillStyle = '#4a6a30'; ctx.beginPath(); ctx.roundRect(-s*0.85, -s*0.65, s*1.7, s*0.78, s*0.1); ctx.fill();
  // Turret with 30mm cannon
  ctx.fillStyle = '#3a5a20'; ctx.beginPath(); ctx.ellipse(s*0.1, -s*0.6, s*0.45, s*0.3, 0, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = '#1a1a0a'; ctx.fillRect(s*0.25, -s*0.7, s*0.85, s*0.14);
  // Side hatch doors
  ctx.fillStyle = '#3a5a20'; ctx.fillRect(-s*0.85, -s*0.45, s*0.1, s*0.55);
  ctx.strokeStyle = '#2a4a18'; ctx.lineWidth = 1;
  ctx.beginPath(); ctx.moveTo(-s*0.85, -s*0.18); ctx.lineTo(-s*0.75, -s*0.18); ctx.stroke();
  // Viewport slits
  ctx.fillStyle = 'rgba(100,180,255,0.45)';
  ctx.fillRect(s*0.52, -s*0.52, s*0.26, s*0.12);
  // Troop silhouettes visible through hatch
  ctx.fillStyle = '#2a3a18';
  for (let i=0;i<3;i++) {
    ctx.beginPath(); ctx.arc(-s*0.35 + i*s*0.3, -s*0.72, s*0.1, 0, Math.PI*2); ctx.fill();
  }
}

function drawGunship(r) {
  const s = r * 1.2;
  const time = Date.now() * 0.012;
  // Shadow
  ctx.fillStyle = 'rgba(0,0,0,0.12)'; ctx.beginPath(); ctx.ellipse(0, s*0.65, s*1.1, s*0.15, 0, 0, Math.PI*2); ctx.fill();
  // Tail boom — heavier than chopper
  ctx.fillStyle = '#111e30';
  ctx.beginPath(); ctx.moveTo(-s*0.28, -s*0.08); ctx.lineTo(-s*1.25, -s*0.08); ctx.lineTo(-s*1.28, s*0.12); ctx.lineTo(-s*0.28, s*0.14); ctx.closePath(); ctx.fill();
  // Tail fin
  ctx.fillStyle = '#0e1828'; ctx.beginPath(); ctx.moveTo(-s*1.1, -s*0.08); ctx.lineTo(-s*1.32, -s*0.48); ctx.lineTo(-s*1.18, -s*0.48); ctx.lineTo(-s*0.95, -s*0.08); ctx.closePath(); ctx.fill();
  // Tail rotor
  ctx.strokeStyle = '#667799'; ctx.lineWidth = 2.5;
  for (let i=0;i<2;i++) { const a=time*2.8+i*Math.PI; ctx.beginPath(); ctx.moveTo(-s*1.25, Math.cos(a)*s*0.04); ctx.lineTo(-s*1.25, Math.cos(a)*s*0.42); ctx.stroke(); }
  // Skids — wider/sturdier
  ctx.strokeStyle = '#111e30'; ctx.lineWidth = 3;
  ctx.beginPath(); ctx.moveTo(-s*0.4, s*0.38); ctx.lineTo(-s*0.55, s*0.62); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(s*0.3, s*0.38); ctx.lineTo(s*0.4, s*0.62); ctx.stroke();
  ctx.lineWidth = 4;
  ctx.beginPath(); ctx.moveTo(-s*0.65, s*0.62); ctx.lineTo(s*0.55, s*0.62); ctx.stroke();
  // Main body — more angular
  ctx.fillStyle = '#1a2e4a';
  ctx.beginPath(); ctx.moveTo(s*0.85, 0); ctx.lineTo(s*0.6, -s*0.5); ctx.lineTo(-s*0.32, -s*0.52); ctx.lineTo(-s*0.32, s*0.48); ctx.lineTo(s*0.6, s*0.46); ctx.closePath(); ctx.fill();
  // Chin gun turret
  ctx.fillStyle = '#0a1820'; ctx.beginPath(); ctx.ellipse(s*0.72, s*0.18, s*0.22, s*0.18, 0, 0, Math.PI*2); ctx.fill();
  ctx.fillStyle = '#111'; ctx.fillRect(s*0.88, s*0.12, s*0.3, s*0.1);
  // Rocket pods (bigger)
  ctx.fillStyle = '#162840';
  ctx.beginPath(); ctx.ellipse(-s*0.08, s*0.36, s*0.25, s*0.1, 0, 0, Math.PI*2); ctx.fill();
  ctx.beginPath(); ctx.ellipse(-s*0.08, -s*0.36, s*0.25, s*0.1, 0, 0, Math.PI*2); ctx.fill();
  // Cockpit
  ctx.fillStyle = 'rgba(140,210,255,0.6)'; ctx.beginPath(); ctx.ellipse(s*0.42, -s*0.08, s*0.38, s*0.32, -0.1, 0, Math.PI*2); ctx.fill();
  ctx.strokeStyle = '#0e1a2a'; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.ellipse(s*0.42, -s*0.08, s*0.38, s*0.32, -0.1, 0, Math.PI*2); ctx.stroke();
  // Rotor mast
  ctx.fillStyle = '#333'; ctx.fillRect(-s*0.06, -s*0.62, s*0.12, s*0.14);
  // Main rotors
  ctx.strokeStyle = '#556688'; ctx.lineWidth = 4;
  for (let i=0;i<3;i++) { const a=time+i*Math.PI*2/3; ctx.beginPath(); ctx.moveTo(Math.cos(a)*s*0.06, Math.sin(a)*s*0.02); ctx.quadraticCurveTo(Math.cos(a)*s*0.7, Math.sin(a)*s*0.14-s*0.04, Math.cos(a)*s*1.55, Math.sin(a)*s*0.32); ctx.stroke(); }
  ctx.fillStyle = '#555'; ctx.beginPath(); ctx.arc(0, -s*0.58, s*0.08, 0, Math.PI*2); ctx.fill();
}

function drawDrone(r, e) {
  const s = r * 1.0;
  const stealth = e.stealth;
  ctx.globalAlpha = stealth ? 0.4 : 1.0; // half-visible stealth effect
  // Fuselage — angular, dark
  ctx.fillStyle = '#2a2a2a';
  ctx.beginPath(); ctx.ellipse(0, 0, s*1.1, s*0.25, 0, 0, Math.PI*2); ctx.fill();
  // Swept wings — very angular
  ctx.fillStyle = '#1a1a1a';
  ctx.beginPath(); ctx.moveTo(s*0.2, 0); ctx.lineTo(-s*0.6, -s*0.9); ctx.lineTo(-s*0.85, -s*0.88); ctx.lineTo(-s*0.2, 0); ctx.closePath(); ctx.fill();
  ctx.beginPath(); ctx.moveTo(s*0.2, 0); ctx.lineTo(-s*0.6, s*0.9); ctx.lineTo(-s*0.85, s*0.88); ctx.lineTo(-s*0.2, 0); ctx.closePath(); ctx.fill();
  // V-tail
  ctx.beginPath(); ctx.moveTo(-s*0.95, 0); ctx.lineTo(-s*1.1, -s*0.38); ctx.lineTo(-s*0.98, -s*0.36); ctx.lineTo(-s*0.82, 0); ctx.closePath(); ctx.fill();
  ctx.beginPath(); ctx.moveTo(-s*0.95, 0); ctx.lineTo(-s*1.1, s*0.38); ctx.lineTo(-s*0.98, s*0.36); ctx.lineTo(-s*0.82, 0); ctx.closePath(); ctx.fill();
  // Sensor nose
  ctx.fillStyle = '#333'; ctx.beginPath(); ctx.ellipse(s*1.1, 0, s*0.15, s*0.1, 0, 0, Math.PI*2); ctx.fill();
  // Stealth shimmer
  if (stealth) {
    ctx.strokeStyle = 'rgba(100,200,255,0.5)'; ctx.lineWidth = 1;
    ctx.beginPath(); ctx.ellipse(0, 0, s*1.1, s*0.25, 0, 0, Math.PI*2); ctx.stroke();
  }
  ctx.globalAlpha = 1.0;
}

function drawParatrooper(r) {
  const s = r * 1.1;
  // Transport plane body
  ctx.fillStyle = '#3a5a3a';
  ctx.beginPath(); ctx.ellipse(0, 0, s*1.2, s*0.38, 0, 0, Math.PI*2); ctx.fill();
  // Wings
  ctx.fillStyle = '#2a4a2a';
  ctx.beginPath(); ctx.moveTo(s*0.1, s*0.06); ctx.lineTo(-s*0.65, s*1.0); ctx.lineTo(-s*0.9, s*0.98); ctx.lineTo(-s*0.28, s*0.06); ctx.closePath(); ctx.fill();
  ctx.beginPath(); ctx.moveTo(s*0.1, -s*0.06); ctx.lineTo(-s*0.65, -s*1.0); ctx.lineTo(-s*0.9, -s*0.98); ctx.lineTo(-s*0.28, -s*0.06); ctx.closePath(); ctx.fill();
  // Cargo door open (showing paratroopers)
  ctx.fillStyle = '#1a2a1a'; ctx.fillRect(-s*0.3, -s*0.3, s*0.45, s*0.6);
  // Parachute figures falling
  ctx.fillStyle = '#ffffff';
  for (let i=0;i<2;i++) {
    const py = s*0.5 + i*s*0.45;
    // Chute
    ctx.beginPath(); ctx.arc(-s*0.08 + i*s*0.3, py - s*0.35, s*0.2, Math.PI, 0); ctx.fill();
    // Strings
    ctx.strokeStyle = '#cccccc'; ctx.lineWidth = 0.8;
    ctx.beginPath(); ctx.moveTo(-s*0.28 + i*s*0.3, py-s*0.35); ctx.lineTo(-s*0.08 + i*s*0.3, py-s*0.08); ctx.stroke();
    ctx.beginPath(); ctx.moveTo(s*0.12 + i*s*0.3, py-s*0.35); ctx.lineTo(-s*0.08 + i*s*0.3, py-s*0.08); ctx.stroke();
    // Trooper
    ctx.fillStyle = '#4a6a22'; ctx.beginPath(); ctx.arc(-s*0.08 + i*s*0.3, py, s*0.1, 0, Math.PI*2); ctx.fill();
    ctx.fillStyle = '#4a6a22';
  }
  // Cockpit
  ctx.fillStyle = 'rgba(140,210,255,0.55)'; ctx.beginPath(); ctx.ellipse(s*0.55, 0, s*0.28, s*0.18, 0, 0, Math.PI*2); ctx.fill();
}

function drawEngineer(r) {
  const s = r * 1.3;
  // Legs
  ctx.fillStyle = '#3a5a1a'; ctx.fillRect(-s*0.25, s*0.28, s*0.22, s*0.6);
  ctx.fillRect(s*0.04, s*0.28, s*0.22, s*0.6);
  // Boots
  ctx.fillStyle = '#1a1208'; ctx.fillRect(-s*0.3, s*0.8, s*0.32, s*0.2);
  ctx.fillRect(s*0.0, s*0.8, s*0.32, s*0.2);
  // Tool vest (tan/yellow)
  ctx.fillStyle = '#c8922a'; ctx.fillRect(-s*0.32, -s*0.2, s*0.64, s*0.54);
  // Tool pouches
  ctx.fillStyle = '#a87020';
  ctx.fillRect(-s*0.3, -s*0.1, s*0.16, s*0.2);
  ctx.fillRect(s*0.14, -s*0.1, s*0.16, s*0.2);
  // Wrench in hand
  ctx.fillStyle = '#888'; ctx.fillRect(s*0.32, -s*0.18, s*0.08, s*0.4);
  ctx.fillRect(s*0.28, -s*0.18, s*0.18, s*0.08);
  ctx.fillRect(s*0.28, -s*0.06, s*0.18, s*0.08);
  // Head
  ctx.fillStyle = '#c8a070'; ctx.beginPath(); ctx.arc(0, -s*0.42, s*0.24, 0, Math.PI*2); ctx.fill();
  // Hard hat (yellow)
  ctx.fillStyle = '#ffcc00'; ctx.beginPath(); ctx.arc(0, -s*0.5, s*0.27, Math.PI, 0); ctx.fill();
  ctx.fillRect(-s*0.3, -s*0.52, s*0.6, s*0.1);
  // Repair aura (green pulse)
  ctx.strokeStyle = `rgba(80,255,80,${0.2 + Math.sin(Date.now()*0.004)*0.2})`;
  ctx.lineWidth = 2;
  ctx.beginPath(); ctx.arc(0, 0, s*1.2, 0, Math.PI*2); ctx.stroke();
}

function drawBombTruck(r) {
  const s = r * 1.2;
  // Wheels
  ctx.fillStyle = '#111';
  for (let wx of [-s*0.7, 0, s*0.7]) {
    ctx.beginPath(); ctx.arc(wx, s*0.62, s*0.28, 0, Math.PI*2); ctx.fill();
    ctx.fillStyle = '#444'; ctx.beginPath(); ctx.arc(wx, s*0.62, s*0.13, 0, Math.PI*2); ctx.fill();
    ctx.fillStyle = '#111';
  }
  // Chassis
  ctx.fillStyle = '#3a1a0a'; ctx.fillRect(-s*0.9, s*0.18, s*1.8, s*0.48);
  // Bomb cargo box — dark and ominous
  ctx.fillStyle = '#2a0a0a';
  ctx.beginPath(); ctx.roundRect(-s*0.85, -s*0.68, s*1.7, s*0.92, s*0.06); ctx.fill();
  // Warning stripes
  ctx.fillStyle = '#ffaa00';
  for (let i=0;i<5;i++) {
    ctx.fillRect(-s*0.85 + i*s*0.34, -s*0.68, s*0.17, s*0.1);
    ctx.fillRect(-s*0.85 + i*s*0.34, -s*0.08, s*0.17, s*0.1);
  }
  // Skull symbol
  ctx.fillStyle = '#ff2200'; ctx.font = `bold ${s*0.5}px monospace`;
  ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
  ctx.fillText('☠', 0, -s*0.32);
  // Danger pulse
  const pulse = Math.sin(Date.now()*0.006)*0.5+0.5;
  ctx.strokeStyle = `rgba(255,30,0,${0.3+pulse*0.5})`; ctx.lineWidth = 2;
  ctx.beginPath(); ctx.roundRect(-s*0.85, -s*0.68, s*1.7, s*0.92, s*0.06); ctx.stroke();
  // Cab
  ctx.fillStyle = '#4a1a0a'; ctx.beginPath(); ctx.roundRect(s*0.48, -s*0.42, s*0.4, s*0.65, s*0.06); ctx.fill();
  ctx.fillStyle = 'rgba(150,210,255,0.4)'; ctx.fillRect(s*0.52, -s*0.35, s*0.32, s*0.28);
}

function drawProjectile(p) {
  ctx.fillStyle = p.color;
  ctx.beginPath();
  ctx.arc(p.x, p.y, p.size, 0, Math.PI*2);
  ctx.fill();

  // Glow
  ctx.fillStyle = p.color + '55';
  ctx.beginPath();
  ctx.arc(p.x, p.y, p.size*2, 0, Math.PI*2);
  ctx.fill();
}

function drawBase() {
  // Start marker
  ctx.fillStyle = '#44ff44';
  ctx.font = 'bold 11px Orbitron';
  ctx.textAlign = 'center';
  ctx.fillText('SPAWN', path[0].x + 28, path[0].y - 20);
  ctx.fillStyle = '#44ff44';
  ctx.fillRect(path[0].x - 8, path[0].y - 8, 16, 16);

  // End marker / base
  const last = path[path.length-1];
  ctx.fillStyle = '#ff4444';
  ctx.font = 'bold 11px Orbitron';
  ctx.textAlign = 'center';
  ctx.fillText('⭐ BASE', last.x - 28, last.y - 20);
  ctx.fillStyle = '#ff4444';
  ctx.beginPath();
  ctx.arc(last.x - 12, last.y, 10, 0, Math.PI*2);
  ctx.fill();
}

// ===================== HOVER PREVIEW =====================
document.addEventListener('DOMContentLoaded', () => {
  document.getElementById('gameCanvas').addEventListener('mousemove', e => {
    if (!selectedTowerType || !gameRunning) return;
    const rect = canvas.getBoundingClientRect();
    const scaleX = canvasW / rect.width;
    const scaleY = canvasH / rect.height;
    const mx = (e.clientX - rect.left) * scaleX;
    const my = (e.clientY - rect.top) * scaleY;
    const tdef = TOWER_TYPES.find(t => t.id === selectedTowerType);
    if (!tdef) return;

    // Draw range preview (next frame will overdraw)
    // We draw directly for immediate feedback
    ctx.beginPath();
    ctx.arc(mx, my, tdef.range, 0, Math.PI*2);
    ctx.strokeStyle = onPath(mx,my) ? 'rgba(255,50,50,0.5)' : 'rgba(255,215,0,0.35)';
    ctx.lineWidth = 1.5;
    ctx.stroke();
    ctx.fillStyle = onPath(mx,my) ? 'rgba(255,50,50,0.08)' : 'rgba(255,215,0,0.06)';
    ctx.fill();
  });
});

// ===================== GAME OVER / WIN =====================
function gameOver() {
  gameRunning = false;
  const ov = document.getElementById('overlay');
  ov.style.display = 'flex';
  ov.innerHTML = `
    <h2>💀 MISSION FAILED</h2>
    <p>The base has been overrun!</p>
    <p style="color:#ffaa44">Wave Reached: ${wave} | Score: ${score} | Kills: ${kills}</p>
    <button onclick="startGame()">🔄 TRY AGAIN</button>
  `;
}

// ===================== MAIN LOOP =====================
let lastTime = 0;
function gameLoop(ts = 0) {
  const dt = Math.min(ts - lastTime, 32);
  lastTime = ts;

  if (gameRunning) {
    updateSpawning(dt);
    updateEnemies(dt);
    updateTowers(dt);
    updateProjectiles(dt);
    updateParticles(dt);
    updateNapalm(dt);
  }
  draw();
  animFrame = requestAnimationFrame(gameLoop);
}

// ===================== HELPERS =====================
function shadeColor(col, pct) {
  let r = parseInt(col.slice(1,3),16), g = parseInt(col.slice(3,5),16), b = parseInt(col.slice(5,7),16);
  r = Math.min(255,Math.max(0,r+pct)); g = Math.min(255,Math.max(0,g+pct)); b = Math.min(255,Math.max(0,b+pct));
  return '#'+[r,g,b].map(v=>v.toString(16).padStart(2,'0')).join('');
}

// ===================== RESIZE =====================
window.addEventListener('resize', () => {
  if (!gameRunning) return;
  const oldW = canvasW, oldH = canvasH;
  initCanvas();
  const scaleX = canvasW / oldW;
  const scaleY = canvasH / oldH;
  // Scale all tower positions to match new canvas size
  towers.forEach(t => {
    t.x = Math.round(t.x * scaleX);
    t.y = Math.round(t.y * scaleY);
  });
  // Rebuild path for new dimensions
  path = buildPath(canvasW, canvasH);
});

// ===================== BOOT =====================
window.addEventListener('DOMContentLoaded', () => {
  initCanvas();
  buildTowerButtons();
  // Attach canvas click after DOM ready
  document.getElementById('gameCanvas').addEventListener('click', handleClick);
  gameLoop();
});
</script>
</body>
</html>

Game Source: Army Tower Defense

Creator: MysticScout59

Libraries: none

Complexity: complex (3745 lines, 126.4 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: army-tower-defense-mysticscout59" to link back to the original. Then publish at arcadelab.ai/publish.