commit 0a1256b47afb9b4d782062c9fba0ed8d8f549e81 Author: liooil Date: Wed Oct 8 12:32:42 2025 +0800 feat: init repo diff --git a/theresmore-helper.js b/theresmore-helper.js new file mode 100644 index 0000000..1ab5462 --- /dev/null +++ b/theresmore-helper.js @@ -0,0 +1,736 @@ +// ==UserScript== +// @name Theresmore 助手 +// @namespace http://tampermonkey.net/ +// @version 20240907 +// @license MIT +// @description Theresmore 助手, 包含 加速, 自动升级建筑, 一键研究, 一键施法, 批量探索, 一键攻击 等功能 +// @author liooil +// @match https://www.theresmoregame.com/play/ +// @match https://theresmoregame.g8hh.com/ +// @match https://theresmoregame.g8hh.com.cn/ +// @icon https://www.theresmoregame.com/play/favicon.ico +// ==/UserScript== + +const customSpeed = 60; //自定义倍速,可自行修改,默认10倍速,修改后刷新游戏生效(不可动态修改 否则会出错) +const 军队加速 = true; +const 进攻失败不损失部队 = true; +const 一键祈祷 = true; +const 将军数量跟随建筑数量 = true; +const 扫描间隔 = 1000 * 2; + +performance.realNow = performance.now; +performance.now = () => { + return performance.realNow() * customSpeed; +}; +/** 建筑自动升级采用白名单 */ +let updateStore = getUpdateStore(); +let oldList = []; +/** 研究/人口手动升级采用黑名单 */ +let blackList = getBlackList(); +let 自动升级建筑 = true; +let 自动升级所有标签页面 = false; +let 自动结盟 = false; +let 咒语已全部激活 = false; +let 搜索次数 = 0; + +function getUpdateStore() { + const value = localStorage.getItem("updateStore"); + if (value) { + return new Set(JSON.parse(value)); + } + return new Set([ + "钢铁厂", + "秘密会议", + "宗教部", + "修道院", + "有人值守仓库", + "大型库房", + "远古保险库", + "储物间", + "圣谷", + "炼金实验室", + "建筑工区域", + "亡者兽群", + "巨型工作区", + "锯木厂", + "铸造厂", + "天文台", + "大学", + "登攀者世界图书馆", + "智慧大厅", + "学校", + "诸神的机器", + "马厩", + "难民区组件", + "证券交易所组件", + "钢铁宫殿组件", + "联盟大使馆", + "内政部", + "粮仓", + "伐木工营地", + "好运之森", + "魔法环", + "战争部", + "魔法塔", + "献祭圣坛", + "神之荣耀", + "寺庙", + "物质转化器", + "工匠作坊", + "市场", + "卡纳瓦贸易站", + "农场", + "住宅区", + "法力深井组件", + "市政厅", + "宅邸", + "矿井", + "工匠公会", + "木匠工坊", + "采石场", + "石匠坊", + "封地", + "食品杂货店", + "钠红石精炼厂", + "金库", + "城墙组件", + "工业厂房", + "光之城组件", + "海湾区组件", + "银行", + "信用社", + "研究工厂", + "壁垒组件", + "磐石巨墙组件", + "大型仓库", + "钠红石仓库", + "存储设施", + "精神花园", + "伊甸园", + "普通房屋", + "军官训练场", + "攻城机器厂", + "雷区", + "军事学院", + "钠红石气球", + "弩炮", + "新兵训练中心", + "兵营", + "炮兵靶场", + "守望者前哨", + "超级大炮组件", + "钠红石护盾", + "亡者大厅", + "撒尔喀特帝国 ", + "鳞岩部落 ", + "恩索诸众 ", + "岛屿前哨", + "定居点大厅", + "棚屋", + "建筑工联合体", + "工匠联合体", + "朝觐者营地", + "炼金联合体", + "旧神教堂", + "发展部", + "火车站", + "物流中心", + "法力提取器", + "大型棚屋", + "战壕", + "工厂", + "自动化联合体组件", + "精灵营地", + "书籍", + "灵魂", + "法力反应堆", + "应许之地组件", + "英雄事迹大厅组件", + "税收检查站", + "精灵村庄", + "法力之塔组件", + "丰饶之谷", + "栅栏组件", + "市中心组件", + "超大集市组件", + "大教堂组件", + "自由思想家学院组件", + "繁荣之泉", + "精炼厂", + "码头", + "海关", + "庄园", + "美德雕像组件", + "佣兵前哨", + "凯旋门组件", + "法力场", + "魔法工坊", + "魔法马厩", + "门之解密", + ]); +} +function saveUpdateStore(updateStore) { + localStorage.setItem("updateStore", JSON.stringify([...updateStore])); +} +function getBlackList() { + const value = localStorage.getItem("blackList"); + if (value) { + return new Set(JSON.parse(value)); + } + return new Set(); +} +function saveBlackList(list) { + localStorage.setItem("blackList", JSON.stringify([...list])); +} + +let timer = setInterval(() => { + //setInterval摆烂 + if (document.querySelector("#main-tabs")) { + clearInterval(timer); + const MainStore = Object.values(document.querySelector("#main-tabs"))[1] + .children._owner.stateNode.props.MainStore; + window.MainStore = MainStore; //暴露存储变量 + console.log(MainStore); + customFunction(MainStore); + } +}, 100); + +const handlers = []; + +setInterval(() => { + const tabListNode = document + .querySelector("#main-tabs") + .querySelector(`div[role=tablist]`); + for (let page = 0; page < tabListNode.childNodes.length; page++) { + const tabNode = tabListNode.childNodes[page]; + const handler = handlers[page]; + if (tabNode && tabNode.getAttribute("aria-selected") === "true") { + const id = tabNode.getAttribute("aria-controls"); + const containerNode = document.getElementById(id); + handler?.(tabNode, containerNode); + } + } + + // 保存 + registerSave(); + + // 将军/指挥官数量跟随军事学院数量 + const commnder = MainStore.ArmyStore.visibleArmy.find( + (e) => e.id === "commander", + ); + const general = MainStore.ArmyStore.visibleArmy.find( + (e) => e.id === "general", + ); + const military_academy = MainStore.run.buildings.find( + (v) => v.id === "military_academy", + ); + if (commnder && general && military_academy) { + general.cap = military_academy.value; + commnder.cap = military_academy.value; + } + // 战略家数量跟随魔法塔数量 + const strategist = MainStore.ArmyStore.visibleArmy.find( + (e) => e.id === "strategist", + ); + const magical_tower = MainStore.run.buildings.find( + (v) => v.id === "magical_tower", + ); + if (strategist && magical_tower) { + strategist.cap = magical_tower.value; + } + // 法力堡垒数量跟随魔法工坊 +}, 扫描间隔); + +// 建筑 +handlers[0] = async (tabNode, containerNode) => { + if (containerNode.firstChild.id !== "auto-upgrade") { + const btn = document.createElement("button"); + btn.id = "auto-upgrade"; + btn.classList.add("btn", "w-full", "mb-2"); + btn.textContent = "A"; + if (自动升级建筑) { + btn.classList.add("btn-green"); + } else { + btn.classList.add("btn-gray"); + } + btn.onclick = () => { + 自动升级建筑 = !自动升级建筑; + if (自动升级建筑) { + btn.classList.remove("btn-gray"); + btn.classList.add("btn-green"); + } else { + btn.classList.remove("btn-green"); + btn.classList.add("btn-gray"); + } + }; + containerNode.prepend(btn); + } + const subTabNodes = containerNode.querySelector(`div[role=tablist]`); + if (subTabNodes) { + if (subTabNodes.lastChild.textContent !== "A") { + const btn = document.createElement("button"); + btn.classList.add("btn", "mb-2"); + btn.id = "auto-upgrade-all"; + btn.textContent = "A"; + if (自动升级所有标签页面) { + btn.classList.add("btn-green"); + } else { + btn.classList.add("btn-gray"); + } + btn.onclick = () => { + 自动升级所有标签页面 = !自动升级所有标签页面; + if (自动升级所有标签页面) { + btn.classList.remove("btn-gray"); + btn.classList.add("btn-green"); + } else { + btn.classList.remove("btn-green"); + btn.classList.add("btn-gray"); + } + }; + subTabNodes.appendChild(btn); + } + } + + const list = containerNode.querySelectorAll(`button.btn`); + let newList = []; + for (const node of list) { + if (node.id === "auto-upgrade" || node.id === "auto-upgrade-all") continue; + const name = node.firstChild.textContent; + newList.push(node); + if (!oldList.includes(node)) { + const button = document.createElement("button"); + button.classList.add( + "absolute", + "-bottom-0", + "right-0", + "inline-block", + "py-px", + "px-2", + "rounded-full", + "font-bold", + "text-xs", + ); + button.textContent = "A"; + if (updateStore.has(name)) { + button.classList.add("text-green-600"); + } else { + button.classList.add("text-gray-600"); + } + button.onclick = (ev) => { + ev.stopPropagation(); + if (updateStore.has(name)) { + updateStore.delete(name); + button.classList.remove("text-green-600"); + button.classList.add("text-gray-600"); + } else { + updateStore.add(name); + button.classList.remove("text-gray-600"); + button.classList.add("text-green-600"); + } + saveUpdateStore(updateStore); + }; + node.appendChild(button); + } + } + oldList = newList; + + if (自动升级建筑) { + if (自动升级所有标签页面) { + let currentSubTab = subTabNodes.querySelector( + `button[aria-selected=true]`, + ); // 当前选中的子tab页 + for (let i = 0; i < subTabNodes.childElementCount; ++i) { + const subTabNode = subTabNodes.childNodes[i]; + if ( + subTabNode === currentSubTab || + subTabNode.id === "auto-upgrade-all" + ) + continue; + subTabNode.click(); + await sleep(100); + for (const node of containerNode.querySelectorAll(`button.btn`)) { + if (node.id === "auto-upgrade" || node.id === "auto-upgrade-all") + continue; + const name = node.firstChild.textContent; + if ( + updateStore.has(name) && + !node.classList.value.includes("btn-off") + ) { + node.click(); + } + } + } + currentSubTab.click(); + await sleep(100); + } + for (const node of containerNode.querySelectorAll(`button.btn`)) { + if (node.id === "auto-upgrade" || node.id === "auto-upgrade-all") + continue; + const name = node.firstChild.textContent; + if (updateStore.has(name) && !node.classList.value.includes("btn-off")) { + node.click(); + } + } + } +}; + +// 研究 +handlers[1] = (tabNode, containerNode) => { + const id = "research-all"; + + const list = containerNode.querySelectorAll(`button.btn`); + for (const node of list) { + if (node.id === id) continue; + const name = node.firstChild.textContent; + if (!node.querySelector("button.btn")) { + const button = document.createElement("button"); + button.classList.add( + "-bottom-0", + "right-0", + "inline-block", + "py-px", + "px-2", + "rounded-full", + "font-bold", + "text-xs", + ); + button.textContent = "A"; + if (blackList.has(name)) { + button.classList.add("text-gray-600"); + } else { + button.classList.add("text-green-600"); + } + button.onclick = (ev) => { + ev.stopPropagation(); + if (blackList.has(name)) { + blackList.delete(name); + button.classList.remove("text-gray-600"); + button.classList.add("text-green-600"); + } else { + blackList.add(name); + button.classList.remove("text-green-600"); + button.classList.add("text-gray-600"); + } + saveBlackList(blackList); + }; + node.appendChild(button); + } + } + + if (containerNode.firstChild.textContent === "ALL") return; + const btn = document.createElement("button"); + btn.id = id; + btn.classList.add("btn", "btn-green", "w-full", "mb-2"); + btn.textContent = "ALL"; + btn.onclick = () => { + for (const node of containerNode.querySelectorAll(`button.btn`)) { + if (node.id === id) continue; + const name = node.firstChild.textContent; + if (blackList.has(name)) continue; + if (!node.classList.value.includes("btn-off")) { + node.click(); + } + } + }; + containerNode.prepend(btn); +}; + +// 人口 +handlers[2] = (tabNode, containerNode) => { + if (containerNode.firstChild.id !== "auto-employ") { + const btn = document.createElement("button"); + btn.id = "auto-employ"; + btn.classList.add("btn", "btn-green", "w-full", "mb-2"); + btn.textContent = "ALL"; + btn.onclick = () => { + for (const jobTitle of containerNode.querySelectorAll(`h5`)) { + const job = jobTitle.textContent; + if (!blackList.has(job)) { + jobTitle.parentNode.parentNode + .querySelector(`button.btn.btn-green.shadow-none`) + ?.click(); + } + } + }; + containerNode.prepend(btn); + } + for (const jobTitle of containerNode.querySelectorAll(`h5`)) { + const job = jobTitle.textContent; + let btn; + if (jobTitle.parentNode.firstChild.tagName === "BUTTON") { + btn = jobTitle.parentNode.firstChild; + } else { + btn = document.createElement("button"); + btn.textContent = "A"; + if (blackList.has(job)) { + btn.classList.add("text-gray-600"); + } else { + btn.classList.add("text-green-600"); + } + btn.onclick = () => { + if (blackList.has(job)) { + blackList.delete(job); + btn.classList.remove("text-gray-600"); + btn.classList.add("text-green-600"); + } else { + blackList.add(job); + btn.classList.remove("text-green-600"); + btn.classList.add("text-gray-600"); + } + }; + jobTitle.parentNode.prepend(btn); + } + } +}; + +// 魔法 +handlers[3] = (tabNode, containerNode) => { + const subTabNodes = containerNode.querySelector(`div[role=tablist]`); + const currentSubTab = subTabNodes.querySelector(`button[aria-selected=true]`); // 当前选中的子tab页 + if (currentSubTab.textContent.includes("咒语")) { + 咒语已全部激活 = [...containerNode.querySelectorAll(`button.btn`)].every( + (node) => node.id === "auto-pray" || node.textContent === "取消咒语", + ); + if (containerNode.firstChild.id === "auto-pray") { + containerNode.firstChild.remove(); + } + const btn = document.createElement("button"); + btn.id = "auto-pray"; + btn.classList.add("btn", "btn-green", "w-full", "mb-2"); + btn.textContent = 咒语已全部激活 ? "全部取消" : "全部释放"; + btn.onclick = () => { + const list = containerNode.querySelectorAll(`button.btn`); + for (const node of list) { + if (node.id === "auto-pray" || node.classList.value.includes("btn-off")) + continue; + if (!咒语已全部激活 && node.textContent === "取消咒语") continue; + node.click(); + } + 咒语已全部激活 = !咒语已全部激活; + btn.textContent = 咒语已全部激活 ? "全部取消" : "全部释放"; + }; + containerNode.prepend(btn); + } else if (currentSubTab.textContent.includes("祈祷")) { + if (containerNode.firstChild.id === "auto-pray") { + containerNode.firstChild.remove(); + } + if (containerNode.querySelector(`button.btn`)) { + const btn = document.createElement("button"); + btn.id = "auto-pray"; + btn.classList.add("btn", "btn-green", "w-full", "mb-2"); + btn.textContent = "全部祈祷"; + btn.onclick = () => { + const list = containerNode.querySelectorAll(`button.btn`); + for (const node of list) { + if ( + node.id === "auto-pray" || + node.classList.value.includes("btn-off") + ) + continue; + node.click(); + } + }; + containerNode.prepend(btn); + } + } +}; + +// 军队 +handlers[4] = async (tabNode, containerNode) => { + const subTabNodes = containerNode.querySelector(`div[role=tablist]`); + const currentSubTab = subTabNodes.querySelector(`button[aria-selected=true]`); // 当前选中的子tab页 + if (currentSubTab.firstChild.textContent === "探索") { + const exploreBtn = containerNode.querySelector(`button.btn.btn-blue`); + if (exploreBtn) { + if (exploreBtn.lastChild.id !== "auto-explore") { + const btn = document.createElement("button"); + btn.id = "auto-explore"; + btn.textContent = "X10"; + btn.classList.add( + "absolute", + "-bottom-0", + "right-0", + "inline-block", + "py-px", + "px-2", + "rounded-full", + "font-bold", + "text-xs", + ); + btn.onclick = (ev) => { + ev.stopPropagation(); + 搜索次数 += 10; + }; + exploreBtn.appendChild(btn); + } + if (搜索次数 > 0) { + exploreBtn.click(); + 搜索次数--; + } + } + } else if (currentSubTab.firstChild.textContent === "攻击") { + const chooseBtn = containerNode.querySelector(`button.btn.btn-dark`); + if (chooseBtn && chooseBtn.firstChild.textContent !== "没有敌人可供攻击") { + if (chooseBtn.lastChild.id !== "auto-attack") { + const btn = document.createElement("button"); + btn.id = "auto-attack"; + btn.textContent = "ALL"; + btn.classList.add( + "absolute", + "-bottom-0", + "right-0", + "inline-block", + "py-px", + "px-2", + "rounded-full", + "font-bold", + "text-xs", + ); + btn.onclick = async (ev) => { + ev.stopPropagation(); + chooseBtn.click(); + await sleep(100); + const dialogNode = document.querySelector("#headlessui-portal-root"); + const enemies = []; + let firstEnemy; + for (const node of dialogNode.querySelectorAll(`h5`)) { + const enemy = node.textContent; + if (!firstEnemy) { + firstEnemy = node; + } else { + enemies.push(enemy); + } + } + if (firstEnemy) { + firstEnemy.click(); + await sleep(100); + const attackBtn = containerNode.querySelector(`button.btn.btn-red`); + attackBtn.click(); + await sleep(2000); + } else { + dialogNode.querySelector(".sr-only").parentNode.click(); + await sleep(100); + } + await sleep(100); + for (const enemy of enemies) { + await attack(containerNode, enemy); + await sleep(2000); + } + }; + chooseBtn.appendChild(btn); + } + + if (MainStore.ArmyStore._fight("oracle")) { + chooseBtn.classList.remove("dark:border-red-700"); + chooseBtn.classList.add("dark:border-green-700"); + } else { + chooseBtn.classList.remove("dark:border-green-700"); + chooseBtn.classList.add("dark:border-red-700"); + } + } + } +}; + +async function attack(containerNode, name) { + const chooseBtn = containerNode.querySelector(`button.btn.btn-dark`); + chooseBtn.click(); + await sleep(100); + const dialogNode = document.querySelector("#headlessui-portal-root"); + for (const node of dialogNode.querySelectorAll(`h5`)) { + if (node.textContent === name) { + node.click(); + break; + } + } + await sleep(100); + const attackBtn = containerNode.querySelector(`button.btn.btn-red`); + attackBtn.click(); +} + +// 外交 +handlers[5] = (tabNode, containerNode) => { + if (containerNode.firstChild.id !== "auto-improve") { + const btn = document.createElement("button"); + btn.id = "auto-improve"; + btn.classList.add("btn", "w-full", "mb-2"); + btn.textContent = "A"; + if (自动结盟) { + btn.classList.add("btn-green"); + } else { + btn.classList.add("btn-gray"); + } + btn.onclick = () => { + 自动结盟 = !自动结盟; + if (自动结盟) { + btn.classList.remove("btn-gray"); + btn.classList.add("btn-green"); + } else { + btn.classList.remove("btn-green"); + btn.classList.add("btn-gray"); + } + }; + containerNode.prepend(btn); + } + + const list = containerNode.querySelectorAll(`button.btn`); + if (自动结盟) { + for (const node of list) { + if (node.id === "auto-improve") continue; + if (node.classList.value.includes("btn-off")) continue; + if ( + node.textContent.includes("派出代表团") || + node.textContent.includes("改善关系") || + node.textContent.includes("联盟") + ) { + node.click(); + } + } + } +}; + +// 保存 +function registerSave() { + const dialogNode = document.querySelector("#headlessui-portal-root"); + if (dialogNode?.querySelector("h3")?.textContent !== "设置") return; + if (dialogNode.querySelector(`#save_url`)) return; + const importBtn = dialogNode.querySelector(`button.btn.btn-green`); + if (!importBtn) return; + const urlIpt = document.createElement("input"); + urlIpt.id = "save_url"; + urlIpt.type = "url"; + importBtn.parentNode.appendChild(urlIpt); + const uploadBtn = document.createElement("button"); + uploadBtn.onclick = async () => { + const saveString = MainStore.SettingsStore._getSaveString(); + await fetch(urlIpt.value, { method: "PUT", body: saveString }); + }; + importBtn.parentNode.appendChild(uploadBtn); + const downloadBtn = document.createElement("button"); + downloadBtn.onclick = async () => { + const res = await fetch(urlIpt.value); + const saveString = await res.text(); + MainStore.SettingsStore.loadFromText(saveString); + }; + importBtn.parentNode.appendChild(downloadBtn); +} + +function customFunction(MainStore) { + //额外功能 + if (军队加速) { + MainStore.ArmyStore.waitTime /= customSpeed; + } + if (进攻失败不损失部队) { + MainStore.ArmyStore.realDestroyArmy = MainStore.ArmyStore.destroyArmy; + MainStore.ArmyStore.destroyArmy = function (...args) { + if (!(args[2] == "army" && args[3] != !0)) { + return this.realDestroyArmy(...args); + } + }; + } +} + +function sleep(time) { + return new Promise((resolve) => { + setTimeout(resolve, time); + }); +}