[hideprofile][html]<!-- ТЕХНО-ЗМЕЙКА (ХЁЛЬВАНИЯ) v.6 - JSONP РЕЙТИНГ -->
<div id="hollvania-snake">
    <div class="snake-header">
        <h2>⚡ TECHNO-CYTE SERPENT ⚡</h2>
        <div class="stats">
            <div>ЗАРАЖЕНИЕ: <span id="currentScore">0</span></div>
            <div>РЕКОРД: <span id="highScore">0</span></div>
        </div>
    </div>

    <canvas id="snakeCanvas" width="400" height="400"></canvas>

    <div class="button-panel">
        <button id="startBtn">🐍 СТАРТ ИГРЫ</button>
        <button id="gameOverBtn" disabled>💀 GAME OVER</button>
        <button id="resetBtn" disabled>⟳ НОВАЯ ИГРА</button>
    </div>

    <div id="leaderboardBlock">
        <h3>🏆 ПОСЛЕДНИЕ ОПЕРАТОРЫ 🏆</h3>
        <div id="rssLeaderboard">Загрузка рейтинга...</div>
        <div class="rssNote"><a href="https://warframe.f-rpg.me/export.php?type=rss&tid=30" target="_blank">📡 лента заражений</a></div>
    </div>
</div>

<style>
    #hollvania-snake {
        max-width: 500px;
        margin: 20px auto;
        background: #202020;
        border: 2px solid #4d6885;
        border-radius: 16px;
        padding: 20px;
        font-family: 'Courier New', monospace;
        text-align: center;
        box-shadow: 0 0 15px rgba(77,104,133,0.5);
    }
    .snake-header h2 {
        color: #ffc53a;
        text-shadow: 0 0 4px #ffc53a;
        margin: 0 0 10px;
    }
    .stats {
        display: flex;
        justify-content: space-between;
        background: #2a2a2a;
        padding: 8px 15px;
        border-radius: 8px;
        margin-bottom: 15px;
        color: #ddd;
    }
    .stats span {
        color: #ffc53a;
        font-weight: bold;
        font-size: 1.3rem;
    }
    canvas {
        background: #0a0a0e;
        border: 2px solid #4d6885;
        border-radius: 8px;
        box-shadow: 0 0 8px #4d6885;
    }
    .button-panel {
        margin-top: 15px;
        display: flex;
        gap: 10px;
        justify-content: center;
    }
    button {
        background: #2a2a2a;
        border: 1px solid #4d6885;
        color: #ffc53a;
        padding: 6px 15px;
        border-radius: 30px;
        cursor: pointer;
        font-family: inherit;
        font-weight: bold;
        transition: 0.2s;
    }
    button:hover:not(:disabled) {
        background: #584f67;
        border-color: #ffc53a;
        box-shadow: 0 0 6px #ffc53a;
    }
    button:disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }
    #leaderboardBlock {
        margin-top: 20px;
        border-top: 1px solid #584f67;
        padding-top: 12px;
    }
    #leaderboardBlock h3 {
        color: #ffc53a;
        font-size: 1rem;
        margin-bottom: 8px;
    }
    .rss-item {
        background: #1a1a1a;
        margin: 6px 0;
        padding: 6px 10px;
        border-left: 3px solid #ffc53a;
        text-align: left;
        font-size: 0.8rem;
        color: #ccc;
    }
    .rss-item strong {
        color: #ffc53a;
    }
    .rssNote {
        font-size: 0.7rem;
        margin-top: 8px;
    }
    .rssNote a {
        color: #4d6885;
        text-decoration: none;
    }
    .game-message {
        background: #2a2a2a;
        color: #ffc53a;
        padding: 4px 12px;
        border-radius: 20px;
        margin-top: 10px;
        font-size: 0.75rem;
        display: inline-block;
    }
</style>

