乐于分享
好东西不私藏

【源代码】坦克大战-优化版,支持手机端和电脑端操作!

【源代码】坦克大战-优化版,支持手机端和电脑端操作!
坦克大战-优化版,支持手机端和电脑端操作!
坦克大战-优化版已发布,新增手机端和各类浏览器兼容;方向键 / WASD 控制坦克移动空格键 射击P 暂停 / 继续游戏内容3 个关卡,每关 20 个敌人,难度递增3 种敌人类型:普通(红色)、快速(粉色)、装甲(蓝色,需 3 发子弹击毁)可破坏的砖墙、不可破坏的钢墙、水域障碍、树林掩护保护底部基地(金色方块),被击毁则游戏结束4 种道具掉落:护盾 (S)、炸弹 (B)、生命 (+)、加速星 (*)3 条生命,出生自带短暂无敌
简单运行方法:保存代码,格式html,直接运行
完整步骤:直接使用代码方法: 本地 HTML 文件双击打开
  1. 1.新建文本文档
    在电脑桌面空白处右键 → 新建 → 文本文档(.txt)
  2. 2.复制全部代码
    把下面一整段从<!DOCTYPE html> 到</html> 的所有代码完整复制,粘贴进新建的记事本里。
  3. 3.保存并改后缀名
  • 记事本左上角:文件 → 保存
  • 点击左上角「文件 - 另存为」:
    • 文件名填:contra.html
    • 4.保存类型:选择所有文件
    • 编码选:UTF-8(关键,防止文字乱码)
  1. 5.运行游戏
    保存后桌面会出现contra.html 文件,双击文件,会自动用浏览器(Chrome/Edge/360 浏览器)打开,直接游玩

<!DOCTYPE html><html lang="zh-CN">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>坦克大战</title>

<style>

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

  body {

    background: #000;

    display: flex;

    justify-content: center;

    align-items: center;

    min-height: 100vh;

    font-family: 'Press Start 2P', 'Courier New', monospace;

    overflow: hidden;

    color: #fff;

  }

  #gameWrapper {

    display: flex;

    gap: 20px;

    align-items: flex-start;

  }

  #gameCanvas {

    border: 3px solid #555;

    image-rendering: pixelated;

    image-rendering: crisp-edges;

  }

  #sidebar {

    width: 160px;

    display: flex;

    flex-direction: column;

    gap: 16px;

    padding-top: 10px;

  }

  .info-block { text-align: center; }

  .info-block .label {

    font-size: 10px;

    color: #888;

    margin-bottom: 6px;

    letter-spacing: 2px;

  }

  .info-block .value {

    font-size: 22px;

    color: #ff0;

    text-shadow: 0 0 8px rgba(255,255,0,0.5);

  }

  .enemy-icons {

    display: flex;

    flex-wrap: wrap;

    gap: 4px;

    justify-content: center;

  }

  .enemy-icon {

    width: 16px; height: 16px;

    background: #e44;

    border-radius: 2px;

  }

  .enemy-icon.dead { background: #333; }

  #startScreen#gameOverScreen#winScreen {

    position: fixed;

    top: 0; left: 0; right: 0; bottom: 0;

    background: rgba(0,0,0,0.92);

    display: flex;

    flex-direction: column;

    justify-content: center;

    align-items: center;

    z-index: 100;

    gap: 30px;

  }

  #startScreen h1 {

    font-size: 36px;

    color: #ff0;

    text-shadow: 0 0 20px rgba(255,255,0,0.6);

    letter-spacing: 6px;

  }

  #startScreen .subtitle {

    font-size: 12px;

    color: #aaa;

    letter-spacing: 3px;

  }

  .btn {

    padding: 14px 40px;

    font-size: 14px;

    font-family: inherit;

    background: transparent;

    color: #ff0;

    border: 2px solid #ff0;

    cursor: pointer;

    letter-spacing: 3px;

    transition: all 0.2s;

  }

  .btn:hover {

    background: #ff0;

    color: #000;

  }

  #gameOverScreen h1 { font-size: 32px; color: #e44; letter-spacing: 4px; }

  #winScreen h1 { font-size: 28px; color: #4f4; letter-spacing: 4px; }

  .score-display { font-size: 16px; color: #ff0; }

  .controls-hint {

    font-size: 10px;

    color: #666;

    line-height: 2;

    text-align: center;

  }

  .hidden { display: none !important; }

  #pauseOverlay {

    position: fixed;

    top: 50%; left: 50%;

    transform: translate(-50%, -50%);

    font-size: 28px;

    color: #ff0;

    letter-spacing: 6px;

    z-index: 90;

    text-shadow: 0 0 20px rgba(255,255,0,0.6);

  }

  /* Mobile Controls */

  #mobileControls {

    display: none;

    position: fixed;

    bottom: 0;

    left: 0;

    right: 0;

    padding: 20px;

    z-index: 80;

    pointer-events: none;

  }

  #mobileControls.active {

    display: flex;

    justify-content: space-between;

    align-items: flex-end;

  }

  .dpad {

    position: relative;

    width: 140px;

    height: 140px;

    pointer-events: auto;

  }

  .dpad-btn {

    position: absolute;

    width: 46px;

    height: 46px;

    background: rgba(255, 255, 0, 0.3);

    border: 2px solid rgba(255, 255, 0, 0.6);

    border-radius: 8px;

    display: flex;

    align-items: center;

    justify-content: center;

    font-size: 20px;

    color: #ff0;

    user-select: none;

    -webkit-user-select: none;

    touch-action: none;

  }

  .dpad-btn:active, .dpad-btn.pressed {

    background: rgba(255, 255, 0, 0.6);

  }

  .dpad-up { top: 0; left: 50%; transform: translateX(-50%); }

  .dpad-down { bottom: 0; left: 50%; transform: translateX(-50%); }

  .dpad-left { left: 0; top: 50%; transform: translateY(-50%); }

  .dpad-right { right: 0; top: 50%; transform: translateY(-50%); }

  .fire-btn {

    width: 80px;

    height: 80px;

    background: rgba(255, 68, 68, 0.4);

    border: 3px solid rgba(255, 68, 68, 0.8);

    border-radius: 50%;

    display: flex;

    align-items: center;

    justify-content: center;

    font-size: 14px;

    color: #fff;

    font-weight: bold;

    pointer-events: auto;

    user-select: none;

    -webkit-user-select: none;

    touch-action: none;

  }

  .fire-btn:active, .fire-btn.pressed {

    background: rgba(255, 68, 68, 0.7);

  }

  .pause-btn {

    width: 44px;

    height: 44px;

    background: rgba(255, 255, 0, 0.3);

    border: 2px solid rgba(255, 255, 0, 0.6);

    border-radius: 8px;

    display: flex;

    align-items: center;

    justify-content: center;

    font-size: 16px;

    color: #ff0;

    font-weight: bold;

    pointer-events: auto;

    user-select: none;

    -webkit-user-select: none;

    touch-action: none;

  }

  .pause-btn:active, .pause-btn.pressed {

    background: rgba(255, 255, 0, 0.6);

  }

  /* Responsive layout */

  [url=home.php?mod=space&uid=945662]@media[/url] (max-width: 768px), (max-height: 600px) {

    #gameWrapper {

      flex-direction: column;

      align-items: center;

      gap: 10px;

    }

    #sidebar {

      flex-direction: row;

      width: auto;

      gap: 20px;

      padding: 10px;

      flex-wrap: wrap;

      justify-content: center;

    }

    #gameCanvas {

      max-width: 90vw;

      max-height: 50vh;

      width: auto;

      height: auto;

    }

    #mobileControls {

      padding: 10px 20px 30px;

    }

    .dpad {

      width: 120px;

      height: 120px;

    }

    .dpad-btn {

      width: 40px;

      height: 40px;

      font-size: 16px;

    }

    .fire-btn {

      width: 70px;

      height: 70px;

      font-size: 12px;

    }

    .controls-hint {

      display: none;

    }

  }

  @media (max-width: 400px) {

    .dpad {

      width: 100px;

      height: 100px;

    }

    .dpad-btn {

      width: 34px;

      height: 34px;

      font-size: 14px;

    }

    .fire-btn {

      width: 60px;

      height: 60px;

    }

  }

