MouseHunt - GWH 2022 Nice/Naughty map colour coder

Color codes mice on Nice/Naughty maps according to type. Max ML shown per group and AR shown individually. ARs given for standard cheese assume SB, if Gouda is relevant the ARs are given as ([Gouda] | [SB]). ARs given for (G)PP are given as ([PP] | [GPP]).

当前为 2022-12-07 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name MouseHunt - GWH 2022 Nice/Naughty map colour coder
  3. // @author tsitu & Leppy & in59te & Warden Slayer
  4. // @namespace https://greasyfork.org/en/users/967077-maidenless
  5. // @version 1.1.1
  6. // @description Color codes mice on Nice/Naughty maps according to type. Max ML shown per group and AR shown individually. ARs given for standard cheese assume SB, if Gouda is relevant the ARs are given as ([Gouda] | [SB]). ARs given for (G)PP are given as ([PP] | [GPP]).
  7. // @match http://www.mousehuntgame.com/*
  8. // @match https://www.mousehuntgame.com/*
  9. // @include https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
  10. // ==/UserScript==
  11.  
  12. // Credit to the minluck and mice population prepared by Seli and Neb.
  13.  
  14. const displayMinLuck = true; // Will display minluck for the group of mouse iff true.
  15. const displayAR = true; // Will display the AR for each uncaught mouse iff true.
  16. const displayHunterCheese = false; // Will display which group of mouse the hunter if attempting iff true.
  17. let assignBaitChange = true; // Avoid the bait change event being registered more than once.
  18.  
  19. // If the chest name contains any of the following as a substring, enable the colour coder.
  20. const chestKeywords = [
  21. "Nice",
  22. "Naughty",
  23. ];
  24.  
  25. // name, AR - per UNIX 1670418873
  26. // ARs given for standard cheese assume SB, if Gouda is relevant the ARs are given as "[Gouda] | [SB]"
  27. // ARs given for (G)PP are given as "[PP] | [GPP]"
  28.  
  29. const standardAnyMice = [
  30. ["Hoarder", "22.74%"], //lowest currently known sb AR%, some areas may be better
  31. ];
  32. const ppAnyMice = [
  33. ["Snowflake", "2.6% | 4.48%"], //lowest currently known, some areas may be better
  34. ["Stuck Snowball", "3.86% | 4.48%"], //lowest currently known, some areas may be better
  35. ];
  36. const gppAnyMice = [
  37. ["Glazy", "4.35%"], //lowest currently known, some areas may be better
  38. ["Joy", "5.86%"], //lowest currently known, some areas may be better
  39. ];
  40. const bossMice = [
  41. ["Frost King", "special"],
  42. ];
  43. const standardHillMice = [
  44. ["Candy Cane", "24.18%"],
  45. ["Nice Knitting", "12.94% | 8.06%"],
  46. ["Shorts-All-Year", "10.94%"],
  47. ["Snow Scavenger", "6.72%"],
  48. ["Toboggan Technician", "11.13%"],
  49. ["Young Prodigy Racer", "19.72% | 15.55%"],
  50. ];
  51. const ppHillMice = [
  52. ["Triple Lutz", "5.04%"],
  53. ];
  54. const ppGppHillMice = [
  55. ["Black Diamond Racer", "19.22% | 8.73%"],
  56. ["Double Black Diamond Racer", "4.02% | 3.63%"],
  57. ["Free Skiing", "4.09% | 3.76%"],
  58. ["Great Giftnapper", "3.06% | 2.61%"],
  59. ["Nitro Racer", "4.24% | 3.89%"],
  60. ["Ol' King Coal", "2.11% | 2.23%"],
  61. ["Rainbow Racer", "2.36% | 2.17%"],
  62. ["Snow Boulder", "7.83% | 11.91%"],
  63. ["Snow Golem Jockey", "9.8% | 8.22%"],
  64. ["Snowball Hoarder", "7.99% | 13.69%"],
  65. ["Sporty Ski Instructor", "12.75% | 10%"],
  66. ["Wreath Thief", "10.3% | 8.41%"],
  67. ];
  68. const standardWorkshopMice = [
  69. ["Gingerbread", "18.77%"],
  70. ["Greedy Al", "11.08%"],
  71. ["Mouse of Winter Future", "14.5% | 9.23%"],
  72. ["Mouse of Winter Past", "11.08%"],
  73. ["Mouse of Winter Present", "22.75% | 16.31%"],
  74. ];
  75. const sbWorkshopMice = [
  76. ["Scrooge", "9.23%"],
  77. ];
  78. const ppWorkshopMice = [
  79. ["Ribbon", "5.97%"],
  80. ];
  81. const ppGppWorkshopMice = [
  82. ["Christmas Tree", "9.7% | 7.99%"],
  83. ["Destructoy", "7.61% | 10.69%"],
  84. ["Elf", "17.44% | 8.7%"],
  85. ["Mad Elf", "1.51% | 1.76%"],
  86. ["Nutcracker", "4.33% | 3.88%"],
  87. ["Ornament", "3.8% | 3.41%"],
  88. ["Present", "10.03% | 7.52%"],
  89. ["Ridiculous Sweater", "8.52% | 9.75%"],
  90. ["Snow Golem Architect", "2.82% | 4.11%"],
  91. ["Stocking", "3.21% | 3.53%"],
  92. ["Toy", "10.62% | 11.52%"],
  93. ["Toy Tinkerer", "7.41% | 5.76%"],
  94. ];
  95. const standardFortressMice = [
  96. ["Confused Courier", "13.75%"],
  97. ["Frigid Foreman", "11.67% | 8.13%"],
  98. ["Miser", "15.63%"],
  99. ["Missile Toe", "15.63%"],
  100. ["Snowblower", "12.78% | 7.5%"],
  101. ["Snowglobe", "21.67% | 9.38%"],
  102. ];
  103. const ppFortressMice = [
  104. ["Builder", "4.29%"],
  105. ];
  106. const ppGppFortressMice = [
  107. ["Borean Commander", "4.91% | 6.37%"],
  108. ["Glacia Ice Fist", "11.66% | 4.46%"],
  109. ["Great Winter Hunt Impostor", "6.75% | 11.78%"],
  110. ["Iceberg Sculptor", "3.07% | 2.55%"],
  111. ["Naughty Nougat", "4.29% | 6.05%"],
  112. ["Reinbo", "19.02% | 4.14%"],
  113. ["S.N.O.W. Golem", "6.75% | 4.78%"],
  114. ["Slay Ride", "9.2% | 11.46%"],
  115. ["Snow Fort", "11.04% | 8.6%"],
  116. ["Snow Sorceress", "3.68% | 6.37%"],
  117. ["Squeaker Claws", "4.91% | 3.18%"],
  118. ["Tundra Huntress", "4.91% | 2.55%"],
  119. ];
  120.  
  121. // group name, mice, minimum luck, bait, bait ID, color
  122. const miceGroups = [
  123. ["Any<br>Standard", standardAnyMice, 10, "", 114, "#B6D7A8"],
  124. ["Any<br>PP", ppAnyMice, 24, "", 2522, "#93C47D"],
  125. ["Any<br>GPP", gppAnyMice, 1, "", 2733 , "#6AA84F "],
  126. ["Any<br>Boss", bossMice, 30, "", 114, "#1C4587"],
  127. ["Hill<br>Standard", standardHillMice, 33, "", 114, "#FCE5CD"],
  128. ["Hill<br>PP", ppHillMice, 12, "", 2522, "#F6B26B"],
  129. ["Hill<br>PP/GPP", ppGppHillMice, 38, "", 2522, "#F9CB9C"],
  130. ["Workshop<br>Standard", standardWorkshopMice, 35, "", 114, "#F4CCCC"],
  131. ["Workshop<br>SB", sbWorkshopMice, 33, "", 114, "#"],
  132. ["Workshop<br>PP", ppWorkshopMice, 8, "", 2522, "#E06666"],
  133. ["Workshop<br>PP/GPP", ppGppWorkshopMice, 44, "", 2522, "#EA9999"],
  134. ["Fortress<br>Standard", standardFortressMice, 38, "", 114, "#C9DAF8"],
  135. ["Fortress<br>PP", ppFortressMice, 17, "", 2522, "#3C78D8"],
  136. ["Fortress<br>PP/GPP", ppGppFortressMice, 53, "", 2522, "#6FA8DC"],
  137. ];
  138.  
  139. class Mouse {
  140. constructor(name, AR) {
  141. this.name = name;
  142. this.AR = AR;
  143. }
  144. }
  145.  
  146. class MiceGroup {
  147. constructor(name, minluck, cheese, baitId, color) {
  148. this.name = name;
  149. this.id = name.replace(/[^a-zA-Z0-9]/g,'')
  150. this.mice = [];
  151. this.minluck = minluck;
  152. this.cheese = cheese;
  153. this.baitId = baitId;
  154. this.color = color;
  155. this.count = 0;
  156. }
  157.  
  158. add(mouse) {
  159. this.mice.push(mouse);
  160. }
  161.  
  162. hasMouse(name) {
  163. for (let i = 0; i < this.mice.length; i++) {
  164. if (this.mice[i].name == name) {
  165. return true;
  166. }
  167. }
  168. return false;
  169. }
  170.  
  171. getAR(name) {
  172. for (let i = 0; i < this.mice.length; i++) {
  173. if (this.mice[i].name == name) {
  174. return this.mice[i].AR;
  175. }
  176. }
  177. return "0.00%";
  178. }
  179. }
  180.  
  181. let allMiceGroups = []; // This contains all info about the various group of mice.
  182. let miceNameDict = {}; // If displayAR == true, we are forced to modify the <span> element's text to mouse name + AR, so we need to be able to go back to the original mouse name.
  183.  
  184. function initialise() {
  185. // Avoid initialising more than once as the script can be called multiple times by other plug-in.
  186. if (allMiceGroups.length > 0) {
  187. return;
  188. }
  189.  
  190. // Populate allMiceGroups from miceGroups
  191. for (let i = 0; i < miceGroups.length; i++) {
  192. let miceGroup = new MiceGroup(
  193. miceGroups[i][0],
  194. miceGroups[i][2],
  195. miceGroups[i][3],
  196. miceGroups[i][4],
  197. miceGroups[i][5]
  198. );
  199. for (let j = 0; j < miceGroups[i][1].length; j++) {
  200. miceGroup.add(new Mouse(miceGroups[i][1][j][0], miceGroups[i][1][j][1]));
  201. }
  202. allMiceGroups.push(miceGroup);
  203. }
  204. }
  205.  
  206. function addAr(mouseSpan, mouseName, miceGroup) {
  207. const mouseNameWithAr = mouseName + " (" + miceGroup.getAR(mouseName) + ")";
  208. //console.log("checking " + mouseNameWithAr + " in dict: " + (mouseNameWithAr in miceNameDict));
  209. if (!(mouseNameWithAr in miceNameDict)) {
  210. miceNameDict[mouseNameWithAr] = mouseName;
  211. }
  212. mouseSpan.querySelector(".treasureMapView-goals-group-goal-name").querySelector("span").firstChild .textContent = mouseNameWithAr;
  213. }
  214.  
  215. const defaultColor = miceGroups[0][5];
  216. const hunterColor = [defaultColor, defaultColor, defaultColor, defaultColor, defaultColor];
  217. var numHunters = 0;
  218.  
  219. function getCheeseColor(cheese) {
  220. for (let i = 0; i < allMiceGroups.length; i++) {
  221. if (allMiceGroups[i].cheese == cheese) {
  222. return allMiceGroups[i].color;
  223. }
  224. }
  225. return defaultColor; // return the default color if no matching cheese.
  226. }
  227.  
  228. function hunterColorize() {
  229. document.querySelectorAll(".treasureMapRootView-subTab:not(.active)")[0].click(); //swap between Goals and Hunters
  230. let hunters = document.querySelectorAll(".treasureMapView-componentContainer");
  231. const list_of_cheese = [];
  232. for (let i = 0; i < hunters.length; i++) {
  233. list_of_cheese.push(hunters[i].children[2].title);
  234. }
  235. //console.log(list_of_cheese);
  236. numHunters = hunters.length;
  237. document.querySelectorAll(".treasureMapRootView-subTab:not(.active)")[0].click();
  238.  
  239. for (let i = 0; i < numHunters; i++) {
  240. hunterColor[i] = getCheeseColor(list_of_cheese[i]);
  241. }
  242. //console.log(hunterColor);
  243. }
  244.  
  245. function colorize() {
  246. const greyColor = "#949494";
  247.  
  248. const isChecked =
  249. localStorage.getItem("highlightPref") === "uncaught-only" ? true : false;
  250. const isCheckedStr = isChecked ? "checked" : "";
  251.  
  252. if (
  253. document.querySelectorAll(".treasureMapView-goals-group-goal").length === 0
  254. ) {
  255. return;
  256. }
  257.  
  258. for (let i = 0; i < allMiceGroups.length; i++) {
  259. allMiceGroups[i].count = 0;
  260. }
  261.  
  262. /*
  263. for (const key of Object.keys(miceNameDict)) {
  264. console.log(key + ": " + miceNameDict[key])
  265. }
  266. */
  267.  
  268. document.querySelectorAll(".treasureMapView-goals-group-goal").forEach(el => {
  269. let mouseName = el.querySelector(".treasureMapView-goals-group-goal-name").querySelector("span").firstChild .textContent;
  270. // Fix up the mouse name if we added AR info in.
  271. if (mouseName in miceNameDict) {
  272. mouseName = miceNameDict[mouseName];
  273. }
  274. //console.log(mouseName);
  275.  
  276. for (let i = 0; i < allMiceGroups.length; i++) {
  277. if (allMiceGroups[i].hasMouse(mouseName)) {
  278. el.style.backgroundColor = allMiceGroups[i].color;
  279. if (el.className.indexOf(" complete ") < 0) {
  280. allMiceGroups[i].count++;
  281. if (displayAR) {
  282. addAr(el, mouseName, allMiceGroups[i]);
  283. }
  284. } else {
  285. if (isChecked) el.style.backgroundColor = "white";
  286. }
  287. }
  288. }
  289. });
  290.  
  291. /*for (let i = 0; i < allMiceGroups.length; i++) {
  292. console.log(allMiceGroups[i].name + " " + allMiceGroups[i].cheese + " " + allMiceGroups[i].count);
  293. }*/
  294.  
  295. // Remove existing tsitu-map-div related elements before proceeding
  296. document.querySelectorAll(".tsitu-map-div").forEach(el => el.remove());
  297.  
  298. const masterDiv = document.createElement("div");
  299. masterDiv.className = "tsitu-map-div";
  300. masterDiv.style =
  301. "display: inline-flex; margin-bottom: 5px; width: 100%; text-align: center; line-height: 1.25; overflow: hidden";
  302. const spanStyle =
  303. "; width: auto; padding: 5px; font-weight: bold; font-size: 12.75px; text-shadow: 0px 0px 11px white";
  304.  
  305. const spans = [];
  306.  
  307. for (let i = 0; i < allMiceGroups.length; i++) {
  308. const newSpan = document.createElement("span");
  309. newSpan.classList.add(allMiceGroups[i].id + "Span");
  310. if (allMiceGroups[i].count > 0) {
  311. newSpan.style = "background-color: " + allMiceGroups[i].color + spanStyle;
  312. }
  313. else {
  314. newSpan.style = "background-color: " + greyColor + spanStyle;
  315. }
  316. newSpan.innerHTML = allMiceGroups[i].name;
  317. if (displayMinLuck) {
  318. newSpan.innerHTML = newSpan.innerHTML + "<br> ML: " + allMiceGroups[i].minluck;
  319. }
  320. newSpan.innerHTML = newSpan.innerHTML + "<br> Mice: " + allMiceGroups[i].count;
  321. if (allMiceGroups[i].count > 0) {
  322. spans.push(newSpan);
  323. }
  324. }
  325.  
  326. const ARDiv = document.createElement("div");
  327. ARDiv.className = "tsitu-map-div";
  328. ARDiv.style =
  329. "display: inline-flex; margin-bottom: 10px; width: 100%; text-align: left; line-height: 1.25; overflow: hidden";
  330. const ARStyle =
  331. "background-color: white; width: auto; padding: 0px; font-weight: bold; font-size: 11px";
  332.  
  333. const ARwarning = document.createElement("span");
  334. ARwarning.innerHTML = "ARs assume SB, if Gouda is relevant the ARs are given as ([Gouda] | [SB]).<br>ARs given for (G)PP are given as ([PP] | [GPP]).";
  335. ARwarning.style = ARStyle;
  336. if (displayAR) {ARDiv.appendChild(ARwarning);}
  337.  
  338. // Highlight uncaught only feature
  339. const highlightLabel = document.createElement("label");
  340. highlightLabel.htmlFor = "tsitu-highlight-box";
  341. highlightLabel.innerText = "Highlight uncaught mice only";
  342.  
  343. const highlightBox = document.createElement("input");
  344. highlightBox.type = "checkbox";
  345. highlightBox.name = "tsitu-highlight-box";
  346. highlightBox.style.verticalAlign = "middle";
  347. highlightBox.checked = isChecked;
  348. highlightBox.addEventListener("click", function () {
  349. if (highlightBox.checked) {
  350. localStorage.setItem("highlightPref", "uncaught-only");
  351. } else {
  352. localStorage.setItem("highlightPref", "all");
  353. }
  354. if (displayHunterCheese) {
  355. hunterColorize();
  356. }
  357. colorize();
  358. });
  359.  
  360. const highlightDiv = document.createElement("div");
  361. highlightDiv.className = "tsitu-map-div";
  362. highlightDiv.style = "float: right; position: relative; z-index: 1";
  363. highlightDiv.appendChild(highlightBox);
  364. highlightDiv.appendChild(highlightLabel);
  365.  
  366. // Assemble masterDiv
  367. for (let i = 0; i < spans.length; i++) {
  368. masterDiv.appendChild(spans[i]);
  369. }
  370.  
  371. // Inject into DOM
  372. const insertEl = document.querySelector(
  373. ".treasureMapView-leftBlock .treasureMapView-block-content"
  374. );
  375. if (
  376. insertEl &&
  377. document.querySelector(
  378. ".treasureMapRootView-header-navigation-item.tasks.active" // On "Active Maps"
  379. )
  380. ) {
  381. insertEl.insertAdjacentElement("afterbegin", highlightDiv);
  382. insertEl.insertAdjacentElement("afterbegin", ARDiv);
  383. insertEl.insertAdjacentElement("afterbegin", masterDiv);
  384. }
  385.  
  386. var canvas = [];
  387. var div = document.getElementsByClassName("treasureMapView-hunter-wrapper mousehuntTooltipParent");
  388.  
  389. if (displayHunterCheese) {
  390. for (var i=0; i<div.length; i++){
  391. canvas[i] = document.createElement('canvas');
  392. canvas[i].id = "hunter-canvas";
  393. canvas[i].style = "; bottom: 0px; left: 0px; position: absolute; width: 15px; height: 15px; background: " + hunterColor[i] + "; border: 1px solid black";
  394. div[i].appendChild(canvas[i]);
  395. }
  396. }
  397.  
  398. // "Goals" button
  399. document.querySelector("[data-type='show_goals']").onclick = function () {
  400. colorize();
  401. };
  402.  
  403. if (assignBaitChange) {
  404. // Avoid assigning the event more than once.
  405. assignBaitChange = false;
  406. for (let i = 0; i < allMiceGroups.length; i++) {
  407. if (allMiceGroups[i].count > 0) {
  408. //console.log(allMiceGroups[i].id + " " + allMiceGroups[i].cheese + " " + allMiceGroups[i].count);
  409. //Warden added this (waves)
  410. $(document).on('click', '.' + allMiceGroups[i].id + 'Span', function() {
  411. hg.utils.TrapControl.setBait(allMiceGroups[i].baitId).go();
  412. });
  413. }
  414. }
  415. }
  416. }
  417.  
  418. // Listen to XHRs, opening a map always at least triggers board.php
  419. const originalOpen = XMLHttpRequest.prototype.open;
  420. XMLHttpRequest.prototype.open = function () {
  421. this.addEventListener("load", function () {
  422. const chestEl = document.querySelector(
  423. ".treasureMapView-mapMenu-rewardName"
  424. );
  425.  
  426. if (chestEl) {
  427. const chestName = chestEl.textContent;
  428. if (
  429. chestName && chestKeywords.some(v => chestName.includes(v))
  430. ) {
  431. initialise();
  432. if (displayHunterCheese) {
  433. hunterColorize();
  434. }
  435. colorize();
  436. }
  437. }
  438. });
  439. originalOpen.apply(this, arguments);
  440. };