Greasy Fork is available in English.

Ultra Snake: Customization & Effects

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

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

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

})();