Greasy Fork镜像 支持简体中文。

Geoguessr Blink Mode

Shows the round briefly, then screen goes black and you have unlimited time to make your guess.

  1. // ==UserScript==
  2. // @name Geoguessr Blink Mode
  3. // @description Shows the round briefly, then screen goes black and you have unlimited time to make your guess.
  4. // @version 1.3.4
  5. // @author macca7224
  6. // @license MIT
  7. // @match https://www.geoguessr.com/*
  8. // @require https://unpkg.com/@popperjs/core@2.11.5/dist/umd/popper.min.js
  9. // @grant none
  10. // @require https://gf.qytechs.cn/scripts/460322-geoguessr-styles-scan/code/Geoguessr%20Styles%20Scan.js?version=1151654
  11. // @namespace https://gf.qytechs.cn/en/scripts/438579-geoguessr-blink-mode
  12. // @icon https://www.svgrepo.com/show/40039/eye.svg
  13. // ==/UserScript==
  14.  
  15. const guiEnabled = true
  16. // ^^^^ Set to false (all lowercase) if you want to hide the GUI and manually enable the script/set the time, otherwise true
  17.  
  18. let timeLimit = 1.5
  19. // ^^^ Modify this number above to change the time
  20.  
  21. let roundDelay = 0
  22. // ^ Modify this number above to change the length of time the round is delayed for
  23.  
  24.  
  25.  
  26. // --------- DON'T MODIFY ANYTHING BELOW THIS LINE -------- //
  27.  
  28.  
  29. const styleElement = document.createElement("style");
  30. document.head.appendChild(styleElement);
  31. styleElement.innerHTML = `
  32. .toggle_toggle__ {
  33. -webkit-appearance: none;
  34. -moz-appearance: none;
  35. appearance: none;
  36. background: var(--ds-color-white-10);
  37. border: 0;
  38. border-radius: 2rem;
  39. cursor: pointer;
  40. display: inline-block;
  41. height: 1.25rem;
  42. position: relative;
  43. transition: background-color .2s ease;
  44. width: 2.5rem
  45. }
  46.  
  47. .toggle_toggle__:checked {
  48. background: var(--ds-color-purple-50)
  49. }
  50.  
  51. .toggle_toggle__:after {
  52. background: var(--ds-color-white);
  53. border-radius: 100%;
  54. content: "";
  55. height: 1rem;
  56. left: 0;
  57. margin: .125rem;
  58. opacity: .1;
  59. position: absolute;
  60. top: 0;
  61. transition: transform .1s ease,opacity .1s ease;
  62. width: 1rem
  63. }
  64.  
  65. .toggle_toggle__:checked:after {
  66. opacity: 1;
  67. transform: translateX(125%)
  68. }
  69.  
  70. .toggle_toggle__:before {
  71. background: var(--ds-color-purple-50);
  72. content: "";
  73. height: 100%;
  74. left: 0;
  75. opacity: 0;
  76. position: absolute;
  77. top: 0;
  78. transition: opacity .3s ease;
  79. width: 100%
  80. }`;
  81.  
  82. const classicGameGuiClasses = ["map-stats_mapStat__", "map-selector_selector__"];
  83. const classicGameGuiHTML = () => `
  84. <div style="width: 30rem; max-width: 80vw; text-align: center; margin: 0 auto;">
  85. <h2 style="margin-bottom: 20px; font-style: italic;">Blink Mode settings</h2>
  86. <div>
  87. <div style="display: flex; justify-content: space-around;">
  88. <div style="display: flex; align-items: center;">
  89. <span style="margin: 0; padding-right: 6px;">Enabled</span>
  90. <input type="checkbox" id="enableScript" onclick="toggleBlinkMode(this)" class="toggle_toggle__">
  91. </div>
  92.  
  93. <div style="display: flex; align-items: center;">
  94. <span style="margin: 0; padding-right: 6px;">Time (Seconds)</span>
  95. <input type="text" id="blinkTime" onchange="changeBlinkTime(this)" style="background: rgba(255,255,255,0.1); color: white; border: none; border-radius: 5px; width: 60px;">
  96. </div>
  97. </div>
  98. <div style="margin-top: 10px">
  99. <span style="margin: 0; padding-right: 6px;">Round Delay (Seconds)</span>
  100. <input type="text" id="delayTime" onchange="changeDelayTime(this)" style="background: rgba(255,255,255,0.1); color: white; border: none; border-radius: 5px; width: 60px;">
  101. </div>
  102. </div>
  103. </div>
  104. `
  105.  
  106. const friendLobbyGuiClasses = ["section_sectionHeader__", "bars_root__", "toggle-option_wrapper__"];
  107. const friendLobbyGuiHTML = () => `
  108. <div class="${cn("settings-modal_settingsSection__")}">
  109. <div class="${cn("section_sectionHeader__")} ${cn("section_sizeMedium__")} ${cn("section_variantLight__")}">
  110. <div class="${cn("bars_root__")}">
  111. <span class="${cn("bars_content__")}"><h2>Blink Mode Settings</h2></span>
  112. <div class="${cn("bars_after__")}"></div>
  113. </div>
  114. </div>
  115. <div class="${cn("settings-modal_section__")}" style="margin-top: 8px">
  116. <div class="${cn("toggle-option_wrapper__")}">
  117. <div class="${cn("toggle-option_label__")}">Enabled</div>
  118. <input type="checkbox" id="enableScript" onclick="toggleBlinkMode(this)" class="toggle_toggle__">
  119. </div>
  120.  
  121. <div class="${cn("numeric-option_wrapper__")}">
  122. <div class="${cn("numeric-option_label__")}">Time (Seconds)</div>
  123. <input type="text" id="blinkTime" onchange="changeBlinkTime(this)" style="background: rgba(255,255,255,0.1); color: white; border: none; border-radius: 5px; width: 60px;">
  124. </div>
  125.  
  126. <div class="${cn("numeric-option_wrapper__")}">
  127. <div class="${cn("numeric-option_label__")}">Round Delay (Seconds)</div>
  128. <input type="text" id="delayTime" onchange="changeDelayTime(this)" style="background: rgba(255,255,255,0.1); color: white; border: none; border-radius: 5px; width: 60px;">
  129. </div>
  130. </div>
  131. </div>
  132. `
  133.  
  134. const guiHeaderClasses = ["menu-item_container__", "quick-search_wrapper__"];
  135. const guiHTMLHeader = () => `
  136. <div id="blinkHeaderToggle" class="${cn("menu-item_container__")}">
  137. <div class="${cn("quick-search_wrapper__")}">
  138. <div class="${cn("slanted-wrapper_root__")} ${cn("slanted-wrapper_variantGrayTransparent__")}">
  139. <div class="${cn("quick-search_searchInputWrapper__")}">
  140. <div id="popup" style="background: rgba(26, 26, 46, 0.9); padding: 15px; width: 200px; border-radius: 10px;">
  141. <div style="display: flex; justify-content: space-between; align-items: center;">
  142. <span>Enabled</span>
  143. <input type="checkbox" id="enableScriptHeader" class="toggle_toggle__" onclick="toggleBlinkMode(this)">
  144. </div>
  145.  
  146. <div style="display: flex; justify-content: space-between; align-items: center; margin-top: 10px;">
  147. <span>Time (Seconds)</span>
  148. <input type="text" id="blinkTimeHeader" onchange="changeBlinkTime(this)" style="background: rgba(255,255,255,0.1); color: white; border: none; border-radius: 5px; width: 60px;">
  149. </div>
  150.  
  151. <div style="display: flex; justify-content: space-between; align-items: center; margin-top: 10px;">
  152. <span style="margin: 0; padding-right: 6px;">Round Delay (Seconds)</span>
  153. <input type="text" id="delayTimeHeader" onchange="changeDelayTime(this)" style="background: rgba(255,255,255,0.1); color: white; border: none; border-radius: 5px; width: 60px;">
  154. </div>
  155. </div>
  156. <button style="width: 59.19px" id="headerGuiToggle" class="${cn("quick-search_searchInputButton__")}"><picture style="justify-content: center" class="${cn("quick-search_iconSection__")}"><img src="https://www.svgrepo.com/show/40039/eye.svg" style="width: 15px; filter: brightness(0) invert(1); opacity: 100%;"></picture></button>
  157. </div>
  158. </div>
  159. </div>
  160. </div>
  161. `
  162.  
  163. const guiPartyHeaderClasses = ["header_item__", "slanted-wrapper_root__", "game-options_optionLabel__"];
  164. const guiPartyHeader = () => `
  165. <div id="blinkHeaderToggle" class="${cn("header_item__")}" style="margin-right: 1rem;">
  166. <div id="popup" style="background: rgba(26, 26, 46, 0.9); padding: 15px; width: 200px; border-radius: 10px; z-index: 999;">
  167. <div style="display: flex; justify-content: space-between; align-items: center;">
  168. <span class="${cn("game-options_optionLabel__")}">Enabled</span>
  169. <input type="checkbox" id="enableScriptHeader" class="toggle_toggle__" onclick="toggleBlinkMode(this)">
  170. </div>
  171.  
  172. <div style="display: flex; justify-content: space-between; align-items: center; margin-top: 10px;">
  173. <span class="${cn("game-options_optionLabel__")}">Time (Seconds)</span>
  174. <input type="text" id="blinkTimeHeader" onchange="changeBlinkTime(this)" style="background: rgba(255,255,255,0.1); color: white; border: none; border-radius: 5px; width: 60px;">
  175. </div>
  176.  
  177. <div style="display: flex; justify-content: space-between; align-items: center; margin-top: 10px;">
  178. <span class="${cn("game-options_optionLabel__")}" style="margin: 0; padding-right: 6px;">Round Delay (Seconds)</span>
  179. <input type="text" id="delayTimeHeader" onchange="changeDelayTime(this)" style="background: rgba(255,255,255,0.1); color: white; border: none; border-radius: 5px; width: 60px;">
  180. </div>
  181. </div>
  182. <div class="${cn("quick-search_wrapper__")}">
  183. <div class="${cn("slanted-wrapper_root__")} ${cn("slanted-wrapper_variantGrayTransparent__")}">
  184. <div class="${cn("slanted-wrapper_start__")} ${cn("slanted-wrapper_right__")}"></div>
  185. <div>
  186. <button id="headerGuiToggle" style="width: 59.19px; background-color: inherit;border: initial;cursor: pointer;min-height: 2rem;min-width: 2rem;padding: var(--padding-y) var(--padding-x);"><picture style="justify-content: center" class="${cn("quick-search_iconSection__")}"><img src="https://www.svgrepo.com/show/40039/eye.svg" style="width: 15px; filter: brightness(0) invert(1); opacity: 60%;"></picture></button>
  187. </div>
  188. <div class="${cn("slanted-wrapper_end__")} ${cn("slanted-wrapper_right__")}"></div>
  189. </div>
  190. </div>
  191. </div>
  192. `
  193.  
  194. if (localStorage.getItem('blinkEnabled') == null) {
  195. localStorage.setItem('blinkEnabled', 'disabled');
  196. }
  197.  
  198. if (!guiEnabled) {
  199. localStorage.setItem('blinkEnabled', 'enabled');
  200. }
  201.  
  202. if (localStorage.getItem('blinkTime') == null || isNaN(localStorage.getItem('blinkTime'))) {
  203. localStorage.setItem('blinkTime', timeLimit);
  204. }
  205. if (localStorage.getItem('delayTime') == null || isNaN(localStorage.getItem('delayTime'))) {
  206. localStorage.setItem('delayTime', roundDelay);
  207. }
  208.  
  209. if (guiEnabled) {
  210. timeLimit = parseFloat(localStorage.getItem('blinkTime'));
  211. roundDelay = parseFloat(localStorage.getItem('delayTime'));
  212. }
  213.  
  214. window.toggleBlinkMode = (e) => {
  215. localStorage.setItem('blinkEnabled', e.checked ? 'enabled' : 'disabled');
  216. if (!e.checked) {
  217. try { showPanoramaCached(); } catch {}
  218. }
  219.  
  220. if (document.querySelector('#enableScript')) {
  221. document.querySelector('#enableScript').checked = e.checked;
  222. }
  223. if (document.querySelector('#enableScriptHeader')) {
  224. document.querySelector('#enableScriptHeader').checked = e.checked;
  225. }
  226. }
  227.  
  228. window.changeBlinkTime = (e) => {
  229. if (!isNaN(e.value)) {
  230. localStorage.setItem('blinkTime', parseFloat(e.value));
  231. timeLimit = parseFloat(e.value);
  232.  
  233. if (document.querySelector('#blinkTime')) {
  234. document.querySelector('#blinkTime').value = e.value;
  235. }
  236. if (document.querySelector('#blinkTimeHeader')) {
  237. document.querySelector('#blinkTimeHeader').value = e.value;
  238. }
  239. }
  240. }
  241.  
  242. window.changeDelayTime = (e) => {
  243. if (!isNaN(e.value)) {
  244. localStorage.setItem('delayTime', parseFloat(e.value));
  245. roundDelay = parseFloat(e.value);
  246.  
  247. if (document.querySelector('#delayTime')) {
  248. document.querySelector('#delayTime').value = e.value;
  249. }
  250. if (document.querySelector('#delayTimeHeader')) {
  251. document.querySelector('#delayTimeHeader').value = e.value;
  252. }
  253. }
  254. }
  255.  
  256. const insertHeaderGui = (header, gui) => {
  257. header.insertAdjacentHTML('afterbegin', gui);
  258. const showButton = document.querySelector('#headerGuiToggle');
  259. const popup = document.querySelector('#popup');
  260. popup.style.display = 'none';
  261.  
  262. document.addEventListener('click', (e) => {
  263. const target = e.target;
  264. if (target == popup || popup.contains(target)) return;
  265. if (target.matches('#headerGuiToggle, #headerGuiToggle *')) {
  266. e.preventDefault();
  267.  
  268. popup.style.display = 'block';
  269. Popper.createPopper(showButton, popup, {
  270. placement: 'bottom',
  271. modifiers: [
  272. {
  273. name: 'offset',
  274. options: {
  275. offset: [0, 10],
  276. },
  277. },
  278. ],
  279. });
  280. } else {
  281. popup.style.display = 'none';
  282. }
  283.  
  284. if (document.querySelector('#enableScriptHeader')) {
  285. if (localStorage.getItem('blinkEnabled') === 'enabled') {
  286. document.querySelector('#enableScriptHeader').checked = true;
  287. }
  288. document.querySelector('#blinkTimeHeader').value = timeLimit;
  289. document.querySelector('#delayTimeHeader').value = roundDelay;
  290. }
  291. });
  292. }
  293.  
  294. const checkInsertGui = () => {
  295. // Play page for classic games
  296. if (document.querySelector('[class*=map-block_mapStatsContainer__]') && document.querySelector('#enableScript') === null && checkAllStylesFound(classicGameGuiClasses)) {
  297. document.querySelector('[class*=map-block_mapStatsContainer__]').insertAdjacentHTML('afterend', classicGameGuiHTML());
  298. if (localStorage.getItem('blinkEnabled') === 'enabled') {
  299. document.querySelector('#enableScript').checked = true;
  300. }
  301. document.querySelector('#blinkTime').value = timeLimit;
  302. document.querySelector('#delayTime').value = roundDelay;
  303. }
  304.  
  305. // Lobby for friends party games
  306. if (document.querySelector('[class*=party-modal_heading__]') && document.querySelector('#enableScript') === null && checkAllStylesFound(friendLobbyGuiClasses)) {
  307. const columns = document.querySelectorAll('[class*=settings-modal_column__]');
  308. columns[columns.length - 1].insertAdjacentHTML('beforeend', friendLobbyGuiHTML());
  309. if (localStorage.getItem('blinkEnabled') === 'enabled') {
  310. document.querySelector('#enableScript').checked = true;
  311. }
  312. document.querySelector('#blinkTime').value = timeLimit;
  313. document.querySelector('#delayTime').value = roundDelay;
  314. }
  315.  
  316. // Header
  317. if (document.querySelector('[class*=header-tablet-desktop_desktopSectionRight__]') && document.querySelector('#blinkHeaderToggle') === null && checkAllStylesFound(guiHeaderClasses)) {
  318. insertHeaderGui(document.querySelector('[class*=header-tablet-desktop_desktopSectionRight__]'), guiHTMLHeader())
  319. } else if (document.querySelector('[class*=party-header_root__]') && document.querySelector('#blinkHeaderToggle') === null && checkAllStylesFound(guiPartyHeaderClasses)) {
  320. insertHeaderGui(document.querySelector('[class*=party-header_right__]'), guiPartyHeader())
  321. } else if (document.querySelector('[class*=header-desktop_desktopSectionRight__]') && document.querySelector('#blinkHeaderToggle') === null && checkAllStylesFound(guiHeaderClasses)) {
  322. insertHeaderGui(document.querySelector('[class*=header-desktop_desktopSectionRight__]'), guiHTMLHeader())
  323. }
  324. }
  325.  
  326. let mapRoot = null;
  327.  
  328. function getMapRoot() {
  329. return document.querySelector("[data-qa=panorama]");
  330. }
  331.  
  332. function hidePanorama() {
  333. mapRoot = getMapRoot() || mapRoot;
  334. hidePanoramaCached();
  335. }
  336.  
  337. function hidePanoramaCached() {
  338. mapRoot.style.filter = 'brightness(0%)';
  339. }
  340.  
  341. function showPanorama() {
  342. mapRoot = getMapRoot() || mapRoot;
  343. showPanoramaCached();
  344. }
  345.  
  346. function showPanoramaCached() {
  347. mapRoot.style.filter = 'brightness(100%)';
  348. }
  349.  
  350. function isLoading() {
  351. return document.querySelector('[class*=fullscreen-spinner_root__]') || !document.querySelector('.widget-scene-canvas');
  352. }
  353.  
  354. let wasBackdropThereOrLoading = false;
  355. function isBackdropThereOrLoading() {
  356. return isLoading() // loading
  357. || document.querySelector('[class*=result-layout_root__]') // classic
  358. || document.querySelector('[class*=overlay_backdrop__]') // duels / team duels
  359. || document.querySelector('[class*=round-starting_wrapper____]') // live challenges
  360. || document.querySelector('[class*=popup_backdrop__]') // BR
  361. || document.querySelector('[class*=game-starting_container__]') || document.querySelector('[class*=round-score_container__]') // bullseye
  362. || document.querySelector('[class*=overlay-modal_backlight__]'); // city streaks
  363. }
  364.  
  365. let showTimeoutID = null
  366. let hideTimeoutID = null
  367. function triggerBlink() {
  368. hidePanorama();
  369. clearTimeout(showTimeoutID);
  370. showTimeoutID = setTimeout(showPanorama, roundDelay * 1000);
  371. clearTimeout(hideTimeoutID);
  372. hideTimeoutID = setTimeout(hidePanorama, (timeLimit + roundDelay) * 1000);
  373. }
  374.  
  375. let played = false;
  376.  
  377. const checkStatus = () => {
  378. if (!(location.pathname.includes('/duels'))) {
  379. return;
  380. }
  381. let timer = document.querySelector("[class^='clock-timer_timerContainer__']");
  382. let stress = document.querySelector("[class^='stress-indicator_container__']");
  383. if ((timer !== null) && !played && (stress == null)) {
  384. showPanorama();
  385. played = true;
  386. } else if (timer == null) {
  387. played = false;
  388. }
  389. }
  390.  
  391. let observer = new MutationObserver((mutations) => {
  392. if (guiEnabled) {
  393. scanStyles().then(_ => { checkInsertGui(); });
  394. }
  395.  
  396. if (localStorage.getItem('blinkEnabled') === 'enabled') {
  397. if (isBackdropThereOrLoading()) {
  398. wasBackdropThereOrLoading = true;
  399. if (!isLoading()) hidePanorama();
  400. } else if (wasBackdropThereOrLoading) {
  401. wasBackdropThereOrLoading = false;
  402. triggerBlink();
  403. }
  404. checkStatus();
  405. }
  406.  
  407. });
  408.  
  409. observer.observe(document.body, {
  410. characterDataOldValue: false,
  411. subtree: true,
  412. childList: true,
  413. characterData: false
  414. });

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址