GeoGuessr Retry Hotkey

Quickly resets the game by navigating to the last visited map and starting a new game.

当前为 2025-01-26 提交的版本,查看 最新版本

// ==UserScript==
// @name         GeoGuessr Retry Hotkey
// @namespace    https://www.geoguessr.com/
// @version      1.3
// @description  Quickly resets the game by navigating to the last visited map and starting a new game.
// @author       Shukaaa (mr aduchi)
// @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";

    // Retry Hotkey
    let RESET_KEY = localStorage.getItem(RESET_KEY_STORAGE) || "p";

    // 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;

        // Open Change Key Dialog when CTRL + ALT + KEY is pressed
        if (event.ctrlKey && event.altKey && event.key === RESET_KEY) {
            openChangeKeyDialog();
            return;
        }

        // 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");
        }
    };

    // Open dialog to change hotkey
    const openChangeKeyDialog = () => {
        const dialog = document.createElement("div");
        dialog.style.position = "fixed";
        dialog.style.top = "50%";
        dialog.style.left = "50%";
        dialog.style.transform = "translate(-50%, -50%)";
        dialog.style.padding = "20px";
        dialog.style.boxShadow = "inset 0 0.0625rem 0 hsla(0,0%,100%,.15),inset 0 -0.0625rem 0 rgba(0,0,0,.25)";
        dialog.style.background = "linear-gradient(180deg,rgba(161,155,217,.6) 0%,rgba(161,155,217,0) 50%,rgba(161,155,217,0) 50%),var(--ds-color-purple-80)";
        dialog.style.color = "var(--ds-color-white)";
        dialog.style.fontSize = "var(--font-size-18)";
        dialog.style.fontWeight = "700";
        dialog.style.fontStyle = "italic";
        dialog.style.zIndex = "10000";
        dialog.style.borderRadius = "0.1875rem";
        dialog.innerHTML = `
            <p>Press a new key to set as the hotkey... (Press DEL to abort)</p>
        `;

        document.body.appendChild(dialog);

        // Handle key press
        const handleNewKey = (event) => {
            if (event.key !== "Delete") {
                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()}`);
            }

            document.body.removeChild(dialog);
            document.removeEventListener("keydown", handleNewKey);
        };

        document.addEventListener("keydown", handleNewKey);
    };

    // 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();
                    }, 50);
                } 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 });
    };

    // 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 URL changes
    };

    // Run the script
    initialize();
})();