// ==UserScript==
// @name 图寻连击计数器
// @namespace https://greasyfork.org/users/1179204
// @version 1.0.2
// @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
// @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={},roundState,countsDiv,countsTitle,countsValue,mapsId
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: "GB", 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){
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 (!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
if(viewer.pano.length===27) [lat,lng]=await checkPano(viewer.pano)
else{
lat=viewer.location.latLng.lat()
lng=viewer.location.latLng.lng()}
const 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){
const guess=await queryOSM(finalGuess.lat,finalGuess.lng)
try{
if(guess.country==='India'&&guess.state==='Arunachal Pradesh'){
guess.country='China'
guess.state='Tibet'}
}
catch(error) {
}
var isStreak
if(streakMode==='country'){
if(matchCountryCode(guess)===matchCountryCode(roundPins[currentRound])){
isStreak=true
}
}
else if(streakMode==='state'){
if(matchState(guess)===matchState(roundPins[currentRound])){
isStreak=true
}
}
if(guess) updateBar(isStreak,guess,roundPins[currentRound],streakMode)
else updateBar(false,'Undefined',roundPins[currentRound],streakMode)
currentRound+=1
}
}
function correctAddress(item){
if(['Taiwan','HongKong','Macau'].includes(item)) return 'China'
else return item
}
function updateBar(status,pin,result){
const infoBar=document.querySelector('.controls___yY74y')
const streakText = infoBar.querySelector('p')
streakText.style.fontSize='24px'
streakText.style.color='#fff'
streakText.style.fontFamily='Baloo Bhaina'
infoBar.appendChild(streakText)
if (infoBar){
if(status){
streakCounts[mapsId][streakMode]+=1
if(streakMode==='country') streakText.textContent = `恭喜你选对 ${correctAddress(result.country).split('/')[0]}, 连击次数: ${ streakCounts[mapsId][streakMode]}`
else if(streakMode='state') streakText.textContent = `恭喜你选对 ${matchState(result)}, 连击次数: ${ streakCounts[mapsId][streakMode]}`
}
else{
const end_count=streakCounts[mapsId][streakMode]
streakCounts[mapsId][streakMode]=0
if(streakMode==='country') streakText.textContent = `答案是 ${correctAddress(result.country).split('/')[0]}, 你选了${correctAddress(pin.country).split('/')[0]}, 连击次数: ${ streakCounts[mapsId][streakMode]}, 本轮达成连击:${end_count}`
else if(streakMode='state')streakText.textContent = `答案是 ${matchState(result)}, 你选了${matchState(pin)}, 连击次数: ${ streakCounts[mapsId][streakMode]}, 本轮达成连击:${end_count}`
}
localStorage.setItem('streakCounts',JSON.stringify(streakCounts))
}
}
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(panel_container){
countsValue.textContent=streakCounts[mapsId][streakMode]}
}
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.county) {
return t.county;
} else {
return 'Undefined';
}
}
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);
})();