// ==UserScript== // @name MWI-AutoCombat // @name:zh-CN MWI自动战斗助手 // @namespace http://tampermonkey.net/ // @version 1.3.1 // @description Auto-manage game queue(自动9战) // @author XIxixi297 // @license CC-BY-NC-SA-4.0 // @match https://www.milkywayidle.com/* // @match https://test.milkywayidle.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com // @downloadURL https://update.greasyfork.icu/scripts/538205/MWI-AutoCombat.user.js // @updateURL https://update.greasyfork.icu/scripts/538205/MWI-AutoCombat.meta.js // ==/UserScript== /** * 关于使用本插件可能存在的脚本行为说明: * * 《游戏规则》 * * 4.机器人、脚本和扩展 * * 4.1禁止机器人: 请勿使用任何自动化程序代替你操作游戏。 * 4.2脚本和扩展: 任何脚本或扩展程序都不得为玩家执行任何操作(向服务器发送任何请求), 仅限使用于显示信息或改进用户界面 (例如: 显示战斗摘要、跟踪掉落、将按钮移动到不同位置)。 * * 请仔细阅读游戏规则条款后,再选择是否安装使用本插件,谢谢! */ (function (workerScript) { if (!/MSIE 10/i.test(navigator.userAgent)) { try { var blob = new Blob(["var fakeIdToId = {};\nonmessage = function (event) {\n\tvar data = event.data,\n\t\tname = data.name,\n\t\tfakeId = data.fakeId,\n\t\ttime;\n\tif(data.hasOwnProperty('time')) {\n\t\ttime = data.time;\n\t}\n\tswitch (name) {\n\t\tcase 'setInterval':\n\t\t\tfakeIdToId[fakeId] = setInterval(function () {\n\t\t\t\tpostMessage({fakeId: fakeId});\n\t\t\t}, time);\n\t\t\tbreak;\n\t\tcase 'clearInterval':\n\t\t\tif (fakeIdToId.hasOwnProperty (fakeId)) {\n\t\t\t\tclearInterval(fakeIdToId[fakeId]);\n\t\t\t\tdelete fakeIdToId[fakeId];\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'setTimeout':\n\t\t\tfakeIdToId[fakeId] = setTimeout(function () {\n\t\t\t\tpostMessage({fakeId: fakeId});\n\t\t\t\tif (fakeIdToId.hasOwnProperty (fakeId)) {\n\t\t\t\t\tdelete fakeIdToId[fakeId];\n\t\t\t\t}\n\t\t\t}, time);\n\t\t\tbreak;\n\t\tcase 'clearTimeout':\n\t\t\tif (fakeIdToId.hasOwnProperty (fakeId)) {\n\t\t\t\tclearTimeout(fakeIdToId[fakeId]);\n\t\t\t\tdelete fakeIdToId[fakeId];\n\t\t\t}\n\t\t\tbreak;\n\t}\n}\n"]); workerScript = window.URL.createObjectURL(blob); } catch (error) { /* Blob is not supported, use external script instead */ } } var worker, fakeIdToCallback = {}, lastFakeId = 0, maxFakeId = 0x7FFFFFFF; if (typeof (Worker) !== 'undefined') { function getFakeId() { do { if (lastFakeId == maxFakeId) { lastFakeId = 0; } else { lastFakeId++; } } while (fakeIdToCallback.hasOwnProperty(lastFakeId)); return lastFakeId; } try { worker = new Worker(workerScript); window.setInterval = function (callback, time) { var fakeId = getFakeId(); fakeIdToCallback[fakeId] = { callback: callback, parameters: Array.prototype.slice.call(arguments, 2) }; worker.postMessage({ name: 'setInterval', fakeId: fakeId, time: time }); return fakeId; }; window.clearInterval = function (fakeId) { if (fakeIdToCallback.hasOwnProperty(fakeId)) { delete fakeIdToCallback[fakeId]; worker.postMessage({ name: 'clearInterval', fakeId: fakeId }); } }; window.setTimeout = function (callback, time) { var fakeId = getFakeId(); fakeIdToCallback[fakeId] = { callback: callback, parameters: Array.prototype.slice.call(arguments, 2), isTimeout: true }; worker.postMessage({ name: 'setTimeout', fakeId: fakeId, time: time }); return fakeId; }; window.clearTimeout = function (fakeId) { if (fakeIdToCallback.hasOwnProperty(fakeId)) { delete fakeIdToCallback[fakeId]; worker.postMessage({ name: 'clearTimeout', fakeId: fakeId }); } }; worker.onmessage = function (event) { var data = event.data, fakeId = data.fakeId, request, parameters, callback; if (fakeIdToCallback.hasOwnProperty(fakeId)) { request = fakeIdToCallback[fakeId]; callback = request.callback; parameters = request.parameters; if (request.hasOwnProperty('isTimeout') && request.isTimeout) { delete fakeIdToCallback[fakeId]; } } if (typeof (callback) === 'string') { try { callback = new Function(callback); } catch (error) { console.log('HackTimer.js by turuslan: Error parsing callback code string: ', error); } } if (typeof (callback) === 'function') { callback.apply(window, parameters); } }; worker.onerror = function (event) { console.log(event); }; } catch (error) { console.log('HackTimer.js by turuslan: Initialisation failed'); console.error(error); } } else { console.log('HackTimer.js by turuslan: Initialisation failed - HTML5 Web Worker is not supported'); } })('HackTimerWorker.js'); (function () { 'use strict'; // 任务数据字典 const hrids = [ "/actions/combat/fly", "/actions/combat/rat", "/actions/combat/skunk", "/actions/combat/porcupine", "/actions/combat/slimy", "/actions/combat/smelly_planet", "/actions/combat/smelly_planet_elite", "/actions/combat/frog", "/actions/combat/snake", "/actions/combat/swampy", "/actions/combat/alligator", "/actions/combat/swamp_planet", "/actions/combat/swamp_planet_elite", "/actions/combat/sea_snail", "/actions/combat/crab", "/actions/combat/aquahorse", "/actions/combat/nom_nom", "/actions/combat/turtle", "/actions/combat/aqua_planet", "/actions/combat/aqua_planet_elite", "/actions/combat/jungle_sprite", "/actions/combat/myconid", "/actions/combat/treant", "/actions/combat/centaur_archer", "/actions/combat/jungle_planet", "/actions/combat/jungle_planet_elite", "/actions/combat/gobo_stabby", "/actions/combat/gobo_slashy", "/actions/combat/gobo_smashy", "/actions/combat/gobo_shooty", "/actions/combat/gobo_boomy", "/actions/combat/gobo_planet", "/actions/combat/gobo_planet_elite", "/actions/combat/eye", "/actions/combat/eyes", "/actions/combat/veyes", "/actions/combat/planet_of_the_eyes", "/actions/combat/planet_of_the_eyes_elite", "/actions/combat/novice_sorcerer", "/actions/combat/ice_sorcerer", "/actions/combat/flame_sorcerer", "/actions/combat/elementalist", "/actions/combat/sorcerers_tower", "/actions/combat/sorcerers_tower_elite", "/actions/combat/gummy_bear", "/actions/combat/panda", "/actions/combat/black_bear", "/actions/combat/grizzly_bear", "/actions/combat/polar_bear", "/actions/combat/bear_with_it", "/actions/combat/bear_with_it_elite", "/actions/combat/magnetic_golem", "/actions/combat/stalactite_golem", "/actions/combat/granite_golem", "/actions/combat/golem_cave", "/actions/combat/golem_cave_elite", "/actions/combat/zombie", "/actions/combat/vampire", "/actions/combat/werewolf", "/actions/combat/twilight_zone", "/actions/combat/twilight_zone_elite", "/actions/combat/abyssal_imp", "/actions/combat/soul_hunter", "/actions/combat/infernal_warlock", "/actions/combat/infernal_abyss", "/actions/combat/infernal_abyss_elite", "/actions/combat/chimerical_den", "/actions/combat/sinister_circus", "/actions/combat/enchanted_fortress", "/actions/combat/pirate_cove" ]; const zhNames = [ "苍蝇", "杰瑞", "臭鼬", "豪猪", "史莱姆", "臭臭星球", "臭臭星球 (精英)", "青蛙", "蛇", "沼泽虫", "夏洛克", "沼泽星球", "沼泽星球 (精英)", "蜗牛", "螃蟹", "水马", "咬咬鱼", "忍者龟", "海洋星球", "海洋星球 (精英)", "丛林精灵", "蘑菇人", "树人", "半人马弓箭手", "丛林星球", "丛林星球 (精英)", "刺刺", "砍砍", "锤锤", "咻咻", "轰轰", "哥布林星球", "哥布林星球 (精英)", "独眼", "叠眼", "复眼", "眼球星球", "眼球星球 (精英)", "新手巫师", "冰霜巫师", "火焰巫师", "元素法师", "巫师之塔", "巫师之塔 (精英)", "软糖熊", "熊猫", "黑熊", "棕熊", "北极熊", "熊熊星球", "熊熊星球 (精英)", "磁力魔像", "钟乳石魔像", "花岗岩魔像", "魔像洞穴", "魔像洞穴 (精英)", "僵尸", "吸血鬼", "狼人", "暮光之地", "暮光之地 (精英)", "深渊小鬼", "灵魂猎手", "地狱术士", "地狱深渊", "地狱深渊 (精英)", "奇幻洞穴", "阴森马戏团", "秘法要塞", "海盗基地" ]; const enNames = [ "Fly", "Jerry", "Skunk", "Porcupine", "Slimy", "Smelly Planet", "Smelly Planet (Elite)", "Frogger", "Thnake", "Swampy", "Sherlock", "Swamp Planet", "Swamp Planet (Elite)", "Gary", "I Pinch", "Aquahorse", "Nom Nom", "Turuto", "Aqua Planet", "Aqua Planet (Elite)", "Jungle Sprite", "Myconid", "Treant", "Centaur Archer", "Jungle Planet", "Jungle Planet (Elite)", "Stabby", "Slashy", "Smashy", "Shooty", "Boomy", "Gobo Planet", "Gobo Planet (Elite)", "Eye", "Eyes", "Veyes", "Planet Of The Eyes", "Planet Of The Eyes (Elite)", "Novice Sorcerer", "Ice Sorcerer", "Flame Sorcerer", "Elementalist", "Sorcerer's Tower", "Sorcerer's Tower (Elite)", "Gummy Bear", "Panda", "Black Bear", "Grizzly Bear", "Polar Bear", "Bear With It", "Bear With It (Elite)", "Magnetic Golem", "Stalactite Golem", "Granite Golem", "Golem Cave", "Golem Cave (Elite)", "Zombie", "Vampire", "Werewolf", "Twilight Zone", "Twilight Zone (Elite)", "Abyssal Imp", "Soul Hunter", "Infernal Warlock", "Infernal Abyss", "Infernal Abyss (Elite)", "Chimerical Den", "Sinister Circus", "Enchanted Fortress", "Pirate Cove" ] // 语言检测 const isZhCN = navigator.language.toLowerCase().includes('zh'); // 翻译字典 const translations = { zh: { title: "自动战斗助手", selectTask: "选择任务:", battleCount: "每个队列战斗次数:", startButton: "开始挂机", stopButton: "停止挂机" }, en: { title: "Combat Assistant", selectTask: "Select Task:", battleCount: "Battles per Queue:", startButton: "Start Auto", stopButton: "Stop Auto" } }; const t = translations[isZhCN ? 'zh' : 'en']; // 队列监听器类 class ReactActionQueueMonitor { constructor(callback, interval = 100) { this.callback = callback; this.interval = interval; this.lastValue = null; this.timer = null; } getProps() { const el = document.querySelector(".Header_leftHeader__PkRWX"); if (!el) return null; const key = Object.keys(el).find(k => k.startsWith('__reactProps$')); return key ? el[key] : null; } getCurrentValue() { const props = this.getProps(); return props?.children?.[0]?._owner?.memoizedProps?.characterActions?.length ?? null; } getActionQueueCap() { const props = this.getProps(); return props?.children?.[0]?._owner?.memoizedProps?.characterInfo?.actionQueueCap ?? null; } start() { if (this.timer) return; this.timer = setInterval(() => { const val = this.getCurrentValue(); const cap = this.getActionQueueCap(); if (val !== null && cap !== null && val !== this.lastValue && val < cap) { this.callback(); } this.lastValue = val; }, this.interval); } stop() { clearInterval(this.timer); this.timer = null; } } // 战斗任务创建器 class CombatTaskCreator { constructor() { this.reactInstance = null; } findReactInstance() { const rootEl = document.querySelector(".GamePage_gamePage__ixiPl"); if (!rootEl) return null; const fiberKey = Object.keys(rootEl).find(k => k.startsWith("__reactFiber$")); if (!fiberKey) return null; let fiber = rootEl[fiberKey]; while (fiber) { const inst = fiber.stateNode; if (inst && typeof inst.sendPing === "function") { return inst; } fiber = fiber.return; } return null; } createCombatAction(hrid, battleCount) { if (!this.reactInstance) { this.reactInstance = this.findReactInstance(); } if (this.reactInstance && typeof this.reactInstance.handleNewCharacterAction === "function") { this.reactInstance.handleNewCharacterAction(hrid, true, battleCount, "", "", 0, 0, 0, false); return true; } return false; } } // 本地存储管理器 class StorageManager { static get(key, defaultValue = null) { try { const value = localStorage.getItem(`combatAssistant_${key}`); return value ? JSON.parse(value) : defaultValue; } catch { return defaultValue; } } static set(key, value) { try { localStorage.setItem(`combatAssistant_${key}`, JSON.stringify(value)); } catch (e) { console.error('Storage error:', e); } } } // 主应用类 class CombatAssistant { constructor() { this.monitor = null; this.taskCreator = new CombatTaskCreator(); this.isRunning = false; this.selectedTaskIndex = StorageManager.get('selectedTask', 0); this.battleCount = StorageManager.get('battleCount', 9); this.panelPosition = StorageManager.get('panelPosition', { x: 100, y: 100 }); this.isCollapsed = StorageManager.get('isCollapsed', false); this.isMinimized = StorageManager.get('isMinimized', false); this.wasDragged = false; this.init(); } init() { this.createUI(); this.setupEventListeners(); this.setupDragAndDrop(); this.loadSettings(); } // 验证输入值是否合法 validateBattleCount(value) { // 检查是否为空或非数字 if (value === '' || isNaN(value)) { return false; } const num = parseInt(value); // 检查是否为整数且在有效范围内 if (!Number.isInteger(num) || num < 1 || num > 9999999999) { return false; } // 检查是否超过10位数(字符串长度检查,排除前导零等情况) const cleanValue = value.toString().replace(/^0+/, '') || '0'; if (cleanValue.length > 10) { return false; } return true; } // 更新开始按钮状态 updateStartButtonState() { const startButton = this.panel.querySelector('#combat-start-button'); const battleCountInput = this.panel.querySelector('#combat-battle-count'); const inputValue = battleCountInput.value; const isValid = this.validateBattleCount(inputValue); startButton.disabled = !isValid || this.isRunning; // 视觉反馈 if (isValid) { battleCountInput.style.borderColor = 'rgba(255,255,255,0.2)'; battleCountInput.style.backgroundColor = 'rgba(0,0,0,0.3)'; } else { battleCountInput.style.borderColor = '#e74c3c'; battleCountInput.style.backgroundColor = 'rgba(231,76,60,0.1)'; } } createUI() { // 创建面板容器 this.panel = document.createElement('div'); this.panel.id = 'combat-assistant-panel'; this.panel.innerHTML = `