Ranged Way Idle

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

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

  1. // ==UserScript==
  2. // @name Ranged Way Idle
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  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: true,
  20. listenKeywordMessages: true,
  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: {},
  39. buyListings: {}
  40. }
  41. };
  42. init();
  43.  
  44. function init() {
  45. globalVariable.whisperAudio.volume = 0.4;
  46. globalVariable.keywordAudio.volume = 0.4;
  47. let observer = new MutationObserver(function (mutationsList, observer) {
  48. if (config.showMarketListingsFunds) showMarketListingsFunds();
  49. if (config.autoTaskSort) autoClickTaskSortButton();
  50. });
  51. observer.observe(document, {childList: true, subtree: true});
  52. const dataProperty = Object.getOwnPropertyDescriptor(MessageEvent.prototype, "data");
  53. const oriGet = dataProperty.get;
  54. dataProperty.get = hookedGet;
  55. Object.defineProperty(MessageEvent.prototype, "data", dataProperty);
  56.  
  57. function hookedGet() {
  58. const socket = this.currentTarget;
  59. if (!(socket instanceof WebSocket)) {
  60. return oriGet.call(this);
  61. }
  62. if (socket.url.indexOf("api.milkywayidle.com/ws") <= -1 && socket.url.indexOf("api-test.milkywayidle.com/ws") <= -1) {
  63. return oriGet.call(this);
  64. }
  65. const message = oriGet.call(this);
  66. Object.defineProperty(this, "data", {value: message});
  67. return handleMessage(message);
  68. }
  69.  
  70. if (config.mournForMagicWayIdle) {
  71. console.log("为法师助手默哀")
  72. }
  73. }
  74.  
  75.  
  76. function handleMessage(message) {
  77. const obj = JSON.parse(message);
  78. if (!obj) return message;
  79. switch (obj.type) {
  80. case "init_character_data":
  81. globalVariable.keywords.push(obj.character.name.toLowerCase());
  82. updateMarketListings(obj.myMarketListings);
  83. break;
  84. case "market_listings_updated":
  85. updateMarketListings(obj.endMarketListings);
  86. break;
  87. case "new_battle":
  88. if (config.notifyDeath) initBattle(obj);
  89. break;
  90. case "battle_updated":
  91. if (config.notifyDeath) checkDeath(obj);
  92. break;
  93. case "market_item_order_books_updated":
  94. if (config.forceUpdateMarketPrice) marketPriceUpdate(obj);
  95. break;
  96. case "chat_message_received":
  97. handleChatMessage(obj);
  98. break;
  99. }
  100. return message;
  101. }
  102.  
  103. function notifyDeath(name) {
  104. new Notification('🎉🎉🎉喜报🎉🎉🎉', {body: `${name} 死了!`});
  105. }
  106.  
  107. function initBattle(obj) {
  108. globalVariable.battleData.players = [];
  109. for (let player of obj.players) {
  110. globalVariable.battleData.players.push({
  111. name: player.name, isAlive: player.currentHitpoints > 0,
  112. });
  113. if (player.currentHitpoints === 0) {
  114. notifyDeath(player.name);
  115. }
  116. }
  117. }
  118.  
  119. function checkDeath(obj) {
  120. for (let key in obj.pMap) {
  121. const index = parseInt(key);
  122. if (globalVariable.battleData.players[index].isAlive && obj.pMap[key].cHP === 0) {
  123. globalVariable.battleData.players[index].isAlive = false;
  124. notifyDeath(globalVariable.battleData.players[index].name);
  125. } else if (!globalVariable.battleData.players[index].isAlive && obj.pMap[key].cHP > 0) {
  126. globalVariable.battleData.players[index].isAlive = true;
  127. }
  128. }
  129. }
  130.  
  131. function marketPriceUpdate(obj) {
  132. // 本函数的代码复制自Magic Way Idle
  133. let itemDetailMap = globalVariable.itemDetailMap;
  134. let itemName = itemDetailMap[obj.marketItemOrderBooks.itemHrid].name;
  135. let ask = -1;
  136. let bid = -1;
  137. // 读取ask最低报价
  138. if (obj.marketItemOrderBooks.orderBooks[0].asks && obj.marketItemOrderBooks.orderBooks[0].asks.length > 0) {
  139. ask = obj.marketItemOrderBooks.orderBooks[0].asks[0].price;
  140. }
  141. // 读取bid最高报价
  142. if (obj.marketItemOrderBooks.orderBooks[0].bids && obj.marketItemOrderBooks.orderBooks[0].bids.length > 0) {
  143. bid = obj.marketItemOrderBooks.orderBooks[0].bids[0].price;
  144. }
  145. // 读取所有物品价格
  146. let jsonObj = JSON.parse(localStorage.getItem("MWITools_marketAPI_json"));
  147. // 修改当前查看物品价格
  148. if (jsonObj.market[itemName]) {
  149. jsonObj.market[itemName].ask = ask;
  150. jsonObj.market[itemName].bid = bid;
  151. }
  152. // 将修改后结果写回marketAPI缓存,完成对marketAPI价格的强制修改
  153. localStorage.setItem("MWITools_marketAPI_json", JSON.stringify(jsonObj));
  154. }
  155.  
  156. function handleChatMessage(obj) {
  157. if (obj.message.chan === "/chat_channel_types/whisper") {
  158. if (config.notifyWhisperMessages) {
  159. globalVariable.whisperAudio.play();
  160. }
  161. } else if (obj.message.chan === "/chat_channel_types/chinese") {
  162. if (config.listenKeywordMessages) {
  163. for (let keyword of globalVariable.keywords) {
  164. if (obj.message.m.toLowerCase().includes(keyword)) {
  165. globalVariable.keywordAudio.play();
  166. }
  167. }
  168. }
  169. }
  170. }
  171.  
  172. function autoClickTaskSortButton() {
  173. const targetElement = document.querySelector('#TaskSort');
  174. if (targetElement && targetElement.textContent !== '手动排序') {
  175. targetElement.click();
  176. targetElement.textContent = '手动排序';
  177. }
  178. }
  179.  
  180. function formatCoinValue(num) {
  181. if (num >= 1e13) {
  182. return (num / 1e12).toFixed(0) + "T";
  183. } else if (num >= 1e10) {
  184. return (num / 1e9).toFixed(0) + "B";
  185. } else if (num >= 1e7) {
  186. return (num / 1e6).toFixed(0) + "M";
  187. } else if (num >= 1e4) {
  188. return (num / 1e3).toFixed(0) + "K";
  189. }
  190. return num.toString();
  191. }
  192.  
  193. function updateMarketListings(obj) {
  194. for (listing of obj) {
  195. if (listing.status === "/market_listing_status/cancelled") {
  196. delete globalVariable.market[listing.isSell ? "sellListings" : "buyListings"][listing.id];
  197. continue
  198. }
  199. globalVariable.market[listing.isSell ? "sellListings" : "buyListings"][listing.id] = {
  200. itemHrid: listing.itemHrid,
  201. price: (listing.orderQuantity - listing.filledQuantity) * (listing.isSell ? Math.ceil(listing.price * 0.98) : listing.price),
  202. unclaimedCoinCount: listing.unclaimedCoinCount,
  203. }
  204. }
  205. globalVariable.market.buyValue = 0;
  206. globalVariable.market.sellValue = 0;
  207. globalVariable.market.unclaimedValue = 0;
  208. for (let id in globalVariable.market.buyListings) {
  209. const listing = globalVariable.market.buyListings[id];
  210. globalVariable.market.buyValue += listing.price;
  211. globalVariable.market.unclaimedValue += listing.unclaimedCoinCount;
  212. }
  213. for (let id in globalVariable.market.sellListings) {
  214. const listing = globalVariable.market.sellListings[id];
  215. globalVariable.market.sellValue += listing.price;
  216. globalVariable.market.unclaimedValue += listing.unclaimedCoinCount;
  217. }
  218. globalVariable.market.hasFundsElement = false;
  219. }
  220.  
  221. function showMarketListingsFunds() {
  222. if (globalVariable.market.hasFundsElement) return;
  223. const targetNode = document.querySelector("div.MarketplacePanel_coinStack__1l0UD");
  224. if (targetNode) {
  225. targetNode.style.top = "0px";
  226. targetNode.style.left = "0px";
  227. let fundsElement = document.querySelector("div.fundsElement");
  228. while (fundsElement) {
  229. fundsElement.remove();
  230. fundsElement = document.querySelector("div.fundsElement");
  231. }
  232. makeNode('<span style="color: rgb(102,204,255); font-weight: bold;">购买预付金</span>', globalVariable.market.buyValue, targetNode, ["125px", "0px"]);
  233. makeNode('<span style="color: rgb(102,204,255); font-weight: bold;">出售可获金</span>', globalVariable.market.sellValue, targetNode, ["125px", "22px"]);
  234. makeNode('<span style="color: rgb(102,204,255); font-weight: bold;">待领取金额</span>', globalVariable.market.unclaimedValue, targetNode, ["0px", "22px"]);
  235. globalVariable.market.hasFundsElement = true;
  236. }
  237. }
  238.  
  239. function makeNode(innerHTML, value, targetNode, style) {
  240. let node = targetNode.cloneNode(true);
  241. node.classList.add("fundsElement");
  242. const countNode = node.querySelector("div.Item_count__1HVvv");
  243. const textNode = node.querySelector("div.Item_name__2C42x");
  244. if (countNode) countNode.textContent = formatCoinValue(value);
  245. if (textNode) textNode.innerHTML = innerHTML;
  246. node.style.left = style[0];
  247. node.style.top = style[1];
  248. targetNode.parentNode.insertBefore(node, targetNode.nextSibling);
  249. }
  250. })();

QingJ © 2025

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