</style>

</head>

<body tabindex="0">

<div id="startScreen">

  <h1>坦克大战</h1>

  <div class="subtitle">BATTLE CITY</div>

  <button class="btn">开始游戏</button>

  <div class="controls-hint" id="desktopHint">

    方向键 / WASD 移动<br>

    空格键 射击<br>

    P 暂停

  </div>

  <div class="controls-hint" id="mobileHint" style="display:none;">

    左侧方向键移动<br>

    右侧 FIRE 射击<br>

    II 按钮暂停

  </div>

</div>

<div id="gameOverScreen" class="hidden">

  <h1>游戏结束</h1>

  <div class="score-display">得分: <span id="finalScore">0</span></div>

  <button class="btn">再来一局</button>

</div>

<div id="winScreen" class="hidden">

  <h1>胜利!</h1>

  <div class="score-display">得分: <span id="winScore">0</span></div>

  <button class="btn">下一关</button>

</div>

<div id="pauseOverlay" class="hidden">暂停</div>

<div id="gameWrapper">

  <canvas id="gameCanvas" width="520" height="520"></canvas>

  <div id="sidebar">

    <div class="info-block">

      <div class="label">关卡</div>

      <div class="value" id="levelDisplay">1</div>

    </div>

    <div class="info-block">

      <div class="label">得分</div>

      <div class="value" id="scoreDisplay">0</div>

    </div>

    <div class="info-block">

      <div class="label">生命</div>

      <div class="value" id="livesDisplay">3</div>

    </div>

    <div class="info-block">

      <div class="label">剩余敌人</div>

      <div class="enemy-icons" id="enemyIcons"></div>

    </div>

    <div class="controls-hint">

      方向键/WASD 移动<br>

      空格 射击<br>

      P 暂停

    </div>

  </div>

</div>

<!-- Mobile Controls -->

<div id="mobileControls">

  <div class="dpad">

    <div class="dpad-btn dpad-up" data-dir="up">▲</div>

    <div class="dpad-btn dpad-down" data-dir="down">▼</div>

    <div class="dpad-btn dpad-left" data-dir="left">&#9664;</div>

    <div class="dpad-btn dpad-right" data-dir="right">&#9654;</div>

  </div>

  <div style="display:flex;flex-direction:column;gap:12px;align-items:center;pointer-events:auto;">

    <div class="pause-btn" id="pauseBtn">II</div>

    <div class="fire-btn" id="fireBtn">FIRE</div>

  </div>

</div>

<script>

// ==================== CONSTANTS ====================

const TILE = 20;

const COLS = 26;

const ROWS = 26;

const W = COLS * TILE;

const H = ROWS * TILE;

const HALF = TILE / 2;

const DIR = { UP: 0, RIGHT: 1, DOWN: 2, LEFT: 3 };

const DX = [0, 1, 0, -1];

const DY = [-1, 0, 1, 0];

const TANK_SIZE = TILE * 2 - 2;

const BULLET_SIZE = 4;

const PLAYER_SPEED = 1.8;

const ENEMY_SPEED = 1.0;

const BULLET_SPEED = 3.5;

const ENEMY_BULLET_SPEED = 2.8;

const ENEMY_SHOOT_INTERVAL = 90;

const SPAWN_INTERVAL = 180;

const MAX_ENEMIES_ON_FIELD = 4;

const TILE_EMPTY = 0;

const TILE_BRICK = 1;

const TILE_STEEL = 2;

const TILE_WATER = 3;

const TILE_TREE = 4;

const TILE_BASE = 5;

const TILE_BASE_DEAD = 6;

const COLORS = {

  bg: '#000',

  brick: '#b35900',

  brickLight: '#cc7a33',

  steel: '#aaa',

  steelLight: '#ccc',

  water: '#2244aa',

  waterLight: '#3366cc',

  tree: '#226622',

  treeLight: '#33aa33',

  base: '#ff0',

  baseDead: '#666',

  playerBody: '#ff0',

  playerTurret: '#cc0',

  playerTrack: '#aa0',

  enemyBody: '#e44',

  enemyTurret: '#c22',

  enemyTrack: '#a22',

  enemyFastBody: '#e8e',

  enemyFastTurret: '#c6c',

  enemyArmorBody: '#4ae',

  enemyArmorTurret: '#28c',

  bullet: '#fff',

  explosion: ['#ff0', '#fa0', '#f60', '#f00', '#800']

};

// ==================== LEVELS ====================

