Slither.io Mobile Mod

Slither mod for mobile

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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';

    // 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 basePointerX = 0;  // Base position for pointer (where it starts)
    let basePointerY = 0;  // Base position for pointer (where it starts)
    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 = []; // Initialize screenButtons array

    const POINTER_BASE_DISTANCE = 100;  // Distance from center where pointer starts
    const MIN_ZOOM = 0.3;
    const MAX_ZOOM = 3.0;

    // Visual features state
    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: true
    };

    // Guideline settings
    let guidelineSettings = {
        thickness: 2,
        color: '#64ff64'
    };

    // Pointer settings
    let pointerSettings = {
        size: 40,
        color: '#64c8ff',
        speed: 2.5
    };

    // Load visual features settings
    function loadVisualFeatures() {
        const saved = localStorage.getItem('slitherVisualFeatures');
        if (saved) {
            try {
                const parsed = JSON.parse(saved);
                visualFeatures = { ...visualFeatures, ...parsed };
                // Migrate old pointerSpeed to new location
                if (parsed.pointerSpeed !== undefined) {
                    pointerSettings.speed = parsed.pointerSpeed;
                    delete visualFeatures.pointerSpeed;
                }
            } catch (e) {
                console.error('Failed to load visual features:', e);
            }
        }
        // Load auto respawn delay
        const savedDelay = localStorage.getItem('slitherAutoRespawnDelay');
        if (savedDelay) {
            autoRespawnDelay = parseInt(savedDelay) || 500;
        }
        // Load guideline settings
        const savedGuideline = localStorage.getItem('slitherGuidelineSettings');
        if (savedGuideline) {
            try {
                guidelineSettings = { ...guidelineSettings, ...JSON.parse(savedGuideline) };
            } catch (e) {
                console.error('Failed to load guideline settings:', e);
            }
        }
        // Load pointer settings
        const savedPointer = localStorage.getItem('slitherPointerSettings');
        if (savedPointer) {
            try {
                pointerSettings = { ...pointerSettings, ...JSON.parse(savedPointer) };
            } catch (e) {
                console.error('Failed to load pointer settings:', e);
            }
        }
    }

    // Save visual features settings
    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));
    }

    // 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
        },
        respawn: {
            x: 20,
            y: window.innerHeight - 110,
            width: 80,
            height: 80,
            color: '#4CAF50',
            opacity: 0.7,
            borderRadius: 50
        }
    };

    // 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.slither && window.slither.id !== undefined && window.playing) return true;
        if (window.playing === true) return true;
        return false;
    }

    // Check if dead
    function isDead() {
        if (!window.slither) return true;
        if (window.dead_mtm !== -1 && window.dead_mtm !== undefined) return true;
        return false;
    }

    // Monitor game state changes
    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;

        // Auto respawn with custom delay
        if (visualFeatures.autoRespawn && isDead() && Date.now() - lastDeadTime > autoRespawnDelay) {
            respawn();
        }
    }

    setInterval(checkGameStateChange, 100);

    // Respawn function - disconnect current game and start new one
    function respawn() {
        try {
            // First, disconnect from current game if playing
            if (isInGame() || window.playing) {
                // Force disconnect
                if (typeof window.disconnect === 'function') {
                    window.disconnect();
                }
                // Reset game state
                window.playing = false;
                if (window.slither) {
                    window.slither.dead = true;
                }

                // Wait a bit for disconnect to complete
                setTimeout(() => {
                    attemptRespawn();
                }, 200);
                return;
            }

            // If not in game, just respawn
            attemptRespawn();
        } catch (e) {
            console.error('Respawn error:', e);
        }
    }

    function attemptRespawn() {
        try {
            // Method 1: Direct simulation of play button click
            const playBtn = document.querySelector('#playh .nsi');
            if (playBtn) {
                playBtn.click();
                console.log('Respawn: clicked play button');
                return;
            }

            // Method 2: Use the global play button reference
            if (window.play_btn && window.play_btn.elem) {
                window.play_btn.elem.click();
                console.log('Respawn: clicked play_btn.elem');
                return;
            }

            // Method 3: Trigger the connect function directly
            if (typeof window.connect === 'function') {
                window.want_play = true;
                window.connect();
                console.log('Respawn: called connect()');
                return;
            }

            // Method 4: Simulate Enter key on nick input
            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);
        }
    }

    // Set custom background color using provided method
    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;
        }

        // Save original if not saved yet
        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");
                }
            }
        }
    }

    // Apply visual features
    function applyVisualFeatures() {
        // Black background
        if (visualFeatures.blackBackground) {
            setCustomBackgroundColor('#000000');
        } else {
            restoreOriginalBackground();
        }

        // Low performance mode
        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;
            }
        }
    }



    // Continuously apply visual features
    setInterval(() => {
        if (isInGame() && window.slither) {
            // Hide boost glow - properly handle the visual effect
            if (visualFeatures.hideBoostGlow && window.slither.pr !== undefined) {

            }

            // Instant dead snake removal
            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) {
                    // Silent catch to prevent console spam
                }
            }

            // Instant food removal - safely remove eaten food
            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) {
                            // Use game's own food removal if available
                            if (typeof window.destroyFood === 'function') {
                                window.destroyFood(food);
                            }
                            // Remove from array
                            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) {
                    // Silent catch to prevent console spam
                }
            }

            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) {
                    // Silent catch
                }
            }

            // ★ 追加:No food wobble - set wobble speed to 0
            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) {
                    // Silent catch
                }
            }
        }

        // ---- Apply snake opacity to all snakes (direct per-snake method) ----
        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);

    // Create visual pointer element
    function createPointer() {
        pointerElement = document.createElement('div');
        updatePointerStyle();
        document.body.appendChild(pointerElement);
    }

    // Update pointer style
    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://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="${color}"
                      stroke="white"
                      stroke-width="2"
                      filter="url(#glow)"/>
                <circle cx="20" cy="20" r="3" fill="white" opacity="0.8"/>
            </svg>
        `;
    }

    // Create guide line
    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);
    }

    // Create snake dot
    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);
    }

    // Update guide line
    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';
    }

    // Update snake dot - shows at the distance of snake's radius in the direction snake is facing
    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 {
            // Get the snake's angle
            const snake = window.slither;
            const ang = snake.ang || 0;

            // Calculate the snake's radius
            const snakeRadius = snake.sc * 29; // Snake radius calculation

            // Screen center
            const centerX = window.innerWidth / 2;
            const centerY = window.innerHeight / 2;

            // Calculate position: center + (angle direction * snake radius * game scale)
            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';
        }
    }


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

        // Only simulate mouse down if touch is active (so pointer position is set)
        if (touchActive) {
            simulateMouseDown();
        } else {
            // Just set acceleration without moving mouse
            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 {
            // Just unset acceleration
            if (typeof window.setAcceleration === 'function') {
                window.setAcceleration(0);
            }
        }
    }

    // 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 respawn button
    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 works both when dead and when alive (to restart)
            respawn();
        }, { passive: false });

        document.body.appendChild(respawnButton);
    }

    // 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: 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;
        `;

        // Prevent all touch events from propagating to game - use stronger blocking
        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);
    }

    // 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: 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', 'エサ出現アニメOFF', visualFeatures.noFoodAnimation)}
                ${createToggleButton('noFoodWobble', 'エサの揺れOFF', 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);

        // Prevent touch events from propagating to game - use stronger blocking
        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);

        // Add visual feature toggle listeners
        ['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);
                });
            }

            // Add button for screen feature
            const addBtn = document.getElementById(`add-${feature}`);
            if (addBtn) {
                addBtn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    addScreenButtonForFeature(feature);
                });
            }
        });

        // Snake opacity slider
        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();
            });
        }

        // Pointer speed slider (now in pointer settings)
        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();
            });
        }

        // Auto respawn delay slider
        const delaySlider = document.getElementById('autoRespawnDelay');
        if (delaySlider) {
            delaySlider.addEventListener('input', (e) => {
                autoRespawnDelay = parseInt(e.target.value);
                document.getElementById('autoRespawnDelay-val').textContent = autoRespawnDelay;
                saveVisualFeatures();
            });
        }

        // Guideline thickness slider
        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();
            });
        }

        // Guideline color picker
        const guidelineColorPicker = document.getElementById('guidelineColor');
        if (guidelineColorPicker) {
            guidelineColorPicker.addEventListener('input', (e) => {
                guidelineSettings.color = e.target.value;
                saveVisualFeatures();
                updateGuideLine();
            });
        }

        // Pointer size slider
        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();
            });
        }

        // Pointer color picker
        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();
            // Show screen buttons edit container when opening settings
            const screenButtonsEditContainer = document.getElementById('screenButtonsEditContainer');
            if (screenButtonsEditContainer && screenButtons.length > 0) {
                screenButtonsEditContainer.style.display = 'block';
                updateScreenButtonsList();
            }
        } else {
            // Close all open edit panels when closing settings
            const featureEditPanel = document.getElementById('feature-button-edit-panel');
            if (featureEditPanel) {
                featureEditPanel.remove();
            }

            // Close button edit panels (boost, zoom, respawn)
            const buttonEditPanels = document.querySelectorAll('[id$="-edit-panel"]');
            buttonEditPanels.forEach(panel => panel.remove());

            // Reset editing button state
            editingButton = null;

            // Hide screen buttons edit container
            const screenButtonsEditContainer = document.getElementById('screenButtonsEditContainer');
            if (screenButtonsEditContainer) {
                screenButtonsEditContainer.style.display = 'none';
            }
        }
    }

    // Update screen buttons list in settings
    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;

        // Add event listeners to edit buttons
        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;
        }
        // Force apply zoom immediately
        if (window.gsc !== undefined) {
            window.gsc = currentZoom;
        }
        if (window.lgsc !== undefined) {
            window.lgsc = currentZoom;
        }
        if (window.sgsc !== undefined) {
            window.sgsc = currentZoom;
        }
    }

    // Enhanced snake angle detection
    function getSnakeAngle() {
        if (!window.slither) return lastKnownAngle;

        // Priority order: eang > wang > ang > ehang
        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];
            }
        }

        // Fallback to global angle
        if (typeof window.ang !== 'undefined') {
            lastKnownAngle = window.ang;
            return window.ang;
        }

        // Fallback to view angle
        if (typeof window.view_ang !== 'undefined') {
            lastKnownAngle = window.view_ang;
            return window.view_ang;
        }

        return lastKnownAngle;
    }

    // Initialize pointer position based on current snake direction
    function initializePointerPosition() {
        const centerX = window.innerWidth / 2;
        const centerY = window.innerHeight / 2;
        const snakeAngle = getSnakeAngle();

        // Set base position at fixed distance from center in snake's direction
        basePointerX = centerX + Math.cos(snakeAngle) * POINTER_BASE_DISTANCE;
        basePointerY = centerY + Math.sin(snakeAngle) * POINTER_BASE_DISTANCE;

        // Initialize pointer at base position
        pointerX = basePointerX;
        pointerY = basePointerY;
    }

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

        // Calculate finger movement from touch start
        const fingerDeltaX = touchCurrentX - touchStartX;
        const fingerDeltaY = touchCurrentY - touchStartY;

        // Apply speed multiplier to finger movement
        const adjustedDeltaX = fingerDeltaX * pointerSettings.speed;
        const adjustedDeltaY = fingerDeltaY * pointerSettings.speed;

        // New pointer position = base position + adjusted finger movement
        pointerX = basePointerX + adjustedDeltaX;
        pointerY = basePointerY + adjustedDeltaY;

        // Update pointer element position
        pointerElement.style.left = pointerX + 'px';
        pointerElement.style.top = pointerY + 'px';
        pointerElement.style.display = visualFeatures.showPointer ? 'block' : 'none';

        // Rotate pointer to point toward center
        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;

        // Update global mouse position variables
        if (window.slither && typeof window.gsc !== 'undefined') {
            const centerX = window.innerWidth / 2;
            const centerY = window.innerHeight / 2;

            // Calculate relative position from pointer to center
            const dx = x - centerX;
            const dy = y - centerY;

            // Use absolute position instead of scaled position
            // This prevents speed increase when pointer is near center
            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);

        // Also set global boost variable
        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);

        // Also set global boost variable
        if (typeof window.setAcceleration === 'function') {
            window.setAcceleration(0);
        }
    }

    // Touch event handlers
    document.addEventListener('touchstart', (e) => {
        // Check if touching settings panel or settings button
        if (e.target.closest('#settings-panel') ||
            e.target === settingsButton ||
            e.target.closest('#feature-button-edit-panel')) {
            return;
        }

        // Check if touching control buttons or feature buttons
        if (e.target.closest('.control-button') || e.target.closest('.feature-button')) {
            return;
        }

        if (!isInGame()) {
            return;
        }

        // This is a gameplay touch
        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;

        // Don't prevent if touching settings or edit panels
        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;
        }

        // Don't stop if touching UI elements
        if (e.target.closest('.control-button') ||
            e.target === settingsButton ||
            e.target.closest('#settings-panel') ||
            e.target.closest('#feature-button-edit-panel')) {
            return;
        }

        // Only stop if all touches are gone
        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 });

    // Continuous visual updates
    setInterval(() => {
        if (isInGame()) {
            updateSnakeDot();
        }
    }, 50);


    // Monitor and force zoom value more frequently
    setInterval(() => {
        if (isInGame() && window.gsc !== undefined) {
            if (Math.abs(window.gsc - currentZoom) > 0.001) {
                window.gsc = currentZoom;
            }
        }
    }, 50);


    // Load screen buttons
    function loadScreenButtons() {
        const saved = localStorage.getItem('slitherScreenButtons');
        if (saved) {
            try {
                screenButtons = JSON.parse(saved);
            } catch (e) {
                console.error('Failed to load screen buttons:', e);
            }
        }
    }

    // Save screen buttons
    function saveScreenButtons() {
        localStorage.setItem('slitherScreenButtons', JSON.stringify(screenButtons));
    }

    // Get feature label for button
    function getFeatureLabel(featureId) {
        const labels = {
            'hideBoostGlow': 'ブースト光',
            'showGuideLine': 'ガイド',
            'showSnakeDot': '点',
            'showPointer': 'ポインター',
            'showSnakeCenterLine': '中心線',
            'noFoodAnimation': 'エサ揺れ',
            'noFoodWobble': 'エサアニメ',
            'instantDeadSnake': '死蛇消去',
            'instantEatFood': '食エサ消去',
            'simpleFoodGraphics': 'シンプルエサ'
        };
        return labels[featureId] || featureId;
    }

    // Add screen button for a feature
    function addScreenButtonForFeature(featureId) {
        // Check if button already exists
        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);

        // Update the screen buttons list immediately
        const screenButtonsEditContainer = document.getElementById('screenButtonsEditContainer');
        if (screenButtonsEditContainer) {
            screenButtonsEditContainer.style.display = 'block';
            updateScreenButtonsList();
        }
    }

    // Create a feature button on screen
    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);
    }

    // Update feature button color
    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')}`;
        }
    }

    // Toggle feature from button
    function toggleFeatureFromButton(featureId, button) {
        visualFeatures[featureId] = !visualFeatures[featureId];
        saveVisualFeatures();
        applyVisualFeatures();
        updateFeatureButtonColor(button, featureId);

        // Update main toggle if open
        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';
        }
    }

    // Delete feature button
    function deleteFeatureButton(buttonId) {
        screenButtons = screenButtons.filter(b => b.id !== buttonId);
        saveScreenButtons();
    }

    // Start dragging feature button
    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);
    }

    // Open feature button edit menu
    function openFeatureButtonEditMenu(buttonData, buttonElement) {
        // Create edit panel
        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);

        // Prevent touch events from propagating to game - use capture phase
        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 });

        // Add event listeners
        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();

        // Load and create screen buttons
        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();
    }

})();