您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Activates a Focus Mode in GeoGuessr Duels which hides opponent names and Rank/Elo automatically. You can turn the mode on and off in the Duels lobby.
// ==UserScript== // @name GeoGuessr Focus Mode // @namespace http://tampermonkey.net/ // @version 1.0 // @description Activates a Focus Mode in GeoGuessr Duels which hides opponent names and Rank/Elo automatically. You can turn the mode on and off in the Duels lobby. // @author AaronThug // @license MIT // @icon https://www.geoguessr.com/favicon.ico // @match https://www.geoguessr.com/* // @grant none // ==/UserScript== (function() { 'use strict'; let focusModeEnabled = false; let MY_USERNAME = ''; function createFocusModeButton() { if (document.querySelector('#focus-mode-container')) return; if (window.location.pathname !== '/multiplayer') return; const gameModeContainer = document.querySelector('.player-section_gameModeContainer__KMM5F'); if (!gameModeContainer) return; const outerContainer = document.createElement('div'); outerContainer.id = 'focus-mode-container'; outerContainer.style.cssText = ` margin-bottom: 20px; display: flex; justify-content: center; `; const panel = document.createElement('div'); panel.style.cssText = ` background: linear-gradient(145deg, #2a2550 0%, #1f1a42 100%); border: 2px solid #3d3764; border-radius: 20px; padding: 16px 24px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.1); display: flex; align-items: center; gap: 16px; backdrop-filter: blur(10px); `; const label = document.createElement('span'); label.textContent = 'Focus Mode'; label.style.cssText = ` font-family: 'neo-sans', 'Helvetica Neue', Arial, sans-serif; font-size: 16px; font-weight: 600; color: #ffffff; user-select: none; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); `; const toggleContainer = document.createElement('div'); toggleContainer.style.cssText = ` width: 52px; height: 28px; background: linear-gradient(145deg, #1a1538 0%, #0f0f2a 100%); border: 1px solid #4a4a6a; border-radius: 14px; position: relative; cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(255, 255, 255, 0.1); `; const toggleKnob = document.createElement('div'); toggleKnob.style.cssText = ` width: 24px; height: 24px; background: linear-gradient(145deg, #f0f0f0 0%, #e0e0e0 100%); border: 1px solid #d0d0d0; border-radius: 50%; position: absolute; top: 2px; left: 2px; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2); `; function updateToggleAppearance() { if (focusModeEnabled) { toggleContainer.style.background = 'linear-gradient(145deg, #4CAF50 0%, #45a049 100%)'; toggleContainer.style.borderColor = '#388e3c'; toggleContainer.style.boxShadow = ` inset 0 1px 2px rgba(255, 255, 255, 0.2), 0 2px 8px rgba(76, 175, 80, 0.4)`; toggleKnob.style.transform = 'translateX(24px)'; toggleKnob.style.background = 'linear-gradient(145deg, #ffffff 0%, #f5f5f5 100%)'; toggleKnob.style.boxShadow = ` 0 3px 8px rgba(0, 0, 0, 0.4), 0 1px 3px rgba(0, 0, 0, 0.2)`; } else { toggleContainer.style.background = 'linear-gradient(145deg, #1a1538 0%, #0f0f2a 100%)'; toggleContainer.style.borderColor = '#4a4a6a'; toggleContainer.style.boxShadow = ` inset 0 2px 4px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(255, 255, 255, 0.1)`; toggleKnob.style.transform = 'translateX(0)'; toggleKnob.style.background = 'linear-gradient(145deg, #f0f0f0 0%, #e0e0e0 100%)'; toggleKnob.style.boxShadow = ` 0 2px 6px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2)`; } } toggleContainer.addEventListener('click', () => { focusModeEnabled = !focusModeEnabled; updateToggleAppearance(); localStorage.setItem('geoguessr-focus-mode', focusModeEnabled); if (focusModeEnabled) { setTimeout(() => { if (!MY_USERNAME) detectMyUsername(); censorOpponentNames(); }, 100); } }); updateToggleAppearance(); toggleContainer.appendChild(toggleKnob); panel.appendChild(label); panel.appendChild(toggleContainer); outerContainer.appendChild(panel); gameModeContainer.parentNode.insertBefore(outerContainer, gameModeContainer); } function detectMyUsername() { const duelsNickElement = document.querySelector('.user-nick_nick__sRjZ2.user-nick_visibleOverflow__oR46v'); if (duelsNickElement && duelsNickElement.textContent) { const username = duelsNickElement.textContent.replace(/\s+/g, ' ').trim(); if (username.length > 0 && username.length < 30) { MY_USERNAME = username; return true; } } try { const userData = localStorage.getItem('gg-user-data'); if (userData) { const user = JSON.parse(userData); if (user.nick || user.username || user.name) { MY_USERNAME = user.nick || user.username || user.name; return true; } } } catch (e) {} const profileSelectors = [ '[data-qa="navbar-profile-button"]', '.navbar-profile-button', '.profile-button' ]; for (const selector of profileSelectors) { const element = document.querySelector(selector); if (element && element.textContent.trim()) { const username = element.textContent.trim(); if (username.length > 0 && username.length < 30 && !username.includes('Profile')) { MY_USERNAME = username; return true; } } } return false; } function censorName(element) { if (!element || !element.textContent) return; const text = element.textContent.trim(); if (text === MY_USERNAME) return; if (text.length < 2 || /^\d+$/.test(text)) return; element.textContent = '?'; } function censorChatMessage(message) { if (window.location.pathname !== '/multiplayer') return; if (!message || !message.textContent || message.dataset.censored) return; const text = message.textContent.trim(); if (text.includes('has guessed') || text.includes('guessed')) { const username = text.split(/has guessed|guessed/)[0].trim(); if (username !== MY_USERNAME) { message.textContent = message.textContent.replace(username, '?'); message.dataset.censored = 'true'; } } } function censorOpponentNames() { if (window.location.pathname !== '/multiplayer') return; if (!focusModeEnabled) return; const matchmakingNames = document.querySelectorAll('.summon-glow-text_root__iYz_y'); matchmakingNames.forEach(element => { if (element.closest('.ranked-leaderboard_root__kpVHS')) return; const textNodes = Array.from(element.childNodes).filter(node => node.nodeType === Node.TEXT_NODE && node.textContent.trim() ); textNodes.forEach(textNode => { const text = textNode.textContent.trim(); if (text !== MY_USERNAME && text.length > 1) { textNode.textContent = '?'; const shadowElement = element.querySelector('.summon-glow-text_shadowPlaceholder___MC0p'); if (shadowElement) shadowElement.textContent = '?'; } }); }); const chatContainer = document.querySelector('.chat-log_scrollContainer__0fy56'); if (chatContainer) { const messages = Array.from(chatContainer.children); messages.forEach(message => { if (!message.dataset.censored) { censorChatMessage(message); } }); } const healthBarNames = document.querySelectorAll('.health-bar-2_nick__dWcMx'); healthBarNames.forEach(element => { if (element.closest('.ranked-leaderboard_root__kpVHS')) return; censorName(element); }); const additionalSelectors = [ '[class*="nick"]:not(.user-nick_visibleOverflow__oR46v)', '[class*="player-name"]', '[class*="opponent"]' ]; additionalSelectors.forEach(selector => { const elements = document.querySelectorAll(selector); elements.forEach(element => { if (element.closest('.side-tray_body__30bbe')) return; if (element.closest('.ranked-leaderboard_root__kpVHS')) return; if (!element.classList.contains('summon-glow-text_root__iYz_y') && !element.classList.contains('health-bar-2_nick__dWcMx') && !element.classList.contains('user-nick_visibleOverflow__oR46v')) { if (element.textContent && element.textContent.trim() && element.textContent.trim().length > 1) { censorName(element); } } }); }); } const observer = new MutationObserver(function(mutations) { let shouldUpdate = false; for (const mutation of mutations) { if ( window.location.pathname === '/multiplayer' && mutation.target.classList?.contains('chat-log_scrollContainer__0fy56') ) { for (const node of mutation.addedNodes) { if (node.nodeType === 1 && !node.dataset.censored) { censorChatMessage(node); } } continue; } if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { shouldUpdate = true; break; } } if (shouldUpdate && focusModeEnabled && window.location.pathname === '/multiplayer') { setTimeout(() => { if (window.location.pathname === '/multiplayer') { createFocusModeButton(); if (!MY_USERNAME) detectMyUsername(); censorOpponentNames(); } }, 200); } }); observer.observe(document.body, { childList: true, subtree: true }); const savedStatus = localStorage.getItem('geoguessr-focus-mode'); if (savedStatus === 'true') { focusModeEnabled = true; } setTimeout(() => { detectMyUsername(); if (window.location.pathname === '/multiplayer') { createFocusModeButton(); } censorOpponentNames(); }, 1000); let lastUrl = location.href; new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; setTimeout(() => { if (!MY_USERNAME) detectMyUsername(); if (window.location.pathname === '/multiplayer') { createFocusModeButton(); } censorOpponentNames(); }, 500); } }).observe(document, { subtree: true, childList: true }); })();