const LEVELS = [

  // Level 1

  [

    "00000000000000000000000000",

    "00000000000000000000000000",

    "00110011001100110011001100",

    "00110011001100110011001100",

    "00110011001100110011001100",

    "00110011001100110011001100",

    "00110011002200110011001100",

    "00110011000000110011001100",

    "00110011000000110011001100",

    "00110011001100110011001100",

    "00000000001100000000000000",

    "00000000001100000000000000",

    "11001100000000001100110000",

    "22001100000000001100002200",

    "00000000001100000000000000",

    "00000000001100000000000000",

    "00110011001100110011001100",

    "00110011001100110011001100",

    "00110011001100110011001100",

    "00110011000000110011001100",

    "00110011000000110011001100",

    "00000000000000000000000000",

    "00000000000000000000000000",

    "00000000001111000000000000",

    "00000000001551000000000000",

    "00000000001551000000000000",

  ],

  // Level 2

  [

    "00000000000000000000000000",

    "00000000000000000000000000",

    "00110022001100110022001100",

    "00110022001100110022001100",

    "00110000001100110000001100",

    "00110000001100110000001100",

    "00000011220000221100000000",

    "00000011220000221100000000",

    "00220000001100000000220000",

    "00220000001100000000220000",

    "00001100110000110011000000",

    "00001100110000110011000000",

    "11000022000000002200001100",

    "11000022000000002200001100",

    "00001100110000110011000000",

    "00001100110000110011000000",

    "00220000001100000000220000",

    "00220000001100000000220000",

    "00110011001100110011001100",

    "00110011001100110011001100",

    "00110011000000110011001100",

    "00110011000000110011001100",

    "00000000000000000000000000",

    "00000000001111000000000000",

    "00000000001551000000000000",

    "00000000001551000000000000",

  ],

  // Level 3

  [

    "00000000000000000000000000",

    "00000000000000000000000000",

    "00110011003300110011001100",

    "00110011003300110011001100",

    "00110011000000110011001100",

    "00110011000000110011001100",

    "00000022001100220000001100",

    "00000022001100220000001100",

    "22110000002200000011002200",

    "22110000002200000011002200",

    "00001133000000331100000000",

    "00001133000000331100000000",

    "00110000221100220000110000",

    "00110000221100220000110000",

    "00001133000000331100000000",

    "00001133000000331100000000",

    "22110000002200000011002200",

    "22110000002200000011002200",

    "00000022001100220000001100",

    "00000022001100220000001100",

    "00110011000000110011001100",

    "00110011000000110011001100",

    "00000000000000000000000000",

    "00000000001111000000000000",

    "00000000001551000000000000",

    "00000000001551000000000000",

  ],

];

// ==================== GAME STATE ====================

const canvas = document.getElementById('gameCanvas');

const ctx = canvas.getContext('2d');

let gameState = 'start'; // start, playing, paused, gameover, win

let level = 0;

let score = 0;

let lives = 3;

let map = [];

let player = null;

let enemies = [];

let bullets = [];

let explosions = [];

let powerUps = [];

let enemiesRemaining = 0;

let enemiesSpawned = 0;

let totalEnemies = 20;

let spawnTimer = 0;

let frameCount = 0;

let baseAlive = true;

let spawnPoints = [];

let keys = {};

let playerInvincible = 0;

let freezeTimer = 0;

// ==================== INPUT ====================

function handleKeyDown(e) {

  keys[e.code] = true;

  // Also map to key for compatibility

  if (e.key) keys[e.key] = true;

  if (e.key === 'p' || e.key === 'P') {

    if (gameState === 'playing') {

      gameState = 'paused';

      document.getElementById('pauseOverlay').classList.remove('hidden');

    } else if (gameState === 'paused') {

      gameState = 'playing';

      document.getElementById('pauseOverlay').classList.add('hidden');

    }

  }

  if (['ArrowUp','ArrowDown','ArrowLeft','ArrowRight',' '].includes(e.key)) {

    e.preventDefault();

  }

}

function handleKeyUp(e) {

  keys[e.code] = false;

  if (e.key) keys[e.key] = false;

}

document.addEventListener('keydown', handleKeyDown);

document.addEventListener('keyup', handleKeyUp);

window.addEventListener('keydown', handleKeyDown);

window.addEventListener('keyup', handleKeyUp);

// Ensure focus for keyboard input in iframe environments

window.addEventListener('load', () => { document.body.focus(); });

document.addEventListener('click', () => { document.body.focus(); });

document.addEventListener('touchstart', () => { document.body.focus(); });

// ==================== MOBILE CONTROLS ====================

const mobileControls = document.getElementById('mobileControls');

const dpadBtns = document.querySelectorAll('.dpad-btn');

const fireBtn = document.getElementById('fireBtn');

// Detect touch device and show controls

function checkTouchDevice() {

  if ('ontouchstart' in window || navigator.maxTouchPoints > 0) {

    mobileControls.classList.add('active');

    document.getElementById('desktopHint').style.display = 'none';

    document.getElementById('mobileHint').style.display = 'block';

  }

}

checkTouchDevice();

// D-pad touch handling

const dirMap = { up: 'ArrowUp', down: 'ArrowDown', left: 'ArrowLeft', right: 'ArrowRight' };

const codeMap = { up: 'ArrowUp', down: 'ArrowDown', left: 'ArrowLeft', right: 'ArrowRight' };

dpadBtns.forEach(btn => {

  const dir = btn.dataset.dir;

  btn.addEventListener('touchstart', (e) => {

    e.preventDefault();

    btn.classList.add('pressed');

    keys[dirMap[dir]] = true;

    keys[codeMap[dir]] = true;

  });

  btn.addEventListener('touchend', (e) => {

    e.preventDefault();

    btn.classList.remove('pressed');

    keys[dirMap[dir]] = false;

    keys[codeMap[dir]] = false;

  });

  btn.addEventListener('touchcancel', (e) => {

    e.preventDefault();

    btn.classList.remove('pressed');

    keys[dirMap[dir]] = false;

    keys[codeMap[dir]] = false;

  });

  // Mouse fallback for testing

  btn.addEventListener('mousedown', (e) => {

    e.preventDefault();

    btn.classList.add('pressed');

    keys[dirMap[dir]] = true;

    keys[codeMap[dir]] = true;

  });

  btn.addEventListener('mouseup', (e) => {

    e.preventDefault();

    btn.classList.remove('pressed');

    keys[dirMap[dir]] = false;

    keys[codeMap[dir]] = false;

  });

  btn.addEventListener('mouseleave', (e) => {

    btn.classList.remove('pressed');

    keys[dirMap[dir]] = false;

    keys[codeMap[dir]] = false;

  });

});

// Fire button touch handling

fireBtn.addEventListener('touchstart', (e) => {

  e.preventDefault();

  fireBtn.classList.add('pressed');

  keys[' '] = true;

  keys['Space'] = true;

});

fireBtn.addEventListener('touchend', (e) => {

  e.preventDefault();

  fireBtn.classList.remove('pressed');

  keys[' '] = false;

  keys['Space'] = false;

});

fireBtn.addEventListener('touchcancel', (e) => {

  e.preventDefault();

  fireBtn.classList.remove('pressed');

  keys[' '] = false;

  keys['Space'] = false;

});

// Mouse fallback for fire button

fireBtn.addEventListener('mousedown', (e) => {

  e.preventDefault();

  fireBtn.classList.add('pressed');

  keys[' '] = true;

  keys['Space'] = true;

});

fireBtn.addEventListener('mouseup', (e) => {

  e.preventDefault();

  fireBtn.classList.remove('pressed');

  keys[' '] = false;

  keys['Space'] = false;

});

fireBtn.addEventListener('mouseleave', (e) => {

  fireBtn.classList.remove('pressed');

  keys[' '] = false;

  keys['Space'] = false;

});

