Slither.io Mobile Mod

Slither mod for mobile

// ==UserScript==
// @name         Slither.io Mobile Mod
// @namespace    slither_mobile_mod
// @version      1.0
// @description  Slither mod for mobile
// @author       XBACT
// @match        *://slither.com/*
// @match        *://slither.io/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    let touchActive = false;
    let touchStartX = 0;
    let touchStartY = 0;
    let touchCurrentX = 0;
    let touchCurrentY = 0;
    let pointerX = 0;
    let pointerY = 0;
    let basePointerX = 0;  
    let basePointerY = 0;  
    let pointerElement = null;
    let guideLine = null;
    let snakeDot = null;
    let boostButton = null;
    let zoomInButton = null;
    let zoomOutButton = null;
    let respawnButton = 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;
    let lastDeadTime = 0;
    let autoRespawnDelay = 500; 
    let screenButtons = []; 
    const POINTER_BASE_DISTANCE = 100;  
    const MIN_ZOOM = 0.3;
    const MAX_ZOOM = 3.0;
    
    let visualFeatures = {
        hideBoostGlow: false,
        showGuideLine: false,
        showSnakeDot: false,
        showPointer: true,
        lowPerformance: false,
        blackBackground: false,
        autoRespawn: false,
        instantDeadSnake: false,
        instantEatFood: false,
        resetZoomOnRespawn: false,
        snakeOpacity: 1.0,
        noFoodAnimation: false,
        noFoodWobble: false,
        showRespawnButton: false
    };
    
    let guidelineSettings = {
        thickness: 2,
        color: '#64ff64'
    };
    
    let pointerSettings = {
        size: 40,
        color: '#64c8ff',
        speed: 1.5
    };
    
    function loadVisualFeatures() {
        const saved = localStorage.getItem('slitherVisualFeatures');
        if (saved) {
            try {
                const parsed = JSON.parse(saved);
                visualFeatures = { ...visualFeatures, ...parsed };
                
                if (parsed.pointerSpeed !== undefined) {
                    pointerSettings.speed = parsed.pointerSpeed;
                    delete visualFeatures.pointerSpeed;
                }
            } catch (e) {
                console.error('Failed to load visual features:', e);
            }
        }
        
        const savedDelay = localStorage.getItem('slitherAutoRespawnDelay');
        if (savedDelay) {
            autoRespawnDelay = parseInt(savedDelay) || 500;
        }
        
        const savedGuideline = localStorage.getItem('slitherGuidelineSettings');
        if (savedGuideline) {
            try {
                guidelineSettings = { ...guidelineSettings, ...JSON.parse(savedGuideline) };
            } catch (e) {
                console.error('Failed to load guideline settings:', e);
            }
        }
        
        const savedPointer = localStorage.getItem('slitherPointerSettings');
        if (savedPointer) {
            try {
                pointerSettings = { ...pointerSettings, ...JSON.parse(savedPointer) };
            } catch (e) {
                console.error('Failed to load pointer settings:', e);
            }
        }
    }
    
    function saveVisualFeatures() {
        localStorage.setItem('slitherVisualFeatures', JSON.stringify(visualFeatures));
        localStorage.setItem('slitherAutoRespawnDelay', autoRespawnDelay.toString());
        localStorage.setItem('slitherGuidelineSettings', JSON.stringify(guidelineSettings));
        localStorage.setItem('slitherPointerSettings', JSON.stringify(pointerSettings));
    }
    
    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
        },
        respawn: {
            x: 20,
            y: window.innerHeight - 110,
            width: 80,
            height: 80,
            color: '#4CAF50',
            opacity: 0.7,
            borderRadius: 50
        }
    };
    
    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);
            }
        }
    }
    
    function saveSettings() {
        localStorage.setItem('slitherMobileSettings', JSON.stringify(buttonSettings));
    }
    
    function isInGame() {
        if (window.slither && window.slither.id !== undefined && window.playing) return true;
        if (window.playing === true) return true;
        return false;
    }
    
    function isDead() {
        if (!window.slither) return true;
        if (window.dead_mtm !== -1 && window.dead_mtm !== undefined) return true;
        return false;
    }
    
    function checkGameStateChange() {
        const currentlyInGame = isInGame();
        if (currentlyInGame && !wasInGame) {
            console.log('Game started');
            if (visualFeatures.resetZoomOnRespawn) {
                currentZoom = 1.0;
                gameStartZoom = 1.0;
                if (window.gsc !== undefined) {
                    window.gsc = 1.0;
                }
            } else {
                currentZoom = gameStartZoom;
                if (window.gsc !== undefined) {
                    window.gsc = currentZoom;
                }
            }
            applyVisualFeatures();
        }
        if (!currentlyInGame && wasInGame) {
            console.log('Game ended');
            lastDeadTime = Date.now();
            if (!visualFeatures.resetZoomOnRespawn) {
                gameStartZoom = currentZoom;
            }
        }
        wasInGame = currentlyInGame;
        
        if (visualFeatures.autoRespawn && isDead() && Date.now() - lastDeadTime > autoRespawnDelay) {
            respawn();
        }
    }
    setInterval(checkGameStateChange, 100);
    
    function respawn() {
        try {
            
            if (isInGame() || window.playing) {
                
                if (typeof window.disconnect === 'function') {
                    window.disconnect();
                }
                
                window.playing = false;
                if (window.slither) {
                    window.slither.dead = true;
                }
                
                setTimeout(() => {
                    attemptRespawn();
                }, 200);
                return;
            }
            
            attemptRespawn();
        } catch (e) {
            console.error('Respawn error:', e);
        }
    }
    function attemptRespawn() {
        try {
            
            const playBtn = document.querySelector('#playh .nsi');
            if (playBtn) {
                playBtn.click();
                console.log('Respawn: clicked play button');
                return;
            }
            
            if (window.play_btn && window.play_btn.elem) {
                window.play_btn.elem.click();
                console.log('Respawn: clicked play_btn.elem');
                return;
            }
            
            if (typeof window.connect === 'function') {
                window.want_play = true;
                window.connect();
                console.log('Respawn: called connect()');
                return;
            }
            
            const nickInput = document.getElementById('nick');
            if (nickInput) {
                nickInput.focus();
                const event = new KeyboardEvent('keydown', {
                    key: 'Enter',
                    keyCode: 13,
                    bubbles: true
                });
                nickInput.dispatchEvent(event);
                console.log('Respawn: simulated Enter key');
            }
        } catch (e) {
            console.error('Attempt respawn error:', e);
        }
    }
    
    let originalBg = null;
    function setCustomBackgroundColor(color) {
        const win = window;
        if (!win.bgi2 || !(win.bgi2 instanceof HTMLCanvasElement)) {
            console.warn("[setCustomBackgroundColor] win.bgi2 is invalid or missing. Reinitializing...");
            win.bgi2 = document.createElement("canvas");
            win.bgi2.width = window.innerWidth;
            win.bgi2.height = window.innerHeight;
            win.bgi2.style.display = "none";
            document.body.appendChild(win.bgi2);
        }
        if (win.bgi2.width === 0 || win.bgi2.height === 0) {
            win.bgi2.width = window.innerWidth;
            win.bgi2.height = window.innerHeight;
        }
        const bgCanvas = win.bgi2.getContext("2d");
        if (!bgCanvas) {
            console.error("[setCustomBackgroundColor] Error: Could not get 2D context for win.bgi2.");
            return;
        }
        
        if (!originalBg && color === '#000000') {
            originalBg = bgCanvas.getImageData(0, 0, win.bgi2.width, win.bgi2.height);
        }
        bgCanvas.clearRect(0, 0, win.bgi2.width, win.bgi2.height);
        bgCanvas.fillStyle = color;
        bgCanvas.fillRect(0, 0, win.bgi2.width, win.bgi2.height);
        if (typeof win.setBgp2 === "function") {
            win.setBgp2(win.bgi2);
        } else {
            win.bgp2 = bgCanvas.createPattern(win.bgi2, "repeat");
        }
        console.log('[setCustomBackgroundColor] Background set to:', color);
    }
    function restoreOriginalBackground() {
        const win = window;
        if (win.bgi2 && originalBg) {
            const bgCanvas = win.bgi2.getContext("2d");
            if (bgCanvas) {
                bgCanvas.putImageData(originalBg, 0, 0);
                if (typeof win.setBgp2 === "function") {
                    win.setBgp2(win.bgi2);
                } else {
                    win.bgp2 = bgCanvas.createPattern(win.bgi2, "repeat");
                }
            }
        }
    }
    
    function applyVisualFeatures() {
        
        if (visualFeatures.blackBackground) {
            setCustomBackgroundColor('#000000');
        } else {
            restoreOriginalBackground();
        }
        
        if (visualFeatures.lowPerformance) {
            if (typeof window.render_mode !== 'undefined') {
                window.render_mode = 1;
                window.want_quality = 0;
                window.high_quality = false;
            }
        } else {
            if (typeof window.render_mode !== 'undefined') {
                window.render_mode = 2;
                window.want_quality = 1;
                window.high_quality = true;
            }
        }
    }
    
    setInterval(() => {
        if (isInGame() && window.slither) {
            
            if (visualFeatures.hideBoostGlow && window.slither.pr !== undefined) {
            }
            
            if (visualFeatures.instantDeadSnake && window.slithers) {
                try {
                    for (let i = window.slithers.length - 1; i >= 0; i--) {
                        const snake = window.slithers[i];
                        if (snake && (snake.dead === true || snake.dead_amt > 0)) {
                            if (typeof window.destroySlitherAtIndex === 'function') {
                                window.destroySlitherAtIndex(i);
                            } else {
                                window.slithers.splice(i, 1);
                            }
                        }
                    }
                } catch (e) {
                    
                }
            }
            
            if (visualFeatures.instantEatFood && window.foods && window.foods_c) {
                try {
                    for (let i = window.foods_c - 1; i >= 0; i--) {
                        const food = window.foods[i];
                        if (food && food.eaten === true) {
                            
                            if (typeof window.destroyFood === 'function') {
                                window.destroyFood(food);
                            }
                            
                            if (i === window.foods_c - 1) {
                                window.foods[i] = null;
                                window.foods_c--;
                            } else {
                                window.foods[i] = window.foods[window.foods_c - 1];
                                window.foods[window.foods_c - 1] = null;
                                window.foods_c--;
                            }
                        }
                    }
                } catch (e) {
                    
                }
            }
            if (visualFeatures.noFoodAnimation && window.foods && window.foods_c) {
                try {
                    for (let i = 0; i < window.foods_c; i++) {
                        const food = window.foods[i];
                        if (food && !food.eaten) {
                            food.fr = 1;   
                            food.rad = 1;  
                        }
                    }
                } catch (e) {
                    
                }
            }
            
            if (visualFeatures.noFoodWobble && window.foods && window.foods_c) {
                try {
                    for (let i = 0; i < window.foods_c; i++) {
                        const food = window.foods[i];
                        if (food) {
                            food.wsp = 0;  
                        }
                    }
                } catch (e) {
                    
                }
            }
        }
        
        if (window.slithers) {
            for (let snake of window.slithers) {
                if (!snake) continue;
                
                if (snake._originalAlpha === undefined) {
                    snake._originalAlpha = (snake.alpha !== undefined ? snake.alpha : 1.0);
                }
                
                if (snake.alpha !== undefined)     snake.alpha     = visualFeatures.snakeOpacity;
                if (snake.alive_amt !== undefined) snake.alive_amt = visualFeatures.snakeOpacity;
                if (snake.opacity !== undefined)   snake.opacity   = visualFeatures.snakeOpacity;
                
                if (snake.pts) {
                    for (let pt of snake.pts) {
                        if (pt && pt.alpha !== undefined) {
                            pt.alpha = visualFeatures.snakeOpacity;
                        }
                    }
                }
            }
        }
        
    }, 50);
    
    function createPointer() {
        pointerElement = document.createElement('div');
        updatePointerStyle();
        document.body.appendChild(pointerElement);
    }
    
    function updatePointerStyle() {
        if (!pointerElement) return;
        const size = pointerSettings.size;
        const color = pointerSettings.color;
        pointerElement.style.cssText = `
            position: fixed;
            width: ${size}px;
            height: ${size}px;
            pointer-events: none;
            z-index: 10000;
            display: none;
            transform: translate(-50%, -50%);
        `;
        pointerElement.innerHTML = `
            <svg width="${size}" height="${size}" viewBox="0 0 40 40" xmlns="http:
                <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="${color}"
                      stroke="white"
                      stroke-width="2"
                      filter="url(#glow)"/>
                <circle cx="20" cy="20" r="3" fill="white" opacity="0.8"/>
            </svg>
        `;
    }
    
    function createGuideLine() {
        guideLine = document.createElement('div');
        guideLine.style.cssText = `
            position: fixed;
            pointer-events: none;
            z-index: 9999;
            display: none;
            transform-origin: left center;
        `;
        document.body.appendChild(guideLine);
    }
    
    function createSnakeDot() {
        snakeDot = document.createElement('div');
        snakeDot.style.cssText = `
            position: fixed;
            width: 8px;
            height: 8px;
            border-radius: 50%;
            background: #ff0000;
            border: 2px solid white;
            pointer-events: none;
            z-index: 10001;
            display: none;
            transform: translate(-50%, -50%);
            box-shadow: 0 0 10px rgba(255, 0, 0, 0.8);
        `;
        document.body.appendChild(snakeDot);
    }
    
    function updateGuideLine() {
        if (!guideLine || !visualFeatures.showGuideLine || !touchActive || !isInGame()) {
            if (guideLine) guideLine.style.display = 'none';
            return;
        }
        const centerX = window.innerWidth / 2;
        const centerY = window.innerHeight / 2;
        const dx = pointerX - centerX;
        const dy = pointerY - centerY;
        const length = Math.sqrt(dx * dx + dy * dy);
        const angle = Math.atan2(dy, dx);
        guideLine.style.left = centerX + 'px';
        guideLine.style.top = centerY + 'px';
        guideLine.style.width = length + 'px';
        guideLine.style.height = guidelineSettings.thickness + 'px';
        guideLine.style.background = guidelineSettings.color;
        guideLine.style.transform = `rotate(${angle}rad)`;
        guideLine.style.display = 'block';
    }
    
    function updateSnakeDot() {
        
        if (!snakeDot || !visualFeatures.showSnakeDot || !isInGame()) {
            if (snakeDot) snakeDot.style.display = 'none';
            return;
        }
        if (!window.slither || !window.slither.pts || window.slither.pts.length === 0) {
            snakeDot.style.display = 'none';
            return;
        }
        try {
            
            const snake = window.slither;
            const ang = snake.ang || 0;
            
            const snakeRadius = snake.sc * 29; 
            
            const centerX = window.innerWidth / 2;
            const centerY = window.innerHeight / 2;
            
            const gsc = window.gsc || 1;
            const screenX = centerX + Math.cos(ang) * snakeRadius * gsc;
            const screenY = centerY + Math.sin(ang) * snakeRadius * gsc;
            snakeDot.style.left = screenX + 'px';
            snakeDot.style.top = screenY + 'px';
            snakeDot.style.display = 'block';
        } catch (e) {
            snakeDot.style.display = 'none';
        }
    }
    
    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 + '%';
    }
    
    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 });
        boostButton.addEventListener('touchcancel', 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)';
        
        if (touchActive) {
            simulateMouseDown();
        } else {
            
            if (typeof window.setAcceleration === 'function') {
                window.setAcceleration(1);
            }
        }
    }
    function handleBoostEnd(e) {
        if (editMode) return;
        e.preventDefault();
        e.stopPropagation();
        isAccelerating = false;
        boostButton.style.filter = 'brightness(1)';
        if (touchActive) {
            simulateMouseUp();
        } else {
            
            if (typeof window.setAcceleration === 'function') {
                window.setAcceleration(0);
            }
        }
    }
    
    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);
    }
    
    function createRespawnButton() {
        respawnButton = document.createElement('button');
        respawnButton.textContent = 'PLAY';
        respawnButton.className = 'control-button';
        respawnButton.dataset.buttonType = 'respawn';
        respawnButton.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: ${visualFeatures.showRespawnButton ? 'flex' : 'none'};
            align-items: center;
            justify-content: center;
            cursor: pointer;
        `;
        applyButtonStyle(respawnButton, 'respawn');
        respawnButton.addEventListener('touchstart', (e) => {
            if (editMode) {
                startDragging(e, respawnButton);
                return;
            }
            e.preventDefault();
            e.stopPropagation();
            respawnButton.style.filter = 'brightness(1.3)';
        }, { passive: false });
        respawnButton.addEventListener('touchend', (e) => {
            if (editMode) return;
            e.preventDefault();
            e.stopPropagation();
            respawnButton.style.filter = 'brightness(1)';
            
            respawn();
        }, { passive: false });
        document.body.appendChild(respawnButton);
    }
    
    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: 100000;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            box-shadow: 0 2px 8px rgba(0,0,0,0.3);
            touch-action: manipulation;
            pointer-events: auto;
        `;
        
        const blockEvent = (e) => {
            e.stopPropagation();
            e.stopImmediatePropagation();
            e.preventDefault();
        };
        settingsButton.addEventListener('touchstart', blockEvent, { passive: false, capture: true });
        settingsButton.addEventListener('touchmove', blockEvent, { passive: false, capture: true });
        settingsButton.addEventListener('touchend', (e) => {
            e.stopPropagation();
            e.stopImmediatePropagation();
            e.preventDefault();
            toggleSettingsPanel();
        }, { passive: false, capture: true });
        document.body.appendChild(settingsButton);
    }
    
    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: 100000;
            display: none;
            overflow-y: auto;
            color: white;
            font-family: Arial, sans-serif;
            box-shadow: 0 4px 20px rgba(0,0,0,0.5);
            touch-action: auto;
            pointer-events: auto;
        `;
        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; touch-action: manipulation;">
                移動: OFF
            </button>
            <div style="margin-bottom: 20px; padding: 12px; background: rgba(255,255,255,0.1); border-radius: 8px;">
                <h4 style="margin: 0 0 10px 0;">見た目</h4>
                ${createToggleButton('hideBoostGlow', 'ブーストの光を非表示', visualFeatures.hideBoostGlow)}
                ${createToggleButton('showGuideLine', 'ラインを表示', visualFeatures.showGuideLine)}
                ${createToggleButton('showSnakeDot', '点を表示', visualFeatures.showSnakeDot)}
                ${createToggleButton('showPointer', 'ポインターを表示', visualFeatures.showPointer)}
                ${createToggleButton('lowPerformance', 'シンプル', visualFeatures.lowPerformance)}
                ${createToggleButton('blackBackground', '背景黒', visualFeatures.blackBackground)}
                ${createToggleButton('instantDeadSnake', '蛇消去', visualFeatures.instantDeadSnake)}
                ${createToggleButton('instantEatFood', 'エサ消去', visualFeatures.instantEatFood)}
                ${createToggleButton('noFoodAnimation', 'エサ出現アニメ無し', visualFeatures.noFoodAnimation)}
                ${createToggleButton('noFoodWobble', 'エサの揺れ無し', visualFeatures.noFoodWobble)}
                ${createSlider('蛇の透明度', 'snakeOpacity', visualFeatures.snakeOpacity, 0.1, 1.0, 0.1, true)}
            </div>
            <div style="margin-bottom: 20px; padding: 12px; background: rgba(255,255,255,0.1); border-radius: 8px;">
                <h4 style="margin: 0 0 10px 0;">ライン設定</h4>
                ${createSlider('太さ', 'guidelineThickness', guidelineSettings.thickness, 1, 10, 1, true)}
                ${createColorPicker('色', 'guidelineColor', guidelineSettings.color)}
            </div>
            <div style="margin-bottom: 20px; padding: 12px; background: rgba(255,255,255,0.1); border-radius: 8px;">
                <h4 style="margin: 0 0 10px 0;">ポインター</h4>
                ${createSlider('サイズ', 'pointerSize', pointerSettings.size, 20, 80, 5, true)}
                ${createSlider('速度', 'pointerSpeed', pointerSettings.speed, 0.5, 5.0, 0.1, true)}
                ${createColorPicker('色', 'pointerColor', pointerSettings.color)}
            </div>
            <div style="margin-bottom: 20px; padding: 12px; background: rgba(255,255,255,0.1); border-radius: 8px;">
                <h4 style="margin: 0 0 10px 0;">設定</h4>
                ${createToggleButton('autoRespawn', 'オートリスポーン', visualFeatures.autoRespawn)}
                ${createSlider('リスポーンディレイ(ms)', 'autoRespawnDelay', autoRespawnDelay, 100, 5000, 100, true)}
                ${createToggleButton('resetZoomOnRespawn', 'リスポーンズームリセット', visualFeatures.resetZoomOnRespawn)}
                ${createToggleButton('showRespawnButton', 'リスポーンボタン表示', visualFeatures.showRespawnButton)}
            </div>
            <div id="buttonSettingsContainer"></div>
            <div id="screenButtonsEditContainer" style="display: none; margin-bottom: 20px; padding: 12px; background: rgba(255,255,255,0.1); border-radius: 8px;">
                <h4 style="margin: 0 0 10px 0;">ボタン編集</h4>
                <div id="screenButtonsList"></div>
            </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; touch-action: manipulation;">
                設定リセット
            </button>
        `;
        document.body.appendChild(settingsPanel);
        
        const blockEvent = (e) => {
            e.stopPropagation();
            e.stopImmediatePropagation();
        };
        settingsPanel.addEventListener('touchstart', blockEvent, { passive: false, capture: true });
        settingsPanel.addEventListener('touchmove', blockEvent, { passive: false, capture: true });
        settingsPanel.addEventListener('touchend', blockEvent, { passive: false, capture: true });
        settingsPanel.addEventListener('mousedown', blockEvent, { passive: false, capture: true });
        settingsPanel.addEventListener('mousemove', blockEvent, { passive: false, capture: true });
        settingsPanel.addEventListener('mouseup', blockEvent, { passive: false, capture: true });
        document.getElementById('toggleEditMode').addEventListener('click', toggleEditMode);
        document.getElementById('resetSettings').addEventListener('click', resetSettings);
        
        ['hideBoostGlow', 'showGuideLine', 'showSnakeDot', 'showPointer', 'lowPerformance', 'blackBackground',
         'autoRespawn', 'instantDeadSnake', 'instantEatFood', 'resetZoomOnRespawn', 'noFoodAnimation','noFoodWobble', 'showRespawnButton'].forEach(feature => {
            const btn = document.getElementById(`toggle-${feature}`);
            if (btn) {
                btn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    toggleVisualFeature(feature);
                });
            }
            
            const addBtn = document.getElementById(`add-${feature}`);
            if (addBtn) {
                addBtn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    addScreenButtonForFeature(feature);
                });
            }
        });
        
        const opacitySlider = document.getElementById('snakeOpacity');
        if (opacitySlider) {
            opacitySlider.addEventListener('input', (e) => {
                visualFeatures.snakeOpacity = parseFloat(e.target.value);
                document.getElementById('snakeOpacity-val').textContent = visualFeatures.snakeOpacity.toFixed(1);
                saveVisualFeatures();
                applyVisualFeatures();
            });
        }
        
        const pointerSpeedSlider = document.getElementById('pointerSpeed');
        if (pointerSpeedSlider) {
            pointerSpeedSlider.addEventListener('input', (e) => {
                pointerSettings.speed = parseFloat(e.target.value);
                document.getElementById('pointerSpeed-val').textContent = pointerSettings.speed.toFixed(1);
                saveVisualFeatures();
            });
        }
        
        const delaySlider = document.getElementById('autoRespawnDelay');
        if (delaySlider) {
            delaySlider.addEventListener('input', (e) => {
                autoRespawnDelay = parseInt(e.target.value);
                document.getElementById('autoRespawnDelay-val').textContent = autoRespawnDelay;
                saveVisualFeatures();
            });
        }
        
        const guidelineThicknessSlider = document.getElementById('guidelineThickness');
        if (guidelineThicknessSlider) {
            guidelineThicknessSlider.addEventListener('input', (e) => {
                guidelineSettings.thickness = parseInt(e.target.value);
                document.getElementById('guidelineThickness-val').textContent = guidelineSettings.thickness;
                saveVisualFeatures();
                updateGuideLine();
            });
        }
        
        const guidelineColorPicker = document.getElementById('guidelineColor');
        if (guidelineColorPicker) {
            guidelineColorPicker.addEventListener('input', (e) => {
                guidelineSettings.color = e.target.value;
                saveVisualFeatures();
                updateGuideLine();
            });
        }
        
        const pointerSizeSlider = document.getElementById('pointerSize');
        if (pointerSizeSlider) {
            pointerSizeSlider.addEventListener('input', (e) => {
                pointerSettings.size = parseInt(e.target.value);
                document.getElementById('pointerSize-val').textContent = pointerSettings.size;
                saveVisualFeatures();
                updatePointerStyle();
            });
        }
        
        const pointerColorPicker = document.getElementById('pointerColor');
        if (pointerColorPicker) {
            pointerColorPicker.addEventListener('input', (e) => {
                pointerSettings.color = e.target.value;
                saveVisualFeatures();
                updatePointerStyle();
            });
        }
        updateSettingsPanel();
    }
    function createToggleButton(id, label, isActive) {
        return `
            <div style="display: flex; gap: 5px; margin-bottom: 8px;">
                <button id="toggle-${id}" style="flex: 1; padding: 8px; background: ${isActive ? '#4CAF50' : '#666'}; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 13px; text-align: left; padding-left: 12px; touch-action: manipulation;">
                    ${label}: <span style="float: right; font-weight: bold; padding-right: 5px;">${isActive ? 'ON' : 'OFF'}</span>
                </button>
                <button id="add-${id}" style="width: 40px; padding: 8px; background: #2196F3; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; font-weight: bold; touch-action: manipulation;">
                    +
                </button>
            </div>
        `;
    }
    function toggleVisualFeature(feature) {
        visualFeatures[feature] = !visualFeatures[feature];
        saveVisualFeatures();
        applyVisualFeatures();
        const btn = document.getElementById(`toggle-${feature}`);
        if (btn) {
            const isActive = visualFeatures[feature];
            btn.style.background = isActive ? '#4CAF50' : '#666';
            btn.querySelector('span').textContent = isActive ? 'ON' : 'OFF';
        }
        if (feature === 'showGuideLine') {
            updateGuideLine();
        }
        if (feature === 'showSnakeDot') {
            updateSnakeDot();
        }
        if (feature === 'showPointer' && pointerElement) {
            pointerElement.style.display = visualFeatures.showPointer ? (touchActive ? 'block' : 'none') : 'none';
        }
        if (feature === 'showRespawnButton' && respawnButton) {
            respawnButton.style.display = visualFeatures.showRespawnButton ? 'flex' : 'none';
        }
    }
    function toggleSettingsPanel() {
        const isVisible = settingsPanel.style.display !== 'none';
        settingsPanel.style.display = isVisible ? 'none' : 'block';
        if (!isVisible) {
            updateSettingsPanel();
            
            const screenButtonsEditContainer = document.getElementById('screenButtonsEditContainer');
            if (screenButtonsEditContainer && screenButtons.length > 0) {
                screenButtonsEditContainer.style.display = 'block';
                updateScreenButtonsList();
            }
        } else {
            
            const featureEditPanel = document.getElementById('feature-button-edit-panel');
            if (featureEditPanel) {
                featureEditPanel.remove();
            }
            
            const buttonEditPanels = document.querySelectorAll('[id$="-edit-panel"]');
            buttonEditPanels.forEach(panel => panel.remove());
            
            editingButton = null;
            
            const screenButtonsEditContainer = document.getElementById('screenButtonsEditContainer');
            if (screenButtonsEditContainer) {
                screenButtonsEditContainer.style.display = 'none';
            }
        }
    }
    
    function updateScreenButtonsList() {
        const listContainer = document.getElementById('screenButtonsList');
        if (!listContainer) return;
        if (screenButtons.length === 0) {
            listContainer.innerHTML = '<p style="color: #999; font-size: 12px; margin: 5px 0;">ありません</p>';
            return;
        }
        let html = '';
        screenButtons.forEach(buttonData => {
            html += `
                <div style="display: flex; gap: 5px; margin-bottom: 8px; align-items: center;">
                    <div style="flex: 1; padding: 8px; background: rgba(100,100,100,0.5); border-radius: 5px; font-size: 12px;">
                        ${getFeatureLabel(buttonData.feature)}
                    </div>
                    <button class="edit-screen-button" data-button-id="${buttonData.id}" style="padding: 8px 12px; background: #2196F3; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 12px;">
                        編集
                    </button>
                </div>
            `;
        });
        listContainer.innerHTML = html;
        
        document.querySelectorAll('.edit-screen-button').forEach(btn => {
            btn.addEventListener('click', (e) => {
                e.stopPropagation();
                const buttonId = btn.dataset.buttonId;
                const buttonData = screenButtons.find(b => b.id === buttonId);
                const buttonElement = document.querySelector(`.feature-button[data-button-id="${buttonId}"]`);
                if (buttonData && buttonElement) {
                    openFeatureButtonEditMenu(buttonData, buttonElement);
                }
            });
        });
    }
    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 => {
            const borderWidth = button.dataset.buttonType === 'boost' || button.dataset.buttonType === 'respawn' ? '3px' : '2px';
            button.style.border = editMode ? '3px dashed yellow' : `${borderWidth} solid white`;
        });
    }
    function updateSettingsPanel() {
        const container = document.getElementById('buttonSettingsContainer');
        if (!container) return;
        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 titles = {
                boost: 'ブースト',
                zoomIn: 'ズームイン',
                zoomOut: 'ズームアウト',
                respawn: 'リスポーン'
            };
            const title = titles[key] || key;
            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; touch-action: manipulation;">
                        ${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, false);
                html += createSlider('高さ', `${key}-height`, s.height, 30, 200, 1, false);
                html += createSlider('角丸', `${key}-radius`, s.borderRadius, 0, 50, 1, false);
                html += createSlider('透明度', `${key}-opacity`, s.opacity, 0.1, 1, 0.1, false);
                html += createColorPicker('色', `${key}-color`, s.color);
                if (key === 'zoomIn' || key === 'zoomOut') {
                    html += createSlider('変更値', `${key}-value`, s.value, 0.01, 1.0, 0.01, false);
                }
                html += `</div>`;
            }
            section.innerHTML = html;
            container.appendChild(section);
            section.querySelector('.edit-button').addEventListener('click', (e) => {
                e.stopPropagation();
                editingButton = editingButton === key ? null : key;
                updateSettingsPanel();
            });
            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 === 'zoomIn' || key === 'zoomOut') {
                    document.getElementById(`${key}-value`).addEventListener('input', (e) => updateSetting(key, 'value', parseFloat(e.target.value)));
                }
            }
        });
    }
    function createSlider(label, id, value, min, max, step, isVisual) {
        const displayValue = step < 0.1 ? value.toFixed(2) : (step < 1 ? value.toFixed(1) : value);
        return `
            <div style="margin: 8px 0;">
                <label style="display: block; margin-bottom: 3px; font-size: 12px;">${label}: <span id="${id}-val">${displayValue}</span></label>
                <input type="range" id="${id}" min="${min}" max="${max}" step="${step}" value="${value}" style="width: 100%; touch-action: pan-x;">
            </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; touch-action: manipulation;">
            </div>
        `;
    }
    function updateSetting(buttonType, property, value) {
        buttonSettings[buttonType][property] = value;
        const valSpan = document.getElementById(`${buttonType}-${property}-val`);
        if (valSpan) {
            if (property === 'value') {
                valSpan.textContent = value.toFixed(2);
            } else if (property === 'opacity') {
                valSpan.textContent = value.toFixed(1);
            } else {
                valSpan.textContent = value.toFixed(0);
            }
        }
        const buttonMap = {
            boost: boostButton,
            zoomIn: zoomInButton,
            zoomOut: zoomOutButton,
            respawn: respawnButton
        };
        const button = buttonMap[buttonType];
        if (button) {
            applyButtonStyle(button, buttonType);
        }
        saveSettings();
    }
    function resetSettings() {
        if (confirm('設定をリセットしますか?')) {
            localStorage.removeItem('slitherMobileSettings');
            localStorage.removeItem('slitherVisualFeatures');
            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;
    }
    function adjustZoom(delta) {
        currentZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, currentZoom + delta));
        if (!visualFeatures.resetZoomOnRespawn) {
            gameStartZoom = currentZoom;
        }
        
        if (window.gsc !== undefined) {
            window.gsc = currentZoom;
        }
        if (window.lgsc !== undefined) {
            window.lgsc = currentZoom;
        }
        if (window.sgsc !== undefined) {
            window.sgsc = currentZoom;
        }
    }
    
    function getSnakeAngle() {
        if (!window.slither) return lastKnownAngle;
        
        const angleProps = ['eang', 'wang', 'ang', 'ehang'];
        for (const prop of angleProps) {
            if (typeof window.slither[prop] !== 'undefined' && window.slither[prop] !== null) {
                lastKnownAngle = window.slither[prop];
                return window.slither[prop];
            }
        }
        
        if (typeof window.ang !== 'undefined') {
            lastKnownAngle = window.ang;
            return window.ang;
        }
        
        if (typeof window.view_ang !== 'undefined') {
            lastKnownAngle = window.view_ang;
            return window.view_ang;
        }
        return lastKnownAngle;
    }
    
    function initializePointerPosition() {
        const centerX = window.innerWidth / 2;
        const centerY = window.innerHeight / 2;
        const snakeAngle = getSnakeAngle();
        
        basePointerX = centerX + Math.cos(snakeAngle) * POINTER_BASE_DISTANCE;
        basePointerY = centerY + Math.sin(snakeAngle) * POINTER_BASE_DISTANCE;
        
        pointerX = basePointerX;
        pointerY = basePointerY;
    }
    
    function updatePointerPosition() {
        if (!touchActive || !pointerElement || !isInGame()) {
            if (pointerElement) pointerElement.style.display = 'none';
            updateGuideLine();
            return;
        }
        
        const fingerDeltaX = touchCurrentX - touchStartX;
        const fingerDeltaY = touchCurrentY - touchStartY;
        
        const adjustedDeltaX = fingerDeltaX * pointerSettings.speed;
        const adjustedDeltaY = fingerDeltaY * pointerSettings.speed;
        
        pointerX = basePointerX + adjustedDeltaX;
        pointerY = basePointerY + adjustedDeltaY;
        
        pointerElement.style.left = pointerX + 'px';
        pointerElement.style.top = pointerY + 'px';
        pointerElement.style.display = visualFeatures.showPointer ? 'block' : 'none';
        
        const centerX = window.innerWidth / 2;
        const centerY = window.innerHeight / 2;
        const angleToCenter = Math.atan2(centerY - pointerY, centerX - pointerX);
        const rotationDegrees = (angleToCenter * 180 / Math.PI) + 90;
        pointerElement.style.transform = `translate(-50%, -50%) rotate(${rotationDegrees}deg)`;
        updateGuideLine();
    }
    let mouseUpdateInterval = null;
    function startMouseTracking() {
        if (mouseUpdateInterval) return;
        console.log('Starting mouse tracking');
        mouseUpdateInterval = setInterval(() => {
            if (touchActive && isInGame()) {
                simulateMouseMove(pointerX, pointerY);
                updateSnakeDot();
            }
        }, 16);
    }
    function stopMouseTracking() {
        if (mouseUpdateInterval) {
            console.log('Stopping mouse tracking');
            clearInterval(mouseUpdateInterval);
            mouseUpdateInterval = null;
        }
    }
    function simulateMouseMove(x, y) {
        if (!isInGame()) return;
        const canvas = document.querySelector('canvas');
        if (!canvas) return;
        
        if (window.slither && typeof window.gsc !== 'undefined') {
            const centerX = window.innerWidth / 2;
            const centerY = window.innerHeight / 2;
            
            const dx = x - centerX;
            const dy = y - centerY;
            
            
            window.xm = dx;
            window.ym = dy;
        }
        const event = new MouseEvent('mousemove', {
            clientX: x,
            clientY: y,
            bubbles: true,
            cancelable: true,
            view: window
        });
        canvas.dispatchEvent(event);
        window.onmousemove && window.onmousemove(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);
        window.onmousedown && window.onmousedown(event);
        
        if (typeof window.setAcceleration === 'function') {
            window.setAcceleration(1);
        }
    }
    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);
        window.onmouseup && window.onmouseup(event);
        
        if (typeof window.setAcceleration === 'function') {
            window.setAcceleration(0);
        }
    }
    
    document.addEventListener('touchstart', (e) => {
        
        if (e.target.closest('#settings-panel') ||
            e.target === settingsButton ||
            e.target.closest('#feature-button-edit-panel')) {
            return;
        }
        
        if (e.target.closest('.control-button') || e.target.closest('.feature-button')) {
            return;
        }
        if (!isInGame()) {
            return;
        }
        
        e.preventDefault();
        e.stopPropagation();
        touchActive = true;
        touchStartX = e.touches[0].clientX;
        touchStartY = e.touches[0].clientY;
        touchCurrentX = touchStartX;
        touchCurrentY = touchStartY;
        console.log('Gameplay touch started');
        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;
        
        if (e.target.closest('#settings-panel') ||
            e.target.closest('#feature-button-edit-panel')) {
            return;
        }
        if (isInGame()) {
            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') ||
            e.target.closest('#feature-button-edit-panel')) {
            return;
        }
        
        if (e.touches.length === 0 && touchActive) {
            console.log('All touches ended');
            if (isInGame()) {
                e.preventDefault();
                e.stopPropagation();
            }
            touchActive = false;
            stopMouseTracking();
            if (pointerElement) {
                pointerElement.style.display = 'none';
            }
            if (guideLine) {
                guideLine.style.display = 'none';
            }
        }
    }, { passive: false, capture: true });
    document.addEventListener('touchcancel', (e) => {
        console.log('Touch cancelled');
        touchActive = false;
        stopMouseTracking();
        if (pointerElement) {
            pointerElement.style.display = 'none';
        }
        if (guideLine) {
            guideLine.style.display = 'none';
        }
    }, { passive: false, capture: true });
    
    setInterval(() => {
        if (isInGame()) {
            updateSnakeDot();
        }
    }, 50);
    
    setInterval(() => {
        if (isInGame() && window.gsc !== undefined) {
            if (Math.abs(window.gsc - currentZoom) > 0.001) {
                window.gsc = currentZoom;
            }
        }
    }, 50);
    
    function loadScreenButtons() {
        const saved = localStorage.getItem('slitherScreenButtons');
        if (saved) {
            try {
                screenButtons = JSON.parse(saved);
            } catch (e) {
                console.error('Failed to load screen buttons:', e);
            }
        }
    }
    
    function saveScreenButtons() {
        localStorage.setItem('slitherScreenButtons', JSON.stringify(screenButtons));
    }
    
    function getFeatureLabel(featureId) {
        const labels = {
            'hideBoostGlow': 'ブースト光',
            'showGuideLine': 'ガイド',
            'showSnakeDot': '点',
            'showPointer': 'ポインター',
            'showSnakeCenterLine': '中心線',
            'noFoodAnimation': 'エサ揺れ',
            'noFoodWobble': 'エサアニメ',
            'instantDeadSnake': '死蛇消去',
            'instantEatFood': '食エサ消去',
            'simpleFoodGraphics': 'シンプルエサ'
        };
        return labels[featureId] || featureId;
    }
    
    function addScreenButtonForFeature(featureId) {
        
        const exists = screenButtons.find(b => b.feature === featureId);
        if (exists) {
            alert('この機能のボタンは既に追加されています');
            return;
        }
        const newButton = {
            id: Date.now().toString(),
            feature: featureId,
            x: window.innerWidth / 2 - 35,
            y: window.innerHeight / 2 - 35,
            width: 70,
            height: 70,
            color: visualFeatures[featureId] ? '#4CAF50' : '#666',
            opacity: 0.7,
            borderRadius: 15
        };
        screenButtons.push(newButton);
        saveScreenButtons();
        createFeatureButton(newButton);
        
        const screenButtonsEditContainer = document.getElementById('screenButtonsEditContainer');
        if (screenButtonsEditContainer) {
            screenButtonsEditContainer.style.display = 'block';
            updateScreenButtonsList();
        }
    }
    
    function createFeatureButton(buttonData) {
        const button = document.createElement('button');
        button.className = 'feature-button';
        button.dataset.buttonId = buttonData.id;
        button.dataset.feature = buttonData.feature;
        button.textContent = getFeatureLabel(buttonData.feature);
        button.style.cssText = `
            position: fixed;
            left: ${buttonData.x}px;
            top: ${buttonData.y}px;
            width: ${buttonData.width}px;
            height: ${buttonData.height}px;
            background-color: ${buttonData.color}${Math.round(buttonData.opacity * 255).toString(16).padStart(2, '0')}`;
        button.style.cssText += `
            border: 2px solid white;
            border-radius: ${buttonData.borderRadius}%;
            color: white;
            font-weight: bold;
            font-size: 11px;
            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;
            word-break: keep-all;
        `;
        updateFeatureButtonColor(button, buttonData.feature);
        button.addEventListener('touchstart', (e) => {
            if (editMode) {
                startDraggingFeatureButton(e, button, buttonData);
                return;
            }
            e.preventDefault();
            e.stopPropagation();
            button.style.filter = 'brightness(1.3)';
        }, { passive: false });
        button.addEventListener('touchend', (e) => {
            if (editMode) return;
            e.preventDefault();
            e.stopPropagation();
            button.style.filter = 'brightness(1)';
            toggleFeatureFromButton(buttonData.feature, button);
        }, { passive: false });
        document.body.appendChild(button);
    }
    
    function updateFeatureButtonColor(button, featureId) {
        const isActive = visualFeatures[featureId];
        const buttonData = screenButtons.find(b => b.id === button.dataset.buttonId);
        if (buttonData) {
            buttonData.color = isActive ? '#4CAF50' : '#666';
            button.style.backgroundColor = `${buttonData.color}${Math.round(buttonData.opacity * 255).toString(16).padStart(2, '0')}`;
        }
    }
    
    function toggleFeatureFromButton(featureId, button) {
        visualFeatures[featureId] = !visualFeatures[featureId];
        saveVisualFeatures();
        applyVisualFeatures();
        updateFeatureButtonColor(button, featureId);
        
        const mainToggle = document.getElementById(`toggle-${featureId}`);
        if (mainToggle) {
            const isActive = visualFeatures[featureId];
            mainToggle.style.background = isActive ? '#4CAF50' : '#666';
            const span = mainToggle.querySelector('span');
            if (span) span.textContent = isActive ? 'ON' : 'OFF';
        }
    }
    
    function deleteFeatureButton(buttonId) {
        screenButtons = screenButtons.filter(b => b.id !== buttonId);
        saveScreenButtons();
    }
    
    function startDraggingFeatureButton(e, button, buttonData) {
        e.preventDefault();
        e.stopPropagation();
        const startX = e.touches[0].clientX;
        const startY = e.touches[0].clientY;
        const offsetX = startX - buttonData.x;
        const offsetY = startY - buttonData.y;
        const handleMove = (e) => {
            e.preventDefault();
            const newX = e.touches[0].clientX - offsetX;
            const newY = e.touches[0].clientY - offsetY;
            buttonData.x = newX;
            buttonData.y = newY;
            button.style.left = newX + 'px';
            button.style.top = newY + 'px';
            saveScreenButtons();
        };
        const handleEnd = () => {
            document.removeEventListener('touchmove', handleMove);
            document.removeEventListener('touchend', handleEnd);
        };
        document.addEventListener('touchmove', handleMove, { passive: false });
        document.addEventListener('touchend', handleEnd);
    }
    
    function openFeatureButtonEditMenu(buttonData, buttonElement) {
        
        let editPanel = document.getElementById('feature-button-edit-panel');
        if (editPanel) {
            editPanel.remove();
        }
        editPanel = document.createElement('div');
        editPanel.id = 'feature-button-edit-panel';
        editPanel.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 300px;
            background: rgba(30, 30, 35, 0.98);
            border: 2px solid white;
            border-radius: 10px;
            padding: 15px;
            z-index: 100000;
            color: white;
            font-family: Arial, sans-serif;
            box-shadow: 0 4px 20px rgba(0,0,0,0.5);
            touch-action: auto;
            pointer-events: auto;
        `;
        editPanel.innerHTML = `
            <h3 style="margin: 0 0 15px 0; text-align: center;">ボタン編集</h3>
            <div style="margin-bottom: 10px;">
                <label style="display: block; margin-bottom: 3px; font-size: 12px;">幅: <span id="fb-width-val">${buttonData.width}</span></label>
                <input type="range" id="fb-width" min="40" max="150" step="5" value="${buttonData.width}" style="width: 100%; touch-action: auto;">
            </div>
            <div style="margin-bottom: 10px;">
                <label style="display: block; margin-bottom: 3px; font-size: 12px;">高さ: <span id="fb-height-val">${buttonData.height}</span></label>
                <input type="range" id="fb-height" min="40" max="150" step="5" value="${buttonData.height}" style="width: 100%; touch-action: auto;">
            </div>
            <div style="margin-bottom: 10px;">
                <label style="display: block; margin-bottom: 3px; font-size: 12px;">角丸: <span id="fb-radius-val">${buttonData.borderRadius}</span></label>
                <input type="range" id="fb-radius" min="0" max="50" step="1" value="${buttonData.borderRadius}" style="width: 100%; touch-action: auto;">
            </div>
            <div style="margin-bottom: 10px;">
                <label style="display: block; margin-bottom: 3px; font-size: 12px;">透明度: <span id="fb-opacity-val">${buttonData.opacity.toFixed(1)}</span></label>
                <input type="range" id="fb-opacity" min="0.1" max="1" step="0.1" value="${buttonData.opacity}" style="width: 100%; touch-action: auto;">
            </div>
            <div style="display: flex; gap: 10px; margin-top: 15px;">
                <button id="fb-delete" style="flex: 1; padding: 10px; background: #f44336; color: white; border: none; border-radius: 5px; cursor: pointer; touch-action: manipulation;">削除</button>
                <button id="fb-close" style="flex: 1; padding: 10px; background: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; touch-action: manipulation;">閉じる</button>
            </div>
        `;
        document.body.appendChild(editPanel);
        
        const blockEvent = (e) => {
            e.stopPropagation();
            e.stopImmediatePropagation();
        };
        editPanel.addEventListener('touchstart', blockEvent, { passive: false, capture: true });
        editPanel.addEventListener('touchmove', blockEvent, { passive: false, capture: true });
        editPanel.addEventListener('touchend', blockEvent, { passive: false, capture: true });
        editPanel.addEventListener('mousedown', blockEvent, { passive: false, capture: true });
        editPanel.addEventListener('mousemove', blockEvent, { passive: false, capture: true });
        editPanel.addEventListener('mouseup', blockEvent, { passive: false, capture: true });
        
        const updateButtonStyle = () => {
            buttonElement.style.width = buttonData.width + 'px';
            buttonElement.style.height = buttonData.height + 'px';
            buttonElement.style.borderRadius = buttonData.borderRadius + '%';
            buttonElement.style.backgroundColor = `${buttonData.color}${Math.round(buttonData.opacity * 255).toString(16).padStart(2, '0')}`;
            saveScreenButtons();
        };
        document.getElementById('fb-width').addEventListener('input', (e) => {
            buttonData.width = parseInt(e.target.value);
            document.getElementById('fb-width-val').textContent = buttonData.width;
            updateButtonStyle();
        });
        document.getElementById('fb-height').addEventListener('input', (e) => {
            buttonData.height = parseInt(e.target.value);
            document.getElementById('fb-height-val').textContent = buttonData.height;
            updateButtonStyle();
        });
        document.getElementById('fb-radius').addEventListener('input', (e) => {
            buttonData.borderRadius = parseInt(e.target.value);
            document.getElementById('fb-radius-val').textContent = buttonData.borderRadius;
            updateButtonStyle();
        });
        document.getElementById('fb-opacity').addEventListener('input', (e) => {
            buttonData.opacity = parseFloat(e.target.value);
            document.getElementById('fb-opacity-val').textContent = buttonData.opacity.toFixed(1);
            updateButtonStyle();
        });
        document.getElementById('fb-delete').addEventListener('click', () => {
            if (confirm('このボタンを削除しますか?')) {
                deleteFeatureButton(buttonData.id);
                buttonElement.remove();
                editPanel.remove();
            }
        });
        document.getElementById('fb-close').addEventListener('click', () => {
            editPanel.remove();
        });
    }
    function init() {
        loadSettings();
        loadVisualFeatures();
        createPointer();
        createGuideLine();
        createSnakeDot();
        createBoostButton();
        createZoomButtons();
        createRespawnButton();
        createSettingsButton();
        createSettingsPanel();
        const canvas = document.querySelector('canvas');
        if (canvas) {
            canvas.style.touchAction = 'none';
        }
        if (window.gsc !== undefined) {
            currentZoom = window.gsc;
            gameStartZoom = currentZoom;
        }
        wasInGame = isInGame();
        applyVisualFeatures();
        
        loadScreenButtons();
        screenButtons.forEach(buttonData => {
            createFeatureButton(buttonData);
        });
        console.log('Slither.io mobile controls initialized v3.4');
        console.log('Visual features:', visualFeatures);
    }
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();