Outdated Idle MMO Battle Automation with GUI and Logs

Automatically clicks "Start Hunt" and "Battle Max" based on game conditions. Includes a GUI for toggling settings, viewing logs, and copying the log, with customizable cooldown and randomization to prevent bans.

目前為 2024-08-26 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Outdated Idle MMO Battle Automation with GUI and Logs
// @namespace     https://web.idle-mmo.com/
// @version       1.2
// @description  Automatically clicks "Start Hunt" and "Battle Max" based on game conditions. Includes a GUI for toggling settings, viewing logs, and copying the log, with customizable cooldown and randomization to prevent bans.
// @author       Unknown Monkey
// @match        https://web.idle-mmo.com/battle
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // Settings
    let autoBattleEnabled = true;
    let autoHuntEnabled = true;
    let cooldownMin = 2000; // Minimum cooldown (milliseconds)
    let cooldownMax = 5000; // Maximum cooldown (milliseconds)
    let randomizeCooldown = true;

    // Function to click buttons by XPath (enhanced with direct click method)
    function clickButtonByXPath(xpath) {
        setTimeout(() => {
            const button = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
            if (button) {
                // Check button state before clicking
                const isButtonDisabled = button.hasAttribute('disabled') || button.classList.contains('disabled');
                logMessage(`Button found with XPath: ${xpath}`);
                logMessage(`Button disabled state: ${isButtonDisabled}`);

                if (!isButtonDisabled) {
                    try {
                        button.click(); // Use click() directly
                        logMessage(`Clicked button with XPath: ${xpath}`);
                    } catch (error) {
                        logMessage(`Error clicking button with XPath: ${xpath} - ${error.message}`);
                    }
                } else {
                    logMessage(`Button with XPath: ${xpath} is disabled`);
                }
            } else {
                logMessage(`Button with XPath: ${xpath} not found`);
            }
        }, randomizeCooldown ? getRandomCooldown() : cooldownMin);
    }

    // Function to get the value of an element by CSS selector
    function getValueBySelector(selector) {
        const element = document.querySelector(selector);
        return element ? element.textContent.trim() : null;
    }

    // Function to get a random cooldown value within the specified range
    function getRandomCooldown() {
        return Math.floor(Math.random() * (cooldownMax - cooldownMin + 1)) + cooldownMin;
    }

    // Function to check if an element exists by XPath
    function elementExistsByXPath(xpath) {
        const element = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        return element !== null;
    }

    // Function to check for the "Start Hunt" button
    function checkForStartHunt() {
        const startHuntXPath = '/html/body/div[1]/main/div[1]/div/div[2]/div[2]/div/div[2]/div[1]/div[1]/div[2]/div/div/div/div[1]/button';
        return elementExistsByXPath(startHuntXPath);
    }

    // Logging function
    function logMessage(message) {
        const logArea = document.getElementById('logArea');
        const timestamp = new Date().toLocaleTimeString();
        logArea.value += `[${timestamp}] ${message}\n`;
        logArea.scrollTop = logArea.scrollHeight;
    }

    // Automation logic (removed "Hunt Again" and only focuses on "Start Hunt" and "Battle Max")
    function automateBattle() {
        if (!autoBattleEnabled) return;

        // Updated XPaths and CSS selectors
        const battleMaxXPath = '/html/body/div[1]/main/div[1]/div/div[2]/div[2]/div/div[2]/div[1]/div[1]/div[2]/div/div[2]/div[1]/button';
        const battleMaxValueSelector = 'div.w-5'; // CSS selector for Battle Max Value
        const startHuntXPath = '/html/body/div[1]/main/div[1]/div/div[2]/div[2]/div/div[2]/div[1]/div[1]/div[2]/div/div/div/div[1]/button';
        const enemiesXPath = '/html/body/div[1]/main/div[1]/div/div[2]/div[2]/div/div[2]/div[1]/div[1]/div[2]/div';

        setTimeout(() => {
            // Re-evaluate the XPath to handle potential stale element reference
            const battleMaxButton = document.evaluate(battleMaxXPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
            const battleMaxValue = getValueBySelector(battleMaxValueSelector);
            const startHuntButton = document.evaluate(startHuntXPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;

            // Check if there are any enemy buttons within the enemiesXPath container
            const enemiesContainer = document.evaluate(enemiesXPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
            const enemyButtons = enemiesContainer ? enemiesContainer.querySelectorAll('button') : [];
            const enemiesExist = enemyButtons.length > 0;

            // Enhanced logging for debugging
            console.log("autoBattleEnabled:", autoBattleEnabled);
            console.log("battleMaxButton:", battleMaxButton);
            console.log("battleMaxButton.disabled:", battleMaxButton ? battleMaxButton.disabled : "null");
            console.log("battleMaxValue:", battleMaxValue);
            console.log("parseInt(battleMaxValue) > 0:", parseInt(battleMaxValue) > 0);
            console.log("startHuntButton:", startHuntButton);

            logMessage(`Enemies Exist: ${enemiesExist}`);
            logMessage(`Battle Max Value: ${battleMaxValue}`);

            // Prioritize "Start Hunt", then "Battle Max"
            if (autoBattleEnabled && startHuntButton) {
                logMessage(`Attempting to click "Start Hunt"`);
                clickButtonByXPath(startHuntXPath);
            } else if (autoBattleEnabled &&
                       battleMaxButton &&
                       !battleMaxButton.disabled &&
                       battleMaxValue !== null &&
                       parseInt(battleMaxValue) > 0) {
                logMessage(`Attempting to click "Battle Max"`);
                clickButtonByXPath(battleMaxXPath);
            } else {
                // Log specific reasons for not clicking
                if (!autoBattleEnabled) logMessage("Auto Battle is disabled.");
                if (!battleMaxButton) logMessage("Battle Max button not found.");
                if (battleMaxButton && battleMaxButton.disabled) logMessage("Battle Max button is disabled.");
                if (battleMaxValue === null) logMessage("Battle Max value is null.");
                if (!(parseInt(battleMaxValue) > 0)) logMessage("Battle Max value is not greater than 0.");
                if (!startHuntButton) logMessage("Start Hunt button not found.");
            }
        }, 500);
    }

    // Set up an interval for automation
    setInterval(automateBattle, randomizeCooldown ? getRandomCooldown() : cooldownMin);

    // GUI creation with a toggle button
    function createGUI() {
        // Create GUI container
        const gui = document.createElement('div');
        gui.id = 'battle-automation-gui';
        gui.innerHTML = `
            <div>
                <h3>Battle Automation</h3>
                <label><input type="checkbox" id="toggleAutoBattle" checked> Auto Battle</label><br>
                <label for="cooldownMin">Cooldown Min (ms):</label>
                <input type="number" id="cooldownMin" value="${cooldownMin}" min="0"><br>
                <label for="cooldownMax">Cooldown Max (ms):</label>
                <input type="number" id="cooldownMax" value="${cooldownMax}" min="0"><br>
                <label><input type="checkbox" id="toggleRandomizeCooldown" checked> Randomize Cooldown</label><br>
                <textarea id="logArea" rows="10" cols="40" readonly></textarea><br>
                <button id="copyLogButton">Copy Log</button><br>
            </div>
        `;
        document.body.appendChild(gui);

        // Create Show/Hide GUI button
        const toggleGUIButton = document.createElement('button');
        toggleGUIButton.id = 'toggleGUIButton';
        toggleGUIButton.textContent = 'Hide GUI';
        toggleGUIButton.style.position = 'fixed';
        toggleGUIButton.style.top = '10px';
        toggleGUIButton.style.right = '10px';
        toggleGUIButton.style.zIndex = '1001'; // Above GUI
        document.body.appendChild(toggleGUIButton);

        // Event listeners for toggling settings
        document.getElementById('toggleAutoBattle').addEventListener('change', (e) => {
            autoBattleEnabled = e.target.checked;
            logMessage(`Auto Battle: ${autoBattleEnabled}`);
        });

        // Event listeners for cooldown settings
        document.getElementById('cooldownMin').addEventListener('change', (e) => {
            cooldownMin = parseInt(e.target.value, 10);
            logMessage(`Cooldown Min set to: ${cooldownMin}`);
        });
        document.getElementById('cooldownMax').addEventListener('change', (e) => {
            cooldownMax = parseInt(e.target.value, 10);
            logMessage(`Cooldown Max set to: ${cooldownMax}`);
        });

        // Event listeners for randomizing cooldown
        document.getElementById('toggleRandomizeCooldown').addEventListener('change', (e) => {
            randomizeCooldown = e.target.checked;
            logMessage(`Randomize Cooldown: ${randomizeCooldown}`);
        });

        // Event listener for copying logs
        document.getElementById('copyLogButton').addEventListener('click', () => {
            const logArea = document.getElementById('logArea');
            logArea.select();
            document.execCommand('copy');
            logMessage('Log copied to clipboard.');
        });

        // Event listener for hiding/showing GUI
        document.getElementById('toggleGUIButton').addEventListener('click', () => {
            const gui = document.getElementById('battle-automation-gui');
            if (gui.style.opacity === '0') {
                gui.style.opacity = '1';
                document.getElementById('toggleGUIButton').textContent = 'Hide GUI';
            } else {
                gui.style.opacity = '0';
                document.getElementById('toggleGUIButton').textContent = 'Show GUI';
            }
        });

        // Inject CSS for GUI
        GM_addStyle(`
            #battle-automation-gui {
                position: fixed;
                top: 10px;
                right: 10px;
                background: rgba(255, 255, 255, 0.5); /* Semi-transparent background */
                border: 1px solid #ccc;
                padding: 10px;
                z-index: 1000;
                font-family: Arial, sans-serif;
                opacity: 1; /* Start with GUI visible */
                transition: opacity 0.3s ease; /* Smooth transition for hiding/showing */
            }
            #battle-automation-gui h3 {
                margin: 0;
                font-size: 16px;
                color: black;
            }
            #battle-automation-gui label,
            #battle-automation-gui input,
            #battle-automation-gui textarea,
            #battle-automation-gui button {
                font-size: 14px;
                color: black;
            }
            #logArea {
                color: red; /* Red text for the log area */
            }
        `);
    }

    // Create GUI on page load
    window.addEventListener('load', createGUI);
})();