// Pause button

const pauseBtn = document.getElementById('pauseBtn');

function togglePause() {

  if (gameState === 'playing') {

    gameState = 'paused';

    document.getElementById('pauseOverlay').classList.remove('hidden');

  } else if (gameState === 'paused') {

    gameState = 'playing';

    document.getElementById('pauseOverlay').classList.add('hidden');

  }

}

pauseBtn.addEventListener('touchstart', (e) => {

  e.preventDefault();

  pauseBtn.classList.add('pressed');

  togglePause();

});

pauseBtn.addEventListener('touchend', (e) => {

  e.preventDefault();

  pauseBtn.classList.remove('pressed');

});

pauseBtn.addEventListener('click', (e) => {

  e.preventDefault();

  togglePause();

});

// ==================== MAP ====================

function loadMap(lvl) {

  const data = LEVELS[lvl % LEVELS.length];

  map = [];

  for (let r = 0; r < ROWS; r++) {

    map[r] = [];

    for (let c = 0; c < COLS; c++) {

      map[r][c] = parseInt(data[r][c]);

    }

  }

}

function drawTile(r, c) {

  const x = c * TILE;

  const y = r * TILE;

  const t = map[r][c];

  if (t === TILE_BRICK) {

    ctx.fillStyle = COLORS.brick;

    ctx.fillRect(x, y, TILE, TILE);

    ctx.fillStyle = COLORS.brickLight;

    ctx.fillRect(x, y, TILE/2, TILE/2);

    ctx.fillRect(x + TILE/2, y + TILE/2, TILE/2, TILE/2);

    ctx.strokeStyle = '#000';

    ctx.lineWidth = 0.5;

    ctx.strokeRect(x, y, TILE, TILE);

  } else if (t === TILE_STEEL) {

    ctx.fillStyle = COLORS.steel;

    ctx.fillRect(x, y, TILE, TILE);

    ctx.fillStyle = COLORS.steelLight;

    ctx.fillRect(x + 2, y + 2, TILE - 4, TILE - 4);

    ctx.fillStyle = COLORS.steel;

    ctx.fillRect(x + 5, y + 5, TILE - 10, TILE - 10);

  } else if (t === TILE_WATER) {

    const wave = Math.sin(frameCount * 0.05 + c + r) * 0.3 + 0.5;

    ctx.fillStyle = wave > 0.5 ? COLORS.water : COLORS.waterLight;

    ctx.fillRect(x, y, TILE, TILE);

    ctx.fillStyle = wave > 0.5 ? COLORS.waterLight : COLORS.water;

    for (let i = 0; i < 3; i++) {

      const wy = y + 4 + i * 6 + Math.sin(frameCount * 0.08 + c * 0.5) * 2;

      ctx.fillRect(x, wy, TILE, 2);

    }

  } else if (t === TILE_BASE) {

    ctx.fillStyle = COLORS.base;

    ctx.fillRect(x, y, TILE, TILE);

    ctx.fillStyle = '#000';

    ctx.fillRect(x + 4, y + 4, TILE - 8, TILE - 8);

    ctx.fillStyle = COLORS.base;

    ctx.fillRect(x + 7, y + 7, TILE - 14, TILE - 14);

  } else if (t === TILE_BASE_DEAD) {

    ctx.fillStyle = COLORS.baseDead;

    ctx.fillRect(x, y, TILE, TILE);

    ctx.fillStyle = '#444';

    ctx.fillRect(x + 3, y + 3, TILE - 6, TILE - 6);

  }

}

function drawTree(r, c) {

  if (map[r][c] !== TILE_TREE) return;

  const x = c * TILE;

  const y = r * TILE;

  ctx.fillStyle = COLORS.tree;

  ctx.fillRect(x, y, TILE, TILE);

  ctx.fillStyle = COLORS.treeLight;

  ctx.fillRect(x + 2, y + 2, 6, 6);

  ctx.fillRect(x + 10, y + 8, 6, 6);

  ctx.fillRect(x + 4, y + 12, 6, 6);

}

// ==================== TANK ====================

class Tank {

  constructor(x, y, dir, isPlayer, type) {

    this.x = x;

    this.y = y;

    this.dir = dir;

    this.isPlayer = isPlayer;

    this.type = type || 'normal'; // normal, fast, armor

    this.speed = isPlayer ? PLAYER_SPEED : (type === 'fast' ? ENEMY_SPEED * 1.6 : ENEMY_SPEED);

    this.hp = isPlayer ? 1 : (type === 'armor' ? 3 : 1);

    this.maxHp = this.hp;

    this.shootCooldown = 0;

    this.alive = true;

    this.moveTimer = 0;

    this.aiDir = dir;

    this.aiDirTimer = 0;

    this.flashTimer = 0;

    this.spawnAnim = 30;

    this.size = TANK_SIZE;

  }

  get cx() { return this.x + this.size / 2; }

  get cy() { return this.y + this.size / 2; }

  update() {

    if (this.spawnAnim > 0) { this.spawnAnim--; return; }

    if (this.shootCooldown > 0) this.shootCooldown--;

    if (this.flashTimer > 0) this.flashTimer--;

    if (this.isPlayer) {

      this.updatePlayer();

    } else {

      if (freezeTimer <= 0) this.updateAI();

    }

  }

  updatePlayer() {

    let moving = false;

    let newDir = this.dir;

    if (keys['ArrowUp'] || keys['KeyW'] || keys['w'] || keys['W']) { newDir = DIR.UP; moving = true; }

    else if (keys['ArrowDown'] || keys['KeyS'] || keys['s'] || keys['S']) { newDir = DIR.DOWN; moving = true; }

    else if (keys['ArrowLeft'] || keys['KeyA'] || keys['a'] || keys['A']) { newDir = DIR.LEFT; moving = true; }

    else if (keys['ArrowRight'] || keys['KeyD'] || keys['d'] || keys['D']) { newDir = DIR.RIGHT; moving = true; }

    this.dir = newDir;

    if (moving) this.move(this.dir);

    if (keys[' '] || keys['Space']) this.shoot();

  }

  updateAI() {

    this.aiDirTimer--;

    if (this.aiDirTimer <= 0) {

      // Change direction periodically or when blocked

      const dirs = [DIR.UP, DIR.RIGHT, DIR.DOWN, DIR.LEFT];

      // Bias towards player direction

      if (player && player.alive && Math.random() < 0.3) {

        const dx = player.cx - this.cx;

        const dy = player.cy - this.cy;

        if (Math.abs(dx) > Math.abs(dy)) {

          this.aiDir = dx > 0 ? DIR.RIGHT : DIR.LEFT;

        } else {

          this.aiDir = dy > 0 ? DIR.DOWN : DIR.UP;

        }

      } else {

        this.aiDir = dirs[Math.floor(Math.random() * 4)];

      }

      this.aiDirTimer = 60 + Math.floor(Math.random() * 120);

    }

    this.dir = this.aiDir;

    const moved = this.move(this.dir);

    if (!moved) {

      this.aiDirTimer = 0; // change direction next frame

    }

    // Shooting

    if (Math.random() < 0.02 || (this.shootCooldown <= 0 && Math.random() < 0.05)) {

      this.shoot();

    }

  }