<script>
    (function(){
        // ---------- ИГРА ----------
        const canvas = document.getElementById('snakeCanvas');
        const ctx = canvas.getContext('2d');
        const scoreSpan = document.getElementById('currentScore');
        const highScoreSpan = document.getElementById('highScore');
        const startBtn = document.getElementById('startBtn');
        const gameOverBtn = document.getElementById('gameOverBtn');
        const resetBtn = document.getElementById('resetBtn');

        const GRID = 20;
        const CELL = canvas.width / GRID;

        let snake = [
            {x: 10, y: 10},
            {x: 9, y: 10},
            {x: 8, y: 10},
            {x: 7, y: 10}
        ];
        let direction = 'RIGHT';
        let nextDir = 'RIGHT';
        let food = {x: 15, y: 10};
        let score = 0;
        let best = 0;
        let gameActive = false;
        let gameInterval = null;

        try {
            let saved = localStorage.getItem('snake_hollvania_best');
            if(saved && !isNaN(parseInt(saved))) best = parseInt(saved);
            highScoreSpan.innerText = best;
        } catch(e) {}

        function randomFood() {
            let free = false;
            let attempts = 0;
            let newFood = null;
            while(!free && attempts < 2000) {
                let fx = Math.floor(Math.random() * GRID);
                let fy = Math.floor(Math.random() * GRID);
                if(!snake.some(s => s.x === fx && s.y === fy)) {
                    newFood = {x: fx, y: fy};
                    free = true;
                }
                attempts++;
            }
            if(!newFood) {
                for(let i=0; i<GRID; i++) {
                    for(let j=0; j<GRID; j++) {
                        if(!snake.some(s => s.x === i && s.y === j)) {
                            newFood = {x: i, y: j};
                            break;
                        }
                    }
                    if(newFood) break;
                }
            }
            food = newFood;
        }

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.strokeStyle = '#4d6885';
            ctx.lineWidth = 0.5;
            for(let i=0; i<=GRID; i++) {
                ctx.beginPath();
                ctx.moveTo(i*CELL, 0);
                ctx.lineTo(i*CELL, canvas.height);
                ctx.stroke();
                ctx.moveTo(0, i*CELL);
                ctx.lineTo(canvas.width, i*CELL);
                ctx.stroke();
            }
            ctx.shadowBlur = 5;
            ctx.shadowColor = '#ffc53a';
            ctx.fillStyle = '#ffc53a';
            ctx.beginPath();
            ctx.arc(food.x*CELL + CELL/2, food.y*CELL + CELL/2, CELL/2-2, 0, Math.PI*2);
            ctx.fill();
            ctx.fillStyle = '#584f67';
            ctx.beginPath();
            ctx.arc(food.x*CELL + CELL/2, food.y*CELL + CELL/2, CELL/4, 0, Math.PI*2);
            ctx.fill();
            ctx.shadowBlur = 0;
            for(let i=0; i<snake.length; i++) {
                let s = snake[i];
                let grad = ctx.createLinearGradient(s.x*CELL, s.y*CELL, (s.x+1)*CELL, (s.y+1)*CELL);
                grad.addColorStop(0, '#4d6885');
                grad.addColorStop(1, '#2c3e55');
                ctx.fillStyle = grad;
                ctx.fillRect(s.x*CELL, s.y*CELL, CELL-1, CELL-1);
                if(i === 0) {
                    ctx.strokeStyle = '#ffc53a';
                    ctx.lineWidth = 2;
                    ctx.strokeRect(s.x*CELL, s.y*CELL, CELL-1, CELL-1);
                }
            }
            let head = snake[0];
            ctx.fillStyle = '#ff3333';
            ctx.beginPath();
            ctx.arc(head.x*CELL + CELL*0.7, head.y*CELL + CELL*0.3, 3, 0, Math.PI*2);
            ctx.arc(head.x*CELL + CELL*0.3, head.y*CELL + CELL*0.3, 3, 0, Math.PI*2);
            ctx.fill();
        }

        function updateScoreUI() {
            scoreSpan.innerText = score;
            if(score > best) {
                best = score;
                highScoreSpan.innerText = best;
                localStorage.setItem('snake_hollvania_best', best);
            }
        }

        function move() {
            if(!gameActive) return;
            direction = nextDir;
            let newHead = {...snake[0]};
            switch(direction) {
                case 'RIGHT': newHead.x++; break;
                case 'LEFT':  newHead.x--; break;
                case 'UP':    newHead.y--; break;
                case 'DOWN':  newHead.y++; break;
            }
            let willEat = (newHead.x === food.x && newHead.y === food.y);
            if(willEat) {
                snake.unshift(newHead);
                score++;
                updateScoreUI();
                randomFood();
                if(snake.length === GRID*GRID) { gameOver(true); return; }
            } else {
                snake.unshift(newHead);
                snake.pop();
            }
            let collision = (newHead.x<0 || newHead.x>=GRID || newHead.y<0 || newHead.y>=GRID);
            if(!willEat) collision = collision || snake.slice(1).some(s => s.x === newHead.x && s.y === newHead.y);
            if(collision) { gameOver(false); return; }
            draw();
        }

        function gameOver(win) {
            if(!gameActive) return;
            gameActive = false;
            if(gameInterval) clearInterval(gameInterval);
            gameInterval = null;
            let msg = document.createElement('div');
            msg.className = 'game-message';
            msg.innerText = win ? '☠️ ПОБЕДА? СБОЙ СИСТЕМЫ ☠️' : '☠️ ЗМЕЙ УНИЧТОЖЕН ☠️';
            document.querySelector('.button-panel').after(msg);
            setTimeout(() => msg.remove(), 3000);
            gameOverBtn.disabled = false;
            resetBtn.disabled = false;
            startBtn.disabled = false;
            window.lastSnakeScore = score;
        }

        function startGame() {
            if(gameInterval) clearInterval(gameInterval);
            snake = [
                {x: 10, y: 10},
                {x: 9, y: 10},
                {x: 8, y: 10},
                {x: 7, y: 10}
            ];
            direction = 'RIGHT';
            nextDir = 'RIGHT';
            score = 0;
            updateScoreUI();
            randomFood();
            gameActive = true;
            draw();
            gameInterval = setInterval(() => { move(); }, 150);
            startBtn.disabled = true;
            gameOverBtn.disabled = true;
            resetBtn.disabled = true;
            window.lastSnakeScore = 0;
        }

        function resetAndRestart() { startGame(); }

        async function sendScoreToForum(scoreVal) {
            if(scoreVal === undefined) { alert("Нет результата"); return; }
            const message = `🐍 TECHNO-CYTE SERPENT: ${scoreVal} очков`;
            const postKey = document.querySelector('input[name="my_post_key"]')?.value;
            if(!postKey) { alert("Не удалось отправить, скопируйте: "+message); return; }
            const tid = 30;
            const form = new URLSearchParams();
            form.append('my_post_key', postKey);
            form.append('tid', tid);
            form.append('message', message);
            form.append('action', 'do_newreply');
            try {
                let res = await fetch('/newreply.php?tid='+tid, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                    body: form,
                    credentials: 'same-origin'
                });
                if(res.ok) {
                    alert("Результат отправлен!");
                    loadLeaderboard();
                } else alert("Ошибка отправки");
            } catch(e) { alert("Ошибка сети"); }
        }

        // ---------- РЕЙТИНГ ЧЕРЕЗ JSONP (export.php?type=js) ----------
        function loadLeaderboard() {
            const container = document.getElementById('rssLeaderboard');
            if(!container) return;
            container.innerHTML = "Загрузка рейтинга...";
           
            // Удаляем старый скрипт
            const oldScript = document.getElementById('jsonp-script');
            if(oldScript) oldScript.remove();
           
            // Уникальное имя функции
            const callbackName = 'jsonp_callback_' + Date.now() + '_' + Math.floor(Math.random()*100000);
            window[callbackName] = function(data) {
                delete window[callbackName];
                if (!data || !Array.isArray(data) || data.length === 0) {
                    container.innerHTML = '<div class="rss-item">Нет сообщений</div>';
                    return;
                }
                let html = '';
                let count = 0;
                // Выводим последние 10 сообщений (первые в массиве — самые новые)
                for (let i = 0; i < data.length && count < 10; i++) {
                    const item = data[i];
                    // Поля: author, subject, message, dateline (Unix timestamp)
                    const author = item.author || 'Аноним';
                    let title = item.subject || item.message;
                    if (title && title.length > 80) title = title.substring(0, 80) + '…';
                    const date = item.dateline ? new Date(item.dateline * 1000).toLocaleString() : '';
                    html += `<div class="rss-item"><strong>${escapeHtml(author)}</strong><br>${escapeHtml(title)}<br><small>${escapeHtml(date)}</small></div>`;
                    count++;
                }
                container.innerHTML = html;
            };
           
            const script = document.createElement('script');
            script.id = 'jsonp-script';
            script.src = `https://warframe.f-rpg.me/export.php?type=js&tid=30&callback=${callbackName}`;
            script.onerror = function() {
                delete window[callbackName];
                container.innerHTML = '<div class="rss-item">Ошибка загрузки рейтинга</div>';
            };
            document.head.appendChild(script);
        }

        function escapeHtml(str) {
            if(!str) return '';
            return str.replace(/[&<>]/g, m => ({ '&':'&amp;', '<':'&lt;', '>':'&gt;' }[m]));
        }

        window.addEventListener('load', () => {
            loadLeaderboard();
            setInterval(loadLeaderboard, 30000);
            startBtn.addEventListener('click', startGame);
            gameOverBtn.addEventListener('click', () => {
                if(gameActive) {
                    gameActive = false;
                    if(gameInterval) clearInterval(gameInterval);
                    gameOver(false);
                } else if(window.lastSnakeScore > 0) {
                    sendScoreToForum(window.lastSnakeScore);
                } else {
                    alert("Сыграйте сначала");
                }
            });
            resetBtn.addEventListener('click', resetAndRestart);
            window.addEventListener('keydown', (e) => {
                if(!gameActive) return;
                let k = e.key;
                if(k === 'ArrowUp' && direction !== 'DOWN') nextDir = 'UP';
                else if(k === 'ArrowDown' && direction !== 'UP') nextDir = 'DOWN';
                else if(k === 'ArrowLeft' && direction !== 'RIGHT') nextDir = 'LEFT';
                else if(k === 'ArrowRight' && direction !== 'LEFT') nextDir = 'RIGHT';
                e.preventDefault();
            });
            draw();
        });
    })();
</script>[/html]