Faker走位练习器

·牛客 "网页小游戏 AI coding" 活动。

·游戏链接:http://www.silencer76.com/nowcoderToFaker/

核心玩法

·使用(左键)点击控制(右键会触发浏览器手势)角色移动,(A键)发射攻击摧毁弹道,(F键)使用闪现,冷却5秒。

·弹道有多种类型,不同难度出现概率数量均不同,随着时间进行,弹幕会增多

游戏视图

制作过程

·链接:https://www.bilibili.com/video/BV17Q6gBfE6m/

游戏代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Faker走位练习器 - 英雄联盟躲技能训练</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Arial', sans-serif;
            -webkit-user-select: none;
            -moz-user-select: none;
            user-select: none;
            -webkit-touch-callout: none;
            -webkit-tap-highlight-color: transparent;
        }
        
        body {
            background: linear-gradient(135deg, #0a0a2a, #1a1a3a);
            color: #fff;
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 20px;
            overflow-x: hidden;
        }
        
        /* 游戏主界面 */
        .game-interface {
            display: flex;
            width: 100%;
            max-width: 1400px;
            flex-direction: column;
            animation: fadeIn 0.5s ease;
        }
        
        /* 顶部控制栏 */
        .top-controls {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
            gap: 20px;
            flex-wrap: wrap;
        }
        
        .game-title {
            flex: 1;
        }
        
        .game-title h1 {
            color: #ffcc00;
            font-size: 2.8rem;
            text-shadow: 0 0 10px rgba(255, 204, 0, 0.5);
            background: linear-gradient(to right, #ffcc00, #ff9900);
            -webkit-background-clip: text;
            -moz-background-clip: text;
            background-clip: text;
            -webkit-text-fill-color: transparent;
            -moz-text-fill-color: transparent;
        }
        
        /* 紧凑游戏控制按钮 */
        .compact-controls {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            justify-content: center;
        }
        
        .compact-control-btn {
            padding: 8px 16px;
            font-size: 1rem;
            background: #2a2a5a;
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.3s;
            border: 1px solid rgba(255, 204, 0, 0.3);
            font-weight: bold;
            white-space: nowrap;
        }
        
        .compact-control-btn:hover {
            background: #3a3a7a;
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
        }
        
        .compact-start-btn {
            background: linear-gradient(45deg, #4CAF50, #2E7D32);
        }
        
        .compact-pause-btn {
            background: linear-gradient(45deg, #2196F3, #0D47A1);
        }
        
        .compact-reset-btn {
            background: linear-gradient(45deg, #f44336, #b71c1c);
        }
        
        .compact-attack-btn {
            background: linear-gradient(45deg, #9C27B0, #673AB7);
        }
        
        .compact-flash-btn {
            background: linear-gradient(45deg, #00BCD4, #0097A7);
        }
        
        .display-mode-controls {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
        }
        
        .display-btn {
            padding: 10px 20px;
            background: rgba(20, 20, 40, 0.8);
            color: white;
            border: 1px solid rgba(255, 204, 0, 0.3);
            border-radius: 6px;
            cursor: pointer;
            font-weight: bold;
            transition: all 0.3s;
        }
        
        .display-btn:hover {
            background: rgba(30, 30, 60, 0.9);
            transform: translateY(-2px);
        }
        
        .main-container {
            display: flex;
            width: 100%;
            gap: 20px;
            margin-bottom: 20px;
        }
        
        /* 左侧面板优化 - 删除了游戏控制栏 */
        .left-panel {
            flex: 0 0 220px;
            background: rgba(0, 0, 0, 0.5);
            border-radius: 10px;
            padding: 20px;
            border: 1px solid rgba(255, 204, 0, 0.3);
            display: flex;
            flex-direction: column;
            gap: 20px;
        }
        
        .game-container {
            flex: 1;
            position: relative;
            min-height: 600px;
            border-radius: 10px;
            overflow: hidden;
            box-shadow: 0 0 30px rgba(0, 0, 0, 0.7);
            border: 2px solid rgba(255, 204, 0, 0.3);
            cursor: crosshair;
            -webkit-user-drag: none;
            touch-action: none;
            background: #0d0d1a;
        }
        
        #gameCanvas {
            display: block;
            width: 100%;
            height: 100%;
            touch-action: none;
        }
        
        /* 顶部游戏状态信息 */
        .top-game-info {
            display: flex;
            align-items: center;
            gap: 15px;
            margin-bottom: 15px;
            padding: 0 10px;
            flex-wrap: wrap;
        }
        
        .current-difficulty {
            font-size: 1.4rem;
            font-weight: bold;
            padding: 8px 20px;
            border-radius: 8px;
            text-align: center;
            min-width: 180px;
            border: 2px solid;
        }
        
        .normal-difficulty {
            color: #4CAF50;
            border-color: #4CAF50;
            background: rgba(76, 175, 80, 0.1);
        }
        
        .hard-difficulty {
            color: #FF9800;
            border-color: #FF9800;
            background: rgba(255, 152, 0, 0.1);
        }
        
        .faker-difficulty {
            color: #FF0000;
            border-color: #FF0000;
            background: rgba(255, 0, 0, 0.1);
            animation: pulse 1.5s infinite;
        }
        
        /* 技能类型面板 - 可折叠 */
        .skill-list-container {
            position: relative;
        }
        
        .skill-toggle-btn {
            padding: 10px 20px;
            background: rgba(20, 20, 40, 0.8);
            color: white;
            border: 1px solid rgba(255, 204, 0, 0.3);
            border-radius: 6px;
            cursor: pointer;
            font-weight: bold;
            transition: all 0.3s;
            font-size: 1.1rem;
        }
        
        .skill-toggle-btn:hover {
            background: rgba(30, 30, 60, 0.9);
            transform: translateY(-2px);
        }
        
        .skill-list-panel {
            position: absolute;
            top: 100%;
            left: 0;
            background: rgba(0, 0, 0, 0.95);
            padding: 20px;
            border-radius: 10px;
            border: 1px solid rgba(255, 204, 0, 0.3);
            z-index: 1000;
            min-width: 300px;
            display: none;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.7);
            backdrop-filter: blur(10px);
        }
        
        .skill-list-panel.show {
            display: block;
        }
        
        .skill-list-panel h4 {
            color: #ffcc00;
            margin-bottom: 15px;
            font-size: 1.4rem;
            text-align: center;
        }
        
        .skill-items {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }
        
        .skill-item {
            display: flex;
            align-items: center;
            font-size: 1.1rem;
            padding: 8px 12px;
            background: rgba(255, 255, 255, 0.05);
            border-radius: 6px;
            transition: all 0.3s;
        }
        
        .skill-item:hover {
            background: rgba(255, 255, 255, 0.1);
            transform: translateX(5px);
        }
        
        .skill-color {
            width: 24px;
            height: 24px;
            border-radius: 50%;
            margin-right: 15px;
            flex-shrink: 0;
            border: 2px solid rgba(255, 255, 255, 0.3);
        }
        
        .skill-name {
            color: #fff;
            font-weight: bold;
            flex: 1;
        }
        
        .skill-desc {
            color: #aaa;
            font-size: 0.9rem;
            margin-left: 10px;
            font-style: italic;
        }
        
        /* 全屏样式优化 */
        body.fullscreen {
            padding: 15px;
            background: #000;
        }
        
        body.fullscreen .game-interface {
            max-width: 100%;
            height: calc(100vh - 30px);
            padding: 0;
        }
        
        body.fullscreen .top-controls {
            margin-bottom: 15px;
        }
        
        body.fullscreen .main-container {
            height: calc(100vh - 180px);
            margin-bottom: 0;
        }
        
        body.fullscreen .game-container {
            min-height: auto;
            height: 100%;
        }
        
        body.fullscreen .left-panel {
            flex: 0 0 200px;
            padding: 15px;
            gap: 15px;
        }
        
        body.fullscreen .compact-controls {
            gap: 8px;
        }
        
        body.fullscreen .compact-control-btn {
            padding: 6px 12px;
            font-size: 0.9rem;
        }
        
        /* 控制面板样式优化 */
        .control-section {
            background: rgba(20, 20, 40, 0.8);
            border-radius: 8px;
            padding: 15px;
            border: 1px solid rgba(255, 204, 0, 0.2);
        }
        
        .control-section h3 {
            color: #ffcc00;
            margin-bottom: 12px;
            font-size: 1.2rem;
            text-align: center;
        }
        
        .stats-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 12px;
            margin-bottom: 15px;
        }
        
        .stat-item {
            text-align: center;
            background: rgba(0, 0, 0, 0.3);
            padding: 12px;
            border-radius: 6px;
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        
        .stat-value {
            font-size: 1.6rem;
            font-weight: bold;
            color: #ffcc00;
            margin-bottom: 4px;
            text-shadow: 0 0 5px rgba(255, 204, 0, 0.5);
        }
        
        .stat-label {
            font-size: 0.95rem;
            color: #aaa;
        }
        
        .difficulty-section {
            display: flex;
            flex-direction: column;
            gap: 10px;
        }
        
        .difficulty-btn {
            padding: 12px;
            border: none;
            border-radius: 6px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s;
            font-size: 1.1rem;
            text-align: center;
        }
        
        .difficulty-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
        }
        
        .difficulty-btn.active {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
        }
        
        .normal-btn {
            background: #4CAF50;
            color: white;
        }
        
        .normal-btn.active {
            background: #2E7D32;
            border: 2px solid #81C784;
        }
        
        .hard-btn {
            background: #FF9800;
            color: white;
        }
        
        .hard-btn.active {
            background: #EF6C00;
            border: 2px solid #FFB74D;
        }
        
        .faker-btn {
            background: linear-gradient(45deg, #FF0000, #FF9900);
            color: white;
        }
        
        .faker-btn.active {
            background: linear-gradient(45deg, #CC0000, #CC6600);
            border: 2px solid #FF6666;
        }
        
        @keyframes pulse {
            0% { box-shadow: 0 0 5px rgba(255, 0, 0, 0.5); }
            50% { box-shadow: 0 0 20px rgba(255, 0, 0, 0.8); }
            100% { box-shadow: 0 0 5px rgba(255, 0, 0, 0.5); }
        }
        
        .combo-display {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 5rem;
            font-weight: bold;
            color: #ffcc00;
            text-shadow: 0 0 10px rgba(255, 204, 0, 0.7);
            opacity: 0;
            pointer-events: none;
            z-index: 50;
            transition: opacity 0.3s;
        }
        
        .click-indicator {
            position: absolute;
            width: 40px;
            height: 40px;
            border: 2px solid #4CAF50;
            border-radius: 50%;
            pointer-events: none;
            z-index: 20;
            opacity: 0;
            transform: translate(-50%, -50%);
            animation: clickEffect 0.5s ease-out;
        }
        
        @keyframes clickEffect {
            0% {
                transform: translate(-50%, -50%) scale(0.5);
                opacity: 0.8;
            }
            100% {
                transform: translate(-50%, -50%) scale(1.5);
                opacity: 0;
            }
        }
        
        .flash-effect {
            position: absolute;
            width: 100%;
            height: 100%;
            background: rgba(255, 255, 255, 0.7);
            pointer-events: none;
            z-index: 30;
            opacity: 0;
            transition: opacity 0.2s;
        }
        
        .game-over {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.85);
            display: none;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 100;
            backdrop-filter: blur(5px);
        }
        
        .game-over h2 {
            font-size: 3.5rem;
            color: #ffcc00;
            margin-bottom: 20px;
            text-align: center;
        }
        
        .final-score {
            font-size: 2.5rem;
            margin-bottom: 30px;
            color: white;
            text-align: center;
        }
        
        /* 鼠标瞄准指示器样式 */
        .aim-indicator {
            position: absolute;
            pointer-events: none;
            z-index: 5;
            opacity: 0;
            transition: opacity 0.2s;
        }
        
        .aim-line {
            position: absolute;
            background: rgba(255, 235, 59, 0.3);
            height: 2px;
            transform-origin: 0 0;
            pointer-events: none;
            z-index: 5;
        }
        
        .aim-circle {
            position: absolute;
            width: 25px;
            height: 25px;
            border: 2px solid #FFEB3B;
            border-radius: 50%;
            pointer-events: none;
            z-index: 5;
        }
        
        .instructions {
            background: rgba(0, 0, 0, 0.6);
            padding: 15px;
            border-radius: 10px;
            width: 100%;
            max-width: 1400px;
            border: 1px solid rgba(255, 204, 0, 0.2);
            margin-top: 0;
        }
        
        .instructions h3 {
            color: #ffcc00;
            margin-bottom: 10px;
            font-size: 1.4rem;
        }
        
        .instructions ul {
            list-style-position: inside;
            margin-left: 10px;
        }
        
        .instructions li {
            margin-bottom: 8px;
            color: #ccc;
            font-size: 1rem;
            line-height: 1.5;
        }
        
        .control-hint {
            color: #ffcc00;
            font-size: 1rem;
            margin-top: 8px;
            text-align: center;
            font-weight: bold;
        }
        
        /* 动画 */
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(20px); }
            to { opacity: 1; transform: translateY(0); }
        }
        
        /* 响应式调整 */
        @media (max-width: 1100px) {
            .main-container {
                flex-direction: column;
            }
            
            .left-panel {
                flex: none;
                width: 100%;
            }
            
            .game-container {
                min-height: 500px;
            }
            
            .top-game-info {
                justify-content: center;
                margin-top: 10px;
            }
            
            .top-controls {
                flex-direction: column;
                align-items: stretch;
            }
        }
        
        @media (max-width: 768px) {
            .display-mode-controls {
                justify-content: center;
            }
            
            .stat-value {
                font-size: 1.4rem;
            }
            
            .skill-list-panel {
                min-width: 250px;
                padding: 15px;
            }
            
            .skill-item {
                font-size: 1rem;
            }
            
            .game-title h1 {
                font-size: 2.2rem;
            }
            
            .current-difficulty {
                font-size: 1.2rem;
                min-width: 150px;
            }
        }
        
        @media (max-width: 600px) {
            body {
                padding: 10px;
            }
            
            body.fullscreen {
                padding: 10px;
            }
            
            .game-container {
                min-height: 400px;
            }
            
            .left-panel {
                padding: 12px;
            }
            
            .control-section {
                padding: 12px;
            }
            
            .compact-control-btn {
                padding: 6px 10px;
                font-size: 0.85rem;
            }
            
            .skill-toggle-btn {
                padding: 8px 15px;
                font-size: 1rem;
            }
        }
        
        @media (max-width: 480px) {
            .game-title h1 {
                font-size: 1.8rem;
            }
            
            .stat-value {
                font-size: 1.2rem;
            }
            
            .stat-label {
                font-size: 0.8rem;
            }
            
            .difficulty-btn {
                padding: 10px;
                font-size: 1rem;
            }
            
            .current-difficulty {
                font-size: 1rem;
                min-width: 120px;
                padding: 6px 12px;
            }
            
            .skill-list-panel {
                min-width: 200px;
                padding: 12px;
            }
        }
    </style>
</head>
<body>
    <!-- 游戏主界面 -->
    <div class="game-interface" id="gameInterface">
        <!-- 顶部控制栏 -->
        <div class="top-controls">
            <div class="game-title">
                <h1>FAKER 走位练习器</h1>
            </div>
            
            <!-- 紧凑游戏控制按钮 -->
            <div class="compact-controls">
                <button class="compact-control-btn compact-start-btn" id="compactStartBtn">开始</button>
                <button class="compact-control-btn compact-pause-btn" id="compactPauseBtn">暂停</button>
                <button class="compact-control-btn compact-reset-btn" id="compactResetBtn">重置</button>
                <button class="compact-control-btn compact-attack-btn" id="compactAttackBtn">攻击(A)</button>
                <button class="compact-control-btn compact-flash-btn" id="compactFlashBtn">闪现(F)</button>
            </div>
            
            <div class="display-mode-controls">
                <button class="display-btn" id="toggleFullscreenBtn">全屏模式</button>
                <button class="display-btn" id="instructionsBtn">游戏说明</button>
            </div>
        </div>
        
        <!-- 顶部游戏信息 -->
        <div class="top-game-info">
            <div class="current-difficulty normal-difficulty" id="currentDifficulty">普通难度</div>
            
            <!-- 技能类型可折叠面板 -->
            <div class="skill-list-container">
                <button class="skill-toggle-btn" id="skillToggleBtn">技能类型</button>
                <div class="skill-list-panel" id="skillListPanel">
                    <h4>技能类型说明</h4>
                    <div class="skill-items">
                        <div class="skill-item">
                            <div class="skill-color" style="background-color: #4CAF50;"></div>
                            <div class="skill-name">直线技能</div>
                            <div class="skill-desc">慢速</div>
                        </div>
                        <div class="skill-item">
                            <div class="skill-color" style="background-color: #FF9800;"></div>
                            <div class="skill-name">快速技能</div>
                            <div class="skill-desc">高速</div>
                        </div>
                        <div class="skill-item">
                            <div class="skill-color" style="background-color: #FF0000;"></div>
                            <div class="skill-name">预判技能</div>
                            <div class="skill-desc">预测走位</div>
                        </div>
                        <div class="skill-item">
                            <div class="skill-color" style="background-color: #9C27B0;"></div>
                            <div class="skill-name">范围技能</div>
                            <div class="skill-desc">AOE/范围</div>
                        </div>
                        <div class="skill-item">
                            <div class="skill-color" style="background-color: #00BCD4;"></div>
                            <div class="skill-name">追踪技能</div>
                            <div class="skill-desc">自动追踪</div>
                        </div>
                        <div class="skill-item">
                            <div class="skill-color" style="background-color: #FFEB3B;"></div>
                            <div class="skill-name">玩家攻击</div>
                            <div class="skill-desc">摧毁技能</div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        
        <div class="main-container">
            <!-- 左侧控制面板 -->
            <div class="left-panel">
                <div class="control-section">
                    <h3>游戏状态</h3>
                    <div class="stats-grid">
                        <div class="stat-item">
                            <div class="stat-value" id="score">0</div>
                            <div class="stat-label">得分</div>
                        </div>
                        <div class="stat-item">
                            <div class="stat-value" id="combo">0</div>
                            <div class="stat-label">连躲次数</div>
                        </div>
                        <div class="stat-item">
                            <div class="stat-value" id="lives">3</div>
                            <div class="stat-label">生命值</div>
                        </div>
                        <div class="stat-item">
                            <div class="stat-value" id="time">0</div>
                            <div class="stat-label">时间(秒)</div>
                        </div>
                        <div class="stat-item">
                            <div class="stat-value" id="attackCooldown">0</div>
                            <div class="stat-label">攻击冷却</div>
                        </div>
                        <div class="stat-item">
                            <div class="stat-value" id="flashCooldown">0</div>
                            <div class="stat-label">闪现冷却</div>
                        </div>
                    </div>
                </div>
                
                <div class="control-section">
                    <h3>难度选择</h3>
                    <div class="difficulty-section">
                        <button class="difficulty-btn normal-btn active" id="normalBtn">普通模式</button>
                        <button class="difficulty-btn hard-btn" id="hardBtn">困难模式</button>
                        <button class="difficulty-btn faker-btn" id="fakerBtn">FAKER! 模式</button>
                    </div>
                </div>
            </div>
            
            <!-- 游戏区域 -->
            <div class="game-container">
                <canvas id="gameCanvas"></canvas>
                <div class="flash-effect" id="flashEffect"></div>
                
                <!-- 鼠标瞄准指示器 -->
                <div class="aim-indicator" id="aimIndicator">
                    <div class="aim-line" id="aimLine"></div>
                    <div class="aim-circle" id="aimCircle"></div>
                </div>
                
                <div class="combo-display" id="comboDisplay">+5 Combo!</div>
                <div class="game-over" id="gameOverScreen">
                    <h2>游戏结束!</h2>
                    <div class="final-score">最终得分: <span id="finalScore">0</span></div>
                    <div class="game-controls">
                        <button class="control-btn start-btn" id="restartBtn">重新开始</button>
                        <button class="control-btn" id="menuBtn">返回菜单</button>
                    </div>
                </div>
            </div>
        </div>
        
        <div class="instructions" id="instructionsPanel" style="display: none;">
            <h3>游戏说明:</h3>
            <ul>
                <li><strong>目标:</strong>控制你的英雄(俯视图样式)躲避所有技能弹道,可以发射攻击摧毁技能</li>
                <li><strong>移动控制:</strong>在游戏区域使用鼠标左键点击地面,英雄会向点击位置移动并改变朝向</li>
                <li><strong>攻击控制:</strong>鼠标悬停显示瞄准线,按A键或点击"攻击(A)"按钮,向鼠标方向发射能量弹</li>
                <li><strong>闪现技能:</strong>按F键或点击"闪现(F)"按钮,瞬间向鼠标方向闪现一段距离</li>
                <li><strong>冷却系统:</strong>
                    <ul>
                        <li>攻击冷却:每次攻击后0.5秒冷却(无限使用)</li>
                        <li>闪现冷却:每次使用后5秒冷却</li>
                    </ul>
                </li>
                <li><strong>难度说明:</strong>
                    <ul>
                        <li><strong>普通:</strong>少量慢速技能,没有追踪和范围技能</li>
                        <li><strong>困难:</strong>增加技能数量和速度,加入预判和少量追踪技能</li>
                        <li><strong>FAKER!:</strong>高密度技能,大量追踪技能和范围技能</li>
                    </ul>
                </li>
                <li><strong>得分规则:</strong>成功躲避技能+1分,摧毁技能+2分,连续操作有额外加分,被击中-1生命</li>
                <li><strong>技巧:</strong>用鼠标瞄准追踪技能,合理使用攻击和闪现,注意冷却时间!</li>
            </ul>
            <div class="control-hint">提示:鼠标左键移动,鼠标瞄准,A键攻击,F键闪现!</div>
        </div>
    </div>

    <script>
        // 游戏模式管理
        let displayMode = 'fullscreen';
        let canvasBaseWidth = 1920;
        let canvasBaseHeight = 1080;
        
        // 技能类型面板状态
        let skillPanelVisible = false;
        
        // 全屏功能
        document.getElementById('toggleFullscreenBtn').addEventListener('click', toggleFullscreen);
        
        // 游戏说明按钮
        document.getElementById('instructionsBtn').addEventListener('click', () => {
            const instructions = document.getElementById('instructionsPanel');
            instructions.style.display = instructions.style.display === 'none' ? 'block' : 'none';
        });
        
        // 技能类型面板切换
        document.getElementById('skillToggleBtn').addEventListener('click', toggleSkillPanel);
        
        // 紧凑控制按钮事件
        document.getElementById('compactStartBtn').addEventListener('click', startGame);
        document.getElementById('compactPauseBtn').addEventListener('click', togglePause);
        document.getElementById('compactResetBtn').addEventListener('click', resetGame);
        document.getElementById('compactAttackBtn').addEventListener('click', playerAttack);
        document.getElementById('compactFlashBtn').addEventListener('click', useFlash);
        
        // 游戏结束按钮
        document.getElementById('restartBtn').addEventListener('click', restartGame);
        document.getElementById('menuBtn').addEventListener('click', returnToMenu);
        
        // 点击其他地方关闭技能面板
        document.addEventListener('click', (e) => {
            const skillPanel = document.getElementById('skillListPanel');
            const skillToggleBtn = document.getElementById('skillToggleBtn');
            
            if (skillPanelVisible && 
                !skillPanel.contains(e.target) && 
                !skillToggleBtn.contains(e.target)) {
                hideSkillPanel();
            }
        });
        
        function toggleSkillPanel() {
            if (skillPanelVisible) {
                hideSkillPanel();
            } else {
                showSkillPanel();
            }
        }
        
        function showSkillPanel() {
            if (gameRunning) {
                // 游戏进行中不显示,防止遮挡
                return;
            }
            
            const skillPanel = document.getElementById('skillListPanel');
            skillPanel.classList.add('show');
            skillPanelVisible = true;
        }
        
        function hideSkillPanel() {
            const skillPanel = document.getElementById('skillListPanel');
            skillPanel.classList.remove('show');
            skillPanelVisible = false;
        }
        
        // 游戏进行时自动关闭技能面板
        function startGame() {
            if (gameRunning) return;
            
            hideSkillPanel(); // 游戏开始时关闭技能面板
            
            gameRunning = true;
            gamePaused = false;
            score = 0;
            combo = 0;
            maxCombo = 0;
            lives = 3;
            gameTime = 0;
            skills = [];
            playerAttacks = [];
            playerPositions = [];
            attackCooldown = 0;
            flashCooldown = 0;
            
            const config = difficultyConfig[difficulty];
            currentSkillInterval = config.skillInterval;
            skillSpawnRate = config.baseSpawnRate;
            
            playerPosition.x = canvas.width / 2;
            playerPosition.y = canvas.height / 2;
            playerTargetPosition.x = canvas.width / 2;
            playerTargetPosition.y = canvas.height / 2;
            playerAttackDirection = { x: 1, y: 0 };
            
            document.getElementById('gameOverScreen').style.display = 'none';
            
            updateUI();
            updateAimIndicator();
            
            document.getElementById('compactPauseBtn').textContent = '暂停';
            
            gameLoop();
        }
        
        function toggleFullscreen() {
            if (displayMode === 'window') {
                displayMode = 'fullscreen';
                enterFullscreen();
            } else {
                displayMode = 'fullscreen';
                if (!document.fullscreenElement) {
                    enterFullscreen();
                } else {
                    exitFullscreen();
                }
            }
        }
        
        function enterFullscreen() {
            document.body.classList.add('fullscreen');
            
            canvasBaseWidth = 1920;
            canvasBaseHeight = 1080;
            
            resizeCanvas();
            
            const element = document.documentElement;
            if (element.requestFullscreen) {
                element.requestFullscreen();
            } else if (element.mozRequestFullScreen) {
                element.mozRequestFullScreen();
            } else if (element.webkitRequestFullscreen) {
                element.webkitRequestfullscreen();
            } else if (element.msRequestFullscreen) {
                element.msRequestFullscreen();
            }
        }
        
        function exitFullscreen() {
            document.body.classList.remove('fullscreen');
            
            if (document.exitFullscreen) {
                document.exitFullscreen();
            } else if (document.mozCancelFullScreen) {
                document.mozCancelFullScreen();
            } else if (document.webkitExitFullscreen) {
                document.webkitExitFullscreen();
            } else if (document.msExitFullscreen) {
                document.msExitFullscreen();
            }
        }
        
        document.addEventListener('fullscreenchange', handleFullscreenChange);
        document.addEventListener('mozfullscreenchange', handleFullscreenChange);
        document.addEventListener('webkitfullscreenchange', handleFullscreenChange);
        document.addEventListener('msfullscreenchange', handleFullscreenChange);
        
        function handleFullscreenChange() {
            const isFullscreen = document.fullscreenElement || 
                                document.mozFullScreenElement || 
                                document.webkitFullscreenElement ||
                                document.msFullscreenElement;
            
            if (!isFullscreen) {
                document.body.classList.remove('fullscreen');
            }
        }
        
        function resizeCanvas() {
            const canvas = document.getElementById('gameCanvas');
            const container = canvas.parentElement;
            
            canvas.width = canvasBaseWidth;
            canvas.height = canvasBaseHeight;
            
            const scaleX = container.clientWidth / canvasBaseWidth;
            const scaleY = container.clientHeight / canvasBaseHeight;
            const scale = Math.min(scaleX, scaleY);
            
            canvas.style.width = (canvasBaseWidth * scale) + 'px';
            canvas.style.height = (canvasBaseHeight * scale) + 'px';
            canvas.style.margin = 'auto';
            canvas.style.display = 'block';
            
            if (playerTargetPosition) {
                playerTargetPosition.x = canvas.width / 2;
                playerTargetPosition.y = canvas.height / 2;
            }
            if (playerPosition) {
                playerPosition.x = canvas.width / 2;
                playerPosition.y = canvas.height / 2;
            }
        }
        
        window.addEventListener('load', () => {
            initGame();
            setTimeout(() => {
                enterFullscreen();
            }, 500);
        });
        
        // =============== 游戏核心代码 ===============
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        const gameContainer = document.querySelector('.game-container');
        
        let mouseX = 0;
        let mouseY = 0;
        let isMouseOverCanvas = false;
        
        let gameRunning = false;
        let gamePaused = false;
        let difficulty = 'normal';
        let score = 0;
        let combo = 0;
        let maxCombo = 0;
        let lives = 3;
        let gameTime = 0;
        let lastSkillTime = 0;
        let baseSkillInterval = 2000;
        let currentSkillInterval = baseSkillInterval;
        let skillSpawnRate = 1;
        
        let playerPosition = { x: 0, y: 0 };
        let playerTargetPosition = { x: 0, y: 0 };
        let playerAttackDirection = { x: 1, y: 0 };
        let playerWidth = 60;
        let playerHeight = 90;
        let playerSpeed = 3.5;
        let playerVelocity = { x: 0, y: 0 };
        let playerPositions = [];
        
        let playerAttacks = [];
        let attackCooldown = 0;
        let attackCooldownTime = 0.5;
        
        let flashCooldown = 0;
        let flashCooldownTime = 5;
        let flashDistance = 150;
        
        let skills = [];
        
        // 弹道管理常量
        const PROJECTILE = {
            // 玩家攻击
            PLAYER_ATTACK: {
                LIFETIME: 1000, // 1秒
                SCREEN_MARGIN: 50, // 离开屏幕50像素后删除
                SPEED: 10
            },
            // 技能
            SKILL: {
                STRAIGHT: { LIFETIME: 5000, SCREEN_MARGIN: 100 },
                FAST_STRAIGHT: { LIFETIME: 5000, SCREEN_MARGIN: 100 },
                PREDICTION: { LIFETIME: 5000, SCREEN_MARGIN: 100 },
                RANGE: { LIFETIME: 3000, SCREEN_MARGIN: 200 },
                HOMING: { LIFETIME: 5000, SCREEN_MARGIN: 100 }
            }
        };
        
        const difficultyConfig = {
            normal: {
                skillSpeed: { min: 2.5, max: 3.5 },
                skillInterval: 2000,
                baseSpawnRate: 1,
                maxSpawnRate: 3,
                skillTypes: ['straight'],
                preditionLevel: 0,
                maxSkills: 8,
                fastSkillChance: 0.1,
                homingSkillChance: 0,
                rangeSkillChance: 0,
            },
            hard: {
                skillSpeed: { min: 3.0, max: 4.5 },
                skillInterval: 1500,
                baseSpawnRate: 1,
                maxSpawnRate: 4,
                skillTypes: ['straight', 'fastStraight', 'range'],
                preditionLevel: 0.3,
                maxSkills: 12,
                fastSkillChance: 0.2,
                homingSkillChance: 0.1,
                rangeSkillChance: 0.15,
            },
            faker: {
                skillSpeed: { min: 3.5, max: 5.5 },
                skillInterval: 1000,
                baseSpawnRate: 2,
                maxSpawnRate: 5,
                skillTypes: ['straight', 'fastStraight', 'prediction', 'range', 'homing'],
                preditionLevel: 0.5,
                maxSkills: 15,
                fastSkillChance: 0.3,
                homingSkillChance: 0.2,
                rangeSkillChance: 0.25,
            }
        };
        
        const skillConfig = {
            straight: { 
                color: '#4CAF50', 
                radius: 15, 
                speed: 3.0,
                lifetime: PROJECTILE.SKILL.STRAIGHT.LIFETIME
            },
            fastStraight: { 
                color: '#FF9800', 
                radius: 12, 
                speed: 5.0,
                lifetime: PROJECTILE.SKILL.FAST_STRAIGHT.LIFETIME
            },
            prediction: { 
                color: '#FF0000', 
                radius: 18, 
                speed: 4.0,
                lifetime: PROJECTILE.SKILL.PREDICTION.LIFETIME
            },
            range: { 
                color: '#9C27B0', 
                radius: 100, 
                speed: 0, 
                lifetime: PROJECTILE.SKILL.RANGE.LIFETIME,
                shrinkSpeed: 0.8,
                targetRadius: 15
            },
            homing: { 
                color: '#00BCD4', 
                radius: 14, 
                speed: 3.5,
                lifetime: PROJECTILE.SKILL.HOMING.LIFETIME
            }
        };
        
        // 初始化游戏
        function initGame() {
            resizeCanvas();
            
            window.addEventListener('resize', resizeCanvas);
            
            gameContainer.addEventListener('mousemove', function(e) {
                const rect = canvas.getBoundingClientRect();
                const scaleX = canvas.width / rect.width;
                const scaleY = canvas.height / rect.height;
                
                mouseX = (e.clientX - rect.left) * scaleX;
                mouseY = (e.clientY - rect.top) * scaleY;
                
                updateAimIndicator();
            });
            
            gameContainer.addEventListener('mouseenter', function() {
                isMouseOverCanvas = true;
                updateAimIndicator();
            });
            
            gameContainer.addEventListener('mouseleave', function() {
                isMouseOverCanvas = false;
                updateAimIndicator();
            });
            
            canvas.addEventListener('mousedown', function(e) {
                if (e.button !== 0) return;
                
                e.preventDefault();
                e.stopPropagation();
                
                if (!gameRunning || gamePaused) return;
                
                const rect = canvas.getBoundingClientRect();
                const scaleX = canvas.width / rect.width;
                const scaleY = canvas.height / rect.height;
                
                const x = (e.clientX - rect.left) * scaleX;
                const y = (e.clientY - rect.top) * scaleY;
                
                playerTargetPosition.x = x;
                playerTargetPosition.y = y;
                
                const dx = x - playerPosition.x;
                const dy = y - playerPosition.y;
                const distance = Math.sqrt(dx * dx + dy * dy);
                if (distance > 0) {
                    playerAttackDirection.x = dx / distance;
                    playerAttackDirection.y = dy / distance;
                }
                
                addClickIndicator(x, y);
                
                return false;
            });
            
            document.addEventListener('keydown', function(e) {
                if (!gameRunning || gamePaused) return;
                
                if (e.key === 'a' || e.key === 'A') {
                    e.preventDefault();
                    playerAttack();
                } else if (e.key === 'f' || e.key === 'F') {
                    e.preventDefault();
                    useFlash();
                } else if (e.key === 'Escape') {
                    if (displayMode === 'fullscreen') {
                        exitFullscreen();
                    }
                }
            });
            
            canvas.addEventListener('contextmenu', function(e) {
                e.preventDefault();
                e.stopPropagation();
                return false;
            });
            
            document.addEventListener('dragstart', function(e) {
                e.preventDefault();
                return false;
            });
            
            const difficultyButtons = document.querySelectorAll('.difficulty-btn');
            difficultyButtons.forEach(btn => {
                btn.addEventListener('click', function() {
                    const level = this.id.replace('Btn', '');
                    setDifficulty(level);
                    
                    difficultyButtons.forEach(b => b.classList.remove('active'));
                    this.classList.add('active');
                });
            });
            
            draw();
        }
        
        function updateAimIndicator() {
            const aimIndicator = document.getElementById('aimIndicator');
            const aimLine = document.getElementById('aimLine');
            const aimCircle = document.getElementById('aimCircle');
            
            if (isMouseOverCanvas && gameRunning && !gamePaused && attackCooldown <= 0) {
                aimIndicator.style.opacity = '1';
                
                const rect = canvas.getBoundingClientRect();
                const scaleX = canvas.width / rect.width;
                const scaleY = canvas.height / rect.height;
                
                const playerScreenX = playerPosition.x / scaleX;
                const playerScreenY = playerPosition.y / scaleY;
                
                const mouseScreenX = mouseX / scaleX;
                const mouseScreenY = mouseY / scaleY;
                
                const dx = mouseScreenX - playerScreenX;
                const dy = mouseScreenY - playerScreenY;
                const distance = Math.sqrt(dx * dx + dy * dy);
                const angle = Math.atan2(dy, dx);
                
                aimLine.style.left = playerScreenX + 'px';
                aimLine.style.top = playerScreenY + 'px';
                aimLine.style.width = Math.min(distance, 300) + 'px';
                aimLine.style.transform = `rotate(${angle}rad)`;
                
                aimCircle.style.left = (mouseScreenX - 12.5) + 'px';
                aimCircle.style.top = (mouseScreenY - 12.5) + 'px';
            } else {
                aimIndicator.style.opacity = '0';
            }
        }
        
        function addClickIndicator(x, y) {
            const indicator = document.createElement('div');
            indicator.className = 'click-indicator';
            
            const rect = canvas.getBoundingClientRect();
            const scaleX = canvas.width / rect.width;
            const scaleY = canvas.height / rect.height;
            
            indicator.style.left = (x / scaleX) + 'px';
            indicator.style.top = (y / scaleY) + 'px';
            
            gameContainer.appendChild(indicator);
            
            setTimeout(() => {
                if (indicator.parentNode) {
                    indicator.parentNode.removeChild(indicator);
                }
            }, 500);
        }
        
        function playerAttack() {
            if (attackCooldown > 0) return;
            
            attackCooldown = attackCooldownTime;
            
            const dx = mouseX - playerPosition.x;
            const dy = mouseY - playerPosition.y;
            const distance = Math.sqrt(dx * dx + dy * dy);
            
            let directionX = 1;
            let directionY = 0;
            
            if (distance > 10) {
                directionX = dx / distance;
                directionY = dy / distance;
            }
            
            playerAttackDirection.x = directionX;
            playerAttackDirection.y = directionY;
            
            const attack = {
                x: playerPosition.x + directionX * 30,
                y: playerPosition.y + directionY * 30,
                radius: 12,
                color: '#FFEB3B',
                speed: PROJECTILE.PLAYER_ATTACK.SPEED,
                directionX: directionX,
                directionY: directionY,
                lifetime: PROJECTILE.PLAYER_ATTACK.LIFETIME,
                age: 0,
                damage: 1
            };
            
            playerAttacks.push(attack);
            
            updateUI();
            updateAimIndicator();
        }
        
        function useFlash() {
            if (flashCooldown > 0) return;
            
            flashCooldown = flashCooldownTime;
            
            const dx = mouseX - playerPosition.x;
            const dy = mouseY - playerPosition.y;
            const distance = Math.sqrt(dx * dx + dy * dy);
            
            let directionX = 0;
            let directionY = 0;
            
            if (distance > 10) {
                directionX = dx / distance;
                directionY = dy / distance;
            }
            
            const flashX = playerPosition.x + directionX * flashDistance;
            const flashY = playerPosition.y + directionY * flashDistance;
            
            showFlashEffect();
            
            playerPosition.x = flashX;
            playerPosition.y = flashY;
            playerTargetPosition.x = flashX;
            playerTargetPosition.y = flashY;
            
            playerAttackDirection.x = directionX;
            playerAttackDirection.y = directionY;
            
            playerPosition.x = Math.max(playerWidth/2, Math.min(canvas.width - playerWidth/2, playerPosition.x));
            playerPosition.y = Math.max(playerHeight/2, Math.min(canvas.height - playerHeight/2, playerPosition.y));
            playerTargetPosition.x = playerPosition.x;
            playerTargetPosition.y = playerPosition.y;
            
            playerPositions = [];
            
            updateUI();
        }
        
        function showFlashEffect() {
            const flashEffect = document.getElementById('flashEffect');
            flashEffect.style.opacity = '1';
            
            setTimeout(() => {
                flashEffect.style.opacity = '0';
            }, 200);
        }
        
        // =============== 弹道删除逻辑优化 ===============
        
        // 检查玩家攻击是否应该被删除
        function shouldRemovePlayerAttack(attack) {
            // 1. 超过生命周期
            if (attack.age > attack.lifetime) return true;
            
            // 2. 完全离开屏幕(使用定义的边界)
            const margin = PROJECTILE.PLAYER_ATTACK.SCREEN_MARGIN;
            if (attack.x < -margin || attack.x > canvas.width + margin || 
                attack.y < -margin || attack.y > canvas.height + margin) {
                return true;
            }
            
            return false;
        }
        
        // 检查技能是否应该被删除
        function shouldRemoveSkill(skill) {
            // 1. 超过生命周期
            if (skill.age > skill.lifetime) return true;
            
            // 2. 范围技能收缩完成
            if (skill.type === 'range' && skill.currentRadius <= skill.targetRadius) return true;
            
            // 3. 完全离开屏幕(使用技能类型特定的边界)
            const margin = PROJECTILE.SKILL[skill.type.toUpperCase()]?.SCREEN_MARGIN || 100;
            if (skill.x < -margin || skill.x > canvas.width + margin || 
                skill.y < -margin || skill.y > canvas.height + margin) {
                return true;
            }
            
            return false;
        }
        
        // 更新玩家攻击 - 使用新的删除逻辑
        function updatePlayerAttacks(deltaTime) {
            for (let i = playerAttacks.length - 1; i >= 0; i--) {
                const attack = playerAttacks[i];
                
                attack.age += deltaTime;
                
                // 使用统一的删除检查
                if (shouldRemovePlayerAttack(attack)) {
                    playerAttacks.splice(i, 1);
                    continue;
                }
                
                attack.x += attack.directionX * attack.speed;
                attack.y += attack.directionY * attack.speed;
            }
        }
        
        // 更新技能位置 - 使用新的删除逻辑
        function updateSkills(deltaTime) {
            for (let i = skills.length - 1; i >= 0; i--) {
                const skill = skills[i];
                
                skill.age += deltaTime;
                
                // 使用统一的删除检查
                if (shouldRemoveSkill(skill)) {
                    // 如果是正常消失(非碰撞),算作成功躲避
                    if (skill.health > 0) {
                        onSkillDodged();
                    }
                    skills.splice(i, 1);
                    continue;
                }
                
                if (skill.type === 'range') {
                    // 范围技能:逐渐缩小并虚化
                    if (skill.currentRadius > skill.targetRadius) {
                        skill.currentRadius -= skill.shrinkSpeed;
                        skill.fadeProgress = (skill.radius - skill.currentRadius) / (skill.radius - skill.targetRadius);
                        
                        if (skill.currentRadius <= skill.targetRadius * 3 && !skill.highlight) {
                            skill.highlight = true;
                            skill.highlightTime = 500;
                        }
                        
                        if (skill.highlight) {
                            skill.highlightTime -= deltaTime;
                            if (skill.highlightTime <= 0) {
                                skill.highlight = false;
                            }
                        }
                    }
                } else {
                    // 移动技能
                    if (skill.homing) {
                        const dx = playerPosition.x - skill.x;
                        const dy = playerPosition.y - skill.y;
                        const distance = Math.sqrt(dx * dx + dy * dy);
                        
                        if (distance > 0) {
                            skill.directionX = dx / distance;
                            skill.directionY = dy / distance;
                        }
                    }
                    
                    skill.x += skill.directionX * skill.speed;
                    skill.y += skill.directionY * skill.speed;
                }
            }
        }
        
        // =============== 其他游戏逻辑 ===============
        
        function updatePlayer(deltaTime) {
            if (!gameRunning) return;
            
            const dx = playerTargetPosition.x - playerPosition.x;
            const dy = playerTargetPosition.y - playerPosition.y;
            const distance = Math.sqrt(dx * dx + dy * dy);
            
            if (distance > 1) {
                const moveDistance = playerSpeed * (deltaTime / 16);
                
                if (distance <= moveDistance) {
                    playerPosition.x = playerTargetPosition.x;
                    playerPosition.y = playerTargetPosition.y;
                } else {
                    const ratio = moveDistance / distance;
                    playerPosition.x += dx * ratio;
                    playerPosition.y += dy * ratio;
                }
                
                playerPosition.x = Math.max(playerWidth/2, Math.min(canvas.width - playerWidth/2, playerPosition.x));
                playerPosition.y = Math.max(playerHeight/2, Math.min(canvas.height - playerHeight/2, playerPosition.y));
                
                playerTargetPosition.x = Math.max(playerWidth/2, Math.min(canvas.width - playerWidth/2, playerTargetPosition.x));
                playerTargetPosition.y = Math.max(playerHeight/2, Math.min(canvas.height - playerHeight/2, playerTargetPosition.y));
                
                if (dx !== 0 || dy !== 0) {
                    const moveDistance = Math.sqrt(dx * dx + dy * dy);
                    if (moveDistance > 0) {
                        playerAttackDirection.x = dx / moveDistance;
                        playerAttackDirection.y = dy / moveDistance;
                    }
                }
                
                playerPositions.push({x: playerPosition.x, y: playerPosition.y});
                if (playerPositions.length > 10) {
                    playerPositions.shift();
                }
                
                if (playerPositions.length >= 2) {
                    const lastPos = playerPositions[playerPositions.length - 2];
                    playerVelocity.x = playerPosition.x - lastPos.x;
                    playerVelocity.y = playerPosition.y - lastPos.y;
                }
            }
            
            if (attackCooldown > 0) {
                attackCooldown -= deltaTime / 1000;
                if (attackCooldown < 0) attackCooldown = 0;
            }
            
            if (flashCooldown > 0) {
                flashCooldown -= deltaTime / 1000;
                if (flashCooldown < 0) flashCooldown = 0;
            }
            
            if (isMouseOverCanvas) {
                updateAimIndicator();
            }
        }
        
        function setDifficulty(level) {
            difficulty = level;
            updateDifficultyDisplay();
            
            const config = difficultyConfig[difficulty];
            
            currentSkillInterval = config.skillInterval;
            skillSpawnRate = config.baseSpawnRate;
            baseSkillInterval = config.skillInterval;
            
            if (gameRunning) {
                resetGame();
                startGame();
            }
        }
        
        function updateDifficultyDisplay() {
            const difficultyElement = document.getElementById('currentDifficulty');
            const difficultyText = 
                difficulty === 'normal' ? '普通难度' : 
                difficulty === 'hard' ? '困难难度' : 'FAKER! 模式';
            
            difficultyElement.textContent = difficultyText;
            
            difficultyElement.className = 'current-difficulty';
            if (difficulty === 'normal') difficultyElement.classList.add('normal-difficulty');
            else if (difficulty === 'hard') difficultyElement.classList.add('hard-difficulty');
            else difficultyElement.classList.add('faker-difficulty');
        }
        
        function gameLoop(timestamp) {
            if (!gameRunning) return;
            
            const deltaTime = timestamp - lastTime || 0;
            lastTime = timestamp;
            
            if (!gamePaused) {
                gameTime += deltaTime / 1000;
                
                updatePlayer(deltaTime);
                updateSkillSpawnRate();
                generateSkills();
                updateSkills(deltaTime);
                updatePlayerAttacks(deltaTime);
                checkCollisions();
                updateUI();
                
                if (lives <= 0) {
                    endGame();
                    return;
                }
            }
            
            draw();
            
            requestAnimationFrame(gameLoop);
        }
        
        function updateSkillSpawnRate() {
            const config = difficultyConfig[difficulty];
            
            const timeFactor = Math.min(gameTime / 60, 1);
            skillSpawnRate = config.baseSpawnRate + (config.maxSpawnRate - config.baseSpawnRate) * timeFactor;
            
            currentSkillInterval = baseSkillInterval * (1 - timeFactor * 0.5);
        }
        
        function generateSkills() {
            if (!gameRunning || gamePaused) return;
            
            const currentTime = Date.now();
            const config = difficultyConfig[difficulty];
            
            if (currentTime - lastSkillTime > currentSkillInterval && skills.length < config.maxSkills) {
                lastSkillTime = currentTime;
                
                const spawnCount = Math.floor(skillSpawnRate) + (Math.random() < (skillSpawnRate % 1) ? 1 : 0);
                
                for (let i = 0; i < spawnCount; i++) {
                    const skillTypes = config.skillTypes;
                    let skillType = skillTypes[Math.floor(Math.random() * skillTypes.length)];
                    
                    if (Math.random() < config.fastSkillChance && skillTypes.includes('fastStraight')) {
                        skillType = 'fastStraight';
                    }
                    
                    if (skillType === 'homing' && Math.random() > config.homingSkillChance) {
                        const nonHomingTypes = skillTypes.filter(type => type !== 'homing');
                        skillType = nonHomingTypes[Math.floor(Math.random() * nonHomingTypes.length)];
                    }
                    
                    if (skillType === 'range' && Math.random() > config.rangeSkillChance) {
                        const nonRangeTypes = skillTypes.filter(type => type !== 'range');
                        skillType = nonRangeTypes[Math.floor(Math.random() * nonRangeTypes.length)];
                    }
                    
                    const skill = createSkill(skillType);
                    skills.push(skill);
                }
            }
        }
        
        function createSkill(skillType) {
            const config = difficultyConfig[difficulty];
            const skillSpec = skillConfig[skillType];
            
            let startX, startY, targetX, targetY;
            
            if (skillType === 'range') {
                const angle = Math.random() * Math.PI * 2;
                const distance = 300 + Math.random() * 400;
                
                startX = playerPosition.x + Math.cos(angle) * distance;
                startY = playerPosition.y + Math.sin(angle) * distance;
                targetX = playerPosition.x;
                targetY = playerPosition.y;
                
                startX = Math.max(100, Math.min(canvas.width - 100, startX));
                startY = Math.max(100, Math.min(canvas.height - 100, startY));
            } else {
                const edge = Math.floor(Math.random() * 4);
                
                switch(edge) {
                    case 0:
                        startX = Math.random() * canvas.width;
                        startY = -50;
                        break;
                    case 1:
                        startX = canvas.width + 50;
                        startY = Math.random() * canvas.height;
                        break;
                    case 2:
                        startX = Math.random() * canvas.width;
                        startY = canvas.height + 50;
                        break;
                    case 3:
                        startX = -50;
                        startY = Math.random() * canvas.height;
                        break;
                }
                
                targetX = playerPosition.x;
                targetY = playerPosition.y;
            }
            
            const speed = skillSpec.speed * (0.8 + Math.random() * 0.4);
            
            if (skillType === 'prediction' && playerPositions.length > 0) {
                const predictionAmount = config.preditionLevel;
                
                targetX = playerPosition.x + playerVelocity.x * 20 * predictionAmount;
                targetY = playerPosition.y + playerVelocity.y * 20 * predictionAmount;
                
                targetX = Math.max(50, Math.min(canvas.width - 50, targetX));
                targetY = Math.max(50, Math.min(canvas.height - 50, targetY));
            } else if (skillType === 'homing') {
                targetX = playerPosition.x;
                targetY = playerPosition.y;
            }
            
            const dx = targetX - startX;
            const dy = targetY - startY;
            const distance = Math.sqrt(dx * dx + dy * dy);
            const directionX = dx / distance;
            const directionY = dy / distance;
            
            const rangeSkillProps = skillType === 'range' ? {
                currentRadius: skillSpec.radius,
                targetRadius: skillSpec.targetRadius,
                shrinkSpeed: skillSpec.shrinkSpeed,
                fadeProgress: 0,
                highlight: false,
                highlightTime: 0
            } : {};
            
            return {
                type: skillType,
                x: startX,
                y: startY,
                radius: skillSpec.radius,
                color: skillSpec.color,
                speed: speed,
                directionX: skillType === 'range' ? 0 : directionX,
                directionY: skillType === 'range' ? 0 : directionY,
                lifetime: skillSpec.lifetime,
                age: 0,
                targetX: targetX,
                targetY: targetY,
                homing: skillType === 'homing',
                health: 1,
                ...rangeSkillProps
            };
        }
        
        function checkCollisions() {
            for (let i = playerAttacks.length - 1; i >= 0; i--) {
                const attack = playerAttacks[i];
                
                for (let j = skills.length - 1; j >= 0; j--) {
                    const skill = skills[j];
                    
                    const dx = attack.x - skill.x;
                    const dy = attack.y - skill.y;
                    const distance = Math.sqrt(dx * dx + dy * dy);
                    
                    if (distance < attack.radius + (skill.type === 'range' ? skill.currentRadius : skill.radius)) {
                        playerAttacks.splice(i, 1);
                        
                        skill.health -= attack.damage;
                        
                        if (skill.health <= 0) {
                            skills.splice(j, 1);
                            onSkillDestroyed();
                        }
                        
                        break;
                    }
                }
            }
            
            for (let i = skills.length - 1; i >= 0; i--) {
                const skill = skills[i];
                
                const dx = playerPosition.x - skill.x;
                const dy = playerPosition.y - skill.y;
                const distance = Math.sqrt(dx * dx + dy * dy);
                
                const playerCollisionRadius = Math.min(playerWidth, playerHeight) / 2 * 0.7;
                const skillRadius = skill.type === 'range' ? skill.currentRadius : skill.radius;
                
                if (distance < playerCollisionRadius + skillRadius) {
                    skills.splice(i, 1);
                    onPlayerHit();
                    return;
                }
            }
        }
        
        function onPlayerHit() {
            lives--;
            combo = 0;
            
            canvas.style.backgroundColor = '#ff0000';
            setTimeout(() => {
                canvas.style.backgroundColor = '#0d0d1a';
            }, 100);
            
            updateUI();
        }
        
        function onSkillDodged() {
            score++;
            combo++;
            
            if (combo > maxCombo) {
                maxCombo = combo;
            }
            
            if (combo >= 5 && combo % 5 === 0) {
                const bonus = Math.floor(combo / 5) * 5;
                score += bonus;
                
                const comboDisplay = document.getElementById('comboDisplay');
                comboDisplay.textContent = `+${bonus} Combo!`;
                comboDisplay.style.opacity = '1';
                
                setTimeout(() => {
                    comboDisplay.style.opacity = '0';
                }, 1000);
            }
            
            updateUI();
        }
        
        function onSkillDestroyed() {
            score += 2;
            combo++;
            
            if (combo > maxCombo) {
                maxCombo = combo;
            }
            
            if (combo >= 5 && combo % 5 === 0) {
                const bonus = Math.floor(combo / 5) * 5;
                score += bonus;
                
                const comboDisplay = document.getElementById('comboDisplay');
                comboDisplay.textContent = `+${bonus} Destroy!`;
                comboDisplay.style.opacity = '1';
                
                setTimeout(() => {
                    comboDisplay.style.opacity = '0';
                }, 1000);
            }
            
            updateUI();
        }
        
        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            drawGrid();
            
            for (const skill of skills) {
                drawSkill(skill);
            }
            
            for (const attack of playerAttacks) {
                drawPlayerAttack(attack);
            }
            
            drawPlayerTopView();
            
            if (difficulty === 'faker' && gameRunning && !gamePaused) {
                drawPlayerTrail();
            }
            
            if (playerPosition.x !== playerTargetPosition.x || playerPosition.y !== playerTargetPosition.y) {
                drawTargetIndicator();
            }
        }
        
        function drawGrid() {
            ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
            ctx.lineWidth = 1;
            
            const baseGridSize = 60;
            const scale = canvas.width / canvasBaseWidth;
            const gridSize = baseGridSize * scale;
            
            for (let x = 0; x <= canvas.width; x += gridSize) {
                ctx.beginPath();
                ctx.moveTo(x, 0);
                ctx.lineTo(x, canvas.height);
                ctx.stroke();
            }
            
            for (let y = 0; y <= canvas.height; y += gridSize) {
                ctx.beginPath();
                ctx.moveTo(0, y);
                ctx.lineTo(canvas.width, y);
                ctx.stroke();
            }
        }
        
        function drawSkill(skill) {
            ctx.save();
            
            const scale = canvas.width / canvasBaseWidth;
            
            if (skill.type === 'range') {
                const currentRadius = skill.currentRadius * scale;
                const targetRadius = skill.targetRadius * scale;
                
                const alpha = 0.7 * (1 - skill.fadeProgress);
                
                ctx.globalAlpha = alpha;
                ctx.strokeStyle = skill.color;
                ctx.lineWidth = 4 * scale;
                ctx.beginPath();
                ctx.arc(skill.x, skill.y, currentRadius, 0, Math.PI * 2);
                ctx.stroke();
                
                if (skill.fadeProgress > 0) {
                    ctx.globalAlpha = alpha * 0.3;
                    ctx.fillStyle = skill.color;
                    ctx.beginPath();
                    ctx.arc(skill.x, skill.y, targetRadius, 0, Math.PI * 2);
                    ctx.fill();
                    
                    for (let r = targetRadius; r < currentRadius; r += 10 * scale) {
                        const progress = (r - targetRadius) / (currentRadius - targetRadius);
                        ctx.globalAlpha = alpha * (1 - progress) * 0.5;
                        ctx.strokeStyle = skill.color;
                        ctx.lineWidth = 2 * scale;
                        ctx.beginPath();
                        ctx.arc(skill.x, skill.y, r, 0, Math.PI * 2);
                        ctx.stroke();
                    }
                }
                
                if (skill.highlight) {
                    ctx.globalAlpha = 0.5;
                    ctx.fillStyle = skill.color;
                    ctx.beginPath();
                    ctx.arc(skill.x, skill.y, currentRadius * 1.2, 0, Math.PI * 2);
                    ctx.fill();
                    
                    ctx.globalAlpha = 0.8;
                    ctx.strokeStyle = '#FFFFFF';
                    ctx.lineWidth = 3 * scale;
                    ctx.beginPath();
                    ctx.arc(skill.x, skill.y, currentRadius * 1.1, 0, Math.PI * 2);
                    ctx.stroke();
                }
            } else {
                const adjustedRadius = skill.radius * scale;
                
                ctx.fillStyle = skill.color;
                ctx.beginPath();
                ctx.arc(skill.x, skill.y, adjustedRadius, 0, Math.PI * 2);
                ctx.fill();
                
                ctx.shadowColor = skill.color;
                ctx.shadowBlur = 15 * scale;
                ctx.beginPath();
                ctx.arc(skill.x, skill.y, adjustedRadius * 0.7, 0, Math.PI * 2);
                ctx.fill();
                
                ctx.strokeStyle = skill.color;
                ctx.lineWidth = 3 * scale;
                ctx.globalAlpha = 0.6;
                ctx.beginPath();
                ctx.moveTo(skill.x, skill.y);
                
                const trailLength = 30 * scale;
                ctx.lineTo(
                    skill.x - skill.directionX * trailLength,
                    skill.y - skill.directionY * trailLength
                );
                ctx.stroke();
                ctx.globalAlpha = 1;
            }
            
            ctx.restore();
        }
        
        function drawPlayerAttack(attack) {
            ctx.save();
            
            const scale = canvas.width / canvasBaseWidth;
            const radius = attack.radius * scale;
            
            ctx.fillStyle = attack.color;
            ctx.shadowColor = attack.color;
            ctx.shadowBlur = 20 * scale;
            ctx.beginPath();
            ctx.arc(attack.x, attack.y, radius, 0, Math.PI * 2);
            ctx.fill();
            
            ctx.strokeStyle = '#FFFFFF';
            ctx.lineWidth = 1.5 * scale;
            ctx.globalAlpha = 0.7;
            ctx.beginPath();
            ctx.arc(attack.x, attack.y, radius * 1.5, 0, Math.PI * 2);
            ctx.stroke();
            
            ctx.strokeStyle = '#FF9800';
            ctx.lineWidth = 3 * scale;
            ctx.globalAlpha = 0.5;
            ctx.beginPath();
            ctx.moveTo(attack.x, attack.y);
            ctx.lineTo(
                attack.x - attack.directionX * 22.5 * scale,
                attack.y - attack.directionY * 22.5 * scale
            );
            ctx.stroke();
            
            ctx.restore();
        }
        
        function drawPlayerTopView() {
            ctx.save();
            
            const scale = canvas.width / canvasBaseWidth;
            const width = playerWidth * scale;
            const height = playerHeight * scale;
            
            ctx.translate(playerPosition.x, playerPosition.y);
            
            const angle = Math.atan2(playerAttackDirection.y, playerAttackDirection.x);
            ctx.rotate(angle);
            
            ctx.fillStyle = '#1E88E5';
            ctx.beginPath();
            ctx.arc(0, height/6, height/2.5, 0, Math.PI * 2);
            ctx.fill();
            
            ctx.fillStyle = '#1565C0';
            ctx.beginPath();
            ctx.ellipse(0, -height/8, width/2.5, height/4, 0, 0, Math.PI * 2);
            ctx.fill();
            
            ctx.fillStyle = '#FFCC99';
            ctx.beginPath();
            ctx.arc(0, 0, width/4, 0, Math.PI * 2);
            ctx.fill();
            
            ctx.fillStyle = '#FFD700';
            ctx.beginPath();
            ctx.arc(0, -width/8, width/4, 0, Math.PI * 2);
            ctx.fill();
            
            ctx.fillStyle = '#333333';
            ctx.beginPath();
            ctx.arc(-width/10, -width/20, width/20, 0, Math.PI * 2);
            ctx.fill();
            
            ctx.beginPath();
            ctx.arc(width/10, -width/20, width/20, 0, Math.PI * 2);
            ctx.fill();
            
            ctx.fillStyle = '#1E88E5';
            ctx.fillRect(width/3, -height/6, width/3, height/8);
            ctx.fillRect(-width/2, -height/6, width/3, height/8);
            
            if (attackCooldown <= 0 && isMouseOverCanvas) {
                ctx.fillStyle = '#4FC3F7';
                ctx.shadowColor = '#4FC3F7';
                ctx.shadowBlur = 15 * scale;
                ctx.beginPath();
                ctx.arc(width/2, -height/12, width/6, 0, Math.PI * 2);
                ctx.fill();
                ctx.shadowBlur = 0;
            }
            
            ctx.restore();
        }
        
        function drawTargetIndicator() {
            ctx.save();
            
            const scale = canvas.width / canvasBaseWidth;
            
            ctx.strokeStyle = '#4CAF50';
            ctx.lineWidth = 3 * scale;
            ctx.setLineDash([7.5 * scale, 7.5 * scale]);
            
            ctx.beginPath();
            ctx.moveTo(playerPosition.x, playerPosition.y);
            ctx.lineTo(playerTargetPosition.x, playerTargetPosition.y);
            ctx.stroke();
            
            ctx.fillStyle = '#4CAF50';
            ctx.beginPath();
            ctx.arc(playerTargetPosition.x, playerTargetPosition.y, 7.5 * scale, 0, Math.PI * 2);
            ctx.fill();
            
            ctx.setLineDash([]);
            ctx.restore();
        }
        
        function drawPlayerTrail() {
            if (playerPositions.length < 2) return;
            
            ctx.save();
            ctx.strokeStyle = 'rgba(30, 136, 229, 0.3)';
            ctx.lineWidth = 3 * (canvas.width / canvasBaseWidth);
            ctx.beginPath();
            ctx.moveTo(playerPositions[0].x, playerPositions[0].y);
            
            for (let i = 1; i < playerPositions.length; i++) {
                ctx.lineTo(playerPositions[i].x, playerPositions[i].y);
            }
            
            ctx.stroke();
            ctx.restore();
        }
        
        function updateUI() {
            document.getElementById('score').textContent = score;
            document.getElementById('combo').textContent = combo;
            document.getElementById('lives').textContent = lives;
            document.getElementById('time').textContent = Math.floor(gameTime);
            document.getElementById('attackCooldown').textContent = attackCooldown > 0 ? attackCooldown.toFixed(1) : '0';
            document.getElementById('flashCooldown').textContent = flashCooldown > 0 ? flashCooldown.toFixed(1) : '0';
            
            const compactAttackBtn = document.getElementById('compactAttackBtn');
            if (attackCooldown > 0 || !gameRunning || gamePaused) {
                compactAttackBtn.disabled = true;
                compactAttackBtn.style.opacity = '0.5';
            } else {
                compactAttackBtn.disabled = false;
                compactAttackBtn.style.opacity = '1';
            }
            
            const compactFlashBtn = document.getElementById('compactFlashBtn');
            if (flashCooldown > 0 || !gameRunning || gamePaused) {
                compactFlashBtn.disabled = true;
                compactFlashBtn.style.opacity = '0.5';
            } else {
                compactFlashBtn.disabled = false;
                compactFlashBtn.style.opacity = '1';
            }
        }
        
        function togglePause() {
            if (!gameRunning) return;
            
            gamePaused = !gamePaused;
            const compactPauseText = gamePaused ? '继续' : '暂停';
            
            document.getElementById('compactPauseBtn').textContent = compactPauseText;
            
            if (gamePaused) {
                document.getElementById('aimIndicator').style.opacity = '0';
            } else {
                updateAimIndicator();
            }
        }
        
        function resetGame() {
            gameRunning = false;
            gamePaused = false;
            score = 0;
            combo = 0;
            maxCombo = 0;
            lives = 3;
            gameTime = 0;
            skills = [];
            playerAttacks = [];
            attackCooldown = 0;
            flashCooldown = 0;
            
            updateUI();
            document.getElementById('compactPauseBtn').textContent = '暂停';
            document.getElementById('aimIndicator').style.opacity = '0';
            
            draw();
        }
        
        function restartGame() {
            resetGame();
            startGame();
        }
        
        function returnToMenu() {
            resetGame();
            document.getElementById('gameOverScreen').style.display = 'none';
        }
        
        function endGame() {
            gameRunning = false;
            
            document.getElementById('finalScore').textContent = score;
            document.getElementById('gameOverScreen').style.display = 'flex';
            document.getElementById('aimIndicator').style.opacity = '0';
        }
        
        let lastTime = 0;
    </script>
</body>
</html>

全部评论

相关推荐

牛至超人:哈工大已经很棒了,不需要加括号了,然后咋没有实习经历呢?火速趁寒假整一段实习,导师不让就狠狠肘击
投了多少份简历才上岸
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务