Ultra Snake: Customization & Effects

Змейка с частицами, скинами и ежедневными квестами

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Ultra Snake: Customization & Effects
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Змейка с частицами, скинами и ежедневными квестами
// @author       AI
// @license MIT
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const GRID_SIZE = 20;
    const TILE_COUNT = 20;

    // Стили
    const style = document.createElement('style');
    style.innerHTML = `
        #snake-game-launcher {
            position: fixed; bottom: 20px; right: 20px; width: 60px; height: 60px;
            background: linear-gradient(135deg, #00ff88, #00bdff);
            border-radius: 50%; cursor: pointer; box-shadow: 0 4px 15px rgba(0,0,0,0.3);
            display: flex; align-items: center; justify-content: center; z-index: 999999;
            transition: transform 0.3s;
        }
        #snake-game-launcher:hover { transform: scale(1.1); }

        #snake-game-container {
            position: fixed; bottom: 90px; right: 20px; width: 400px; height: 520px;
            background: #121212; border-radius: 20px; box-shadow: 0 15px 50px rgba(0,0,0,0.6);
            display: none; flex-direction: column; overflow: hidden; z-index: 999999;
            font-family: 'Segoe UI', sans-serif; border: 1px solid #333; color: white;
        }

        #snake-header {
            padding: 15px; background: #1a1a1a; display: flex;
            justify-content: space-between; align-items: center; border-bottom: 1px solid #333;
        }

        .snake-screen {
            flex: 1; display: none; flex-direction: column; align-items: center;
            justify-content: center; padding: 20px; text-align: center; overflow-y: auto;
        }
        .snake-screen.active { display: flex; }

        .snake-btn {
            background: linear-gradient(135deg, #00ff88, #00bdff); border: none;
            padding: 12px 25px; border-radius: 12px; color: #111; font-weight: bold;
            cursor: pointer; margin: 6px; transition: all 0.2s; min-width: 160px;
            text-transform: uppercase; font-size: 12px;
        }
        .snake-btn:hover { filter: brightness(1.2); transform: translateY(-1px); }
        .snake-btn:disabled { background: #444; color: #888; cursor: not-allowed; }

        .skin-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 15px; }
        .skin-item {
            background: #222; padding: 10px; border-radius: 10px; border: 2px solid transparent;
            cursor: pointer; transition: 0.2s;
        }
        .skin-item.active { border-color: #00ff88; background: #2a2a2a; }
        .skin-preview { width: 30px; height: 30px; border-radius: 5px; margin: 5px auto; }

        .task-card {
            background: #1a1a1a; width: 90%; padding: 10px; border-radius: 10px;
            margin: 5px 0; border-left: 4px solid #00ff88; text-align: left;
        }
        .task-progress { font-size: 12px; color: #00ff88; }

        #snake-canvas-container { position: relative; width: 400px; height: 400px; display: none; }
        canvas { display: block; background-color: #0a0a0a; }

        #snake-ui-overlay {
            position: absolute; top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0,0,0,0.9); display: none; flex-direction: column;
            align-items: center; justify-content: center;
        }
    `;
    document.head.appendChild(style);

    // HTML Структура
    const container = document.createElement('div');
    container.id = 'snake-game-container';
    container.innerHTML = `
        <div id="snake-header">
            <span id="user-display">Snake Elite</span>
            <div id="snake-score-display" style="display:none; color: #00ff88; font-weight:bold;">0</div>
            <span class="close-snake" style="cursor:pointer; font-size:20px;">×</span>
        </div>

        <div id="screen-profile" class="snake-screen">
            <h2>Создание профиля</h2>
            <input type="text" id="snake-nick-input" class="snake-btn" style="background:#222; color:white; text-transform:none;" placeholder="Никнейм..." maxlength="12">
            <button class="snake-btn" id="btn-save-profile">Начать приключение</button>
        </div>

        <div id="screen-menu" class="snake-screen">
            <h1 id="welcome-msg">Привет!</h1>
            <button class="snake-btn" id="btn-start-game">Играть</button>
            <button class="snake-btn" id="btn-show-skins">🎨 Скины</button>
            <button class="snake-btn" id="btn-show-achievements">🏆 Достижения</button>
        </div>

        <div id="screen-skins" class="snake-screen">
            <h2>Выберите стиль</h2>
            <div class="skin-grid" id="skin-list"></div>
            <button class="snake-btn" id="btn-back-menu-skins">Назад</button>
        </div>

        <div id="screen-achievements" class="snake-screen">
            <h2>Ваш прогресс</h2>
            <div id="achievements-data" style="margin-bottom:15px;"></div>
            <h3>Ежедневные задачи</h3>
            <div id="tasks-list"></div>
            <button class="snake-btn" id="btn-back-menu-ach">Назад</button>
        </div>

        <div id="snake-canvas-container">
            <canvas id="snakeCanvas" width="400" height="400"></canvas>
            <div id="snake-ui-overlay">
                <h2 id="snake-result-title">Игра окончена</h2>
                <p id="snake-final-score">Счет: 0</p>
                <button class="snake-btn" id="snake-restart">Снова</button>
                <button class="snake-btn" id="snake-to-menu">В меню</button>
            </div>
        </div>
    `;
    document.body.appendChild(container);

    // Параметры игры и частицы
    const canvas = document.getElementById('snakeCanvas');
    const ctx = canvas.getContext('2d');
    let snake = [], food = {}, dx = 1, dy = 0, score = 0, gameInterval = null;
    let particles = [];

    const SKINS = [
        { id: 'classic', name: 'Неон', color: '#00ff88', req: 0 },
        { id: 'gold', name: 'Золото', color: '#ffd700', req: 150 },
        { id: 'plasma', name: 'Плазма', color: '#ff00ff', req: 300 },
        { id: 'rainbow', name: 'Радуга', color: 'rainbow', req: 500 }
    ];

    const Storage = {
        get: (key, def) => JSON.parse(localStorage.getItem(key) || JSON.stringify(def)),
        set: (key, val) => localStorage.setItem(key, JSON.stringify(val)),
        updateTasks: (scoreInc) => {
            let data = Storage.get('snake_tasks', { date: new Date().toDateString(), eaten: 0, games: 0 });
            if (data.date !== new Date().toDateString()) {
                data = { date: new Date().toDateString(), eaten: 0, games: 0 };
            }
            if (scoreInc > 0) data.eaten++;
            Storage.set('snake_tasks', data);
        }
    };

    function createParticles(x, y, color) {
        for (let i = 0; i < 12; i++) {
            particles.push({
                x: x * GRID_SIZE + 10,
                y: y * GRID_SIZE + 10,
                vx: (Math.random() - 0.5) * 8,
                vy: (Math.random() - 0.5) * 8,
                life: 1.0,
                color: color
            });
        }
    }

    function drawParticles() {
        particles.forEach((p, i) => {
            p.x += p.vx; p.y += p.vy;
            p.life -= 0.05;
            if (p.life <= 0) particles.splice(i, 1);
            else {
                ctx.fillStyle = p.color;
                ctx.globalAlpha = p.life;
                ctx.fillRect(p.x, p.y, 4, 4);
            }
        });
        ctx.globalAlpha = 1.0;
    }

    function render() {
        ctx.fillStyle = '#0a0a0a';
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        drawParticles();

        const currentSkinId = Storage.get('snake_current_skin', 'classic');
        const skin = SKINS.find(s => s.id === currentSkinId);

        // Еда
        ctx.fillStyle = '#ff4444';
        ctx.shadowBlur = 15; ctx.shadowColor = '#ff4444';
        ctx.beginPath();
        ctx.arc(food.x * GRID_SIZE + 10, food.y * GRID_SIZE + 10, 7, 0, Math.PI * 2);
        ctx.fill();
        ctx.shadowBlur = 0;

        // Змейка
        snake.forEach((seg, i) => {
            let color = skin.color;
            if (color === 'rainbow') color = `hsl(${(Date.now() / 10 + i * 20) % 360}, 100%, 50%)`;
            
            ctx.fillStyle = color;
            if (i === 0) {
                ctx.shadowBlur = 10; ctx.shadowColor = color;
            }
            ctx.beginPath();
            ctx.roundRect(seg.x * GRID_SIZE + 1, seg.y * GRID_SIZE + 1, 18, 18, 4);
            ctx.fill();
            ctx.shadowBlur = 0;
        });
    }

    function initGame() {
        showScreen('game');
        snake = [{x: 10, y: 10}, {x: 9, y: 10}];
        generateFood();
        dx = 1; dy = 0; score = 0;
        document.getElementById('snake-score-display').innerText = 0;
        document.getElementById('snake-ui-overlay').style.display = 'none';
        
        let tasks = Storage.get('snake_tasks', { date: new Date().toDateString(), eaten: 0, games: 0 });
        tasks.games++;
        Storage.set('snake_tasks', tasks);

        if (gameInterval) clearInterval(gameInterval);
        gameInterval = setInterval(() => {
            move();
            render();
        }, 100);
    }

    function move() {
        const head = { x: snake[0].x + dx, y: snake[0].y + dy };
        
        if (head.x < 0 || head.x >= TILE_COUNT || head.y < 0 || head.y >= TILE_COUNT ||
            snake.some(s => s.x === head.x && s.y === head.y)) {
            return gameOver();
        }

        snake.unshift(head);
        if (head.x === food.x && head.y === food.y) {
            score += 10;
            document.getElementById('snake-score-display').innerText = score;
            const currentSkin = SKINS.find(s => s.id === Storage.get('snake_current_skin', 'classic'));
            createParticles(food.x, food.y, currentSkin.color === 'rainbow' ? '#fff' : currentSkin.color);
            Storage.updateTasks(10);
            generateFood();
        } else {
            snake.pop();
        }
    }

    function generateFood() {
        food = { x: Math.floor(Math.random() * TILE_COUNT), y: Math.floor(Math.random() * TILE_COUNT) };
        if (snake.some(s => s.x === food.x && s.y === food.y)) generateFood();
    }

    function gameOver() {
        clearInterval(gameInterval);
        const highscore = Storage.get('snake_highscore', 0);
        if (score > highscore) Storage.set('snake_highscore', score);
        document.getElementById('snake-final-score').innerText = `Счет: ${score}`;
        document.getElementById('snake-ui-overlay').style.display = 'flex';
    }

    function showScreen(id) {
        document.querySelectorAll('.snake-screen').forEach(s => s.classList.remove('active'));
        const canvasCont = document.getElementById('snake-canvas-container');
        const scoreDisp = document.getElementById('snake-score-display');
        
        canvasCont.style.display = id === 'game' ? 'block' : 'none';
        scoreDisp.style.display = id === 'game' ? 'block' : 'none';
        if (id !== 'game') document.getElementById(id).classList.add('active');
    }

    // Обработка скинов
    function updateSkinList() {
        const list = document.getElementById('skin-list');
        const highscore = Storage.get('snake_highscore', 0);
        const current = Storage.get('snake_current_skin', 'classic');
        
        list.innerHTML = SKINS.map(s => {
            const locked = highscore < s.req;
            return `
                <div class="skin-item ${current === s.id ? 'active' : ''}" data-id="${s.id}" ${locked ? 'style="opacity:0.5"' : ''}>
                    <div class="skin-preview" style="background:${s.color === 'rainbow' ? 'linear-gradient(to right, red, orange, yellow, green, blue, violet)' : s.color}"></div>
                    <div style="font-size:12px">${s.name}</div>
                    <div style="font-size:10px; color:${locked ? '#ff4444' : '#00ff88'}">${locked ? 'Рекорд ' + s.req : 'Открыто'}</div>
                </div>
            `;
        }).join('');

        document.querySelectorAll('.skin-item').forEach(el => {
            el.onclick = () => {
                const s = SKINS.find(x => x.id === el.dataset.id);
                if (highscore >= s.req) {
                    Storage.set('snake_current_skin', s.id);
                    updateSkinList();
                }
            };
        });
    }

    // Инициализация интерфейса
    document.getElementById('snake-game-launcher').onclick = () => {
        const cont = document.getElementById('snake-game-container');
        cont.style.display = cont.style.display === 'flex' ? 'none' : 'flex';
        const nick = Storage.get('snake_nick', null);
        if (!nick) showScreen('screen-profile');
        else {
            document.getElementById('welcome-msg').innerText = `Привет, ${nick}!`;
            showScreen('screen-menu');
        }
    };

    document.getElementById('btn-save-profile').onclick = () => {
        const val = document.getElementById('snake-nick-input').value.trim();
        if (val) { Storage.set('snake_nick', val); showScreen('screen-menu'); }
    };

    document.getElementById('btn-start-game').onclick = initGame;
    document.getElementById('snake-restart').onclick = initGame;
    document.getElementById('btn-show-skins').onclick = () => { showScreen('screen-skins'); updateSkinList(); };
    document.getElementById('btn-back-menu-skins').onclick = () => showScreen('screen-menu');
    document.getElementById('btn-back-menu-ach').onclick = () => showScreen('screen-menu');
    document.getElementById('snake-to-menu').onclick = () => showScreen('screen-menu');
    document.querySelector('.close-snake').onclick = () => {
        document.getElementById('snake-game-container').style.display = 'none';
        clearInterval(gameInterval);
    };

    document.getElementById('btn-show-achievements').onclick = () => {
        const highscore = Storage.get('snake_highscore', 0);
        const tasks = Storage.get('snake_tasks', { eaten: 0, games: 0 });
        
        document.getElementById('achievements-data').innerHTML = `Рекорд: <b style="color:#00ff88">${highscore}</b>`;
        document.getElementById('tasks-list').innerHTML = `
            <div class="task-card">
                <div>Собрать 20 еды за день</div>
                <div class="task-progress">${Math.min(tasks.eaten, 20)} / 20</div>
            </div>
            <div class="task-card" style="border-color: #00bdff">
                <div>Сыграть 5 раз</div>
                <div class="task-progress">${Math.min(tasks.games, 5)} / 5</div>
            </div>
        `;
        showScreen('screen-achievements');
    };

    // Управление
    window.addEventListener('keydown', (e) => {
        const keys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'KeyW', 'KeyS', 'KeyA', 'KeyD'];
        if (keys.includes(e.code) && document.getElementById('snake-canvas-container').style.display === 'block') {
            e.preventDefault();
            switch(e.code) {
                case 'ArrowUp': case 'KeyW': if (dy !== 1) { dx = 0; dy = -1; } break;
                case 'ArrowDown': case 'KeyS': if (dy !== -1) { dx = 0; dy = 1; } break;
                case 'ArrowLeft': case 'KeyA': if (dx !== 1) { dx = -1; dy = 0; } break;
                case 'ArrowRight': case 'KeyD': if (dx !== -1) { dx = 1; dy = 0; } break;
            }
        }
    });

    const launcher = document.createElement('div');
    launcher.id = 'snake-game-launcher';
    launcher.innerHTML = `<svg viewBox="0 0 24 24" style="width:30px; fill:white;"><path d="M7,5V9H17V5H7M7,11V15H17V11H7M7,17V21H17V17H7Z" /></svg>`;
    document.body.appendChild(launcher);

})();