- // ==UserScript==
- // @name 🐭️ MouseHunt - Item Links
- // @description Adds a drop rate table from MHCT, links to the MouseHunt wiki, MHCT looter, and Markethunt, as well as various other features to the item view page.
- // @version 2.1.0
- // @license MIT
- // @author bradp
- // @namespace bradp
- // @match https://www.mousehuntgame.com/*
- // @icon https://i.mouse.rip/mh-improved/icon-64.png
- // @run-at document-end
- // @grant none
- // @require https://cdn.jsdelivr.net/npm/script-migration@1.1.1
- // ==/UserScript==
-
- var mhui = (() => {
- var __defProp = Object.defineProperty;
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
- var __getOwnPropNames = Object.getOwnPropertyNames;
- var __hasOwnProp = Object.prototype.hasOwnProperty;
- var __export = (target, all) => {
- for (var name in all)
- __defProp(target, name, { get: all[name], enumerable: true });
- };
- var __copyProps = (to, from, except, desc) => {
- if (from && typeof from === "object" || typeof from === "function") {
- for (let key of __getOwnPropNames(from))
- if (!__hasOwnProp.call(to, key) && key !== except)
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
- }
- return to;
- };
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
- var __async = (__this, __arguments, generator) => {
- return new Promise((resolve, reject) => {
- var fulfilled = (value) => {
- try {
- step(generator.next(value));
- } catch (e) {
- reject(e);
- }
- };
- var rejected = (value) => {
- try {
- step(generator.throw(value));
- } catch (e) {
- reject(e);
- }
- };
- var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
- step((generator = generator.apply(__this, __arguments)).next());
- });
- };
-
- // src/modules/better-item-view/index.js
- var better_item_view_exports = {};
- __export(better_item_view_exports, {
- default: () => better_item_view_default
- });
-
- // src/utils/event-registry.js
- var eventsAdded = {};
- var onEvent = (event, callback, remove = false) => {
- if (!eventRegistry) {
- return;
- }
- const id = `${event}-${remove.toString()}-${callback.toString()}`;
- if (eventsAdded[id]) {
- return;
- }
- eventsAdded[id] = true;
- eventRegistry.addEventListener(event, callback, null, remove);
- };
-
- // src/utils/styles.js
- var addModuleStyles = (styles, identifier = "mh-improved-styles", replace = false) => {
- const existingStyles = document.querySelector(`#${identifier}`);
- styles = Array.isArray(styles) ? styles.join("\n") : styles;
- if (existingStyles) {
- if (replace) {
- existingStyles.innerHTML = styles;
- } else {
- existingStyles.innerHTML += styles;
- }
- return existingStyles;
- }
- const style = document.createElement("style");
- style.id = identifier;
- style.innerHTML = styles;
- document.head.append(style);
- return style;
- };
- var addStyles = (styles, module = false, identifier = "mh-improved-styles") => {
- if (!module) {
- throw new Error("Module ID is required for adding module styles.", module);
- }
- const key = `${identifier}-${module}`;
- let stylesEl = addModuleStyles(styles, key, true);
- onEvent(`mh-improved-settings-changed-${module}`, (enabled) => {
- if (enabled) {
- stylesEl = addModuleStyles(styles, key, true);
- } else if (stylesEl) {
- stylesEl.remove();
- }
- });
- };
-
- // src/utils/settings.js
- var getSettingDirect = (key = null, defaultValue = null, identifier = "mousehunt-improved-settings") => {
- const settings = JSON.parse(localStorage.getItem(identifier)) || {};
- if (!key) {
- return settings;
- }
- if (!key.includes(".")) {
- if (settings[key] === void 0) {
- return defaultValue;
- }
- return settings[key];
- }
- const groupAndKey = getGroupAndKey(key);
- if (!groupAndKey.group) {
- if (settings[groupAndKey.key] === void 0) {
- return defaultValue;
- }
- return settings[groupAndKey.key];
- }
- const groupSettings = settings[groupAndKey.group] || {};
- if (groupSettings[groupAndKey.key] === void 0) {
- return defaultValue;
- }
- return groupSettings[groupAndKey.key];
- };
- var getGroupAndKey = (key) => {
- const split = key.split(".");
- if (split.length === 1) {
- return {
- group: null,
- key: split[0]
- };
- }
- if (split[0] === "location-huds-enabled") {
- return {
- group: "location-huds-enabled",
- key: split[1]
- };
- }
- return {
- group: `${split[0]}-settings`,
- key: split[1]
- };
- };
- var getSetting = (key, defaultValue = false) => {
- return getSettingDirect(key, defaultValue, "mousehunt-improved-settings");
- };
-
- // src/utils/elements.js
- var makeElement = (tag, classes = "", text = "", appendTo = null) => {
- const element = document.createElement(tag);
- if (Array.isArray(classes)) {
- classes = classes.join(" ");
- }
- if (classes && classes.length) {
- element.className = classes;
- }
- element.innerHTML = text;
- if (appendTo) {
- appendTo.append(element);
- return appendTo;
- }
- return element;
- };
- var makeLink = (text, href, encodeAsSpace = false) => {
- if (encodeAsSpace) {
- href = href.replaceAll("_", "%20");
- }
- return `<a href="${href}" target="_mouse" class="mousehuntActionButton tiny"><span>${text}</span></a>`;
- };
- var makeTooltip = (options) => {
- if (!options.appendTo) {
- return false;
- }
- const { appendTo, className = "", text = "" } = options;
- const tooltip = makeElement("div", ["PreferencesPage__blackTooltip", "mh-improved-tooltip", className]);
- makeElement("span", "PreferencesPage__blackTooltipText", text, tooltip);
- appendTo.append(tooltip);
- return tooltip;
- };
-
- // src/utils/db.js
- var database = (databaseName) => __async(void 0, null, function* () {
- return new Promise((resolve, reject) => {
- const request = indexedDB.open(`mh-improved-${databaseName}`, 6);
- request.onerror = (event) => {
- reject(event.target.error);
- };
- request.onsuccess = (event) => {
- resolve(event.target.result);
- };
- request.onupgradeneeded = (event) => {
- const db = event.target.result;
- if (!db.objectStoreNames.contains(databaseName)) {
- db.createObjectStore(databaseName, { keyPath: "id" });
- }
- };
- });
- });
- var dbGet = (databaseName, id) => __async(void 0, null, function* () {
- const db = yield database(databaseName);
- const transaction = db.transaction(databaseName, "readonly");
- transaction.onerror = (event) => {
- throw new Error(event.target.error);
- };
- const objectStore = transaction.objectStore(databaseName);
- const request = objectStore.get(id);
- return new Promise((resolve, reject) => {
- request.onsuccess = () => {
- resolve(request.result);
- };
- request.onerror = () => {
- reject(request.error);
- };
- transaction.oncomplete = () => {
- db.close();
- };
- });
- });
- var dbSet = (databaseName, data) => __async(void 0, null, function* () {
- const db = yield database(databaseName);
- const transaction = db.transaction(databaseName, "readwrite");
- const objectStore = transaction.objectStore(databaseName);
- data = {
- data,
- id: data.id || Date.now()
- };
- const request = objectStore.put(data);
- return new Promise((resolve, reject) => {
- request.onsuccess = () => {
- resolve(request.result);
- };
- request.onerror = () => {
- reject(request.error);
- };
- transaction.oncomplete = () => {
- db.close();
- };
- });
- });
-
- // src/utils/global.js
- var getGlobal = (key) => {
- if (window && window.mhui) {
- return window.mhui[key] || false;
- }
- if ("undefined" !== typeof app && app && app.mhui) {
- return app.mhui[key] || false;
- }
- return false;
- };
-
- // src/utils/data.js
- var getHeaders = () => {
- return {
- "Content-Type": "application/json",
- "X-MH-Improved": "true",
- "X-MH-Improved-Version": mhImprovedVersion || "unknown",
- "X-MH-Improved-Platform": mhImprovedPlatform || "unknown"
- };
- };
-
- // src/utils/events.js
- var runCallbacks = (settings, parentNode, callbacks) => {
- Object.keys(settings).forEach((key) => {
- if (parentNode && parentNode.classList && parentNode.classList.contains(settings[key].selector)) {
- settings[key].isVisible = true;
- if (callbacks[key] && callbacks[key].show) {
- callbacks[key].show();
- }
- } else if (settings[key].isVisible) {
- settings[key].isVisible = false;
- if (callbacks[key] && callbacks[key].hide) {
- callbacks[key].hide();
- }
- }
- });
- return settings;
- };
- var overlayMutationObserver = null;
- var overlayCallbacks = [];
- var onOverlayChange = (callbacks) => {
- let overlayData = {
- map: {
- isVisible: false,
- selector: "treasureMapPopup"
- },
- item: {
- isVisible: false,
- selector: "itemViewPopup"
- },
- mouse: {
- isVisible: false,
- selector: "mouseViewPopup"
- },
- image: {
- isVisible: false,
- selector: "largerImage"
- },
- convertible: {
- isVisible: false,
- selector: "convertibleOpenViewPopup"
- },
- adventureBook: {
- isVisible: false,
- selector: "adventureBookPopup"
- },
- marketplace: {
- isVisible: false,
- selector: "marketplaceViewPopup"
- },
- gifts: {
- isVisible: false,
- selector: "giftSelectorViewPopup"
- },
- support: {
- isVisible: false,
- selector: "supportPageContactUsForm"
- },
- premiumShop: {
- isVisible: false,
- selector: "MHCheckout"
- }
- };
- overlayCallbacks.push(callbacks);
- if (overlayMutationObserver) {
- return;
- }
- overlayMutationObserver = true;
- const observer = new MutationObserver(() => {
- overlayCallbacks.forEach((callback) => {
- if (callback.change) {
- callback.change();
- }
- const overlayType = document.querySelector("#overlayPopup");
- if (overlayType && overlayType.classList.length <= 0) {
- return;
- }
- const overlayBg = document.querySelector("#overlayBg");
- if (overlayBg && overlayBg.classList.length > 0) {
- if (callback.show) {
- callback.show();
- }
- } else if (callback.hide) {
- callback.hide();
- }
- overlayData = runCallbacks(overlayData, overlayType, callback);
- });
- });
- const observeTarget = document.querySelector("#overlayPopup");
- if (observeTarget) {
- observer.observe(observeTarget, {
- attributes: true,
- attributeFilter: ["class"]
- });
- }
- };
-
- // src/utils/maps.js
- var mapper = (key = false) => {
- if (key) {
- const mapperData = getGlobal("mapper");
- if (!mapperData || !mapperData[key]) {
- return false;
- }
- return mapperData[key];
- }
- return getGlobal("mapper");
- };
- var mapData = () => {
- const m = mapper();
- if (!m) {
- return {};
- }
- return m.mapData;
- };
- var getCachedValue = (key) => __async(void 0, null, function* () {
- var _a;
- const value = yield dbGet("ar-cache", key);
- if (!((_a = value == null ? void 0 : value.data) == null ? void 0 : _a.value)) {
- return null;
- }
- return value.data.value;
- });
- var setCachedValue = (key, value) => __async(void 0, null, function* () {
- yield dbSet("ar-cache", { id: key, value });
- });
- var getArForMouse = (id, type = "mouse") => __async(void 0, null, function* () {
- let mhctJson = [];
- const cacheKey = `${type}-${id}`;
- const cachedAr = yield getCachedValue(cacheKey);
- if (cachedAr) {
- return cachedAr;
- }
- const isItem = "item" === type;
- const mhctPath = isItem ? "mhct-item" : "mhct";
- let mhctData = [];
- const data = mapData() || {};
- const mapType = (data == null ? void 0 : data.map_type) || "";
- let url = `https://api.mouse.rip/${mhctPath}/${id}`;
- if (mapType.toLowerCase().includes("halloween")) {
- url = `https://api.mouse.rip/${mhctPath}/${id}-hlw_22`;
- }
- try {
- mhctData = yield fetch(url, { headers: getHeaders() });
- } catch (error) {
- console.error("Error fetching MHCT data:", error);
- yield new Promise((resolve) => setTimeout(resolve, 500));
- try {
- mhctData = yield fetch(url, { headers: getHeaders() });
- } catch (errorRetry) {
- console.error("Error fetching MHCT data:", errorRetry);
- return [];
- }
- }
- if (!mhctData.ok) {
- return [];
- }
- mhctJson = yield mhctData.json();
- if (!mhctJson || mhctJson.length === 0) {
- return [];
- }
- if (isItem) {
- for (const rate of mhctJson) {
- rate.rate = Number.parseInt(rate.drop_pct * 100);
- delete rate.drop_ct;
- }
- }
- if (mhctJson.error) {
- return [];
- }
- mhctJson = mhctJson.filter((rate) => {
- if (rate.rate === 0) {
- return false;
- }
- if (rate.rate === 9999) {
- rate.rate = 1e4;
- }
- return true;
- });
- yield setCachedValue(cacheKey, mhctJson);
- return mhctJson;
- });
-
- // src/utils/messages.js
- hadAddedErrorStyles = false;
-
- // src/modules/better-item-view/settings/index.js
- var settings_default = () => __async(void 0, null, function* () {
- return [
- {
- id: "better-item-view.show-drop-rates",
- title: "Show drop rates",
- default: true
- },
- {
- id: "better-item-view.show-item-hover",
- title: "Show item details on hover (in journal)",
- default: true
- }
- ];
- });
-
- // src/modules/better-item-view/styles.css
- var styles_default = '.itemView-titleContainer{height:26px}.itemView-header-name{display:flex;align-items:center;justify-content:space-between}.mh-item-links{display:flex;justify-content:flex-end;margin-right:-10px}.mh-item-links a{margin-right:5px}.itemView-header-name .mh-item-links span{display:inline-block;font-size:11px;font-weight:400}.itemView-has-mhct .mouse-ar-wrapper{display:grid;grid-template-columns:150px auto 50px;place-items:center stretch;padding:5px;margin:5px 0;font-size:12px}.itemView-has-mhct .has-stages .mouse-ar-wrapper{grid-template-columns:110px 140px auto 50px}.itemView-has-mhct .mouse-ar-wrapper div{padding:0 2px}.itemView-has-mhct .mice-ar-wrapper{margin-right:10px}.mouse-ar-wrapper .stage{font-size:10px}.mouse-ar-wrapper .cheese{font-size:11px}.itemView-has-mhct .ar-header{display:flex;align-items:center;justify-content:space-between;height:26px;padding-bottom:2px;margin-top:10px;margin-bottom:10px;font-size:12px;font-weight:900;border-bottom:1px solid #ccc}.itemView-has-mhct .ar-link{font-size:9px}.itemView-has-mhct .rate{text-align:right}.itemView-has-mhct .mouse-ar-wrapper:nth-child(odd){background-color:#e7e7e7}.itemView-has-mhct .itemView-description{font-weight:500;line-height:19px}.itemView-action.crafting_item b{display:none}.itemView-action.crafting_item:before{content:"This can be used to craft other items!"}.itemViewContainer.map_piece .itemView-action-text.map_piece,.itemViewContainer.base .itemView-action-text.base,.itemViewContainer.weapon .itemView-actio-textn.weapon,.itemViewContainer.bait .itemView-action-text.bait,.itemViewContainer.trinket .itemView-action-text.trinket,.itemViewContainer.potion .itemView-action-text.potion,.itemViewContainer.readiness_item .itemView-action-text.readiness_item,.itemViewContainer.convertible .itemView-action-text.convertible,.itemViewContainer.torn_page .itemView-action-text.torn_page,.itemViewContainer.crafting_item .itemView-action-text.crafting_item,.itemViewContainer.collectible .itemView-action-text.collectible,.itemViewContainer.message_item .itemView-action-text.message_item,.itemViewContainer.bonus_loot .itemView-action-text.bonus_loot,.itemViewContainer.stat .itemView-action-text.stat,.itemViewContainer.quest .itemView-action-text.quest,.itemViewContainer.skin .itemView-action-text.skin{display:none!important}.itemViewContainer .shopCustomization .itemViewStatBlock-stat{display:flex;flex-direction:column;align-items:center}.itemViewContainer .itemViewStatBlock-stat{display:flex;flex-direction:row;align-items:center;justify-content:flex-start}.itemViewContainer .itemViewStatBlock-stat-value{flex:1;text-align:left}.itemViewContainer .itemViewStatBlock-stat.cheeseEffect{font-size:9px;text-align:center}.itemViewContainer .itemViewStatBlock.trinket .itemViewStatBlock-padding{display:flex;flex-direction:column;align-items:stretch;width:100px}.itemViewContainer .itemViewStatBlock.trinket{width:100px;font-size:13px}#overlayPopup.itemViewPopup #jsDialogClose{z-index:1}#overlayPopup.itemViewPopup .itemView-header-classification{right:25px}.itemView-actionContainer{display:flex;flex-wrap:wrap;gap:10px}.itemView-action{border-top:none}.itemViewContainer.potion .inventoryPage-item-recipeOptions li{width:365px}.itemView-character-image{width:auto;height:84px;margin-top:-15px;margin-left:-9px}.itemView-character-name{left:-11px;width:75px;font-size:15px}.itemView-padding{margin-left:70px}.itemView-thumbnail.large{margin-left:-15px}input.itemView-action-convert-quantity{width:50px}.itemViewPopup .itemViewStatBlock-padding{flex-direction:column}.itemView-character .itemView-character-image{transition:all .4s ease-out;transform-origin:bottom}.itemView-character:hover .itemView-character-image{transform:scale(1.2) rotate(-10deg) translate(5px)}.itemView-header-classification{visibility:hidden}.itemView-header-classification span{visibility:visible}.itemViewStatBlock-stat{display:flex;align-items:center}.itemView-sidebar-checklistItem:nth-child(1),.itemView-sidebar-checklistItem:nth-child(2),.itemView-sidebar-checklistItem.checked{display:block}.itemView-sidebar-checklistItem{background:url(https://www.mousehuntgame.com/images/icons/bad_idea.png) 1px 4px no-repeat;background-size:14px}.itemView-partsContainer{display:flex;flex-direction:column;align-items:stretch;padding-top:15px;padding-bottom:10px;margin-top:15px;border-top:1px solid #666}\n';
-
- // src/modules/better-item-view/index.js
- var getLinkMarkup = (name, id) => {
- return makeLink("MHCT", `https://www.mhct.win/loot.php?item=${id}`, true) + makeLink("Wiki", `https://mhwiki.hitgrab.com/wiki/index.php/${name}`);
- };
- var addLinks = (itemId) => {
- const title = document.querySelector(".itemView-header-name");
- if (!title) {
- return;
- }
- const currentLinks = document.querySelector(".mh-item-links");
- if (currentLinks) {
- currentLinks.remove();
- }
- const div = document.createElement("div");
- div.classList.add("mh-item-links");
- div.innerHTML = getLinkMarkup(title.innerText, itemId);
- title.append(div);
- const values = document.querySelector(".mouseView-values");
- const desc = document.querySelector(".mouseView-descriptionContainer");
- if (values && desc) {
- desc.insertBefore(values, desc.firstChild);
- }
- };
- var updateItemView = () => __async(void 0, null, function* () {
- const itemView = document.querySelector(".itemViewContainer");
- if (!itemView) {
- return;
- }
- const itemId = itemView.getAttribute("data-item-id");
- if (!itemId) {
- return;
- }
- const sidebar = document.querySelector(".itemView-sidebar");
- if (sidebar) {
- const crafting = document.querySelector(".itemView-action.crafting_item");
- if (crafting) {
- sidebar.append(crafting);
- }
- const smashing = document.querySelector(".itemView-partsContainer");
- if (smashing) {
- sidebar.append(smashing);
- if (smashing.getAttribute("data-has-changed-title")) {
- return;
- }
- const smashingTitle = smashing.querySelector("b");
- if (smashingTitle) {
- smashingTitle.innerText = "Hunter's Hammer to get:";
- smashing.setAttribute("data-has-changed-title", "true");
- smashing.innerHtml = smashing.innerHTML.replace("If you smash it, you'll get:", "");
- }
- }
- }
- addLinks(itemId);
- if (!getSetting("better-item-view.show-drop-rates", true)) {
- return;
- }
- const id = Number.parseInt(itemId, 10);
- const ignored = [
- 2473,
- // Mina's gift
- 823,
- // party charm
- 803,
- // chrome charm
- 420,
- // king's credits
- 1980,
- // king's keys
- 585
- // scrambles
- ];
- if (ignored.includes(id)) {
- return;
- }
- let mhctJson = yield getArForMouse(itemId, "item");
- if (!mhctJson || mhctJson === void 0) {
- return;
- }
- itemView.classList.add("mouseview-has-mhct");
- const container = itemView.querySelector(".itemView-padding");
- if (!container) {
- return;
- }
- const arWrapper = makeElement("div", "ar-wrapper");
- const title = makeElement("div", "ar-header");
- const titleText = makeElement("div", "ar-title", "Drop Rates", title);
- makeTooltip({
- appendTo: titleText,
- text: 'The best location and bait, according to data gathered by <a href="https://mhct.win/" target="_blank" rel="noopener noreferrer">MHCT</a>.'
- });
- const link = makeElement("a", "ar-link", "View on MHCT \u2192");
- link.href = `https://www.mhct.win/loot.php?item=${itemId}`;
- link.target = "_mhct";
- title.append(link);
- arWrapper.append(title);
- const itemsArWrapper = makeElement("div", "item-ar-wrapper");
- const hasStages = mhctJson.some((itemAr) => itemAr.stage);
- if (hasStages) {
- itemsArWrapper.classList.add("has-stages");
- }
- mhctJson = mhctJson.filter((itemAr) => Number.parseInt(itemAr.drop_pct, 10) > 0).slice(0, 10);
- mhctJson.forEach((itemAr) => {
- const dropPercent = Number.parseInt(itemAr.drop_pct, 10).toFixed(2);
- if (dropPercent !== "0.00") {
- const itemArWrapper = makeElement("div", "mouse-ar-wrapper");
- makeElement("div", "location", itemAr.location, itemArWrapper);
- if (hasStages) {
- makeElement("div", "stage", itemAr.stage, itemArWrapper);
- }
- makeElement("div", "cheese", itemAr.cheese, itemArWrapper);
- makeElement("div", "rate", `${dropPercent}%`, itemArWrapper);
- itemsArWrapper.append(itemArWrapper);
- }
- });
- if (mhctJson.length > 0) {
- arWrapper.append(itemsArWrapper);
- container.append(arWrapper);
- }
- });
- var init = () => __async(void 0, null, function* () {
- addStyles(styles_default, "better-item-view");
- if (getSetting("better-item-view.show-item-hover", true)) {
- }
- onOverlayChange({ item: { show: updateItemView } });
- });
- var better_item_view_default = {
- id: "better-item-view",
- name: "Better Item View",
- type: "better",
- default: true,
- description: "Updates the styles and shows drop rates, links to MHCT, and MH Wiki.",
- load: init,
- settings: settings_default
- };
- return __toCommonJS(better_item_view_exports);
- })();
- mhImprovedVersion = "0.0.0-userscript;"
- mhImprovedPlatform = "userscript";
- mhui.default.load();
- migrateUserscript('Item Links', 'https://greasyfork.org/en/scripts/445920-mousehunt-item-links');