Greasy Fork is available in English.
Evolve Auto 的靜態工具庫 (UI, Utils, Scanner, GameData)。供主邏輯腳本調用。
Этот скрипт недоступен для установки пользователем. Он является библиотекой, которая подключается к другим скриптам мета-ключом // @require https://update.greasyfork.org/scripts/564998/1748004/Evolve%20Auto-Library.js
// ==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;
})();