您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动记录国家/一级行政区连击次数
当前为
// ==UserScript== // @name 图寻连击计数器 // @namespace https://greasyfork.org/users/1179204 // @version 1.0.9 // @description 自动记录国家/一级行政区连击次数 // @author KaKa // @match *://tuxun.fun/* // @exclude *://tuxun.fun/replay-pano?* // @icon data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNDggNDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbD0iIzAwMDAwMCI+PGcgaWQ9IlNWR1JlcG9fYmdDYXJyaWVyIiBzdHJva2Utd2lkdGg9IjAiPjwvZz48ZyBpZD0iU1ZHUmVwb190cmFjZXJDYXJyaWVyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjwvZz48ZyBpZD0iU1ZHUmVwb19pY29uQ2FycmllciI+PHRpdGxlPjcwIEJhc2ljIGljb25zIGJ5IFhpY29ucy5jbzwvdGl0bGU+PHBhdGggZD0iTTI0LDEuMzJjLTkuOTIsMC0xOCw3LjgtMTgsMTcuMzhBMTYuODMsMTYuODMsMCwwLDAsOS41NywyOS4wOWwxMi44NCwxNi44YTIsMiwwLDAsMCwzLjE4LDBsMTIuODQtMTYuOEExNi44NCwxNi44NCwwLDAsMCw0MiwxOC43QzQyLDkuMTIsMzMuOTIsMS4zMiwyNCwxLjMyWiIgZmlsbD0iI2ZmOTQyNyI+PC9wYXRoPjxwYXRoIGQ9Ik0yNS4zNywxMi4xM2E3LDcsMCwxLDAsNS41LDUuNUE3LDcsMCwwLDAsMjUuMzcsMTIuMTNaIiBmaWxsPSIjZmZmZmZmIj48L3BhdGg+PC9nPjwvc3ZnPg== // @require https://cdn.jsdelivr.net/npm/sweetalert2@11 // @require https://unpkg.com/gcoord/dist/gcoord.global.prod.js // @copyright KaKa // @license BSD // ==/UserScript== (function() { const Language='zh' // ISO 639-1 语言代码 - https://baike.baidu.com/item/ISO%20639 let viewer,map,finalGuess,currentRound,gameState=false,roundPins={},gameMode,roundState,countsDiv,countsTitle,countsValue,mapsId,avgScore,avgValue_ let api_key=JSON.parse(localStorage.getItem('api_key')); let streakCounts=JSON.parse(localStorage.getItem('streakCounts')) let streakMode=JSON.parse(localStorage.getItem('streakMode')) if (!streakCounts){ streakCounts={} } if (!streakMode){ streakMode='country' } const CC_DICT = { AX: "FI", AS: "US", AI: "GB", AW: "NL", BM: "GB", BQ: "NL", BV: "NO", IO: "GB", KY: "UK", CX: "AU", CC: "AU", CK: "NZ", CW: "NL", FK: "AR", FO: "DK", GF: "FR", PF: "FR", TF: "FR", GI: "UK", GL: "DK", GP: "FR", GU: "US", GG: "GB", HM: "AU", HK: "CN", IM: "GB", JE: "GB", MO: "CN", MQ: "FR", YT: "FR", MS: "GB", AN: "NL", NC: "FR", NU: "NZ", NF: "AU", MP: "US", PS: "IL", PN: "GB", PR: "US", RE: "FR", BL: "FR", SH: "GB", MF: "FR", PM: "FR", SX: "NL", GS: "GB", SJ: "NO", TK: "NZ", TC: "GB", UM: "US", VG: "GB", VI: "US", WF: "FR", EH: "MA", TW: "CN" }; let intervalId=setInterval(function(){ const streetViewContainer= document.getElementById('viewer') if(streetViewContainer){ getSVContainer() getMap() if(map&&viewer&&viewer.location&&gameMode){ mapListener() clearInterval(intervalId)} } },500); function getMap(){ var mapContainer = document.getElementById('map') const keys = Object.keys(mapContainer) const key = keys.find(key => key.startsWith("__reactFiber$")) const props = mapContainer[key] const x = props.child.memoizedProps.value.map map=x.getMap() } function getSVContainer(){ const streetViewContainer= document.getElementById('viewer') const keys = Object.keys(streetViewContainer) const key = keys.find(key => key.startsWith("__reactFiber")) const props = streetViewContainer[key] viewer=props.return.child.memoizedProps.children[1].props.googleMapInstance const gameData=props.return.return.return.return.return.memoizedState.next.next.memoizedState.current.gameData if(gameData){ if(gameData.status&&gameData.status==='ongoing'){ gameState=roundState=true mapsId=gameData.mapsId if (['challenge','infinity'].includes(gameData.type)) gameMode=gameData.type if (!streakCounts[mapsId]){ streakCounts[mapsId]={'country':0,'state':0} } currentRound=gameData.rounds.length if(gameData.rounds[currentRound-1].endTime) currentRound+=1 } } } function mapListener(){ setMapObserver() setSVObserver() if (!roundPins[currentRound]){ getRoundPin() updatePanel(streakMode) } var mapContainer = document.querySelector('.maplibregl-canvas') const observer = new MutationObserver((mutationsList, observer) => { for(let mutation of mutationsList) { if (mutation.type === 'attributes' && mutation.attributeName === 'style') { handleSizeChange(mapContainer); } } }); observer.observe(mapContainer, { attributes: true, attributeFilter: ['style'] }); } function setMapObserver() { map.on('click', (e) => { if (gameState&&roundState) finalGuess=e.lngLat }); } function setSVObserver() { viewer.addListener('position_changed', () => { if (!roundPins[currentRound]&&gameState){ getRoundPin() } }); } async function getRoundPin(){ var lat,lng,add if(viewer.pano.length===27) { [lat,lng]=await checkPano(viewer.pano) if(api_key) add=await queryGD(lat,lng) else add=await queryOSM(lat,lng) } else{ lat=viewer.location.latLng.lat() lng=viewer.location.latLng.lng() add=await queryOSM(lat,lng)} roundPins[currentRound]=add } function handleSizeChange(target) { const { width, height } = target.getBoundingClientRect(); const currentScreenWidth = window.innerWidth; const widthRatio = (width / currentScreenWidth) * 100; if (widthRatio>=90) { streakCheck() roundState=false } else { roundState=true updatePanel() } } async function queryOSM(lat, lng) { const url =`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=jsonv2&accept-language=${Language}`; const response = await fetch(url); if (response.ok) { let data = await response.json(); if(data.address) return data.address } else { return null; } } async function streakCheck(){ if(!roundState) return if(finalGuess){ var guess if(viewer.pano.length===27&&api_key) { if(api_key) guess=await queryGD(finalGuess.lat,finalGuess.lng) else guess=await queryOSM(finalGuess.lat,finalGuess.lng) } else guess=await queryOSM(finalGuess.lat,finalGuess.lng) guess=correctState(guess) const answer=correctState(roundPins[currentRound]) var isStreak if(streakMode==='country'){ if(matchCountryCode(guess)===matchCountryCode(answer)){ isStreak=true } } else if(streakMode==='state'){ if(matchState(guess)===matchState(answer)){ isStreak=true } } if(guess) updateBar(isStreak,guess,answer,streakMode) else updateBar(false,'Undefined',answer,streakMode) currentRound+=1 } } function correctAddress(item){ if(['Taiwan','HongKong','Macau','臺灣','台湾'].includes(item)) return Language=== 'zh' ? '中国' : 'China' else if(['科索沃','Kosovo'].includes(item)) return Language=== 'zh' ? '塞尔维亚' : 'Serbia' else if(!item) return 'Undefined' else return item } function correctState(item){ try{ if(item.country==='India'&&item.state==='Arunachal Pradesh'){ item.country='China' item.state='Tibet'} else if(item.country==='印度'&&item.state==='阿鲁纳恰尔邦'){ item.country='中国' item.state='西藏自治区'} else if(item.country==='Japan'){ if(!item.province&&item.city) item.province='Tokyo'} else if(item.country.includes('日本')){ if(!item.province&&item.city) item.province='东京都'} } catch(error) { console.log('failed to correct') } if(item) return item } function updateBar(status,pin,result){ const roundBar=document.querySelector('.scoreReulstValue___gFyI2') if (roundBar)roundBar.textContent=roundBar.textContent.split('/')[0] const infoBar=document.querySelector('.controls___yY74y') const pText=infoBar.querySelector('p') if(pText) pText.style.display='none' const streakText = document.createElement('div') streakText.style.fontSize='24px' streakText.style.color='#fff' streakText.style.marginTop='15px' streakText.style.fontFamily='Baloo Bhaina' infoBar.appendChild(streakText) if (infoBar) { let message = ''; let answer = ''; let guess = ''; let streakMessage = ''; const correctTextColor = 'green'; const userTextColor = 'red'; const streakColor = 'yellow'; if (status) { streakCounts[mapsId][streakMode] += 1; if (streakMode === 'country') { answer = correctAddress(result.country).split('/')[0]; message = `恭喜你选对 <span style="color: ${correctTextColor};">${answer}</span> , 连击次数: <span style="color: ${streakColor};">${streakCounts[mapsId][streakMode]}</span>`; } else if (streakMode === 'state') { answer = matchState(result).split('/')[0]; message = `恭喜你选对 <span style="color: ${correctTextColor};">${answer}</span> , 连击次数: <span style="color: ${streakColor};">${streakCounts[mapsId][streakMode]}</span>`; } } else { const end_count = streakCounts[mapsId][streakMode]; streakCounts[mapsId][streakMode] = 0; if (streakMode === 'country') { answer = correctAddress(result.country).split('/')[0]; guess = correctAddress(pin.country).split('/')[0]; message = `答案是 <span style="color: ${correctTextColor};">${answer}</span> , 你选了 <span style="color: ${userTextColor};">${guess}</span> , 连击次数: <span style="color: ${streakColor};">${streakCounts[mapsId][streakMode]}</span> , 本轮达成连击: <span style="color: ${streakColor};">${end_count}</span>`; } else if (streakMode === 'state') { answer = matchState(result).split('/')[0]; guess = matchState(pin).split('/')[0]; message = `答案是 <span style="color: ${correctTextColor};">${answer}</span> , 你选了 <span style="color: ${userTextColor};">${guess}</span> , 连击次数: <span style="color: ${streakColor};">${streakCounts[mapsId][streakMode]}</span> , 本轮达成连击: <span style="color: ${streakColor};">${end_count}</span>`; } } streakText.innerHTML = message; localStorage.setItem('streakCounts', JSON.stringify(streakCounts)); } const scoreBar=document.querySelector('.scoreReulst___qqkPH') const avgDiv=document.createElement('div') const scoresDiv=document.querySelectorAll('.scoreReulstValue___gFyI2')[3] if(scoresDiv.textContent) var total_scores=parseInt(scoresDiv.textContent.replace(',', '')) const roundsDiv=document.querySelectorAll('.scoreReulstValue___gFyI2')[0] if(roundsDiv.textContent) var rounds=parseInt(roundsDiv.textContent.split('/')[0]) if(total_scores&&rounds) avgScore=total_scores/rounds if(avgScore) avgScore=formatNumber(parseInt(avgScore)) const avgTitle=document.createElement('div') avgTitle.className='scoreReulstLabel___pgClU' avgTitle.textContent='平均分' const avgValue=document.createElement('div') avgValue.className='scoreReulstValue___gFyI2' avgValue.textContent=avgScore avgDiv.appendChild(avgTitle) avgDiv.appendChild(avgValue) scoreBar.appendChild(avgDiv) } async function queryGD(lat, lng) { const apiUrl = `https://restapi.amap.com/v3/geocode/regeo?output=json&location=${lng},${lat}&key=${api_key}&radius=20`; try { const response = await fetch(apiUrl); if (!response.ok) { throw new Error('Request failed with status: ' + response.status); } const data = await response.json(); if (data.status === '1' && data.regeocode) { return data.regeocode.addressComponent; } else { localStorage.removeItem('api_key'); Swal.fire('无效的API密钥', '请刷新页面并重新输入正确的高德地图API密钥', 'error'); throw new Error('Request failed: ' + data.info); } } catch (error) { console.error('Error fetching address:', error); throw error; } } function checkPano(id) { return new Promise((resolve, reject) => { const url = `https://mapsv0.bdimg.com/?qt=sdata&sid=${id}`; fetch(url) .then(response => response.json()) .then(data => { try { if (data.result.error !== 404) { const [lng,lat] = gcoord.transform([data.content[0].X/100, data.content[0].Y /100],gcoord.BD09MC, gcoord.WGS84) resolve([lat,lng]) } else { resolve(false) } } catch (error) { resolve(false) } }) .catch(error => { console.error('Request failed:', error); reject(error); }); }); } function updatePanel(){ const panel_container=document.querySelector('.roundWrapper___eTnOj ') if(!countsDiv){ countsDiv=document.createElement('div') countsDiv.className='roundInfoBox___ikizG' countsTitle=document.createElement('div') countsTitle.className='roundInfoTitle___VOdv2' if(streakMode==='country') countsTitle.textContent='国家连击' else countsTitle.textContent='一级行政区连击' countsValue=document.createElement('div') countsValue.className='roundInfoValue___zV6GS' countsDiv.appendChild(countsTitle) countsDiv.appendChild(countsValue) const divider = document.createElement('div'); divider.classList.add('ant-divider', 'css-i874aq', 'ant-divider-vertical'); divider.setAttribute('role', 'separator'); panel_container.appendChild(divider) panel_container.appendChild(countsDiv) if(gameMode){ const divider_ = document.createElement('div'); divider_.classList.add('ant-divider', 'css-i874aq', 'ant-divider-vertical'); divider_.setAttribute('role', 'separator'); panel_container.appendChild(divider_) const avgDiv=document.createElement('div') avgDiv.className='roundInfoBox___ikizG' const avgTitle=document.createElement('div') avgTitle.className='roundInfoTitle___VOdv2' avgTitle.textContent='平均分' avgValue_=document.createElement('div') avgValue_.className='roundInfoValue___zV6GS' avgValue_.textContent=avgScore avgDiv.appendChild(avgTitle) avgDiv.appendChild(avgValue_) panel_container.appendChild(avgDiv) } } if(panel_container){ countsValue.textContent=streakCounts[mapsId][streakMode] avgValue_.textContent=avgScore } } function matchCountryCode(t) { if (t&&t.country_code){ const cc=t.country_code.toUpperCase() if(CC_DICT[cc])return CC_DICT[cc] else return cc } else return 'Undefined' } function matchState(t) { if(!t) return 'Undefined' if(t.country_code==='tw') return Language=== 'zh' ? '台湾省' : 'Taiwan Province' if (t.state) { return t.state; }else if (t.province) { return t.province; } else if (t.territory) { return t.territory; } else if (t.state_district) { return t.state_district; } else if (t.county) { return t.county; } else { return 'Undefined'; } } function formatNumber(number) { const numberStr = number.toString(); const formattedNumber = numberStr.replace(/\B(?=(\d{3})+(?!\d))/g, ','); return formattedNumber; } let onKeyDown = (e) => { if (e.key === 'p' || e.key === 'P') { e.stopImmediatePropagation(); if(streakMode!='state')streakMode='state' else streakMode='country' countsTitle.textContent = streakMode === 'country' ? '国家连击' : '一级行政区连击'; countsValue.textContent=streakCounts[mapsId][streakMode] localStorage.setItem('streakMode',JSON.stringify(streakMode)) Swal.fire({ title: '切换成功', text:`${streakMode === 'country' ? '国家连击' : '一级行政区连击'}连击计数器已就绪`, icon: 'success', timer: 1200, showConfirmButton: false, }); } } document.addEventListener("keydown", onKeyDown); })();