DelugeRPG: Auto-Walker

Automatically walks on the DelugeRPG map with randomized human-like movement, detection pause, and safety features.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

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

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         DelugeRPG: Auto-Walker
// @namespace    https://greasyfork.org/users/Codex9
// @version      1
// @description  Automatically walks on the DelugeRPG map with randomized human-like movement, detection pause, and safety features.
// @author       Codex9
// @license      MIT
// @match        https://m.delugerpg.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // ═══════════════════════════════════════════════════════════════
    // TARGETS — edit to customise
    // ═══════════════════════════════════════════════════════════════
    const TARGET_POKEMON = [
        'Meowth', 'Munchlax', 'Eevee', 'Snorlax', 'Charmander', 'Kangaskhan'
    ];
    const SHINY_TYPES = [
        'Dark', 'Chrome', 'Retro', 'Shiny'
    ];
    const ALWAYS_STOP = [
        'Mythical', 'Legendary'
    ];
    // ═══════════════════════════════════════════════════════════════

    const CFG = {
        moveMin: 850,
        moveMax: 1600,
        boundTiles: 4,
        softZone: 2,
        distractChance: 0.012,
        sessionMs: 20 * 60 * 1000,
        breakMs: 4 * 60 * 1000,
        heartbeatMs: 3000,
        startDelay: 1800
    };

    const CARDINALS = [
        { dir: 'n', dx: 0, dy: -1 },
        { dir: 's', dx: 0, dy: 1 },
        { dir: 'e', dx: 1, dy: 0 },
        { dir: 'w', dx: -1, dy: 0 }
    ];

    const DIAGONALS = [
        { dir: 'ne', dx: 1, dy: -1 },
        { dir: 'nw', dx: -1, dy: -1 },
        { dir: 'se', dx: 1, dy: 1 },
        { dir: 'sw', dx: -1, dy: 1 }
    ];

    const ALL_MOVES = [...CARDINALS, ...DIAGONALS];

    // ── STATE ─────────────────────────────────────────────────────────
    const S = {
        x: 0,
        y: 0,
        active: false,
        paused: false,
        waiting: false,
        sessionStart: Date.now(),
        lastMove: Date.now(),
        lastFire: 0,
        moves: 0,
        lastURL: location.href,
        observer: null,
        observedEl: null,
        heartbeat: null
    };

    // ── SKEWED DELAY ──────────────────────────────────────────────────
    function skew(min, max) {
        const r = Math.random();
        if (r < 0.70) {
            return Math.floor(Math.random() * (max - min) * 0.35) + min;
        }
        if (r < 0.90) {
            return Math.floor(Math.random() * (max - min) * 0.40) + min + Math.floor((max - min) * 0.35);
        }
        return Math.floor(Math.random() * (max - min) * 0.25) + min + Math.floor((max - min) * 0.75);
    }

    // ── BOX-MULLER GAUSSIAN ───────────────────────────────────────────
    // Kept for walker — direction buttons are small, center bias prevents misses
    function gaussian() {
        let u = 0, v = 0;
        while (u === 0) u = Math.random();
        while (v === 0) v = Math.random();
        const n = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
        return Math.min(0.85, Math.max(0.15, 0.5 + n * 0.15));
    }

    // ── PHANTOM CLICK ─────────────────────────────────────────────────
    function phantomClick(el) {
        if (!el) return false;
        const rect = el.getBoundingClientRect();
        const cx = rect.left + rect.width * gaussian();
        const cy = rect.top + rect.height * gaussian();
        const base = { bubbles: true, view: window, clientX: cx, clientY: cy };

        el.dispatchEvent(new PointerEvent('pointerdown', base));
        el.dispatchEvent(new MouseEvent('mousedown', base));

        setTimeout(() => {
            el.dispatchEvent(new MouseEvent('mouseup', base));
            el.dispatchEvent(new PointerEvent('pointerup', base));
            el.click();
        }, 40 + Math.floor(Math.random() * 60));

        return true;
    }

    // ── RUBBER BAND MOVE PICKER ───────────────────────────────────────
    function pickMove() {
        const dist = Math.max(Math.abs(S.x), Math.abs(S.y));

        // Hard wall — only inward moves allowed at boundary
        if (dist >= CFG.boundTiles) {
            const inward = ALL_MOVES.filter(m => {
                const nx = S.x + m.dx;
                const ny = S.y + m.dy;
                return Math.max(Math.abs(nx), Math.abs(ny)) < dist;
            });
            if (inward.length > 0) {
                const pick = inward[Math.floor(Math.random() * inward.length)];
                console.log(`[PW] Hard wall (${S.x},${S.y}) → ${pick.dir}`);
                return pick;
            }
        }

        const pool = [];

        for (const m of ALL_MOVES) {
            const nx = S.x + m.dx;
            const ny = S.y + m.dy;
            const newDist = Math.max(Math.abs(nx), Math.abs(ny));

            // Never step outside hard boundary
            if (newDist > CFG.boundTiles) continue;

            let weight = 10;

            if (dist > CFG.softZone) {
                const op = (dist - CFG.softZone) / (CFG.boundTiles - CFG.softZone);
                if (newDist < dist) {
                    weight = Math.floor(10 + op * 30);
                } else if (newDist === dist) {
                    weight = Math.floor(10 * (1 - op * 0.7));
                } else {
                    weight = Math.floor(10 * (1 - op * 0.9));
                }
            }

            weight = Math.max(1, weight);
            for (let i = 0; i < weight; i++) pool.push(m);
        }

        if (pool.length === 0) {
            const safe = ALL_MOVES.filter(m =>
                Math.max(Math.abs(S.x + m.dx), Math.abs(S.y + m.dy)) <= CFG.boundTiles
            );
            return safe.length > 0
                ? safe[Math.floor(Math.random() * safe.length)]
                : CARDINALS[0];
        }

        return pool[Math.floor(Math.random() * pool.length)];
    }

    // ── PRIORITY DETECTOR ─────────────────────────────────────────────
    function checkPriority() {
        const body = document.body.innerText;
        if (!body.includes('Appeared!')) return false;

        for (const word of ALWAYS_STOP) {
            if (body.includes(word)) {
                stopWalker(`!!! ${word.toUpperCase()} DETECTED !!!`);
                return true;
            }
        }

        const hasType = SHINY_TYPES.some(t => body.includes(t));
        const hasName = TARGET_POKEMON.some(n => body.includes(n));

        if (hasType && hasName) {
            const lines = body.split('Appeared!')[0].trim().split('\n');
            const name = lines[lines.length - 1].trim();
            stopWalker(`!!! TARGET: ${name} !!!`);
            return true;
        }

        return false;
    }

    function stopWalker(msg) {
        S.paused = true;
        console.warn(`[PW] ${msg}`);
        if (navigator.vibrate) navigator.vibrate([500, 200, 500, 200, 500]);
        try {
            const ctx = new (window.AudioContext || window.webkitAudioContext)();
            const osc = ctx.createOscillator();
            osc.connect(ctx.destination);
            osc.frequency.value = 880;
            osc.start();
            setTimeout(() => osc.stop(), 800);
        } catch (e) {}
    }

    // ── FIND DIRECTION BUTTON ─────────────────────────────────────────
    function findDirBtn(dir) {
        const fn = window.mapMove || window.move;
        if (typeof fn === 'function') return { native: fn, dir };

        return (
            document.querySelector(`a[onclick*="'${dir}'"]`) ||
            document.querySelector(`[id$='_${dir}']`) ||
            document.querySelector(`[data-dir="${dir}"]`) ||
            [...document.querySelectorAll('a, button')].find(
                el => (el.innerText || '').trim().toLowerCase() === dir
            ) ||
            null
        );
    }

    // ── EXECUTE MOVE ──────────────────────────────────────────────────
    function doMove() {
        if (S.paused || S.waiting || !S.active) return;
        if (checkPriority()) return;

        // Distraction pause — 1.2% chance
        if (Math.random() < CFG.distractChance) {
            const idle = 5000 + Math.floor(Math.random() * 8000);
            S.paused = true;
            setTimeout(() => {
                S.paused = false;
                scheduleMove();
            }, idle);
            return;
        }

        const chosen = pickMove();
        const btn = findDirBtn(chosen.dir);

        if (!btn) {
            setTimeout(doMove, 600);
            return;
        }

        if (btn.native) {
            btn.native(btn.dir);
        } else {
            phantomClick(btn);
        }

        S.x += chosen.dx;
        S.y += chosen.dy;
        S.waiting = true;
        S.lastMove = Date.now();
        S.moves++;

        console.log(
            `[PW] #${S.moves} ${chosen.dir.toUpperCase()} (${S.x},${S.y}) d:${Math.max(Math.abs(S.x), Math.abs(S.y))}`
        );
    }

    // ── SCHEDULE MOVE ─────────────────────────────────────────────────
    function scheduleMove() {
        if (!S.active || S.paused) return;
        setTimeout(doMove, skew(CFG.moveMin, CFG.moveMax));
    }

    // ── SESSION BREAK ─────────────────────────────────────────────────
    function checkSession() {
        if (Date.now() - S.sessionStart > CFG.sessionMs) {
            S.paused = true;
            console.log('[PW] Session break.');
            setTimeout(() => {
                S.paused = false;
                S.sessionStart = Date.now();
                S.moves = 0;
                scheduleMove();
            }, CFG.breakMs);
            return true;
        }
        return false;
    }

    // ── OBSERVER ──────────────────────────────────────────────────────
    function setupObserver() {
        const target = document.getElementById('map-grid') || document.body;
        if (S.observer && S.observedEl === target) return;
        if (S.observer) S.observer.disconnect();

        S.observer = new MutationObserver(() => {
            if (!S.waiting) return;
            const now = Date.now();
            if (now - S.lastFire < 350) return;
            S.lastFire = now;
            S.waiting = false;
            if (checkSession()) return;
            scheduleMove();
        });

        S.observer.observe(target, { childList: true, subtree: true });
        S.observedEl = target;
        console.log(`[PW] Observer: ${target.id || 'body'}`);
    }

    // ── URL WATCHER ───────────────────────────────────────────────────
    function onURL() {
        const cur = location.href;
        if (cur === S.lastURL) return;
        S.lastURL = cur;
        console.log(`[PW] URL: ${cur}`);

        if (cur.includes('/map/')) {
            resetAndInit();
        } else {
            S.active = false;
            S.paused = true;
            if (S.observer) {
                S.observer.disconnect();
                S.observer = null;
                S.observedEl = null;
            }
        }
    }

    // Native pushState
    const _push = history.pushState;
    history.pushState = function() {
        _push.apply(history, arguments);
        setTimeout(onURL, 500);
    };

    // Native popstate
    window.addEventListener('popstate', () => setTimeout(onURL, 500));

    // history.js library (DelugeRPG uses this)
    if (window.History && window.History.Adapter) {
        window.History.Adapter.bind(window, 'statechange', () => {
            setTimeout(onURL, 500);
        });
    }

    // Fallback poll
    setInterval(onURL, 1000);

    // ── HEARTBEAT ─────────────────────────────────────────────────────
    function startHeartbeat() {
        if (S.heartbeat) clearInterval(S.heartbeat);
        S.heartbeat = setInterval(() => {
            if (!S.active || S.paused) return;
            setupObserver();
            if (S.waiting && Date.now() - S.lastMove > 6000) {
                console.log('[PW] Stuck — resetting.');
                S.waiting = false;
                scheduleMove();
            }
        }, CFG.heartbeatMs);
    }

    // ── RESET + INIT ──────────────────────────────────────────────────
    function resetAndInit() {
        S.x = 0;
        S.y = 0;
        S.active = false;
        S.paused = false;
        S.waiting = false;
        S.lastFire = 0;
        S.moves = 0;
        S.lastMove = Date.now();
        S.sessionStart = Date.now();
        if (S.observer) {
            S.observer.disconnect();
            S.observer = null;
            S.observedEl = null;
        }
        init();
    }

    function init() {
        if (!location.href.includes('/map/')) return;
        S.active = true;
        setupObserver();
        startHeartbeat();
        setTimeout(scheduleMove, CFG.startDelay);
        console.log(
            `[PW] Phantom Walker 30.5 online | ±${CFG.boundTiles} tiles | soft ±${CFG.softZone}`
        );
    }

    init();

})();