Slither.io Mobile Mod

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

ของเมื่อวันที่ 20-10-2025 ดู เวอร์ชันล่าสุด

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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();
    }

})();