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 — стоп.');
})();