PCOL-ASSIST

双人手动计分器,移动端虚拟按键

// ==UserScript==
// @name         PCOL-ASSIST
// @author       葉月Hikaru
// @match        http://www.heyzxz.me/pcol/*
// @version 7.6
// @namespace https://greasyfork.org/users/your-id
// @description 双人手动计分器,移动端虚拟按键
// ==/UserScript==
(function() {
    'use strict';
    const keysConfig = [
        { key: '+', text: '-', top: 30, left: 40 },
        { key: 'w', text: 'W', top: 30, left: 50 },
        { key: '-', text: '+', top: 30, left: 60 },
        { key: 'space', text: 'SPACE', top: 30, left: 70 },
        { key: 'x', text: 'X', top: 30, left: 80 },
        { key: 'c', text: 'C', top: 30, left: 90 },
        { key: 'p', text: 'P', top: 120, left: 90 },
        { key: 'a', text: 'A', top: 120, left: 30 },
        { key: 's', text: 'S', top: 120, left: 50 },
        { key: 'd', text: 'D', top: 120, left: 70 },
        { key: 'meta', text: 'Cmd', top: 120, left: 80 }
    ];
    const originalButtons = [];
    let areButtonsVisible = true;
    let isScoreboardVisible = true;
    let scores = { player1: 0, player2: 0 };
    let buttonCooldowns = {};
    let minusCooldowns = {};
    let longPressTimer = null;
    const LONG_PRESS_DELAY = 500;
    const scoreColors = {
        1: 'rgba(255, 0, 0, 0.6)',
        2: 'rgba(255, 255, 0, 0.6)',
        3: 'rgba(0, 255, 0, 0.6)',
        4: 'rgba(139, 69, 19, 0.6)',
        5: 'rgba(0, 0, 255, 0.6)',
        6: 'rgba(255, 192, 203, 0.6)',
        7: 'rgba(0, 0, 0, 0.6)'
    };
    let currentPlayer = 'player1';

    function createVirtualKey(keyConfig) {
        const btn = document.createElement('button');
        btn.id = `pcol-assist-${keyConfig.key}-btn`;
        btn.textContent = keyConfig.text;
        btn.style.cssText = `
            position: fixed;
            top: ${keyConfig.top}px;
            left: ${keyConfig.left}%;
            transform: ${keyConfig.left === 50 ? 'translateX(-50%)' : 'translateX(0)'};
            width: 60px;
            height: 60px;
            font-size: ${keyConfig.text === 'SPACE' ? '16px' : '28px'};
            background-color: rgba(255, 165, 0, 0.9);
            color: white;
            border: 2px solid white;
            border-radius: 50%;
            cursor: pointer;
            z-index: 9999;
            transition: all 0.1s ease; 
        `;

        function setPressedState(isPressed) {
            if (isPressed) {
                btn.style.backgroundColor = 'rgba(255, 140, 0, 0.7)';
                btn.style.transform = `${keyConfig.left === 50 ? 'translateX(-50%)' : 'translateX(0)'} scale(0.9)`;
                btn.style.borderColor = '#ffd700';
            } else {
                btn.style.backgroundColor = 'rgba(255, 165, 0, 0.9)';
                btn.style.transform = `${keyConfig.left === 50 ? 'translateX(-50%)' : 'translateX(0)'}`;
                btn.style.borderColor = 'white';
            }
        }

        function triggerEvent(type, keyInfo = {}) {
            if (keyConfig.key === '+' || keyConfig.key === '-') {
                const wheelEvent = new WheelEvent('wheel', {
                    deltaY: keyConfig.key === '+' ? -300 : 300,
                    bubbles: true,
                    cancelable: true
                });
                document.querySelector('canvas')?.dispatchEvent(wheelEvent) || document.dispatchEvent(wheelEvent);
            } else {
                const eventKey = keyInfo.key || (
                    keyConfig.key === 'space' ? ' ' : 
                    keyConfig.key === 'meta' ? 'Meta' : 
                    keyConfig.key
                );
                const eventCode = keyInfo.code || (
                    keyConfig.key === 'space' ? 'Space' : 
                    keyConfig.key === 'meta' ? 'MetaLeft' : 
                    `Key${keyConfig.key.toUpperCase()}`
                );
                const eventKeyCode = keyInfo.keyCode || (
                    keyConfig.key === 'w' ? 87 : 
                    keyConfig.key === 'a' ? 65 : 
                    keyConfig.key === 's' ? 83 : 
                    keyConfig.key === 'd' ? 68 : 
                    keyConfig.key === 'x' ? 88 : 
                    keyConfig.key === 'c' ? 67 : 
                    keyConfig.key === 'p' ? 80 : 
                    keyConfig.key === 'meta' ? 91 :
                    32
                );
                const event = new KeyboardEvent(type, {
                    key: eventKey === ' ' ? 'Spacebar' : eventKey,
                    code: eventCode,
                    keyCode: eventKeyCode,
                    metaKey: keyConfig.key === 'meta' && type === 'keydown',
                    bubbles: true,
                    cancelable: true
                });
                document.querySelector('canvas')?.dispatchEvent(event) || document.dispatchEvent(event);
            }
        }

        ['touchstart', 'mousedown'].forEach(e => btn.addEventListener(e, e => {
            e.preventDefault();
            setPressedState(true);
            triggerEvent('keydown');
        }));

        ['touchend', 'mouseup', 'mouseleave', 'touchcancel'].forEach(e => btn.addEventListener(e, e => {
            e.preventDefault();
            setPressedState(false);
            triggerEvent('keyup');
        }));

        document.body.appendChild(btn);
        originalButtons.push(btn);
    }
    
    function createVisibilityToggle() {
        const toggle = document.createElement('button');
        toggle.id = 'pcol-assist-visibility-toggle';
        toggle.textContent = '按钮: 显示';
        toggle.style.cssText = `
            position: fixed;
            top: 60px;
            left: 20px;
            width: 100px;
            height: 40px;
            font-size: 16px;
            background-color: rgba(0, 128, 0, 0.9);
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            z-index: 9999;
        `;
        toggle.addEventListener('click', () => {
            areButtonsVisible = !areButtonsVisible;
            originalButtons.forEach(btn => {
                btn.style.display = areButtonsVisible ? 'block' : 'none';
            });
            if (areButtonsVisible) {
                toggle.textContent = '按钮: 显示';
                toggle.style.backgroundColor = 'rgba(0, 128, 0, 0.9)';
            } else {
                toggle.textContent = '按钮: 隐藏';
                toggle.style.backgroundColor = 'rgba(128, 128, 128, 0.9)';
            }
        });
        document.body.appendChild(toggle);
    }

    function createScoreboardToggle() {
        const toggle = document.createElement('button');
        toggle.id = 'pcol-assist-scoreboard-toggle';
        toggle.textContent = '记分板: 显示';
        toggle.style.cssText = `
            position: fixed;
            top: 60px;
            left: 140px;
            width: 100px;
            height: 40px;
            font-size: 16px;
            background-color: rgba(0, 128, 0, 0.9);
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            z-index: 9999;
        `;
        toggle.addEventListener('click', () => {
            isScoreboardVisible = !isScoreboardVisible;
            const scoreboard = document.getElementById('pcol-assist-scoreboard');
            scoreboard.style.display = isScoreboardVisible ? 'flex' : 'none';
            if (isScoreboardVisible) {
                toggle.textContent = '记分板: 显示';
                toggle.style.backgroundColor = 'rgba(0, 128, 0, 0.9)';
            } else {
                toggle.textContent = '记分板: 隐藏';
                toggle.style.backgroundColor = 'rgba(128, 128, 128, 0.9)';
            }
        });
        document.body.appendChild(toggle);
    }

    function createFullscreenToggle() {
        const toggle = document.createElement('button');
        toggle.id = 'pcol-assist-fullscreen-toggle';
        toggle.textContent = '全屏';
        toggle.style.cssText = `
            position: fixed;
            top: 60px;
            left: 260px;
            width: 80px;
            height: 40px;
            font-size: 16px;
            background-color: rgba(70, 130, 180, 0.9);
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            z-index: 9999;
        `;
        toggle.addEventListener('click', () => {
            const docEl = document.documentElement;
            const isFullscreen = document.webkitIsFullScreen || document.fullscreenElement;
            
            if (!isFullscreen) {
                if (docEl.webkitRequestFullscreen) {
                    docEl.webkitRequestFullscreen();
                } else if (docEl.requestFullscreen) {
                    docEl.requestFullscreen();
                }
                toggle.textContent = '退出全屏';
            } else {
                if (document.webkitExitFullscreen) {
                    document.webkitExitFullscreen();
                } else if (document.exitFullscreen) {
                    document.exitFullscreen();
                }
                toggle.textContent = '全屏';
            }
        });
        document.body.appendChild(toggle);
    }

    function updatePlayerSelection() {
        const player1Container = document.getElementById('pcol-player-container-player1');
        const player2Container = document.getElementById('pcol-player-container-player2');
        
        player1Container.style.border = 'none';
        player2Container.style.border = 'none';
        
        if (currentPlayer === 'player1') {
            player1Container.style.border = '3px solid #4CAF50';
        } else {
            player2Container.style.border = '3px solid #4CAF50';
        }
    }

    function createScoreboard() {
        const scoreboard = document.createElement('div');
        scoreboard.id = 'pcol-assist-scoreboard';
        scoreboard.style.cssText = `
            position: fixed;
            top: 100px;
            left: 10px;
            display: flex;
            gap: 20px;
            z-index: 9998;
        `;
        function createPlayerScore(playerId, name) {
            const container = document.createElement('div');
            container.id = `pcol-player-container-${playerId}`;
            container.style.cssText = `
                display: flex;
                flex-direction: column;
                align-items: center;
                background-color: rgba(0, 0, 0, 0.7);
                padding: 10px;
                border-radius: 8px;
                cursor: pointer;
            `;
            container.addEventListener('click', () => {
                currentPlayer = playerId;
                updatePlayerSelection();
            });
            const nameEl = document.createElement('div');
            nameEl.textContent = name;
            nameEl.style.cssText = `
                color: white;
                font-size: 20px;
                font-weight: bold;
                margin-bottom: 5px;
            `;
            const scoreEl = document.createElement('div');
            scoreEl.id = `pcol-score-${playerId}`;
            scoreEl.textContent = '0';
            scoreEl.style.cssText = `
                color: white;
                font-size: 36px;
                width: 80px;
                text-align: center;
                margin-bottom: 10px;
                border: 2px solid white;
                border-radius: 4px;
                padding: 5px 0;
            `;
            const buttonsContainer = document.createElement('div');
            buttonsContainer.style.cssText = `
                display: grid;
                grid-template-columns: repeat(2, 1fr);
                gap: 5px;
            `;
            for (let i = 1; i <= 7; i++) {
                const btn = document.createElement('button');
                btn.id = `pcol-add-${playerId}-${i}`;
                btn.textContent = `+${i}`;
                btn.style.cssText = `
                    width: 40px;
                    height: 40px;
                    font-size: 18px;
                    background-color: ${scoreColors[i]};
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                `;
                btn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    const btnId = btn.id;
                    if (buttonCooldowns[btnId]) return;
                    scores[playerId] += i;
                    scoreEl.textContent = scores[playerId];
                    buttonCooldowns[btnId] = true;
                    btn.style.backgroundColor = scoreColors[i].replace('0.6', '0.3');
                    setTimeout(() => {
                        buttonCooldowns[btnId] = false;
                        btn.style.backgroundColor = scoreColors[i];
                    }, 1000);
                });
                buttonsContainer.appendChild(btn);
            }
            const minusBtn = document.createElement('button');
            minusBtn.id = `pcol-minus-${playerId}-1`;
            minusBtn.textContent = '-1';
            minusBtn.style.cssText = `
                width: 40px;
                height: 40px;
                font-size: 18px;
                background-color: rgba(178, 34, 34, 0.6);
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
            `;
            minusBtn.addEventListener('mousedown', (e) => {
                e.stopPropagation();
                longPressTimer = setTimeout(() => {
                    scores[playerId] = 0;
                    scoreEl.textContent = '0';
                }, LONG_PRESS_DELAY);
            });
            minusBtn.addEventListener('mouseup', (e) => {
                e.stopPropagation();
                if (longPressTimer) {
                    clearTimeout(longPressTimer);
                    longPressTimer = null;
                    const btnId = minusBtn.id;
                    if (minusCooldowns[btnId]) return;
                    if (scores[playerId] > 0) {
                        scores[playerId] -= 1;
                        scoreEl.textContent = scores[playerId];
                        minusCooldowns[btnId] = true;
                        minusBtn.style.backgroundColor = 'rgba(139, 0, 0, 0.3)';
                        setTimeout(() => {
                            minusCooldowns[btnId] = false;
                            minusBtn.style.backgroundColor = 'rgba(178, 34, 34, 0.6)';
                        }, 200);
                    }
                }
            });
            minusBtn.addEventListener('touchstart', (e) => {
                e.stopPropagation();
                longPressTimer = setTimeout(() => {
                    scores[playerId] = 0;
                    scoreEl.textContent = '0';
                }, LONG_PRESS_DELAY);
            });
            minusBtn.addEventListener('touchend', (e) => {
                e.stopPropagation();
                if (longPressTimer) {
                    clearTimeout(longPressTimer);
                    longPressTimer = null;
                    const btnId = minusBtn.id;
                    if (minusCooldowns[btnId]) return;
                    if (scores[playerId] > 0) {
                        scores[playerId] -= 1;
                        scoreEl.textContent = scores[playerId];
                        minusCooldowns[btnId] = true;
                        minusBtn.style.backgroundColor = 'rgba(139, 0, 0, 0.3)';
                        setTimeout(() => {
                            minusCooldowns[btnId] = false;
                            minusBtn.style.backgroundColor = 'rgba(178, 34, 34, 0.6)';
                        }, 200);
                    }
                }
            });
            minusBtn.addEventListener('mouseleave', () => {
                if (longPressTimer) {
                    clearTimeout(longPressTimer);
                    longPressTimer = null;
                }
            });
            buttonsContainer.appendChild(minusBtn);
            container.appendChild(nameEl);
            container.appendChild(scoreEl);
            container.appendChild(buttonsContainer);
            return container;
        }
        scoreboard.appendChild(createPlayerScore('player1', 'P1'));
        scoreboard.appendChild(createPlayerScore('player2', 'P2'));
        document.body.appendChild(scoreboard);
        updatePlayerSelection();
    }

    keysConfig.forEach(createVirtualKey);
    createVisibilityToggle();
    createScoreboardToggle();
    createFullscreenToggle();
    createScoreboard();
})();