您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Displays GeoGuessr location in a dragable infobox. Press '1' to toggle the info, '2' to switch views. For educational use.
// ==UserScript== // @name Geoguessr Location Finder // @namespace http://tampermonkey.net/ // @version 2.11 // @description Displays GeoGuessr location in a dragable infobox. Press '1' to toggle the info, '2' to switch views. For educational use. // @author WannabeLynx // @match https://www.geoguessr.com/* // @grant none // @run-at document-start // @license MIT // ==/UserScript== (function() { 'use strict'; const SHOW_ON_START = true; let currentCoordinates = { lat: null, lng: null }; let countryInfo = { name: null, code: null }; let displayMode = 'coords'; const style = ` #location-finder-container { position: fixed; top: 10px; left: 10px; background-color: rgba(0, 0, 0, 0.85); color: white; padding: 15px; border-radius: 10px; z-index: 9999; font-family: 'Inter', sans-serif; font-size: 14px; display: none; /* Initially hidden */ cursor: move; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5); border: 1px solid #444; min-width: 250px; } #location-finder-container h3 { margin: 0 0 10px 0; font-size: 16px; text-align: center; border-bottom: 1px solid #555; padding-bottom: 8px; } #location-finder-container p { margin: 8px 0; min-height: 22px; /* Prevent layout shift while loading country */ } #location-finder-container img { height: 20px; border: 1px solid #555; border-radius: 3px; } #location-finder-container a { color: #4da6ff; text-decoration: none; display: block; text-align: center; margin-top: 10px; background-color: #333; padding: 8px 12px; border-radius: 5px; transition: background-color 0.3s; } #location-finder-container a:hover { background-color: #555; } `; let infoContainer; function createUI() { if (document.getElementById('location-finder-container')) return; const styleSheet = document.createElement("style"); styleSheet.innerText = style; document.head.appendChild(styleSheet); infoContainer = document.createElement('div'); infoContainer.id = 'location-finder-container'; infoContainer.innerHTML = '<h3>Location Finder</h3><p>Waiting for a new round...</p>'; document.body.appendChild(infoContainer); makeDraggable(infoContainer); } async function fetchCountryInfo(lat, lng) { if (!lat || !lng) return; try { const response = await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&accept-language=en`); const data = await response.json(); if (data && data.address) { countryInfo = { name: data.address.country, code: data.address.country_code }; console.log('Location Finder: Found country info', countryInfo); if (displayMode === 'country') { updateInfoBox(); } } else { throw new Error("No address data in response."); } } catch (error) { console.error('Location Finder: Error fetching country info:', error); countryInfo = { name: 'Could not retrieve country', code: null }; } } function updateInfoBox() { if (!infoContainer || !currentCoordinates.lat) return; const { lat, lng } = currentCoordinates; const mapsLink = `https://www.google.com/maps/@${lat},${lng},4z?entry=ttu`; let contentHTML = ''; if (displayMode === 'coords') { contentHTML = ` <p><strong>Latitude:</strong> ${lat.toFixed(6)}</p> <p><strong>Longitude:</strong> ${lng.toFixed(6)}</p> `; } else { if (countryInfo.name && countryInfo.code) { const flagUrl = `https://flagcdn.com/w40/${countryInfo.code}.png`; contentHTML = ` <p style="display: flex; align-items: center; gap: 10px;"> <img src="${flagUrl}" alt="${countryInfo.name} Flag"> <strong>${countryInfo.name}</strong> </p> `; } else { contentHTML = `<p>${countryInfo.name || 'Loading country...'}</p>`; } } infoContainer.innerHTML = ` <h3>Location Finder</h3> ${contentHTML} <a href="${mapsLink}" target="_blank">Open in Google Maps</a> `; } function handleCompetitiveMode() { if (!infoContainer) createUI(); infoContainer.innerHTML = ` <h3>Location Finder</h3> <p style="color: #ff6b6b; font-weight: bold;">Competitive mode detected!</p> <p>Script is disabled to prevent cheating. Play fair.</p> `; infoContainer.style.display = 'block'; } function isCompetitiveMode() { const url = window.location.href; const competitivePaths = ['/duels', '/battle-royale', '/competitive', '/multiplayer']; return competitivePaths.some(path => url.includes(path)); } function toggleInfoBox() { if (!infoContainer) return; infoContainer.style.display = (infoContainer.style.display === 'none') ? 'block' : 'none'; } function makeDraggable(element) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; element.onmousedown = dragMouseDown; function dragMouseDown(e) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; element.style.top = (element.offsetTop - pos2) + "px"; element.style.left = (element.offsetLeft - pos1) + "px"; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; } } function parseGameData(data) { if (!data) return null; let roundData = null; if (data.rounds && (data.round || data.roundNumber)) { roundData = data.rounds[(data.round || data.roundNumber) - 1]; } else if (data.player && data.player.currentRound) { roundData = data.player.currentRound; } if (roundData) { if (roundData.lat && roundData.lng) return { lat: roundData.lat, lng: roundData.lng }; if (roundData.panorama?.lat && roundData.panorama?.lng) return { lat: roundData.panorama.lat, lng: roundData.panorama.lng }; } return null; } function setupKeyListeners() { document.addEventListener('keydown', (e) => { if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return; if (e.key === '1') { e.stopImmediatePropagation(); toggleInfoBox(); } else if (e.key === '2') { if (currentCoordinates.lat !== null && !isCompetitiveMode()) { e.stopImmediatePropagation(); displayMode = displayMode === 'coords' ? 'country' : 'coords'; updateInfoBox(); } } }, true); } function interceptFetch() { const originalFetch = window.fetch; window.fetch = function(input, init) { const url = typeof input === 'string' ? input : input.url; if (typeof url === 'string' && url.includes('/api/v3/games/')) { return originalFetch.apply(this, arguments).then(response => { const clonedResponse = response.clone(); clonedResponse.json().then(data => { if (isCompetitiveMode()) { handleCompetitiveMode(); return; } const coords = parseGameData(data); if (coords && (coords.lat !== currentCoordinates.lat || coords.lng !== currentCoordinates.lng)) { console.log('Location Finder (fetch): Found new coordinates', coords); currentCoordinates = coords; countryInfo = { name: null, code: null }; displayMode = 'coords'; updateInfoBox(); fetchCountryInfo(coords.lat, coords.lng); if (SHOW_ON_START) { infoContainer.style.display = 'block'; } } }).catch(err => { // ignore }); return response; }); } return originalFetch.apply(this, arguments); }; } if (document.readyState === 'loading') { window.addEventListener('DOMContentLoaded', createUI); } else { createUI(); } setupKeyListeners(); interceptFetch(); })();