您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
counts 5ks in singleplayer games
// ==UserScript== // @name GeoGuessr 5K Counter // @namespace https://greasyfork.org/en/users/1501889 // @version 1.4 // @description counts 5ks in singleplayer games // @author Clemens // @match https://www.geoguessr.com/* // @icon https://www.google.com/s2/favicons?domain=geoguessr.com // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_getResourceURL // @resource fiveKImage https://cdn.7tv.app/emote/01JWCSDV98Q7BJDT737J3B8JEN/4x.avif // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; const stats = { total: GM_getValue('geo5k_total', 0), minimized: GM_getValue('geo5k_minimized', false), width: GM_getValue('geo5k_width', 150), height: GM_getValue('geo5k_height', 80), trackedElements: new WeakSet() }; function saveStats() { GM_setValue('geo5k_total', stats.total); GM_setValue('geo5k_minimized', stats.minimized); GM_setValue('geo5k_width', stats.width); GM_setValue('geo5k_height', stats.height); } const fiveKImageUrl = GM_getResourceURL('fiveKImage'); GM_addStyle(` #geo5k-counter { position: fixed !important; left: 20px; top: 75vh; background: rgba(30, 30, 30, 0.9); color: #f0f0f0; padding: 8px 12px; border-radius: 8px; font-family: 'Segoe UI', system-ui, sans-serif; font-size: 14px; z-index: 99999 !important; border: 1px solid rgba(255, 255, 255, 0.1); min-width: 140px; min-height: 80px; width: ${stats.width}px; height: ${stats.height}px; cursor: move; user-select: none; backdrop-filter: blur(4px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); box-sizing: border-box; display: flex; flex-direction: column; justify-content: space-between; transition: all 0.2s ease-in-out; } #geo5k-counter.minimized { width: 16px !important; height: 16px !important; min-width: 16px !important; min-height: 16px !important; padding: 0 !important; background: #f00; border-radius: 50% !important; display: flex; align-items: center; justify-content: center; overflow: hidden; cursor: pointer; } #geo5k-counter.minimized > * { display: none !important; } #geo5k-top-bar { display: flex; justify-content: space-between; align-items: center; } #geo5k-minimize-btn { width: 16px; height: 16px; background: #f00; border-radius: 50%; cursor: pointer; z-index: 1; } #geo5k-reset-btn { background: #f00; color: #fff; border: none; border-radius: 50%; width: 16px; height: 16px; padding: 0; font-size: 0; cursor: pointer; transition: all 0.2s ease; position: relative; } #geo5k-reset-btn::after { content: "R"; font-size: 10px; font-weight: bold; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } #geo5k-reset-btn:hover { background: #ff6b6b; } #geo5k-content { display: flex; align-items: flex-start; justify-content: center; gap: 12px; margin-top: 4px; } #geo5k-total { font-weight: 500; color: #ffffff; font-size: 36px; line-height: 48px; } .five-k-image { height: 48px; width: 48px; object-fit: contain; } #geo5k-resize-handle { position: absolute; right: 0; bottom: 0; width: 12px; height: 12px; cursor: nwse-resize; background: linear-gradient(135deg, transparent 50%, rgba(255,255,255,0.2) 50%); } .geo5k-highlight { animation: geo5k-pulse 0.5s; } @keyframes geo5k-pulse { 0% { transform: scale(1); } 50% { transform: scale(1.1); } 100% { transform: scale(1); } } `); function createDisplay() { let display = document.getElementById('geo5k-counter'); if (!display) { display = document.createElement('div'); display.id = 'geo5k-counter'; if (stats.minimized) display.classList.add('minimized'); display.innerHTML = ` <div id="geo5k-top-bar"> <span id="geo5k-minimize-btn"></span> <button id="geo5k-reset-btn" title="Reset Total Count"></button> </div> <div id="geo5k-content"> <img src="${fiveKImageUrl}" class="five-k-image" alt="5K"> <span id="geo5k-total">${stats.total}</span> </div> <div id="geo5k-resize-handle"></div> `; document.body.appendChild(display); document.getElementById('geo5k-reset-btn').addEventListener('click', function(e) { e.stopPropagation(); if (confirm('Reset total 5K count to zero?')) { stats.total = 0; stats.trackedElements = new WeakSet(); saveStats(); updateDisplay(); } }); document.getElementById('geo5k-minimize-btn').addEventListener('click', function(e) { e.stopPropagation(); toggleMinimize(); }); display.addEventListener('click', function(e) { if (stats.minimized) { toggleMinimize(); } }); setupDragging(display); setupResize(display); } return display; } function toggleMinimize() { stats.minimized = !stats.minimized; saveStats(); const display = document.getElementById('geo5k-counter'); if (display) { display.classList.toggle('minimized', stats.minimized); } } function setupDragging(display) { let isDragging = false; let startX, startY, initialX, initialY; const dragElements = [display, document.getElementById('geo5k-minimize-btn')]; dragElements.forEach(element => { if (!element) return; element.addEventListener('mousedown', (e) => { if (e.button !== 0 || e.target.id === 'geo5k-resize-handle') return; isDragging = true; startX = e.clientX; startY = e.clientY; initialX = display.offsetLeft; initialY = display.offsetTop; display.style.transition = 'none'; e.preventDefault(); }); }); function moveHandler(e) { if (!isDragging) return; const dx = e.clientX - startX; const dy = e.clientY - startY; const newX = Math.max(0, Math.min(initialX + dx, window.innerWidth - display.offsetWidth)); const newY = Math.max(0, Math.min(initialY + dy, window.innerHeight - display.offsetHeight)); display.style.left = `${newX}px`; display.style.top = `${newY}px`; } function upHandler() { if (!isDragging) return; isDragging = false; display.style.transition = ''; } document.addEventListener('mousemove', moveHandler); document.addEventListener('mouseup', upHandler); } function setupResize(display) { const resizeHandle = document.getElementById('geo5k-resize-handle'); let isResizing = false; let startX, startY, startWidth, startHeight; let debounceTimer; resizeHandle.addEventListener('mousedown', function(e) { if (stats.minimized) return; isResizing = true; startX = e.clientX; startY = e.clientY; startWidth = parseInt(document.defaultView.getComputedStyle(display).width, 10); startHeight = parseInt(document.defaultView.getComputedStyle(display).height, 10); display.style.transition = 'none'; e.preventDefault(); e.stopPropagation(); }); function resizeMove(e) { if (!isResizing) return; const width = Math.max(140, startWidth + e.clientX - startX); const height = Math.max(80, startHeight + e.clientY - startY); clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { display.style.width = `${width}px`; display.style.height = `${height}px`; }, 1); } function resizeEnd() { if (!isResizing) return; isResizing = false; display.style.transition = ''; stats.width = parseInt(display.style.width); stats.height = parseInt(display.style.height); saveStats(); } document.addEventListener('mousemove', resizeMove); document.addEventListener('mouseup', resizeEnd); } function updateDisplay() { const totalEl = document.getElementById('geo5k-total'); if (totalEl) totalEl.textContent = stats.total; } function scanFor5K() { const scoreContainers = document.querySelectorAll('.round-result_pointsIndicatorWrapper__7JxD_'); for (const container of scoreContainers) { if (stats.trackedElements.has(container)) continue; const scoreElement = container.querySelector('.shadow-text_root__KeAY1 div div'); if (!scoreElement) continue; const scoreText = scoreElement.textContent.trim(); if (scoreText === '5,000' || scoreText === '5000') { stats.trackedElements.add(container); handle5KDetection(scoreElement); } } } function handle5KDetection(element) { const now = Date.now(); if (now - stats.lastDetection < 50) return; stats.lastDetection = now; stats.total++; saveStats(); updateDisplay(); element.classList.add('geo5k-highlight'); setTimeout(() => element.classList.remove('geo5k-highlight'), 500); } function init() { createDisplay(); updateDisplay(); const fastScanner = setInterval(scanFor5K, 3); const observer = new MutationObserver(scanFor5K); observer.observe(document.body, { childList: true, subtree: true }); window.addEventListener('beforeunload', () => { clearInterval(fastScanner); observer.disconnect(); }); } if (document.readyState === 'complete') { init(); } else { document.addEventListener('DOMContentLoaded', init); } })();