几何决斗脚本


// ==UserScript==
// @name         几何决斗
// @namespace
// @version      42.5
// @author       sunhy
// @description  修复:语法错误、属性面板保存、爆炸箭bug
// @match        *://*/*
// @grant        none
// @icon        https://cdn.luogu.com.cn/upload/image_hosting/vbrmr14i.png
// ==/UserScript==

(function() {
    'use strict';

    const CONFIG = {
        WIDTH: 1200,
        HEIGHT: 800,
        SIZE: 26,
        SPEED: {
            NORMAL: 10,
            ATTACK: 5,
            CHARGE: 2
        },
        ARROW_SPEED: 12,
        COOLDOWNS: {
            NORMAL: 200,
            EXPLOSION: 300,
            SPLIT: 400
        },
        CHARGE_TIME: 400,
        AIM_LINE_TIME: 300,
        MAX_PARTICLES: 200,
        SHAKE_DURATION: 60
    };

    // 状态变量
    let fishMode = true;
    let gameMode = 'single';
    let gameState = 'playing';
    let screenShake = 0;
    let shakeTimer = 0;
    let winner = '';
    let winningPlayer = '';
    let aiMode = 'enhanced';
    let cheatMode = false;
    let cheatPanelVisible = false;

    const game = {
        keys: {},
        p1: null, p2: null,
        arrows: [],
        aimLines: [],
        particles: [],
        bgOffset: 0,
        frame: 0
    };

    // 箭矢类型
    const arrowType = {
        p1: 'normal',
        p2: 'normal'
    };

    // 显示计时器
    const typeDisplayTimer = {
        p1: 0,
        p2: 0
    };

    // 破解属性 - 默认值
    const defaultCheatProperties = {
        playerSpeed: 1.5,
        arrowCooldown: 0.5,
        arrowSpeed: 1.5,
        knockbackForce: 1.5,
        playerSize: 0.8,
        arrowSize: 1.0,
        knockbackResist: 0.5,
        chargeTime: 0.5,
        splitArrowCount: 3,
        explosiveRadius: 1.5,
        infiniteArrows: false,
        arrowPenetration: false,
        homingArrows: false,
        invincible: false,
    };

    // 当前破解属性
    const cheatProperties = { ...defaultCheatProperties };

    // 单人模式属性备份
    let singleModeProperties = { ...defaultCheatProperties };

    // === 本地存储功能 ===
    function saveCheatProperties() {
        try {
            localStorage.setItem('geometryDuel_cheatProperties', JSON.stringify(cheatProperties));
            console.log('破解属性已保存');
        } catch (error) {
            console.error('保存属性失败:', error);
        }
    }

    function loadCheatProperties() {
        try {
            const saved = localStorage.getItem('geometryDuel_cheatProperties');
            if (saved) {
                const loaded = JSON.parse(saved);
                Object.keys(defaultCheatProperties).forEach(key => {
                    if (loaded[key] !== undefined) {
                        cheatProperties[key] = loaded[key];
                    }
                });
                console.log('破解属性已加载');
                return true;
            }
        } catch (error) {
            console.error('加载属性失败:', error);
        }
        return false;
    }

    // 保存单人模式属性
    function saveSingleModeProperties() {
        if (gameMode === 'single') {
            singleModeProperties = { ...cheatProperties };
        }
    }

    // 恢复单人模式属性
    function restoreSingleModeProperties() {
        if (gameMode === 'single') {
            Object.assign(cheatProperties, singleModeProperties);
        } else {
            // 双人模式时重置为默认值
            Object.assign(cheatProperties, defaultCheatProperties);
        }
    }
    // === 本地存储功能结束 ===

    // === 创建界面元素 ===
    const container = document.createElement('div');
    container.style.cssText = 'position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:2147483647;background:#fff;opacity:0;pointer-events:none;';

    const canvas = document.createElement('canvas');
    canvas.width = CONFIG.WIDTH;
    canvas.height = CONFIG.HEIGHT;
    canvas.style.cssText = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);';
    container.appendChild(canvas);

    const modeBtn = document.createElement('div');
    modeBtn.textContent = '单人';
    modeBtn.style.cssText = 'position:absolute;top:20px;right:20px;padding:8px 16px;background:#000;color:#fff;font-family:monospace;font-size:14px;cursor:pointer;z-index:2147483648;opacity:0;pointer-events:none;';
    modeBtn.onclick = (e) => {
        e.stopPropagation();
        // 保存当前模式的属性
        saveSingleModeProperties();

        // 切换模式
        if (gameMode === 'single') {
            gameMode = 'dual';
            modeBtn.textContent = '双人';
        } else if (gameMode === 'dual') {
            gameMode = 'single';
            modeBtn.textContent = '单人';
        }

        // 恢复对应模式的属性
        restoreSingleModeProperties();

        resetGame();
        updateButtonVisibility();
    };
    container.appendChild(modeBtn);

    // AI模式按钮
    const aiModeBtn = document.createElement('div');
    aiModeBtn.textContent = 'AI: 增强';
    aiModeBtn.style.cssText = `
        position: absolute;
        top: 20px;
        left: 20px;
        padding: 8px 16px;
        background: #000;
        color: #fff;
        font-family: monospace;
        font-size: 14px;
        cursor: pointer;
        z-index: 2147483648;
        opacity: 0;
        pointer-events: none;
        border-radius: 4px;
        border: 2px solid #0ff;
    `;
    aiModeBtn.onclick = (e) => {
        e.stopPropagation();
        aiMode = aiMode === 'enhanced' ? 'simple' : 'enhanced';
        aiModeBtn.textContent = `AI: ${aiMode === 'enhanced' ? '增强' : '简单'}`;
        aiModeBtn.style.borderColor = aiMode === 'enhanced' ? '#0ff' : '#ff0';
    };
    container.appendChild(aiModeBtn);

    // 破解模式按钮
    const cheatModeBtn = document.createElement('div');
    cheatModeBtn.textContent = '破解模式: 关';
    cheatModeBtn.style.cssText = `
        position: absolute;
        top: 60px;
        left: 20px;
        padding: 8px 16px;
        background: #000;
        color: #0f0;
        font-family: monospace;
        font-size: 14px;
        cursor: pointer;
        z-index: 2147483648;
        opacity: 0;
        pointer-events: none;
        border-radius: 4px;
        border: 2px solid #0f0;
    `;
    cheatModeBtn.onclick = (e) => {
        e.stopPropagation();
        cheatMode = !cheatMode;
        cheatModeBtn.textContent = `破解模式: ${cheatMode ? '开' : '关'}`;
        cheatModeBtn.style.borderColor = cheatMode ? '#f00' : '#0f0';
        cheatModeBtn.style.color = cheatMode ? '#f00' : '#0f0';

        if (cheatMode) loadCheatProperties();
        updateButtonVisibility();

        cheatPanelVisible = false;
        updateCheatPanel();

        if (cheatMode && gameMode !== 'single') {
            gameMode = 'single';
            modeBtn.textContent = '单人';
            restoreSingleModeProperties();
            resetGame();
        }
    };
    container.appendChild(cheatModeBtn);

    // 属性面板按钮
    const cheatPanelBtn = document.createElement('div');
    cheatPanelBtn.textContent = '属性面板';
    cheatPanelBtn.style.cssText = `
        position: absolute;
        top: 100px;
        left: 20px;
        padding: 8px 16px;
        background: #000;
        color: #ff0;
        font-family: monospace;
        font-size: 14px;
        cursor: pointer;
        z-index: 2147483648;
        opacity: 0;
        pointer-events: none;
        border-radius: 4px;
        border: 2px solid #ff0;
    `;
    cheatPanelBtn.onclick = (e) => {
        e.stopPropagation();
        cheatPanelVisible = !cheatPanelVisible;
        updateCheatPanel();
    };
    container.appendChild(cheatPanelBtn);

    // 属性面板
    const cheatPanel = document.createElement('div');
    cheatPanel.id = 'cheatPanel';
    cheatPanel.style.cssText = `
        position: absolute;
        top: 140px;
        left: 20px;
        background: rgba(255, 255, 255, 0.95);
        color: #000;
        font-family: monospace;
        font-size: 12px;
        padding: 10px;
        border-radius: 4px;
        border: 2px solid #f00;
        z-index: 2147483648;
        opacity: 0;
        pointer-events: none;
        max-width: 300px;
        max-height: 400px;
        overflow-y: auto;
        box-shadow: 0 0 10px rgba(0,0,0,0.3);
    `;
    container.appendChild(cheatPanel);

    // 添加到页面
    if (document.body) {
        document.body.appendChild(container);
    } else {
        window.addEventListener('load', () => document.body.appendChild(container));
    }

    const ctx = canvas.getContext('2d');
    // === 界面元素创建结束 ===

    // === 辅助函数 ===
    function updateButtonVisibility() {
        if (fishMode) {
            modeBtn.style.opacity = '0';
            modeBtn.style.pointerEvents = 'none';
            aiModeBtn.style.opacity = '0';
            aiModeBtn.style.pointerEvents = 'none';
            cheatModeBtn.style.opacity = '0';
            cheatModeBtn.style.pointerEvents = 'none';
            cheatPanelBtn.style.opacity = '0';
            cheatPanelBtn.style.pointerEvents = 'none';
        } else {
            modeBtn.style.opacity = '1';
            modeBtn.style.pointerEvents = 'auto';
            aiModeBtn.style.opacity = gameMode === 'single' ? '1' : '0';
            aiModeBtn.style.pointerEvents = gameMode === 'single' ? 'auto' : 'none';
            cheatModeBtn.style.opacity = gameMode === 'single' ? '1' : '0';
            cheatModeBtn.style.pointerEvents = gameMode === 'single' ? 'auto' : 'none';
            cheatPanelBtn.style.opacity = (gameMode === 'single' && cheatMode) ? '1' : '0';
            cheatPanelBtn.style.pointerEvents = (gameMode === 'single' && cheatMode) ? 'auto' : 'none';
        }
    }

    // ESC隐藏/显示功能
    function toggleFish() {
        fishMode = !fishMode;
        container.style.opacity = fishMode ? '0' : '1';
        container.style.pointerEvents = fishMode ? 'none' : 'auto';
        updateButtonVisibility();
        cheatPanelVisible = false;
        updateCheatPanel();
    }

    // 属性面板UI生成
    function createSliderRow(label, property, value, min, max, step) {
        return `
            <div style="margin: 5px 0; display: flex; align-items: center;">
                <span style="flex: 1; min-width: 80px;">${label}:</span>
                <input type="range" id="slider_${property}" min="${min}" max="${max}" step="${step}" value="${value}" style="flex: 2; margin: 0 10px;">
                <span id="value_${property}" style="width: 40px; text-align: right;">${value.toFixed(1)}</span>
            </div>
        `;
    }

    function createCheckboxRow(label, property, checked) {
        return `
            <div style="margin: 5px 0; display: flex; align-items: center;">
                <input type="checkbox" id="checkbox_${property}" ${checked ? 'checked' : ''} style="margin-right: 8px;">
                <label for="checkbox_${property}" style="flex: 1;">${label}</label>
            </div>
        `;
    }

    function updateCheatPanel() {
        if (!cheatMode || !cheatPanelVisible || gameMode !== 'single') {
            cheatPanel.style.opacity = '0';
            cheatPanel.style.pointerEvents = 'none';
            return;
        }

        // 加载当前属性
        loadCheatProperties();

        cheatPanel.style.opacity = '1';
        cheatPanel.style.pointerEvents = 'auto';

        let html = '<div style="margin-bottom: 10px; font-weight: bold; text-align: center; color: #f00;">破解属性面板</div>';

        // 基础属性
        html += '<div style="margin-bottom: 5px; font-weight: bold; border-bottom: 1px solid #ccc;">基础属性</div>';
        html += createSliderRow('玩家速度', 'playerSpeed', cheatProperties.playerSpeed, 0.5, 3.0, 0.1);
        html += createSliderRow('射击间隔', 'arrowCooldown', cheatProperties.arrowCooldown, 0.1, 1.0, 0.1);
        html += createSliderRow('箭矢速度', 'arrowSpeed', cheatProperties.arrowSpeed, 0.5, 3.0, 0.1);
        html += createSliderRow('击退力', 'knockbackForce', cheatProperties.knockbackForce, 0.5, 3.0, 0.1);
        html += createSliderRow('玩家体积', 'playerSize', cheatProperties.playerSize, 0.5, 1.5, 0.1);
        html += createSliderRow('箭矢体积', 'arrowSize', cheatProperties.arrowSize, 0.5, 2.0, 0.1);
        html += createSliderRow('受击退', 'knockbackResist', cheatProperties.knockbackResist, 0.1, 2.0, 0.1);
        html += createSliderRow('蓄力时间', 'chargeTime', cheatProperties.chargeTime, 0.1, 1.0, 0.1);

        // 箭矢属性
        html += '<div style="margin: 10px 0 5px 0; font-weight: bold; border-bottom: 1px solid #ccc;">箭矢属性</div>';
        html += createSliderRow('分裂箭数量', 'splitArrowCount', cheatProperties.splitArrowCount, 2, 5, 1);
        html += createSliderRow('爆炸范围', 'explosiveRadius', cheatProperties.explosiveRadius, 0.5, 3.0, 0.1);

        // 特殊功能
        html += '<div style="margin: 10px 0 5px 0; font-weight: bold; border-bottom: 1px solid #ccc;">特殊功能</div>';
        html += createCheckboxRow('无限箭矢', 'infiniteArrows', cheatProperties.infiniteArrows);
        html += createCheckboxRow('穿透箭矢', 'arrowPenetration', cheatProperties.arrowPenetration);
        html += createCheckboxRow('追踪箭矢', 'homingArrows', cheatProperties.homingArrows);
        html += createCheckboxRow('无敌模式', 'invincible', cheatProperties.invincible);

        // 控制按钮
        html += '<div style="margin-top: 15px; text-align: center;">';
        html += '<button id="savePropertiesBtn" style="padding: 5px 15px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer; font-family: monospace;">保存</button>';
        html += '<button id="resetPropertiesBtn" style="padding: 5px 15px; margin-left: 10px; background: #f44336; color: white; border: none; border-radius: 3px; cursor: pointer; font-family: monospace;">重置</button>';
        html += '</div>';

        html += '<div id="saveStatus" style="margin-top: 10px; font-size: 11px; color: #666; text-align: center;"></div>';

        cheatPanel.innerHTML = html;

        // 绑定事件
        document.querySelectorAll('#cheatPanel input[type="range"]').forEach(slider => {
            slider.addEventListener('input', (e) => {
                const property = e.target.id.replace('slider_', '');
                const value = parseFloat(e.target.value);
                cheatProperties[property] = value;
                e.target.nextElementSibling.textContent = value.toFixed(1);
                saveCheatProperties();
            });
        });

        document.querySelectorAll('#cheatPanel input[type="checkbox"]').forEach(checkbox => {
            checkbox.addEventListener('change', (e) => {
                const property = e.target.id.replace('checkbox_', '');
                cheatProperties[property] = e.target.checked;
                saveCheatProperties();
            });
        });

        // 保存按钮
        const saveBtn = document.getElementById('savePropertiesBtn');
        if (saveBtn) {
            saveBtn.addEventListener('click', () => {
                saveCheatProperties();
                const statusEl = document.getElementById('saveStatus');
                if (statusEl) {
                    statusEl.textContent = '属性已保存!';
                    setTimeout(() => { statusEl.textContent = ''; }, 2000);
                }
            });
        }

        // 重置按钮
        const resetBtn = document.getElementById('resetPropertiesBtn');
        if (resetBtn) {
            resetBtn.addEventListener('click', () => {
                Object.assign(cheatProperties, defaultCheatProperties);
                saveCheatProperties();
                updateCheatPanel();

                const statusEl = document.getElementById('saveStatus');
                if (statusEl) {
                    statusEl.textContent = '已重置为默认值!';
                    setTimeout(() => { statusEl.textContent = ''; }, 2000);
                }
            });
        }
    }

    // === 数学工具类 ===
    class Vec {
        constructor(x, y) { this.x = x; this.y = y; }
        add(v) { return new Vec(this.x + v.x, this.y + v.y); }
        sub(v) { return new Vec(this.x - v.x, this.y - v.y); }
        mult(n) { return new Vec(this.x * n, this.y * n); }
        mag() { return Math.sqrt(this.x * this.x + this.y * this.y); }
        norm() { const m = this.mag(); return m ? new Vec(this.x / m, this.y / m) : new Vec(0, 0); }
        angle() { return Math.atan2(this.y, this.x); }
        dist(v) { return Math.sqrt((this.x - v.x) ** 2 + (this.y - v.y) ** 2); }
    }

    // === 粒子系统 ===
    function addSquareParticle(x, y, vx, vy, size, life, color = '#000') {
        if (game.particles.length >= CONFIG.MAX_PARTICLES) return;
        game.particles.push({
            x, y, vx, vy, size, life, maxLife: life, color,
            rot: Math.random() * Math.PI,
            rotSpeed: (Math.random() - 0.5) * 0.2
        });
    }

    function updateParticles() {
        for (let i = game.particles.length - 1; i >= 0; i--) {
            const p = game.particles[i];
            p.x += p.vx;
            p.y += p.vy;
            p.vx *= 0.96;
            p.vy *= 0.96;
            p.rot += p.rotSpeed;
            p.life -= 0.03;
            if (p.life <= 0) game.particles.splice(i, 1);
        }
    }

    function drawParticles(ctx) {
        for (const p of game.particles) {
            const a = p.life / p.maxLife;
            ctx.globalAlpha = a * 0.9;
            ctx.save();
            ctx.translate(p.x, p.y);
            ctx.rotate(p.rot);
            const s = p.size * a;
            ctx.fillStyle = p.color;
            ctx.fillRect(-s / 2, -s / 2, s, s);
            ctx.strokeStyle = '#000';
            ctx.lineWidth = 1.5;
            ctx.strokeRect(-s / 2, -s / 2, s, s);
            ctx.restore();
        }
        ctx.globalAlpha = 1;
    }

    // === 游戏核心类 ===
    class Arrow {
        constructor(owner, pos, target, type = 'normal', offsetAngle = 0) {
            this.owner = owner;
            this.type = type;
            this.x = pos.x;
            this.y = pos.y;
            this.spawnX = pos.x;
            this.spawnY = pos.y;

            let dir = target.sub(pos);
            if (offsetAngle !== 0) {
                const angle = dir.angle() + offsetAngle;
                dir = new Vec(Math.cos(angle), Math.sin(angle));
            } else {
                dir = dir.norm();
            }

            // 基础速度
            let speed = CONFIG.ARROW_SPEED;

            // 修复bug:只在单人破解模式下应用箭矢速度增强
            if (cheatMode && owner === 'p1' && gameMode === 'single') {
                speed *= cheatProperties.arrowSpeed;
            }

            this.vx = dir.x * speed;
            this.vy = dir.y * speed;
            this.angle = dir.angle();
            this.active = true;
            this.life = 200;

            // 修复bug:只在单人破解模式下应用箭矢体积
            this.arrowSizeMultiplier = (cheatMode && owner === 'p1' && gameMode === 'single') ?
                cheatProperties.arrowSize : 1.0;

            // 爆炸箭属性
            this.exploded = false;

            // 分裂箭属性
            this.isSplitChild = offsetAngle !== 0;
            this.splitTarget = target;
            this.splitCurveTime = 0;

            // 修复bug:只在单人破解模式下应用特殊功能
            this.homing = (cheatMode && cheatProperties.homingArrows && owner === 'p1' && gameMode === 'single');
            this.penetration = (cheatMode && cheatProperties.arrowPenetration && owner === 'p1' && gameMode === 'single');
            this.penetrated = false;
        }

        getTip() {
            return {
                x: this.x + Math.cos(this.angle) * 8 * this.arrowSizeMultiplier,
                y: this.y + Math.sin(this.angle) * 8 * this.arrowSizeMultiplier
            };
        }

        getTail() {
            return {
                x: this.x - Math.cos(this.angle) * 8 * this.arrowSizeMultiplier,
                y: this.y - Math.sin(this.angle) * 8 * this.arrowSizeMultiplier
            };
        }

        getHitRadius() {
            return 10 * this.arrowSizeMultiplier;
        }

        // 爆炸效果 - 恢复上一版效果
        createExplosion() {
            if (this.exploded) return;
            this.exploded = true;
            this.active = false;

            // 基础爆炸范围
            const baseRadius = 200;

            // 修复bug:只在单人破解模式下应用爆炸范围增强
            const explosionRadius = (cheatMode && this.owner === 'p1' && gameMode === 'single') ?
                baseRadius * cheatProperties.explosiveRadius : baseRadius;
            const centerForce = 25;

            // 产生黑色方形粒子
            for(let i = 0; i < 40; i++) {
                const angle = Math.random() * Math.PI * 2;
                const dist = Math.random() * explosionRadius;
                const x = this.x + Math.cos(angle) * dist;
                const y = this.y + Math.sin(angle) * dist;
                const force = centerForce * (1 - dist/explosionRadius) * 0.3;
                addSquareParticle(
                    x, y,
                    Math.cos(angle) * force,
                    Math.sin(angle) * force,
                    3 + Math.random() * 4,
                    0.8,
                    '#000'
                );
            }

            // 对玩家造成击退效果
            const players = [game.p1, game.p2].filter(p => p);
            for(const player of players) {
                const dx = player.pos.x - this.x;
                const dy = player.pos.y - this.y;
                const dist = Math.sqrt(dx*dx + dy*dy);

                if(dist < explosionRadius) {
                    let force = centerForce * (1 - dist/explosionRadius);

                    // 修复bug:只在单人破解模式下增强击退力
                    if (cheatMode && this.owner === 'p1' && gameMode === 'single') {
                        force *= cheatProperties.knockbackForce;
                    }

                    const knockbackDir = new Vec(dx, dy).norm();
                    player.vel.x += knockbackDir.x * force;
                    player.vel.y += knockbackDir.y * force;
                }
            }

            // 对其他箭矢造成影响
            for(const other of game.arrows) {
                if(other === this || !other.active) continue;
                const dx = other.x - this.x;
                const dy = other.y - this.y;
                const dist = Math.sqrt(dx*dx + dy*dy);

                if(dist < explosionRadius) {
                    other.active = false;
                    for(let i = 0; i < 8; i++) {
                        const a = Math.random() * Math.PI * 2;
                        addSquareParticle(
                            other.x, other.y,
                            Math.cos(a)*2,
                            Math.sin(a)*2,
                            4, 0.7, '#000'
                        );
                    }
                }
            }
        }

        update() {
            if (!this.active) return false;

            // 追踪箭逻辑
            if (this.homing && !this.isSplitChild) {
                const targetPlayer = this.owner === 'p1' ? game.p2 : game.p1;
                if (targetPlayer) {
                    const dx = targetPlayer.pos.x - this.x;
                    const dy = targetPlayer.pos.y - this.y;
                    const dist = Math.sqrt(dx * dx + dy * dy);

                    if (dist > 0) {
                        const targetDir = new Vec(dx, dy).norm();
                        const currentDir = new Vec(this.vx, this.vy).norm();
                        const blend = 0.1;
                        const newDir = new Vec(
                            currentDir.x * (1 - blend) + targetDir.x * blend,
                            currentDir.y * (1 - blend) + targetDir.y * blend
                        ).norm();

                        const speed = Math.sqrt(this.vx * this.vx + this.vy * this.vy);
                        this.vx = newDir.x * speed;
                        this.vy = newDir.y * speed;
                        this.angle = newDir.angle();
                    }
                }
            }

            // 分裂箭子箭的弧形飞行 - 恢复上一版效果
            if (this.type === 'split' && this.isSplitChild) {
                this.splitCurveTime += 0.02;

                // 获取当前目标(实时的玩家位置)
                const targetPlayer = this.owner === 'p1' ? game.p2 : game.p1;
                if (targetPlayer) {
                    const currentTarget = targetPlayer.pos;
                    const dx = currentTarget.x - this.x;
                    const dy = currentTarget.y - this.y;
                    const dist = Math.sqrt(dx*dx + dy*dy);

                    if (dist > 0) {
                        // 计算向当前目标位置的方向
                        const targetDir = new Vec(dx, dy).norm();
                        const currentDir = new Vec(this.vx, this.vy).norm();

                        // 增强追踪修正,让子箭能跟上被击退的玩家
                        const blend = 0.1;
                        const newDir = new Vec(
                            currentDir.x * (1-blend) + targetDir.x * blend,
                            currentDir.y * (1-blend) + targetDir.y * blend
                        ).norm();

                        const speed = Math.sqrt(this.vx*this.vx + this.vy*this.vy);
                        this.vx = newDir.x * speed;
                        this.vy = newDir.y * speed;
                        this.angle = newDir.angle();

                        // 更新目标位置
                        this.splitTarget = currentTarget;
                    }
                }
            }

            this.x += this.vx;
            this.y += this.vy;
            this.life--;

            // 边界检测
            if (this.penetration) {
                // 穿透箭矢
                if (this.x < 0) this.x = CONFIG.WIDTH;
                else if (this.x > CONFIG.WIDTH) this.x = 0;
                if (this.y < 0) this.y = CONFIG.HEIGHT;
                else if (this.y > CONFIG.HEIGHT) this.y = 0;
            } else if (this.x < 0 || this.x > CONFIG.WIDTH || this.y < 0 || this.y > CONFIG.HEIGHT) {
                if (this.type === 'explosion') {
                    this.createExplosion();
                }
                return false;
            }

            // 粒子效果
            if (game.frame % 4 === 0) {
                addSquareParticle(this.x, this.y, 0, 0, 2, 0.5, '#000');
            }

            // 寿命检查
            if (this.life <= 0) {
                if (this.type === 'explosion') {
                    this.createExplosion();
                }
                return false;
            }

            // 箭矢碰撞
            const myTip = this.getTip();
            for (const other of game.arrows) {
                if (other === this || !other.active || other.owner === this.owner) continue;
                const otherTip = other.getTip();
                const tipDist = Math.sqrt((myTip.x - otherTip.x) ** 2 + (myTip.y - otherTip.y) ** 2);
                if (tipDist < 10) {
                    // 碰撞粒子
                    for (let i = 0; i < 8; i++) {
                        const a = Math.random() * Math.PI * 2;
                        addSquareParticle(
                            (myTip.x + otherTip.x) / 2,
                            (myTip.y + otherTip.y) / 2,
                            Math.cos(a) * 2,
                            Math.sin(a) * 2,
                            4, 0.7, '#000'
                        );
                    }

                    other.active = false;

                    // 爆炸箭碰撞时爆炸
                    if (this.type === 'explosion' && !this.exploded) {
                        this.createExplosion();
                    }
                    if (other.type === 'explosion' && !other.exploded) {
                        other.createExplosion();
                    }

                    return false;
                }
            }

            // 命中玩家
            const target = this.owner === 'p1' ? game.p2 : game.p1;
            const tip = this.getTip();
            const dist = Math.sqrt((tip.x - target.pos.x) ** 2 + (tip.y - target.pos.y) ** 2);

            if (dist < target.size) {
                // 无敌模式检测
                if (cheatMode && cheatProperties.invincible && target.id === 'p1' && gameMode === 'single') {
                    return false;
                }

                if (this.type === 'explosion' && !this.exploded) {
                    this.createExplosion();
                } else {
                    // 普通击退
                    let knockbackForce = 15;

                    // 修复bug:只在单人破解模式下应用击退力增强
                    if (cheatMode && this.owner === 'p1' && gameMode === 'single') {
                        knockbackForce *= cheatProperties.knockbackForce;
                    }

                    const knockbackDir = new Vec(Math.cos(this.angle), Math.sin(this.angle));

                    // 修复bug:只在单人破解模式下应用受击退减弱
                    if (cheatMode && target.id === 'p1' && gameMode === 'single') {
                        knockbackForce *= cheatProperties.knockbackResist;
                    }

                    target.vel.x = knockbackDir.x * knockbackForce;
                    target.vel.y = knockbackDir.y * knockbackForce;

                    // 命中粒子
                    for (let i = 0; i < 12; i++) {
                        const a = Math.random() * Math.PI * 2;
                        addSquareParticle(tip.x, tip.y, Math.cos(a) * 2, Math.sin(a) * 2, 4, 0.9, '#000');
                    }
                }

                // 穿透箭矢不消失
                if (!this.penetration) {
                    return false;
                }
            }

            return true;
        }

        draw(ctx) {
            ctx.save();
            ctx.translate(this.x, this.y);
            ctx.rotate(this.angle);

            ctx.strokeStyle = '#000';
            ctx.fillStyle = '#000';

            const sizeMult = this.arrowSizeMultiplier;

            ctx.lineWidth = 2;
            ctx.beginPath();
            ctx.moveTo(-12 * sizeMult, 0);
            ctx.lineTo(8 * sizeMult, 0);
            ctx.stroke();

            ctx.beginPath();
            ctx.moveTo(10 * sizeMult, 0);
            ctx.lineTo(2 * sizeMult, 4 * sizeMult);
            ctx.lineTo(2 * sizeMult, -4 * sizeMult);
            ctx.closePath();
            ctx.fill();

            ctx.strokeStyle = '#000';
            ctx.lineWidth = 1;
            ctx.beginPath();
            ctx.moveTo(-10 * sizeMult, -3 * sizeMult);
            ctx.lineTo(-14 * sizeMult, 0);
            ctx.lineTo(-10 * sizeMult, 3 * sizeMult);
            ctx.stroke();

            ctx.restore();
        }
    }

    class AimLine {
        constructor(owner, pos, angle) {
            this.owner = owner;
            this.x = pos.x;
            this.y = pos.y;
            this.angle = angle;
            this.life = Math.ceil(CONFIG.AIM_LINE_TIME / 16.67);
            this.active = true;
            this.maxLife = this.life;
        }

        update() {
            const player = this.owner === 'p1' ? game.p1 : game.p2;
            this.x = player.pos.x;
            this.y = player.pos.y;
            this.life--;
            if (this.life <= 0) {
                this.fireLaser();
                return false;
            }
            return true;
        }

        fireLaser() {
            const player = this.owner === 'p1' ? game.p1 : game.p2;
            const target = this.owner === 'p1' ? game.p2 : game.p1;
            const dir = new Vec(Math.cos(this.angle), Math.sin(this.angle));
            let hit = false;
            let laserLength = 2000;

            for (let i = 0; i < 200; i++) {
                const checkX = this.x + dir.x * i * 10;
                const checkY = this.y + dir.y * i * 10;
                if (checkX < 0 || checkX > CONFIG.WIDTH || checkY < 0 || checkY > CONFIG.HEIGHT) {
                    laserLength = i * 10;
                    break;
                }
                const dist = Math.sqrt((checkX - target.pos.x) ** 2 + (checkY - target.pos.y) ** 2);
                if (dist < target.size) {
                    hit = true;
                    if (this.owner === 'p1') {
                        winner = 'p1';
                        winningPlayer = gameMode === 'single' ? '您' : 'P1';
                    } else {
                        winner = 'p2';
                        winningPlayer = gameMode === 'single' ? 'AI' : 'P2';
                    }
                    break;
                }
            }

            screenShake = hit ? 30 : 15;
            shakeTimer = CONFIG.SHAKE_DURATION;

            if (hit) {
                gameState = 'killcam';
            }
        }

        draw(ctx) {
            const dir = new Vec(Math.cos(this.angle), Math.sin(this.angle));
            const endX = this.x + dir.x * 1200;
            const endY = this.y + dir.y * 1200;
            const progress = 1 - (this.life / this.maxLife);
            const flash = 0.5 + Math.sin(Date.now() / 15) * 0.3 + progress * 0.4;
            const alpha = Math.min(1, flash);
            ctx.strokeStyle = `rgba(255,30,30,${alpha})`;
            ctx.lineWidth = 2 + progress * 2;
            ctx.setLineDash([10 - progress * 5, 5]);
            ctx.beginPath();
            ctx.moveTo(this.x, this.y);
            ctx.lineTo(endX, endY);
            ctx.stroke();
            ctx.setLineDash([]);
        }
    }

    class Player {
        constructor(id, isAI) {
            this.id = id;
            this.isAI = isAI;
            this.arrowType = id === 'p1' ? arrowType.p1 : arrowType.p2;
            this.reset();
        }

        reset() {
            this.pos = new Vec(this.id === 'p1' ? 150 : CONFIG.WIDTH - 150, this.id === 'p1' ? CONFIG.HEIGHT - 150 : 150);
            this.vel = new Vec(0, 0);
            this.size = CONFIG.SIZE;
            this.rot = 0;
            this.charging = false;
            this.chargeStart = 0;
            this.chargeLevel = 0;
            this.chargeAng = 0;
            this.aimAngle = 0;
            this.lastAttack = 0;
            this.aiTimer = 0;
            this.canUlt = true;
        }

        get speed() {
            if (this.charging) return CONFIG.SPEED.CHARGE;
            if (cheatMode && this.id === 'p1' && gameMode === 'single') {
                return CONFIG.SPEED.NORMAL * cheatProperties.playerSpeed;
            }
            return CONFIG.SPEED.NORMAL;
        }

        get displaySize() {
            if (cheatMode && this.id === 'p1' && gameMode === 'single') {
                return CONFIG.SIZE * cheatProperties.playerSize;
            }
            return CONFIG.SIZE;
        }

        getCooldown() {
            if (this.arrowType === 'normal') return CONFIG.COOLDOWNS.NORMAL;
            if (this.arrowType === 'explosion') return CONFIG.COOLDOWNS.EXPLOSION;
            if (this.arrowType === 'split') return CONFIG.COOLDOWNS.SPLIT;
            return CONFIG.COOLDOWNS.NORMAL;
        }

        update() {
            this.pos.x += this.vel.x;
            this.pos.y += this.vel.y;

            const currentSize = this.displaySize;

            if (this.pos.x < currentSize) {
                this.pos.x = currentSize;
                this.vel.x = Math.abs(this.vel.x) * 0.7;
            } else if (this.pos.x > CONFIG.WIDTH - currentSize) {
                this.pos.x = CONFIG.WIDTH - currentSize;
                this.vel.x = -Math.abs(this.vel.x) * 0.7;
            }
            if (this.pos.y < currentSize) {
                this.pos.y = currentSize;
                this.vel.y = Math.abs(this.vel.y) * 0.7;
            } else if (this.pos.y > CONFIG.HEIGHT - currentSize) {
                this.pos.y = CONFIG.HEIGHT - currentSize;
                this.vel.y = -Math.abs(this.vel.y) * 0.7;
            }

            this.vel.x *= 0.94;
            this.vel.y *= 0.94;

            if (Math.abs(this.vel.x) + Math.abs(this.vel.y) > 0.5) {
                let targetRot = Math.atan2(this.vel.y, this.vel.x);
                let diff = targetRot - this.rot;
                while (diff > Math.PI) diff -= Math.PI * 2;
                while (diff < -Math.PI) diff += Math.PI * 2;
                this.rot += diff * 0.12;
            }

            if (this.charging) {
                if (cheatMode && this.id === 'p1' && gameMode === 'single') {
                    this.chargeLevel = Math.min((Date.now() - this.chargeStart) / (CONFIG.CHARGE_TIME * cheatProperties.chargeTime), 1);
                } else {
                    this.chargeLevel = Math.min((Date.now() - this.chargeStart) / CONFIG.CHARGE_TIME, 1);
                }
                this.chargeAng += 0.15;
                const target = this.id === 'p1' ? game.p2.pos : game.p1.pos;
                this.aimAngle = target.sub(this.pos).angle();
            }

            if (typeDisplayTimer[this.id] > 0) {
                typeDisplayTimer[this.id]--;
            }
        }

        move(dir) {
            if (dir.mag() > 0) {
                const want = dir.norm().mult(this.speed);
                this.vel.x += (want.x - this.vel.x) * 0.15;
                this.vel.y += (want.y - this.vel.y) * 0.15;
            }
        }

        switchArrowType(newType) {
            if (this.arrowType !== newType) {
                this.arrowType = newType;
                arrowType[this.id] = newType;
                typeDisplayTimer[this.id] = 60;
            }
        }

        cycleArrowType() {
            const types = ['normal', 'explosion', 'split'];
            const currentIndex = types.indexOf(this.arrowType);
            const nextIndex = (currentIndex + 1) % types.length;
            this.switchArrowType(types[nextIndex]);
        }

        shoot(target) {
            let cooldown = this.getCooldown();

            if (cheatMode && this.id === 'p1' && gameMode === 'single') {
                cooldown *= cheatProperties.arrowCooldown;
            }

            if (!(cheatMode && cheatProperties.infiniteArrows && this.id === 'p1' && gameMode === 'single')) {
                if (Date.now() - this.lastAttack < cooldown) return;
            }

            this.lastAttack = Date.now();

            if (this.arrowType === 'split') {
                // 恢复上一版分裂箭效果
                const mainAngle = target.sub(this.pos).angle();
                const dist = target.sub(this.pos).mag();
                const predictTime = dist / CONFIG.ARROW_SPEED;
                const predictTarget = new Vec(
                    target.x + (this.id === 'p1' ? game.p2.vel.x : game.p1.vel.x) * predictTime * 0.5,
                    target.y + (this.id === 'p1' ? game.p2.vel.y : game.p1.vel.y) * predictTime * 0.5
                );

                // 修复bug:只在单人破解模式下应用分裂箭数量增强
                const splitCount = (cheatMode && this.id === 'p1' && gameMode === 'single') ?
                    cheatProperties.splitArrowCount : 3;

                if (splitCount === 3) {
                    // 主箭
                    const mainArrow = new Arrow(this.id, this.pos, predictTarget, 'split');
                    game.arrows.push(mainArrow);

                    // 左侧子箭 (-15度)
                    const leftTarget = new Vec(
                        this.pos.x + Math.cos(mainAngle - Math.PI/12) * dist,
                        this.pos.y + Math.sin(mainAngle - Math.PI/12) * dist
                    );
                    const leftArrow = new Arrow(this.id, this.pos, leftTarget, 'split', -Math.PI/12);
                    leftArrow.isSplitChild = true;
                    leftArrow.splitTarget = predictTarget;
                    game.arrows.push(leftArrow);

                    // 右侧子箭 (+15度)
                    const rightTarget = new Vec(
                        this.pos.x + Math.cos(mainAngle + Math.PI/12) * dist,
                        this.pos.y + Math.sin(mainAngle + Math.PI/12) * dist
                    );
                    const rightArrow = new Arrow(this.id, this.pos, rightTarget, 'split', Math.PI/12);
                    rightArrow.isSplitChild = true;
                    rightArrow.splitTarget = predictTarget;
                    game.arrows.push(rightArrow);
                } else if (splitCount === 2) {
                    // 2支箭
                    const angles = [-Math.PI/8, Math.PI/8];
                    for (const angle of angles) {
                        const adjustedTarget = new Vec(
                            this.pos.x + Math.cos(mainAngle + angle) * dist,
                            this.pos.y + Math.sin(mainAngle + angle) * dist
                        );
                        const arrow = new Arrow(this.id, this.pos, adjustedTarget, 'split', angle);
                        arrow.isSplitChild = true;
                        arrow.splitTarget = predictTarget;
                        game.arrows.push(arrow);
                    }
                } else if (splitCount === 4) {
                    // 4支箭
                    const angles = [-Math.PI/6, -Math.PI/12, Math.PI/12, Math.PI/6];
                    for (const angle of angles) {
                        const adjustedTarget = new Vec(
                            this.pos.x + Math.cos(mainAngle + angle) * dist,
                            this.pos.y + Math.sin(mainAngle + angle) * dist
                        );
                        const arrow = new Arrow(this.id, this.pos, adjustedTarget, 'split', angle);
                        arrow.isSplitChild = true;
                        arrow.splitTarget = predictTarget;
                        game.arrows.push(arrow);
                    }
                } else if (splitCount === 5) {
                    // 5支箭
                    const angles = [-Math.PI/6, -Math.PI/12, 0, Math.PI/12, Math.PI/6];
                    for (const angle of angles) {
                        const adjustedTarget = new Vec(
                            this.pos.x + Math.cos(mainAngle + angle) * dist,
                            this.pos.y + Math.sin(mainAngle + angle) * dist
                        );
                        const arrow = new Arrow(this.id, this.pos, adjustedTarget, 'split', angle);
                        arrow.isSplitChild = true;
                        arrow.splitTarget = predictTarget;
                        game.arrows.push(arrow);
                    }
                }
            } else {
                game.arrows.push(new Arrow(this.id, this.pos, target, this.arrowType));
            }
        }

        startCharge() {
            if (this.charging) return;
            this.charging = true;
            this.chargeStart = Date.now();
            const target = this.id === 'p1' ? game.p2.pos : game.p1.pos;
            this.aimAngle = target.sub(this.pos).angle();
        }

        releaseCharge() {
            if (!this.charging) return;
            const full = this.chargeLevel >= 1;
            this.charging = false;
            this.chargeLevel = 0;
            if (full && this.canUlt) game.aimLines.push(new AimLine(this.id, this.pos, this.aimAngle));
        }

        draw(ctx) {
            ctx.save();
            ctx.translate(this.pos.x, this.pos.y);
            ctx.rotate(this.rot);

            const currentSize = this.displaySize;

            if (this.id === 'p1') {
                ctx.fillStyle = '#fff';
                ctx.fillRect(-currentSize / 2, -currentSize / 2, currentSize, currentSize);
                ctx.strokeStyle = '#000';
                ctx.lineWidth = 2.5;
                ctx.strokeRect(-currentSize / 2, -currentSize / 2, currentSize, currentSize);
            } else {
                ctx.fillStyle = '#000';
                ctx.fillRect(-currentSize / 2, -currentSize / 2, currentSize, currentSize);
                ctx.strokeStyle = '#fff';
                ctx.lineWidth = 2.5;
                ctx.strokeRect(-currentSize / 2, -currentSize / 2, currentSize, currentSize);
            }
            ctx.restore();

            if (typeDisplayTimer[this.id] > 0) {
                const typeText = {
                    'normal': '普通',
                    'explosion': '爆炸',
                    'split': '分裂'
                }[this.arrowType] || '普通';

                ctx.fillStyle = this.id === 'p1' ?
                    `rgba(0,0,0,0.8)` :
                    `rgba(255,255,255,0.8)`;
                ctx.font = 'bold 16px monospace';
                ctx.textAlign = 'center';
                ctx.fillText(typeText, this.pos.x, this.pos.y - 40);
            }

            if (this.charging) {
                const r = currentSize + 16;
                const alpha = 0.4 + this.chargeLevel * 0.6;
                ctx.save();
                ctx.translate(this.pos.x, this.pos.y);
                ctx.rotate(this.chargeAng);
                ctx.strokeStyle = `rgba(0,0,0,${alpha})`;
                ctx.lineWidth = 3;
                ctx.beginPath();
                ctx.arc(0, 0, r, 0.2, Math.PI * 0.8);
                ctx.stroke();
                ctx.rotate(-this.chargeAng * 1.5);
                ctx.strokeStyle = `rgba(0,0,0,${alpha * 0.8})`;
                ctx.lineWidth = 2;
                ctx.beginPath();
                ctx.arc(0, 0, r - 8, Math.PI * 1.2, -0.2);
                ctx.stroke();
                ctx.restore();

                if (this.chargeLevel >= 1) {
                    ctx.fillStyle = `rgba(200,0,0,${0.5 + Math.sin(Date.now() / 20) * 0.5})`;
                    ctx.font = 'bold 18px monospace';
                    ctx.textAlign = 'center';
                    ctx.fillText('MAX', this.pos.x, this.pos.y - 50);
                }
            }
        }
    }

    // === 游戏逻辑函数 ===
    function resetGame() {
        game.p1 = new Player('p1', false);
        game.p2 = new Player('p2', gameMode === 'single');

        if (cheatMode) {
            loadCheatProperties();
        }

        game.arrows = [];
        game.aimLines = [];
        game.particles = [];
        gameState = 'playing';
        screenShake = 0;
        shakeTimer = 0;
        winner = '';
        winningPlayer = '';

        updateButtonVisibility();
        cheatPanelVisible = false;
        updateCheatPanel();
    }

    function update() {
        if (gameState === 'killcam') {
            if (shakeTimer > 0) {
                shakeTimer--;
                if (shakeTimer <= 0) screenShake = 0;
            } else screenShake = 0;
            updateParticles();
            return;
        }
        if (gameState !== 'playing') return;
        game.frame++;

        if (shakeTimer > 0) {
            shakeTimer--;
            if (shakeTimer <= 0) screenShake = 0;
        } else if (screenShake > 0) {
            screenShake *= 0.9;
            if (screenShake < 0.5) screenShake = 0;
        }

        if (game.p1) {
            let moveDir = new Vec(0, 0);
            if (game.keys['w']) moveDir.y -= 1;
            if (game.keys['s']) moveDir.y += 1;
            if (game.keys['a']) moveDir.x -= 1;
            if (game.keys['d']) moveDir.x += 1;
            game.p1.move(moveDir);
            game.p1.update();
        }

        if (game.p2) {
            if (game.p2.isAI) {
                // 简化AI逻辑
                const target = game.p1.pos;
                const dx = target.x - game.p2.pos.x;
                const dy = target.y - game.p2.pos.y;
                const dist = Math.sqrt(dx * dx + dy * dy);

                let moveDir = new Vec(0, 0);
                if (dist > 250) {
                    moveDir = new Vec(dx, dy).norm().mult(0.6);
                } else if (dist < 100) {
                    moveDir = new Vec(-dx, -dy).norm().mult(0.4);
                } else {
                    const sideDir = new Vec(-dy, dx);
                    if (Math.random() > 0.5) sideDir.mult(-1);
                    moveDir = sideDir.norm().mult(0.3);
                }

                game.p2.move(moveDir);

                if (game.p2.aiTimer++ > 12 && dist > 80 && dist < 400) {
                    if (Math.random() < 0.2) {
                        game.p2.shoot(target);
                        game.p2.aiTimer = 0;
                    }
                }
            } else {
                let moveDir = new Vec(0, 0);
                if (game.keys['i']) moveDir.y -= 1;
                if (game.keys['k']) moveDir.y += 1;
                if (game.keys['j']) moveDir.x -= 1;
                if (game.keys['l']) moveDir.x += 1;
                game.p2.move(moveDir);
            }
            game.p2.update();
        }

        game.arrows = game.arrows.filter(a => a.update());
        game.aimLines = game.aimLines.filter(l => l.update());
        updateParticles();
    }

    function draw() {
        // 注意:这里没有fishMode检查,由CSS控制显示/隐藏

        let sx = 0, sy = 0;
        if (screenShake > 0) {
            sx = (Math.random() - 0.5) * screenShake;
            sy = (Math.random() - 0.5) * screenShake;
        }

        ctx.save();
        ctx.translate(sx, sy);

        // 背景
        ctx.fillStyle = '#fff';
        ctx.fillRect(0, 0, CONFIG.WIDTH, CONFIG.HEIGHT);

        // 网格
        ctx.strokeStyle = '#ddd';
        ctx.lineWidth = 1;
        game.bgOffset = (game.bgOffset + 0.5) % 40;
        for (let i = -40; i < CONFIG.WIDTH + 40; i += 40) {
            ctx.beginPath();
            ctx.moveTo(i - game.bgOffset, 0);
            ctx.lineTo(i - game.bgOffset, CONFIG.HEIGHT);
            ctx.stroke();
        }
        for (let i = -40; i < CONFIG.HEIGHT + 40; i += 40) {
            ctx.beginPath();
            ctx.moveTo(0, i - game.bgOffset);
            ctx.lineTo(CONFIG.WIDTH, i - game.bgOffset);
            ctx.stroke();
        }

        // 游戏对象
        if (game.p1) game.p1.draw(ctx);
        if (game.p2) game.p2.draw(ctx);
        for (const arrow of game.arrows) arrow.draw(ctx);
        for (const line of game.aimLines) line.draw(ctx);

        // 粒子
        drawParticles(ctx);

        // UI
        ctx.fillStyle = '#666';
        ctx.font = '14px monospace';
        ctx.textAlign = 'left';

        if (gameMode === 'single') {
            ctx.fillText('P1: WASD移动 | J射击 | K蓄力 | L切换箭矢', 20, 30);
            ctx.fillText(`AI模式: ${aiMode === 'enhanced' ? '增强' : '简单'}`, 20, 50);

            if (game.p1) {
                const arrowTypeText = {
                    'normal': '普通',
                    'explosion': '爆炸',
                    'split': '分裂'
                };
                ctx.fillText(`当前箭矢: ${arrowTypeText[game.p1.arrowType]}`, 20, 70);
            }

            if (cheatMode) {
                ctx.fillText('破解模式: 开', 20, 90);
            } else {
                ctx.fillText('破解模式: 关', 20, 90);
            }
        } else {
            ctx.fillText('P1: WASD移动 | Q射击 | E蓄力 | X切换', 20, 30);
            ctx.fillText('P2: IJKL移动 | U射击 | O蓄力 | M切换', 20, 50);
        }

        ctx.fillText('ESC隐藏/显示 | 点击右上角切换单/双人', 20, gameMode === 'single' ? 110 : 70);

        // 标注
        ctx.fillStyle = 'rgba(128, 128, 128, 0.6)';
        ctx.font = '12px monospace';
        ctx.textAlign = 'right';
        ctx.fillText('出自:sunhy,洛谷:862592,有意者私信', CONFIG.WIDTH - 20, CONFIG.HEIGHT - 20);

        // KO画面
        if (gameState === 'killcam') {
            ctx.fillStyle = 'rgba(0,0,0,0.7)';
            ctx.fillRect(0, 0, CONFIG.WIDTH, CONFIG.HEIGHT);
            ctx.fillStyle = '#fff';
            ctx.font = 'bold 60px monospace';
            ctx.textAlign = 'center';
            ctx.fillText('KO', CONFIG.WIDTH / 2, CONFIG.HEIGHT / 2 - 20);
            ctx.font = 'bold 40px monospace';

            if (winner === 'p1') {
                ctx.fillText(gameMode === 'single' ? '🎉 您赢了!' : '🎉 P1 赢了!', CONFIG.WIDTH / 2, CONFIG.HEIGHT / 2 + 40);
            } else if (winner === 'p2') {
                ctx.fillText(gameMode === 'single' ? '💀 您输了!' : '🎉 P2 赢了!', CONFIG.WIDTH / 2, CONFIG.HEIGHT / 2 + 40);
            }

            ctx.font = '20px monospace';
            ctx.fillText('按空格键重新开始', CONFIG.WIDTH / 2, CONFIG.HEIGHT / 2 + 100);
        }

        ctx.restore();
    }

    // === 事件监听 ===
    document.addEventListener('keydown', (e) => {
        if (e.key === 'Escape') {
            e.preventDefault();
            toggleFish();
            return;
        }
        if (fishMode) return;

        if (gameState === 'killcam') {
            if (e.code === 'Space') resetGame();
            return;
        }

        const k = e.key.toLowerCase();
        game.keys[k] = true;

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

        // 箭矢类型切换
        if (gameMode === 'single') {
            if (k === 'l' && game.p1) {
                e.preventDefault();
                game.p1.cycleArrowType();
            }
        } else {
            if (k === 'x' && game.p1) {
                e.preventDefault();
                game.p1.cycleArrowType();
            }
            if (k === 'm' && game.p2 && !game.p2.isAI) {
                e.preventDefault();
                game.p2.cycleArrowType();
            }
        }

        // 射击控制
        if (gameMode === 'single') {
            if (game.p1) {
                if (k === 'j') {
                    e.preventDefault();
                    game.p1.shoot(game.p2.pos);
                }
                if (k === 'k' && !game.p1.charging) {
                    e.preventDefault();
                    game.p1.startCharge();
                }
            }
        } else {
            if (game.p1) {
                if (k === 'q') {
                    e.preventDefault();
                    game.p1.shoot(game.p2.pos);
                }
                if (k === 'e' && !game.p1.charging) {
                    e.preventDefault();
                    game.p1.startCharge();
                }
            }
            if (gameMode === 'dual' && game.p2 && !game.p2.isAI) {
                if (k === 'u') {
                    e.preventDefault();
                    game.p2.shoot(game.p1.pos);
                }
                if (k === 'o' && !game.p2.charging) {
                    e.preventDefault();
                    game.p2.startCharge();
                }
            }
        }
    });

    document.addEventListener('keyup', (e) => {
        const k = e.key.toLowerCase();
        game.keys[k] = false;
        if (fishMode) return;
        if (gameState === 'killcam') return;

        if (gameMode === 'single' && k === 'k' && game.p1 && game.p1.charging) {
            game.p1.releaseCharge();
        }

        if (gameMode === 'dual') {
            if (k === 'e' && game.p1 && game.p1.charging) {
                game.p1.releaseCharge();
            }
            if (k === 'o' && game.p2 && game.p2.charging && !game.p2.isAI) {
                game.p2.releaseCharge();
            }
        }
    });

    // === 游戏启动 ===
    function loop() {
        update();
        draw();
        requestAnimationFrame(loop);
    }

    window.addEventListener('load', () => {
        loadCheatProperties();
        // 初始化单人模式属性备份
        singleModeProperties = { ...cheatProperties };
        resetGame();
        loop();
    });

    // 窗口大小调整
    function resize() {
        const s = Math.min(window.innerWidth / CONFIG.WIDTH, window.innerHeight / CONFIG.HEIGHT) * 0.98;
        canvas.style.width = (CONFIG.WIDTH * s) + 'px';
        canvas.style.height = (CONFIG.HEIGHT * s) + 'px';
    }
    window.addEventListener('resize', resize);
    resize();
})();

posted @ 2026-05-05 15:50  舒凌  阅读(43)  评论(0)    收藏  举报