// ==UserScript== // @name Evolve Helper // @namespace http://tampermonkey.net/ // @version 2024-12-08_11-55 // @description accerlate and automate // @grant none // @author liooil // @homepage https://xiteng.site/git/liooil/evolve-helper // @match https://pmotschmann.github.io/Evolve/ // @icon https://www.google.com/s2/favicons?sz=64&domain=github.io // @updateURL https://xiteng.site/git/liooil/evolve-helper/raw/branch/main/evolve-helper.js // @downloadURL https://xiteng.site/git/liooil/evolve-helper/raw/branch/main/evolve-helper.js // ==/UserScript== let autoIds = getStrSet("autoIds") ?? new Set(); let vueMethod; let scanTimer = setInterval(scan, getInt("scanInterval") ?? 1000); let autoTimer = setInterval(auto, getInt("autoInterval") ?? 100); function scan() { const versionLogEl = document.getElementById("versionLog"); if (versionLogEl) { menuBtn(versionLogEl, "scanInterval", "int", 1000, (val) => { clearInterval(scanTimer); scanTimer = setInterval(scan, val); }); menuBtn(versionLogEl, "autoInterval", "int", 100, (val) => { clearInterval(autoTimer); autoTimer = setInterval(scan, val); }); menuBtn(versionLogEl, "customSpeed", "int", 1, () => { if (!vueMethod._data.s.pause) { fromScript = true; vueMethod.pause(); } }); menuBtn(versionLogEl, "autoRun", "bool", true); menuBtn(versionLogEl, "autoAll", "bool", false); vueMethod = document.getElementById("topBar").__vue__; } for (const node of document.querySelectorAll("div.action.vb")) { autoBtn(node, node.firstChild); } for (const node of document.querySelectorAll("div.resource.crafted")) { if (node.style.display === "none") continue; autoBtn(node, node.firstChild); } for (const node of document.getElementById("market")?.querySelectorAll("div.market-item") ?? []) { if (node.style.display === "none") continue; for (const type of ['buy', 'sell']) { const span = node.querySelector('span.' + type); if (!span) continue; autoBtn(span, span, node.id + '-' + type); } } } function auto() { if (!getBool("autoRun")) return; if (getBool("autoAll")) { for (const node of document.querySelectorAll("div.action.vb")) { if (node.classList.contains("cna")) continue; const a = el.querySelector("a.button"); if (a) a.click(); } } for (const id of autoIds) { for (const type of ['buy', 'sell']) { if (id.endsWith('-' + type)) { const el = document.getElementById(id.slice(0, -type.length - 1)); if (!el) continue; const prev = el.querySelector('span.' + type); if (!prev) continue; prev.nextElementSibling.click(); } } const el = document.getElementById(id); if (!el || el.classList.contains("cna")) continue; /** @type {HTMLElement} */ const a = el.querySelector("a.button,a:has(span[data-val='A'])"); if (a) a.click(); } } /** * @param {string} key * @param {number} defaultVal */ function useInt(key, defaultVal) { let text = localStorage.getItem(key); let val = text ? parseInt(text) : defaultVal; return [ () => val, /** @type {(val: number) => void} */ (v) => { val = v; localStorage.setItem(key, v.toString()); } ]; } /** * @param {string} key */ function getInt(key) { const val = localStorage.getItem(key); if (val !== null) return parseFloat(val); } /** * @param {string} key * @param {number} val */ function setInt(key, val) { localStorage.setItem(key, val) } /** * @param {string} key */ function getStrSet(key) { const val = localStorage.getItem(key); if (val !== null) return new Set(val.split(",")); } /** * @param {string} key * @param {Set} val */ function setStrSet(key, val) { localStorage.setItem(key, [...val.keys()].join(",")) } /** * @param {string} key */ function getBool(key) { const val = localStorage.getItem(key); if (val !== null) return val === "true"; } /** * @param {string} key * @param {boolean} val */ function setBool(key, val) { localStorage.setItem(key, val.toString()); } /** * @param {HTMLElement} nextElementSibling * @param {string} id * @param {"int" | "bool"} type * @param {number} defaultVal * @param {(val: number) => void} cb */ function menuBtn(nextElementSibling, id, type, defaultVal, cb) { if (document.getElementById(id)) return; const el = document.createElement("span"); el.id = id; el.classList.add("version"); el.style.cursor = "pointer"; el.textContent = `${id}=${getInt(id) ?? defaultVal}`; el.onclick = () => { let val; if (type === "bool") { val = !(getBool(id) ?? defaultVal); setBool(id, val); } else { let input = prompt(`${id}=`, `${getInt(id) ?? defaultVal}`); if (input === null) return; val = parseInt(input) setInt(id, input); } el.textContent = `${id}=${val}`; cb?.(val); } nextElementSibling.before(el); return el; } /** * add auto button * @param {HTMLElement} root * @param {HTMLElement?} parent */ function autoBtn(root, parent = root, id = root.id) { /** @type {HTMLButtonElement} */ let auto = root.querySelector(".auto"); if (!auto) { auto = parent.appendChild(document.createElement("span")); auto.classList.add("auto"); auto.textContent = "A"; auto.onclick = (e) => { e.preventDefault(); e.stopPropagation(); if (autoIds.has(id)) { autoIds.delete(id); auto.style.color = "gray"; } else { autoIds.add(id); auto.style.color = "green"; } setStrSet("autoIds", autoIds); } } if (autoIds.has(id)) { auto.style.color = "green"; } else { auto.style.color = "gray"; } } let fromScript = false; const oldPost = Worker.prototype.postMessage; Worker.prototype.postMessage = async function (...args) { let that = this async function hookPost() { if (args[0].period) { args[0].period = args[0].period / (getInt("customSpeed") ?? 1) } oldPost.apply(that, args) } let hookResult = await hookPost() if (fromScript) { vueMethod.pause() fromScript = false } return hookResult }