TheresMore Modifier - Gemini Enhanced Edition

A heavily modified and intelligent version based on Keith's original work. Features a task scheduler, conflict avoidance, and highly automated gameplay loops.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         TheresMore Modifier - Gemini Enhanced Edition
// @name:zh-CN   TheresMore 修改器 - Gemini 增强版
// @namespace    https://github.com/VincentHDLee
// @version      6.0.0
// @description  A heavily modified and intelligent version based on Keith's original work. Features a task scheduler, conflict avoidance, and highly automated gameplay loops.
// @description:zh-CN 在Keith的原版基础上进行了大量重构和智能化升级。拥有任务调度器、冲突规避、智能黑白名单等功能,实现高度自动化的游戏体验。
// @author       VincentHDLee (Original by Keith, Co-authored by Gemini)
// @match        https://theresmoregame.g8hh.com.cn/*
// @match        https://theresmore.thpatch.net/*
// @grant        none
// @license      MIT
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// ==/UserScript==

(function () {
  'use strict';

  // --- 1. 最终版全局配置与知识库 ---
  const ids = {
    resources: ['research','food','wood','stone','gold','tools','copper','iron','cow','horse','luck','mana','building_material','faith','supplies','crystal','steel','saltpetre','natronite', 'lumix'],
    prestige: ['legacy'],
    special: ['relic','coin','tome_wisdom','gem','titan_gift','light']
  };
  const houseList = ['房屋', '市政厅', '宅邸', '住宅区', '发展部', '定居点大厅'];
  const universalBlacklist = ['神殿', '雕像'];

  // [v5.3 最终版] 危险研究关键词黑名单
  const dangerousResearchKeywords = [
      '结束时代', '结束本局游戏', '进入一个新时代', '开启新纪元', '新世界',
      '结束当前周目', '发射', '光荣退休', '点亮亡者之门', '无智之恶','富甲天下'
  ];

  // [v5.3 最终版] 安全主题白名单
  const safeThemePrefixes = ['青铜', '铁', '钢', '精金', '秘银', '经济', '宗教', '战争', '内部', '地窖', '黑暗', '神圣', '召唤', '信号', '长矛', '黄金', '纳红石'];

  // [v5.3 最终版] 已知选择组黑名单
  const knownChoiceGroups = [
      ['赢得民心', '说服贵族'], ['信仰的灯塔', '军事定居点', '生产中心'], ['保护之力', '增量之力'],
      ['展示火焰', '注入火焰'], ['驱逐德鲁伊', '接纳德鲁伊'], ['召唤窃魂者', '修建堡垒'],
      ['专注于发展', '专注于魔法', '专注于研究'], ['召唤尼哈鲁尔', '重建堡垒'], ['渴望神谕', '渴望魔法', '渴望战斗'],
      ['泽尼克斯的统御者', '泽尼克斯的训战官'], ['泽尼克斯的大法师', '泽尼克斯的铸金使'],
      ['黄金工厂', '法力工厂'], ['月明之夜', '全日庆祝活动'], ['光荣退休', '光荣游行']
  ];

  const mainSchedulerInterval = 500;
  const TASK_LEASE_DURATION = 3000;
  const BUILD_CLICKS_BEFORE_SWITCH = 5;

  let autoResearchOn = false, autoUpdateOn = false, autoMagicOn = false, infiniteResourcesOn = false;
  let mainSchedulerTimer = null, infiniteResourcesTimer = null;
  let taskLeaseHolder = null, taskLeaseEndTime = 0, schedulerTurn = 0;
  let buildCounter = 0, buildBlackList = [], isCurseTurn = true;

  // --- 2. UI 渲染 ---
  const link = document.createElement('link'); link.href = 'https://fonts.googleapis.com/css2?family=Cinzel:wght@700&display=swap'; link.rel = 'stylesheet'; document.head.appendChild(link);
  const style = `
        #mod-ui { position: fixed; top: 10px; right: 10px; background: #000; color: #ddd; border: 1px solid #333; padding: 10px; z-index: 10000; width: 320px; box-shadow: 0 0 10px rgba(0,0,0,0.4); font-family: Arial, sans-serif; }
        #mod-ui h3 { margin: 10 0 10px 0; font-size: 18px; color: #d4af37; cursor: move; font-family: 'Cinzel', serif; text-shadow: 1px 1px 2px #000; }
        #mod-ui h4 { margin: 10px 0 5px 0; font-size: 14px; color: #ccc; }
        #mod-ui button { padding: 8px 10px; width: 100%; background-color: #004499; color: #ddd; border: none; cursor: pointer; box-sizing: border-box; margin-bottom: 10px; }
        #mod-ui button.toggle-btn { position: absolute; top: 10px; right: 10px; width: 30px; height: 30px; background-color: #1d1e20; border: none; border-radius: 30%; cursor: pointer; z-index: 10001; }
        #mod-ui button:hover { background-color: #002266; }
        #mod-ui button.toggle-btn:hover { background-color: #333; }
        #mod-ui .function-row { display: flex; flex-direction: column; align-items: stretch; gap: 5px; margin-top: 8px; margin-bottom: 5px; }
        #mod-ui .function-row > button { width: 100%; padding: 5px 10px; margin:0; }
        #mod-ui .locations-container { display: flex; justify-content: space-around; padding: 5px 0 5px 0; background-color: #111; border-radius: 4px; margin-top: 8px; margin-bottom: 5px;}
        #mod-ui .locations-container label { display: flex; align-items: center; gap: 4px; color: #ccc; font-size: 12px;}
        #mod-ui .locations-container input[type=checkbox] { margin: 0; }
    `;
    const styleElement = document.createElement('style'); styleElement.innerText = style; document.head.appendChild(styleElement);
    const uiElement = document.createElement('div'); uiElement.id = 'mod-ui';
    uiElement.innerHTML = `
        <h3>TheresMore Modifier</h3>
        <button id="togglebtn" class="toggle-btn"></button>
        <div id="mod-ui-content">
            <h4>资源修改</h4>
            <button id="toggle-infinite-resources-btn">无限资源: 已关闭</button>
            <h4>功能开关</h4>
            <div class="function-row"><button id="toggleSpeed">瞬间完成: 已关闭</button></div>
            <div id="build-locations" class="locations-container">
                <label><input type="checkbox" name="build-location" value="城市" checked>城市</label>
                <label><input type="checkbox" name="build-location" value="定居点" checked>定居点</label>
                <label><input type="checkbox" name="build-location" value="深渊" checked>深渊</label>
            </div>
            <div class="function-row"><button id="toggleAutoUpdate">自动建造: 已关闭</button></div>
            <div class="function-row"><button id="toggleAutoResearch">自动研究: 已关闭</button></div>
            <div class="function-row"><button id="toggleAutoMagic">自动魔法: 已关闭</button></div>
        </div>
    `;
    document.body.appendChild(uiElement);

  // --- 3. 辅助函数定义 ---
  function toggleBtnText(id, text) { const btn = document.getElementById(id); if (btn) btn.textContent = text; }
  function getGameData() { const r = document.getElementById('root'), k = r ? Object.keys(r).find(k => k.startsWith('__reactContainer$')) : null; return k ? r[k]?.stateNode?.current?.child?.memoizedProps?.MainStore : null; }
  function setAllResourcesToMax() { const MAX_AMOUNT = 9999999999999999; const gameData = getGameData(); if (!gameData?.run?.resources) return; const allResourceIds = [...Object.values(ids).flat()]; gameData.run.resources.forEach(resource => { if (allResourceIds.includes(resource.id)) { resource.value = MAX_AMOUNT; } }); }
  function hasSharedSubstring(strA, strB, length = 2) { const [shortStr, longStr] = strA.length < strB.length ? [strA, strB] : [strB, strA]; for (let i = 0; i <= shortStr.length - length; i++) { const substring = shortStr.substring(i, i + length); if (longStr.includes(substring)) return true; } return false; }

  // [v5.3 最终版] 三层智能审查函数
  function findSafeButtons(buttons) {
      const whitelistedButtons = new Set(buttons.filter(b => safeThemePrefixes.some(prefix => b.textContent.trim().startsWith(prefix))));
      const nonWhitelisted = buttons.filter(b => !whitelistedButtons.has(b));
      const toSkip = new Set();
      const nonWhitelistedTexts = nonWhitelisted.map(b => b.textContent.trim());

      for (const group of knownChoiceGroups) {
          const foundItems = group.filter(item => nonWhitelistedTexts.includes(item));
          if (foundItems.length > 1) {
              foundItems.forEach(itemText => {
                  const btn = nonWhitelisted.find(b => b.textContent.trim() === itemText);
                  if(btn) toSkip.add(btn);
              });
          }
      }

      const remainingForFinalCheck = nonWhitelisted.filter(b => !toSkip.has(b));
      const remainingTexts = remainingForFinalCheck.map(b => b.textContent.trim());

      for (let i = 0; i < remainingTexts.length; i++) {
          for (let j = i + 1; j < remainingTexts.length; j++) {
              if (hasSharedSubstring(remainingTexts[i], remainingTexts[j])) {
                  toSkip.add(remainingForFinalCheck[i]);
                  toSkip.add(remainingForFinalCheck[j]);
              }
          }
      }

      const safeStandaloneButtons = remainingForFinalCheck.filter(b => !toSkip.has(b));
      return [...whitelistedButtons, ...safeStandaloneButtons];
  }

  // --- 4. 任务调度器核心 ---
  function checkSchedulerState() { const anyTaskOn = autoResearchOn || autoUpdateOn || autoMagicOn; if (anyTaskOn && !mainSchedulerTimer) { mainSchedulerTimer = setInterval(runTasks, mainSchedulerInterval); } else if (!anyTaskOn && mainSchedulerTimer) { clearInterval(mainSchedulerTimer); mainSchedulerTimer = null; } }
  function patrolAndClosePopup() { const d = document.querySelector('#headlessui-portal-root'); if (d && d.innerHTML !== '') { const b = d.querySelector('button.absolute') || Array.from(d.querySelectorAll('button')).find(btn => btn.textContent.includes('取消') || btn.textContent.includes('关闭')) || (d.querySelector('.sr-only') && d.querySelector('.sr-only').parentElement); if (b) { b.click(); return true; } } return false; }
  function runTasks() { if (patrolAndClosePopup()) { taskLeaseHolder = 'build'; taskLeaseEndTime = Date.now() + TASK_LEASE_DURATION; if (autoUpdateOn) autoClickBuilding(); schedulerTurn = 1; return; } if (taskLeaseHolder && Date.now() > taskLeaseEndTime) { taskLeaseHolder = null; } if (taskLeaseHolder) { if (taskLeaseHolder === 'research' && autoResearchOn) autoClickResearch(); else if (taskLeaseHolder === 'build' && autoUpdateOn) autoClickBuilding(); else if (taskLeaseHolder === 'magic' && autoMagicOn) autoClickMagic(); return; } const taskOrder = [{ name: 'research', enabled: autoResearchOn, func: autoClickResearch }, { name: 'build', enabled: autoUpdateOn, func: autoClickBuilding }, { name: 'magic', enabled: autoMagicOn, func: autoClickMagic }]; for (let i = 0; i < taskOrder.length; i++) { let currentIndex = (schedulerTurn + i) % taskOrder.length; const task = taskOrder[currentIndex]; if (task.enabled) { if (task.func()) { taskLeaseHolder = task.name; taskLeaseEndTime = Date.now() + TASK_LEASE_DURATION; schedulerTurn = (currentIndex + 1) % taskOrder.length; return; } } } schedulerTurn = (schedulerTurn + 1) % taskOrder.length; }

  // --- 5. 各功能模块逻辑 ---
  function autoResearchChanger() { autoResearchOn = !autoResearchOn; toggleBtnText('toggleAutoResearch', `自动研究: ${autoResearchOn ? '已开启' : '已关闭'}`); checkSchedulerState(); }
  function autoClickResearch() {
      const researchTabButton = Array.from(document.querySelectorAll('#main-tabs > div[role=tablist] > button')).find(tab => tab.textContent.includes('研究'));
      if (!researchTabButton) return false;
      if (researchTabButton.getAttribute('aria-selected') === 'false') { researchTabButton.click(); return true; }
      const panel = document.getElementById(researchTabButton.getAttribute('aria-controls')); if (!panel) return false;
      const researchSubTab = Array.from(panel.querySelectorAll('button[role="tab"]')).find(t => t.textContent.includes('研究'));
      if(researchSubTab && researchSubTab.getAttribute('aria-selected') === 'false') { researchSubTab.click(); return true; }
      let availableButtons = Array.from(panel.querySelectorAll('button.btn:not(.btn-off)'));
      availableButtons = availableButtons.filter(b => {
          const text = b.textContent.trim();
          return !universalBlacklist.some(keyword => text.includes(keyword)) && !dangerousResearchKeywords.some(keyword => text.includes(keyword));
      });
      if (availableButtons.length === 0) return false;
      const buttonsToClick = findSafeButtons(availableButtons);
      if (buttonsToClick.length > 0) { buttonsToClick.forEach(button => button.click()); return true; }
      return false;
  }

  function autoUpdateChanger() { autoUpdateOn = !autoUpdateOn; toggleBtnText('toggleAutoUpdate', `自动建造: ${autoUpdateOn ? '已开启' : '已关闭'}`); buildCounter = 0; checkSchedulerState(); }
  function autoClickBuilding() {
      const buildTab = document.querySelector('#main-tabs > div[role=tablist] > button'); if (!buildTab) return false;
      if (buildTab.getAttribute('aria-selected') === 'false') { buildTab.click(); buildCounter = 0; return true; }
      const panel = document.getElementById(buildTab.getAttribute('aria-controls')); if (!panel) return false;
      const enabledLocations = Array.from(document.querySelectorAll('#build-locations input:checked')).map(cb => cb.value);
      if (enabledLocations.length === 0) return false;
      const subTabsContainer = panel.querySelector('div[role=tablist]'); const hasMultipleSubTabs = subTabsContainer && subTabsContainer.children.length > 1;
      const currentSubTab = subTabsContainer?.querySelector('button[aria-selected=true]');
      if (hasMultipleSubTabs && currentSubTab && !enabledLocations.includes(currentSubTab.textContent.trim())) { const firstValidTab = Array.from(subTabsContainer.children).find(t => enabledLocations.includes(t.textContent.trim())); if (firstValidTab) { firstValidTab.click(); buildCounter = 0; return true; } }
      buildBlackList = universalBlacklist.concat(judgeFood());
      const upgradeables = Array.from(panel.querySelectorAll('button.btn')).filter(n => { if (n.classList.contains('btn-off')) return false; const text = n.textContent.trim(); return !buildBlackList.some(w => text.includes(w)); });
      let didSomeAction = false;
      if (upgradeables.length > 0) { upgradeables.forEach(n => n.click()); buildCounter++; didSomeAction = true; }
      if (hasMultipleSubTabs && (buildCounter >= BUILD_CLICKS_BEFORE_SWITCH || (didSomeAction === false && upgradeables.length === 0))) {
          if (currentSubTab) {
               const allSubTabs = Array.from(subTabsContainer.children); const validSubTabs = allSubTabs.filter(t => enabledLocations.includes(t.textContent.trim()));
               if (validSubTabs.length > 0) {
                   const currentIndexInValid = validSubTabs.indexOf(currentSubTab);
                   const nextIndex = (currentIndexInValid + 1) % validSubTabs.length;
                   validSubTabs[nextIndex].click(); buildCounter = 0; didSomeAction = true;
               }
          }
      }
      return didSomeAction;
  }
  function judgeFood() { const foodTable = document.querySelector('table'); if (!foodTable) return []; const r = Array.from(foodTable.querySelectorAll('tr')).find(n => n.innerText.includes('食物')); if (!r) return []; const c = r.childNodes[2]; if (!c) return []; const v = Number(c.innerText.split('/')[0]); return v < 5 ? houseList : []; }

  function autoMagicChanger() { autoMagicOn = !autoMagicOn; toggleBtnText('toggleAutoMagic', `自动魔法: ${autoMagicOn ? '已开启' : '已关闭'}`); checkSchedulerState(); }
  function autoClickMagic() {
      const magicTabButton = Array.from(document.querySelectorAll('#main-tabs > div[role=tablist] > button')).find(tab => tab.textContent.includes('魔法'));
      if (!magicTabButton) return false;
      if (magicTabButton.getAttribute('aria-selected') === 'false') { magicTabButton.click(); return true; }
      const panel = document.getElementById(magicTabButton.getAttribute('aria-controls')); if (!panel) return false;
      const subTabsContainer = panel.querySelector('div[role=tablist]'); if (!subTabsContainer) return false;
      let didWork = false;
      if (isCurseTurn) {
          const curseTab = Array.from(subTabsContainer.children).find(t => t.textContent.includes('咒语'));
          if (curseTab) { if (curseTab.getAttribute('aria-selected') === 'false') { curseTab.click(); return true; } const cursePanel = document.getElementById(curseTab.getAttribute('aria-controls')); if(!cursePanel) return false; const curseButtons = Array.from(cursePanel.querySelectorAll('button')).filter(b => b.textContent.includes('施放咒语')); if (curseButtons.length > 0) { curseButtons.forEach(b => b.click()); didWork = true; } }
      } else {
          const prayerTab = Array.from(subTabsContainer.children).find(t => t.textContent.includes('祈祷'));
          if (prayerTab) { if (prayerTab.getAttribute('aria-selected') === 'false') { prayerTab.click(); return true; } const prayerPanel = document.getElementById(prayerTab.getAttribute('aria-controls')); if(!prayerPanel) return false; let prayerButtons = Array.from(prayerPanel.querySelectorAll('button.btn:not(.btn-off)')); prayerButtons = prayerButtons.filter(b => !universalBlacklist.some(keyword => b.textContent.includes(keyword))); if(prayerButtons.length > 0) { const buttonsToClick = findSafeButtons(prayerButtons); if (buttonsToClick.length > 0) { buttonsToClick.forEach(b => b.click()); didWork = true; } } }
      }
      isCurseTurn = !isCurseTurn; return didWork;
  }

  let speedOn = false, game;
  function toggleInstantComplete() { speedOn = !speedOn; toggleBtnText('toggleSpeed', `瞬间完成: ${speedOn ? '已开启' : '已关闭'}`); if (!game) game = getGameData(); if (!game) { return; } if (!game.ArmyStore.originalWaitTime) game.ArmyStore.originalWaitTime = game.ArmyStore.waitTime; if (!game.ArmyStore.realDestroyArmy) game.ArmyStore.realDestroyArmy = game.ArmyStore.destroyArmy; if (speedOn) { game.ArmyStore.waitTime = 1; game.ArmyStore.destroyArmy = function (...args) { if (!(args[2] == 'army' && args[3] != !0)) return this.realDestroyArmy(...args); }; } else { game.ArmyStore.waitTime = game.ArmyStore.originalWaitTime; game.ArmyStore.destroyArmy = game.ArmyStore.realDestroyArmy; } }
  function toggleInfiniteResources() { infiniteResourcesOn = !infiniteResourcesOn; toggleBtnText('toggle-infinite-resources-btn', `无限资源: ${infiniteResourcesOn ? '已开启' : '已关闭'}`); if (infiniteResourcesOn) { setAllResourcesToMax(); infiniteResourcesTimer = setInterval(setAllResourcesToMax, 1500); } else { clearInterval(infiniteResourcesTimer); infiniteResourcesTimer = null; } }

  // --- 6. 事件监听器绑定 ---
  function init() {
      const upIcon = `<svg width="12" height="12" viewBox="0 0 24 24"><path fill="none" stroke="#ddd" stroke-width="4" d="M4 16l8-8 8 8"/></svg>`;
      const downIcon = `<svg width="12" height="12" viewBox="0 0 24 24"><path fill="none" stroke="#ddd" stroke-width="4" d="M4 8l8 8 8-8"/></svg>`;
      const findAndBind = (id, handler) => document.getElementById(id)?.addEventListener('click', handler);
      findAndBind('toggleAutoResearch', autoResearchChanger);
      findAndBind('toggleAutoUpdate', autoUpdateChanger);
      findAndBind('toggleSpeed', toggleInstantComplete);
      findAndBind('toggle-infinite-resources-btn', toggleInfiniteResources);
      findAndBind('toggleAutoMagic', autoMagicChanger);
      const toggleVisibilityBtn = document.getElementById('togglebtn');
      toggleVisibilityBtn.innerHTML = upIcon;
      findAndBind('togglebtn', () => { const c = document.querySelector('#mod-ui-content'), i = c.style.display === 'none'; c.style.display = i ? 'block' : 'none'; toggleVisibilityBtn.innerHTML = i ? upIcon : downIcon; });
      let drag_pos1 = 0, drag_pos2 = 0, drag_pos3 = 0, drag_pos4 = 0;
      uiElement.querySelector('h3').onmousedown = (e) => { e = e || window.event; e.preventDefault(); drag_pos3 = e.clientX; drag_pos4 = e.clientY; document.onmouseup = () => { document.onmouseup = null; document.onmousemove = null; }; document.onmousemove = (ev) => { ev = ev || window.event; ev.preventDefault(); drag_pos1 = drag_pos3 - ev.clientX; drag_pos2 = drag_pos4 - ev.clientY; drag_pos3 = ev.clientX; drag_pos4 = ev.clientY; uiElement.style.top = (uiElement.offsetTop - drag_pos2) + "px"; uiElement.style.left = (uiElement.offsetLeft - drag_pos1) + "px"; }; };
  }
  setTimeout(init, 2000);

})();