Genshin Impact Interactive World Map Enhancements

Makes everything markable, removes right sidebar & more

  1. // ==UserScript==
  2. // @name Genshin Impact Interactive World Map Enhancements
  3. // @description Makes everything markable, removes right sidebar & more
  4. // @namespace https://github.com/rainniel/tampermonkey-scripts
  5. // @supportURL https://github.com/rainniel/tampermonkey-scripts/issues
  6. // @version 1.0
  7. // @author Rainniel
  8. // @license MIT
  9. // @match https://genshin-impact-map.appsample.com/
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=genshin-impact-map.appsample.com
  11. // @require https://unpkg.com/jquery@3.7.1/dist/jquery.min.js
  12. // ==/UserScript==
  13. /* globals jQuery, $, waitForKeyElements */
  14.  
  15. (function () {
  16. 'use strict';
  17.  
  18. // Intercept & modify the marking data request and makes
  19. // everything markable except for Teleporters and Waverider waypoints
  20. (function (open) {
  21. XMLHttpRequest.prototype.open = function (method, url) {
  22. this.addEventListener('readystatechange', function () {
  23. if (this.readyState === 4 && this.status === 200) {
  24.  
  25. let jsonResponse = null;
  26. try {
  27. jsonResponse = JSON.parse(this.responseText);
  28. } catch (e) { }
  29.  
  30. if (jsonResponse) {
  31. const hasCode = 'code' in jsonResponse;
  32. const hasData = 'data' in jsonResponse;
  33. const hasHeaders = 'headers' in jsonResponse;
  34. const hasTime = 'time' in jsonResponse;
  35.  
  36. if (hasCode && hasData && hasHeaders && hasTime) {
  37. if (Array.isArray(jsonResponse.data)) {
  38. const excludeList = ['o2', 'o3', 'o154', 'o190'];
  39. jsonResponse.data.forEach(item => {
  40. if (Array.isArray(item) && item.length > 3) {
  41. if (!excludeList.includes(item[1]) && item[3] < 5) {
  42. item[3] = 5;
  43. }
  44. }
  45. });
  46. }
  47. Object.defineProperty(this, 'responseText', { value: jsonResponse });
  48. }
  49. }
  50. }
  51. }, false);
  52. open.apply(this, arguments);
  53. };
  54. })(XMLHttpRequest.prototype.open);
  55.  
  56. // Key elements
  57. const MapSidebarMenuClass = '.MapSidebarMenu';
  58. const HideFoundMarkersClass = '.css-108jljv';
  59. const PlacesFoundClass = '.css-1s9cync';
  60. const CommentImageClass = '.css-8ndowl';
  61. const RightBarClass = '.MapLayout_Rightbar';
  62.  
  63. // Removes the map focus blue border
  64. $('head').append('<style>.gm-style iframe + div { border:none!important; }</style>');
  65.  
  66. // Waits for element to appear then executes a function, has 20 seconds timeout
  67. function onElementReady(selector, action) {
  68. function checkElement() {
  69. return !!document.querySelector(selector);
  70. }
  71.  
  72. if (checkElement()) {
  73. action(document.querySelector(selector));
  74. } else {
  75. const checkInterval = setInterval(function () {
  76. if (checkElement()) {
  77. action(document.querySelector(selector));
  78. clearInterval(checkInterval);
  79. }
  80. }, 100);
  81.  
  82. setTimeout(function () {
  83. clearInterval(checkInterval);
  84. }, 20000);
  85. }
  86. }
  87.  
  88. // Simulate mouse left click
  89. function clickElement(element) {
  90. if (element) {
  91. const clickEvent = new MouseEvent('click', {
  92. bubbles: true,
  93. cancelable: true,
  94. });
  95. element.dispatchEvent(clickEvent);
  96. }
  97. }
  98.  
  99. // Toggles 'Hide Found Markers' on/off in left sidebar Markers
  100. function toggleHideFoundMarkers() {
  101. const elements = document.querySelector(HideFoundMarkersClass);
  102. if (elements) {
  103. const checkbox = elements.querySelectorAll('.PrivateSwitchBase-input').item(0);
  104. clickElement(checkbox);
  105. }
  106. }
  107.  
  108. // Waits for user data to load
  109. onElementReady(PlacesFoundClass, function () {
  110. const placesFoundElement = $(PlacesFoundClass);
  111. let lastText = placesFoundElement.text();
  112.  
  113. const placesFoundInterval = setInterval(function () {
  114. if (lastText != placesFoundElement.text()) {
  115. clearInterval(placesFoundInterval);
  116.  
  117. toggleHideFoundMarkers();
  118.  
  119. // Adds 'Hide Found Markers' toggle button to sidebar menu
  120. const dividerElements = $(`${MapSidebarMenuClass} ul hr`);
  121. if (dividerElements.length > 0) {
  122.  
  123. // SVG credits to Google Fonts
  124. dividerElements.first().after(`
  125. <li class="li-toggle-hide-found-markers" style="padding:4px; display: none" tabindex="-1">
  126. <button class="toggle-hide-found-markers btn btn-success" style="width:100%" title="Toggle Hide Found Markers">
  127. <svg viewBox="0 -960 960 960" fill="#fff">
  128. <path d="M482-160q-134 0-228-93t-94-227v-7l-64 64-56-56 160-160 160 160-56 56-64-64v7q0 100 70.5 170T482-240q26 0 51-6t49-18l60 60q-38 22-78 33t-82 11Zm278-161L600-481l56-56 64 64v-7q0-100-70.5-170T478-720q-26 0-51 6t-49 18l-60-60q38-22 78-33t82-11q134 0 228 93t94 227v7l64-64 56 56-160 160Z"/>
  129. </svg>
  130. </button>
  131. </li>
  132. `);
  133.  
  134. $(document).on('click', '.toggle-hide-found-markers', toggleHideFoundMarkers);
  135. $('.li-toggle-hide-found-markers').slideDown();
  136. }
  137. }
  138. }, 100);
  139.  
  140. setTimeout(function () {
  141. clearInterval(placesFoundInterval);
  142. }, 20000);
  143. });
  144.  
  145. // Removes the right sidebar
  146. onElementReady(RightBarClass, function () {
  147. $(RightBarClass).remove();
  148. });
  149.  
  150. // Moves the comment image pop-up to the left side
  151. document.addEventListener('click', function (event) {
  152. const img = event.target.closest('img');
  153. if (img) {
  154. const imgWidth = $(`${CommentImageClass} img`).width();
  155. const iframeWidth = $(`${CommentImageClass} iframe`).width();
  156. let width = 0;
  157.  
  158. if (imgWidth > 0) {
  159. width = imgWidth;
  160. } else if (iframeWidth > 0) {
  161. width = iframeWidth;
  162. }
  163.  
  164. $(CommentImageClass).width(width + 40);
  165. }
  166. });
  167. })();