  move(dir) {

    const nx = this.x + DX[dir] * this.speed;

    const ny = this.y + DY[dir] * this.speed;

    // Boundary check

    if (nx < 0 || ny < 0 || nx + this.size > W || ny + this.size > H) return false;

    // Tile collision

    if (this.checkTileCollision(nx, ny)) return false;

    // Tank-tank collision

    if (this.checkTankCollision(nx, ny)) return false;

    this.x = nx;

    this.y = ny;

    return true;

  }

  checkTileCollision(nx, ny) {

    const margin = 1;

    const left = Math.floor((nx + margin) / TILE);

    const top = Math.floor((ny + margin) / TILE);

    const right = Math.floor((nx + this.size - 1 - margin) / TILE);

    const bottom = Math.floor((ny + this.size - 1 - margin) / TILE);

    for (let r = top; r <= bottom; r++) {

      for (let c = left; c <= right; c++) {

        if (r < 0 || r >= ROWS || c < 0 || c >= COLS) return true;

        const t = map[r][c];

        if (t === TILE_BRICK || t === TILE_STEEL || t === TILE_WATER || t === TILE_BASE || t === TILE_BASE_DEAD) {

          return true;

        }

      }

    }

    return false;

  }

  checkTankCollision(nx, ny) {

    const allTanks = [player, ...enemies].filter(t => t && t.alive && t !== this && t.spawnAnim <= 0);

    for (const other of allTanks) {

      if (nx < other.x + other.size && nx + this.size > other.x &&

          ny < other.y + other.size && ny + this.size > other.y) {

        return true;

      }

    }

    return false;

  }

  shoot() {

    if (this.shootCooldown > 0) return;

    // Limit bullets per tank

    const myBullets = bullets.filter(b => b.owner === this);

    if (this.isPlayer && myBullets.length >= 2) return;

    if (!this.isPlayer && myBullets.length >= 1) return;

    const bx = this.cx - BULLET_SIZE / 2 + DX[this.dir] * (this.size / 2);

    const by = this.cy - BULLET_SIZE / 2 + DY[this.dir] * (this.size / 2);

    const speed = this.isPlayer ? BULLET_SPEED : ENEMY_BULLET_SPEED;

    bullets.push(new Bullet(bx, by, this.dir, this.isPlayer, this, speed));

    this.shootCooldown = this.isPlayer ? 15 : ENEMY_SHOOT_INTERVAL;

  }

  draw() {

    if (!this.alive) return;

    // Spawn animation

    if (this.spawnAnim > 0) {

      const flash = this.spawnAnim % 8 < 4;

      ctx.strokeStyle = flash ? '#fff' : '#ff0';

      ctx.lineWidth = 2;

      const s = this.size * (1 - this.spawnAnim / 30) * 0.8;

      ctx.strokeRect(this.cx - s/2, this.cy - s/2, s, s);

      return;

    }

    ctx.save();

    ctx.translate(this.cx, this.cy);

    ctx.rotate(this.dir * Math.PI / 2);

    const s = this.size;

    const hs = s / 2;

    // Flash when hit

    if (this.flashTimer > 0 && this.flashTimer % 4 < 2) {

      ctx.globalAlpha = 0.5;

    }

    // Body colors

    let bodyColor, turretColor, trackColor;

    if (this.isPlayer) {

      bodyColor = COLORS.playerBody;

      turretColor = COLORS.playerTurret;

      trackColor = COLORS.playerTrack;

    } else if (this.type === 'fast') {

      bodyColor = COLORS.enemyFastBody;

      turretColor = COLORS.enemyFastTurret;

      trackColor = COLORS.enemyFastBody;

    } else if (this.type === 'armor') {

      bodyColor = this.hp > 1 ? COLORS.enemyArmorBody : COLORS.enemyBody;

      turretColor = this.hp > 1 ? COLORS.enemyArmorTurret : COLORS.enemyTurret;

      trackColor = COLORS.enemyArmorBody;

    } else {

      bodyColor = COLORS.enemyBody;

      turretColor = COLORS.enemyTurret;

      trackColor = COLORS.enemyTrack;

    }

    // Tracks

    ctx.fillStyle = trackColor;

    ctx.fillRect(-hs, -hs, s * 0.22, s);

    ctx.fillRect(hs - s * 0.22, -hs, s * 0.22, s);

    // Track details

    ctx.fillStyle = '#000';

    for (let i = 0; i < 5; i++) {

      const ty = -hs + 3 + i * (s / 5);

      ctx.fillRect(-hs + 1, ty, s * 0.2, 2);

      ctx.fillRect(hs - s * 0.22 + 1, ty, s * 0.2, 2);

    }

    // Body

    ctx.fillStyle = bodyColor;

    ctx.fillRect(-hs + s * 0.22, -hs + 2, s * 0.56, s - 4);

    // Turret base

    ctx.fillStyle = turretColor;

    ctx.fillRect(-s * 0.18, -s * 0.18, s * 0.36, s * 0.36);

    // Barrel

    ctx.fillStyle = turretColor;

    ctx.fillRect(-2, -hs - 2, 4, hs);

    // Invincibility shield

    if (this.isPlayer && playerInvincible > 0) {

      ctx.strokeStyle = playerInvincible % 6 < 3 ? '#0ff' : '#fff';

      ctx.lineWidth = 1.5;

      ctx.beginPath();

      ctx.arc(0, 0, hs + 2, 0, Math.PI * 2);

      ctx.stroke();

    }

    ctx.restore();

  }

  hit() {

    this.hp--;

    this.flashTimer = 15;

    if (this.hp <= 0) {

      this.alive = false;

      addExplosion(this.cx, this.cy, true);

      if (!this.isPlayer) {

        score += this.type === 'armor' ? 300 : this.type === 'fast' ? 200 : 100;

        updateUI();

      }

    }

  }

}

// ==================== BULLET ====================

class Bullet {

  constructor(x, y, dir, isPlayerBullet, owner, speed) {

    this.x = x;

    this.y = y;

    this.dir = dir;

    this.isPlayerBullet = isPlayerBullet;

    this.owner = owner;

    this.speed = speed;

    this.alive = true;

    this.size = BULLET_SIZE;

  }

  update() {

    this.x += DX[this.dir] * this.speed;

    this.y += DY[this.dir] * this.speed;

    // Boundary

    if (this.x < 0 || this.y < 0 || this.x > W || this.y > H) {

      this.alive = false;

      addExplosion(this.x, this.y, false);

      return;

    }

    // Tile collision

    this.checkTileHit();

    if (!this.alive) return;

    // Tank collision

    this.checkTankHit();

  }

