Greasy Fork

来自缓存

Greasy Fork is available in English.

Automatizador de Resgate

O prédio está em chamas. Vidas dependem de você. Logs temáticos para maior imersão.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Automatizador de Resgate
// @namespace    http://tampermonkey.net/
// @version      10.15.3
// @description  O prédio está em chamas. Vidas dependem de você. Logs temáticos para maior imersão.
// @author       Chris Popper & Equipe de Resgate
// @match        *://*.popmundo.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // --- CONFIGURAÇÕES ---
    const HOSTNAME = window.location.hostname;
    const TRAVEL_WAIT_TIME_MS = (4 * 60) * 1000; // 4 minutos
    const RESCUE_FLOOR_BUTTON_TEXT = 'Floor 2-100';
    const ROOF_BUTTON_TEXT = 'Roof';
    const BUTTON_FAIL_MESSAGE = 'Você pressiona o botão e nada acontece... SOCORRO!';
    const HELICOPTER_ID = '236397494';
    const PANEL_POSITION_KEY = 'rescue_panel_position';
    const OXYGEN_SUPPLY_TEXT = 'Suprimento de oxigênio';

    // --- DADOS DOS MAPAS ---
    const MAP_DATA_BY_ID = {
        "238144658": { mapName: "Burning Office (1.1)", moves: { toVictim: ['Leste', 'Sul', 'Leste', 'Sul', 'Sul', 'Leste', 'Leste', 'Norte'] } },
        "238144562": { mapName: "Burning Office (1.2)", moves: { toVictim: ['Leste', 'Sul', 'Sul', 'Leste', 'Leste', 'Leste', 'Sul'] } },
        "237550635": { mapName: "Burning Office (1.3)", moves: { toVictim: ['Leste', 'Sul', 'Sul', 'Leste', 'Norte', 'Para Baixo', 'Sul', 'Leste', 'Norte'] } },
        "237550282": { mapName: "Burning Office (1.4)", moves: { toVictim: ['Leste', 'Leste', 'Leste', 'Norte'] } },
        "238144341": { mapName: "Burning Office (1.5)", moves: { toVictim: ['Leste', 'Leste', 'Leste', 'Sul', 'Oeste'] } },
        "238144434": { mapName: "Burning Office (1.6)", moves: { toVictim: ['Leste', 'Sul', 'Sul', 'Sul', 'Oeste'] } },
        "237550408": { mapName: "Burning Office (1.7)", moves: { toVictim: ['Leste', 'Leste', 'Leste', 'Leste', 'Norte'] } },
        "236934729": { mapName: "Collapsed Floor (2.1)", moves: { toVictim: ['Leste', 'Sul', 'Sul', 'Leste', 'Para Cima', 'Oeste', 'Norte', 'Norte', 'Leste', 'Leste', 'Leste', 'Para Cima', 'Oeste', 'Oeste', 'Oeste', 'Sul', 'Sul', 'Oeste', 'Para Baixo'] } },
        "237549465": { mapName: "Collapsed Floor (2.2)", moves: { toVictim: ['Leste', 'Sudeste', 'Norte', 'Para Cima', 'Oeste'] } },
        "237550032": { mapName: "Collapsed Floor (2.3)", moves: { toVictim: ['Leste', 'Norte', 'Leste', 'Para Cima', 'Sul', 'Oeste', 'Sul', 'Sul', 'Leste', 'Leste', 'Sul'] } },
        "237550178": { mapName: "Collapsed Floor (2.4)", moves: { toVictim: ['Leste', 'Sul', 'Sul', 'Oeste', 'Sul', 'Para Baixo', 'Norte', 'Leste', 'Norte', 'Norte', 'Leste', 'Sul'] } },
        "236936071": { mapName: "Collapsed Floor (2.5)", moves: { toVictim: ['Leste', 'Sul', 'Oeste', 'Para Baixo', 'Leste', 'Sul', 'Leste', 'Norte', 'Para Baixo', 'Norte', 'Leste', 'Leste', 'Sul', 'Para Cima'] } },
        "238145228": { mapName: "Collapsed Office (3.1)", moves: { toVictim: ['Leste', 'Norte', 'Para Cima', 'Leste', 'Para Baixo', 'Leste', 'Sul', 'Sul', 'Leste', 'Para Cima', 'Norte', 'Oeste'] } },
        "238144898": { mapName: "Collapsed Office (3.2)", moves: { toVictim: ['Leste', 'Sul', 'Sul', 'Leste', 'Leste', 'Para Baixo', 'Norte', 'Oeste', 'Sul', 'Oeste', 'Norte'] } },
        "238145154": { mapName: "Collapsed Office (3.3)", moves: { toVictim: ['Leste', 'Leste', 'Leste', 'Leste', 'Para Baixo', 'Sul', 'Sul'] } },
        "238145032": { mapName: "Collapsed Office (3.5)", moves: { toVictim: ['Leste', 'Leste', 'Leste', 'Sul', 'Oeste', 'Para Baixo'] } },
        "238144785": { mapName: "Collapsed Office (3.6)", moves: { toVictim: ['Leste', 'Leste', 'Leste', 'Sul', 'Oeste', 'Para Baixo', 'Leste', 'Sul', 'Oeste', 'Oeste', 'Sul', 'Oeste'] } },
        "236937629": { mapName: "Collapsed Office Floor (4.1)", moves: { toVictim: ['Leste', 'Sul', 'Leste', 'Sul', 'Para Baixo', 'Leste', 'Norte', 'Norte', 'Leste', 'Norte', 'Para Baixo', 'Oeste', 'Sul', 'Oeste'] } },
        "236937430": { mapName: "Collapsed Office Floor (4.2)", moves: { toVictim: ['Leste', 'Sul', 'Sul', 'Leste', 'Leste', 'Para Baixo', 'Norte', 'Leste', 'Para Baixo', 'Oeste', 'Norte', 'Oeste', 'Oeste', 'Sul', 'Oeste', 'Para Cima', 'Leste', 'Sul', 'Leste', 'Para Baixo'] } },
        "236361115": { mapName: "Floor 1 (5.1)", moves: { toVictim: ['Leste', 'Leste', 'Leste', 'Sul', 'Sul', 'Sul'] } },
        "236361116": { mapName: "Floor 2 (6.1)", moves: { toVictim: ['Leste', 'Sul', 'Sul', 'Leste', 'Norte'] } },
        "238145480": { mapName: "Skyscraper GYMs (7.1)", moves: { toVictim: ['Leste', 'Sul', 'Sul', 'Oeste'] } },
        "238145617": { mapName: "Skyscraper GYMs (7.2)", moves: { toVictim: ['Leste', 'Norte', 'Leste', 'Leste', 'Leste'] } },
        "238145366": { mapName: "Skyscraper GYMs (7.3)", moves: { toVictim: ['Leste', 'Sul', 'Sul', 'Leste', 'Leste'] } },
        "238145773": { mapName: "Skyscraper Library (8.1)", moves: { toVictim: ['Leste', 'Norte', 'Leste', 'Leste', 'Leste'] } },
        "238145650": { mapName: "Skyscraper Library (8.2)", moves: { toVictim: ['Leste', 'Sul', 'Sul', 'Leste', 'Leste', 'Leste'] } },
        "236936724": { mapName: "Wanda Massage Parlor (9.1)", moves: { toVictim: ['Leste', 'Leste', 'Leste', 'Leste', 'Norte', 'Para Cima', 'Sul', 'Oeste', 'Oeste', 'Oeste', 'Norte', 'Oeste'] } }
    };

    // --- LÓGICA DE ESTADO ---
    const STATE = { current: sessionStorage.getItem('rescue_state'), moveIndex: parseInt(sessionStorage.getItem('rescue_moveIndex'), 10), mapId: sessionStorage.getItem('rescue_mapId'), roofElevatorId: sessionStorage.getItem('rescue_roofElevatorId'), waitUntil: parseInt(sessionStorage.getItem('rescue_waitUntil'), 10), nextState: sessionStorage.getItem('rescue_nextState'), returnState: sessionStorage.getItem('rescue_returnState') };
    if (isNaN(STATE.moveIndex)) STATE.moveIndex = 0;
    function setState(newState, extra = {}) { sessionStorage.setItem('rescue_state', newState); STATE.current = newState; for (const key in extra) { const value = extra[key]; if (value !== undefined) { sessionStorage.setItem(`rescue_${key}`, value); STATE[key] = value; } } }
    function clearState() { Object.keys(sessionStorage).forEach(key => { if (key.startsWith('rescue_')) sessionStorage.removeItem(key); }); STATE.current = null; sessionStorage.removeItem('rescue_log');}

    let lastClickedButtonForRetry = null;

    // --- SISTEMA DE LOG PERSISTENTE ---
    const LOG_KEY = 'rescue_log';
    const MAX_LOG_ENTRIES = 100;
    let countdownInterval = null;
    function log(message) {
        const timestamp = new Date().toLocaleTimeString('pt-BR');
        const logEntry = { time: timestamp, message: message };
        let logs = JSON.parse(sessionStorage.getItem(LOG_KEY) || '[]');
        logs.push(logEntry);
        if (logs.length > MAX_LOG_ENTRIES) { logs = logs.slice(logs.length - MAX_LOG_ENTRIES); }
        sessionStorage.setItem(LOG_KEY, JSON.stringify(logs));
        console.log(`[Comando Resgate Log] ${timestamp} - ${message}`);
        renderLog();
    }
    function renderLog() {
        const logContainer = document.getElementById('rescue-log-container');
        if (!logContainer) return;
        const logs = JSON.parse(sessionStorage.getItem(LOG_KEY) || '[]');
        logContainer.innerHTML = '';
        if (logs.length === 0) {
            logContainer.innerHTML = '<div class="rescue-log-entry"><span>Aguardando chamado...</span></div>';
            return;
        }
        logs.forEach(entry => {
            const entryDiv = document.createElement('div');
            entryDiv.className = 'rescue-log-entry';
            if (entry.message.toLowerCase().includes('erro:') || entry.message.toLowerCase().includes('bloqueado')) { entryDiv.classList.add('error'); }
            else if (entry.message.toLowerCase().includes('vítima localizada') || entry.message.toLowerCase().includes('missão cumprida')) { entryDiv.classList.add('success'); }
            entryDiv.innerHTML = `<span class="timestamp">${entry.time}</span> <span class="message">${entry.message}</span>`;
            logContainer.appendChild(entryDiv);
        });
        logContainer.scrollTop = logContainer.scrollHeight;
    }
    function clearLog() {
        sessionStorage.removeItem(LOG_KEY);
        renderLog();
    }
    function startUICountdown() {
        if (countdownInterval) clearInterval(countdownInterval);
        const logContainer = document.getElementById('rescue-log-container');
        if (!logContainer) return;
        const lastLogEntry = logContainer.querySelector('.rescue-log-entry:last-child .message');
        if (!lastLogEntry) return;
        const originalMessage = lastLogEntry.innerText;
        countdownInterval = setInterval(() => {
            if (STATE.current !== 'WAITING' || !STATE.waitUntil) {
                clearInterval(countdownInterval);
                return;
            }
            const timeLeft = Math.round((STATE.waitUntil - Date.now()) / 1000);
            if (timeLeft > 0) {
                const minutes = Math.floor(timeLeft / 60);
                const seconds = timeLeft % 60;
                lastLogEntry.innerText = `${originalMessage}... ${minutes}m ${seconds}s restantes.`;
            } else {
                clearInterval(countdownInterval);
            }
        }, 1000);
    }

    // --- FUNÇÕES AUXILIARES ---
    const clickElement = (selector) => {
        const element = document.querySelector(selector);
        if (element) {
            lastClickedButtonForRetry = element;
            element.click();
            return true;
        }
        log(`ERRO: Escombros bloquearam o caminho: ${selector}.`);
        clearState();
        return false;
    };
    const clickLinkByText = (text) => {
        const lowerCaseText = text.toLowerCase();
        const xpath = `//a[translate(normalize-space(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ', 'abcdefghijklmnopqrstuvwxyzàáâãäåçèéêëìíîïñòóôõöùúûüý')='${lowerCaseText}']`;
        const link = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        if (link) {
            lastClickedButtonForRetry = link;
            link.click();
            return true;
        }
        log(`ERRO: Acesso "${text}" destruído pelo fogo.`);
        clearState();
        return false;
    };
    const goToUrl = (path) => { const fullUrl = `https://${HOSTNAME}${path}`; window.location.href = fullUrl; };
    const clickElevatorButtonByText = (buttonText) => {
        const xpath = `//em[contains(text(),'${buttonText}')]/ancestor::tr//input[@type='image']`;
        const button = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        if (button) {
            lastClickedButtonForRetry = button;
            button.click();
            return true;
        }
        log(`ERRO: Acesso ao elevador "${buttonText}" bloqueado.`);
        clearState();
        return false;
    };
    function generateReturnPath(forwardPath) {
        const oppositeMoves = { 'Norte': 'Sul', 'Sul': 'Norte', 'Leste': 'Oeste', 'Oeste': 'Leste', 'Para Cima': 'Para Baixo', 'Para Baixo': 'Para Cima' };
        const reversedPath = forwardPath.slice().reverse();
        const returnPath = reversedPath.map(move => oppositeMoves[move] || move);
        return returnPath;
    }

    // --- LÓGICA DE DETECÇÃO ---
    function detectCurrentLocation() {
        const h1 = document.querySelector('h1');
        if (!h1) return { type: 'UNKNOWN' };
        const titleText = h1.innerText.trim();
        if (titleText === 'Elevator') return { type: 'BASE' };
        if (titleText === 'Skyscraper Roof') {
             const roofElevatorLink = document.querySelector('a[href*="/Locale/ItemDetails/"][id*="lnkItem"]');
             const roofElevatorId = roofElevatorLink ? roofElevatorLink.getAttribute('href').split('/').pop() : null;
             return { type: 'ROOF', roofElevatorId: roofElevatorId };
        }
        const elevatorButton = document.querySelector('a[href*="/Locale/ItemDetails/"][id*="lnkItem"]');
        if (elevatorButton) {
            const mapId = elevatorButton.getAttribute('href').split('/').pop();
            if (MAP_DATA_BY_ID[mapId]) {
                return { type: 'MAP', mapId: mapId, mapName: MAP_DATA_BY_ID[mapId].mapName };
            }
        }
        const exploreLink = document.evaluate("//a[normalize-space()='Explorar a área']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        if (exploreLink) {
            return { type: 'IN_TRANSIT' };
        }
        return { type: 'UNKNOWN' };
    }

    function exploreUntilReady(successCondition, successState, successLog, extraStateData = {}) {
        const location = detectCurrentLocation();
        if (successCondition(location)) {
            log(successLog);
            setState(successState, { ...extraStateData, mapId: location.mapId, roofElevatorId: location.roofElevatorId });
            setTimeout(processState, 500);
        } else {
            log("Visibilidade baixa. Verificando rota novamente...");
            setTimeout(() => clickLinkByText("Explorar a área"), 1500 + Math.random() * 1000);
        }
    }

    // --- MÁQUINA DE ESTADOS PRINCIPAL ---
    function processState() {
        if (!STATE.current) return;
        if (STATE.current === 'WAITING') {
            const timeLeft = Math.round((STATE.waitUntil - Date.now()) / 1000);
            if (timeLeft > 0) {
                setTimeout(processState, 1000);
                return;
            } else {
                setState(STATE.nextState);
                sessionStorage.removeItem('rescue_nextState');
                sessionStorage.removeItem('rescue_waitUntil');
                clickLinkByText("Explorar a área");
                return;
            }
        }

        const moveSequence = (state, moves, nextState) => {
            if (!document.location.pathname.endsWith('/Compass')) { log("Orientando-me pela fumaça. Bússola em mãos."); goToUrl('/World/Popmundo.aspx/Locale/Compass'); return; }
            if (STATE.moveIndex < moves.length) { const moveDirection = moves[STATE.moveIndex]; log(`Avançando para ${moveDirection}... O calor é intenso.`); setState(state, { moveIndex: STATE.moveIndex + 1 }); clickLinkByText(moveDirection); } else { setState(nextState); setTimeout(processState, 500); }
        };
        const helicopterMoves = { toHelicopter: ['Leste', 'Sul', 'Leste'], backFromHelicopter: ['Oeste', 'Norte', 'Oeste'] };

        function startWaiting(nextState) {
            log("Avanço tático em andamento. Aguardando liberação...");
            setState('WAITING', { waitUntil: Date.now() + TRAVEL_WAIT_TIME_MS, nextState: nextState });
            startUICountdown();
            setTimeout(processState, 1000);
        }

        switch (STATE.current) {
            // ESTADO CRÍTICO: Garante que estamos na visão correta para clicar em botões.
            case 'REFRESH_LOCATION_VIEW':
                log("Confirmando coordenadas e atualizando a visão local...");
                const nextState = STATE.nextState;
                sessionStorage.removeItem('rescue_nextState');
                setState(nextState); // Prepara o estado para a próxima página
                clickLinkByText("Explorar a área"); // Causa o recarregamento para a visão correta
                break;

            case 'GO_TO_RESCUE_FLOOR':
                log("Preparando para acessar o andar do incêndio...");
                setState('REFRESH_LOCATION_VIEW', { nextState: 'DO_GO_TO_RESCUE_FLOOR_CLICK' });
                processState();
                break;
            case 'DO_GO_TO_RESCUE_FLOOR_CLICK':
                log("Acessando o andar do incêndio. Elevador de serviço ativo.");
                setState('START_WAIT_TO_RESCUE');
                clickElevatorButtonByText(RESCUE_FLOOR_BUTTON_TEXT);
                break;

            case 'START_WAIT_TO_RESCUE': startWaiting('AWAITING_MAP_LOAD'); break;
            case 'AWAITING_MAP_LOAD':
                exploreUntilReady(
                    loc => loc.type === 'MAP',
                    'BEGIN_MAP_EXPLORATION',
                    'Cheguei ao andar do incêndio. Fumaça densa e calor extremo.'
                );
                break;
            case 'BEGIN_MAP_EXPLORATION':
                const location = detectCurrentLocation();
                if (location.type === 'MAP') { log(`Consultando planta do ${location.mapName}. Traçando rota de busca.`); setState('EXPLORE_TO_VICTIM', { mapId: location.mapId, moveIndex: 0 }); goToUrl('/World/Popmundo.aspx/Locale/Compass'); } else { log("ERRO: Desorientado pela fumaça. Onde estou?"); clearState(); }
                break;
            case 'EXPLORE_TO_VICTIM': log("Busca primária iniciada. Avançando no setor..."); moveSequence('EXPLORE_TO_VICTIM', MAP_DATA_BY_ID[STATE.mapId].moves.toVictim, 'RESCUE_VICTIM'); break;

            case 'RESCUE_VICTIM':
                 log("Preparando para estabilizar a vítima...");
                 setState('REFRESH_LOCATION_VIEW', { nextState: 'DO_RESCUE_VICTIM_CLICK' });
                 processState();
                 break;
            case 'DO_RESCUE_VICTIM_CLICK':
                 log("VÍTTIMA LOCALIZADA! Sinais vitais presentes. Iniciando procedimento de estabilização.");
                 setState('GOTO_COMPASS_FOR_RETURN');
                 clickElement('input[src*="Item_Use.png"]');
                 break;

            case 'GOTO_COMPASS_FOR_RETURN': log("Vítima estabilizada. Preparando para extração imediata."); setState('MOVE_BACK_SEQUENCE', { moveIndex: 0 }); goToUrl('/World/Popmundo.aspx/Locale/Compass'); break;
            case 'MOVE_BACK_SEQUENCE':
                log("Iniciando extração. Retornando pela rota de fuga segura.");
                const forwardPath = MAP_DATA_BY_ID[STATE.mapId].moves.toVictim;
                const returnPath = generateReturnPath(forwardPath);
                moveSequence('MOVE_BACK_SEQUENCE', returnPath, 'GO_TO_ELEVATOR_ITEM_PAGE');
                break;
            case 'GO_TO_ELEVATOR_ITEM_PAGE': log("Ponto de extração à vista. Mantendo o ritmo."); setState('USE_ELEVATOR_TO_RETURN'); goToUrl(`/World/Popmundo.aspx/Locale/ItemDetails/${STATE.mapId}`); break;

            case 'USE_ELEVATOR_TO_RETURN':
                log("Evacuando para a área de triagem no térreo.");
                setState('WAITING_AFTER_RETURN');
                clickElement('#ctl00_cphLeftColumn_ctl00_btnItemUse');
                break;

            case 'WAITING_AFTER_RETURN': startWaiting('AWAITING_BASE_LOAD_BEFORE_ROOF'); break;
            case 'AWAITING_BASE_LOAD_BEFORE_ROOF':
                exploreUntilReady(
                    loc => loc.type === 'BASE',
                    'GO_TO_REFILL_OXYGEN',
                    'Chegamos ao Térreo. Vítima entregue à equipe médica local.',
                    { returnState: 'GO_TO_ROOF' }
                );
                break;

            // --- FLUXO DE RECARGA DE OXIGÊNIO (CORRIGIDO) ---
            case 'GO_TO_REFILL_OXYGEN':
                log("Nível de oxigênio crítico. Localizando suprimento de emergência.");
                if (clickLinkByText(OXYGEN_SUPPLY_TEXT)) {
                    setState('REFILLING_OXYGEN_PAGE');
                } else {
                    log("AVISO: Não encontrei o Suprimento de Oxigênio! Ignorando recarga, prioridade é a missão.");
                    setState(STATE.returnState);
                    setTimeout(processState, 500);
                }
                break;
            case 'REFILLING_OXYGEN_PAGE':
                log("Confirmando recarga de O².");
                if (clickElement('#ctl00_cphLeftColumn_ctl00_btnItemUse')) {
                    setState('AWAITING_BASE_LOAD_AFTER_REFILL');
                } else {
                    log("ERRO: Botão de recarga não encontrado. Abortando recarga e continuando a missão.");
                    setState(STATE.returnState);
                    setTimeout(processState, 500);
                }
                break;
            case 'AWAITING_BASE_LOAD_AFTER_REFILL':
                exploreUntilReady(
                    loc => loc.type === 'BASE',
                    'CONTINUE_AFTER_REFILL',
                    'Recarga de O² completa. Níveis normalizados. Retornando ao ponto de partida.'
                );
                break;
            case 'CONTINUE_AFTER_REFILL':
                const nextStateAfterRefill = STATE.returnState;
                sessionStorage.removeItem('rescue_returnState');
                setState(nextStateAfterRefill);
                processState();
                break;
            // --- FIM DO FLUXO DE RECARGA ---

            case 'GO_TO_ROOF':
                log("Preparando para evacuação pelo telhado...");
                setState('REFRESH_LOCATION_VIEW', { nextState: 'DO_GO_TO_ROOF_CLICK' });
                processState();
                break;
            case 'DO_GO_TO_ROOF_CLICK':
                log("Acesso ao solo comprometido. Evacuando para o ponto de extração no telhado.");
                setState('START_WAIT_TO_ROOF');
                clickElevatorButtonByText(ROOF_BUTTON_TEXT);
                break;

            case 'START_WAIT_TO_ROOF': startWaiting('AWAITING_ROOF_LOAD'); break;
            case 'AWAITING_ROOF_LOAD':
                exploreUntilReady(
                    loc => loc.type === 'ROOF' && loc.roofElevatorId,
                    'ARRIVED_AT_ROOF',
                    'Chegamos ao telhado. Equipe de extração aérea a postos.'
                );
                break;
            case 'ARRIVED_AT_ROOF':
                log("Avançando para a zona de pouso designada.");
                setState('MOVE_TO_HELICOPTER', { moveIndex: 0 });
                goToUrl('/World/Popmundo.aspx/Locale/Compass');
                break;
            case 'MOVE_TO_HELICOPTER': log("Sinalizando para o helicóptero. Rotores à vista!"); moveSequence('MOVE_TO_HELICOPTER', helicopterMoves.toHelicopter, 'USE_HELICOPTER'); break;
            case 'USE_HELICOPTER': log("Contato visual com a equipe de voo. Preparando transferência da vítima."); setState('CLICK_USE_ON_HELICOPTER'); goToUrl(`/World/Popmundo.aspx/Locale/ItemDetails/${HELICOPTER_ID}`); break;

            case 'CLICK_USE_ON_HELICOPTER':
                log("Transferência concluída. Vítima a bordo e segura.");
                setState('EXPLORE_ROOF_AFTER_HELICOPTER');
                clickElement('#ctl00_cphLeftColumn_ctl00_btnItemUse');
                break;

            case 'EXPLORE_ROOF_AFTER_HELICOPTER':
                log("Helicóptero decolou. Retornando da zona de pouso.");
                setState('GOTO_COMPASS_FOR_ROOF_RETURN');
                clickLinkByText("Explorar a área");
                break;
            case 'GOTO_COMPASS_FOR_ROOF_RETURN':
                log("Preparando para atravessar o telhado de volta para o elevador.");
                setState('MOVE_BACK_FROM_HELICOPTER', { moveIndex: 0 });
                goToUrl('/World/Popmundo.aspx/Locale/Compass');
                break;
            case 'MOVE_BACK_FROM_HELICOPTER':
                log("Atravessando o telhado de volta para o acesso interno.");
                moveSequence('MOVE_BACK_FROM_HELICOPTER', helicopterMoves.backFromHelicopter, 'GO_TO_ROOF_ELEVATOR_PAGE');
                break;
            case 'GO_TO_ROOF_ELEVATOR_PAGE': log("Elevador de acesso ao telhado localizado. Acesso liberado."); setState('USE_ELEVATOR_FROM_ROOF'); goToUrl(`/World/Popmundo.aspx/Locale/ItemDetails/${STATE.roofElevatorId}`); break;

            case 'USE_ELEVATOR_FROM_ROOF':
                log("Descendo. Retornando ao Posto de Comando no térreo.");
                setState('WAITING_AFTER_RETURN_FROM_ROOF');
                clickElement('#ctl00_cphLeftColumn_ctl00_btnItemUse');
                break;

            case 'WAITING_AFTER_RETURN_FROM_ROOF': startWaiting('AWAITING_BASE_LOAD_FINAL'); break;
            case 'AWAITING_BASE_LOAD_FINAL':
                exploreUntilReady(
                    loc => loc.type === 'BASE',
                    'GO_TO_REFILL_OXYGEN',
                    'MISSÃO CUMPRIDA! Retorno à base. Reabastecendo para o próximo chamado.',
                    { returnState: 'GO_TO_RESCUE_FLOOR' }
                );
                break;
        }
    }

    // --- CONTROLES DA AUTOMAÇÃO ---
    function startAutomation() {
        const location = detectCurrentLocation();
        if (location.type === 'BASE' || location.type === 'MAP' || location.type === 'ROOF') {
            log("Alerta recebido! Central, estamos a caminho. Iniciando protocolo de resgate.");
            if (STATE.current) {
                log(`Retomando automação do estado: ${STATE.current}`);
            } else if (location.type === 'MAP') {
                setState('BEGIN_MAP_EXPLORATION');
            } else {
                 setState('GO_TO_RESCUE_FLOOR');
            }
            processState();
        } else {
            log("ERRO: Preciso estar no local do incêndio para iniciar.");
        }
        createUI();
    }
    function stopAutomation() {
        if (countdownInterval) clearInterval(countdownInterval);
        clearState();
        log("Comando, abortar missão! Repito, abortar! Retornando à base imediatamente.");
        location.reload();
    }

    // --- FUNÇÃO PARA TORNAR O PAINEL ARRASTÁVEL ---
    function makePanelDraggable(panel) {
        const header = panel.querySelector('.rescue-header');
        let isDragging = false;
        let offsetX, offsetY;
        header.addEventListener('mousedown', (e) => {
            isDragging = true;
            offsetX = e.clientX - panel.offsetLeft;
            offsetY = e.clientY - panel.offsetTop;
            document.body.style.userSelect = 'none';
            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp);
        });
        function onMouseMove(e) {
            if (!isDragging) return;
            panel.style.left = `${e.clientX - offsetX}px`;
            panel.style.top = `${e.clientY - offsetY}px`;
            panel.style.right = 'auto';
        }
        function onMouseUp() {
            if (!isDragging) return;
            isDragging = false;
            document.body.style.userSelect = '';
            document.removeEventListener('mousemove', onMouseMove);
            document.removeEventListener('mouseup', onMouseUp);
            const pos = { top: panel.style.top, left: panel.style.left };
            localStorage.setItem(PANEL_POSITION_KEY, JSON.stringify(pos));
        }
    }

    // --- CRIAÇÃO DA UI ---
    function createUI() {
        const oldPanel = document.getElementById('rescue-panel');
        if (oldPanel) oldPanel.remove();
        const styles = `
            #rescue-panel { position: fixed; top: 15px; right: 15px; width: 320px; background-color: #f0f2f5; border: 1px solid #ccc; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 9999; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 14px; color: #333; }
            .rescue-header { padding: 10px 15px; background-color: #fff; border-bottom: 1px solid #ddd; border-radius: 8px 8px 0 0; display: flex; justify-content: space-between; align-items: center; cursor: move; }
            .rescue-header h3 { margin: 0; font-size: 16px; font-weight: 600; color: #1c1e21; }
            .header-right-controls { display: flex; align-items: center; }
            .rescue-version { font-size: 12px; color: #888; background-color: #e4e6eb; padding: 2px 6px; border-radius: 4px;}
            #clear-log-button { margin-left: 10px; font-size: 12px; color: #6c757d; text-decoration: none; cursor: pointer; }
            #clear-log-button:hover { color: #d9534f; text-decoration: underline; }
            .rescue-controls { padding: 15px; border-bottom: 1px solid #ddd; }
            .rescue-controls p { font-size: 13px; color: #606770; margin: 0 0 10px; text-align: center; }
            .rescue-button { width: 100%; padding: 10px; cursor: pointer; border: none; border-radius: 6px; font-size: 14px; font-weight: bold; transition: background-color 0.2s ease, transform 0.1s ease; }
            .rescue-button:active { transform: scale(0.98); }
            #start-rescue-button { background-color: #d9534f; color: white; }
            #start-rescue-button:hover { background-color: #c9302c; }
            #stop-rescue-button { background-color: #5bc0de; color: white; }
            #stop-rescue-button:hover { background-color: #31b0d5; }
            #rescue-log-container { max-height: 250px; overflow-y: auto; background-color: #fff; padding: 10px 15px; font-size: 12px; border-radius: 0 0 8px 8px; }
            .rescue-log-entry { padding: 4px 0; border-bottom: 1px solid #f0f2f5; display: flex; font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; }
            .rescue-log-entry:last-child { border-bottom: none; }
            .rescue-log-entry .timestamp { color: #888; margin-right: 8px; flex-shrink: 0; }
            .rescue-log-entry .message { word-break: break-word; }
            .rescue-log-entry.error .message { color: #d9534f; font-weight: bold; }
            .rescue-log-entry.success .message { color: #5cb85c; font-weight: bold; }
        `;
        const styleSheet = document.createElement("style");
        styleSheet.type = "text/css";
        styleSheet.innerText = styles;
        document.head.appendChild(styleSheet);

        const panel = document.createElement('div');
        panel.id = 'rescue-panel';
        panel.innerHTML = `
            <div class="rescue-header">
                <h3>Central de Comando</h3>
                <div class="header-right-controls">
                    <span class="rescue-version">v10.15.3</span>
                    <a id="clear-log-button" title="Apagar o registro de atividades">Limpar Log</a>
                </div>
            </div>
            <div id="rescue-controls" class="rescue-controls"></div>
            <div id="rescue-log-container"></div>
        `;
        const savedPosition = localStorage.getItem(PANEL_POSITION_KEY);
        if (savedPosition) {
            const pos = JSON.parse(savedPosition);
            panel.style.top = pos.top;
            panel.style.left = pos.left;
            panel.style.right = 'auto';
        }
        document.body.appendChild(panel);
        document.getElementById('clear-log-button').addEventListener('click', clearLog);

        const controlsDiv = document.getElementById('rescue-controls');
        if (STATE.current) {
            controlsDiv.innerHTML = `<button id="stop-rescue-button" class="rescue-button">Recuar para a Base</button>`;
            document.getElementById('stop-rescue-button').addEventListener('click', stopAutomation);
        } else {
            controlsDiv.innerHTML = `
                <p><strong>O prédio está em chamas. Vidas dependem de você.</strong></p>
                <button id="start-rescue-button" class="rescue-button">Entrar no Incêndio</button>
            `;
            document.getElementById('start-rescue-button').addEventListener('click', startAutomation);
        }
        renderLog();
        makePanelDraggable(panel);
    }

    // --- OBSERVER DE ERRO ---
    function setupErrorObserver() {
        const targetNode = document.getElementById('notifications');
        if (!targetNode) return;
        const callback = (mutationsList, observer) => {
            for (const mutation of mutationsList) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    mutation.addedNodes.forEach(node => {
                        if (node.nodeType === 1 && node.classList.contains('notification-error') && node.innerText.includes(BUTTON_FAIL_MESSAGE)) {
                            if (lastClickedButtonForRetry) {
                                log("Botão emperrou! O fogo deve ter danificado os circuitos. Tentando novamente em 2s...");
                                setTimeout(() => {
                                    log("Forçando o botão...");
                                    lastClickedButtonForRetry.click();
                                }, 2000);
                            } else {
                                log("ERRO CRÍTICO: Botão emperrou, mas perdi o rastro de qual botão era. Abortando.");
                                stopAutomation();
                            }
                        }
                    });
                }
            }
        };
        const observer = new MutationObserver(callback);
        observer.observe(targetNode, { childList: true });
    }

    // --- INICIALIZAÇÃO ---
    window.addEventListener('load', () => {
        createUI();
        if (STATE.current) {
            setupErrorObserver();
            if (STATE.current === 'WAITING') {
                startUICountdown();
            }
            setTimeout(processState, 500);
        }
    });
})();