Combat Mini-Game Bot

Автоматизация мини-игры на реакцию: определяет действие НПЦ и нажимает нужную кнопку

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Combat Mini-Game Bot
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Автоматизация мини-игры на реакцию: определяет действие НПЦ и нажимает нужную кнопку
// @match        https://www.crazygames.com/game/llama-legends/*
// @match        https://llamalegends.com/*
// @match        https://igre.games/en/llama-legends/*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    // ─── Параметры ───────────────────────────────────────────────────────────
    const POLL_MS             = 50;   // интервал опроса DOM
    const LOSE_TIMEOUT_MS     = 3000; // ждём проигрыша по таймауту на 51-м раунде
    const TRAIN_AGAIN_DELAY_MS = 2000; // пауза перед нажатием "Train Again"
    const MAX_SCORING_ROUNDS  = 50;   // раунды 1–50 приносят очки

    // ─── Машина состояний ────────────────────────────────────────────────────
    // Возможные состояния:
    //   'idle'          — бот не запущен
    //   'wait_npc'      — ждём появления действия НПЦ (фон ещё отсутствует)
    //   'wait_buttons'  — НПЦ обнаружен, ждём активации наших кнопок
    //   'wait_npc_gone' — кнопка нажата, ждём исчезновения фона НПЦ (смена раунда)
    //   'lose_round'    — 51-й раунд, намеренно ничего не нажимаем
    //   'wait_train'    — ждём появления кнопки "Train Again"
    let state       = 'idle';
    let roundCount  = 0;
    let npcAction   = null;  // действие НПЦ в текущем раунде ('red'/'blue'/'yellow')
    let pollInterval = null;


    function simulateClick(el) {
    if (!el) return;

    const rect = el.getBoundingClientRect();
    const x = rect.left + rect.width / 2;
    const y = rect.top + rect.height / 2;

    ['pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click'].forEach(type => {
        el.dispatchEvent(new MouseEvent(type, {
            view: window,
            bubbles: true,
            cancelable: true,
            clientX: x,
            clientY: y,
            button: 0
        }));
    });
}

    // ─── Определение действия НПЦ ────────────────────────────────────────────
    function detectNpcAction() {
        if (document.querySelector('.bg-red-500.blur-2xl'))    return 'red';
        if (document.querySelector('.bg-blue-500.blur-2xl'))   return 'blue';
        if (document.querySelector('.bg-yellow-500.blur-2xl')) return 'yellow';
        return null;
    }

    // ─── Поиск кнопки по тексту ──────────────────────────────────────────────
    function findButton(text) {
        return [...document.querySelectorAll('button')].find(
            b => b.textContent.trim() === text
        ) || null;
    }

    // ─── Проверка: кнопка существует и активна ───────────────────────────────
    function isEnabled(btn) {
        if (!btn) return false;
        if (btn.disabled) return false;
        if (window.getComputedStyle(btn).pointerEvents === 'none') return false;
        return true;
    }

    // ─── Выбор нужной кнопки по действию НПЦ ─────────────────────────────────
    // красный (атака НПЦ) → Block!
    // синий   (блок НПЦ)  → Attack!
    // жёлтый  (прыжок)    → Spit!
    function getResponseButton(action) {
        switch (action) {
            case 'red':    return findButton('Block!');
            case 'blue':   return findButton('Attack!');
            case 'yellow': return findButton('Spit!');
            default:       return null;
        }
    }

    // ─── Основной тик ────────────────────────────────────────────────────────
    function tick() {
        if (state === 'idle') return;

        switch (state) {

            // Ждём появления действия НПЦ
            case 'wait_npc': {
                const action = detectNpcAction();
                if (!action) return; // фона ещё нет — ждём

                roundCount++;
                npcAction = action;
                log(`Раунд ${roundCount} начался, НПЦ: ${action}`);

                // 51-й раунд — намеренно проигрываем
                if (roundCount > MAX_SCORING_ROUNDS) {
                    log(`Раунд ${roundCount} > ${MAX_SCORING_ROUNDS}, ждём таймаута (${LOSE_TIMEOUT_MS} мс)...`);
                    state = 'lose_round';
                    stopPoll();
                    setTimeout(() => {
                        if (state !== 'lose_round') return;
                        log('Раунд проигран по таймауту, ищем "Train Again"...');
                        state = 'wait_train';
                        startPoll();
                    }, LOSE_TIMEOUT_MS);
                    return;
                }

                // Нормальный раунд — ждём активации кнопок
                state = 'wait_buttons';
                return;
            }

            // Ждём, пока кнопка станет активной, и кликаем
            case 'wait_buttons': {
                const btn = getResponseButton(npcAction);
                if (!isEnabled(btn)) return; // кнопка ещё серая — ждём

                simulateClick(btn);
                log(`Раунд ${roundCount}: НПЦ — ${npcAction}, нажали "${btn.textContent.trim()}"`);
                state = 'wait_npc_gone';
                return;
            }

            // Ждём, пока фон НПЦ исчезнет — это сигнал о начале нового раунда
            case 'wait_npc_gone': {
                const action = detectNpcAction();
                if (action !== null) return; // фон ещё виден — ждём

                // Фон исчез: сервер принял ответ, сейчас пойдёт новый раунд
                npcAction = null;
                state = 'wait_npc';
                return;
            }

            // Ждём кнопку "Train Again"
            case 'wait_train': {
                const trainBtn = [...document.querySelectorAll('button')].find(
                    b => b.textContent.trim() === 'Train Again'
                );
                if (!trainBtn) return; // кнопка ещё не появилась

                log(`Найдена кнопка "Train Again", нажмём через ${TRAIN_AGAIN_DELAY_MS} мс...`);
                state = 'idle'; // временно гасим, чтобы повторно не сработало
                stopPoll();
                setTimeout(() => {
                    simulateClick(trainBtn);
                    log('Нажали "Train Again", начинаем новый цикл');
                    roundCount = 0;
                    npcAction  = null;
                    state      = 'wait_npc';
                    startPoll();
                }, TRAIN_AGAIN_DELAY_MS);
                return;
            }
        }
    }

    // ─── Управление интервалом ────────────────────────────────────────────────
    function startPoll() {
        if (pollInterval) return;
        pollInterval = setInterval(tick, POLL_MS);
    }

    function stopPoll() {
        if (pollInterval) {
            clearInterval(pollInterval);
            pollInterval = null;
        }
    }

    // ─── Старт и стоп ────────────────────────────────────────────────────────
    function startBot() {
        if (state !== 'idle') {
            log('Бот уже запущен!');
            return;
        }
        roundCount = 0;
        npcAction  = null;
        state      = 'wait_npc';
        showOverlay();
        log('Бот запущен. Alt+Shift+X — остановить.');
        startPoll();
    }

    function stopBot() {
        if (state === 'idle') return;
        state = 'idle';
        stopPoll();
        hideOverlay();
        log('Бот остановлен.');
    }

    // ─── Оверлей ─────────────────────────────────────────────────────────────
    let overlay = null;

    function showOverlay() {
        overlay = document.createElement('div');
        overlay.id = '__combat_bot_overlay';
        overlay.style.cssText = `
            position: fixed;
            top: 12px;
            right: 12px;
            z-index: 999999;
            background: rgba(15,15,20,0.82);
            color: #e2e8f0;
            font: 13px/1.6 monospace;
            padding: 8px 14px;
            border-radius: 8px;
            pointer-events: none;
            min-width: 180px;
            backdrop-filter: blur(4px);
            border: 1px solid rgba(255,255,255,0.1);
        `;
        overlay.innerHTML = `
            <b style="color:#6ee7b7">▶ BOT ACTIVE</b><br>
            Раунд: <span id="__bot_rounds">0</span> / ${MAX_SCORING_ROUNDS}<br>
            <span id="__bot_state" style="color:#94a3b8">wait_npc</span>
        `;
        document.body.appendChild(overlay);
    }

    function hideOverlay() {
        if (overlay) { overlay.remove(); overlay = null; }
    }

    function updateOverlay() {
        const r = document.getElementById('__bot_rounds');
        const s = document.getElementById('__bot_state');
        if (r) r.textContent = roundCount;
        if (s) s.textContent = state;
    }

    // ─── Логгер ───────────────────────────────────────────────────────────────
    function log(msg) {
        console.log(`[CombatBot] ${msg}`);
        updateOverlay();
    }

    // ─── Шорткаты ─────────────────────────────────────────────────────────────
    // Alt+Shift+F — запуск
    // Alt+Shift+X — остановка
    document.addEventListener('keydown', function (e) {
        if (e.altKey && e.shiftKey && e.code === 'KeyF') {
            e.preventDefault();
            startBot();
        }
        if (e.altKey && e.shiftKey && e.code === 'KeyX') {
            e.preventDefault();
            stopBot();
        }
    });

    console.log('[CombatBot] Скрипт загружен. Alt+Shift+F — старт, Alt+Shift+X — стоп.');
})();