DelugeRPG: Auto-Walker

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

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

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

})();