Auto-battler with battle type hotkey (Normal/Inverse)
// ==UserScript==
// @name DelugeRPG: Auto-Battle
// @version v3.2
// @description Auto-battler with battle type hotkey (Normal/Inverse)
// @author Anxarden
// @namespace https://greasyfork.org/users/789978-anxarden
// @license MIT
// @match https://www.delugerpg.com/battle/trainer
// @match https://www.delugerpg.com/battle/trainer/*
// @match https://www.delugerpg.com/battle/user
// @match https://www.delugerpg.com/battle/user/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
const CFG = {
minDelay: 250,
maxDelay: 650,
rateLimitWait: 11500,
sessionMs: 15 * 60 * 1000,
breakMs: 3 * 60 * 1000,
observerThrottle: 200
};
let state = {
cooldown: false,
lastFire: 0,
sessionStart: Date.now(),
battleMode: 'normal' // 'normal' or 'inverse'
};
// ── LOAD/SAVE SETTINGS ──
function loadSettings() {
const saved = localStorage.getItem('deluge_battle_mode');
if (saved === 'normal' || saved === 'inverse') {
state.battleMode = saved;
}
}
function saveSettings() {
localStorage.setItem('deluge_battle_mode', state.battleMode);
}
// ── TOAST NOTIFICATION ──
function showToast(message, isError = false) {
const toast = document.createElement('div');
toast.textContent = message;
toast.style.position = 'fixed';
toast.style.bottom = '20px';
toast.style.right = '20px';
toast.style.backgroundColor = isError ? '#f44336' : '#4CAF50';
toast.style.color = 'white';
toast.style.padding = '8px 16px';
toast.style.borderRadius = '8px';
toast.style.zIndex = '9999';
toast.style.fontSize = '14px';
toast.style.fontWeight = 'bold';
toast.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)';
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 2000);
}
// ── HOTKEY TOGGLE ──
document.addEventListener('keydown', (e) => {
if (e.key.toLowerCase() === 'b') {
e.preventDefault();
state.battleMode = state.battleMode === 'normal' ? 'inverse' : 'normal';
saveSettings();
const modeName = state.battleMode === 'normal' ? 'Normal Battle' : 'Inverse Battle';
showToast(`Battle mode: ${modeName}`);
console.log(`[EB] Battle mode switched to ${state.battleMode}`);
// Immediately re-check for buttons
executeAction();
}
});
// ── SKEWED DELAY ──
function getSkewedDelay() {
const { minDelay: min, maxDelay: max } = CFG;
const range = max - min;
const r = Math.random();
if (r < 0.70) {
return min + Math.floor(Math.random() * range * 0.35);
}
if (r < 0.90) {
return min + Math.floor(range * 0.35) + Math.floor(Math.random() * range * 0.40);
}
return min + Math.floor(range * 0.75) + Math.floor(Math.random() * range * 0.25);
}
// ── CLICK ──
function clickElement(el) {
if (!el || el.disabled) return false;
el.click();
return true;
}
// ── FIND BUTTON BY TEXT ──
function findButtonByText(label) {
const lowerLabel = label.toLowerCase();
const buttons = document.querySelectorAll(`
button, a, input[type="submit"], input[type="button"],
.btn, .jconfirm-buttons button, .jconfirm button
`);
for (const btn of buttons) {
const text = (btn.innerText || btn.value || '').trim().toLowerCase();
if (text.includes(lowerLabel) && btn.offsetParent !== null && !btn.disabled) {
return btn;
}
}
return null;
}
// ── SLOT 1 MOVE ──
function getSlot1Move() {
const moveSelectors = [
'.move-button:first-child',
'.moves-list .move:first-child button',
'#battle-moves .move:first-child',
'.battle-moves button:first-of-type',
'.moves button:nth-child(1)'
];
for (const sel of moveSelectors) {
const el = document.querySelector(sel);
if (el?.offsetParent !== null && !el.disabled) return el;
}
const moveArea = document.querySelector('.moves, #moves, .battle-moves, .move-list');
if (moveArea) {
return [...moveArea.querySelectorAll('button, .move-button')].find(
b => b.offsetParent !== null && !b.disabled
);
}
return null;
}
// ── SESSION BREAK ──
function handleSessionBreak() {
if (Date.now() - state.sessionStart > CFG.sessionMs) {
console.log('[EB] Session break — 3 minutes pause.');
state.cooldown = true;
setTimeout(() => {
state.cooldown = false;
state.sessionStart = Date.now();
console.log('[EB] Break finished, resuming.');
executeAction();
}, CFG.breakMs);
return true;
}
return false;
}
// ── BLOCKERS (captcha, rate limit) ──
function isBlockedPage() {
const bodyText = document.body.innerText;
if (document.title.includes('Just a moment') || document.querySelector('#cf-wrapper')) {
console.warn('[EB] Cloudflare challenge – stopping.');
return true;
}
if (bodyText.includes('Security Check') ||
document.querySelector('img[src*="captcha"], .g-recaptcha')) {
console.warn('[EB] Captcha – stopping.');
return true;
}
if (/one battle every 10 seconds/i.test(bodyText)) {
const wait = CFG.rateLimitWait + Math.floor(Math.random() * 2000);
console.log(`[EB] Rate limit – waiting ${(wait / 1000).toFixed(1)}s`);
state.cooldown = true;
setTimeout(() => { state.cooldown = false; }, wait);
return true;
}
return false;
}
// ── DYNAMIC ACTIONS (respects battleMode) ──
function getActions() {
const baseActions = [
{ name: 'Start Battle', find: () => findButtonByText('Start Battle') },
{ name: 'Slot 1 Move', find: getSlot1Move },
{ name: 'Battle Again', find: () => findButtonByText('Battle Again') },
{ name: 'Same User Again', find: () => findButtonByText('Battle Same User Again') },
{ name: 'Continue', find: () => findButtonByText('Continue') },
{ name: 'Next', find: () => findButtonByText('Next') },
{ name: 'Attack', find: () => findButtonByText('Attack') }
];
// Insert the selected battle type at the beginning (highest priority)
if (state.battleMode === 'normal') {
return [
{ name: 'Normal Battle', find: () => findButtonByText('!Normal Battle') },
...baseActions
];
} else {
return [
{ name: 'Inverse Battle', find: () => findButtonByText('Inverse Battle') },
...baseActions
];
}
}
// ── MAIN ACTION ──
function executeAction() {
if (state.cooldown) return;
if (handleSessionBreak()) return;
if (isBlockedPage()) return;
const actions = getActions();
for (const action of actions) {
const btn = action.find();
if (btn) {
const delay = getSkewedDelay();
console.log(`[EB] → ${action.name} in ${delay}ms`);
setTimeout(() => clickElement(btn), delay);
return;
}
}
}
// ── OBSERVER ──
const observer = new MutationObserver(() => {
const now = Date.now();
if (now - state.lastFire >= CFG.observerThrottle) {
state.lastFire = now;
executeAction();
}
});
observer.observe(document.body, { childList: true, subtree: true });
// ── HISTORY SUPPORT ──
if (window.History && window.History.Adapter) {
window.History.Adapter.bind(window, 'statechange', () => {
setTimeout(executeAction, 500);
});
}
// ── INIT ──
loadSettings();
executeAction();
console.log(`[EB] Auto-Battle v3 online – mode: ${state.battleMode} (press B to toggle)`);
showToast(`Battle mode: ${state.battleMode === 'normal' ? 'Normal' : 'Inverse'} (press B to toggle)`, false);
})();