Ultra Snake: Customization & Effects

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

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

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

})();