<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  <title>清脆打砖块 - Crisp Breakout</title>
  <style>
    :root {
      --bg-dark: #0f1423;
      --panel-bg: rgba(26, 32, 53, 0.75);
      --panel-border: rgba(255, 255, 255, 0.12);
      --accent: #00f2ff;
      --accent-glow: rgba(0, 242, 255, 0.4);
      --gold: #ffd166;
      --danger: #ff477e;
      --text-main: #f0f4ff;
      --text-dim: #8a99b5;
    }

    * {
      box-sizing: border-box;
      user-select: none;
      -webkit-user-select: none;
      margin: 0;
      padding: 0;
    }

    body {
      background: radial-gradient(circle at center 30%, #171e36 0%, var(--bg-dark) 100%);
      color: var(--text-main);
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      overflow: hidden;
      touch-action: none;
    }

    .game-wrapper {
      position: relative;
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 12px;
      gap: 10px;
    }

    /* 顶部仪表盘 */
    .hud-bar {
      width: 100%;
      max-width: 800px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      background: var(--panel-bg);
      backdrop-filter: blur(12px);
      border: 1px solid var(--panel-border);
      border-radius: 14px;
      padding: 8px 18px;
      box-shadow: 0 8px 24px rgba(0,0,0,0.3);
    }

    .stat-item {
      display: flex;
      flex-direction: column;
      align-items: center;
      min-width: 70px;
    }

    .stat-label {
      font-size: 11px;
      font-weight: 600;
      color: var(--text-dim);
      letter-spacing: 1px;
      text-transform: uppercase;
    }

    .stat-value {
      font-size: 20px;
      font-weight: 800;
      color: #fff;
      text-shadow: 0 0 10px var(--accent-glow);
    }

    .stat-value.score {
      color: var(--accent);
    }

    .stat-value.combo {
      color: var(--gold);
      transition: transform 0.1s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    }

    .combo-pop {
      transform: scale(1.35);
    }

    .lives-container {
      display: flex;
      gap: 5px;
      margin-top: 2px;
    }

    .heart-icon {
      width: 16px;
      height: 16px;
      fill: var(--danger);
      filter: drop-shadow(0 0 4px rgba(255, 71, 126, 0.6));
      transition: all 0.3s ease;
    }

    .heart-icon.lost {
      fill: #3b445c;
      filter: none;
      transform: scale(0.8);
      opacity: 0.5;
    }

    /* 画布容器 */
    .canvas-container {
      position: relative;
      border-radius: 16px;
      overflow: hidden;
      box-shadow: 0 16px 40px rgba(0,0,0,0.5), inset 0 0 0 1px rgba(255,255,255,0.1);
      background: rgba(10, 14, 26, 0.95);
    }

    #gameCanvas {
      display: block;
      cursor: none;
    }

    /* 浮动操作按钮 */
    .controls-hint {
      display: flex;
      gap: 16px;
      font-size: 12px;
      color: var(--text-dim);
      align-items: center;
      margin-top: 4px;
    }

    .key-badge {
      display: inline-block;
      padding: 2px 7px;
      background: rgba(255, 255, 255, 0.08);
      border: 1px solid rgba(255, 255, 255, 0.15);
      border-radius: 5px;
      font-family: monospace;
      font-size: 11px;
      color: var(--text-main);
    }

    .btn-icon {
      background: rgba(255, 255, 255, 0.08);
      border: 1px solid rgba(255, 255, 255, 0.15);
      color: var(--text-main);
      padding: 6px 12px;
      border-radius: 8px;
      cursor: pointer;
      font-size: 12px;
      display: inline-flex;
      align-items: center;
      gap: 6px;
      transition: all 0.2s;
    }

    .btn-icon:hover {
      background: rgba(255, 255, 255, 0.18);
      border-color: var(--accent);
      color: #fff;
    }

    /* 遮罩覆盖层 (开始/暂停/结束) */
    .overlay {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(10, 14, 26, 0.82);
      backdrop-filter: blur(8px);
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      opacity: 1;
      transition: opacity 0.25s ease;
      z-index: 10;
    }

    .overlay.hidden {
      opacity: 0;
      pointer-events: none;
    }

    .overlay-title {
      font-size: 38px;
      font-weight: 900;
      letter-spacing: 2px;
      margin-bottom: 8px;
      background: linear-gradient(135deg, #00f2ff, #7000ff);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      text-shadow: 0 0 30px rgba(0, 242, 255, 0.3);
    }

    .overlay-sub {
      font-size: 14px;
      color: var(--text-dim);
      margin-bottom: 24px;
      text-align: center;
      line-height: 1.6;
      max-width: 80%;
    }

    .play-btn {
      background: linear-gradient(135deg, #00d2ff 0%, #0077ff 100%);
      color: #fff;
      border: none;
      font-size: 16px;
      font-weight: 700;
      padding: 12px 36px;
      border-radius: 30px;
      cursor: pointer;
      box-shadow: 0 6px 20px rgba(0, 140, 255, 0.4);
      transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    }

    .play-btn:hover {
      transform: translateY(-2px) scale(1.04);
      box-shadow: 0 8px 25px rgba(0, 180, 255, 0.6);
    }

    .play-btn:active {
      transform: translateY(1px) scale(0.98);
    }

    /* 道具图示栏 */
    .powerup-hints {
      display: flex;
      gap: 10px;
      margin-top: 18px;
      font-size: 11px;
      color: var(--text-dim);
    }
    .p-tag {
      display: flex;
      align-items: center;
      gap: 4px;
      background: rgba(255,255,255,0.05);
      padding: 4px 8px;
      border-radius: 6px;
    }
    .p-dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
    }
  </style>
</head>
<body>

  <div class="game-wrapper">
    <!-- 顶部状态栏 -->
    <div class="hud-bar">
      <div class="stat-item">
        <span class="stat-label">关卡 / Level</span>
        <span class="stat-value" id="hudLevel">1</span>
      </div>
      <div class="stat-item">
        <span class="stat-label">得分 / Score</span>
        <span class="stat-value score" id="hudScore">0</span>
      </div>
      <div class="stat-item">
        <span class="stat-label">连击 / Combo</span>
        <span class="stat-value combo" id="hudCombo">x0</span>
      </div>
      <div class="stat-item">
        <span class="stat-label">生命 / Lives</span>
        <div class="lives-container" id="hudLives">
          <!-- 生成心形图标 -->
        </div>
      </div>
      <div class="stat-item">
        <button class="btn-icon" id="soundToggleBtn">🔊 音效开</button>
      </div>
    </div>

    <!-- 游戏画布与覆盖层 -->
    <div class="canvas-container" id="canvasContainer">
      <canvas id="gameCanvas" width="800" height="600"></canvas>

      <!-- 初始/暂停/结束遮罩 -->
      <div class="overlay" id="gameOverlay">
        <h1 class="overlay-title" id="overlayTitle">清脆打砖块</h1>
        <p class="overlay-sub" id="overlayDesc">
          体验纯正清脆的晶体敲击与连击音阶<br>
          移动鼠标或按 [A / D / 方向键] 控制挡板，[空格] 发球
        </p>
        <button class="play-btn" id="overlayBtn">开始游戏</button>

        <div class="powerup-hints">
          <div class="p-tag"><span class="p-dot" style="background:#00f2ff"></span> 三重分身球</div>
          <div class="p-tag"><span class="p-dot" style="background:#ff3366"></span> 穿透激光火球</div>
          <div class="p-tag"><span class="p-dot" style="background:#00ff88"></span> 加宽挡板</div>
          <div class="p-tag"><span class="p-dot" style="background:#ffbb00"></span> 磁力吸附</div>
        </div>
      </div>
    </div>

    <!-- 底部控制提示 -->
    <div class="controls-hint">
      <span><span class="key-badge">鼠标</span> 全页面跟随 / <span class="key-badge">A / D</span> 移动</span>
      <span><span class="key-badge">空格 / 单击</span> 发球 / 射击</span>
      <span><span class="key-badge">P</span> 暂停</span>
      <span><span class="key-badge">R</span> 重玩</span>
    </div>
  </div>

  <script src="/blog/assets/leaderboard.js"></script>
  <script>
    /* =========================================================================
       1. 清脆 Web Audio 原生音效引擎 (Synthesized Crisp Sound FX)
       ========================================================================= */
    class CrispSoundEngine {
      constructor() {
        this.ctx = null;
        this.enabled = true;
        // 五度音阶/大调音阶基频序列（连击时逐级上升产生清脆优美的旋律感）
        this.comboPitches = [
          523.25, // C5
          587.33, // D5
          659.25, // E5
          783.99, // G5
          880.00, // A5
          1046.50, // C6
          1174.66, // D6
          1318.51, // E6
          1567.98, // G6
          1760.00, // A6
          2093.00  // C7 (极清脆高音)
        ];
      }

      initContext() {
        if (!this.ctx) {
          const AudioContext = window.AudioContext || window.webkitAudioContext;
          this.ctx = new AudioContext();
        }
        if (this.ctx && this.ctx.state === 'suspended') {
          this.ctx.resume();
        }
      }

      // 1.1 砖块击碎/击中音效 (Crystal Hit) - 带纯净正弦波与清脆高频谐波
      playBrickHit(combo = 0, isHard = false) {
        if (!this.enabled) return;
        this.initContext();
        if (!this.ctx) return;

        const now = this.ctx.currentTime;
        const pitchIndex = Math.min(combo, this.comboPitches.length - 1);
        const baseFreq = this.comboPitches[pitchIndex];

        // 主振荡器 (正弦波，快速衰减出玻璃/水晶清脆感)
        const osc = this.ctx.createOscillator();
        const gain = this.ctx.createGain();
        osc.type = isHard ? 'triangle' : 'sine';
        osc.frequency.setValueAtTime(baseFreq, now);
        // 微小音调滑降，带来物理撞击真实感
        osc.frequency.exponentialRampToValueAtTime(baseFreq * 0.96, now + 0.08);

        gain.gain.setValueAtTime(0.32, now);
        gain.gain.exponentialRampToValueAtTime(0.0001, now + (isHard ? 0.06 : 0.12));

        // 谐波振荡器 (高音清脆叮音)
        const chime = this.ctx.createOscillator();
        const chimeGain = this.ctx.createGain();
        chime.type = 'sine';
        chime.frequency.setValueAtTime(baseFreq * 2.76, now);
        chimeGain.gain.setValueAtTime(0.18, now);
        chimeGain.gain.exponentialRampToValueAtTime(0.0001, now + 0.05);

        osc.connect(gain);
        gain.connect(this.ctx.destination);
        chime.connect(chimeGain);
        chimeGain.connect(this.ctx.destination);

        osc.start(now);
        osc.stop(now + 0.13);
        chime.start(now);
        chime.stop(now + 0.06);
      }

      // 1.2 挡板反弹音效 (Paddle Bounce) - 弹性木质/质感敲击
      playPaddleHit(offsetFactor = 0) {
        if (!this.enabled) return;
        this.initContext();
        if (!this.ctx) return;

        const now = this.ctx.currentTime;
        const osc = this.ctx.createOscillator();
        const gain = this.ctx.createGain();

        // 根据击中挡板位置微调音高
        const freq = 340 + Math.abs(offsetFactor) * 120;
        osc.type = 'triangle';
        osc.frequency.setValueAtTime(freq * 1.5, now);
        osc.frequency.exponentialRampToValueAtTime(freq, now + 0.04);

        gain.gain.setValueAtTime(0.28, now);
        gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.08);

        osc.connect(gain);
        gain.connect(this.ctx.destination);

        osc.start(now);
        osc.stop(now + 0.09);
      }

      // 1.3 墙壁反弹音效 (Wall Tap) - 轻柔短促高音
      playWallHit() {
        if (!this.enabled) return;
        this.initContext();
        if (!this.ctx) return;

        const now = this.ctx.currentTime;
        const osc = this.ctx.createOscillator();
        const gain = this.ctx.createGain();
        osc.type = 'sine';
        osc.frequency.setValueAtTime(800, now);
        osc.frequency.exponentialRampToValueAtTime(600, now + 0.03);

        gain.gain.setValueAtTime(0.12, now);
        gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.04);

        osc.connect(gain);
        gain.connect(this.ctx.destination);
        osc.start(now);
        osc.stop(now + 0.05);
      }

      // 1.4 拾取道具音效 (Powerup Collect) - 清脆上升琶音
      playPowerup() {
        if (!this.enabled) return;
        this.initContext();
        if (!this.ctx) return;

        const now = this.ctx.currentTime;
        const notes = [587.33, 783.99, 1174.66, 1567.98];
        notes.forEach((freq, i) => {
          const osc = this.ctx.createOscillator();
          const gain = this.ctx.createGain();
          const t = now + i * 0.04;
          osc.type = 'sine';
          osc.frequency.setValueAtTime(freq, t);
          gain.gain.setValueAtTime(0.2, t);
          gain.gain.exponentialRampToValueAtTime(0.0001, t + 0.1);
          osc.connect(gain);
          gain.connect(this.ctx.destination);
          osc.start(t);
          osc.stop(t + 0.11);
        });
      }

      // 1.5 爆炸/穿透音效 (Explosion / Laser Fire)
      playExplode() {
        if (!this.enabled) return;
        this.initContext();
        if (!this.ctx) return;

        const now = this.ctx.currentTime;
        const osc = this.ctx.createOscillator();
        const gain = this.ctx.createGain();
        osc.type = 'sawtooth';
        osc.frequency.setValueAtTime(280, now);
        osc.frequency.exponentialRampToValueAtTime(40, now + 0.2);

        gain.gain.setValueAtTime(0.3, now);
        gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.22);

        osc.connect(gain);
        gain.connect(this.ctx.destination);
        osc.start(now);
        osc.stop(now + 0.23);
      }

      // 1.6 激光发射
      playLaser() {
        if (!this.enabled) return;
        this.initContext();
        if (!this.ctx) return;

        const now = this.ctx.currentTime;
        const osc = this.ctx.createOscillator();
        const gain = this.ctx.createGain();
        osc.type = 'sawtooth';
        osc.frequency.setValueAtTime(1400, now);
        osc.frequency.exponentialRampToValueAtTime(300, now + 0.08);

        gain.gain.setValueAtTime(0.15, now);
        gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.09);

        osc.connect(gain);
        gain.connect(this.ctx.destination);
        osc.start(now);
        osc.stop(now + 0.09);
      }

      // 1.7 掉球扣命音效 (Ball Lost)
      playBallLost() {
        if (!this.enabled) return;
        this.initContext();
        if (!this.ctx) return;

        const now = this.ctx.currentTime;
        const osc = this.ctx.createOscillator();
        const gain = this.ctx.createGain();
        osc.type = 'sine';
        osc.frequency.setValueAtTime(380, now);
        osc.frequency.exponentialRampToValueAtTime(110, now + 0.35);

        gain.gain.setValueAtTime(0.35, now);
        gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.38);

        osc.connect(gain);
        gain.connect(this.ctx.destination);
        osc.start(now);
        osc.stop(now + 0.4);
      }

      // 1.8 关卡胜利音效 (Stage Clear)
      playLevelWin() {
        if (!this.enabled) return;
        this.initContext();
        if (!this.ctx) return;

        const now = this.ctx.currentTime;
        const notes = [523.25, 659.25, 783.99, 1046.5, 1318.51, 1567.98, 2093.0];
        notes.forEach((freq, idx) => {
          const t = now + idx * 0.07;
          const osc = this.ctx.createOscillator();
          const gain = this.ctx.createGain();
          osc.type = 'triangle';
          osc.frequency.setValueAtTime(freq, t);
          gain.gain.setValueAtTime(0.25, t);
          gain.gain.exponentialRampToValueAtTime(0.0001, t + 0.25);
          osc.connect(gain);
          gain.connect(this.ctx.destination);
          osc.start(t);
          osc.stop(t + 0.26);
        });
      }
    }

    /* =========================================================================
       2. 游戏常量与配置
       ========================================================================= */
    const CANVAS_WIDTH = 800;
    const CANVAS_HEIGHT = 600;

    const BRICK_COLORS = {
      1: { fill: '#00f2ff', glow: '#00f2ff', points: 10, name: 'cyan' },
      2: { fill: '#7000ff', glow: '#9933ff', points: 20, name: 'purple' },
      3: { fill: '#ff0077', glow: '#ff3388', points: 30, name: 'pink' },
      4: { fill: '#ffd166', glow: '#ffe082', points: 40, name: 'gold' },
      5: { fill: '#06d6a0', glow: '#48ebb4', points: 50, name: 'green' },
      bomb: { fill: '#ff3b30', glow: '#ff6961', points: 25, name: 'bomb' },
      metal: { fill: '#8e9aaf', glow: '#c5d3e8', points: 0, name: 'metal' }
    };

    const POWERUP_TYPES = {
      MULTI_BALL: { id: 'multi', color: '#00f2ff', symbol: '●●', label: '分身球' },
      LASER_BALL: { id: 'laser', color: '#ff3366', symbol: '⚡', label: '穿透火球' },
      WIDE_PADDLE: { id: 'wide', color: '#00ff88', symbol: '↔', label: '加宽' },
      MAGNET: { id: 'magnet', color: '#ffbb00', symbol: '🧲', label: '磁力吸附' },
      GUN: { id: 'gun', color: '#e056fd', symbol: '▲▲', label: '激光炮' },
      LIFE: { id: 'life', color: '#ff477e', symbol: '♥', label: '+1生命' }
    };

    /* 关卡布局数据定义 (1:单层, 2:双层, 3:三层, B:炸弹, M:金属不可破, P:道具高概率砖) */
    const LEVEL_MAPS = [
      // Level 1: 经典平铺拱门
      [
        "  1111111111  ",
        " 222222222222 ",
        " 333333333333 ",
        "  4444444444  ",
        "   55555555   "
      ],
      // Level 2: 菱形与炸弹核心
      [
        "     4444     ",
        "   33333333   ",
        "  222B22B222  ",
        " 111111111111 ",
        "  222B22B222  ",
        "   33333333   ",
        "     4444     "
      ],
      // Level 3: 城堡要塞 (含金属砖)
      [
        " M 22 M 22 M ",
        " M 33 M 33 M ",
        " 11111111111 ",
        " 22B22222B22 ",
        " 33333333333 ",
        " M  44444  M "
      ],
      // Level 4: 心形阵列
      [
        "  222   222  ",
        " 33333 33333 ",
        " 44444444444 ",
        " 111B111B111 ",
        "  222222222  ",
        "   3333333   ",
        "    44444    ",
        "     555     ",
        "      1      "
      ],
      // Level 5: 终极矩阵 (多重耐久+炸弹连环)
      [
        " 555555555555 ",
        " 4B44444444B4 ",
        " 333333333333 ",
        " 22M222222M22 ",
        " 111111111111 ",
        " 333B3333B333 ",
        " 444444444444 "
      ]
    ];

    /* =========================================================================
       3. 核心游戏引擎与物理系统
       ========================================================================= */
    class BreakoutGame {
      constructor() {
        this.canvas = document.getElementById('gameCanvas');
        this.ctx = this.canvas.getContext('2d');
        this.sound = new CrispSoundEngine();

        // 状态变量
        this.state = 'START'; // 'START' | 'PLAYING' | 'PAUSED' | 'GAMEOVER' | 'VICTORY'
        this.level = 1;
        this.score = 0;
        this.lives = 3;
        this.maxLives = 3;
        this.combo = 0;
        this.comboTimer = 0;
        this.arenaStartedAt = 0;
        this.arenaSubmitted = false;
        this.arenaEligible = true;

        // 挡板
        this.paddle = {
          x: CANVAS_WIDTH / 2 - 60,
          y: CANVAS_HEIGHT - 35,
          width: 120,
          baseWidth: 120,
          height: 14,
          speed: 8.5,
          color: '#00f2ff',
          hasMagnet: false,
          hasGun: false,
          gunCooldown: 0
        };

        // 球列表 (支持多球分身)
        this.balls = [];
        // 砖块列表
        this.bricks = [];
        // 掉落道具列表
        this.powerups = [];
        // 粒子特效
        this.particles = [];
        // 漂浮数字特效 (Combo / Points)
        this.floatingTexts = [];
        // 激光子弹
        this.bullets = [];

        // 屏幕震动
        this.screenShake = 0;

        // 键盘按键追踪
        this.keys = { left: false, right: false, space: false };

        // 初始化
        this.initDOM();
        this.initEvents();
        this.loadLevel(this.level);
        this.render();

        // 提供全局自动化接口，便于外部或测试
        window.game = this;
      }

      initDOM() {
        this.hudLevel = document.getElementById('hudLevel');
        this.hudScore = document.getElementById('hudScore');
        this.hudCombo = document.getElementById('hudCombo');
        this.hudLives = document.getElementById('hudLives');
        this.overlay = document.getElementById('gameOverlay');
        this.overlayTitle = document.getElementById('overlayTitle');
        this.overlayDesc = document.getElementById('overlayDesc');
        this.overlayBtn = document.getElementById('overlayBtn');
        this.soundToggleBtn = document.getElementById('soundToggleBtn');

        this.updateLivesDisplay();
      }

      updateLivesDisplay() {
        this.hudLives.innerHTML = '';
        for (let i = 0; i < this.maxLives; i++) {
          const heart = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
          heart.setAttribute('viewBox', '0 0 24 24');
          heart.classList.add('heart-icon');
          if (i >= this.lives) {
            heart.classList.add('lost');
          }
          heart.innerHTML = '<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>';
          this.hudLives.appendChild(heart);
        }
      }

      initEvents() {
        // 鼠标控制挡板：监听整个窗口，指针离开画布后仍能稳定跟随到边缘。
        window.addEventListener('mousemove', (e) => {
          if (this.state !== 'PLAYING' && this.state !== 'START') return;
          const rect = this.canvas.getBoundingClientRect();
          const scaleX = CANVAS_WIDTH / rect.width;
          const clampedClientX = Math.max(rect.left, Math.min(rect.right, e.clientX));
          const mouseX = (clampedClientX - rect.left) * scaleX;
          this.paddle.x = Math.max(0, Math.min(CANVAS_WIDTH - this.paddle.width, mouseX - this.paddle.width / 2));
        });

        // 点击发球/射击
        this.canvas.addEventListener('mousedown', () => {
          this.sound.initContext();
          this.launchBallOrShoot();
        });

        // 触摸支持
        this.canvas.addEventListener('touchmove', (e) => {
          e.preventDefault();
          if (this.state !== 'PLAYING' && this.state !== 'START') return;
          const rect = this.canvas.getBoundingClientRect();
          const scaleX = CANVAS_WIDTH / rect.width;
          const touchX = (e.touches[0].clientX - rect.left) * scaleX;
          this.paddle.x = Math.max(0, Math.min(CANVAS_WIDTH - this.paddle.width, touchX - this.paddle.width / 2));
        }, { passive: false });

        this.canvas.addEventListener('touchstart', (e) => {
          this.sound.initContext();
          this.launchBallOrShoot();
        });

        // 键盘事件
        window.addEventListener('keydown', (e) => {
          if (e.code === 'KeyA' || e.code === 'ArrowLeft') this.keys.left = true;
          if (e.code === 'KeyD' || e.code === 'ArrowRight') this.keys.right = true;
          if (e.code === 'Space') {
            e.preventDefault();
            this.sound.initContext();
            this.launchBallOrShoot();
          }
          if (e.code === 'KeyP') {
            this.togglePause();
          }
          if (e.code === 'KeyR') {
            this.restartGame();
          }
        });

        window.addEventListener('keyup', (e) => {
          if (e.code === 'KeyA' || e.code === 'ArrowLeft') this.keys.left = false;
          if (e.code === 'KeyD' || e.code === 'ArrowRight') this.keys.right = false;
        });

        // 遮罩层按钮
        this.overlayBtn.addEventListener('click', (e) => {
          e.stopPropagation();
          this.sound.initContext();
          if (this.state === 'START' || this.state === 'GAMEOVER' || this.state === 'VICTORY') {
            this.restartGame();
            this.launchBallOrShoot();
          } else if (this.state === 'PAUSED') {
            this.togglePause();
          }
        });

        // 音效开关
        this.soundToggleBtn.addEventListener('click', () => {
          this.sound.enabled = !this.sound.enabled;
          this.soundToggleBtn.textContent = this.sound.enabled ? '🔊 音效开' : '🔇 音效关';
          if (this.sound.enabled) this.sound.initContext();
        });
      }

      loadLevel(lvl) {
        this.level = lvl;
        this.hudLevel.textContent = this.level;
        this.bricks = [];
        this.powerups = [];
        this.bullets = [];
        this.resetPaddle();
        this.resetBalls();

        const mapIndex = (lvl - 1) % LEVEL_MAPS.length;
        const map = LEVEL_MAPS[mapIndex];

        const brickRows = map.length;
        const brickCols = map[0].length;
        const padding = 6;
        const totalPaddingX = padding * (brickCols + 1);
        const brickWidth = (CANVAS_WIDTH - totalPaddingX) / brickCols;
        const brickHeight = 22;
        const startY = 60;

        for (let r = 0; r < brickRows; r++) {
          const rowStr = map[r];
          for (let c = 0; c < brickCols; c++) {
            const char = rowStr[c] || ' ';
            if (char === ' ') continue;

            const bx = padding + c * (brickWidth + padding);
            const by = startY + r * (brickHeight + padding);

            let type = 1;
            let hp = 1;
            let isBomb = false;
            let isMetal = false;

            if (char >= '1' && char <= '5') {
              hp = parseInt(char);
              type = hp;
            } else if (char === 'B') {
              isBomb = true;
              type = 'bomb';
              hp = 1;
            } else if (char === 'M') {
              isMetal = true;
              type = 'metal';
              hp = 999;
            }

            this.bricks.push({
              x: bx,
              y: by,
              width: brickWidth,
              height: brickHeight,
              type: type,
              hp: hp,
              maxHp: hp,
              isBomb: isBomb,
              isMetal: isMetal,
              shatterScale: 1
            });
          }
        }
      }

      resetPaddle() {
        // 第一关稍宽，给新玩家更多容错；后续关卡恢复标准宽度。
        this.paddle.width = this.level === 1 ? 144 : this.paddle.baseWidth;
        this.paddle.x = CANVAS_WIDTH / 2 - this.paddle.width / 2;
        this.paddle.hasMagnet = false;
        this.paddle.hasGun = false;
      }

      resetBalls() {
        this.balls = [{
          x: this.paddle.x + this.paddle.width / 2,
          y: this.paddle.y - 9,
          radius: 7,
          vx: 0,
          vy: 0,
          // 第一关由原 6.8 降至 5.2，之后平滑递增，避免一开局就过难。
          speed: 5.2 + (this.level - 1) * 0.55,
          stuck: true,
          isLaser: false,
          wallBounceCount: 0, // 侧墙反弹防发呆计数器
          trail: []
        }];
      }

      // 规范化球速并强制保证最小垂直分量，彻底根除“极度水平/等待时间过长”的假死问题
      normalizeBallVelocity(ball) {
        const currentSpeed = ball.speed || 7.0;
        const minVy = currentSpeed * 0.38; // 保证至少拥有 38% 的垂直速度 (约 <= 67° 倾斜角)

        let vx = ball.vx;
        let vy = ball.vy;

        // 如果垂直速度过小，强制保底并借用横向速度
        if (Math.abs(vy) < minVy) {
          vy = (vy >= 0 ? 1 : -1) * minVy;
          const maxVx = Math.sqrt(Math.max(0.1, currentSpeed * currentSpeed - vy * vy));
          vx = (vx >= 0 ? 1 : -1) * maxVx;
        }

        // 维持总速度恒定
        const mag = Math.hypot(vx, vy);
        if (mag > 0.001) {
          ball.vx = (vx / mag) * currentSpeed;
          ball.vy = (vy / mag) * currentSpeed;
        }
      }

      launchBallOrShoot() {
        if (this.state === 'START') {
          this.state = 'PLAYING';
          this.overlay.classList.add('hidden');
        }

        // 发射粘在挡板上的球
        let launched = false;
        this.balls.forEach(ball => {
          if (ball.stuck) {
            ball.stuck = false;
            // 向上自然发球，带微小的随机偏角 (约 ±15°)
            const tiltAngle = (Math.random() - 0.5) * 0.5; // -0.25 ~ +0.25 弧度
            ball.vx = ball.speed * Math.sin(tiltAngle);
            ball.vy = -ball.speed * Math.cos(tiltAngle);
            this.normalizeBallVelocity(ball);
            launched = true;
          }
        });
        if (launched) {
          if (!this.arenaStartedAt) {
            this.arenaStartedAt = performance.now();
            AdnovelArena.start('arcade');
          }
          this.sound.playPaddleHit(0);
        }

        // 如果拥有激光炮且没有刚发球，则发射子弹
        if (this.paddle.hasGun && this.paddle.gunCooldown <= 0) {
          this.fireLaserGun();
        }
      }

      fireLaserGun() {
        this.paddle.gunCooldown = 15; // 射击冷却
        this.sound.playLaser();
        const y = this.paddle.y - 4;
        this.bullets.push({ x: this.paddle.x + 8, y: y, vy: -12, radius: 3 });
        this.bullets.push({ x: this.paddle.x + this.paddle.width - 8, y: y, vy: -12, radius: 3 });

        // 枪口微光粒子
        this.createSparks(this.paddle.x + 8, y, '#e056fd', 4);
        this.createSparks(this.paddle.x + this.paddle.width - 8, y, '#e056fd', 4);
      }

      togglePause() {
        if (this.state === 'PLAYING') {
          this.arenaEligible = false;
          this.state = 'PAUSED';
          this.overlayTitle.textContent = '游戏暂停';
          this.overlayDesc.textContent = '点击按钮或按 [P] 键继续游戏';
          this.overlayBtn.textContent = '继续';
          this.overlay.classList.remove('hidden');
        } else if (this.state === 'PAUSED') {
          this.state = 'PLAYING';
          this.overlay.classList.add('hidden');
        }
      }

      restartGame() {
        this.state = 'PLAYING';
        this.score = 0;
        this.lives = 3;
        this.combo = 0;
        this.arenaStartedAt = 0;
        this.arenaSubmitted = false;
        this.arenaEligible = true;
        this.hudScore.textContent = '0';
        this.hudCombo.textContent = 'x0';
        this.updateLivesDisplay();
        this.overlay.classList.add('hidden');
        this.loadLevel(1);
      }

      // ================= 粒子与特效系统 =================
      createSparks(x, y, color, count = 10, speedMult = 1) {
        for (let i = 0; i < count; i++) {
          const angle = Math.random() * Math.PI * 2;
          const speed = (Math.random() * 4 + 1.5) * speedMult;
          this.particles.push({
            x: x,
            y: y,
            vx: Math.cos(angle) * speed,
            vy: Math.sin(angle) * speed,
            size: Math.random() * 3.5 + 1.5,
            color: color,
            alpha: 1,
            decay: Math.random() * 0.03 + 0.02,
            type: 'spark'
          });
        }
      }

      createGlassShards(brick) {
        const shardCount = 8;
        const color = BRICK_COLORS[brick.type] ? BRICK_COLORS[brick.type].fill : '#00f2ff';
        for (let i = 0; i < shardCount; i++) {
          const angle = Math.random() * Math.PI * 2;
          const speed = Math.random() * 5 + 2;
          this.particles.push({
            x: brick.x + brick.width * Math.random(),
            y: brick.y + brick.height * Math.random(),
            vx: Math.cos(angle) * speed,
            vy: Math.sin(angle) * speed - 2, // 向上爆开后受重力下落
            gravity: 0.15,
            size: Math.random() * 5 + 2,
            color: color,
            alpha: 1,
            rot: Math.random() * Math.PI,
            vRot: (Math.random() - 0.5) * 0.2,
            decay: 0.025,
            type: 'shard'
          });
        }
      }

      addFloatingText(text, x, y, color = '#ffd166', size = 18) {
        this.floatingTexts.push({
          text: text,
          x: x,
          y: y,
          vy: -1.8,
          alpha: 1,
          decay: 0.022,
          color: color,
          size: size
        });
      }

      spawnPowerup(x, y) {
        // 25% 概率掉落道具
        if (Math.random() > 0.28) return;

        const keys = Object.keys(POWERUP_TYPES);
        const randKey = keys[Math.floor(Math.random() * keys.length)];
        const def = POWERUP_TYPES[randKey];

        this.powerups.push({
          x: x,
          y: y,
          vy: 2.2,
          def: def,
          width: 28,
          height: 28,
          rot: 0
        });
      }

      applyPowerup(p) {
        this.sound.playPowerup();
        this.addFloatingText(p.def.label, p.x, p.y, p.def.color, 16);
        this.createSparks(p.x, p.y, p.def.color, 15);

        switch (p.def.id) {
          case 'multi':
            // 复制出两个新球，角度散开
            const currentBalls = [...this.balls];
            currentBalls.forEach(b => {
              for (let i = -1; i <= 1; i += 2) {
                const angle = Math.atan2(b.vy, b.vx) + i * 0.45;
                const spd = b.speed;
                const newBall = {
                  x: b.x,
                  y: b.y,
                  radius: b.radius,
                  vx: Math.cos(angle) * spd,
                  vy: Math.sin(angle) * spd,
                  speed: spd,
                  stuck: false,
                  isLaser: b.isLaser,
                  wallBounceCount: 0,
                  trail: []
                };
                this.normalizeBallVelocity(newBall);
                this.balls.push(newBall);
              }
            });
            break;

          case 'laser':
            // 火球穿透模式
            this.balls.forEach(b => b.isLaser = true);
            setTimeout(() => {
              this.balls.forEach(b => b.isLaser = false);
            }, 8000);
            break;

          case 'wide':
            this.paddle.width = Math.min(CANVAS_WIDTH * 0.4, this.paddle.baseWidth * 1.6);
            setTimeout(() => {
              this.paddle.width = this.paddle.baseWidth;
            }, 10000);
            break;

          case 'magnet':
            this.paddle.hasMagnet = true;
            setTimeout(() => {
              this.paddle.hasMagnet = false;
            }, 12000);
            break;

          case 'gun':
            this.paddle.hasGun = true;
            setTimeout(() => {
              this.paddle.hasGun = false;
            }, 9000);
            break;

          case 'life':
            if (this.lives < this.maxLives) {
              this.lives++;
              this.updateLivesDisplay();
            }
            break;
        }
      }

      triggerBomb(bombBrick) {
        this.sound.playExplode();
        this.screenShake = 12;
        this.createSparks(bombBrick.x + bombBrick.width / 2, bombBrick.y + bombBrick.height / 2, '#ff3b30', 30, 2);

        // 炸开周围相邻范围的所有砖块
        const radius = 90;
        const bxCenter = bombBrick.x + bombBrick.width / 2;
        const byCenter = bombBrick.y + bombBrick.height / 2;

        this.bricks.forEach(b => {
          if (b.hp <= 0 || b === bombBrick) return;
          const cx = b.x + b.width / 2;
          const cy = b.y + b.height / 2;
          const dist = Math.hypot(cx - bxCenter, cy - byCenter);
          if (dist < radius) {
            if (!b.isMetal) {
              b.hp = 0;
              this.createGlassShards(b);
              this.score += 15;
            }
          }
        });
      }

      // ================= 游戏主循环更新 =================
      update() {
        if (this.state !== 'PLAYING') return;

        // 1. 挡板键盘移动
        if (this.keys.left) {
          this.paddle.x = Math.max(0, this.paddle.x - this.paddle.speed);
        }
        if (this.keys.right) {
          this.paddle.x = Math.min(CANVAS_WIDTH - this.paddle.width, this.paddle.x + this.paddle.speed);
        }

        // 射击冷却
        if (this.paddle.gunCooldown > 0) this.paddle.gunCooldown--;

        // 连击计时器衰减
        if (this.comboTimer > 0) {
          this.comboTimer--;
          if (this.comboTimer <= 0) {
            this.combo = 0;
            this.hudCombo.textContent = 'x0';
          }
        }

        // 2. 激光子弹更新
        for (let i = this.bullets.length - 1; i >= 0; i--) {
          const bullet = this.bullets[i];
          bullet.y += bullet.vy;

          let hit = false;
          for (let j = 0; j < this.bricks.length; j++) {
            const brick = this.bricks[j];
            if (brick.hp <= 0) continue;

            if (bullet.x >= brick.x && bullet.x <= brick.x + brick.width &&
                bullet.y >= brick.y && bullet.y <= brick.y + brick.height) {
              hit = true;
              if (!brick.isMetal) {
                brick.hp--;
                this.sound.playBrickHit(this.combo, brick.hp > 0);
                if (brick.hp <= 0) {
                  this.createGlassShards(brick);
                  this.spawnPowerup(brick.x + brick.width / 2, brick.y + brick.height / 2);
                  this.score += 10;
                } else {
                  this.createSparks(bullet.x, bullet.y, '#e056fd', 4);
                }
              } else {
                this.sound.playWallHit();
              }
              break;
            }
          }

          if (hit || bullet.y < 0) {
            this.bullets.splice(i, 1);
          }
        }

        // 3. 球的物理移动与碰撞检测
        for (let i = this.balls.length - 1; i >= 0; i--) {
          const ball = this.balls[i];

          // 粘在挡板上
          if (ball.stuck) {
            ball.x = this.paddle.x + this.paddle.width / 2;
            ball.y = this.paddle.y - ball.radius - 1;
            continue;
          }

          // 轨迹记录
          ball.trail.unshift({ x: ball.x, y: ball.y, alpha: 0.6 });
          if (ball.trail.length > 8) ball.trail.pop();

          // 移动
          ball.x += ball.vx;
          ball.y += ball.vy;

          // 左右墙壁碰撞 (带连续弹墙防发呆机制)
          if (ball.x - ball.radius <= 0) {
            ball.x = ball.radius;
            ball.vx = Math.abs(ball.vx);
            ball.wallBounceCount = (ball.wallBounceCount || 0) + 1;
            if (ball.wallBounceCount >= 2) {
              // 连续撞侧墙未碰到砖块或挡板，主动倾斜增加垂直分量
              ball.vy = (ball.vy >= 0 ? 1 : -1) * Math.max(Math.abs(ball.vy), ball.speed * 0.55);
            }
            this.normalizeBallVelocity(ball);
            this.sound.playWallHit();
            this.createSparks(ball.x, ball.y, '#00f2ff', 4);
          } else if (ball.x + ball.radius >= CANVAS_WIDTH) {
            ball.x = CANVAS_WIDTH - ball.radius;
            ball.vx = -Math.abs(ball.vx);
            ball.wallBounceCount = (ball.wallBounceCount || 0) + 1;
            if (ball.wallBounceCount >= 2) {
              ball.vy = (ball.vy >= 0 ? 1 : -1) * Math.max(Math.abs(ball.vy), ball.speed * 0.55);
            }
            this.normalizeBallVelocity(ball);
            this.sound.playWallHit();
            this.createSparks(ball.x, ball.y, '#00f2ff', 4);
          }

          // 顶部碰撞
          if (ball.y - ball.radius <= 0) {
            ball.y = ball.radius;
            ball.vy = Math.abs(ball.vy);
            ball.wallBounceCount = 0;
            this.normalizeBallVelocity(ball);
            this.sound.playWallHit();
            this.createSparks(ball.x, ball.y, '#00f2ff', 4);
          }

          // 挡板碰撞
          if (
            ball.y + ball.radius >= this.paddle.y &&
            ball.y - ball.radius <= this.paddle.y + this.paddle.height &&
            ball.x >= this.paddle.x &&
            ball.x <= this.paddle.x + this.paddle.width &&
            ball.vy > 0
          ) {
            ball.wallBounceCount = 0; // 重置撞墙计数

            if (this.paddle.hasMagnet) {
              ball.stuck = true;
              this.sound.playPaddleHit(0);
              continue;
            }

            // 计算击中挡板相对中心的位置 (-1 ~ 1)
            const hitOffset = ((ball.x - this.paddle.x) / this.paddle.width) * 2 - 1;
            // 严格限制最大发射角度为 58° (约 1.01 弧度)，杜绝近乎水平飞出的情况
            const maxBounceAngle = Math.PI * 0.32;
            const bounceAngle = hitOffset * maxBounceAngle;

            const currentSpeed = ball.speed || 7.0;
            ball.vx = currentSpeed * Math.sin(bounceAngle);
            ball.vy = -currentSpeed * Math.cos(bounceAngle);

            this.normalizeBallVelocity(ball);

            this.sound.playPaddleHit(hitOffset);
            this.createSparks(ball.x, this.paddle.y, '#00f2ff', 8);

            // 击中挡板不重置连击，但给挡板微发光
            this.paddleGlow = 1;
          }

          // 砖块碰撞检测
          for (let j = 0; j < this.bricks.length; j++) {
            const brick = this.bricks[j];
            if (brick.hp <= 0) continue;

            // 简易 AABB + 圆形碰撞检测
            const nearestX = Math.max(brick.x, Math.min(ball.x, brick.x + brick.width));
            const nearestY = Math.max(brick.y, Math.min(ball.y, brick.y + brick.height));
            const deltaX = ball.x - nearestX;
            const deltaY = ball.y - nearestY;
            const distSq = deltaX * deltaX + deltaY * deltaY;

            if (distSq <= ball.radius * ball.radius) {
              // 发生碰撞！重置撞墙计数
              ball.wallBounceCount = 0;
              let isDestroyed = false;

              if (brick.isMetal) {
                this.sound.playWallHit();
                this.createSparks(nearestX, nearestY, '#8e9aaf', 6);
              } else if (brick.isBomb) {
                brick.hp = 0;
                this.triggerBomb(brick);
                isDestroyed = true;
              } else {
                brick.hp--;
                this.combo++;
                this.comboTimer = 75; // 约1.2秒连击窗口
                this.hudCombo.textContent = `x${this.combo}`;
                this.hudCombo.classList.remove('combo-pop');
                void this.hudCombo.offsetWidth; // 触发重绘
                this.hudCombo.classList.add('combo-pop');

                const points = (BRICK_COLORS[brick.type]?.points || 10) * Math.min(this.combo, 10);
                this.score += points;
                this.hudScore.textContent = this.score;

                if (this.combo >= 3) {
                  this.addFloatingText(`${this.combo} COMBO! +${points}`, brick.x + brick.width / 2, brick.y, '#ffd166');
                }

                if (brick.hp <= 0) {
                  isDestroyed = true;
                  this.sound.playBrickHit(this.combo, false);
                  this.createGlassShards(brick);
                  this.spawnPowerup(brick.x + brick.width / 2, brick.y + brick.height / 2);
                } else {
                  this.sound.playBrickHit(this.combo, true);
                  this.createSparks(nearestX, nearestY, BRICK_COLORS[brick.type].fill, 6);
                }
              }

              // 如果不是激光穿透球，则反弹
              if (!ball.isLaser || brick.isMetal) {
                // 判断从哪一侧撞击
                const overlapLeft = (ball.x + ball.radius) - brick.x;
                const overlapRight = (brick.x + brick.width) - (ball.x - ball.radius);
                const overlapTop = (ball.y + ball.radius) - brick.y;
                const overlapBottom = (brick.y + brick.height) - (ball.y - ball.radius);

                const minOverlapX = Math.min(overlapLeft, overlapRight);
                const minOverlapY = Math.min(overlapTop, overlapBottom);

                if (minOverlapX < minOverlapY) {
                  ball.vx = -ball.vx;
                } else {
                  ball.vy = -ball.vy;
                }
                this.normalizeBallVelocity(ball);
              }

              break; // 单帧只处理一个砖块撞击
            }
          }

          // 掉出底边判定
          if (ball.y - ball.radius > CANVAS_HEIGHT) {
            this.balls.splice(i, 1);
          }
        }

        // 4. 检查是否所有球都丢失
        if (this.balls.length === 0) {
          this.lives--;
          this.sound.playBallLost();
          this.updateLivesDisplay();
          this.combo = 0;
          this.hudCombo.textContent = 'x0';

          if (this.lives <= 0) {
            // 游戏结束
            this.state = 'GAMEOVER';
            this.submitArena('gameover');
            this.overlayTitle.textContent = '游戏结束';
            this.overlayDesc.textContent = `最终得分: ${this.score} (到达第 ${this.level} 关)`;
            this.overlayBtn.textContent = '再玩一次';
            this.overlay.classList.remove('hidden');
          } else {
            // 重新在挡板上生成球
            this.resetPaddle();
            this.resetBalls();
          }
        }

        // 5. 道具掉落更新
        for (let i = this.powerups.length - 1; i >= 0; i--) {
          const p = this.powerups[i];
          p.y += p.vy;
          p.rot += 0.03;

          // 挡板拾取
          if (
            p.y + p.height / 2 >= this.paddle.y &&
            p.y - p.height / 2 <= this.paddle.y + this.paddle.height &&
            p.x + p.width / 2 >= this.paddle.x &&
            p.x - p.width / 2 <= this.paddle.x + this.paddle.width
          ) {
            this.applyPowerup(p);
            this.powerups.splice(i, 1);
            continue;
          }

          // 掉出屏幕
          if (p.y > CANVAS_HEIGHT + 30) {
            this.powerups.splice(i, 1);
          }
        }

        // 6. 检查关卡是否清除 (排除不可摧毁的金属砖)
        const remainingDestructible = this.bricks.filter(b => !b.isMetal && b.hp > 0).length;
        if (remainingDestructible === 0 && this.bricks.length > 0) {
          this.sound.playLevelWin();
          if (this.level < LEVEL_MAPS.length) {
            this.loadLevel(this.level + 1);
          } else {
            // 通关胜利
            this.state = 'VICTORY';
            this.submitArena('victory');
            this.overlayTitle.textContent = '恭喜通关！';
            this.overlayDesc.textContent = `太棒了！你打通了所有关卡！\n最终得分: ${this.score}`;
            this.overlayBtn.textContent = '重新挑战';
            this.overlay.classList.remove('hidden');
          }
        }

        // 7. 粒子更新
        for (let i = this.particles.length - 1; i >= 0; i--) {
          const pt = this.particles[i];
          pt.x += pt.vx;
          pt.y += pt.vy;
          if (pt.gravity) pt.vy += pt.gravity;
          if (pt.rot !== undefined) pt.rot += pt.vRot;
          pt.alpha -= pt.decay;
          if (pt.alpha <= 0) {
            this.particles.splice(i, 1);
          }
        }

        // 8. 浮动文字更新
        for (let i = this.floatingTexts.length - 1; i >= 0; i--) {
          const ft = this.floatingTexts[i];
          ft.y += ft.vy;
          ft.alpha -= ft.decay;
          if (ft.alpha <= 0) {
            this.floatingTexts.splice(i, 1);
          }
        }

        // 9. 屏幕震动衰减
        if (this.screenShake > 0) {
          this.screenShake *= 0.85;
          if (this.screenShake < 0.2) this.screenShake = 0;
        }
      }

      // ================= 画面绘制渲染 =================
      render() {
        requestAnimationFrame(() => {
          this.update();
          this.draw();
          this.render();
        });
      }

      draw() {
        this.ctx.save();

        // 屏幕震动偏移
        if (this.screenShake > 0) {
          const ox = (Math.random() - 0.5) * this.screenShake * 2;
          const oy = (Math.random() - 0.5) * this.screenShake * 2;
          this.ctx.translate(ox, oy);
        }

        // 清空画布与背景网格
        this.ctx.fillStyle = '#0a0e1a';
        this.ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

        // 科技感背景网格线
        this.ctx.strokeStyle = 'rgba(255, 255, 255, 0.025)';
        this.ctx.lineWidth = 1;
        for (let x = 0; x < CANVAS_WIDTH; x += 40) {
          this.ctx.beginPath();
          this.ctx.moveTo(x, 0);
          this.ctx.lineTo(x, CANVAS_HEIGHT);
          this.ctx.stroke();
        }
        for (let y = 0; y < CANVAS_HEIGHT; y += 40) {
          this.ctx.beginPath();
          this.ctx.moveTo(0, y);
          this.ctx.lineTo(CANVAS_WIDTH, y);
          this.ctx.stroke();
        }

        // 绘制砖块 (带晶体质感与边缘光效)
        this.bricks.forEach(brick => {
          if (brick.hp <= 0) return;

          const colorDef = BRICK_COLORS[brick.type] || BRICK_COLORS[1];
          const x = brick.x;
          const y = brick.y;
          const w = brick.width;
          const h = brick.height;

          // 渐变填充
          const grad = this.ctx.createLinearGradient(x, y, x, y + h);
          if (brick.isMetal) {
            grad.addColorStop(0, '#a8b5c9');
            grad.addColorStop(1, '#606c88');
          } else if (brick.isBomb) {
            grad.addColorStop(0, '#ff5252');
            grad.addColorStop(1, '#b71540');
          } else {
            grad.addColorStop(0, colorDef.fill);
            grad.addColorStop(1, this.adjustBrightness(colorDef.fill, -40));
          }

          this.ctx.fillStyle = grad;
          this.ctx.beginPath();
          this.ctx.roundRect(x, y, w, h, 4);
          this.ctx.fill();

          // 顶面高光线 (清脆晶莹质感)
          this.ctx.strokeStyle = 'rgba(255, 255, 255, 0.45)';
          this.ctx.lineWidth = 1.5;
          this.ctx.beginPath();
          this.ctx.moveTo(x + 3, y + 2);
          this.ctx.lineTo(x + w - 3, y + 2);
          this.ctx.stroke();

          // 耐久多层与裂纹绘制
          if (brick.hp > 1 && !brick.isMetal) {
            this.ctx.fillStyle = 'rgba(255, 255, 255, 0.85)';
            this.ctx.font = 'bold 10px monospace';
            this.ctx.textAlign = 'center';
            this.ctx.textBaseline = 'middle';
            this.ctx.fillText(`★${brick.hp}`, x + w / 2, y + h / 2 + 1);
          } else if (brick.isBomb) {
            this.ctx.fillStyle = '#fff';
            this.ctx.font = '12px sans-serif';
            this.ctx.textAlign = 'center';
            this.ctx.textBaseline = 'middle';
            this.ctx.fillText('💣', x + w / 2, y + h / 2 + 1);
          } else if (brick.isMetal) {
            this.ctx.fillStyle = '#fff';
            this.ctx.font = '11px sans-serif';
            this.ctx.textAlign = 'center';
            this.ctx.textBaseline = 'middle';
            this.ctx.fillText('🔒', x + w / 2, y + h / 2 + 1);
          }
        });

        // 绘制激光子弹
        this.bullets.forEach(b => {
          this.ctx.fillStyle = '#e056fd';
          this.ctx.shadowColor = '#e056fd';
          this.ctx.shadowBlur = 8;
          this.ctx.fillRect(b.x - 2, b.y - 8, 4, 12);
          this.ctx.shadowBlur = 0;
        });

        // 绘制挡板 (玻璃拟态 + 光晕)
        const pGrad = this.ctx.createLinearGradient(this.paddle.x, this.paddle.y, this.paddle.x, this.paddle.y + this.paddle.height);
        pGrad.addColorStop(0, '#ffffff');
        pGrad.addColorStop(0.3, this.paddle.hasGun ? '#e056fd' : (this.paddle.hasMagnet ? '#ffd166' : '#00f2ff'));
        pGrad.addColorStop(1, '#0077ff');

        this.ctx.fillStyle = pGrad;
        this.ctx.shadowColor = this.paddle.hasGun ? '#e056fd' : '#00f2ff';
        this.ctx.shadowBlur = 14;
        this.ctx.beginPath();
        this.ctx.roundRect(this.paddle.x, this.paddle.y, this.paddle.width, this.paddle.height, 7);
        this.ctx.fill();
        this.ctx.shadowBlur = 0;

        // 挡板激光炮枪管
        if (this.paddle.hasGun) {
          this.ctx.fillStyle = '#fff';
          this.ctx.fillRect(this.paddle.x + 6, this.paddle.y - 5, 4, 6);
          this.ctx.fillRect(this.paddle.x + this.paddle.width - 10, this.paddle.y - 5, 4, 6);
        }

        // 绘制球与拖尾
        this.balls.forEach(ball => {
          // 拖尾
          ball.trail.forEach((t, idx) => {
            this.ctx.fillStyle = ball.isLaser ? `rgba(255, 51, 102, ${t.alpha * 0.4})` : `rgba(0, 242, 255, ${t.alpha * 0.4})`;
            this.ctx.beginPath();
            this.ctx.arc(t.x, t.y, ball.radius * (1 - idx * 0.08), 0, Math.PI * 2);
            this.ctx.fill();
          });

          // 球体本体 (发光水晶球)
          const bGrad = this.ctx.createRadialGradient(ball.x - 2, ball.y - 2, 1, ball.x, ball.y, ball.radius);
          bGrad.addColorStop(0, '#ffffff');
          bGrad.addColorStop(0.4, ball.isLaser ? '#ff3366' : '#00f2ff');
          bGrad.addColorStop(1, ball.isLaser ? '#990033' : '#0077cc');

          this.ctx.shadowColor = ball.isLaser ? '#ff3366' : '#00f2ff';
          this.ctx.shadowBlur = 12;
          this.ctx.fillStyle = bGrad;
          this.ctx.beginPath();
          this.ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
          this.ctx.fill();
          this.ctx.shadowBlur = 0;
        });

        // 绘制掉落道具 (浮动胶囊)
        this.powerups.forEach(p => {
          this.ctx.save();
          this.ctx.translate(p.x, p.y);
          this.ctx.rotate(Math.sin(p.rot) * 0.2);

          // 胶囊背景
          this.ctx.shadowColor = p.def.color;
          this.ctx.shadowBlur = 10;
          this.ctx.fillStyle = 'rgba(20, 28, 48, 0.85)';
          this.ctx.strokeStyle = p.def.color;
          this.ctx.lineWidth = 2;
          this.ctx.beginPath();
          this.ctx.roundRect(-p.width / 2, -p.height / 2, p.width, p.height, 8);
          this.ctx.fill();
          this.ctx.stroke();

          // 胶囊图标
          this.ctx.shadowBlur = 0;
          this.ctx.fillStyle = p.def.color;
          this.ctx.font = 'bold 13px sans-serif';
          this.ctx.textAlign = 'center';
          this.ctx.textBaseline = 'middle';
          this.ctx.fillText(p.def.symbol, 0, 1);

          this.ctx.restore();
        });

        // 绘制粒子 (火花与玻璃碎片)
        this.particles.forEach(pt => {
          this.ctx.save();
          this.ctx.globalAlpha = Math.max(0, pt.alpha);
          this.ctx.fillStyle = pt.color;
          if (pt.type === 'shard') {
            this.ctx.translate(pt.x, pt.y);
            this.ctx.rotate(pt.rot);
            this.ctx.beginPath();
            this.ctx.moveTo(-pt.size, -pt.size);
            this.ctx.lineTo(pt.size, -pt.size / 2);
            this.ctx.lineTo(0, pt.size);
            this.ctx.closePath();
            this.ctx.fill();
          } else {
            this.ctx.beginPath();
            this.ctx.arc(pt.x, pt.y, pt.size, 0, Math.PI * 2);
            this.ctx.fill();
          }
          this.ctx.restore();
        });

        // 绘制浮动连击/得分文字
        this.floatingTexts.forEach(ft => {
          this.ctx.save();
          this.ctx.globalAlpha = Math.max(0, ft.alpha);
          this.ctx.font = `900 ${ft.size}px sans-serif`;
          this.ctx.fillStyle = ft.color;
          this.ctx.textAlign = 'center';
          this.ctx.shadowColor = ft.color;
          this.ctx.shadowBlur = 8;
          this.ctx.fillText(ft.text, ft.x, ft.y);
          this.ctx.restore();
        });

        this.ctx.restore();
      }

      submitArena(outcome) {
        if (this.arenaSubmitted || !this.arenaEligible || !this.arenaStartedAt) return;
        this.arenaSubmitted = true;
        AdnovelArena.submit({
          mode: 'arcade',
          score: this.score,
          durationMs: Math.round(performance.now() - this.arenaStartedAt),
          meta: { level: this.level, outcome, paused: false }
        });
      }

      adjustBrightness(col, percent) {
        let num = parseInt(col.replace('#', ''), 16);
        let r = (num >> 16) + percent;
        let g = ((num >> 8) & 0x00FF) + percent;
        let b = (num & 0x0000FF) + percent;
        r = Math.min(255, Math.max(0, r));
        g = Math.min(255, Math.max(0, g));
        b = Math.min(255, Math.max(0, b));
        return `#${(g | (b << 8) | (r << 16)).toString(16).padStart(6, '0')}`;
      }
    }

    // 启动游戏实例
    AdnovelArena.init({ game: 'breakout', defaultMode: 'arcade', modes: { arcade: '街机模式' } });
    window.addEventListener('DOMContentLoaded', () => {
      new BreakoutGame();
    });
  </script>
</body>
</html>