  checkTileHit() {

    const cx = this.x + this.size / 2;

    const cy = this.y + this.size / 2;

    const c = Math.floor(cx / TILE);

    const r = Math.floor(cy / TILE);

    // Check surrounding tiles

    for (let dr = -1; dr <= 1; dr++) {

      for (let dc = -1; dc <= 1; dc++) {

        const tr = r + dr;

        const tc = c + dc;

        if (tr < 0 || tr >= ROWS || tc < 0 || tc >= COLS) continue;

        const tx = tc * TILE;

        const ty = tr * TILE;

        if (this.x + this.size <= tx || this.x >= tx + TILE ||

            this.y + this.size <= ty || this.y >= ty + TILE) continue;

        const t = map[tr][tc];

        if (t === TILE_BRICK) {

          map[tr][tc] = TILE_EMPTY;

          this.alive = false;

          addExplosion(cx, cy, false);

          return;

        } else if (t === TILE_STEEL) {

          if (this.isPlayerBullet) {

            // Player bullets can't destroy steel (unless power-up)

          }

          this.alive = false;

          addExplosion(cx, cy, false);

          return;

        } else if (t === TILE_BASE) {

          map[tr][tc] = TILE_BASE_DEAD;

          baseAlive = false;

          this.alive = false;

          addExplosion(tx + TILE/2, ty + TILE/2, true);

          return;

        }

      }

    }

  }

  checkTankHit() {

    if (this.isPlayerBullet) {

      for (const enemy of enemies) {

        if (!enemy.alive || enemy.spawnAnim > 0) continue;

        if (this.x < enemy.x + enemy.size && this.x + this.size > enemy.x &&

            this.y < enemy.y + enemy.size && this.y + this.size > enemy.y) {

          this.alive = false;

          enemy.hit();

          return;

        }

      }

    } else {

      if (player && player.alive && player.spawnAnim <= 0) {

        if (this.x < player.x + player.size && this.x + this.size > player.x &&

            this.y < player.y + player.size && this.y + this.size > player.y) {

          this.alive = false;

          if (playerInvincible <= 0) {

            player.hit();

            if (!player.alive) {

              lives--;

              updateUI();

              if (lives > 0) {

                setTimeout(() => respawnPlayer(), 1500);

              }

            }

          }

          return;

        }

      }

    }

    // Bullet-bullet collision

    for (const other of bullets) {

      if (other === this || !other.alive) continue;

      if (this.isPlayerBullet !== other.isPlayerBullet) {

        if (this.x < other.x + other.size && this.x + this.size > other.x &&

            this.y < other.y + other.size && this.y + this.size > other.y) {

          this.alive = false;

          other.alive = false;

          return;

        }

      }

    }

  }

  draw() {

    if (!this.alive) return;

    ctx.fillStyle = COLORS.bullet;

    ctx.shadowColor = '#ff0';

    ctx.shadowBlur = 4;

    ctx.fillRect(this.x, this.y, this.size, this.size);

    ctx.shadowBlur = 0;

  }

}

// ==================== EXPLOSION ====================

class Explosion {

  constructor(x, y, big) {

    this.x = x;

    this.y = y;

    this.big = big;

    this.frame = 0;

    this.maxFrame = big ? 20 : 12;

    this.alive = true;

  }

  update() {

    this.frame++;

    if (this.frame >= this.maxFrame) this.alive = false;

  }

  draw() {

    if (!this.alive) return;

    const progress = this.frame / this.maxFrame;

    const radius = this.big ? 15 * (1 - progress) : 10 * (1 - progress);

    const colorIdx = Math.floor(progress * COLORS.explosion.length);

    ctx.fillStyle = COLORS.explosion[colorIdx] || COLORS.explosion[COLORS.explosion.length - 1];

    ctx.beginPath();

    ctx.arc(this.x, this.y, radius, 0, Math.PI * 2);

    ctx.fill();

  }

}

// ==================== GAME FUNCTIONS ====================

function addExplosion(x, y, big) {

  explosions.push(new Explosion(x, y, big));

}

function respawnPlayer() {

  if (lives <= 0) return;

  player = new Tank(12 * TILE, 24 * TILE, DIR.UP, true);

  playerInvincible = 120;

}

function updateUI() {

  document.getElementById('levelDisplay').textContent = level + 1;

  document.getElementById('scoreDisplay').textContent = score;

  document.getElementById('livesDisplay').textContent = lives;

  const enemyIcons = document.getElementById('enemyIcons');

  enemyIcons.innerHTML = '';

  for (let i = 0; i < totalEnemies; i++) {

    const icon = document.createElement('div');

    icon.className = 'enemy-icon';

    if (i < enemiesSpawned) icon.classList.add('dead');

    enemyIcons.appendChild(icon);

  }

  document.getElementById('finalScore').textContent = score;

  document.getElementById('winScore').textContent = score;

}

function initLevel() {

  loadMap(level);

  enemies = [];

  bullets = [];

  explosions = [];

  powerUps = [];

  enemiesRemaining = totalEnemies;

  enemiesSpawned = 0;

  spawnTimer = 0;

  baseAlive = true;

  spawnPoints = [

    { x: 0, y: 0 },

    { x: 12 * TILE, y: 0 },

    { x: 24 * TILE, y: 0 }

  ];

  // Reset base tiles

  for (let r = ROWS - 2; r < ROWS; r++) {

    for (let c = 11; c <= 14; c++) {

      if (map[r][c] === TILE_BASE_DEAD) map[r][c] = TILE_BASE;

    }

  }

  // Reset player

  if (player && player.alive) {

    player.x = 12 * TILE;

    player.y = 24 * TILE;

    player.dir = DIR.UP;

    player.spawnAnim = 30;

  } else {

    player = new Tank(12 * TILE, 24 * TILE, DIR.UP, true);

  }

  playerInvincible = 60;

  updateUI();

}

function spawnEnemy() {

  if (enemies.length >= MAX_ENEMIES_ON_FIELD || enemiesSpawned >= totalEnemies) return;

  const spawnPoint = spawnPoints[Math.floor(Math.random() * spawnPoints.length)];

  const types = ['normal', 'fast', 'armor'];

  const type = Math.random() < 0.3 ? 'armor' : (Math.random() < 0.4 ? 'fast' : 'normal');

  const enemy = new Tank(spawnPoint.x, spawnPoint.y, DIR.DOWN, false, type);

  enemies.push(enemy);

  enemiesSpawned++;

  updateUI();

}

function checkWinCondition() {

  if (enemiesRemaining <= 0) {

    gameState = 'win';

    document.getElementById('winScreen').classList.remove('hidden');

    document.getElementById('winScore').textContent = score;

  }

}

