PokeRogue - Autoplay (basic)

Autoplay helper for PokeRogue: auto-progress and basic auto-battle. Use responsibly (single-player / local saves only).

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         PokeRogue - Autoplay (basic)
// @namespace    http://tampermonkey.net/
// @version      0.9
// @description  Autoplay helper for PokeRogue: auto-progress and basic auto-battle. Use responsibly (single-player / local saves only).
// @author       Generated with ChatGPT
// @match        https://pokerogue.net/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // CONFIG
    const CONFIG = {
        pollInterval: 300,        // ms between checks
        thinkDelay: 300,          // delay before making a move (ms)
        preferHighestPower: true, // choose move with highest power if data available
        autoUseItems: false,      // whether to try to auto-use items from menu
        stopOnLevelUp: false,     // pause autoplay if party levels up
    };

    // internal state
    let running = false;
    let loopHandle = null;

    function log(...args){ console.log('[ARBOT]',...args); }

    // try to safely find the main scene/game object
    function findScene(){
        // common places found by community scripts
        if (window.scene) return window.scene;
        if (window.game && window.game.scene) return window.game.scene;
        if (globalThis?.pokerogue?.getScene) return globalThis.pokerogue.getScene();
        // scan globals for an object that looks like starterData/dexData
        for (const k of Object.keys(window)){
            try{
                const v = window[k];
                if (v && typeof v === 'object'){
                    if ('starterData' in v && 'dexData' in v) return v;
                }
            }catch(e){}
        }
        return null;
    }

    // helpers to simulate clicks on UI elements (buttons that appear in the DOM)
    function clickElement(el){ if(!el) return false; el.dispatchEvent(new MouseEvent('mousedown',{bubbles:true})); el.dispatchEvent(new MouseEvent('mouseup',{bubbles:true})); el.click(); return true; }

    // try to advance dialogue / continue screens
    function tryAdvanceUI(){
        // Usually the game shows "Next" or continues on space/enter; try to click primary button
        const primary = document.querySelector('.dialogue-button, .primary, button.primary, button');
        if(primary){ clickElement(primary); return true; }
        // fallback: press space
        const evt = new KeyboardEvent('keydown', { key: ' ', code: 'Space', bubbles: true });
        document.dispatchEvent(evt);
        return false;
    }

    // basic battle decision logic - best-effort: inspect party and active battle object
    function autoBattle(scene){
        try{
            // community scripts expose scene.fight or scene.battle or scene.battleData
            const battle = scene.fight || scene.battle || scene.battleData || (scene.scene && (scene.scene.fight||scene.scene.battle));
            if(!battle) return false;

            // find active pokemon and available moves
            const active = battle.activePokemon || battle.activePoke || (battle.player && battle.player.active);
            const options = battle.moves || active?.moves || (battle.moveOptions);

            // if there are DOM buttons for moves, try to click best one
            const moveButtons = Array.from(document.querySelectorAll('.move-button, .battle-move, button.move')).filter(Boolean);
            if(moveButtons.length>0){
                // choose button index using simple heuristic
                let bestIndex = 0;
                if(options && options.length === moveButtons.length){
                    let bestScore = -Infinity;
                    for(let i=0;i<options.length;i++){
                        const m = options[i];
                        // estimate power from fields 'power' 'damage' or 'basePower'
                        const power = Number(m?.power ?? m?.damage ?? m?.basePower ?? 0);
                        const acc = Number(m?.accuracy ?? 100);
                        let score = power * (acc/100);
                        // prefer non-damaging moves less
                        if(m?.category === 'status') score *= 0.5;
                        if(score>bestScore){ bestScore = score; bestIndex = i; }
                    }
                }
                // click chosen move after small delay
                setTimeout(()=> clickElement(moveButtons[bestIndex]), CONFIG.thinkDelay);
                return true;
            }

            // fallback: if battle has a method to choose move, call it
            if(typeof battle.chooseMove === 'function'){
                // try to select by highest power
                const moves = battle.moves || active?.moves || [];
                let idx = 0;
                if(moves.length){
                    let best = -Infinity;
                    for(let i=0;i<moves.length;i++){
                        const m = moves[i];
                        const p = Number(m?.power ?? m?.damage ?? 0);
                        if(p>best){ best = p; idx = i; }
                    }
                }
                setTimeout(()=> { try{ battle.chooseMove(idx); }catch(e){/*ignore*/} }, CONFIG.thinkDelay);
                return true;
            }

        }catch(e){ /* ignore */ }
        return false;
    }

    function mainLoop(){
        const scene = findScene();
        if(!scene){ return; }

        // If currently in battle/selection, try to auto-battle
        if(autoBattle(scene)){ return; }

        // otherwise try to advance UI (menus, dialogue, shop screens)
        tryAdvanceUI();
    }

    // public controls (exposed on window for quick toggling)
    function start(){ if(running) return; running = true; log('autoplay started'); loopHandle = setInterval(mainLoop, CONFIG.pollInterval); }
    function stop(){ if(!running) return; running = false; clearInterval(loopHandle); loopHandle = null; log('autoplay stopped'); }

    // expose controls
    globalThis.PokeRogueAutoplay = { start, stop, running: () => running, CONFIG };

    // small UI hint on page
    const badge = document.createElement('div');
    badge.textContent = 'AR-BOT';
    Object.assign(badge.style, {position:'fixed',right:'8px',bottom:'8px',padding:'6px 8px',background:'#111',color:'#fff',zIndex:99999,fontSize:'12px',borderRadius:'6px',opacity:0.6,cursor:'pointer'});
    badge.title = 'Click to toggle autoplay (or use PokeRogueAutoplay.start()/stop() in console)';
    badge.onclick = ()=>{ running? stop():start(); badge.style.background = running? '#a00' : '#111'; };
    document.body.appendChild(badge);

    log('Autoplay helper injected. Use PokeRogueAutoplay.start() to begin.');

})();