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.

// ==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);

})();