// ==UserScript==
// @name MouseHunt - QoL Utilities
// @author Tran Situ (tsitu)
// @namespace https://greasyfork.org/en/users/232363-tsitu
// @version 1.1.7
// @description Miscellaneous utilities to turbo-charge your MH experience
// @match http://www.mousehuntgame.com/*
// @match https://www.mousehuntgame.com/*
// ==/UserScript==
(function () {
/**
* IDEA BANK
* TODO: Alert when sounding horn after unseen trap check (user set 00, 15, 30, 45) to avoid wasted hunt if TC'd something
* TODO: Track outbound supply transfers
*/
// Adds direct hunter ID / snuid navigation popup via button on 'Friends' dropdown
(function hunterIdNav() {
document
.querySelectorAll(".tsitu-hunter-id-nav")
.forEach(el => el.remove());
const target = document.querySelector("li .friend_list");
if (target) {
const li = document.createElement("li");
li.className = "tsitu-hunter-id-nav";
const button = document.createElement("a");
button.href = "#";
button.innerText = "ID Navigation";
button.onclick = function () {
const existing = document.querySelector("#tsitu-hunter-id-nav-ui");
if (existing) existing.remove();
else render();
return false;
};
const icon = document.createElement("div");
icon.className = "icon";
button.appendChild(icon);
li.appendChild(button);
target.insertAdjacentElement("afterend", li);
function render() {
document
.querySelectorAll("#tsitu-hunter-id-nav-ui")
.forEach(el => el.remove());
const div = document.createElement("div");
div.id = "tsitu-hunter-id-nav-ui";
div.style.backgroundColor = "#F5F5F5";
div.style.position = "fixed";
div.style.zIndex = "9999";
div.style.left = "22.3vw";
div.style.top = "28vh";
div.style.border = "solid 3px #696969";
div.style.borderRadius = "20px";
div.style.padding = "10px";
div.style.textAlign = "center";
const closeButton = document.createElement("button", {
id: "close-button"
});
closeButton.textContent = "x";
closeButton.onclick = function () {
document.body.removeChild(div);
};
const table = document.createElement("table");
table.style.textAlign = "left";
table.style.borderSpacing = "1em 0";
const rowHid = document.createElement("tr");
const rowSnuid = document.createElement("tr");
const hidRadio = document.createElement("input");
hidRadio.type = "radio";
hidRadio.name = "tsitu-hunter-id";
hidRadio.id = "tsitu-radio-hid";
hidRadio.defaultChecked = true;
hidRadio.onchange = function () {
processRadio();
};
const hidRadioLabel = document.createElement("label");
hidRadioLabel.innerText = "Hunter ID: ";
hidRadioLabel.htmlFor = "tsitu-radio-hid";
const hidInput = document.createElement("input");
hidInput.type = "text";
hidInput.id = "tsitu-input-hid";
hidInput.placeholder = "e.g. 3795351";
hidInput.onkeyup = function (event) {
if (event.keyCode === 13) {
goButton.click();
}
};
const colHid = document.createElement("td");
const colHidInput = document.createElement("td");
colHid.appendChild(hidRadio);
colHid.appendChild(document.createTextNode("\u00A0"));
colHid.appendChild(hidRadioLabel);
colHidInput.appendChild(hidInput);
rowHid.appendChild(colHid);
rowHid.appendChild(colHidInput);
const snuidRadio = document.createElement("input");
snuidRadio.type = "radio";
snuidRadio.name = "tsitu-hunter-id";
snuidRadio.id = "tsitu-radio-snuid";
snuidRadio.onchange = function () {
processRadio();
};
const snuidRadioLabel = document.createElement("label");
snuidRadioLabel.innerText = "SN User ID: ";
snuidRadioLabel.htmlFor = "tsitu-radio-snuid";
const snuidInput = document.createElement("input");
snuidInput.type = "text";
snuidInput.id = "tsitu-input-snuid";
snuidInput.placeholder = "e.g. 1062432650";
snuidInput.onkeyup = function (event) {
if (event.keyCode === 13) {
goButton.click();
}
};
const colSnuid = document.createElement("td");
const colSnuidInput = document.createElement("td");
colSnuid.appendChild(snuidRadio);
colSnuid.appendChild(document.createTextNode("\u00A0"));
colSnuid.appendChild(snuidRadioLabel);
colSnuidInput.appendChild(snuidInput);
rowSnuid.appendChild(colSnuid);
rowSnuid.appendChild(colSnuidInput);
function processRadio() {
if (hidRadio.checked) {
hidInput.disabled = false;
snuidInput.disabled = true;
localStorage.setItem("tsitu-id-radio", "hid");
} else if (snuidRadio.checked) {
hidInput.disabled = true;
snuidInput.disabled = false;
localStorage.setItem("tsitu-id-radio", "snuid");
}
}
const goButton = document.createElement("button");
goButton.style.fontWeight = "bold";
goButton.innerText = "Go";
goButton.onclick = function () {
if (hidRadio.checked) {
const val = hidInput.value;
if (
val.length > 0 &&
val.length === parseInt(val).toString().length
) {
const newWindow = window.open(
`https://www.mousehuntgame.com/profile.php?id=${val}`
);
}
} else if (snuidRadio.checked) {
const val = snuidInput.value;
if (
val.length > 0 &&
val.length === parseInt(val).toString().length
) {
const newWindow = window.open(
`https://www.mousehuntgame.com/profile.php?snuid=${val}`
);
}
}
};
table.appendChild(rowHid);
table.appendChild(rowSnuid);
div.appendChild(closeButton);
div.appendChild(document.createElement("br"));
div.appendChild(document.createElement("br"));
div.appendChild(table);
div.appendChild(document.createElement("br"));
div.appendChild(goButton);
document.body.appendChild(div);
// Apply cached radio selection
const radioCache = localStorage.getItem("tsitu-id-radio");
if (radioCache) {
if (radioCache === "hid") {
hidRadio.checked = true;
} else if (radioCache === "snuid") {
snuidRadio.checked = true;
}
}
processRadio();
}
}
})();
// TODO: Claim-time map participants/duster tracker (move to mapping helper?)
(function mapTracker() {
const map = temp1;
if (map.can_claim_reward && map.is_complete) {
//
}
const data = {};
map.hunters.forEach(hunter => {
if (hunter.is_active) {
data[hunter.user_id] = {
name: hunter.name
};
const completedItems = hunter.completed_goal_ids.item;
const completedMice = hunter.completed_goal_ids.mouse;
if (completedItems.length > 0) {
data[hunter.user_id].i = completedItems.length;
}
if (completedMice.length > 0) {
data[hunter.user_id].m = completedMice.length;
}
if (hunter.upgrader) {
data[hunter.user_id].d = true;
}
}
});
console.log(data);
});
(function lockConvertibleButtons() {
// Observe for 'Special' tab of Inventory
const observerTarget = document.querySelector(".mousehuntPage-content");
if (observerTarget) {
MutationObserver =
window.MutationObserver ||
window.WebKitMutationObserver ||
window.MozMutationObserver;
const observer = new MutationObserver(function () {
const isSpecial = observerTarget.querySelector(
".mousehuntHud-page-tabContent.special.active"
);
if (isSpecial) {
// Disconnect and reconnect later to prevent infinite mutation loop
observer.disconnect();
render();
observer.observe(observerTarget, {
childList: true,
subtree: true
});
}
});
observer.observe(observerTarget, {
childList: true,
subtree: true
});
}
function render() {
document
.querySelectorAll(".tsitu-lock-convertible")
.forEach(el => el.remove());
const convertibles = document.querySelectorAll(
".inventoryPage-item.full.convertible"
);
if (convertibles.length > 0) {
// Apply cached locks on page load
const cacheRaw = localStorage.getItem("tsitu-convertible-locks");
if (cacheRaw) {
const cache = JSON.parse(cacheRaw);
convertibles.forEach(el => {
const id = el.getAttribute("data-item-id");
if (cache.indexOf(id) >= 0) {
el.querySelectorAll(
".inventoryPage-item-content-action .button"
).forEach(button => {
if (!button.classList.contains("disabled")) {
button.classList.toggle("disabled");
}
});
}
});
}
// Generate individual item lock buttons
convertibles.forEach(el => {
const a = document.createElement("a");
a.href = "#";
a.className =
"inventoryPage-item-larryLexicon tsitu-lock-convertible";
a.style.right = "22px";
a.style.height = "15px";
a.innerText = "🔒";
a.onclick = function () {
el.querySelectorAll(
".inventoryPage-item-content-action .button"
).forEach(button => {
button.classList.toggle("disabled");
});
updateLockCache();
return false;
};
const target = el.querySelector(".inventoryPage-item-name");
if (target) target.insertAdjacentElement("afterend", a);
});
function updateLockCache() {
const cacheArr = [];
convertibles.forEach(el => {
const button = el.querySelector(
".inventoryPage-item-content-action .button"
);
if (button.classList.contains("disabled")) {
cacheArr.push(el.getAttribute("data-item-id"));
}
});
localStorage.setItem(
"tsitu-convertible-locks",
JSON.stringify(cacheArr)
);
}
// Add buttons for [un]locking entire tabs
const lockableTabs = [
"Baskets & Kits",
"Scrolls, Posters, Assignments",
"Spring Egg Hunt",
"Treasure Chests"
];
observerTarget
.querySelectorAll(".inventoryPage-tagContent-tagGroup.clear-block")
.forEach(tab => {
const tabType = tab.getAttribute("data-name");
if (lockableTabs.indexOf(tabType) >= 0) {
const span = document.createElement("span");
span.className =
"inventoryPage-tagContent-tagTitle tsitu-lock-convertible";
span.style.margin = "5px 0 5px 0";
const lockAll = document.createElement("button");
lockAll.innerText = "Lock Tab";
lockAll.onclick = function () {
if (
confirm(
`Are you sure you'd like to lock all convertibles on this tab?\n\n- ${tabType}`
)
) {
tab
.querySelectorAll(
".inventoryPage-item-content-action .button"
)
.forEach(button => {
if (!button.classList.contains("disabled")) {
button.classList.toggle("disabled");
}
});
updateLockCache();
}
};
const unlockAll = document.createElement("button");
unlockAll.innerText = "Unlock Tab";
unlockAll.onclick = function () {
if (
confirm(
`Are you sure you'd like to unlock all convertibles on this tab?\n\n- ${tabType}`
)
) {
tab
.querySelectorAll(
".inventoryPage-item-content-action .button"
)
.forEach(button => {
if (button.classList.contains("disabled")) {
button.classList.toggle("disabled");
}
});
updateLockCache();
}
};
span.appendChild(lockAll);
span.appendChild(document.createTextNode("\u00A0\u00A0"));
span.appendChild(unlockAll);
const target = tab.querySelector(
".inventoryPage-tagContent-tagTitle"
);
if (target) target.appendChild(span);
}
});
}
}
})();
// Adds RH location and 'Travel' button directly underneath clue
(function relicHunterHintTravel() {
const hintMap = {
"Standing on the other side of a green and purple portal.":
"Acolyte Realm",
"Inside an elaborate one-way trap designed by Plankrun.": "Acolyte Realm",
"Outside a smoky purple tower.": "Acolyte Realm",
"Roaming amongst the most powerful of Lich mice.": "Balack's Cove",
"Lurking in a damp and darkened grotto.": "Balack's Cove",
"Searching for the best deals in the Burroughs.": "Bazaar",
"Ducking between stalls and tents and loud merchants.": "Bazaar",
"Under the pointiest tent in all the Kingdom!": "Bazaar",
"Hobnobbing with giants.": "Bountiful Beanstalk",
"Infiltrating a lofty castle.": "Bountiful Beanstalk",
"Taking a relaxing hike through a forested area.": "Calm Clearing",
"By a peaceful rock in a grassy clearing.": "Calm Clearing",
"Tucked behind dense trees where it's quiet and peaceful.":
"Calm Clearing",
"Watching the peaceful gathering of tribal mice.": "Cape Clawed",
"On a small bit of land near a volcano.": "Cape Clawed",
"Listening for sinister secrets deep underground.": "Catacombs",
"Walking through dark hallways in search of a Keeper's Candle.":
"Catacombs",
"Keeping an eye on the long-arm of the law.": "Claw Shot City",
"Spitting in a spittoon! Yuck!": "Claw Shot City",
"Leafing through ancient tomes of knowledge.": "Crystal Library",
"Expanding knowledge and climbing endless ladders.": "Crystal Library",
"Ankle deep in rocky, tropical sand.": "Derr Dunes",
"Tumbling down hills of rreD sand.": "Derr Dunes",
"Practicing an ancient art with fledgling warriors.": "Dojo",
"Safely inside the bottom floor of a bamboo building.": "Dojo",
"Carefully watching the training activities of advanced students.":
"Dojo",
"Near the bluE waters of the island.": "Elub Shore",
"Watching the calm waters of Rodentia while remaining safely ashore.":
"Elub Shore",
"Marching through the Sandtail Desert.": "Fiery Warpath",
"Dodging arrows, spears, swords, and spells!": "Fiery Warpath",
"Investigating what can be built in a workshop.": "Floating Islands",
"Searching the skies for treasure.": "Floating Islands",
"Peering through an oculus.": "Floating Islands",
"In the clouds above Hollow Heights.": "Floating Islands",
"Watching hunters' dirigibles fly by.": "Floating Islands",
"Avoiding falling victim to Sky Pirates.": "Floating Islands",
"Trapped between two planes of existence.": "Forbidden Grove",
"Behind heavy stone gates.": "Forbidden Grove",
"Reaping what she sowed...": "Foreword Farm",
"Cultivating a hearty yield...": "Foreword Farm",
"Toiling away in fertile fields...": "Foreword Farm",
"Carefully navigating a subterranean and humid environment.":
"Fungal Cavern",
"Deep inside of an infested, glowing, twisting, unending cave of untold riches...":
"Fungal Cavern",
"Tracing the deep patterns of bark growing on ancient towers.":
"Great Gnarled Tree",
"By a tree older than Gnawnia itself.": "Great Gnarled Tree",
"Finding shade in the largest tree in the Kingdom.": "Great Gnarled Tree",
"Near the loud and low horns and the dinging of bells.": "Harbour",
"Visiting where many new hunters seek out seafaring mice.": "Harbour",
"By the sea where there's plenty of fresh air and sunshine.": "Harbour",
"Where royal strength rewards hunting prowess.": "King's Arms",
"Under a circular roof atop arm-shared paths.": "King's Arms",
"Browsing wares available with a most royal currency.": "King's Arms",
"Climbing up spiralling, menacing stairs.": "King's Gauntlet",
"Trekking up a massive tower in Valour.": "King's Gauntlet",
"Atop a tall tower with the perfect view of an Eclipse.":
"King's Gauntlet",
"Performing bizarre experiments and chemical reactions.": "Laboratory",
"Where the powerful and strange breeds of mice first arose.":
"Laboratory",
"Amongst brightly glowing potions.": "Laboratory",
"Waist-deep in a shallow, sparkling pond.": "Lagoon",
"Amongst sparkling, still water.": "Lagoon",
"Climbing jagged rocks and slick moss.": "Lagoon",
"Tending to a most troublesome and dangerous garden.": "Living Garden",
"Enjoying a drink on the overgrown rooftop patio.": "Living Garden",
"Looking across vast landscapes and the many horizons of the land.":
"Mountain",
"Cutting through the pass to reach the town on the other side.":
"Mountain",
"In a treacherous environment where only the toughest of mice survive.":
"Mountain",
"Investigating the spirits of slain mice.": "Mousoleum",
"Surveying where scientists harvest 'spare parts'.": "Mousoleum",
"Studying the spooky remains of Zombie Mice.": "Mousoleum",
"Climbing and exploring some long lost ruins.": "Moussu Picchu",
"Up high upon a weather changing plateau.": "Moussu Picchu",
"Visiting a walled city that is no stranger to sieges.": "Muridae Market",
"Keeping a close eye on would-be thieves...": "Muridae Market",
"Investigating a well-seasoned Gumbo Cheese.": "Nerg Plains",
"Running through flat fields of greeN.": "Nerg Plains",
"Reeling it in...": "Prologue Pond",
"Choosing the right tackle...": "Prologue Pond",
"Enjoying a quick dip in a cheesy bath.": "Queso River",
"Sipping delicious liquid cheese from a river.": "Queso River",
"Protecting her ears from the sound of loud pumps.": "Queso River",
"Testing out balance on the high seas.": "S.S. Huntington IV",
"Looking a bit queasy...": "S.S. Huntington IV",
"Watching the sky and wondering what the weather will bring.":
"Seasonal Garden",
"Braving the ever-changing elements.": "Seasonal Garden",
"Walking along the coldest waters in Gnawnia.": "Slushy Shoreline",
"At the beachside site of an invasion force!": "Slushy Shoreline",
"Shivering near the edges of the mainland.": "Slushy Shoreline",
"Walking along the bottom of the Rodentia Ocean.": "Sunken City",
"Investigating powerful diving equipment.": "Sunken City",
"Sitting down at a wooden table...": "Table of Contents",
"Creating clever characters...": "Table of Contents",
"Amongst the triumphant trumpets of master hunters of old.":
"Tournament Hall",
"Browsing the rewards of competitive champions.": "Tournament Hall",
"Competing for the limelight amongst the finest champions.":
"Tournament Hall",
"Exploring the deep and winding caverns near a technologically-advanced underground city.":
"Town of Digby",
"Amongst powerful drills and excavation equipment.": "Town of Digby",
"Hiding in the shadows while standing in the limelight.": "Town of Digby",
"Hiding within the hustle and bustle in the city of the crown.":
"Town of Gnawnia",
"Trying to spot the King himself.": "Town of Gnawnia",
"In a town with a dense population.": "Town of Gnawnia",
"Standing among the ranks of new students out in the field.":
"Training Grounds",
"Watching the careful training of artful students.": "Training Grounds",
"Relaxing in the shade of tall engraved rock.": "Training Grounds",
"Observing the churning and grinding of the new harvest.": "Windmill",
"By an agricultural structure once owned by one of Gnawnia's most prosperous farmers.":
"Windmill",
"Grinding up hundreds of tiny seeds from a stalky, golden plant.":
"Windmill"
};
// MutationObserver logic for map UI
// Observers are attached to a *specific* element (will DC if removed from DOM)
const observerTarget = document.getElementById("overlayPopup");
if (observerTarget) {
MutationObserver =
window.MutationObserver ||
window.WebKitMutationObserver ||
window.MozMutationObserver;
const observer = new MutationObserver(function () {
// Render if Relic Hunter hint is available
const rhHint = observerTarget.querySelector(
".treasureMapInventoryView-relicHunter-hint"
);
// Prevent conflict with 'Mapping Helper'
const mapTab = observerTarget.querySelector(
".treasureMapManagerView-header-navigation-item.tasks.active"
);
// Prevent conflict with 'Bulk Map Invites'
const inviteHeader = document.querySelector(
// ".treasureMapPopup-inviteFriend-header"
".treasureMapManagerDialogView-userSelector"
);
if (rhHint && !mapTab && !inviteHeader) {
// Disconnect and reconnect later to prevent infinite mutation loop
observer.disconnect();
render();
observer.observe(observerTarget, {
childList: true,
subtree: true
});
}
});
observer.observe(observerTarget, {
childList: true,
subtree: true
});
}
function render() {
document.querySelectorAll(".tsitu-rh-helper").forEach(el => el.remove());
const div = document.createElement("div");
div.style.textAlign = "center";
div.className = "tsitu-rh-helper";
const button = document.createElement("a");
button.href = "#";
button.className = "mousehuntActionButton small";
const locSpan = document.createElement("span");
locSpan.style.fontSize = "12px";
locSpan.innerText = "N/A";
const rhHint = observerTarget.querySelector(
".treasureMapInventoryView-relicHunter-hint"
);
const hint = rhHint.textContent;
Object.keys(hintMap).forEach(key => {
const loc = hintMap[key];
if (hint == key) {
locSpan.innerText = `Location: ${loc}`;
button.onclick = function () {
const newWindow = window.open(
"https://www.mousehuntgame.com/travel.php?tab=map"
);
newWindow.addEventListener("load", function () {
const hud = newWindow.document.querySelector(
".mousehuntHud-page-tabContent[data-tab='map']"
);
if (hud && hud.classList.contains("full")) {
newWindow.document
.querySelectorAll(
".travelPage-map-region-environment-link-name"
)
.forEach(el => {
if (el.textContent == loc) {
el.click();
}
});
const scrollTo = newWindow.document.querySelector(
".travelPage-map-simpleToggle.full"
);
if (scrollTo) {
scrollTo.scrollIntoView({
behavior: "auto",
block: "nearest",
inline: "nearest"
});
}
}
});
return false;
};
}
});
const buttonText = document.createElement("span");
buttonText.innerText = "Travel";
button.appendChild(buttonText);
div.appendChild(button);
div.appendChild(locSpan);
rhHint.insertAdjacentElement("afterend", div);
}
})();
})();