Slither.io Mobile Mod

iPhone/iPad touch controls for slither.io with visual pointer and customization

当前为 2025-10-20 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Slither.io Mobile Mod
// @namespace    slither_mobile_mod
// @version      1.0
// @description  iPhone/iPad touch controls for slither.io with visual pointer and customization
// @author       XBACT
// @match        *://slither.com/*
// @match        *://slither.io/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Touch control state
    let touchActive = false;
    let touchStartX = 0;
    let touchStartY = 0;
    let touchCurrentX = 0;
    let touchCurrentY = 0;
    let pointerX = 0;
    let pointerY = 0;
    let pointerElement = null;
    let boostButton = null;
    let zoomInButton = null;
    let zoomOutButton = null;
    let settingsButton = null;
    let settingsPanel = null;
    let isAccelerating = false;
    let lastKnownAngle = 0;
    let currentZoom = 1.0;
    let gameStartZoom = 1.0;
    let editMode = false;
    let draggingButton = null;
    let dragOffsetX = 0;
    let dragOffsetY = 0;
    let editingButton = null;
    let wasInGame = false;

    const POINTER_SPEED_MULTIPLIER = 2.5;
    const INITIAL_POINTER_DISTANCE = 120;
    const MIN_POINTER_DISTANCE = 10;
    const MIN_ZOOM = 0.3;
    const MAX_ZOOM = 3.0;

    // Default button settings
    let buttonSettings = {
        boost: {
            x: window.innerWidth - 110,
            y: window.innerHeight - 110,
            width: 80,
            height: 80,
            color: '#ff3232',
            opacity: 0.7,
            borderRadius: 50
        },
        zoomIn: {
            x: window.innerWidth - 80,
            y: window.innerHeight - 210,
            width: 50,
            height: 50,
            color: '#6496ff',
            opacity: 0.7,
            borderRadius: 10,
            value: 0.05
        },
        zoomOut: {
            x: window.innerWidth - 80,
            y: window.innerHeight - 270,
            width: 50,
            height: 50,
            color: '#6496ff',
            opacity: 0.7,
            borderRadius: 10,
            value: 0.05
        }
    };

    // Load saved settings
    function loadSettings() {
        const saved = localStorage.getItem('slitherMobileSettings');
        if (saved) {
            try {
                const parsed = JSON.parse(saved);
                buttonSettings = { ...buttonSettings, ...parsed };
            } catch (e) {
                console.error('Failed to load settings:', e);
            }
        }
    }

    // Save settings
    function saveSettings() {
        localStorage.setItem('slitherMobileSettings', JSON.stringify(buttonSettings));
    }

    // Check if currently in game
    function isInGame() {
        if (window.snake && window.snake.id !== undefined) return true;
        if (window.playing === true) return true;
        const loginDiv = document.getElementById('login');
        if (loginDiv && loginDiv.style.display === 'none') return true;
        const playBtn = document.getElementById('playh');
        if (playBtn && playBtn.style.display === 'none') return true;
        return false;
    }

    // Monitor game state changes
    function checkGameStateChange() {
        const currentlyInGame = isInGame();
        
        // Detect game start
        if (currentlyInGame && !wasInGame) {
            console.log('Game started - resetting zoom to:', gameStartZoom);
            currentZoom = gameStartZoom;
            if (window.gsc !== undefined) {
                window.gsc = currentZoom;
            }
        }
        
        // Detect game end
        if (!currentlyInGame && wasInGame) {
            console.log('Game ended - saving zoom:', currentZoom);
            gameStartZoom = currentZoom;
        }
        
        wasInGame = currentlyInGame;
    }

    // Run game state check regularly
    setInterval(checkGameStateChange, 100);

    // Create visual pointer element
    function createPointer() {
        pointerElement = document.createElement('div');
        pointerElement.style.cssText = `
            position: fixed;
            width: 40px;
            height: 40px;
            pointer-events: none;
            z-index: 10000;
            display: none;
            transform: translate(-50%, -50%);
        `;
        
        pointerElement.innerHTML = `
            <svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
                <defs>
                    <filter id="glow">
                        <feGaussianBlur stdDeviation="2" result="coloredBlur"/>
                        <feMerge>
                            <feMergeNode in="coloredBlur"/>
                            <feMergeNode in="SourceGraphic"/>
                        </feMerge>
                    </filter>
                </defs>
                <path d="M20 38 L10 25 L16 25 L16 2 L24 2 L24 25 L30 25 Z" 
                      fill="rgba(100, 200, 255, 0.9)" 
                      stroke="white" 
                      stroke-width="2"
                      filter="url(#glow)"/>
                <circle cx="20" cy="20" r="3" fill="white" opacity="0.8"/>
            </svg>
        `;
        
        document.body.appendChild(pointerElement);
    }

    // Apply button style
    function applyButtonStyle(button, settings) {
        const s = buttonSettings[settings];
        button.style.left = s.x + 'px';
        button.style.top = s.y + 'px';
        button.style.width = s.width + 'px';
        button.style.height = s.height + 'px';
        button.style.backgroundColor = `${s.color}${Math.round(s.opacity * 255).toString(16).padStart(2, '0')}`;
        button.style.borderRadius = s.borderRadius + '%';
    }

    // Create boost button
    function createBoostButton() {
        boostButton = document.createElement('button');
        boostButton.textContent = 'BOOST';
        boostButton.className = 'control-button';
        boostButton.dataset.buttonType = 'boost';
        boostButton.style.cssText = `
            position: fixed;
            border: 3px solid white;
            color: white;
            font-weight: bold;
            font-size: 14px;
            z-index: 10001;
            touch-action: none;
            user-select: none;
            box-shadow: 0 4px 10px rgba(0,0,0,0.3);
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
        `;
        
        applyButtonStyle(boostButton, 'boost');
        
        boostButton.addEventListener('touchstart', handleBoostStart, { passive: false });
        boostButton.addEventListener('touchend', handleBoostEnd, { passive: false });
        
        document.body.appendChild(boostButton);
    }

    function handleBoostStart(e) {
        if (editMode) {
            startDragging(e, boostButton);
            return;
        }
        e.preventDefault();
        e.stopPropagation();
        if (!isInGame()) return;
        isAccelerating = true;
        boostButton.style.filter = 'brightness(1.3)';
        simulateMouseDown();
    }

    function handleBoostEnd(e) {
        if (editMode) return;
        e.preventDefault();
        e.stopPropagation();
        isAccelerating = false;
        boostButton.style.filter = 'brightness(1)';
        simulateMouseUp();
        // Don't stop mouse tracking here - keep it running if touch is still active
    }

    // Create zoom buttons
    function createZoomButtons() {
        zoomInButton = document.createElement('button');
        zoomInButton.textContent = '+';
        zoomInButton.className = 'control-button';
        zoomInButton.dataset.buttonType = 'zoomIn';
        zoomInButton.style.cssText = `
            position: fixed;
            border: 2px solid white;
            color: white;
            font-weight: bold;
            font-size: 24px;
            z-index: 10001;
            touch-action: none;
            user-select: none;
            box-shadow: 0 4px 10px rgba(0,0,0,0.3);
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
        `;
        
        applyButtonStyle(zoomInButton, 'zoomIn');
        
        zoomInButton.addEventListener('touchstart', (e) => {
            if (editMode) {
                startDragging(e, zoomInButton);
                return;
            }
            e.preventDefault();
            e.stopPropagation();
            zoomInButton.style.filter = 'brightness(1.3)';
            adjustZoom(buttonSettings.zoomIn.value);
        }, { passive: false });
        
        zoomInButton.addEventListener('touchend', (e) => {
            if (editMode) return;
            e.preventDefault();
            e.stopPropagation();
            zoomInButton.style.filter = 'brightness(1)';
        }, { passive: false });
        
        document.body.appendChild(zoomInButton);

        zoomOutButton = document.createElement('button');
        zoomOutButton.textContent = '−';
        zoomOutButton.className = 'control-button';
        zoomOutButton.dataset.buttonType = 'zoomOut';
        zoomOutButton.style.cssText = `
            position: fixed;
            border: 2px solid white;
            color: white;
            font-weight: bold;
            font-size: 24px;
            z-index: 10001;
            touch-action: none;
            user-select: none;
            box-shadow: 0 4px 10px rgba(0,0,0,0.3);
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
        `;
        
        applyButtonStyle(zoomOutButton, 'zoomOut');
        
        zoomOutButton.addEventListener('touchstart', (e) => {
            if (editMode) {
                startDragging(e, zoomOutButton);
                return;
            }
            e.preventDefault();
            e.stopPropagation();
            zoomOutButton.style.filter = 'brightness(1.3)';
            adjustZoom(-buttonSettings.zoomOut.value);
        }, { passive: false });
        
        zoomOutButton.addEventListener('touchend', (e) => {
            if (editMode) return;
            e.preventDefault();
            e.stopPropagation();
            zoomOutButton.style.filter = 'brightness(1)';
        }, { passive: false });
        
        document.body.appendChild(zoomOutButton);
    }

    // Create settings button
    function createSettingsButton() {
        settingsButton = document.createElement('button');
        settingsButton.innerHTML = '⚙';
        settingsButton.style.cssText = `
            position: fixed;
            top: 10px;
            right: 10px;
            width: 40px;
            height: 40px;
            border-radius: 50%;
            background: rgba(100, 100, 100, 0.7);
            border: 2px solid white;
            color: white;
            font-size: 20px;
            z-index: 10002;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            box-shadow: 0 2px 8px rgba(0,0,0,0.3);
            touch-action: none;
        `;
        
        settingsButton.addEventListener('click', (e) => {
            e.stopPropagation();
            toggleSettingsPanel();
        });
        
        settingsButton.addEventListener('touchend', (e) => {
            e.preventDefault();
            e.stopPropagation();
        }, { passive: false });
        
        document.body.appendChild(settingsButton);
    }

    // Create settings panel
    function createSettingsPanel() {
        settingsPanel = document.createElement('div');
        settingsPanel.id = 'settings-panel';
        settingsPanel.style.cssText = `
            position: fixed;
            top: 60px;
            right: 10px;
            width: 320px;
            max-height: 80vh;
            background: rgba(30, 30, 35, 0.98);
            border: 2px solid white;
            border-radius: 10px;
            padding: 15px;
            z-index: 10003;
            display: none;
            overflow-y: auto;
            color: white;
            font-family: Arial, sans-serif;
            box-shadow: 0 4px 20px rgba(0,0,0,0.5);
        `;
        
        settingsPanel.innerHTML = `
            <h3 style="margin: 0 0 15px 0; text-align: center;">コントロール設定</h3>
            <button id="toggleEditMode" style="width: 100%; padding: 10px; margin-bottom: 15px; background: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; font-size: 14px;">
                移動モード: OFF
            </button>
            <div id="buttonSettingsContainer"></div>
            <button id="resetSettings" style="width: 100%; padding: 10px; margin-top: 15px; background: #f44336; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 14px;">
                設定をリセット
            </button>
        `;
        
        document.body.appendChild(settingsPanel);
        
        document.getElementById('toggleEditMode').addEventListener('click', toggleEditMode);
        document.getElementById('resetSettings').addEventListener('click', resetSettings);
        
        updateSettingsPanel();
    }

    function toggleSettingsPanel() {
        const isVisible = settingsPanel.style.display !== 'none';
        settingsPanel.style.display = isVisible ? 'none' : 'block';
        
        if (!isVisible) {
            updateSettingsPanel();
        }
    }

    function toggleEditMode() {
        editMode = !editMode;
        const btn = document.getElementById('toggleEditMode');
        btn.textContent = `移動モード: ${editMode ? 'ON' : 'OFF'}`;
        btn.style.background = editMode ? '#ff9800' : '#4CAF50';
        
        document.querySelectorAll('.control-button').forEach(button => {
            button.style.border = editMode ? '3px dashed yellow' : button.dataset.buttonType === 'boost' ? '3px solid white' : '2px solid white';
        });
    }

    function updateSettingsPanel() {
        const container = document.getElementById('buttonSettingsContainer');
        container.innerHTML = '';
        
        Object.keys(buttonSettings).forEach(key => {
            const s = buttonSettings[key];
            const section = document.createElement('div');
            section.style.cssText = 'margin-bottom: 15px; padding: 12px; background: rgba(255,255,255,0.1); border-radius: 8px;';
            
            const title = key === 'boost' ? 'ブースト' : key === 'zoomIn' ? 'ズームイン' : 'ズームアウト';
            
            let html = `
                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
                    <h4 style="margin: 0;">${title}</h4>
                    <button class="edit-button" data-button="${key}" style="padding: 5px 12px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px;">
                        ${editingButton === key ? '閉じる' : '編集'}
                    </button>
                </div>
            `;
            
            if (editingButton === key) {
                html += `<div style="padding-top: 10px; border-top: 1px solid rgba(255,255,255,0.2);">`;
                html += createSlider('幅', `${key}-width`, s.width, 30, 200, 1);
                html += createSlider('高さ', `${key}-height`, s.height, 30, 200, 1);
                html += createSlider('角丸', `${key}-radius`, s.borderRadius, 0, 50, 1);
                html += createSlider('透明度', `${key}-opacity`, s.opacity, 0.1, 1, 0.1);
                html += createColorPicker('色', `${key}-color`, s.color);
                
                if (key !== 'boost') {
                    html += createSlider('変更値', `${key}-value`, s.value, 0.01, 1.0, 0.01);
                }
                html += `</div>`;
            }
            
            section.innerHTML = html;
            container.appendChild(section);
            
            // Add event listener for edit button
            section.querySelector('.edit-button').addEventListener('click', () => {
                editingButton = editingButton === key ? null : key;
                updateSettingsPanel();
            });
            
            // Add event listeners for controls if this button is being edited
            if (editingButton === key) {
                document.getElementById(`${key}-width`).addEventListener('input', (e) => updateSetting(key, 'width', parseFloat(e.target.value)));
                document.getElementById(`${key}-height`).addEventListener('input', (e) => updateSetting(key, 'height', parseFloat(e.target.value)));
                document.getElementById(`${key}-radius`).addEventListener('input', (e) => updateSetting(key, 'borderRadius', parseFloat(e.target.value)));
                document.getElementById(`${key}-opacity`).addEventListener('input', (e) => updateSetting(key, 'opacity', parseFloat(e.target.value)));
                document.getElementById(`${key}-color`).addEventListener('input', (e) => updateSetting(key, 'color', e.target.value));
                
                if (key !== 'boost') {
                    document.getElementById(`${key}-value`).addEventListener('input', (e) => updateSetting(key, 'value', parseFloat(e.target.value)));
                }
            }
        });
    }

    function createSlider(label, id, value, min, max, step) {
        return `
            <div style="margin: 8px 0;">
                <label style="display: block; margin-bottom: 3px; font-size: 12px;">${label}: <span id="${id}-val">${value.toFixed(step < 0.1 ? 2 : 1)}</span></label>
                <input type="range" id="${id}" min="${min}" max="${max}" step="${step}" value="${value}" style="width: 100%;">
            </div>
        `;
    }

    function createColorPicker(label, id, value) {
        return `
            <div style="margin: 8px 0;">
                <label style="display: block; margin-bottom: 3px; font-size: 12px;">${label}</label>
                <input type="color" id="${id}" value="${value}" style="width: 100%; height: 30px; cursor: pointer; border-radius: 4px;">
            </div>
        `;
    }

    function updateSetting(buttonType, property, value) {
        buttonSettings[buttonType][property] = value;
        
        // Update value display
        const valSpan = document.getElementById(`${buttonType}-${property}-val`);
        if (valSpan) {
            if (property === 'value') {
                valSpan.textContent = value.toFixed(2);
            } else {
                valSpan.textContent = value.toFixed(1);
            }
        }
        
        // Apply to button
        const button = buttonType === 'boost' ? boostButton : buttonType === 'zoomIn' ? zoomInButton : zoomOutButton;
        applyButtonStyle(button, buttonType);
        
        saveSettings();
    }

    function resetSettings() {
        if (confirm('設定をリセットしますか?')) {
            localStorage.removeItem('slitherMobileSettings');
            location.reload();
        }
    }

    function startDragging(e, button) {
        e.preventDefault();
        e.stopPropagation();
        draggingButton = button;
        const rect = button.getBoundingClientRect();
        dragOffsetX = e.touches[0].clientX - rect.left;
        dragOffsetY = e.touches[0].clientY - rect.top;
    }

    // Adjust zoom level
    function adjustZoom(delta) {
        currentZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, currentZoom + delta));
        gameStartZoom = currentZoom; // Update the base zoom level
        if (window.gsc !== undefined) {
            window.gsc = currentZoom;
        }
        console.log('Zoom adjusted to:', currentZoom.toFixed(2));
    }

    // Get snake angle
    function getSnakeAngle() {
        if (window.snake) {
            if (typeof window.snake.ang !== 'undefined') {
                lastKnownAngle = window.snake.ang;
                return window.snake.ang;
            }
            if (typeof window.snake.eang !== 'undefined') {
                lastKnownAngle = window.snake.eang;
                return window.snake.eang;
            }
            if (typeof window.snake.wang !== 'undefined') {
                lastKnownAngle = window.snake.wang;
                return window.snake.wang;
            }
        }
        return lastKnownAngle;
    }

    // Initialize pointer position
    function initializePointerPosition() {
        const centerX = window.innerWidth / 2;
        const centerY = window.innerHeight / 2;
        const snakeAngle = getSnakeAngle();
        pointerX = centerX + Math.cos(snakeAngle) * INITIAL_POINTER_DISTANCE;
        pointerY = centerY + Math.sin(snakeAngle) * INITIAL_POINTER_DISTANCE;
    }

    // Update pointer position
    function updatePointerPosition() {
        if (!touchActive || !pointerElement || !isInGame()) {
            if (pointerElement) pointerElement.style.display = 'none';
            return;
        }

        const fingerDeltaX = touchCurrentX - touchStartX;
        const fingerDeltaY = touchCurrentY - touchStartY;
        const centerX = window.innerWidth / 2;
        const centerY = window.innerHeight / 2;
        const snakeAngle = getSnakeAngle();
        const initialX = centerX + Math.cos(snakeAngle) * INITIAL_POINTER_DISTANCE;
        const initialY = centerY + Math.sin(snakeAngle) * INITIAL_POINTER_DISTANCE;

        let newPointerX = initialX + (fingerDeltaX * POINTER_SPEED_MULTIPLIER);
        let newPointerY = initialY + (fingerDeltaY * POINTER_SPEED_MULTIPLIER);

        const distanceFromCenter = Math.sqrt(Math.pow(newPointerX - centerX, 2) + Math.pow(newPointerY - centerY, 2));
        if (distanceFromCenter < MIN_POINTER_DISTANCE) {
            const angle = Math.atan2(newPointerY - centerY, newPointerX - centerX);
            newPointerX = centerX + Math.cos(angle) * MIN_POINTER_DISTANCE;
            newPointerY = centerY + Math.sin(angle) * MIN_POINTER_DISTANCE;
        }

        pointerX = newPointerX;
        pointerY = newPointerY;
        pointerElement.style.left = pointerX + 'px';
        pointerElement.style.top = pointerY + 'px';
        pointerElement.style.display = 'block';

        const angleToCenter = Math.atan2(centerY - pointerY, centerX - pointerX);
        const rotationDegrees = (angleToCenter * 180 / Math.PI) + 90;
        pointerElement.style.transform = `translate(-50%, -50%) rotate(${rotationDegrees}deg)`;
    }

    // Simulate mouse events
    let mouseUpdateInterval = null;

    function startMouseTracking() {
        if (mouseUpdateInterval) return;
        mouseUpdateInterval = setInterval(() => {
            if (touchActive && isInGame()) {
                simulateMouseMove(pointerX, pointerY);
            }
        }, 16);
    }

    function stopMouseTracking() {
        if (mouseUpdateInterval) {
            clearInterval(mouseUpdateInterval);
            mouseUpdateInterval = null;
        }
    }

    function simulateMouseMove(x, y) {
        if (!isInGame()) return;
        
        const canvas = document.querySelector('canvas');
        if (!canvas) return;

        const event = new MouseEvent('mousemove', {
            clientX: x,
            clientY: y,
            bubbles: true,
            cancelable: true,
            view: window
        });
        canvas.dispatchEvent(event);
    }

    function simulateMouseDown() {
        if (!isInGame()) return;
        const canvas = document.querySelector('canvas');
        if (!canvas) return;

        const event = new MouseEvent('mousedown', {
            button: 0,
            buttons: 1,
            bubbles: true,
            cancelable: true,
            view: window
        });
        canvas.dispatchEvent(event);
    }

    function simulateMouseUp() {
        const canvas = document.querySelector('canvas');
        if (!canvas) return;

        const event = new MouseEvent('mouseup', {
            button: 0,
            buttons: 0,
            bubbles: true,
            cancelable: true,
            view: window
        });
        canvas.dispatchEvent(event);
    }

    // Touch event handlers
    document.addEventListener('touchstart', (e) => {
        // Allow settings panel interaction
        if (e.target.closest('#settings-panel')) {
            return;
        }

        // Allow settings button
        if (e.target === settingsButton) {
            return;
        }

        // Allow control buttons
        if (e.target.closest('.control-button')) {
            return;
        }

        // For menu screen, only prevent default but don't start pointer tracking
        if (!isInGame()) {
            // Allow normal touch interaction on menu
            return;
        }

        // In game: block and handle with pointer
        e.preventDefault();
        e.stopPropagation();

        touchActive = true;
        touchStartX = e.touches[0].clientX;
        touchStartY = e.touches[0].clientY;
        touchCurrentX = touchStartX;
        touchCurrentY = touchStartY;
        
        initializePointerPosition();
        updatePointerPosition();
        startMouseTracking();
    }, { passive: false, capture: true });

    document.addEventListener('touchmove', (e) => {
        if (draggingButton) {
            e.preventDefault();
            e.stopPropagation();
            const newX = e.touches[0].clientX - dragOffsetX;
            const newY = e.touches[0].clientY - dragOffsetY;
            const buttonType = draggingButton.dataset.buttonType;
            buttonSettings[buttonType].x = newX;
            buttonSettings[buttonType].y = newY;
            draggingButton.style.left = newX + 'px';
            draggingButton.style.top = newY + 'px';
            saveSettings();
            return;
        }

        if (!touchActive) return;
        
        // Block native touch events only in game
        if (isInGame() && !e.target.closest('#settings-panel')) {
            e.preventDefault();
            e.stopPropagation();
        }

        touchCurrentX = e.touches[0].clientX;
        touchCurrentY = e.touches[0].clientY;
        
        if (isInGame()) {
            updatePointerPosition();
        }
    }, { passive: false, capture: true });

    document.addEventListener('touchend', (e) => {
        if (draggingButton) {
            draggingButton = null;
            e.preventDefault();
            e.stopPropagation();
            return;
        }

        if (!e.target.closest('.control-button') && e.target !== settingsButton && !e.target.closest('#settings-panel')) {
            if (isInGame()) {
                e.preventDefault();
                e.stopPropagation();
            }
        }

        touchActive = false;
        stopMouseTracking();
        if (pointerElement) {
            pointerElement.style.display = 'none';
        }
    }, { passive: false, capture: true });

    // Initialize
    function init() {
        loadSettings();
        createPointer();
        createBoostButton();
        createZoomButtons();
        createSettingsButton();
        createSettingsPanel();
        
        const canvas = document.querySelector('canvas');
        if (canvas) {
            canvas.style.touchAction = 'none';
        }

        if (window.gsc !== undefined) {
            currentZoom = window.gsc;
            gameStartZoom = currentZoom;
        }

        wasInGame = isInGame();

        console.log('Slither.io mobile controls initialized');
        console.log('Initial zoom:', currentZoom.toFixed(2));
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();