// ==UserScript==
// @name 图寻音效
// @namespace https://greasyfork.org/users/1179204
// @version 1.0.2
// @description 增加地图点击音效以及倒计时提醒音效
// @author KaKa
// @icon https://www.svgrepo.com/show/521999/bell.svg
// @match *://tuxun.fun/*
// @exclude *://tuxun.fun/replay-pano?*
// @grant GM_addStyle
// @license BSD
// @downloadURL none
// ==/UserScript==
(function() {
let map,streetViewPanorama,currentIndex=0,isPointCount=false,isPlay=true
let bellIcon=``
let silenceIcon=``
let iconUrl=svgToUrl(bellIcon)
const button = document.createElement('button');
button.id = 'replay';
GM_addStyle(`
#replay {
position:absolute;
left:24px;
bottom:20%;
width: 40px;
height: 40px;
border: none;
border-radius:20px;
background-image: url('${iconUrl}');
background-size: auto;
background-position: center;
background-color:#000000;
background-repeat: no-repeat;
cursor: pointer;
color: white;
font-size: 16px;
display: inline-flex;
align-items: center;
justify-content: center;
z-Index:9999
}
`);
button.onclick = () => {
if(!isPlay){
isPlay=true
button.style.backgroundImage=`url('${iconUrl}')`
}
else{
isPlay=false
button.style.backgroundImage=`url(${svgToUrl(silenceIcon)})`
}
}
const water_effect='https://www.geoguessr.com/_next/static/audio/place-guess-water-81b402df882cdb731e83a5463582d9c6.mp3'
const land_effects=[
'https://www.geoguessr.com/_next/static/audio/place-guess-land-1ac6606a711c3f128d917f3287d06b5f.mp3',
'https://www.geoguessr.com/_next/static/audio/place-guess-land-2-9a5a0a2fa6b2595f8a8e59312b5f41a9.mp3',
'https://www.geoguessr.com/_next/static/audio/place-guess-land-3-16f00d5f32e748cc49b45c8197b1986a.mp3',
'https://www.geoguessr.com/_next/static/audio/place-guess-land-4-9e3d39d2743652364f518acfc8ec9163.mp3',
'https://www.geoguessr.com/_next/static/audio/place-guess-land-5-dac7ac12a1b2ab0c6e97063e8e017095.mp3',
'https://www.geoguessr.com/_next/static/audio/place-guess-land-6-c4f4f4a8fcf8a6b2fbf1771d6538cadd.mp3']
const countdown='https://www.geoguessr.com/_next/static/audio/new-effect-timer-countdown-0ebc3024e8d1ef071d03f958f2813c0f.mp3'
const pointCount='https://www.geoguessr.com/_next/static/audio/effect-point-count-4401d2046fe66715ea454bc6d2dddc2d.mp3'
let intervalId=setInterval(function(){
const streetViewContainer= document.getElementById('viewer')
const wrapper=document.querySelector('.wrapper___NMMQn')
if(streetViewContainer){
getSvContainer()
getMap()
if(map&&streetViewPanorama){
wrapper.appendChild(button)
mapListener()
map.on('click', async (e) => {
const lat=e.lngLat.lat
const lng=e.lngLat.lng
if(!isPointCount){
const isWater=await isTileCenterBlue(lat,lng)
if(!isWater)playAudio(land_effects[currentIndex])
else playAudio(water_effect)
currentIndex+=1
if(currentIndex===6)currentIndex=0}
});
clearInterval(intervalId)}
}
},500);
function svgToUrl(svgText) {
const svgBlob = new Blob([svgText], {type: 'image/svg+xml'});
const svgUrl = URL.createObjectURL(svgBlob);
return svgUrl;
}
function getSvContainer(){
const streetViewContainer= document.getElementById('viewer')
const keys = Object.keys(streetViewContainer)
const key = keys.find(key => key.startsWith("__reactFiber"))
const props = streetViewContainer[key]
streetViewPanorama=props.return.child.memoizedProps.children[1].props.googleMapInstance
}
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 getTileUrl(lat,lng){
function lon2tile(lng,zoom) {
return (Math.floor((lng+180)/360*Math.pow(2,zoom)));
}
function lat2tile(lat,zoom){
return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom)));
}
const zoom=20
const tileX=lon2tile(lng,zoom)
const tileY=lat2tile(lat,zoom)
return `https://maprastertile-drcn.dbankcdn.cn/display-service/v1/online-render/getTile/23.12.09.11/${zoom}/${tileX}/${tileY}/?language=zh&p=46&scale=2&mapType=ROADMAP&presetStyleId=standard&pattern=JPG&key=DAEDANitav6P7Q0lWzCzKkLErbrJG4kS1u%2FCpEe5ZyxW5u0nSkb40bJ%2BYAugRN03fhf0BszLS1rCrzAogRHDZkxaMrloaHPQGO6LNg==`
}
async function isTileCenterBlue(lat, lng) {
const tileSize = 256;
const url = getTileUrl(lat, lng);
try {
const response = await fetch(url);
const blob = await response.blob();
const imageBitmap = await createImageBitmap(blob);
const canvas = document.createElement('canvas');
canvas.width = tileSize;
canvas.height = tileSize;
const context = canvas.getContext('2d');
context.drawImage(imageBitmap, 0, 0, tileSize, tileSize);
const imageData = context.getImageData(0, 0, tileSize, tileSize);
const data = imageData.data;
const centerX = Math.floor(tileSize / 2);
const centerY = Math.floor(tileSize / 2);
const centerIndex = (centerY * tileSize + centerX) * 4;
const r = data[centerIndex];
const g = data[centerIndex + 1];
const b = data[centerIndex + 2];
function isCloseToBlue(r, g, b) {
return r===129 && g===212 &&b===255;
}
return isCloseToBlue(r, g, b);
} catch (error) {
console.error('Error fetching or processing the tile:', error);
return false;
}
}
function playAudio(source) {
if(!isPlay)return
const audio = new Audio();
audio.loop = false;
audio.volume = 1;
if (source===pointCount) audio.volume = 0.5
if (source===countdown) audio.volume = 0.6
if (source===water_effect) audio.volume = 0.8
audio.src = source;
audio.play().catch(error => {
console.error('播放音频失败:', error);
});
if(source===countdown){
setTimeout(() => {
audio.pause();
audio.remove();
}, 16000);}
else{
audio.addEventListener('ended', () => {
audio.remove();
if (source===pointCount)isPointCount=false
});
}
}
function mapListener(){
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 handleSizeChange(target) {
const currentUrl = window.location.href;
const { width, height } = target.getBoundingClientRect();
const currentScreenWidth = window.innerWidth;
const widthRatio = (width / currentScreenWidth) * 100;
if (widthRatio>=90){
if(currentUrl.includes('challenge')){
isPointCount=true
setTimeout(function(){playAudio(pointCount)},200)
}
}
}
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
node.querySelectorAll && node.querySelectorAll('.countdownTimer___pqf8b').forEach(childNode => {
let intervalId = setInterval(function() {
const timeLeft = childNode.innerHTML.toString();
if (timeLeft.includes(':15')) {
playAudio(countdown)
clearInterval(intervalId);
}
}, 100);
});
});
}
}
});
const config = { childList: true, subtree: true };
observer.observe(document.body, config);
})();