🐭️ MouseHunt - Minluck & Catch Rate Estimate

View the minluck and catch rate estimate, right on the camp page.

  1. // ==UserScript==
  2. // @name 🐭️ MouseHunt - Minluck & Catch Rate Estimate
  3. // @description View the minluck and catch rate estimate, right on the camp page.
  4. // @version 2.1.1
  5. // @license MIT
  6. // @author bradp
  7. // @namespace bradp
  8. // @match https://www.mousehuntgame.com/*
  9. // @icon https://i.mouse.rip/mh-improved/icon-64.png
  10. // @run-at document-end
  11. // @grant none
  12. // @require https://cdn.jsdelivr.net/npm/script-migration@1.1.1
  13. // ==/UserScript==
  14.  
  15. var mhui = (() => {
  16. var __defProp = Object.defineProperty;
  17. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  18. var __getOwnPropNames = Object.getOwnPropertyNames;
  19. var __hasOwnProp = Object.prototype.hasOwnProperty;
  20. var __export = (target, all) => {
  21. for (var name in all)
  22. __defProp(target, name, { get: all[name], enumerable: true });
  23. };
  24. var __copyProps = (to, from, except, desc) => {
  25. if (from && typeof from === "object" || typeof from === "function") {
  26. for (let key of __getOwnPropNames(from))
  27. if (!__hasOwnProp.call(to, key) && key !== except)
  28. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  29. }
  30. return to;
  31. };
  32. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  33. var __async = (__this, __arguments, generator) => {
  34. return new Promise((resolve, reject) => {
  35. var fulfilled = (value) => {
  36. try {
  37. step(generator.next(value));
  38. } catch (e) {
  39. reject(e);
  40. }
  41. };
  42. var rejected = (value) => {
  43. try {
  44. step(generator.throw(value));
  45. } catch (e) {
  46. reject(e);
  47. }
  48. };
  49. var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
  50. step((generator = generator.apply(__this, __arguments)).next());
  51. });
  52. };
  53.  
  54. // src/modules/catch-rate-estimate/index.js
  55. var catch_rate_estimate_exports = {};
  56. __export(catch_rate_estimate_exports, {
  57. default: () => catch_rate_estimate_default
  58. });
  59.  
  60. // src/utils/event-registry.js
  61. var doEvent2 = (eventName, params) => {
  62. if (!eventRegistry) {
  63. return;
  64. }
  65. eventRegistry.doEvent(eventName, params);
  66. };
  67. var eventsAdded = {};
  68. var onEvent = (event, callback, remove = false) => {
  69. if (!eventRegistry) {
  70. return;
  71. }
  72. const id = `${event}-${remove.toString()}-${callback.toString()}`;
  73. if (eventsAdded[id]) {
  74. return;
  75. }
  76. eventsAdded[id] = true;
  77. eventRegistry.addEventListener(event, callback, null, remove);
  78. };
  79.  
  80. // src/utils/styles.js
  81. var addModuleStyles = (styles, identifier = "mh-improved-styles", replace = false) => {
  82. const existingStyles = document.querySelector(`#${identifier}`);
  83. styles = Array.isArray(styles) ? styles.join("\n") : styles;
  84. if (existingStyles) {
  85. if (replace) {
  86. existingStyles.innerHTML = styles;
  87. } else {
  88. existingStyles.innerHTML += styles;
  89. }
  90. return existingStyles;
  91. }
  92. const style = document.createElement("style");
  93. style.id = identifier;
  94. style.innerHTML = styles;
  95. document.head.append(style);
  96. return style;
  97. };
  98. var addStyles = (styles, module = false, identifier = "mh-improved-styles") => {
  99. if (!module) {
  100. throw new Error("Module ID is required for adding module styles.", module);
  101. }
  102. const key = `${identifier}-${module}`;
  103. let stylesEl = addModuleStyles(styles, key, true);
  104. onEvent(`mh-improved-settings-changed-${module}`, (enabled) => {
  105. if (enabled) {
  106. stylesEl = addModuleStyles(styles, key, true);
  107. } else if (stylesEl) {
  108. stylesEl.remove();
  109. }
  110. });
  111. };
  112.  
  113. // src/utils/settings.js
  114. var getSettingDirect = (key = null, defaultValue = null, identifier = "mousehunt-improved-settings") => {
  115. const settings = JSON.parse(localStorage.getItem(identifier)) || {};
  116. if (!key) {
  117. return settings;
  118. }
  119. if (!key.includes(".")) {
  120. if (settings[key] === void 0) {
  121. return defaultValue;
  122. }
  123. return settings[key];
  124. }
  125. const groupAndKey = getGroupAndKey(key);
  126. if (!groupAndKey.group) {
  127. if (settings[groupAndKey.key] === void 0) {
  128. return defaultValue;
  129. }
  130. return settings[groupAndKey.key];
  131. }
  132. const groupSettings = settings[groupAndKey.group] || {};
  133. if (groupSettings[groupAndKey.key] === void 0) {
  134. return defaultValue;
  135. }
  136. return groupSettings[groupAndKey.key];
  137. };
  138. var getGroupAndKey = (key) => {
  139. const split = key.split(".");
  140. if (split.length === 1) {
  141. return {
  142. group: null,
  143. key: split[0]
  144. };
  145. }
  146. if (split[0] === "location-huds-enabled") {
  147. return {
  148. group: "location-huds-enabled",
  149. key: split[1]
  150. };
  151. }
  152. return {
  153. group: `${split[0]}-settings`,
  154. key: split[1]
  155. };
  156. };
  157. var getSetting = (key, defaultValue = false) => {
  158. return getSettingDirect(key, defaultValue, "mousehunt-improved-settings");
  159. };
  160.  
  161. // src/utils/elements.js
  162. var makeElement = (tag, classes = "", text = "", appendTo = null) => {
  163. const element = document.createElement(tag);
  164. if (Array.isArray(classes)) {
  165. classes = classes.join(" ");
  166. }
  167. if (classes && classes.length) {
  168. element.className = classes;
  169. }
  170. element.innerHTML = text;
  171. if (appendTo) {
  172. appendTo.append(element);
  173. return appendTo;
  174. }
  175. return element;
  176. };
  177.  
  178. // src/utils/db.js
  179. var database = (databaseName) => __async(void 0, null, function* () {
  180. return new Promise((resolve, reject) => {
  181. const request = indexedDB.open(`mh-improved-${databaseName}`, 6);
  182. request.onerror = (event) => {
  183. reject(event.target.error);
  184. };
  185. request.onsuccess = (event) => {
  186. resolve(event.target.result);
  187. };
  188. request.onupgradeneeded = (event) => {
  189. const db = event.target.result;
  190. if (!db.objectStoreNames.contains(databaseName)) {
  191. db.createObjectStore(databaseName, { keyPath: "id" });
  192. }
  193. };
  194. });
  195. });
  196. var dbGet = (databaseName, id) => __async(void 0, null, function* () {
  197. const db = yield database(databaseName);
  198. const transaction = db.transaction(databaseName, "readonly");
  199. transaction.onerror = (event) => {
  200. throw new Error(event.target.error);
  201. };
  202. const objectStore = transaction.objectStore(databaseName);
  203. const request = objectStore.get(id);
  204. return new Promise((resolve, reject) => {
  205. request.onsuccess = () => {
  206. resolve(request.result);
  207. };
  208. request.onerror = () => {
  209. reject(request.error);
  210. };
  211. transaction.oncomplete = () => {
  212. db.close();
  213. };
  214. });
  215. });
  216. var dbSet = (databaseName, data) => __async(void 0, null, function* () {
  217. const db = yield database(databaseName);
  218. const transaction = db.transaction(databaseName, "readwrite");
  219. const objectStore = transaction.objectStore(databaseName);
  220. data = {
  221. data,
  222. id: data.id || Date.now()
  223. };
  224. const request = objectStore.put(data);
  225. return new Promise((resolve, reject) => {
  226. request.onsuccess = () => {
  227. resolve(request.result);
  228. };
  229. request.onerror = () => {
  230. reject(request.error);
  231. };
  232. transaction.oncomplete = () => {
  233. db.close();
  234. };
  235. });
  236. });
  237.  
  238. // src/utils/global.js
  239. var getGlobal = (key) => {
  240. if (window && window.mhui) {
  241. return window.mhui[key] || false;
  242. }
  243. if ("undefined" !== typeof app && app && app.mhui) {
  244. return app.mhui[key] || false;
  245. }
  246. return false;
  247. };
  248.  
  249. // src/utils/debug.js
  250. var debuglog = (module, message, ...args) => {
  251. if (getSetting("debug.all", false) || getSetting(`debug.${module}`, false) || getGlobal("mh-improved-updating")) {
  252. console.log(
  253. `%cMH Improved %c${module}%c ${message}`,
  254. "color: #90588c; font-weight: 900",
  255. "color: #90588c; font-weight: 400",
  256. "color: inherit; font-weight: inherit",
  257. ...args
  258. );
  259. }
  260. };
  261.  
  262. // src/utils/data.js
  263. var validDataFiles = /* @__PURE__ */ new Set([
  264. "effs",
  265. "environments-events",
  266. "environments",
  267. "item-thumbnails",
  268. "items-tradable",
  269. "m400-locations",
  270. "marketplace-hidden-items",
  271. "mice-groups",
  272. "mice-regions",
  273. "mice-thumbnails",
  274. "minlucks",
  275. "relic-hunter-hints",
  276. "scoreboards",
  277. "ultimate-checkmark",
  278. "upscaled-images",
  279. "wisdom"
  280. ]);
  281. var isValidDataFile = (file) => {
  282. return validDataFiles.has(file);
  283. };
  284. var getCacheExpiration = (key = null) => __async(void 0, null, function* () {
  285. return yield cacheGet(`expiration-${key}`, false);
  286. });
  287. var setCacheExpiration = (key) => __async(void 0, null, function* () {
  288. debuglog("utils-data", `Setting cache expiration for ${key}`);
  289. cacheSet(`expiration-${key}`, Date.now() + (Math.floor(Math.random() * 7) + 7) * 24 * 60 * 60 * 1e3);
  290. });
  291. var isCacheExpired = (key) => __async(void 0, null, function* () {
  292. const expiration = yield getCacheExpiration(key);
  293. if (!expiration) {
  294. return true;
  295. }
  296. return expiration.date < Date.now();
  297. });
  298. var fetchData = (key, retries = 0) => __async(void 0, null, function* () {
  299. try {
  300. const data = yield fetch(`https://api.mouse.rip/${key}`, {
  301. method: "GET",
  302. headers: getHeaders()
  303. });
  304. return yield data.json();
  305. } catch (error) {
  306. console.error(`Error fetching data for ${key}:`, error);
  307. if (retries >= 3) {
  308. return false;
  309. }
  310. yield new Promise((resolve) => setTimeout(resolve, 500 * retries));
  311. return fetchData(key, retries + 1);
  312. }
  313. });
  314. var getData = (key) => __async(void 0, null, function* () {
  315. if (!isValidDataFile(key)) {
  316. debuglog("utils-data", `Cannot get data for ${key}, invalid key`);
  317. return false;
  318. }
  319. const isExpired = yield isCacheExpired(key);
  320. if (!isExpired) {
  321. const cachedData = yield dataCacheGet(key, false);
  322. if (cachedData) {
  323. return cachedData;
  324. }
  325. }
  326. debuglog("utils-data", `Fetching data for ${key}\u2026`);
  327. const data = yield fetchData(key);
  328. debuglog("utils-data", `Fetched data for ${key}`, data);
  329. if (data) {
  330. dataCacheSet(key, data);
  331. setCacheExpiration(key);
  332. }
  333. return data;
  334. });
  335. var getHeaders = () => {
  336. return {
  337. "Content-Type": "application/json",
  338. "X-MH-Improved": "true",
  339. "X-MH-Improved-Version": mhImprovedVersion || "unknown",
  340. "X-MH-Improved-Platform": mhImprovedPlatform || "unknown"
  341. };
  342. };
  343. var cacheSet = (key, value) => {
  344. dbSet("cache", { id: key, value });
  345. };
  346. var dataCacheSet = (key, value) => {
  347. dbSet("data", { id: key, value });
  348. };
  349. var cacheGetHelper = (key, defaultValue = false, db = "cache") => __async(void 0, null, function* () {
  350. var _a;
  351. const cached = yield dbGet(db, key);
  352. if (!((_a = cached == null ? void 0 : cached.data) == null ? void 0 : _a.value)) {
  353. return defaultValue;
  354. }
  355. return cached.data.value;
  356. });
  357. var cacheGet = (key, defaultValue = false) => __async(void 0, null, function* () {
  358. return yield cacheGetHelper(key, defaultValue, "cache");
  359. });
  360. var dataCacheGet = (key, defaultValue = false) => __async(void 0, null, function* () {
  361. return yield cacheGetHelper(key, defaultValue, "data");
  362. });
  363.  
  364. // src/utils/page.js
  365. var getCurrentPage = () => {
  366. var _a, _b, _c, _d;
  367. if (!((_b = (_a = hg == null ? void 0 : hg.utils) == null ? void 0 : _a.PageUtil) == null ? void 0 : _b.getCurrentPage)) {
  368. return null;
  369. }
  370. const page = hg.utils.PageUtil.getCurrentPage();
  371. if (!page) {
  372. const query = ((_d = (_c = hg == null ? void 0 : hg.utils) == null ? void 0 : _c.PageUtil) == null ? void 0 : _d.getQueryParams()) || {};
  373. if ((query == null ? void 0 : query.switch_to) && "mobile" === query.switch_to) {
  374. return "camp";
  375. }
  376. return null;
  377. }
  378. return page.toLowerCase();
  379. };
  380. var getCurrentTab = () => {
  381. var _a, _b;
  382. if (!((_b = (_a = hg == null ? void 0 : hg.utils) == null ? void 0 : _a.PageUtil) == null ? void 0 : _b.getCurrentPageTab)) {
  383. return getCurrentPage();
  384. }
  385. const tab = hg.utils.PageUtil.getCurrentPageTab() || "";
  386. if (tab.length <= 0) {
  387. return getCurrentPage();
  388. }
  389. return tab.toLowerCase();
  390. };
  391. var getCurrentSubtab = () => {
  392. const subtab = hg.utils.PageUtil.getCurrentPageSubTab();
  393. if (!subtab || subtab.length <= 0) {
  394. return getCurrentTab();
  395. }
  396. return subtab.toLowerCase();
  397. };
  398. var isCurrentPage = (targetPage = null, targetTab = null, targetSubtab = null, forceCurrentPage = null, forceCurrentTab = null, forceCurrentSubtab = null) => {
  399. if (!targetPage) {
  400. return false;
  401. }
  402. const currentPage = forceCurrentPage || getCurrentPage();
  403. if (!targetTab) {
  404. return currentPage === targetPage;
  405. }
  406. const currentTab = forceCurrentTab || getCurrentTab();
  407. if (!targetSubtab) {
  408. return currentPage === targetPage && currentTab === targetTab;
  409. }
  410. const currentSubtab = forceCurrentSubtab || getCurrentSubtab();
  411. if (currentSubtab === currentTab) {
  412. return currentPage === targetPage && currentTab === targetTab;
  413. }
  414. return currentPage === targetPage && currentTab === targetTab && currentSubtab === targetSubtab;
  415. };
  416.  
  417. // src/utils/location.js
  418. var getCurrentLocation = () => {
  419. const location = (user == null ? void 0 : user.environment_type) || "";
  420. return location.toLowerCase();
  421. };
  422.  
  423. // src/utils/horn.js
  424. var showHornMessage = (options) => {
  425. const huntersHornView = document.querySelector(".huntersHornView__messageContainer");
  426. if (!huntersHornView) {
  427. return;
  428. }
  429. const settings = {
  430. title: options.title || "Hunters Horn",
  431. text: options.text || "This is a message from the Hunters Horn",
  432. button: options.button || "OK",
  433. action: options.action || (() => {
  434. }),
  435. dismiss: options.dismiss || null,
  436. type: options.type || "recent_linked_turn",
  437. classname: options.classname || "",
  438. image: options.image || null,
  439. imageLink: options.imageLink || null,
  440. imageCallback: options.imageCallback || null
  441. };
  442. const backdrop = document.querySelector(".huntersHornView__backdrop");
  443. if (backdrop) {
  444. backdrop.classList.add("huntersHornView__backdrop--active");
  445. }
  446. const gameInfo = document.querySelector(".mousehuntHud-gameInfo");
  447. if (gameInfo) {
  448. gameInfo.classList.add("blur");
  449. }
  450. const messageWrapper = makeElement("div", ["huntersHornView__message huntersHornView__message--active", settings.classname]);
  451. const message = makeElement("div", ["huntersHornMessageView", `huntersHornMessageView--${settings.type}`]);
  452. makeElement("div", "huntersHornMessageView__title", settings.title, message);
  453. const content = makeElement("div", "huntersHornMessageView__content");
  454. if (settings.image) {
  455. const imgWrapper = makeElement("div", "huntersHornMessageView__friend");
  456. const img = makeElement("a", "huntersHornMessageView__friendProfilePic");
  457. if (settings.imageLink) {
  458. img.href = settings.imageLink;
  459. } else if (settings.imageCallback) {
  460. img.addEventListener("click", settings.imageCallback);
  461. } else {
  462. img.href = "#";
  463. }
  464. img.style.backgroundImage = `url(${settings.image})`;
  465. imgWrapper.append(img);
  466. content.append(imgWrapper);
  467. }
  468. makeElement("div", "huntersHornMessageView__text", settings.text, content);
  469. const buttonSpacer = makeElement("div", "huntersHornMessageView__buttonSpacer");
  470. const button = makeElement("button", "huntersHornMessageView__action");
  471. const buttonLabel = makeElement("div", "huntersHornMessageView__actionLabel");
  472. makeElement("span", "huntersHornMessageView__actionText", settings.button, buttonLabel);
  473. button.append(buttonLabel);
  474. button.addEventListener("click", () => {
  475. if (settings.action) {
  476. settings.action();
  477. }
  478. messageWrapper.innerHTML = "";
  479. backdrop.classList.remove("huntersHornView__backdrop--active");
  480. gameInfo.classList.remove("blur");
  481. });
  482. buttonSpacer.append(button);
  483. content.append(buttonSpacer);
  484. message.append(content);
  485. if (settings.dismiss) {
  486. const countdown = makeElement("button", ["huntersHornMessageView__countdown"]);
  487. makeElement("div", "huntersHornMessageView__countdownButtonImage", "", countdown);
  488. const svgMarkup = `<svg class="huntersHornMessageView__countdownSVG">
  489. <circle r="46%" cx="50%" cy="50%" class="huntersHornMessageView__countdownCircleTrack"></circle>
  490. <circle r="46%" cx="50%" cy="50%" class="huntersHornMessageView__countdownCircle" style="animation-duration: ${settings.dismiss}ms;"></circle>
  491. </svg>`;
  492. countdown.innerHTML += svgMarkup;
  493. countdown.addEventListener("click", () => {
  494. countdown.classList.add("huntersHornMessageView__countdown--complete");
  495. messageWrapper.innerHTML = "";
  496. backdrop.classList.remove("huntersHornView__backdrop--active");
  497. gameInfo.classList.remove("blur");
  498. });
  499. message.append(countdown);
  500. }
  501. messageWrapper.append(message);
  502. const existingMessages = huntersHornView.querySelector(".huntersHornView__message");
  503. if (existingMessages) {
  504. existingMessages.remove();
  505. }
  506. huntersHornView.append(messageWrapper);
  507. if (settings.dismiss) {
  508. setTimeout(() => {
  509. const countdown = messageWrapper.querySelector(".huntersHornMessageView__countdown");
  510. if (countdown) {
  511. countdown.classList.add("huntersHornMessageView__countdown--complete");
  512. }
  513. messageWrapper.innerHTML = "";
  514. backdrop.classList.remove("huntersHornView__backdrop--active");
  515. gameInfo.classList.remove("blur");
  516. }, settings.dismiss);
  517. }
  518. };
  519.  
  520. // src/utils/events.js
  521. var requestCallbacks = {};
  522. var onRequestHolder = null;
  523. var onRequest = (url = null, callback = null, skipSuccess = false, ignore = []) => {
  524. url = "*" === url ? "*" : `managers/ajax/${url}`;
  525. if (ignore.includes(url)) {
  526. return;
  527. }
  528. if (!callback) {
  529. return;
  530. }
  531. if (!requestCallbacks[url]) {
  532. requestCallbacks[url] = [];
  533. }
  534. requestCallbacks[url].push({
  535. callback,
  536. skipSuccess
  537. });
  538. if (onRequestHolder) {
  539. return;
  540. }
  541. const req = XMLHttpRequest.prototype.open;
  542. XMLHttpRequest.prototype.open = function() {
  543. this.addEventListener("load", function() {
  544. if (this.responseText) {
  545. let response = {};
  546. try {
  547. response = JSON.parse(this.responseText);
  548. } catch (e) {
  549. return;
  550. }
  551. Object.keys(requestCallbacks).forEach((key) => {
  552. if ("*" === key || this.responseURL.includes(key)) {
  553. requestCallbacks[key].forEach((item) => {
  554. if (item.callback && typeof item.callback === "function" && (item.skipSuccess || (response == null ? void 0 : response.success))) {
  555. item.callback(response);
  556. }
  557. });
  558. }
  559. });
  560. }
  561. });
  562. Reflect.apply(req, this, arguments);
  563. };
  564. onRequestHolder = true;
  565. };
  566. var onTravel = (location, options) => {
  567. eventRegistry.addEventListener("travel_complete", () => onTravelCallback(location, options));
  568. };
  569. var onTravelCallback = (location, options) => {
  570. if (location && location !== getCurrentLocation()) {
  571. return;
  572. }
  573. if (options == null ? void 0 : options.shouldAddReminder) {
  574. showHornMessage({
  575. title: options.title || "",
  576. text: options.text || "",
  577. button: options.button || "Dismiss",
  578. action: options.action || null
  579. });
  580. }
  581. if (options.callback) {
  582. options.callback();
  583. }
  584. };
  585. var callbacks = [];
  586. var hasAddedNavigationListener = false;
  587. var onNavigation = (callback, options = {}) => {
  588. const defaults = {
  589. page: false,
  590. tab: false,
  591. subtab: false,
  592. onLoad: true,
  593. anyTab: false,
  594. anySubtab: false
  595. };
  596. const { page, tab, subtab, onLoad, anyTab, anySubtab } = Object.assign(defaults, options);
  597. const bypassMatch = !page;
  598. if (onLoad && (bypassMatch || isCurrentPage(
  599. page,
  600. anyTab ? getCurrentTab() : tab,
  601. anySubtab ? getCurrentSubtab() : subtab
  602. ))) {
  603. callback();
  604. }
  605. callbacks.push({ callback, page, tab, subtab, bypassMatch });
  606. if (!hasAddedNavigationListener) {
  607. addNavigationListeners();
  608. hasAddedNavigationListener = true;
  609. }
  610. };
  611. var addNavigationListeners = () => {
  612. eventRegistry.addEventListener("set_page", (e) => {
  613. var _a;
  614. const tabs = ((_a = e == null ? void 0 : e.data) == null ? void 0 : _a.tabs) || {};
  615. const currentTab = Object.keys(tabs).find((key) => tabs[key].is_active_tab);
  616. const forceCurrentTab = currentTab == null ? void 0 : currentTab.type;
  617. callbacks.forEach(({ callback, page, tab, subtab, bypassMatch }) => {
  618. if (bypassMatch) {
  619. callback();
  620. return;
  621. }
  622. if (!subtab) {
  623. if (isCurrentPage(page, tab, false, getCurrentPage(), forceCurrentTab)) {
  624. callback();
  625. }
  626. return;
  627. }
  628. if ((currentTab == null ? void 0 : currentTab.subtabs) && (currentTab == null ? void 0 : currentTab.subtabs.length) > 0) {
  629. const forceSubtab = currentTab.subtabs.find((searchTab) => searchTab.is_active_subtab).subtab_type;
  630. if (isCurrentPage(page, tab, subtab, getCurrentPage(), forceCurrentTab, forceSubtab)) {
  631. callback();
  632. }
  633. }
  634. });
  635. });
  636. eventRegistry.addEventListener("set_tab", (e) => {
  637. callbacks.forEach(({ callback, page, tab, subtab, bypassMatch }) => {
  638. if (bypassMatch) {
  639. callback();
  640. return;
  641. }
  642. if (isCurrentPage(page, tab, subtab, getCurrentPage(), e.page_arguments.tab, e.page_arguments.sub_tab)) {
  643. callback();
  644. }
  645. });
  646. });
  647. };
  648.  
  649. // src/utils/messages.js
  650. hadAddedErrorStyles = false;
  651.  
  652. // src/utils/utils.js
  653. var isLoggedIn = () => {
  654. return user && user.user_id && "login" !== getCurrentPage();
  655. };
  656. var requests = {};
  657. var doRequest = (_0, ..._1) => __async(void 0, [_0, ..._1], function* (url, formData = {}, skipChecks = false) {
  658. var _a;
  659. if (!isLoggedIn()) {
  660. return;
  661. }
  662. if ("undefined" === typeof lastReadJournalEntryId || "undefined" === typeof user) {
  663. return;
  664. }
  665. if (!lastReadJournalEntryId || !user || !(user == null ? void 0 : user.unique_hash)) {
  666. return;
  667. }
  668. const requestKey = Object.keys(formData).length ? `${url}-${JSON.stringify(formData)}` : url;
  669. const timeRequested = Date.now();
  670. debuglog("utils-data", `Making request: ${requestKey} at ${timeRequested}`);
  671. if (requests[requestKey] && !skipChecks) {
  672. debuglog("utils-data", `Request already in progress: ${requestKey}`);
  673. if (requests[requestKey].in_progress) {
  674. return new Promise((resolve) => {
  675. const timeout = setTimeout(() => __async(void 0, null, function* () {
  676. debuglog("utils-data", `Request timed out: ${requestKey}, starting new request`);
  677. clearInterval(interval);
  678. const newRequest = yield doRequest(url, formData, true);
  679. resolve(newRequest);
  680. }), 2500);
  681. const interval = setInterval(() => {
  682. debuglog("utils-data", `Checking if request is complete: ${requestKey}`);
  683. if (!requests[requestKey].in_progress) {
  684. debuglog("utils-data", `Returning saved response: ${requestKey}`);
  685. clearInterval(interval);
  686. clearTimeout(timeout);
  687. resolve(requests[requestKey].response);
  688. }
  689. }, 100);
  690. });
  691. } else if (requests[requestKey].time_requested > timeRequested - 350) {
  692. debuglog("utils-data", `Request already completed: ${requestKey}`);
  693. return requests[requestKey].response;
  694. }
  695. }
  696. debuglog("utils-data", `Starting request: ${requestKey}`);
  697. requests[requestKey] = {
  698. in_progress: true,
  699. time_requested: timeRequested
  700. };
  701. const form = new FormData();
  702. form.append("sn", "Hitgrab");
  703. form.append("hg_is_ajax", 1);
  704. form.append("last_read_journal_entry_id", lastReadJournalEntryId != null ? lastReadJournalEntryId : 0);
  705. form.append("uh", (_a = user == null ? void 0 : user.unique_hash) != null ? _a : "");
  706. for (const key in formData) {
  707. form.append(key, formData[key]);
  708. }
  709. const requestBody = new URLSearchParams(form).toString();
  710. let response;
  711. let attempts = 0;
  712. while (!response && attempts < 3) {
  713. try {
  714. response = yield fetch(
  715. callbackurl ? callbackurl + url : "https://www.mousehuntgame.com/" + url,
  716. {
  717. method: "POST",
  718. body: requestBody,
  719. headers: {
  720. "Content-Type": "application/x-www-form-urlencoded"
  721. }
  722. }
  723. );
  724. } catch (error) {
  725. attempts++;
  726. console.error(`Attempt ${attempts} failed. Retrying...`, error);
  727. }
  728. }
  729. if (attempts >= 3) {
  730. console.error("Failed to fetch after maximum attempts");
  731. }
  732. let data;
  733. try {
  734. data = yield response.json();
  735. } catch (error) {
  736. console.error(`Error parsing response for ${url}:`, error, url, formData, response);
  737. return false;
  738. }
  739. requests[requestKey] = {
  740. time_requested: timeRequested,
  741. response: data
  742. };
  743. return data;
  744. });
  745.  
  746. // src/modules/catch-rate-estimate/data.js
  747. var miceEffs;
  748. var hasGottenEffs = false;
  749. var getMiceEffectiveness = () => __async(void 0, null, function* () {
  750. if (!hasGottenEffs) {
  751. miceEffs = yield getData("effs");
  752. hasGottenEffs = true;
  753. }
  754. const response = yield doRequest("managers/ajax/users/getmiceeffectiveness.php");
  755. return response == null ? void 0 : response.effectiveness;
  756. });
  757. var getMouse = (mouseId) => __async(void 0, null, function* () {
  758. if (!miceEffs || !hasGottenEffs) {
  759. miceEffs = yield getData("effs");
  760. hasGottenEffs = true;
  761. }
  762. const mouse = miceEffs.find((m) => m.type === mouseId);
  763. return mouse;
  764. });
  765. var getMousePower = (mouseId) => __async(void 0, null, function* () {
  766. var _a;
  767. const mouse = yield getMouse(mouseId);
  768. return (_a = mouse == null ? void 0 : mouse.effectivenesses) == null ? void 0 : _a.power;
  769. });
  770. var getMouseEffectiveness = (mouseId) => __async(void 0, null, function* () {
  771. const mouse = yield getMouse(mouseId);
  772. return mouse.effectivenesses[user.trap_power_type_name.toLowerCase()];
  773. });
  774. var getMinluck = (mousePower, effectiveness2) => __async(void 0, null, function* () {
  775. if (effectiveness2 === 0) {
  776. return "\u221E";
  777. }
  778. const minluck = Math.ceil(
  779. Math.ceil(Math.sqrt(mousePower / 2)) / Math.min(effectiveness2 / 100, 1.4)
  780. );
  781. const checkCatchRate = getCatchRate(mousePower, effectiveness2, 0, minluck);
  782. return checkCatchRate.rate === 1 ? minluck : minluck + 1;
  783. });
  784. var getPercent = (rate) => {
  785. if (rate === 1) {
  786. return "100%";
  787. }
  788. const percent = (rate * 100).toFixed(2);
  789. return `${percent}%`;
  790. };
  791. var getCatchRate = (mousePower, effectiveness2, power = null, luck = null) => {
  792. effectiveness2 = effectiveness2 / 100;
  793. if (null === power) {
  794. power = user.trap_power;
  795. }
  796. if (null === luck) {
  797. luck = user.trap_luck;
  798. }
  799. const rate = Math.min(
  800. 1,
  801. (effectiveness2 * power + 2 * Math.pow(Math.floor(Math.min(effectiveness2, 1.4) * luck), 2)) / (effectiveness2 * power + mousePower)
  802. );
  803. return {
  804. rate,
  805. percent: getPercent(rate)
  806. };
  807. };
  808.  
  809. // src/modules/catch-rate-estimate/styles.css
  810. var styles_default = '#mh-improved-cre{padding-right:5px;margin:5px 0;cursor:default}#mh-improved-cre table{width:100%}#mh-improved-cre thead{box-shadow:0 -1px #d3cecb inset}.mh-dark-mode #mh-improved-cre thead{box-shadow:0 -1px #5c5c5c inset}#mh-improved-cre table th{font-weight:700;text-align:center}#mh-improved-cre table th.name,#mh-improved-cre table:first-child{text-align:left}.mh-improved-cre-data{min-width:70px;text-align:center}.mh-improved-cre-data-good{color:#138f13}.mh-dark-mode .mh-improved-cre-data-good{color:#4fe54f}.mh-improved-cre-data-bad{color:#bb4646}.mh-dark-mode .mh-improved-cre-data-bad{color:#fb9b9b}.mh-improved-cre-name{padding-left:5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mh-improved-cre-name a{color:inherit}#mh-improved-cre.cre-loading{display:flex;align-items:flex-end;justify-content:center;height:100%;min-height:50px;padding:20px 0;background-image:url(https://www.mousehuntgame.com/images/ui/loaders/drip_spinner.gif?asset_cache_version=2);background-repeat:no-repeat;background-position:center 10px;background-size:55px}#mh-improved-cre.cre-loading:after{position:absolute;right:0;bottom:-5px;left:0;font-weight:400;color:#926944;text-align:center;content:"Loading Catch Rate Estimator\\2026"}span.mh-improved-cre-no-mice{font-size:12px}\n';
  811.  
  812. // src/modules/catch-rate-estimate/index.js
  813. var lastStats = [];
  814. var effectiveness = null;
  815. var isUpdating = false;
  816. var updateMinLucks = () => __async(void 0, null, function* () {
  817. if ("camp" !== getCurrentPage()) {
  818. return;
  819. }
  820. if (isUpdating) {
  821. return;
  822. }
  823. isUpdating = true;
  824. let minluckList = document.querySelector("#mh-improved-cre");
  825. if (!minluckList) {
  826. minluckList = makeElement("div", ["campPage-trap-trapEffectiveness", "cre-loading"]);
  827. minluckList.id = "mh-improved-cre";
  828. const statsContainer = document.querySelector(".trapSelectorView__trapStatSummaryContainer");
  829. if (!statsContainer) {
  830. isUpdating = false;
  831. return;
  832. }
  833. statsContainer.append(minluckList);
  834. }
  835. const currentStats = [
  836. user.trap_power,
  837. user.trap_luck,
  838. user.trap_attraction_bonus,
  839. user.trap_cheese_effect,
  840. user.trap_luck,
  841. user.trap_power,
  842. user.trap_power_bonus,
  843. user.trap_power_type_name,
  844. user.trinket_item_id,
  845. user.base_item_id,
  846. user.weapon_item_id,
  847. user.bait_item_id,
  848. user.environment_id
  849. ];
  850. if (currentStats !== lastStats) {
  851. effectiveness = yield getMiceEffectiveness();
  852. lastStats = currentStats;
  853. }
  854. if (!effectiveness) {
  855. isUpdating = false;
  856. return;
  857. }
  858. const miceIds = Object.values(effectiveness).flatMap(({ mice }) => mice).map((mouse) => {
  859. return {
  860. name: mouse.name,
  861. type: mouse.type
  862. };
  863. });
  864. yield renderList(miceIds);
  865. isUpdating = false;
  866. });
  867. var renderList = (list) => __async(void 0, null, function* () {
  868. let minluckList = document.querySelector("#mh-improved-cre");
  869. if (!minluckList) {
  870. minluckList = makeElement("div", "campPage-trap-trapEffectiveness");
  871. minluckList.id = "mh-improved-cre";
  872. const statsContainer = document.querySelector(".trapSelectorView__trapStatSummaryContainer");
  873. if (!statsContainer) {
  874. return;
  875. }
  876. statsContainer.append(minluckList);
  877. doEvent2("mh-improved-cre-list-rendered");
  878. }
  879. const existing = document.querySelector("#mh-improved-cre-table");
  880. if (existing) {
  881. existing.remove();
  882. }
  883. minluckList.classList.remove("cre-loading");
  884. const table = makeElement("table");
  885. table.id = "mh-improved-cre-table";
  886. const tableheader = makeElement("thead");
  887. makeElement("th", "name", "Mouse", tableheader);
  888. makeElement("th", "", "Minluck", tableheader);
  889. makeElement("th", "", "Catch Rate", tableheader);
  890. table.append(tableheader);
  891. const rows = [];
  892. for (const mouse of list) {
  893. const mousePower = yield getMousePower(mouse.type);
  894. const mouseEffectiveness = yield getMouseEffectiveness(mouse.type);
  895. const minluck = yield getMinluck(mousePower, mouseEffectiveness);
  896. const catchRate = yield getCatchRate(mousePower, mouseEffectiveness);
  897. const crClass = ["mh-improved-cre-data"];
  898. if (catchRate.rate * 100 >= 100) {
  899. crClass.push("mh-improved-cre-data-good");
  900. } else if (catchRate.rate * 100 <= 60) {
  901. crClass.push("mh-improved-cre-data-bad");
  902. }
  903. if (user.trap_luck >= minluck) {
  904. crClass.push("mh-improved-cre-data-minlucked");
  905. }
  906. rows.push({
  907. mouse: mouse.name,
  908. type: mouse.type,
  909. minluck,
  910. catchRateValue: catchRate.rate,
  911. catchRate: catchRate.percent,
  912. crClass
  913. });
  914. }
  915. rows.sort((a, b) => {
  916. if (a.catchRateValue !== b.catchRateValue) {
  917. return a.catchRateValue - b.catchRateValue;
  918. }
  919. return b.minluck - a.minluck;
  920. });
  921. if (rows.length === 0) {
  922. makeElement("span", "mh-improved-cre-no-mice", "No mice found.", table);
  923. minluckList.append(table);
  924. return;
  925. }
  926. rows.forEach(({ mouse, type, minluck, catchRate, crClass }) => {
  927. const row = makeElement("tr", "mh-improved-cre-row");
  928. const name = makeElement("td", "mh-improved-cre-name");
  929. const nameLink = makeElement("a", "", mouse);
  930. nameLink.addEventListener("click", (e) => {
  931. e.preventDefault();
  932. hg.views.MouseView.show(type);
  933. });
  934. name.append(nameLink);
  935. row.append(name);
  936. makeElement("td", crClass, minluck, row);
  937. makeElement("td", crClass, catchRate, row);
  938. table.append(row);
  939. });
  940. minluckList.append(table);
  941. });
  942. var main = () => __async(void 0, null, function* () {
  943. onNavigation(updateMinLucks, {
  944. page: "camp"
  945. });
  946. onRequest("*", updateMinLucks, false, ["users/getmiceeffectiveness.php"]);
  947. onTravel(null, { callback: updateMinLucks });
  948. });
  949. var init = () => __async(void 0, null, function* () {
  950. addStyles(styles_default, "catch-rate-estimate");
  951. setTimeout(main, 240);
  952. });
  953. var catch_rate_estimate_default = {
  954. id: "catch-rate-estimate",
  955. name: "Catch Rate Estimator & Minlucks",
  956. type: "feature",
  957. default: true,
  958. description: "Minluck and catch rate estimates.",
  959. order: 200,
  960. load: init
  961. };
  962. return __toCommonJS(catch_rate_estimate_exports);
  963. })();
  964. mhImprovedVersion = "0.0.0-userscript-cre"
  965. mhImprovedPlatform = "userscript";
  966. mhui.default.load();
  967. migrateUserscript('Minluck & Catch Rate Estimate', 'https://greasyfork.org/en/scripts/449334-mousehunt-minluck-catch-rate-estimate');