Ranged Way Idle

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

当前为 2025-05-13 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Ranged Way Idle
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.4
  5. // @description 死亡提醒、强制刷新MWITools的价格、私信提醒音、自动任务排序、显示购买预付金/出售可获金/待领取金额、默哀法师助手
  6. // @author AlphB
  7. // @match https://www.milkywayidle.com/*
  8. // @match https://test.milkywayidle.com/*
  9. // @grant GM_notification
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com
  11. // @grant none
  12. // @license CC-BY-NC-SA-4.0
  13. // ==/UserScript==
  14.  
  15. (function () {
  16. const config = {
  17. notifyDeath: true,
  18. forceUpdateMarketPrice: true,
  19. notifyWhisperMessages: false,
  20. listenKeywordMessages: false,
  21. autoTaskSort: true,
  22. showMarketListingsFunds: true,
  23. mournForMagicWayIdle: true
  24. }
  25. const globalVariable = {
  26. battleData: {
  27. players: null
  28. },
  29. itemDetailMap: JSON.parse(localStorage.getItem("initClientData")).itemDetailMap,
  30. whisperAudio: new Audio(`https://upload.thbwiki.cc/d/d1/se_bonus2.mp3`),
  31. keywordAudio: new Audio(`https://upload.thbwiki.cc/c/c9/se_pldead00.mp3`),
  32. keywords: [],
  33. market: {
  34. hasFundsElement: false,
  35. sellValue: null,
  36. buyValue: null,
  37. unclaimedValue: null,
  38. sellListings: null,
  39. buyListings: null
  40. },
  41. taskTokenValueData: null
  42. };
  43. init();
  44.  
  45. function init() {
  46. globalVariable.whisperAudio.volume = 0.4;
  47. globalVariable.keywordAudio.volume = 0.4;
  48. let observer = new MutationObserver(function (mutationsList, observer) {
  49. if (config.showMarketListingsFunds) showMarketListingsFunds();
  50. if (config.autoTaskSort) autoClickTaskSortButton();
  51. });
  52. observer.observe(document, {childList: true, subtree: true});
  53. const dataProperty = Object.getOwnPropertyDescriptor(MessageEvent.prototype, "data");
  54. const oriGet = dataProperty.get;
  55. dataProperty.get = hookedGet;
  56. Object.defineProperty(MessageEvent.prototype, "data", dataProperty);
  57.  
  58. function hookedGet() {
  59. const socket = this.currentTarget;
  60. if (!(socket instanceof WebSocket)) {
  61. return oriGet.call(this);
  62. }
  63. if (socket.url.indexOf("api.milkywayidle.com/ws") <= -1 && socket.url.indexOf("api-test.milkywayidle.com/ws") <= -1) {
  64. return oriGet.call(this);
  65. }
  66. const message = oriGet.call(this);
  67. Object.defineProperty(this, "data", {value: message});
  68. return handleMessage(message);
  69. }
  70.  
  71. if (config.mournForMagicWayIdle) {
  72. console.log("为法师助手默哀")
  73. }
  74. }
  75.  
  76.  
  77. function handleMessage(message) {
  78. const obj = JSON.parse(message);
  79. if (!obj) return message;
  80. switch (obj.type) {
  81. case "init_character_data":
  82. globalVariable.market.sellListings = {};
  83. globalVariable.market.buyListings = {};
  84. globalVariable.keywords.push(obj.character.name.toLowerCase());
  85. updateMarketListings(obj.myMarketListings);
  86. break;
  87. case "market_listings_updated":
  88. updateMarketListings(obj.endMarketListings);
  89. break;
  90. case "new_battle":
  91. if (config.notifyDeath) initBattle(obj);
  92. break;
  93. case "battle_updated":
  94. if (config.notifyDeath) checkDeath(obj);
  95. break;
  96. case "market_item_order_books_updated":
  97. if (config.forceUpdateMarketPrice) marketPriceUpdate(obj);
  98. break;
  99. case "chat_message_received":
  100. handleChatMessage(obj);
  101. break;
  102. }
  103. return message;
  104. }
  105.  
  106. function notifyDeath(name) {
  107. new Notification('🎉🎉🎉喜报🎉🎉🎉', {body: `${name} 死了!`});
  108. }
  109.  
  110. function initBattle(obj) {
  111. globalVariable.battleData.players = [];
  112. for (let player of obj.players) {
  113. globalVariable.battleData.players.push({
  114. name: player.name, isAlive: player.currentHitpoints > 0,
  115. });
  116. if (player.currentHitpoints === 0) {
  117. notifyDeath(player.name);
  118. }
  119. }
  120. }
  121.  
  122. function checkDeath(obj) {
  123. for (let key in obj.pMap) {
  124. const index = parseInt(key);
  125. if (globalVariable.battleData.players[index].isAlive && obj.pMap[key].cHP === 0) {
  126. globalVariable.battleData.players[index].isAlive = false;
  127. notifyDeath(globalVariable.battleData.players[index].name);
  128. } else if (!globalVariable.battleData.players[index].isAlive && obj.pMap[key].cHP > 0) {
  129. globalVariable.battleData.players[index].isAlive = true;
  130. }
  131. }
  132. }
  133.  
  134. function marketPriceUpdate(obj) {
  135. // 本函数的代码复制自Magic Way Idle
  136. let itemDetailMap = globalVariable.itemDetailMap;
  137. let itemName = itemDetailMap[obj.marketItemOrderBooks.itemHrid].name;
  138. let ask = -1;
  139. let bid = -1;
  140. // 读取ask最低报价
  141. if (obj.marketItemOrderBooks.orderBooks[0].asks && obj.marketItemOrderBooks.orderBooks[0].asks.length > 0) {
  142. ask = obj.marketItemOrderBooks.orderBooks[0].asks[0].price;
  143. }
  144. // 读取bid最高报价
  145. if (obj.marketItemOrderBooks.orderBooks[0].bids && obj.marketItemOrderBooks.orderBooks[0].bids.length > 0) {
  146. bid = obj.marketItemOrderBooks.orderBooks[0].bids[0].price;
  147. }
  148. // 读取所有物品价格
  149. let jsonObj = JSON.parse(localStorage.getItem("MWITools_marketAPI_json"));
  150. // 修改当前查看物品价格
  151. if (jsonObj.market[itemName]) {
  152. jsonObj.market[itemName].ask = ask;
  153. jsonObj.market[itemName].bid = bid;
  154. }
  155. // 将修改后结果写回marketAPI缓存,完成对marketAPI价格的强制修改
  156. localStorage.setItem("MWITools_marketAPI_json", JSON.stringify(jsonObj));
  157. globalVariable.taskTokenValueData = getTaskTokenValue();
  158. }
  159.  
  160. function handleChatMessage(obj) {
  161. if (obj.message.chan === "/chat_channel_types/whisper") {
  162. if (config.notifyWhisperMessages) {
  163. globalVariable.whisperAudio.play();
  164. }
  165. } else if (obj.message.chan === "/chat_channel_types/chinese") {
  166. if (config.listenKeywordMessages) {
  167. for (let keyword of globalVariable.keywords) {
  168. if (obj.message.m.toLowerCase().includes(keyword)) {
  169. globalVariable.keywordAudio.play();
  170. }
  171. }
  172. }
  173. }
  174. }
  175.  
  176. function autoClickTaskSortButton() {
  177. const targetElement = document.querySelector('#TaskSort');
  178. if (targetElement && targetElement.textContent !== '手动排序') {
  179. targetElement.click();
  180. targetElement.textContent = '手动排序';
  181. }
  182. }
  183.  
  184. function formatCoinValue(num) {
  185. if (num >= 1e13) {
  186. return Math.floor(num / 1e12) + "T";
  187. } else if (num >= 1e10) {
  188. return Math.floor(num / 1e9) + "B";
  189. } else if (num >= 1e7) {
  190. return Math.floor(num / 1e6) + "M";
  191. } else if (num >= 1e4) {
  192. return Math.floor(num / 1e3) + "K";
  193. }
  194. return num.toString();
  195. }
  196.  
  197. function updateMarketListings(obj) {
  198. for (let listing of obj) {
  199. if (listing.status === "/market_listing_status/cancelled") {
  200. delete globalVariable.market[listing.isSell ? "sellListings" : "buyListings"][listing.id];
  201. continue
  202. }
  203. globalVariable.market[listing.isSell ? "sellListings" : "buyListings"][listing.id] = {
  204. itemHrid: listing.itemHrid,
  205. price: (listing.orderQuantity - listing.filledQuantity) * (listing.isSell ? Math.ceil(listing.price * 0.98) : listing.price),
  206. unclaimedCoinCount: listing.unclaimedCoinCount,
  207. }
  208. }
  209. globalVariable.market.buyValue = 0;
  210. globalVariable.market.sellValue = 0;
  211. globalVariable.market.unclaimedValue = 0;
  212. for (let id in globalVariable.market.buyListings) {
  213. const listing = globalVariable.market.buyListings[id];
  214. globalVariable.market.buyValue += listing.price;
  215. globalVariable.market.unclaimedValue += listing.unclaimedCoinCount;
  216. }
  217. for (let id in globalVariable.market.sellListings) {
  218. const listing = globalVariable.market.sellListings[id];
  219. globalVariable.market.sellValue += listing.price;
  220. globalVariable.market.unclaimedValue += listing.unclaimedCoinCount;
  221. }
  222. globalVariable.market.hasFundsElement = false;
  223. }
  224.  
  225. function showMarketListingsFunds() {
  226. function makeNode(innerHTML, value, targetNode, style) {
  227. let node = targetNode.cloneNode(true);
  228. node.classList.add("fundsElement");
  229. const countNode = node.querySelector("div.Item_count__1HVvv");
  230. const textNode = node.querySelector("div.Item_name__2C42x");
  231. if (countNode) countNode.textContent = formatCoinValue(value);
  232. if (textNode) textNode.innerHTML = innerHTML;
  233. node.style.left = style[0];
  234. node.style.top = style[1];
  235. targetNode.parentNode.insertBefore(node, targetNode.nextSibling);
  236. }
  237.  
  238. if (globalVariable.market.hasFundsElement) return;
  239. const targetNode = document.querySelector("div.MarketplacePanel_coinStack__1l0UD");
  240. if (targetNode) {
  241. targetNode.style.top = "0px";
  242. targetNode.style.left = "0px";
  243. let fundsElement = document.querySelector("div.fundsElement");
  244. while (fundsElement) {
  245. fundsElement.remove();
  246. fundsElement = document.querySelector("div.fundsElement");
  247. }
  248. makeNode('<span style="color: rgb(102,204,255); font-weight: bold;">购买预付金</span>', globalVariable.market.buyValue, targetNode, ["125px", "0px"]);
  249. makeNode('<span style="color: rgb(102,204,255); font-weight: bold;">出售可获金</span>', globalVariable.market.sellValue, targetNode, ["125px", "22px"]);
  250. makeNode('<span style="color: rgb(102,204,255); font-weight: bold;">待领取金额</span>', globalVariable.market.unclaimedValue, targetNode, ["0px", "22px"]);
  251. globalVariable.market.hasFundsElement = true;
  252. }
  253. }
  254.  
  255. function getTaskTokenValue() {
  256. const chestDropData = JSON.parse(localStorage.getItem("Edible_Tools")).Chest_Drop_Data;
  257. const lootsName = ["大陨石舱", "大工匠匣", "大宝箱"];
  258. const bidValueList = [
  259. parseFloat(chestDropData["Large Meteorite Cache"]["期望产出Bid"]),
  260. parseFloat(chestDropData["Large Artisan's Crate"]["期望产出Bid"]),
  261. parseFloat(chestDropData["Large Treasure Chest"]["期望产出Bid"]),
  262. ]
  263. const askValueList = [
  264. parseFloat(chestDropData["Large Meteorite Cache"]["期望产出Ask"]),
  265. parseFloat(chestDropData["Large Artisan's Crate"]["期望产出Ask"]),
  266. parseFloat(chestDropData["Large Treasure Chest"]["期望产出Ask"]),
  267. ]
  268. const res = {
  269. bidValue: Math.max(...bidValueList),
  270. askValue: Math.max(...askValueList)
  271. }
  272. res.bidLoots = lootsName[bidValueList.indexOf(res.bidValue)];
  273. res.askLoots = lootsName[askValueList.indexOf(res.askValue)];
  274. res.bidValue = Math.round(res.bidValue / 30 + parseFloat(chestDropData["Purple's Gift"]["期望产出Bid"]) / 50);
  275. res.askValue = Math.round(res.askValue / 30 + parseFloat(chestDropData["Purple's Gift"]["期望产出Ask"]) / 50);
  276. console.log(res);
  277. if (config.forceUpdateMarketPrice) {
  278. const marketJSON = JSON.parse(localStorage.getItem("MWITools_marketAPI_json"));
  279. marketJSON.market["Task Token"].ask = res.askValue;
  280. marketJSON.market["Task Token"].bid = res.bidValue;
  281. localStorage.setItem("MWITools_marketAPI_json", JSON.stringify(marketJSON));
  282. }
  283. return res;
  284. }
  285. })();

QingJ © 2025

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