function startGame() {

  level = 0;

  score = 0;

  lives = 3;

  initLevel();

  gameState = 'playing';

  document.getElementById('startScreen').classList.add('hidden');

  document.getElementById('gameOverScreen').classList.add('hidden');

  document.getElementById('winScreen').classList.add('hidden');

}

function restartGame() {

  gameState = 'start';

  document.getElementById('gameOverScreen').classList.add('hidden');

  document.getElementById('winScreen').classList.add('hidden');

  document.getElementById('startScreen').classList.remove('hidden');

}

function nextLevel() {

  level++;

  document.getElementById('winScreen').classList.add('hidden');

  initLevel();

  gameState = 'playing';

}

// ==================== GAME LOOP ====================

function gameLoop() {

  frameCount++;

  if (gameState === 'playing') {

    // Update timers

    if (playerInvincible > 0) playerInvincible--;

    if (freezeTimer > 0) freezeTimer--;

    // Spawn enemies

    spawnTimer++;

    if (spawnTimer >= SPAWN_INTERVAL && enemiesSpawned < totalEnemies) {

      spawnTimer = 0;

      spawnEnemy();

    }

    // Update player

    if (player && player.alive) {

      player.update();

    }

    // Update enemies

    enemies = enemies.filter(enemy => enemy.alive);

    enemies.forEach(enemy => enemy.update());

    // Update bullets

    bullets = bullets.filter(bullet => bullet.alive);

    bullets.forEach(bullet => bullet.update());

    // Update explosions

    explosions = explosions.filter(explosion => explosion.alive);

    explosions.forEach(explosion => explosion.update());

    // Update enemy remaining count

    enemiesRemaining = totalEnemies - enemiesSpawned + enemies.length;

    // Check win/lose conditions

    if (enemiesRemaining <= 0) {

      checkWinCondition();

    }

    if (lives <= 0 || !baseAlive) {

      gameState = 'gameover';

      document.getElementById('gameOverScreen').classList.remove('hidden');

      document.getElementById('finalScore').textContent = score;

    }

  }

  // Draw everything

  ctx.clearRect(0, 0, W, H);

  // Draw map

  for (let r = 0; r < ROWS; r++) {

    for (let c = 0; c < COLS; c++) {

      drawTile(r, c);

    }

  }

  // Draw trees (above everything)

  for (let r = 0; r < ROWS; r++) {

    for (let c = 0; c < COLS; c++) {

      if (map[r][c] === TILE_TREE) {

        drawTree(r, c);

      }

    }

  }

  // Draw tanks

  enemies.forEach(enemy => enemy.draw());

  if (player && player.alive) player.draw();

  // Draw bullets

  bullets.forEach(bullet => bullet.draw());

  // Draw explosions

  explosions.forEach(explosion => explosion.draw());

  // Update UI

  updateUI();

  requestAnimationFrame(gameLoop);

}

// ==================== INITIALIZATION ====================

function init() {

  // Set canvas size

  canvas.width = W;

  canvas.height = H;

  // Add button event listeners

  const startBtn = document.querySelector('#startScreen .btn');

  const restartBtn = document.querySelector('#gameOverScreen .btn');

  const nextLevelBtn = document.querySelector('#winScreen .btn');

  startBtn.addEventListener('click', startGame);

  restartBtn.addEventListener('click', startGame);

  nextLevelBtn.addEventListener('nextLevel', nextLevel);

  // Start game loop

  requestAnimationFrame(gameLoop);

}

// Start the game when page loads

window.addEventListener('load', init);

// ==================== POWER-UP ====================

class PowerUp {

  constructor(x, y, type) {

    this.x = x;

    this.y = y;

    this.type = type; // 'star', 'shield', 'bomb', 'life'

    this.alive = true;

    this.timer = 600; // 10 seconds

    this.size = TILE * 2;

  }

  update() {

    this.timer--;

    if (this.timer <= 0) this.alive = false;

    if (player && player.alive) {

      if (this.x < player.x + player.size && this.x + this.size > player.x &&

          this.y < player.y + player.size && this.y + this.size > player.y) {

        this.alive = false;

        this.apply();

      }

    }

  }

  apply() {

    switch (this.type) {

      case 'shield':

        playerInvincible = 300;

        break;

      case 'bomb':

        enemies.forEach(e => {

          if (e.alive) {

            e.alive = false;

            addExplosion(e.cx, e.cy, true);

            score += 50;

          }

        });

        break;

      case 'life':

        lives++;

        break;

      case 'star':

        player.speed = PLAYER_SPEED * 1.3;

        break;

    }

    score += 500;

    updateUI();

  }

  draw() {

    if (!this.alive) return;

    if (this.timer % 20 < 10 && this.timer < 120) return; // blink when about to expire

    const x = this.x;

    const y = this.y;

    const s = this.size;

    ctx.fillStyle = '#222';

    ctx.fillRect(x, y, s, s);

    ctx.strokeStyle = '#fff';

    ctx.lineWidth = 1;

    ctx.strokeRect(x, y, s, s);

    ctx.font = '16px monospace';

    ctx.textAlign = 'center';

    ctx.textBaseline = 'middle';

    switch (this.type) {

      case 'shield':

        ctx.fillStyle = '#0ff';

        ctx.fillText('S', x + s/2, y + s/2);

        break;

      case 'bomb':

        ctx.fillStyle = '#f44';

        ctx.fillText('B', x + s/2, y + s/2);

        break;

      case 'life':

        ctx.fillStyle = '#4f4';

        ctx.fillText('+', x + s/2, y + s/2);

        break;

      case 'star':

        ctx.fillStyle = '#ff0';

        ctx.fillText('*', x + s/2, y + s/2);

        break;

    }

  }

}

// ==================== GAME LOGIC ====================

function spawnEnemy() {

  if (enemiesSpawned >= totalEnemies) return;

  if (enemies.filter(e => e.alive).length >= MAX_ENEMIES_ON_FIELD) return;

  const sp = spawnPoints[enemiesSpawned % spawnPoints.length];

  // Check if spawn point is clear

  const allTanks = [player, ...enemies].filter(t => t && t.alive);

  for (const t of allTanks) {

    if (sp.x < t.x + t.size && sp.x + TANK_SIZE > t.x &&

        sp.y < t.y + t.size && sp.y + TANK_SIZE > t.y) {

      return; // spawn point blocked

    }

  }

  const types = ['normal', 'normal', 'fast', 'armor'];

  const typeIdx = Math.min(Math.floor(enemiesSpawned / 5), types.length - 1);

  const type = types[Math.floor(Math.random() * (typeIdx + 1))];

  const enemy = new Tank(sp.x, sp.y, DIR.DOWN, false, type);

  enemies.push(enemy);

  enemiesSpawned++;

  updateUI();

}

function respawnPlayer() {

  if (gameState !== 'playing') return;

  player = new Tank(4 * TILE, 24 * TILE, DIR.UP, true);

  playerInvincible = 120;

}

