Ultra Snake: Customization & Effects

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

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

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

(У мене вже є менеджер скриптів, дайте мені встановити його!)

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

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

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

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

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

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

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

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

})();