DelugeRPG: Auto-Walker

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

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

// ==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();

})();