function startGame() {

  document.getElementById('startScreen').classList.add('hidden');

  document.getElementById('gameOverScreen').classList.add('hidden');

  document.getElementById('winScreen').classList.add('hidden');

  level = 0;

  score = 0;

  lives = 3;

  initLevel();

  gameState = 'playing';

  document.body.focus();

}

function nextLevel() {

  document.getElementById('winScreen').classList.add('hidden');

  level++;

  initLevel();

  gameState = 'playing';

  document.body.focus();

}

function initLevel() {

  loadMap(level);

  bullets = [];

  explosions = [];

  powerUps = [];

  enemies = [];

  enemiesSpawned = 0;

  totalEnemies = 20;

  enemiesRemaining = totalEnemies;

  spawnTimer = 0;

  baseAlive = true;

  playerInvincible = 120;

  freezeTimer = 0;

  spawnPoints = [

    { x: 0, y: 0 },

    { x: 12 * TILE, y: 0 },

    { x: 24 * TILE, y: 0 }

  ];

  player = new Tank(4 * TILE, 24 * TILE, DIR.UP, true);

  updateUI();

}

function updateUI() {

  document.getElementById('levelDisplay').textContent = level + 1;

  document.getElementById('scoreDisplay').textContent = score;

  document.getElementById('livesDisplay').textContent = lives;

  const remaining = totalEnemies - enemiesSpawned + enemies.filter(e => e.alive).length;

  const icons = document.getElementById('enemyIcons');

  icons.innerHTML = '';

  for (let i = 0; i < totalEnemies; i++) {

    const div = document.createElement('div');

    div.className = 'enemy-icon' + (i >= remaining ? ' dead' : '');

    icons.appendChild(div);

  }

}

function checkWinLose() {

  if (!baseAlive) {

    gameState = 'gameover';

    document.getElementById('finalScore').textContent = score;

    document.getElementById('gameOverScreen').classList.remove('hidden');

    return;

  }

  if (lives <= 0 && (!player || !player.alive)) {

    gameState = 'gameover';

    document.getElementById('finalScore').textContent = score;

    document.getElementById('gameOverScreen').classList.remove('hidden');

    return;

  }

  const allDead = enemies.every(e => !e.alive);

  if (enemiesSpawned >= totalEnemies && allDead) {

    gameState = 'win';

    document.getElementById('winScore').textContent = score;

    document.getElementById('winScreen').classList.remove('hidden');

  }

}

// ==================== MAIN LOOP ====================

function update() {

  if (gameState !== 'playing') return;

  frameCount++;

  // Spawn enemies

  spawnTimer++;

  if (spawnTimer >= SPAWN_INTERVAL) {

    spawnTimer = 0;

    spawnEnemy();

  }

  // Update timers

  if (playerInvincible > 0) playerInvincible--;

  if (freezeTimer > 0) freezeTimer--;

  // Update player

  if (player && player.alive) player.update();

  // Update enemies

  enemies.forEach(e => { if (e.alive) e.update(); });

  // Update bullets

  bullets.forEach(b => { if (b.alive) b.update(); });

  bullets = bullets.filter(b => b.alive);

  // Update explosions

  explosions.forEach(e => e.update());

  explosions = explosions.filter(e => e.alive);

  // Update power-ups

  powerUps.forEach(p => { if (p.alive) p.update(); });

  powerUps = powerUps.filter(p => p.alive);

  // Random power-up drop

  if (frameCount % 600 === 0 && Math.random() < 0.5) {

    const types = ['shield', 'bomb', 'life', 'star'];

    const type = types[Math.floor(Math.random() * types.length)];

    const px = Math.floor(Math.random() * (COLS - 2)) * TILE;

    const py = (4 + Math.floor(Math.random() * 16)) * TILE;

    powerUps.push(new PowerUp(px, py, type));

  }

  checkWinLose();

}

function draw() {

  // Background

  ctx.fillStyle = COLORS.bg;

  ctx.fillRect(0, 0, W, H);

  // Don't draw map if not initialized

  if (!map || map.length === 0) return;

  // Draw map (bottom layer - no trees)

  for (let r = 0; r < ROWS; r++) {

    for (let c = 0; c < COLS; c++) {

      if (map[r][c] !== TILE_EMPTY && map[r][c] !== TILE_TREE) {

        drawTile(r, c);

      }

    }

  }

  // Draw power-ups

  powerUps.forEach(p => p.draw());

  // Draw tanks

  if (player && player.alive) player.draw();

  enemies.forEach(e => { if (e.alive) e.draw(); });

  // Draw bullets

  bullets.forEach(b => { if (b.alive) b.draw(); });

  // Draw trees (top layer - covers tanks)

  for (let r = 0; r < ROWS; r++) {

    for (let c = 0; c < COLS; c++) {

      drawTree(r, c);

    }

  }

  // Draw explosions (topmost)

  explosions.forEach(e => e.draw());

  // Debug: show active keys

  const activeKeys = Object.entries(keys).filter(([k, v]) => v).map(([k]) => k);

  if (activeKeys.length > 0) {

    ctx.fillStyle = 'rgba(0,0,0,0.7)';

    ctx.fillRect(2, H - 22, 200, 20);

    ctx.fillStyle = '#0f0';

    ctx.font = '10px monospace';

    ctx.textAlign = 'left';

    ctx.textBaseline = 'middle';

    ctx.fillText('Keys: ' + activeKeys.join(', '), 6, H - 12);

  }

}

function gameLoop() {

  update();

  draw();

  requestAnimationFrame(gameLoop);

}

// Start the loop

gameLoop();

</script>

</body>

</html>

点击链接查看更多资源:


科技工作坊 · 让每个孩子都有机会走进科技

喜欢就点个吧↓
基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-07 02:09:06 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/838544.html
  2. 运行时间 : 0.176523s [ 吞吐率:5.66req/s ] 内存消耗:5,378.64kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=ba959484420e8ac58e1a9576caa97244
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000553s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000823s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000293s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000230s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000462s ]
  6. SELECT * FROM `set` [ RunTime:0.000234s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000498s ]
  8. SELECT * FROM `article` WHERE `id` = 838544 LIMIT 1 [ RunTime:0.000673s ]
  9. UPDATE `article` SET `lasttime` = 1783361346 WHERE `id` = 838544 [ RunTime:0.006291s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.010515s ]
  11. SELECT * FROM `article` WHERE `id` < 838544 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.007552s ]
  12. SELECT * FROM `article` WHERE `id` > 838544 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000515s ]
  13. SELECT * FROM `article` WHERE `id` < 838544 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.006201s ]
  14. SELECT * FROM `article` WHERE `id` < 838544 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.024897s ]
  15. SELECT * FROM `article` WHERE `id` < 838544 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.007334s ]
0.178255s