您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Quickly resets the game by navigating to the last visited map and starting a new game.
// ==UserScript== // @name GeoGuessr Retry Hotkey // @namespace https://www.geoguessr.com/ // @version 1.4 // @description Quickly resets the game by navigating to the last visited map and starting a new game. // @author Shukaaa (aduchi nom) // @match https://www.geoguessr.com/* // @grant GM_setValue // @grant GM_getValue // @grant window.focus // @license MIT // ==/UserScript== (function () { 'use strict'; // Constants for script metadata, storage keys const SCRIPT_NAME = "GeoGuessr Retry Hotkey"; const LAST_VISITED_MAP_KEY = "lastVisitedMap"; const PLAY_TRIGGERED_KEY = "playTriggered"; const RESET_KEY_STORAGE = "resetKey"; const DELAY_STORAGE = "rh-delay"; // Retry Hotkey let RESET_KEY = localStorage.getItem(RESET_KEY_STORAGE) || "p"; let DELAY = localStorage.getItem(DELAY_STORAGE) || 50; // Helper: Logging function for consistent console outputs const log = (message, level = "log") => { const levels = { log: console.log, warn: console.warn }; const logFunction = levels[level] || console.log; logFunction(`[${SCRIPT_NAME}] ${message}`); }; // Helper: Save to GM storage const saveToStorage = (key, value) => GM_setValue(key, value); // Helper: Load from GM storage const loadFromStorage = (key, defaultValue = null) => GM_getValue(key, defaultValue); // Handle keydown for resetting the game const handleKeyDown = (event) => { const currentURL = window.location.href; // Trigger reset if (currentURL.includes("/game/") && event.key === RESET_KEY) { const lastVisitedMap = loadFromStorage(LAST_VISITED_MAP_KEY); if (lastVisitedMap) { log(`'${RESET_KEY.toUpperCase()}' key pressed. Navigating to: ${lastVisitedMap}`); saveToStorage(PLAY_TRIGGERED_KEY, true); // Set playTriggered to true window.location.href = lastVisitedMap; // Redirect to the map } else { log("No last map URL found. Reset aborted.", "warn"); } } if (!currentURL.includes("/game/")) { log("Reset aborted: Not on a game page.", "warn"); } }; // Automatically click the play button on the map page const attemptPlay = () => { const currentURL = window.location.href; const playTriggered = loadFromStorage(PLAY_TRIGGERED_KEY, false); if (currentURL.includes("/maps/") && playTriggered) { log("Map page detected with playTriggered=true. Attempting to start a new game..."); // Try finding the play button container const playButtonContainer = document.querySelector("div[class*='map-selector_playButtons']"); if (playButtonContainer) { const playButton = playButtonContainer.querySelector("button"); if (playButton) { saveToStorage(PLAY_TRIGGERED_KEY, false); playButton.focus(); setTimeout(() => { playButton.click(); }, DELAY); } else { log("'Play' button not found inside container.", "warn"); } } else { log("Play button container not found.", "warn"); } } }; // Save the current map URL if on a map page const saveCurrentMap = () => { const currentURL = window.location.href; if (currentURL.includes("/maps/")) { log(`Saving current map URL: ${currentURL}`); saveToStorage(LAST_VISITED_MAP_KEY, currentURL); } }; // Check for URL Changes const observeUrlChanges = () => { let lastUrl = window.location.href; const observer = new MutationObserver(() => { const currentUrl = window.location.href; if (currentUrl !== lastUrl) { log(`URL changed: ${currentUrl}`); lastUrl = currentUrl; // Save the map URL if it's a map page saveCurrentMap(); } }); observer.observe(document.body, { childList: true, subtree: true }); }; const addConfigurationsToMenuOverlay = () => { const SETTINGS_INITIALIZED_ATTR = "data-retry-hotkey-settings-initialized"; // Utility functions for creating reusable components made by geoguessr const cloneComponent = (selector, modifier = (clone) => clone) => { const component = document.querySelector(selector); if (!component) return null; const clone = component.cloneNode(true); return modifier(clone); }; const createTitle = (title) => cloneComponent("div[class*='game-menu_headerContainer']", (clone) => { clone.childNodes[0].innerHTML = title; return clone; }); const createDivider = () => cloneComponent("div[class*='game-menu_divider']"); const createOptionContainer = (optionName) => cloneComponent("div[class*='game-menu_volumeContainer']", (clone) => { clone.removeChild(clone.lastChild); clone.childNodes[0].innerHTML = optionName; return clone; }); const createButton = (text, onClick) => { const button = document.createElement("button"); button.style.cursor = "pointer"; button.style.color = "#fff"; button.style.border = ".0625rem solid var(--ds-color-white-80)"; button.style.padding = "0.75rem 1.5rem"; button.style.borderRadius = "3.75rem"; button.style.marginTop = "1em"; button.innerHTML = text; if (onClick) button.onclick = onClick; return button; }; const createBlockquote = (text) => { const blockquote = document.createElement("blockquote"); blockquote.style.borderLeft = ".333rem solid #ccc"; blockquote.style.color = "#ccc"; blockquote.style.marginLeft = "0"; blockquote.style.paddingLeft = "0.5em"; blockquote.innerHTML = text return blockquote } const initializeSettingsMenu = () => { const settingsContainer = document.querySelector("div[class*='game-menu_settingsContainer']"); if (!settingsContainer || settingsContainer.hasAttribute(SETTINGS_INITIALIZED_ATTR)) return; settingsContainer.appendChild(createDivider()); settingsContainer.appendChild(createTitle("Retry Hotkey Settings")); const switchHotkeySetting = createOptionContainer("Hotkey"); const updateHotkeyButton = createButton("Set new hotkey (Current Hotkey: " + RESET_KEY.toUpperCase() + ")", () => { updateHotkeyButton.innerHTML = "Press a new key to set as the hotkey"; updateHotkeyButton.style.color = "#ccc"; updateHotkeyButton.disabled = true; const handleNewKey = (event) => { RESET_KEY = event.key; localStorage.setItem(RESET_KEY_STORAGE, RESET_KEY); log(`New hotkey set: ${RESET_KEY}`); alert(`New hotkey set to: ${RESET_KEY.toUpperCase()}`); updateHotkeyButton.disabled = false; updateHotkeyButton.style.color = "#fff"; updateHotkeyButton.innerHTML = "Set new hotkey (Current Hotkey: " + RESET_KEY.toUpperCase() + ")"; document.removeEventListener("keydown", handleNewKey); }; document.addEventListener("keydown", handleNewKey); }); switchHotkeySetting.appendChild(updateHotkeyButton); const changeDelaySetting = createOptionContainer("Delay"); const changeDelayInfoText = createBlockquote("When dealing with bad internet connection or bugs that the match won't automatically start, it can be helpful to increase the delay") const changeDelayButton = createButton("Change Delay (Current Delay: " + DELAY + "ms)", () => { const newDelay = prompt("Enter new delay in ms") if (isNaN(newDelay)) { alert("Input is not a number") return } DELAY = Number(newDelay) localStorage.setItem(DELAY_STORAGE, DELAY); log(`New delay set: ${DELAY}`); alert(`New delay set to: ${DELAY}`); changeDelayButton.innerHTML = "Change Delay (Current Delay: " + DELAY + "ms)" }); changeDelaySetting.appendChild(changeDelayInfoText); changeDelaySetting.appendChild(changeDelayButton); settingsContainer.appendChild(switchHotkeySetting); settingsContainer.appendChild(changeDelaySetting); settingsContainer.setAttribute(SETTINGS_INITIALIZED_ATTR, true); }; initializeSettingsMenu(); }; const observeSettingsView = () => { const observer = new MutationObserver(() => { const settingsView = document.querySelector("div[class*='game-menu_inGameMenuOverlay']"); if (settingsView) { log("Settings view loaded."); addConfigurationsToMenuOverlay() } }); observer.observe(document.body, { childList: true, subtree: true }); }; // Initialize script const initialize = () => { document.addEventListener("keydown", handleKeyDown); // Add keydown listener attemptPlay(); // Check if play needs to be triggered saveCurrentMap(); // Save the map URL if relevant observeUrlChanges(); // Start observing DOM and URL changes observeSettingsView(); // Start observing the menu overlay view to add own settings }; // Run the script initialize(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址