Ranged Way Idle

死亡提醒、强制刷新MWITools的价格、私信提醒音、自动任务排序、显示购买预付金/出售可获金/待领取金额、显示任务价值、商店物品价值、默哀法师助手

  1. // ==UserScript==
  2. // @name Ranged Way Idle
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.8
  5. // @description 死亡提醒、强制刷新MWITools的价格、私信提醒音、自动任务排序、显示购买预付金/出售可获金/待领取金额、显示任务价值、商店物品价值、默哀法师助手
  6. // @author AlphB
  7. // @match https://www.milkywayidle.com/*
  8. // @match https://test.milkywayidle.com/*
  9. // @grant GM_notification
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // @icon https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com
  13. // @grant none
  14. // @license CC-BY-NC-SA-4.0
  15. // ==/UserScript==
  16.  
  17. (function () {
  18. const config = {
  19. notifyDeath: {enable: true, desc: "战斗中角色死亡时发送通知"},
  20. forceUpdateMarketPrice: {enable: true, desc: "进入市场时,强制更新MWITools的市场价格(依赖MWITools)"},
  21. notifyWhisperMessages: {enable: false, desc: "接受到私信时播放提醒音"},
  22. listenKeywordMessages: {enable: false, desc: "中文频道消息含有关键词时播放提醒音"},
  23. matchByRegex: {enable: false, desc: "改用正则表达式匹配中文频道消息(依赖上一条功能)"},
  24. autoTaskSort: {enable: true, desc: "自动点击MWI TaskManager的任务排序按钮(依赖MWI TaskManager)"},
  25. showMarketListingsFunds: {enable: true, desc: "显示购买预付金/出售可获金/待领取金额"},
  26. mournForMagicWayIdle: {enable: true, desc: "在控制台默哀法师助手"},
  27. showTaskValue: {enable: true, desc: "显示任务期望奖励和任务代币的价值(依赖食用工具)"},
  28. debugPrintMessages: {enable: false, desc: "控制台打印所有消息(不推荐打开)"},
  29. keywords: [],
  30. }
  31. const globalVariable = {
  32. battleData: {
  33. players: null,
  34. lastNotifyTime: 0,
  35. },
  36. whisperAudio: new Audio(`https://upload.thbwiki.cc/d/d1/se_bonus2.mp3`),
  37. keywordAudio: new Audio(`https://upload.thbwiki.cc/c/c9/se_pldead00.mp3`),
  38. market: {
  39. hasFundsElement: false,
  40. sellValue: null,
  41. buyValue: null,
  42. unclaimedValue: null,
  43. sellListings: null,
  44. buyListings: null
  45. },
  46. task: {
  47. taskListElement: null,
  48. taskShopElement: null,
  49. taskTokenValueData: null,
  50. hasTaskValueElement: false,
  51. hasTaskShopValueElement: false,
  52. taskValueElements: [],
  53. taskShopElements: [],
  54. tokenValue: {
  55. Bid: null,
  56. Ask: null
  57. }
  58. }
  59. };
  60.  
  61.  
  62. init();
  63.  
  64. function init() {
  65. readConfig();
  66. // 任务代币计算功能需要食用工具
  67. if (!('Edible_Tools' in localStorage) ||
  68. !JSON.parse(localStorage.getItem('Edible_Tools')) ||
  69. (!("Chest_Drop_Data" in JSON.parse(localStorage.getItem('Edible_Tools'))))) {
  70. config.showTaskValue.enable = false;
  71. saveConfig({showTaskValue: false});
  72. }
  73. // 更新市场价格需要MWITools支持
  74. if (!('MWITools_marketAPI_json' in localStorage) ||
  75. !JSON.parse(localStorage.getItem('MWITools_marketAPI_json')) ||
  76. (!("marketData" in JSON.parse(localStorage.getItem('MWITools_marketAPI_json'))))) {
  77. config.forceUpdateMarketPrice.enable = false;
  78. saveConfig({forceUpdateMarketPrice: false});
  79. }
  80. globalVariable.whisperAudio.volume = 0.4;
  81. globalVariable.keywordAudio.volume = 0.4;
  82. let observer = new MutationObserver(function () {
  83. if (config.showMarketListingsFunds.enable) showMarketListingsFunds();
  84. if (config.autoTaskSort.enable) autoClickTaskSortButton();
  85. if (config.showTaskValue.enable) {
  86. showTaskValue();
  87. showTaskShopItemValue();
  88. }
  89. showConfigMenu();
  90. });
  91. observer.observe(document, {childList: true, subtree: true});
  92. if (config.showTaskValue.enable) {
  93. globalVariable.task.taskTokenValueData = getTaskTokenValue();
  94. }
  95. if (config.mournForMagicWayIdle.enable) {
  96. console.log("为法师助手默哀");
  97. }
  98.  
  99. const oriGet = Object.getOwnPropertyDescriptor(MessageEvent.prototype, "data").get;
  100.  
  101. function hookedGet() {
  102. const socket = this.currentTarget;
  103. if (!(socket instanceof WebSocket) || !socket.url ||
  104. (socket.url.indexOf("api.milkywayidle.com/ws") === -1 && socket.url.indexOf("api-test.milkywayidle.com/ws") === -1)) {
  105. return oriGet.call(this);
  106. }
  107. const message = oriGet.call(this);
  108. return handleMessage(message);
  109. }
  110.  
  111. Object.defineProperty(MessageEvent.prototype, "data", {
  112. get: hookedGet,
  113. configurable: true,
  114. enumerable: true
  115. });
  116. }
  117.  
  118. function readConfig() {
  119. const localConfig = localStorage.getItem("ranged_way_idle_config");
  120. if (localConfig) {
  121. const localConfigObj = JSON.parse(localConfig);
  122. for (let key in localConfigObj) {
  123. if (config.hasOwnProperty(key) && key !== 'keywords') {
  124. config[key].enable = localConfigObj[key];
  125. }
  126. }
  127. config.keywords = localConfigObj.keywords;
  128. }
  129. }
  130.  
  131. function saveConfig(obj) {
  132. // 仅保存enable开关和keywords
  133. const saveConfigObj = {};
  134. const configMenu = document.querySelectorAll("div#ranged_way_idle_config_menu input");
  135. if (configMenu.length === 0) return;
  136. for (const checkbox of configMenu) {
  137. config[checkbox.id].enable = checkbox.checked;
  138. saveConfigObj[checkbox.id] = checkbox.checked;
  139. }
  140. for (let key in obj) {
  141. saveConfigObj[key] = obj[key];
  142. }
  143. saveConfigObj.keywords = config.keywords;
  144. localStorage.setItem("ranged_way_idle_config", JSON.stringify(saveConfigObj));
  145. }
  146.  
  147. function showConfigMenu() {
  148. const targetNode = document.querySelector("div.SettingsPanel_profileTab__214Bj");
  149. if (targetNode) {
  150. if (!targetNode.querySelector("#ranged_way_idle_config_menu")) {
  151. // enable开关部分
  152. targetNode.insertAdjacentHTML("beforeend", `<div id="ranged_way_idle_config_menu"></div>`);
  153. const insertElem = targetNode.querySelector("div#ranged_way_idle_config_menu");
  154. insertElem.insertAdjacentHTML(
  155. "beforeend",
  156. `<div style="float: left;" id="ranged_way_idle_config">${
  157. "Ranged Way Idle 设置(刷新后生效)"
  158. }</div></br>`
  159. );
  160. insertElem.insertAdjacentHTML(
  161. "beforeend",
  162. `<div style="float: left;" id="ranged_way_idle_config">${
  163. "若刷新后选项变化或仍不生效,说明插件不兼容,可能是因为未安装插件或版本过久"
  164. }</div></br>`
  165. );
  166. for (let key in config) {
  167. if (key === 'keywords') continue;
  168. insertElem.insertAdjacentHTML(
  169. "beforeend",
  170. `<div style="float: left;">
  171. <input type="checkbox" id="${key}" ${config[key].enable ? "checked" : ""}>${config[key].desc}
  172. </div></br>`
  173. );
  174. }
  175. insertElem.addEventListener("change", saveConfig);
  176.  
  177. // 控制 keywords 列表
  178. const container = document.createElement('div');
  179. container.style.marginTop = '20px';
  180. container.classList.add("ranged_way_idle_keywords_config_menu")
  181. const input = document.createElement('input');
  182. input.type = 'text';
  183. input.style.width = '200px';
  184. input.placeholder = 'Ranged Way Idle 监听' + (config.matchByRegex.enable ? '正则' : '关键词');
  185. const button = document.createElement('button');
  186. button.textContent = '添加';
  187. const listContainer = document.createElement('div');
  188. listContainer.style.marginTop = '10px';
  189. container.appendChild(input);
  190. container.appendChild(button);
  191. container.appendChild(listContainer);
  192. targetNode.insertBefore(container, targetNode.nextSibling);
  193.  
  194. function renderList() {
  195. listContainer.innerHTML = '';
  196. config.keywords.forEach((item, index) => {
  197. const itemDiv = document.createElement('div');
  198. itemDiv.textContent = item;
  199. itemDiv.style.margin = 'auto';
  200. itemDiv.style.width = '200px';
  201. itemDiv.style.cursor = 'pointer';
  202. itemDiv.addEventListener('click', () => {
  203. config.keywords.splice(index, 1);
  204. renderList();
  205. });
  206. listContainer.appendChild(itemDiv);
  207. });
  208. saveConfig();
  209. }
  210.  
  211. renderList();
  212. button.addEventListener('click', () => {
  213. const newItem = input.value.trim();
  214. if (newItem) {
  215. config.keywords.push(newItem);
  216. input.value = '';
  217. saveConfig();
  218. renderList();
  219. }
  220. });
  221. }
  222. }
  223. }
  224.  
  225. function handleMessage(message) {
  226. try {
  227. if (config.debugPrintMessages.enable) console.log(message);
  228. const obj = JSON.parse(message);
  229. if (!obj) return message;
  230. switch (obj.type) {
  231. case "init_character_data":
  232. globalVariable.market.sellListings = {};
  233. globalVariable.market.buyListings = {};
  234. updateMarketListings(obj.myMarketListings);
  235. break;
  236. case "market_listings_updated":
  237. updateMarketListings(obj.endMarketListings);
  238. break;
  239. case "new_battle":
  240. if (config.notifyDeath.enable) initBattle(obj);
  241. break;
  242. case "battle_updated":
  243. if (config.notifyDeath.enable) checkDeath(obj);
  244. break;
  245. case "market_item_order_books_updated":
  246. if (config.forceUpdateMarketPrice.enable) marketPriceUpdate(obj);
  247. break;
  248. case "quests_updated":
  249. for (let e of globalVariable.task.taskValueElements) {
  250. e.remove();
  251. }
  252. globalVariable.task.taskValueElements = [];
  253. globalVariable.task.hasTaskValueElement = false;
  254. break;
  255. case "chat_message_received":
  256. handleChatMessage(obj);
  257. break;
  258. }
  259. } catch (e) {
  260. console.error(e);
  261. }
  262. return message;
  263. }
  264.  
  265. function notifyDeath(name) {
  266. // 如果间隔小于60秒,强制不播报
  267. const nowTime = Date.now();
  268. if (nowTime - globalVariable.battleData.lastNotifyTime < 60000) return;
  269. globalVariable.battleData.lastNotifyTime = nowTime;
  270. new Notification('🎉🎉🎉喜报🎉🎉🎉', {body: `${name} 死了!`});
  271. }
  272.  
  273. function initBattle(obj) {
  274. // 处理战斗中各个玩家的角色名,供播报死亡信息
  275. globalVariable.battleData.players = [];
  276. for (let player of obj.players) {
  277. globalVariable.battleData.players.push({
  278. name: player.name, isAlive: player.currentHitpoints > 0,
  279. });
  280. if (player.currentHitpoints === 0) {
  281. notifyDeath(player.name);
  282. }
  283. }
  284. }
  285.  
  286. function checkDeath(obj) {
  287. // 检查玩家是否死亡
  288. if (!globalVariable.battleData.players) return;
  289. for (let key in obj.pMap) {
  290. const index = parseInt(key);
  291. if (globalVariable.battleData.players[index].isAlive && obj.pMap[key].cHP === 0) {
  292. // 角色 活->死 时发送提醒
  293. globalVariable.battleData.players[index].isAlive = false;
  294. notifyDeath(globalVariable.battleData.players[index].name);
  295. } else if (obj.pMap[key].cHP > 0) {
  296. globalVariable.battleData.players[index].isAlive = true;
  297. }
  298. }
  299. }
  300.  
  301. function marketPriceUpdate(obj) {
  302. // 强制刷新MWITools的市场价格数据
  303. if (config.showTaskValue.enable) {
  304. globalVariable.task.taskTokenValueData = getTaskTokenValue();
  305. }
  306. const marketAPIjson = JSON.parse(localStorage.getItem('MWITools_marketAPI_json'));
  307. if (!('MWITools_marketAPI_json' in localStorage) ||
  308. !JSON.parse(localStorage.getItem('MWITools_marketAPI_json')) ||
  309. (!("marketData" in JSON.parse(localStorage.getItem('MWITools_marketAPI_json'))))) return;
  310. const itemHrid = obj.marketItemOrderBooks.itemHrid;
  311. if (!(itemHrid in marketAPIjson.marketData)) return;
  312. const orderBooks = obj.marketItemOrderBooks.orderBooks;
  313. for (let enhanceLevel in orderBooks) {
  314. if (!(enhanceLevel in marketAPIjson.marketData[itemHrid])) {
  315. marketAPIjson.marketData[itemHrid][enhanceLevel] = {a: 0, b: 0};
  316. }
  317. const ask = orderBooks[enhanceLevel].asks;
  318. if (ask && ask.length) {
  319. marketAPIjson.marketData[itemHrid][enhanceLevel].a = Math.min(...ask.map(listing => listing.price));
  320. }
  321. const bid = orderBooks[enhanceLevel].bids;
  322. if (bid && ask.length) {
  323. marketAPIjson.marketData[itemHrid][enhanceLevel].b = Math.max(...bid.map(listing => listing.price));
  324. }
  325. }
  326. // 将修改后结果写回marketAPI缓存,完成对marketAPI价格的强制修改
  327. localStorage.setItem("MWITools_marketAPI_json", JSON.stringify(marketAPIjson));
  328. }
  329.  
  330. function handleChatMessage(obj) {
  331. // 处理聊天信息
  332. if (obj.message.chan === "/chat_channel_types/whisper") {
  333. if (config.notifyWhisperMessages.enable) {
  334. globalVariable.whisperAudio.play();
  335. }
  336. } else if (obj.message.chan === "/chat_channel_types/chinese") {
  337. if (config.listenKeywordMessages.enable) {
  338. for (let keyword of config.keywords) {
  339. if (!config.matchByRegex.enable && obj.message.m.includes(keyword)) {
  340. globalVariable.keywordAudio.play();
  341. } else if (config.matchByRegex.enable) {
  342. const regex = new RegExp(keyword, "g");
  343. if (regex.test(obj.message.m)) {
  344. globalVariable.keywordAudio.play();
  345. }
  346. }
  347. }
  348.  
  349. }
  350. }
  351. }
  352.  
  353. function autoClickTaskSortButton() {
  354. // 点击MWI TaskManager的任务排序按钮
  355. const targetElement = document.querySelector('#TaskSort');
  356. if (targetElement && targetElement.textContent !== '手动排序') {
  357. targetElement.click();
  358. targetElement.textContent = '手动排序';
  359. }
  360. }
  361.  
  362. function formatCoinValue(num) {
  363. if (isNaN(num)) return "NaN";
  364. if (num >= 1e13) {
  365. return Math.floor(num / 1e12) + "T";
  366. } else if (num >= 1e10) {
  367. return Math.floor(num / 1e9) + "B";
  368. } else if (num >= 1e7) {
  369. return Math.floor(num / 1e6) + "M";
  370. } else if (num >= 1e4) {
  371. return Math.floor(num / 1e3) + "K";
  372. }
  373. return num.toString();
  374. }
  375.  
  376. function updateMarketListings(obj) {
  377. // 更新市场价格
  378. for (let listing of obj) {
  379. if (listing.status === "/market_listing_status/cancelled") {
  380. delete globalVariable.market[listing.isSell ? "sellListings" : "buyListings"][listing.id];
  381. continue
  382. }
  383. const tax = (listing.itemHrid === "/items/bag_of_10_cowbells") ? 0.82 : 0.98;
  384. globalVariable.market[listing.isSell ? "sellListings" : "buyListings"][listing.id] = {
  385. itemHrid: listing.itemHrid,
  386. price: (listing.orderQuantity - listing.filledQuantity) * (listing.isSell ? Math.floor(listing.price * tax) : listing.price),
  387. unclaimedCoinCount: listing.unclaimedCoinCount,
  388. }
  389. }
  390. globalVariable.market.buyValue = 0;
  391. globalVariable.market.sellValue = 0;
  392. globalVariable.market.unclaimedValue = 0;
  393. for (let id in globalVariable.market.buyListings) {
  394. const listing = globalVariable.market.buyListings[id];
  395. globalVariable.market.buyValue += listing.price;
  396. globalVariable.market.unclaimedValue += listing.unclaimedCoinCount;
  397. }
  398. for (let id in globalVariable.market.sellListings) {
  399. const listing = globalVariable.market.sellListings[id];
  400. globalVariable.market.sellValue += listing.price;
  401. globalVariable.market.unclaimedValue += listing.unclaimedCoinCount;
  402. }
  403. globalVariable.market.hasFundsElement = false;
  404. }
  405.  
  406. function showMarketListingsFunds() {
  407. // 如果已经存在节点,不必更新
  408. if (globalVariable.market.hasFundsElement) return;
  409. const coinStackElement = document.querySelector("div.MarketplacePanel_coinStack__1l0UD");
  410. // 不在市场面板,不必更新
  411. if (coinStackElement) {
  412. coinStackElement.style.top = "0px";
  413. coinStackElement.style.left = "0px";
  414. let fundsElement = coinStackElement.parentNode.querySelector("div.fundsElement");
  415. while (fundsElement) {
  416. fundsElement.remove();
  417. fundsElement = coinStackElement.parentNode.querySelector("div.fundsElement");
  418. }
  419. makeNode("购买预付金", globalVariable.market.buyValue, ["125px", "0px"]);
  420. makeNode("出售可获金", globalVariable.market.sellValue, ["125px", "22px"]);
  421. makeNode("待领取金额", globalVariable.market.unclaimedValue, ["0px", "22px"]);
  422. globalVariable.market.hasFundsElement = true;
  423. }
  424.  
  425. function makeNode(text, value, style) {
  426. let node = coinStackElement.cloneNode(true);
  427. node.classList.add("fundsElement");
  428. const countNode = node.querySelector("div.Item_count__1HVvv");
  429. const textNode = node.querySelector("div.Item_name__2C42x");
  430. if (countNode) countNode.textContent = formatCoinValue(value);
  431. if (textNode) textNode.innerHTML = `<span style="color: rgb(102,204,255); font-weight: bold;">${text}</span>`;
  432. node.style.left = style[0];
  433. node.style.top = style[1];
  434. coinStackElement.parentNode.insertBefore(node, coinStackElement.nextSibling);
  435. }
  436. }
  437.  
  438. function getTaskTokenValue() {
  439. const chestDropData = JSON.parse(localStorage.getItem("Edible_Tools")).Chest_Drop_Data;
  440. const lootsName = ["大陨石舱", "大工匠匣", "大宝箱"];
  441. const bidValueList = [
  442. parseFloat(chestDropData["Large Meteorite Cache"]["期望产出Bid"]),
  443. parseFloat(chestDropData["Large Artisan's Crate"]["期望产出Bid"]),
  444. parseFloat(chestDropData["Large Treasure Chest"]["期望产出Bid"]),
  445. ]
  446. const askValueList = [
  447. parseFloat(chestDropData["Large Meteorite Cache"]["期望产出Ask"]),
  448. parseFloat(chestDropData["Large Artisan's Crate"]["期望产出Ask"]),
  449. parseFloat(chestDropData["Large Treasure Chest"]["期望产出Ask"]),
  450. ]
  451. const res = {
  452. bidValue: Math.max(...bidValueList),
  453. askValue: Math.max(...askValueList)
  454. }
  455. // bid和ask的最佳兑换选项
  456. res.bidLoots = lootsName[bidValueList.indexOf(res.bidValue)];
  457. res.askLoots = lootsName[askValueList.indexOf(res.askValue)];
  458. // bid和ask的任务代币价值
  459. res.bidValue = Math.round(res.bidValue / 30);
  460. res.askValue = Math.round(res.askValue / 30);
  461. // 小紫牛的礼物的额外价值计算
  462. res.giftValueBid = Math.round(parseFloat(chestDropData["Purple's Gift"]["期望产出Bid"]));
  463. res.giftValueAsk = Math.round(parseFloat(chestDropData["Purple's Gift"]["期望产出Ask"]));
  464. if (config.forceUpdateMarketPrice.enable) {
  465. const marketJSON = JSON.parse(localStorage.getItem("MWITools_marketAPI_json"));
  466. marketJSON.marketData["/items/task_token"] = {"0": {a: res.askValue, b: res.bidValue}};
  467. localStorage.setItem("MWITools_marketAPI_json", JSON.stringify(marketJSON));
  468. }
  469. res.rewardValueBid = res.bidValue + res.giftValueBid / 50;
  470. res.rewardValueAsk = res.askValue + res.giftValueAsk / 50;
  471. return res;
  472. }
  473.  
  474. function showTaskValue() {
  475. globalVariable.task.taskListElement = document.querySelector("div.TasksPanel_taskList__2xh4k");
  476. // 如果不在任务面板,则销毁显示任务价值的元素
  477. if (!globalVariable.task.taskListElement) {
  478. globalVariable.task.taskValueElements = [];
  479. globalVariable.task.hasTaskValueElement = false;
  480. globalVariable.task.taskListElement = null;
  481. return;
  482. }
  483. // 如果已经存在任务价值的元素,不再更新
  484. if (globalVariable.task.hasTaskValueElement) return;
  485. globalVariable.task.hasTaskValueElement = true;
  486. const taskNodes = [...globalVariable.task.taskListElement.querySelectorAll("div.RandomTask_randomTask__3B9fA")];
  487.  
  488. function convertKEndStringToNumber(str) {
  489. if (str.endsWith('K') || str.endsWith('k')) {
  490. return Number(str.slice(0, -1)) * 1000;
  491. } else {
  492. return Number(str);
  493. }
  494. }
  495.  
  496. taskNodes.forEach(function (node) {
  497. const reward = node.querySelector("div.RandomTask_rewards__YZk7D");
  498. const coin = convertKEndStringToNumber(reward.querySelectorAll("div.Item_count__1HVvv")[0].innerText);
  499. const tokenCount = Number(reward.querySelectorAll("div.Item_count__1HVvv")[1].innerText);
  500. const newDiv = document.createElement("div");
  501. newDiv.textContent = `奖励期望收益:
  502. ${formatCoinValue(coin + tokenCount * globalVariable.task.taskTokenValueData.rewardValueAsk)} /
  503. ${formatCoinValue(coin + tokenCount * globalVariable.task.taskTokenValueData.rewardValueBid)}`;
  504. newDiv.style.color = "rgb(248,0,248)";
  505. newDiv.classList.add("rewardValue");
  506. node.querySelector("div.RandomTask_action__3eC6o").appendChild(newDiv);
  507. globalVariable.task.taskValueElements.push(newDiv);
  508. });
  509. }
  510.  
  511. function showTaskShopItemValue() {
  512. globalVariable.task.taskShopElement = document.querySelector("div.TasksPanel_buyableGrid__2Ua51");
  513. // 如果不在商店面板,则销毁显示价值的元素
  514. if (!globalVariable.task.taskShopElement) {
  515. globalVariable.task.taskShopValueElements = [];
  516. globalVariable.task.hasTaskShopValueElement = false;
  517. globalVariable.task.taskShopElement = null;
  518. return;
  519. }
  520. // 如果已经存在价值的元素,不再更新
  521. if (globalVariable.task.hasTaskShopValueElement) return;
  522. globalVariable.task.hasTaskShopValueElement = true;
  523. const taskNodes = [...globalVariable.task.taskShopElement.querySelectorAll("div.TasksPanel_item__DWSpv")];
  524.  
  525. function convertKEndStringToNumber(str) {
  526. if (str.endsWith('K') || str.endsWith('k')) {
  527. return Number(str.slice(0, -1)) * 1000;
  528. } else {
  529. return Number(str);
  530. }
  531. }
  532.  
  533. const chestDropData = JSON.parse(localStorage.getItem("Edible_Tools")).Chest_Drop_Data;
  534. // parseFloat(chestDropData["Large Meteorite Cache"]["期望产出Bid"]),
  535. // parseFloat(chestDropData["Large Artisan's Crate"]["期望产出Bid"]),
  536. // parseFloat(chestDropData["Large Treasure Chest"]["期望产出Bid"]),
  537. taskNodes.forEach(function (node) {
  538. if (node.childNodes[2].textContent !== "30") return;
  539. const newDiv = document.createElement("div");
  540. if (node.childNodes[1].childNodes[0].childNodes[0].href.baseVal.endsWith("large_meteorite_cache")) {
  541. newDiv.textContent = `
  542. ${formatCoinValue(parseFloat(chestDropData["Large Meteorite Cache"]["期望产出Ask"]))} /
  543. ${formatCoinValue(parseFloat(chestDropData["Large Meteorite Cache"]["期望产出Bid"]))}`;
  544. } else if (node.childNodes[1].childNodes[0].childNodes[0].href.baseVal.endsWith("large_artisans_crate")) {
  545. newDiv.textContent = `
  546. ${formatCoinValue(parseFloat(chestDropData["Large Artisan's Crate"]["期望产出Ask"]))} /
  547. ${formatCoinValue(parseFloat(chestDropData["Large Artisan's Crate"]["期望产出Bid"]))}`;
  548. } else if (node.childNodes[1].childNodes[0].childNodes[0].href.baseVal.endsWith("large_treasure_chest")) {
  549. newDiv.textContent = `
  550. ${formatCoinValue(parseFloat(chestDropData["Large Treasure Chest"]["期望产出Ask"]))} /
  551. ${formatCoinValue(parseFloat(chestDropData["Large Treasure Chest"]["期望产出Bid"]))}`;
  552. }
  553. newDiv.style.color = "rgb(248,0,248)";
  554. newDiv.classList.add("taskShopValue");
  555. node.childNodes[2].insertAdjacentElement('beforebegin',newDiv);
  556. globalVariable.task.taskShopValueElements.push(newDiv);
  557. });
  558. }
  559. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址