您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
一些超级有用的MWI的QoL功能
// ==UserScript== // @name Ranged Way Idle // @namespace http://tampermonkey.net/ // @version 2.14 // @description 一些超级有用的MWI的QoL功能 // @author AlphB // @match https://www.milkywayidle.com/* // @match https://test.milkywayidle.com/* // @grant GM_notification // @grant GM_getValue // @grant GM_setValue // @icon https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com // @grant none // @license CC-BY-NC-SA-4.0 // ==/UserScript== (function () { const config = { notifyDeath: {enable: true, desc: "战斗中角色死亡时发送通知"}, forceUpdateMarketPrice: {enable: true, desc: "进入市场时,强制更新MWITools的市场价格(依赖MWITools)"}, notifyWhisperMessages: {enable: false, desc: "接受到私信时播放提醒音"}, listenKeywordMessages: {enable: false, desc: "中文频道消息含有关键词时播放提醒音"}, matchByRegex: {enable: false, desc: "改用正则表达式匹配中文频道消息(依赖上一条功能)"}, autoTaskSort: {enable: true, desc: "自动点击MWI TaskManager的任务排序按钮(依赖MWI TaskManager)"}, showMarketListingsFunds: {enable: true, desc: "显示购买预付金/出售可获金/待领取金额"}, mournForMagicWayIdle: {enable: true, desc: "在控制台默哀法师助手"}, showTaskValue: {enable: true, desc: "显示任务期望奖励和任务代币的价值(依赖食用工具)"}, showMarketAPITime: {enable: true, desc: "显示商店物品的API更新时间(依赖MWITools)"}, showListingBestPrice: {enable: true, desc: "显示你的挂单的物品的左一/右一价格(依赖MWITools)"}, forceUpdateAPIButton: {enable: true, desc: "添加强制刷新市场数据API的按钮(在 我的挂牌 界面)"}, doNotTellMeUpgrade: {enable: false, desc: "禁用升级行动队列的按钮(点击时也不会跳转商店)"}, debugPrintMessages: {enable: false, desc: "控制台打印所有消息(不推荐打开)"}, keywords: [], } const globalVariable = { battleData: { players: null, lastNotifyTime: 0, }, characterID: null, whisperAudio: new Audio(`https://upload.thbwiki.cc/d/d1/se_bonus2.mp3`), keywordAudio: new Audio(`https://upload.thbwiki.cc/c/c9/se_pldead00.mp3`), market: { hasFundsElement: false, sellValue: null, buyValue: null, unclaimedValue: null, sellListings: null, buyListings: null }, task: { taskListElement: null, taskShopElement: null, taskTokenValueData: null, hasTaskValueElement: false, hasTaskShopValueElement: false, taskValueElements: [], taskShopElements: [], tokenValue: { Bid: null, Ask: null } }, marketAPITime: { time: null, element: null, }, marketBestPriceElement: [], disabledUpgradeButtons: [], }; init(); function init() { readConfig(); // 任务代币计算功能需要食用工具 if (!('Edible_Tools' in localStorage) || !JSON.parse(localStorage.getItem('Edible_Tools')) || (!("Chest_Drop_Data" in JSON.parse(localStorage.getItem('Edible_Tools'))))) { config.showTaskValue.enable = false; saveConfig({showTaskValue: false}); } // 更新市场价格需要MWITools支持 if (!('MWITools_marketAPI_json' in localStorage) || !JSON.parse(localStorage.getItem('MWITools_marketAPI_json')) || (!("marketData" in JSON.parse(localStorage.getItem('MWITools_marketAPI_json'))))) { config.forceUpdateMarketPrice.enable = false; saveConfig({forceUpdateMarketPrice: false}); } globalVariable.whisperAudio.volume = 0.4; globalVariable.keywordAudio.volume = 0.4; let observer = new MutationObserver(function () { if (config.showMarketListingsFunds.enable) showMarketListingsFunds(); if (config.autoTaskSort.enable) autoClickTaskSortButton(); if (config.showTaskValue.enable) { showTaskValue(); showTaskShopItemValue(); } if (config.showMarketAPITime.enable) { showMarketAPITime(); } if (config.showListingBestPrice.enable) { showListingBestPrice(); } if (config.forceUpdateAPIButton.enable) { forceUpdateAPIButton(); } if (config.doNotTellMeUpgrade.enable) { doNotTellMeUpgrade(); } showConfigMenu(); }); observer.observe(document, {childList: true, subtree: true}); if (config.showTaskValue.enable) { globalVariable.task.taskTokenValueData = getTaskTokenValue(); } if (config.mournForMagicWayIdle.enable) { console.log("为法师助手默哀"); } const OriginalWebSocket = window.WebSocket; // 重写WebSocket构造函数 window.WebSocket = function (...args) { const socket = new OriginalWebSocket(...args); const originalSend = socket.send; socket.send = function (data) { handleMessage(data, 'send'); return originalSend.call(this, data); }; socket.addEventListener('message', function (event) { handleMessage(event.data, 'get'); }); return socket; }; for (const prop in OriginalWebSocket) { if (OriginalWebSocket.hasOwnProperty(prop)) { window.WebSocket[prop] = OriginalWebSocket[prop]; } } } function readConfig() { const localConfig = localStorage.getItem("ranged_way_idle_config"); if (localConfig) { const localConfigObj = JSON.parse(localConfig); for (let key in localConfigObj) { if (config.hasOwnProperty(key) && key !== 'keywords') { config[key].enable = localConfigObj[key]; } } config.keywords = localConfigObj.keywords; } } function saveConfig(obj) { // 仅保存enable开关和keywords const saveConfigObj = {}; const configMenu = document.querySelectorAll("div#ranged_way_idle_config_menu input"); if (configMenu.length === 0) return; for (const checkbox of configMenu) { config[checkbox.id].enable = checkbox.checked; saveConfigObj[checkbox.id] = checkbox.checked; } for (let key in obj) { saveConfigObj[key] = obj[key]; } saveConfigObj.keywords = config.keywords; localStorage.setItem("ranged_way_idle_config", JSON.stringify(saveConfigObj)); } function showConfigMenu() { const targetNode = document.querySelector("div.SettingsPanel_profileTab__214Bj"); if (targetNode) { if (!targetNode.querySelector("#ranged_way_idle_config_menu")) { // enable开关部分 targetNode.insertAdjacentHTML("beforeend", `<div id="ranged_way_idle_config_menu"></div>`); const insertElem = targetNode.querySelector("div#ranged_way_idle_config_menu"); insertElem.insertAdjacentHTML( "beforeend", `<div style="float: left;" id="ranged_way_idle_config">${ "Ranged Way Idle 设置(刷新后生效)" }</div></br>` ); insertElem.insertAdjacentHTML( "beforeend", `<div style="float: left;" id="ranged_way_idle_config">${ "若刷新后选项变化或仍不生效,说明插件不兼容,可能是因为未安装插件或版本过久" }</div></br>` ); for (let key in config) { if (key === 'keywords') continue; insertElem.insertAdjacentHTML( "beforeend", `<div style="float: left;"> <input type="checkbox" id="${key}" ${config[key].enable ? "checked" : ""}>${config[key].desc} </div></br>` ); } insertElem.addEventListener("change", saveConfig); // 控制 keywords 列表 const container = document.createElement('div'); container.style.marginTop = '20px'; container.classList.add("ranged_way_idle_keywords_config_menu") const input = document.createElement('input'); input.type = 'text'; input.style.width = '200px'; input.placeholder = 'Ranged Way Idle 监听' + (config.matchByRegex.enable ? '正则' : '关键词'); const button = document.createElement('button'); button.textContent = '添加'; const listContainer = document.createElement('div'); listContainer.style.marginTop = '10px'; container.appendChild(input); container.appendChild(button); container.appendChild(listContainer); targetNode.insertBefore(container, targetNode.nextSibling); function renderList() { listContainer.innerHTML = ''; config.keywords.forEach((item, index) => { const itemDiv = document.createElement('div'); itemDiv.textContent = item; itemDiv.style.margin = 'auto'; itemDiv.style.width = '200px'; itemDiv.style.cursor = 'pointer'; itemDiv.addEventListener('click', () => { config.keywords.splice(index, 1); renderList(); }); listContainer.appendChild(itemDiv); }); saveConfig(); } renderList(); button.addEventListener('click', () => { const newItem = input.value.trim(); if (newItem) { config.keywords.push(newItem); input.value = ''; saveConfig(); renderList(); } }); } } } function handleMessage(message, type) { let obj; try { obj = JSON.parse(message); } catch (err) { console.log(type, obj); return; } if (config.debugPrintMessages.enable) console.log(type, obj); if (type !== 'get' || !obj) return; try { switch (obj.type) { case "init_character_data": globalVariable.market.sellListings = {}; globalVariable.market.buyListings = {}; updateMarketListings(obj.myMarketListings); globalVariable.characterID = obj.character.id; break; case "market_listings_updated": updateMarketListings(obj.endMarketListings); break; case "new_battle": if (config.notifyDeath.enable) initBattle(obj); break; case "battle_updated": if (config.notifyDeath.enable) checkDeath(obj); break; case "market_item_order_books_updated": if (config.forceUpdateMarketPrice.enable) marketPriceUpdate(obj); break; case "quests_updated": for (let e of globalVariable.task.taskValueElements) { e.remove(); } globalVariable.task.taskValueElements = []; globalVariable.task.hasTaskValueElement = false; break; case "chat_message_received": handleChatMessage(obj); break; } } catch (e) { console.error(e); } } function notifyDeath(name) { // 如果间隔小于60秒,强制不播报 const nowTime = Date.now(); if (nowTime - globalVariable.battleData.lastNotifyTime < 60000) return; globalVariable.battleData.lastNotifyTime = nowTime; new Notification('🎉🎉🎉喜报🎉🎉🎉', {body: `${name} 死了!`}); } function initBattle(obj) { // 处理战斗中各个玩家的角色名,供播报死亡信息 globalVariable.battleData.players = []; for (let player of obj.players) { globalVariable.battleData.players.push({ name: player.name, isAlive: player.currentHitpoints > 0, }); if (player.currentHitpoints === 0) { notifyDeath(player.name); } } } function checkDeath(obj) { // 检查玩家是否死亡 if (!globalVariable.battleData.players) return; for (let key in obj.pMap) { const index = parseInt(key); if (globalVariable.battleData.players[index].isAlive && obj.pMap[key].cHP === 0) { // 角色 活->死 时发送提醒 globalVariable.battleData.players[index].isAlive = false; notifyDeath(globalVariable.battleData.players[index].name); } else if (obj.pMap[key].cHP > 0) { globalVariable.battleData.players[index].isAlive = true; } } } function marketPriceUpdate(obj) { // 强制刷新MWITools的市场价格数据 if (config.showTaskValue.enable) { globalVariable.task.taskTokenValueData = getTaskTokenValue(); } const marketAPIjson = JSON.parse(localStorage.getItem('MWITools_marketAPI_json')); if (!('MWITools_marketAPI_json' in localStorage) || !JSON.parse(localStorage.getItem('MWITools_marketAPI_json')) || (!("marketData" in JSON.parse(localStorage.getItem('MWITools_marketAPI_json'))))) return; const itemHrid = obj.marketItemOrderBooks.itemHrid; if (!(itemHrid in marketAPIjson.marketData)) return; const orderBooks = obj.marketItemOrderBooks.orderBooks; for (let enhanceLevel in orderBooks) { if (!(enhanceLevel in marketAPIjson.marketData[itemHrid])) { marketAPIjson.marketData[itemHrid][enhanceLevel] = {a: 0, b: 0}; } const ask = orderBooks[enhanceLevel].asks; if (ask && ask.length) { marketAPIjson.marketData[itemHrid][enhanceLevel].a = Math.min(...ask.map(listing => listing.price)); } const bid = orderBooks[enhanceLevel].bids; if (bid && bid.length) { marketAPIjson.marketData[itemHrid][enhanceLevel].b = Math.max(...bid.map(listing => listing.price)); } } // 将修改后结果写回marketAPI缓存,完成对marketAPI价格的强制修改 localStorage.setItem("MWITools_marketAPI_json", JSON.stringify(marketAPIjson)); } function handleChatMessage(obj) { // 处理聊天信息 if (obj.message.chan === "/chat_channel_types/whisper") { if (config.notifyWhisperMessages.enable && obj.message.rId === globalVariable.characterID) { globalVariable.whisperAudio.play(); } } else if (obj.message.chan === "/chat_channel_types/chinese") { if (config.listenKeywordMessages.enable) { for (let keyword of config.keywords) { if (!config.matchByRegex.enable && obj.message.m.includes(keyword)) { globalVariable.keywordAudio.play(); } else if (config.matchByRegex.enable) { const regex = new RegExp(keyword, "g"); if (regex.test(obj.message.m)) { globalVariable.keywordAudio.play(); } } } } } } function autoClickTaskSortButton() { // 点击MWI TaskManager的任务排序按钮 const targetElement = document.querySelector('#TaskSort'); if (targetElement && targetElement.textContent !== '手动排序') { targetElement.click(); targetElement.textContent = '手动排序'; } } function formatCoinValue(num) { if (isNaN(num)) return "NaN"; if (num >= 1e13) { return Math.floor(num / 1e12) + "T"; } else if (num >= 1e10) { return Math.floor(num / 1e9) + "B"; } else if (num >= 1e7) { return Math.floor(num / 1e6) + "M"; } else if (num >= 1e4) { return Math.floor(num / 1e3) + "K"; } return num.toString(); } function updateMarketListings(obj) { // 更新市场价格 for (let listing of obj) { if (listing.status === "/market_listing_status/cancelled") { delete globalVariable.market[listing.isSell ? "sellListings" : "buyListings"][listing.id]; continue } const tax = (listing.itemHrid === "/items/bag_of_10_cowbells") ? 0.82 : 0.98; globalVariable.market[listing.isSell ? "sellListings" : "buyListings"][listing.id] = { itemHrid: listing.itemHrid, price: (listing.orderQuantity - listing.filledQuantity) * (listing.isSell ? Math.floor(listing.price * tax) : listing.price), unclaimedCoinCount: listing.unclaimedCoinCount, } } globalVariable.market.buyValue = 0; globalVariable.market.sellValue = 0; globalVariable.market.unclaimedValue = 0; for (let id in globalVariable.market.buyListings) { const listing = globalVariable.market.buyListings[id]; globalVariable.market.buyValue += listing.price; globalVariable.market.unclaimedValue += listing.unclaimedCoinCount; } for (let id in globalVariable.market.sellListings) { const listing = globalVariable.market.sellListings[id]; globalVariable.market.sellValue += listing.price; globalVariable.market.unclaimedValue += listing.unclaimedCoinCount; } globalVariable.market.hasFundsElement = false; } function showMarketListingsFunds() { // 如果已经存在节点,不必更新 if (globalVariable.market.hasFundsElement) return; const coinStackElement = document.querySelector("div.MarketplacePanel_coinStack__1l0UD"); // 不在市场面板,不必更新 if (coinStackElement) { coinStackElement.style.top = "0px"; coinStackElement.style.left = "0px"; let fundsElement = coinStackElement.parentNode.querySelector("div.fundsElement"); while (fundsElement) { fundsElement.remove(); fundsElement = coinStackElement.parentNode.querySelector("div.fundsElement"); } makeNode("购买预付金", globalVariable.market.buyValue, ["125px", "0px"]); makeNode("出售可获金", globalVariable.market.sellValue, ["125px", "22px"]); makeNode("待领取金额", globalVariable.market.unclaimedValue, ["0px", "22px"]); globalVariable.market.hasFundsElement = true; } function makeNode(text, value, style) { let node = coinStackElement.cloneNode(true); node.classList.add("fundsElement"); const countNode = node.querySelector("div.Item_count__1HVvv"); const textNode = node.querySelector("div.Item_name__2C42x"); if (countNode) countNode.textContent = formatCoinValue(value); if (textNode) textNode.innerHTML = `<span style="color: rgb(102,204,255); font-weight: bold;">${text}</span>`; node.style.left = style[0]; node.style.top = style[1]; coinStackElement.parentNode.insertBefore(node, coinStackElement.nextSibling); } } function getTaskTokenValue() { const chestDropData = JSON.parse(localStorage.getItem("Edible_Tools")).Chest_Drop_Data; const lootsName = ["大陨石舱", "大工匠匣", "大宝箱"]; const bidValueList = [ parseFloat(chestDropData["Large Meteorite Cache"]["期望产出Bid"]), parseFloat(chestDropData["Large Artisan's Crate"]["期望产出Bid"]), parseFloat(chestDropData["Large Treasure Chest"]["期望产出Bid"]), ] const askValueList = [ parseFloat(chestDropData["Large Meteorite Cache"]["期望产出Ask"]), parseFloat(chestDropData["Large Artisan's Crate"]["期望产出Ask"]), parseFloat(chestDropData["Large Treasure Chest"]["期望产出Ask"]), ] const res = { bidValue: Math.max(...bidValueList), askValue: Math.max(...askValueList) } // bid和ask的最佳兑换选项 res.bidLoots = lootsName[bidValueList.indexOf(res.bidValue)]; res.askLoots = lootsName[askValueList.indexOf(res.askValue)]; // bid和ask的任务代币价值 res.bidValue = Math.round(res.bidValue / 30); res.askValue = Math.round(res.askValue / 30); // 小紫牛的礼物的额外价值计算 res.giftValueBid = Math.round(parseFloat(chestDropData["Purple's Gift"]["期望产出Bid"])); res.giftValueAsk = Math.round(parseFloat(chestDropData["Purple's Gift"]["期望产出Ask"])); if (config.forceUpdateMarketPrice.enable) { const marketJSON = JSON.parse(localStorage.getItem("MWITools_marketAPI_json")); marketJSON.marketData["/items/task_token"] = {"0": {a: res.askValue, b: res.bidValue}}; localStorage.setItem("MWITools_marketAPI_json", JSON.stringify(marketJSON)); } res.rewardValueBid = res.bidValue + res.giftValueBid / 50; res.rewardValueAsk = res.askValue + res.giftValueAsk / 50; return res; } function showTaskValue() { globalVariable.task.taskListElement = document.querySelector("div.TasksPanel_taskList__2xh4k"); // 如果不在任务面板,则销毁显示任务价值的元素 if (!globalVariable.task.taskListElement) { globalVariable.task.taskValueElements = []; globalVariable.task.hasTaskValueElement = false; globalVariable.task.taskListElement = null; return; } // 如果已经存在任务价值的元素,不再更新 if (globalVariable.task.hasTaskValueElement) return; globalVariable.task.hasTaskValueElement = true; const taskNodes = [...globalVariable.task.taskListElement.querySelectorAll("div.RandomTask_randomTask__3B9fA")]; function convertKEndStringToNumber(str) { if (str.endsWith('K') || str.endsWith('k')) { return Number(str.slice(0, -1)) * 1000; } else { return Number(str); } } taskNodes.forEach(function (node) { const reward = node.querySelector("div.RandomTask_rewards__YZk7D"); const coin = convertKEndStringToNumber(reward.querySelectorAll("div.Item_count__1HVvv")[0].innerText); const tokenCount = Number(reward.querySelectorAll("div.Item_count__1HVvv")[1].innerText); const newDiv = document.createElement("div"); newDiv.textContent = `奖励期望收益: ${formatCoinValue(coin + tokenCount * globalVariable.task.taskTokenValueData.rewardValueAsk)} / ${formatCoinValue(coin + tokenCount * globalVariable.task.taskTokenValueData.rewardValueBid)}`; newDiv.style.color = "rgb(248,0,248)"; newDiv.classList.add("rewardValue"); node.querySelector("div.RandomTask_action__3eC6o").appendChild(newDiv); globalVariable.task.taskValueElements.push(newDiv); }); } function showTaskShopItemValue() { globalVariable.task.taskShopElement = document.querySelector("div.TasksPanel_buyableGrid__2Ua51"); // 如果不在商店面板,则销毁显示价值的元素 if (!globalVariable.task.taskShopElement) { globalVariable.task.taskShopValueElements = []; globalVariable.task.hasTaskShopValueElement = false; globalVariable.task.taskShopElement = null; return; } // 如果已经存在价值的元素,不再更新 if (globalVariable.task.hasTaskShopValueElement) return; globalVariable.task.hasTaskShopValueElement = true; const taskNodes = [...globalVariable.task.taskShopElement.querySelectorAll("div.TasksPanel_item__DWSpv")]; function convertKEndStringToNumber(str) { if (str.endsWith('K') || str.endsWith('k')) { return Number(str.slice(0, -1)) * 1000; } else { return Number(str); } } const chestDropData = JSON.parse(localStorage.getItem("Edible_Tools")).Chest_Drop_Data; // parseFloat(chestDropData["Large Meteorite Cache"]["期望产出Bid"]), // parseFloat(chestDropData["Large Artisan's Crate"]["期望产出Bid"]), // parseFloat(chestDropData["Large Treasure Chest"]["期望产出Bid"]), taskNodes.forEach(function (node) { if (node.childNodes[2].textContent !== "30") return; const newDiv = document.createElement("div"); if (node.childNodes[1].childNodes[0].childNodes[0].href.baseVal.endsWith("large_meteorite_cache")) { newDiv.textContent = ` ${formatCoinValue(parseFloat(chestDropData["Large Meteorite Cache"]["期望产出Ask"]))} / ${formatCoinValue(parseFloat(chestDropData["Large Meteorite Cache"]["期望产出Bid"]))}`; } else if (node.childNodes[1].childNodes[0].childNodes[0].href.baseVal.endsWith("large_artisans_crate")) { newDiv.textContent = ` ${formatCoinValue(parseFloat(chestDropData["Large Artisan's Crate"]["期望产出Ask"]))} / ${formatCoinValue(parseFloat(chestDropData["Large Artisan's Crate"]["期望产出Bid"]))}`; } else if (node.childNodes[1].childNodes[0].childNodes[0].href.baseVal.endsWith("large_treasure_chest")) { newDiv.textContent = ` ${formatCoinValue(parseFloat(chestDropData["Large Treasure Chest"]["期望产出Ask"]))} / ${formatCoinValue(parseFloat(chestDropData["Large Treasure Chest"]["期望产出Bid"]))}`; } newDiv.style.color = "rgb(248,0,248)"; newDiv.classList.add("taskShopValue"); node.childNodes[2].insertAdjacentElement('beforebegin', newDiv); globalVariable.task.taskShopValueElements.push(newDiv); }); } function showMarketAPITime() { const root = document.querySelector("div.MarketplacePanel_buttonContainer__vJQud"); if (!root) return; const nowTime = JSON.parse(localStorage.getItem('MWITools_marketAPI_json')).timestamp; if (nowTime === globalVariable.marketAPIUpdateTime) return; globalVariable.marketAPIUpdateTime = nowTime; if (globalVariable.marketAPITimeElement) { globalVariable.marketAPITimeElement.remove(); } globalVariable.marketAPITimeElement = document.createElement("div"); globalVariable.marketAPITimeElement.textContent = "API更新时间: " + new Date(nowTime * 1000).toLocaleString(); globalVariable.marketAPITimeElement.style.color = "rgb(102,204,255)"; globalVariable.marketAPITimeElement.classList.add("marketAPIUpdateTime"); root.insertBefore(globalVariable.marketAPITimeElement, root.lastChild); } function showListingBestPrice() { const head = document.querySelector("div.MarketplacePanel_myListingsTableContainer__2s6pm > table > thead > tr"); if (head && !head.querySelector(".bestPriceHead")) { const cloneNode = head.childNodes[3].cloneNode(); cloneNode.textContent = "左一/右一价格"; cloneNode.title = '绿色表示最佳价格(左一/右一),红色表示已经被压价'; cloneNode.classList.add("bestPriceHead"); head.insertBefore(cloneNode, head.childNodes[4]); } const body = document.querySelector("div.MarketplacePanel_myListingsTableContainer__2s6pm > table > tbody"); if (!body) return; const rows = body.querySelectorAll("tr"); if (!rows) return; const marketJSON = JSON.parse(localStorage.getItem("MWITools_marketAPI_json")); for (const row of body.querySelectorAll("tr")) { const mode = row.childNodes[1].classList[1].includes("sell") ? "a" : "b"; const itemHrid = '/items/' + row.childNodes[2].firstChild.firstChild.firstChild.firstChild.firstChild.firstChild.firstChild.href.baseVal.split('#')[1]; const enhanceLevelElement = row.querySelector('.Item_enhancementLevel__19g-e'); const enhanceLevel = enhanceLevelElement ? Number(enhanceLevelElement.textContent) : 0; let marketAPIPrice; try { marketAPIPrice = Number(marketJSON.marketData[itemHrid][enhanceLevel][mode]); } catch (e) { marketAPIPrice = null; } const listingPriceStr = row.querySelector('.MarketplacePanel_price__hIzrY').firstChild.textContent; if (row.querySelector(".bestPrice") && parseStringPrice(row.querySelector(".bestPrice").textContent) === marketAPIPrice) continue; if (row.querySelector(".bestPrice")) { row.querySelector(".bestPrice").remove(); } const newNode = document.createElement("td"); newNode.classList.add("bestPrice"); newNode.textContent = parseIntPrice(marketAPIPrice); if (marketAPIPrice) { if (mode === 'a') { // 左 newNode.style.color = marketAPIPrice < parseStringPrice(listingPriceStr) ? "#FC1200" : "#12F355"; } else { // 右 newNode.style.color = marketAPIPrice > parseStringPrice(listingPriceStr) ? "#FC1200" : "#12F355"; } } else { newNode.style.color = "#004FFF"; } row.insertBefore(newNode, row.childNodes[4]); } function parseIntPrice(price) { if (price === null) { return "NULL"; } if (price < 1e5) { return price.toString(); } if (price < 1e7) { return (price / 1e3) + "K"; } if (price < 1e10) { return (price / 1e6) + "M"; } if (price < 1e13) { return (price / 1e9) + "B"; } if (price < 1e16) { return (price / 1e9) + "T"; } } function parseStringPrice(price) { if (price === "NULL") { return null; } if (price.endsWith('K') || price.endsWith('k')) { return Number(price.slice(0, -1)) * 1e3; } if (price.endsWith('M')) { return Number(price.slice(0, -1)) * 1e6; } if (price.endsWith('B')) { return Number(price.slice(0, -1)) * 1e9; } if (price.endsWith('T')) { return Number(price.slice(0, -1)) * 1e12; } return Number(price); } } function forceUpdateAPIButton() { const root = document.querySelector("div.MarketplacePanel_listingCount__3nVY_"); if (!root || root.querySelector(".forceUpdateAPIButton")) return; const button = root.querySelector("button:nth-child(1)").cloneNode(true); button.textContent = "强制刷新API"; button.classList.add("forceUpdateAPIButton"); button.addEventListener("click", async function () { const resp = await fetch("https://www.milkywayidle.com/game_data/marketplace.json"); const text = await resp.text(); localStorage.setItem("MWITools_marketAPI_json", text); alert("强制刷新API成功,市场数据更新于" + new Date(JSON.parse(text).timestamp * 1000).toLocaleString()); }); root.appendChild(button); } function doNotTellMeUpgrade() { const buttons = document.querySelectorAll("button"); for (const button of buttons) { if ((button.textContent === "Upgrade Queue Capacity" || button.textContent === "升级行动队列") && button.disabled === false) { button.disabled = true; globalVariable.disabledUpgradeButtons.push(button); } } let newList = []; for (const button of globalVariable.disabledUpgradeButtons) { if (button.textContent !== "Upgrade Queue Capacity" && button.textContent !== "升级行动队列") { button.disabled = false; } else { newList.push(button); } } globalVariable.disabledUpgradeButtons = newList; } })();