Evolve Auto-Library

Evolve Auto 的靜態工具庫 (UI, Utils, Scanner, GameData)。供主邏輯腳本調用。

Este script no debería instalarse directamente. Es una biblioteca que utilizan otros scripts mediante la meta-directiva de inclusión // @require https://update.greasyfork.org/scripts/564998/1748004/Evolve%20Auto-Library.js

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Evolve Auto-Library
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  強制 UI 恢復版。
// @author       Gemini & User
// @match        https://pmotschmann.github.io/Evolve/
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    window.EvolveAuto = window.EvolveAuto || {};
    const GameData = {
        jobs: [
            { id: 'civ-banker', name: '銀行', tier: 1, type: 'gold' },
            { id: 'civ-professor', name: '教授', tier: 1, type: 'gold' },
            { id: 'civ-scientist', name: '科學家', tier: 1, type: 'science' },
            { id: 'civ-miner', name: '礦工', tier: 1, type: 'gold' },
            { id: 'civ-coal_miner', name: '煤礦', tier: 1, type: 'gold' },
            { id: 'virtual-steel', name: '鋼 (監控)', tier: 1, type: 'resource', produces: 'Steel', isVirtual: true },
            { id: 'craftPlywood', name: '膠合板', tier: 2, type: 'craft' },
            { id: 'craftBrick', name: '砌磚', tier: 2, type: 'craft' },
            { id: 'craftWrought_Iron', name: '鍛鐵', tier: 2, type: 'craft' },
            { id: 'craftSheet_Metal', name: '板金', tier: 2, type: 'craft' },
            { id: 'civ-cement_worker', name: '水泥', tier: 2, type: 'civ-craft' },
            { id: 'civ-lumberjack', name: '伐木', tier: 3, type: 'junk', produces: 'Lumber' },
            { id: 'civ-quarry_worker', name: '石工', tier: 3, type: 'junk', produces: 'Stone' },
            { id: 'civ-entertainer', name: '藝人', tier: 1, type: 'morale' },
            { id: 'civ-farmer', name: '農夫', tier: 4, type: 'food' }
        ],
        resToJobReverse: { 'civ-lumberjack': 'Lumber', 'civ-quarry_worker': 'Stone', 'civ-miner': 'Iron', 'civ-coal_miner': 'Coal', 'civ-cement_worker': 'Cement', 'craftPlywood': 'Plywood', 'craftBrick': 'Brick', 'craftWrought_Iron': 'Wrought_Iron', 'craftSheet_Metal': 'Sheet_Metal' },
        crateResources: [ 'Food', 'Lumber', 'Stone', 'Copper', 'Iron', 'Aluminium', 'Cement', 'Coal', 'Steel' ],
        mapping: { 'lumber': 'Lumber', 'stone': 'Stone', 'plywood': 'Plywood', 'brick': 'Brick', 'wrought_iron': 'Wrought_Iron', 'sheet_metal': 'Sheet_Metal', 'steel': 'Steel', 'cement': 'Cement' }
    };

    const Utils = {
        state: { vueRoot: null, resKey: null },
        log: function(msg, color="white") { console.log(`%c[Auto] ${msg}`, `color: ${color}`); },
        parseNum: function(str) { if (!str) return 0; str = String(str).trim(); let mult = 1; if (str.includes('k')) mult = 1e3; else if (str.includes('M')) mult = 1e6; return parseFloat(str.replace(/[^\d.\-]/g, '')) * mult; },
        getRes: function(name) {
            let res = { visible: false, cur: 0, max: 0, pct: 0, rate: 0 };
            const el = document.getElementById(`res${name}`);
            if (el && el.style.display !== 'none') {
                const countTxt = el.querySelector('.count')?.textContent || "0/0";
                const parts = countTxt.split('/');
                res.cur = this.parseNum(parts[0]);
                res.max = this.parseNum(parts[1]);
                res.pct = res.max > 0 ? res.cur / res.max : 0;
                res.visible = true;
                const diffEl = el.querySelector('.diff');
                if (diffEl) res.rate = this.parseNum(diffEl.textContent);
            }
            return res;
        },
        getJobInfo: function(id) {
            const el = document.getElementById(id);
            if (!el) return { id, cur: 0, max: 0, isFull: true };
            const countTxt = el.querySelector('.count')?.textContent || "0/0";
            let cur = 0, max = 999;
            if (countTxt.includes('/')) { const p = countTxt.split('/'); cur = this.parseNum(p[0]); max = this.parseNum(p[1]); }
            else cur = this.parseNum(countTxt);
            return { id, cur, max, isFull: cur >= max };
        },
        getFoundry: function() {
            const el = document.querySelector('#foundry .foundry .count');
            if (!el) return { isFull: false };
            const p = el.textContent.split('/');
            return { isFull: this.parseNum(p[0]) >= this.parseNum(p[1]) };
        },
        getCrateInfo: function() {
            const el = document.querySelector('#crateTotal .crtTotal');
            if (!el) return { free: 0, max: 0 };
            const m = el.textContent.match(/(\d+)\s*\/\s*(\d+)/);
            return m ? { free: parseInt(m[1]), max: parseInt(m[2]) } : { free: 0, max: 0 };
        },
        getContainerInfo: function() {
            const el = document.querySelector('#crateTotal .cntTotal');
            if (!el) return { free: 0, max: 0 };
            const m = el.textContent.match(/(\d+)\s*\/\s*(\d+)/);
            return m ? { free: parseInt(m[1]), max: parseInt(m[2]) } : { free: 0, max: 0 };
        },
        click: function(id, type='add') {
            const el = document.getElementById(id);
            if (!el) return false;
            const btn = type === 'sub' ? el.querySelector('.sub') : (el.querySelector('.add') || el);
            if (btn && !btn.classList.contains('disabled')) { btn.click(); return true; }
            return false;
        },
        findButtonByTitle: function(title) {
            const btns = document.querySelectorAll('button, .button');
            for (let b of btns) { if (b.textContent.includes(title)) return b; }
            return null;
        },
        getQueueItemData: function() { return document.querySelector('#buildQueue .buildList li:first-child a'); }
    };

    const UI = {
        init: function() {
            if (document.getElementById('rim-hud')) return;
            const box = document.createElement('div');
            box.id = 'rim-hud';
            box.style.cssText = "position: fixed; bottom: 20px; right: 20px; width: 450px; background: rgba(0,0,0,0.9); border: 1px solid #00ffea; color: #00ffea; font-family: monospace; font-size: 11px; z-index: 10000; display: flex;";
            document.body.appendChild(box);

            const sidebar = document.createElement('div');
            sidebar.style.cssText = "width: 50px; border-right: 1px solid #00ffea; padding: 5px; display: flex; flex-direction: column; gap: 5px;";
            box.appendChild(sidebar);

            const createBtn = (txt, color, fn) => {
                const b = document.createElement('button');
                b.innerText = txt;
                b.style.cssText = `background:${color}; color:#fff; border:1px solid #fff; font-size:9px; cursor:pointer; padding:2px;`;
                b.onclick = (e) => { e.stopPropagation(); if(window.EvolveAuto.Core) fn(window.EvolveAuto.Core); };
                sidebar.appendChild(b);
            };

            createBtn('RUN', '#27ae60', (c) => c.togglePause());
            createBtn('WS', '#e67e22', (c) => c.toggleWorkshopPriority());
            // ★ 強制補回 COPY 按鈕
            createBtn('COPY', '#222', () => {
                const txt = document.getElementById('rim-content').innerText;
                navigator.clipboard.writeText(txt);
            });

            const main = document.createElement('div');
            main.style.cssText = "flex: 1; display: flex; flex-direction: column;";
            box.appendChild(main);

            const header = document.createElement('div');
            header.innerText = 'Evolve Auto V4.73';
            header.style.cssText = "background:#00574f; color:#fff; padding:3px; text-align:center;";
            main.appendChild(header);

            const content = document.createElement('div');
            content.id = 'rim-content';
            content.style.cssText = "padding:5px; height:350px; overflow-y:auto; white-space:pre-wrap;";
            main.appendChild(content);
        },
        update: function(html) {
            const el = document.getElementById('rim-content');
            if (el) el.innerHTML = html;
        },
        updatePauseState: function(p) { Utils.log(p ? "Paused" : "Running"); }
    };

    window.EvolveAuto.GameData = GameData;
    window.EvolveAuto.Utils = Utils;
    window.EvolveAuto.UI = UI;
})();