您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Improve the search logic of search bars in the game
当前为
// ==UserScript== // @name MouseHunt Enhanced Search (Beta) // @description Improve the search logic of search bars in the game // @author LethalVision // @version 0.6.0 // @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 http://greasyfork.icu/en/users/683695-lethalvision // ==/UserScript== // reference dictionaries in the format of <item name>:<search tags> - item names *must* be exact // search tags are case insensitive, set tags to empty to indicate that the item should be skipped // only exceptions/special cases need to be defined here, the script automatically generates the acronyms otherwise const CHEESE = { "Bonefort Cheese":"BF ","Checkmate Cheese":"CMC","Cloud Cheesecake":"CCC","Dragonvine Cheese":"DVC","Empowered SUPER|brie+":"ESB","Galleon Gouda":"GGC", "Limelight Cheese":"LLC","Nian Gao'da Cheese":"NGD","Polter-Geitost":"PG ","SUPER|brie+":"SB ","Wildfire Queso":"WF " } const CHARMS = { "Baitkeep Charm":"BKC","Festive Anchor Charm":"FAC_EAC","Timesplit Charm":"TSC","Dragonbane Charm":"DBC","Super Dragonbane Charm":"SDBC","Extreme Dragonbane Charm":"EDBC", "Ultimate Dragonbane Charm":"UDBC","Party Charm":"PaC","Super Party Charm":"SPaC","Extreme Party Charm":"EPaC","Ultimate Party Charm":"UPaC", "Rift Vacuum Charm":"RVC_Calcified Rift Mist_CRM","Rift Super Vacuum Charm":"RSVC_Calcified Rift Mist_CRM" } const CRAFTING = { "Chrome Celestial Dissonance Upgrade Kit":"CCDT","Chrome MonstroBot Upgrade Kit":"CMBT","Chrome Oasis Upgrade Kit":"COWNT_CPOT","Chrome School of Sharks Upgrade Kit":"CSOS", "Chrome Sphynx Wrath Upgrade Kit":"CSW","Chrome Storm Wrought Ballista Upgrade Kit":"CSWBT","Chrome Temporal Turbine Upgrade Kit":"CTT", "Chrome Thought Obliterator Upgrade Kit":"CTOT_CTHOT_CF2","Ful'Mina's Tooth":"Fulmina","Sandblasted Metal":"SBM","Stale SUPER brie+":"stale SB" } const SPECIAL = { "Ful'Mina's Gift":"fulmina","Ful'mina's Charged Toothlet":"fulmina","SUPER|brie+ Supply Pack":"SB ","Timesplit Rune":"TSR","Sky Sprocket":"HAL_high altitude loot", "Skysoft Silk":"HAL_high altitude loot","Enchanted Wing":"HAL_high altitude loot","Cloudstone Bangle":"HAL_high altitude loot","Sky Glass":"glore","Sky Ore":"glore" } const WEAPON = { "Biomolecular Re-atomizer Trap":"BRAT_BRT","Birthday Party Piñata Bonanza":"BPPB_Pinata","Blackstone Pass Trap":"BPT_BSP","Brain Extractor":"BE _Brain Bits", "Christmas Crystalabra Trap":"CCT_Calcified Rift Mist_CRM","Chrome MonstroBot":"CMB","Chrome RhinoBot":"CRB","Chrome Thought Obliterator Trap":"CTOT_CTHOT_CF2", "Circlet of Pursuing Trap":"A2","Circlet of Seeking Trap":"A1","Enraged RhinoBot":"ERB","Glacier Gatler":"GG_Charm","Ice Blaster":"IB_Charm","Icy RhinoBot":"IRB", "Interdimensional Crossbow":"IDCT_ICT","Maniacal Brain Extractor":"MBE_Brain Bits","Multi-Crystal Laser":"MCL", "Mysteriously unYielding Null-Onyx Rampart of Cascading Amperes":"MYNORCA","RhinoBot":"RB ","Rift Glacier Gatler":"RGG_Charm","S.A.M. F.E.D. DN-5":"SAMFED DN5_SAM FED DN5", "S.L.A.C.":"SLAC","S.L.A.C. II":"SLAC2_SLAC 2","S.S. Scoundrel Sleigher Trap":"SSSST_S4T","S.T.I.N.G. Trap":"STING_L1","S.T.I.N.G.E.R. Trap":"STINGER_L2", "Sandstorm MonstroBot":"SMB","Sleeping Stone Trap":"SST_T1","Slumbering Boulder Trap":"SBT_T2","Snow Barrage":"SNOB","Steam Laser Mk. I":"SLM1_SLMK1_SLMK 1_MARK 1", "Steam Laser Mk. II":"SLM2_SLMK2_SLMK 2_MARK 2","Steam Laser Mk. II (Broken!)":"SLM2_SLMK2_SLMK 2_MARK 2","Steam Laser Mk. III":"SLM3_SLMK3_SLMK 3_MARK 3", "Surprise Party Trap":"SPT_Party Charm","Tarannosaurus Rex Trap":"TREX","The Forgotten Art of Dance":"TFAOD_FART_FAD","The Haunted Manor Trap":"THMT_Charm", "The Holiday Express Trap":"THET_Charm","Thought Manipulator Trap":"TMT_F1","Thought Obliterator Trap":"TOT_THOT_F2","Timesplit Dissonance Trap":"TDT_TDW_TSD_TSW" } const BASE = { "10 Layer Birthday Cake Base":"Ten Layer_Charm","Clockwork Base":"CWB_CB ","Condemned Base":"CB _Charm","Cupcake Birthday Base":"CBB_Charm", "Extra Sweet Cupcake Birthday Base":"ESCBB_Charm","Rift Mist Diffuser Base":"RMDB_Charm","Skello-ton Base":"SB _Brain Bits", "Sprinkly Sweet Cupcake Birthday Base":"SSCBB_Charm","All Season Express Track Base":"ASETB_Charm" } // trap selector ignore lists const ignoreList = ['no_tag_selected','recommended','default','search_match']; var tagList; // piggyback on Mousehunt's jQuery // this is a smart thing to do that will never backfire on me var $ = $ || window.$; // one-time init functions go here function init() { initMp(); initTrap(); initInv(); } // == Marketplace Search == function initMp() { // extend templateutil.render to inject desired search terms const _parentRender = hg.utils.TemplateUtil.render; hg.utils.TemplateUtil.render = function(templateType, templateData) { if (templateType == 'ViewMarketplace_search') { // only edit marketplace search templateData.search_terms.forEach(category => updateMpSearch(category)); } return _parentRender(templateType, templateData); } } function updateMpSearch(category) { var refDict = getDict(category.name); if (!refDict) { // invalid name return; } category.terms.forEach(function(listing) { var tag = getInitials(listing.value, refDict); if (tag && listing.value.indexOf('hidden') == -1) { // skip if the listing already has "hidden" tag added listing.value += `<span class="hidden">${tag}</span>`; } }); } // == Trap Setup Search == function initTrap() { // hook into ajax calls to inject search tags into trap data var callback = function(options, originalOptions, jqXHR) { const componentUrl = 'managers/ajax/users/gettrapcomponents.php'; if (options.url.indexOf(componentUrl) != -1) { var _parentSuccess = originalOptions.success || options.success; var _extendedSuccess = function (data) { if (data.components && data.components.length > 0) { //console.log(data.components); data.components.forEach(component => updateTrapSearch(component)); } _parentSuccess(data); }; originalOptions.success = options.success = _extendedSuccess; } } $.ajaxPrefilter(callback); // hook into renderFromFile to hide the injected search tags const _parentRenderFile = hg.utils.TemplateUtil.renderFromFile; hg.utils.TemplateUtil.renderFromFile = function(TemplateGroupSource,templateType,templateData) { if (TemplateGroupSource == 'CampPage' && templateType == 'tag_groups') { modifyTemplateData(templateData); // ugly timeout delay here because cleanup should only run after renderFromFile returns setTimeout(cleanTrapSelector, 5); } return _parentRenderFile(TemplateGroupSource,templateType,templateData); } } // inject search tags into trap data function updateTrapSearch(component) { var refDict = getDict(component.classification); if (!refDict) { // invalid name return; } var tag = getInitials(component.name, refDict); if (!tag) { return; // item not in refDict and getInitials fails to generate an acronym for it } // Tag the trap component with its acronym if (!component.tag_types) { component.tag_types = []; } // count number of non-hidden tags var count = component.tag_types.length; const hiddenTagList = ['draconic', 'tactical', 'shadow', 'physical', 'rift', 'hydro', 'arcane', 'forgotten', 'law', 'charm_weak', 'charm_strong', 'charm_epic', 'charm_rare', 'trap_parts', 'bait_standard', 'event']; for (var i=0; i<hiddenTagList.length; i++) { if (component.tag_types.indexOf(hiddenTagList[i]) != -1) { count--; } if (count == 0) break; } if (count == 0) { // add default tag if all tags are hidden component.tag_types.push('default'); } component.tag_types.push(tag); } // remove the search tags before they are rendered function modifyTemplateData(templateData) { if (!templateData.tag_groups || templateData.tag_groups.length == 0) { return; // skip if it's empty } var tagList = app.pages.CampPage.getTags(); templateData.tag_groups = templateData.tag_groups.filter(function(tagGroup) { var tagName = tagGroup.type; // if tagName is invalid, reject this tag group // else if tagName is in ignoreList or tagList, render this tag group return (tagName && (ignoreList.includes(tagName) || tagList[tagName])); }); } // unfortunately the tag selector filter doesn't use renderFromFile so we have to // remove the search tags from the HTML directly function cleanTrapSelector() { var tagList = app.pages.CampPage.getTags(); var count = 0; // clean up the tag dropdown var selectQuery = document.querySelectorAll('select[data-filter=tag]'); if (selectQuery.length > 0) { var selectElem = selectQuery[0]; // remove the custom tags we added for the enhanced search for (var i=0; i<selectElem.length; i++) { var optValue = selectElem.options[i].value; if (!ignoreList.includes(optValue) && !tagList[optValue]) { selectElem.remove(i); i--; // options now has one less element count++; } } //console.log(`Removed ${count} select options`); } } // == Inventory Search == // TODO: break this up for the different tabs? (cheese, crafting, special, etc.) function initInv() { const inventoryUrl = 'managers/ajax/pages/page.php'; const originalOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function() { this.addEventListener("load", function() { if ( this.responseURL.indexOf(inventoryUrl) != -1) { updateInventory(); } }); originalOpen.apply(this, arguments); }; // in case the window is refreshed in inventory.php if (window.location.href.includes("inventory.php")) { updateInventory(); } } // inject search tags into the HTML element function updateInventory() { var query = document.querySelectorAll('.inventoryPage-item:not(.tagged)'); if (query.length > 0) { query.forEach(function(item){ item.classList.add("tagged"); var refDict = getDict(item.getAttribute('data-item-classification')) if (!refDict) { // invalid classification return; } var dataName = item.getAttribute('data-name'); var tag = getInitials(dataName, refDict); if (!tag) { return; } else { item.setAttribute('data-name', `${dataName}_${tag}`); } }); } } // == helpers == // returns the appropriate reference dict given a classification string // returns undefined if there is no match function getDict(classification) { if (typeof classification === 'string') { switch(classification.toLowerCase()) { case 'cheese': case 'bait': return CHEESE; case 'charms': case 'trinket': return CHARMS; case 'crafting': return CRAFTING; case 'special': case 'stat': return SPECIAL; case 'weapon': return WEAPON; case 'base': return BASE; } } // no match return undefined; } // get acronym from a given item name and refDict // this generates an acronym if item name is valid but not in the refDict, but returns undefined otherwise function getInitials(itemName, refDict) { // check refDict first if (refDict[itemName] != undefined) { return refDict[itemName]; } var wordList = itemName.split(' '); if (wordList.length < 2) { // 0-1 letter acronyms are useless return undefined; } else if (refDict == CHARMS && itemName.indexOf('Rift 20') != -1) { return 'R'+ wordList[1]; // special provision for rift 20xx charms } else if (refDict == CHARMS && itemName.indexOf('Warpath') != -1) { return undefined; // there's like 10+ warpath charms to exclude } else if (refDict == SPECIAL && itemName.indexOf('Airship') != -1) { return undefined; // exclude all airship parts } var acronym = ''; for (var i=0; i < wordList.length; i++) { if (wordList[i].length == 0) { // empty word, skip continue; } var letter = wordList[i].charAt(0); // first letter of word if (isNaN(letter)) { // not a number acronym += letter; } else { // is a number // return nothing, this skips weird acronyms like 20xx charms and KG cheese return undefined; } } if (acronym.length == 2) { // pad out length 2 tags so that they're searchable via "XX " acronym += ' '; } return acronym.toUpperCase(); } // This just checks if there are filters active in the trap selector // ported wholesale from source code because "hasFilter" is public but this isn't?? Hitgrab why function hasActiveFilters() { var campPage = app.pages.CampPage; return campPage.hasFilter('componentName') || campPage.hasFilter('componentPowerType') || campPage.hasFilter('componentTagType'); } // start script init();