您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add information to the Sunken City HUD that's available on the app.
// ==UserScript== // @name MouseHunt Sunken City HUD Enhancer // @description Add information to the Sunken City HUD that's available on the app. // @author LethalVision // @version 0.7 // @match https://www.mousehuntgame.com/* // @match https://apps.facebook.com/mousehunt/* // @icon https://www.google.com/s2/favicons?domain=mousehuntgame.com // @grant none // @license MIT // @namespace https://gf.qytechs.cn/en/users/683695-lethalvision // ==/UserScript== (function() { const DEBUG = false; const UPDATED_TAG = 'sc_hud_enhancer'; const SD_ITEM_TYPE = 'sand_dollar_stat_item'; // yes this script uses a disgusting mix of both web and app data to make pretty numbers var quest; var appZoneData; /** * adapted from Brad's style adder * * @param {string} styles The styles to add. */ function addStyles(styles) { const STYLE_ID = 'mh-sc-hud'; const existingStyles = document.getElementById(STYLE_ID); if (existingStyles) { return; } const style = document.createElement('style'); style.id = STYLE_ID; style.innerHTML = styles; document.head.appendChild(style); }; // listen to HUD/Sonar render functions, and call the respective updaters function addHooks() { const originalRender = app.views.HeadsUpDisplayView.hud.render; app.views.HeadsUpDisplayView.hud.render = function(tmpUser) { const value = originalRender(tmpUser); debugLog('SC HUD: render'); setTimeout(updateHud, 100); return value; }; const originalSonar = app.views.HeadsUpDisplayView.hud.sunkenCitySonar; app.views.HeadsUpDisplayView.hud.sunkenCitySonar = function() { originalSonar(); setTimeout(updateSonar, 100); }; } // fetch SC data through app endpoint function fetchAppZoneData() { debugLog('SC HUD: fetchAppZoneData'); const xhr = new XMLHttpRequest(); xhr.open("GET", "https://www.mousehuntgame.com/api/action/passiveturn", true); xhr.onload = () => { const parsedResponse = JSON.parse(xhr.response); const locationStats = parsedResponse?.user?.location_stats; if (!locationStats) { console.log('SC HUD: Cannot get location stats from app API response'); return; } appZoneData = locationStats; updateHud(); }; xhr.send(); } // main update function for HUD function updateHud() { if (user.environment_type != 'sunken_city') { return; } quest = user.quests.QuestSunkenCity; if (!quest.is_diving) { // skip HUD update if not diving return; } // fetch app data if not present/outdated if (!appZoneData || appZoneData.zones[0].num != quest.active_zone) { fetchAppZoneData(); return; } const HUD = document.getElementById('hudLocationContent'); // try again later if HUD is animating const player = HUD.querySelector('.player'); if (!player || player.classList.contains('animate') || player.classList.contains('launch')) { setTimeout(updateHud, 250); return; } // add distance/length const hudZones = HUD.querySelectorAll('.zone'); updateZoneElems(hudZones); // change current zone display const hudDistance = HUD.querySelector('.distanceContainer'); if (!hudDistance.classList.contains(UPDATED_TAG)) { // save and replace current dive const currentDiveElem = hudDistance.querySelector('.distance'); const currentDive = currentDiveElem.innerText; currentDiveElem.remove(); hudDistance.innerHTML = '<span class="zone_progress"></span>' + hudDistance.innerHTML; // update tooltip const tooltipElem = hudDistance.querySelector('.toolTip'); tooltipElem.innerHTML = `<b>Current:</b> <span class="distance">${currentDive}</span><br>` + tooltipElem.innerHTML; hudDistance.classList.add(UPDATED_TAG); } const currentZone = appZoneData.zones[0]; hudDistance.querySelector('.zone_progress').innerText = `${quest.distance - currentZone.start}m / ${currentZone.length}m`; // add sand dollars const hudCraftingItems = HUD.querySelector('.craftingItems'); if (!hudCraftingItems.classList.contains(UPDATED_TAG)) { const sdName = 'Sand Dollars'; const sdDesc = 'Used as currency in the Sunken City!'; const tooltip = `<div class="toolTip"><b>${sdName}</b><br>${sdDesc}<div class="arrow"><span></span></div></div>`; hudCraftingItems.innerHTML += `<a href="#" onclick="hg.views.ItemView.show('${SD_ITEM_TYPE}'); return false;"><img src=""><span class="item quantity" data-item-type="${SD_ITEM_TYPE}"></span>${tooltip}</a>`; hudCraftingItems.querySelectorAll('a').forEach(elem => {elem.style.lineHeight = '24px'}); hudCraftingItems.classList.add(UPDATED_TAG); } hudCraftingItems.querySelectorAll('.item.quantity').forEach(elem => { if (!elem.innerText.includes(',')) { elem.innerText = numberFormat(elem.innerText); } }); // fetch SD item, then handle UI update in the callback function hg.utils.UserInventory.getItem(SD_ITEM_TYPE, (data) => {updateSandDollar(data, HUD)}, true); } // update function for Sonar function updateSonar() { if (!document.querySelector('.sonar') || !appZoneData) { return; } const sonarZones = document.querySelectorAll('.sonar .zone'); updateZoneElems(sonarZones); // show extra zones that can't be seen on sonar const sonarWater = document.querySelector('.sonar .water'); var extraZoneElem = sonarWater.querySelector('.hidden_zone'); if (!extraZoneElem) { // create a tooltip styled box extraZoneElem = document.createElement('div'); extraZoneElem.className = 'hidden_zone'; extraZoneElem.style.display = 'none'; sonarWater.appendChild(extraZoneElem); } const hiddenZones = getUnseenZones(); extraZoneElem.style.fontSize = hiddenZones.length > 2 ? '8px' : ''; extraZoneElem.innerHTML = ''; if (hiddenZones.length > 0) { hiddenZones.forEach(zone => { extraZoneElem.innerHTML += `<div><b>${zone.name} (${zone.length}m)</b><br>${zone.start - quest.distance}m away</div>`; }); extraZoneElem.style.display = ''; } } // === Helpers === // updater for zones, generalized for both HUD and sonar function updateZoneElems(zoneElems) { for (var i = 0; i < zoneElems.length; i++) { // assume hud zones are arranged sequentially const zoneElem = zoneElems[i]; if (!zoneElem.classList.contains(UPDATED_TAG)) { // create spans to be updated later zoneElem.querySelector('.length')?.remove(); zoneElem.querySelector('.name').innerHTML += '<span class="length"></span><br><span class="distance_to_zone"></span>'; zoneElem.classList.add(UPDATED_TAG); } // update zone length const lengthElem = zoneElem.querySelector('.length'); const distanceAwayElem = zoneElem.querySelector('.distance_to_zone'); if (zoneElem.classList.contains('past')) { lengthElem.innerText = ''; distanceAwayElem.innerText = ''; } else if (zoneElem.classList.contains('active')) { lengthElem.innerText = ` (${getZoneByElem(zoneElem).length}m)`; distanceAwayElem.innerText = 'Current zone'; } else { const zoneData = getZoneByElem(zoneElem); lengthElem.innerText = ` (${zoneData.length}m)`; distanceAwayElem.innerText = `${zoneData.start - quest.distance}m away`; } } } // util to query zone by its num function getZoneByNum(zoneNum) { return appZoneData.zones.find(zone => zone.num == zoneNum); } // return zone data for given zone DOM elem function getZoneByElem(zoneElem) { const elemZoneNum = parseInt(zoneElem.getAttribute('data-zone-num')); var zoneData = getZoneByNum(elemZoneNum); if (!zoneData) { // quest data can have more entries than app data if upcoming zones are short // forge zone data in the app format using quest data const questZone = quest.zones.find(zone => zone.num == elemZoneNum); const prevZone = getZoneByNum(elemZoneNum - 1); if (questZone && prevZone) { const newZoneData = {}; newZoneData.num = elemZoneNum; newZoneData.type = questZone.type; newZoneData.name = questZone.name; newZoneData.length = questZone.length; newZoneData.start = prevZone.start + prevZone.length; appZoneData.zones.push(newZoneData); return newZoneData; } } return zoneData; } // returns zones that cannot be completely seen function getUnseenZones() { const unseenZones = []; appZoneData.zones.forEach(zone => { if (zone.start - quest.distance >= 1000) { // arbitrary distance, set at where the sonar starts cutting off zone info unseenZones.push(zone); } }); // hidden zone debug if (DEBUG && unseenZones.length == 0) { unseenZones.push(appZoneData.zones[2]); unseenZones.push(appZoneData.zones[3]); } return unseenZones; } // update Sand Dollar image + count function updateSandDollar(data, HUD) { var sdElem; HUD.querySelectorAll('.craftingItems a').forEach((elem) => { const itemType = elem.querySelector('.item.quantity')?.getAttribute('data-item-type'); if (itemType == SD_ITEM_TYPE) { sdElem = elem; } }); if (!sdElem) { return; } sdElem.querySelector('img').src = data.thumbnail; // numberFormat is defined by Mousehunt sdElem.querySelector('.item.quantity').innerText = numberFormat(data.quantity); } // debug logger function debugLog(logItem){ if (DEBUG) console.log(logItem); } // === Init === // I don't want to mess with styles, but the !important tags leave me no choice addStyles(` .hidden_zone{ position:absolute; top:28px; right:2px; padding:5px; border:2px solid #000; background:#fff; color:#000; font-size:9px; border-radius:10px; z-index:20; } .hidden_zone div{ padding-bottom:2px; } .sunkenCityHud .craftingItems a .toolTip{ left:61px; } .sunkenCityHud .sunkenCharms a .toolTip{ display:none!important; } .sunkenCityHud .sunkenCharms a:hover .toolTip{ display:block!important; } .sunkenCityHud.murky_depths .water .zone .name{ text-shadow:0px 0px 1px #d0bbf6,1px 1px 1px #8330a2; }`); addHooks(); updateHud(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址