GeoGuessr Random Pan and Zoom

Starts the game with a random pan and zoom

// ==UserScript==
// @name         GeoGuessr Random Pan and Zoom
// @description  Starts the game with a random pan and zoom
// @version      1.0
// @author       Rawblocky
// @match        *://*.geoguessr.com/*
// @run-at       document-start
// @icon         https://www.google.com/s2/favicons?domain=geoguessr.com
// @grant        none
// @license      MIT
// @namespace https://greasyfork.org/users/1435525
// ==/UserScript==

const SETTINGS = {
	heading: {
    // 0 to 360
		minValue: 0,
		maxValue: 360,
		enabled: true,
	},
	pitch: {
    // -90 to 90
		minValue: -45,
		maxValue: 45,
		enabled: true,
	},
	zoom: {
    // 1 to 4
		minValue: 1,
		maxValue: 4,
		enabled: true,
	},

	activateOnRoundStart: true,
	keybindEnabled: true, // binded to 'p'
};

let MWGTM_SV;

function getRandom(minValue, maxValue) {
	return Math.random() * (maxValue - minValue) + minValue;
}

function randomizePov() {
	if (!MWGTM_SV) return;
	const pov = MWGTM_SV.getPov();
	if (SETTINGS.heading.enabled) {
		pov.heading =
			getRandom(SETTINGS.heading.minValue, SETTINGS.heading.maxValue) % 360;
	}
	if (SETTINGS.pitch.enabled) {
		pov.pitch = getRandom(SETTINGS.pitch.minValue, SETTINGS.pitch.maxValue);
	}
	if (SETTINGS.zoom.enabled) {
		pov.zoom = getRandom(SETTINGS.zoom.minValue, SETTINGS.zoom.maxValue);
	}
	MWGTM_SV.setPov(pov);
}

function overrideOnLoad(googleScript, observer, overrider) {
	const oldOnload = googleScript.onload;
	googleScript.onload = (event) => {
		const google = window.google;
		if (google) {
			observer.disconnect();
			overrider(google);
		}
		if (oldOnload) {
			oldOnload.call(googleScript, event);
		}
	};
}

function grabGoogleScript(mutations) {
	for (const mutation of mutations) {
		for (const newNode of mutation.addedNodes) {
			const asScript = newNode;
			if (
				asScript &&
				asScript.src &&
				asScript.src.startsWith("https://maps.googleapis.com/")
			) {
				return asScript;
			}
		}
	}
	return null;
}

function injecter(overrider) {
	if (document.documentElement) {
		injecterCallback(overrider);
	}
}

function injecterCallback(overrider) {
	new MutationObserver((mutations, observer) => {
		const googleScript = grabGoogleScript(mutations);
		if (googleScript) {
			overrideOnLoad(googleScript, observer, overrider);
		}
	}).observe(document.documentElement, { childList: true, subtree: true });
}

function isLoading() {
	return (
		document.querySelector("[class*=fullscreen-spinner_root__]") ||
		document.querySelector("[class*=round-score-2_isMounted__]") ||
		document.querySelector("[class*=new-game-2_isAnimated__]") ||
		!document.querySelector(".widget-scene-canvas")
	);
}

let wasBackdropThereOrLoading = false;
function isBackdropThereOrLoading() {
	return (
		isLoading() || // loading
		document.querySelector("[class*=result-layout_root__]") || // classic
		document.querySelector("[class*=overlay_backdrop__]") || // duels / team duels
		document.querySelector("[class*=round-starting_wrapper____]") || // live challenges
		document.querySelector("[class*=popup_backdrop__]") || // BR
		document.querySelector("[class*=game-starting_container__]") ||
		document.querySelector("[class*=round-score_container__]") || // bullseye
		document.querySelector("[class*=overlay-modal_backlight__]")
	); // city streaks
}

document.addEventListener("DOMContentLoaded", (event) => {
	injecter(() => {
		const google = window["google"] || unsafeWindow["google"];
		if (!google) return;

		google.maps.StreetViewPanorama = class extends (
			google.maps.StreetViewPanorama
		) {
			constructor(...args) {
				super(...args);
				MWGTM_SV = this;
				randomizePov();
			}
		};
	});
});

if (SETTINGS.keybindEnabled) {
	document.addEventListener("keyup", (event) => {
		if (event.key === "p") {
			randomizePov();
		}
	});
}

let wasDuels = false;
let isFirstRound = false;

if (SETTINGS.activateOnRoundStart) {
	let observer = new MutationObserver((mutations) => {
		if (!MWGTM_SV) return;

		const isDuels =
			location.pathname.includes("duels/") ||
			location.pathname.includes("/multiplayer")
		if (isDuels != wasDuels) {
			wasDuels = isDuels;
			if (isDuels) {
				isFirstRound = true;
			}
		}

		if (isBackdropThereOrLoading()) {
			wasBackdropThereOrLoading = true;
			randomizePov();
			if (!isLoading());
		} else if (wasBackdropThereOrLoading) {
			wasBackdropThereOrLoading = false;
			if (isDuels && isFirstRound) {
        // First round of duels game doesn't get affected; delay
				isFirstRound = false;
				setTimeout(() => {
					randomizePov(true);
				}, 500);
			}
		}
	});

	observer.observe(document.body, {
		characterDataOldValue: false,
		subtree: true,
		childList: true,
		characterData: false,
	});
}