// ==UserScript==
// @name Idle MMO Battle Automation with GUI and Logs
// @namespace https://web.idle-mmo.com/
// @version 1.1
// @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 Hidden
// @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);
})();