// ==UserScript==
// @name Geoguessr GeoStats Script
// @description Keeps track of various stats and displays it on an interactive map on your profile (Duels and Classic)
// @author nappyslappy
// @version 2.2.8
// @match https://www.geoguessr.com/*
// @grant none
// @license none
// @icon https://www.google.com/s2/favicons?sz=64&domain=geoguessr.com
// @copyright nappyslappy 2023, (https://greasyfork.org/en/users/922456)
// @namespace https://greasyfork.org/en/users/922456-nappyslappy
// @downloadURL https://update.greasyfork.icu/scripts/446052/Geoguessr%20GeoStats%20Script.user.js
// @updateURL https://update.greasyfork.icu/scripts/446052/Geoguessr%20GeoStats%20Script.meta.js
// ==/UserScript==
//Published: June 6, 2022
//Last Update: April 21, 2024
/******************************** SETUP ********************************/
/* */
/* If you haven't already, download the free extension 'tampermonkey' */
/* and then install this script */
/* */
/* Go to your profile page and setup the script at the bottom */
/* */
/* You do not need an API key to use this script */
/* nor do you need to change anything below */
/* */
/***********************************************************************/
//************************************************************* START OF SCRIPT *************************************************************//
// NUM OF COUNTRIES SHOWN IN TOP INCORRECT LIST
let TOP_INCORRECT_NUM = 5;
// CHANGE THIS NUMBER ^^^^^ TO THE NUMBER OF COUNTRIES YOU WANT SHOWN
//GLOBAL VARIABLES
//Get User ID
let USER_ID = JSON.parse(document.getElementById('__NEXT_DATA__').innerText).props.accountProps.account.user.userId;
//Currently Fixing Bugs
window.localStorage.setItem(`geostats-fixing-bugs-${USER_ID}`, false);
//Checking Profile
let statsToProfile = false;
let setupToProfile = false;
let updateToProfile = false;
let fixingToProfile = false;
//Checking Duels Rounds
let player_index = 0;
let previous_last_guess_number = 0;
let checkingDuelsRoundOver = false;
let checkingDuelsGameOver = false;
//Checking Classic Rounds
let checkingClassicRoundOver = false;
//Checking Challenge Rounds
let checkingChallengeRoundOver = false;
//Map Colors
let green_range = 0.75;
let yellow_range = 0.25;
let red_range = 0;
//Set the map color ranges
if(window.localStorage.getItem(`duels-map-color-left-range-${USER_ID}`)){
green_range = parseInt(window.localStorage.getItem(`duels-map-color-right-range-${USER_ID}`)) * 0.01;
yellow_range = parseInt(window.localStorage.getItem(`duels-map-color-left-range-${USER_ID}`)) * 0.01;
}
else{
window.localStorage.setItem(`duels-map-color-left-range-${USER_ID}`, 25);
window.localStorage.setItem(`duels-map-color-right-range-${USER_ID}`, 75);
}
//Set the healing rounds toggle switch
if(window.localStorage.getItem('allow-healing-rounds') == null){
window.localStorage.setItem('allow-healing-rounds', JSON.stringify({include: true}));
}
//Set the competitive mode toggle switch
if(window.localStorage.getItem('competitive-only-mode') == null){
window.localStorage.setItem('competitive-only-mode', JSON.stringify({include: true}));
}
//Set the included maps
if(!window.localStorage.getItem(`geostats-classic-included-maps-${USER_ID}`)){
window.localStorage.setItem(`geostats-classic-included-maps-${USER_ID}`, JSON.stringify({ids: ['us-state-streak','country-streak']}));
}
//Evaluate the new duels guess
function evaluateDuelsGuess(guess,location,distance,userScore,opponentScore,startTime,timerTime,guessTime,opponentDistance){
if(guess == 'AQ'){
return;
}
let correct_exists = window.localStorage.getItem(`${location}-number-correct-${USER_ID}`);
let total_exists = window.localStorage.getItem(`${location}-number-total-${USER_ID}`);
let correct_overall = window.localStorage.getItem(`overall-correct-${USER_ID}`);
let total_overall = window.localStorage.getItem(`overall-total-${USER_ID}`);
let correct_value = 1;
let total_value = 1;
//Setting correct value
if(guess === location){
if(correct_exists !== null){
correct_value = parseInt(correct_exists,10);
correct_value = correct_value + 1;
}
window.localStorage.setItem(`${location}-number-correct-${USER_ID}`,correct_value);
}
//Setting overall values
if(total_overall !== null){
if(guess === location){
window.localStorage.setItem(`overall-correct-${USER_ID}`,((parseInt(correct_overall,10))+1));
}
window.localStorage.setItem(`overall-total-${USER_ID}`,((parseInt(total_overall,10))+1));
}
else{
if(guess === location){
window.localStorage.setItem(`overall-correct-${USER_ID}`,1);
}
else{
window.localStorage.setItem(`overall-correct-${USER_ID}`,0);
}
window.localStorage.setItem(`overall-total-${USER_ID}`,1);
}
//Setting total value
if(total_exists !== null){
total_value = parseInt(total_exists,10);
total_value = total_value + 1;
}
window.localStorage.setItem(`${location}-number-total-${USER_ID}`,total_value);
//Setting distance
let distance_average = window.localStorage.getItem(`${location}-distance-average-${USER_ID}`);
let distance_number = window.localStorage.getItem(`${location}-distance-number-${USER_ID}`);
if(distance_average === null && distance_number === null){
window.localStorage.setItem(`${location}-distance-average-${USER_ID}`,distance);
window.localStorage.setItem(`${location}-distance-number-${USER_ID}`,1);
}
else{
distance_number = parseInt(distance_number) + 1;
distance_average = ((distance_average * (distance_number - 1)) + distance) / distance_number;
window.localStorage.setItem(`${location}-distance-average-${USER_ID}`,distance_average);
window.localStorage.setItem(`${location}-distance-number-${USER_ID}`,distance_number);
}
//SETTING ALL OTHER STATS FOR COUNTRY AND OVERALL
let countryStats = {
totalCount: 0,
totalCorrect: 0,
averageScore: 0,
averageDistance: 0,
closerThanOpponent: 0,
averageTime: 0,
guessedFirst: 0,
total5ks: 0,
countryCorrectStreak: 0,
countryCorrectMax: 0,
countryWrongStreak: 0,
countryWrongMax: 0,
distanceFromOpponent: [0,0],
//Wrong country
incorrectCountry: {},
};
let overallStats = {
//Total
totalCount: 0,
totalCorrect: 0,
totalWin: 0,
totalLoss: 0,
total5ks: 0,
totalGames: 0,
totalSeconds: 0,
//Average
averageScore: 0,
averageDistance: 0,
averageGameLengthWin: 0,
averageGameLengthLoss: 0,
averageTime: 0,
//Wrong country
incorrectCountry: {},
//Other
guessedFirst: 0,
closerThanOpponent: 0,
countryCorrectStreak: 0,
countryCorrectMax: 0,
countryWrongStreak: 0,
countryWrongMax: 0,
opponentRating: 0,
distanceFromOpponent: [0,0],
};
if(window.localStorage.getItem(`${location}-all-country-stats-${USER_ID}`)){
countryStats = JSON.parse(window.localStorage.getItem(`${location}-all-country-stats-${USER_ID}`));
}
if(window.localStorage.getItem(`overall-country-stats-${USER_ID}`)){
overallStats = JSON.parse(window.localStorage.getItem(`overall-country-stats-${USER_ID}`));
}
//Time String to Seconds
function timeToSeconds(value){
let time = value.slice(11,19);
time = time.split(/[.:]/);
time = (parseInt(time[0]) * 60 * 60) + (parseInt(time[1]) * 60) + parseInt(time[2]);
return time;
}
//Get Correct Time Values
startTime = timeToSeconds(startTime);
timerTime = timeToSeconds(timerTime);
guessTime = timeToSeconds(guessTime);
let timeToGuess = guessTime - startTime;
//Total Count
countryStats.totalCount++;
overallStats.totalCount++;
//Total Correct
countryStats.totalCorrect = correct_value;
overallStats.totalCorrect = parseInt(correct_overall,10);
//Average Score
countryStats.averageScore = ((countryStats.averageScore * (countryStats.totalCount - 1)) + userScore) / countryStats.totalCount;
overallStats.averageScore = ((overallStats.averageScore * (overallStats.totalCount - 1)) + userScore) / overallStats.totalCount;
//Average Distance
countryStats.averageDistance = ((countryStats.averageDistance * (countryStats.totalCount - 1)) + distance) / countryStats.totalCount;
overallStats.averageDistance = ((overallStats.averageDistance * (overallStats.totalCount - 1)) + distance) / overallStats.totalCount;
//Average Time
if(timeToGuess >= 0){
countryStats.averageTime = ((countryStats.averageTime * (countryStats.totalCount - 1)) + timeToGuess) / countryStats.totalCount;
overallStats.averageTime = ((overallStats.averageTime * (overallStats.totalCount - 1)) + timeToGuess) / overallStats.totalCount;
}
//Closer Than Opponent
if(userScore >= opponentScore){
countryStats.closerThanOpponent++;
overallStats.closerThanOpponent++;
}
//Guessed First
if(guessTime == timerTime){
countryStats.guessedFirst++;
overallStats.guessedFirst++;
}
//Total 5ks
if(userScore == 5000){
countryStats.total5ks++;
overallStats.total5ks++;
}
//Country Streaks (Correct)
if(guess == location){
//Country
countryStats.countryWrongStreak = 0;
countryStats.countryCorrectStreak++;
if(countryStats.countryCorrectStreak > countryStats.countryCorrectMax){
countryStats.countryCorrectMax = countryStats.countryCorrectStreak;
}
//Overall
overallStats.countryWrongStreak = 0;
overallStats.countryCorrectStreak++;
if(overallStats.countryCorrectStreak > overallStats.countryCorrectMax){
overallStats.countryCorrectMax = overallStats.countryCorrectStreak;
}
}
//Country Streaks (Incorrect)
if(guess != location){
//Country
countryStats.countryCorrectStreak = 0;
countryStats.countryWrongStreak++;
if(countryStats.countryWrongStreak > countryStats.countryWrongMax){
countryStats.countryWrongMax = countryStats.countryWrongStreak;
}
//Overall
overallStats.countryCorrectStreak = 0;
overallStats.countryWrongStreak++;
if(overallStats.countryWrongStreak > overallStats.countryWrongMax){
overallStats.countryWrongMax = overallStats.countryWrongStreak;
}
}
//Wrong Country
if(guess != location){
//Overall
if(!overallStats.incorrectCountry){
overallStats.incorrectCountry = {};
}
if(location != 'ERROR'){
if(overallStats.incorrectCountry[`${location}`]){
overallStats.incorrectCountry[`${location}`]++;
}
else{
overallStats.incorrectCountry[`${location}`] = 1;
}
}
//Country
if(!countryStats.incorrectCountry){
countryStats.incorrectCountry = {};
}
if(guess != 'ERROR'){
if(countryStats.incorrectCountry[`${guess}`]){
countryStats.incorrectCountry[`${guess}`]++;
}
else{
countryStats.incorrectCountry[`${guess}`] = 1;
}
}
}
//Distance From Opponent
if(opponentDistance != 'DNF'){
let distanceDifference = opponentDistance - distance;
//Country
if(countryStats.distanceFromOpponent){
countryStats.distanceFromOpponent[0]++;
countryStats.distanceFromOpponent[1] = ((countryStats.distanceFromOpponent[1] * (countryStats.distanceFromOpponent[0] - 1)) + distanceDifference) / countryStats.distanceFromOpponent[0];
}
else{
countryStats.distanceFromOpponent = [1,distanceDifference];
}
//Overall
if(overallStats.distanceFromOpponent){
overallStats.distanceFromOpponent[0]++;
overallStats.distanceFromOpponent[1] = ((overallStats.distanceFromOpponent[1] * (overallStats.distanceFromOpponent[0] - 1)) + distanceDifference) / overallStats.distanceFromOpponent[0];
}
else{
overallStats.distanceFromOpponent = [1,distanceDifference];
}
}
//Save Updated Stats
window.localStorage.setItem(`${location}-all-country-stats-${USER_ID}`, JSON.stringify(countryStats));
window.localStorage.setItem(`overall-country-stats-${USER_ID}`, JSON.stringify(overallStats));
};
//Add Stats When Duels Game Is Finished
function duelsGameFinished(userHealth,opponentHealth,opponentRating,userBefore,userAfter,gameId,gameLength,start,end){
if(window.sessionStorage.getItem('current-duels-game-id') == gameId){
return;
}
window.sessionStorage.setItem('current-duels-game-id', gameId);
//Time String to Seconds
function timeToSeconds(value){
let time = value.slice(11,19);
time = time.split(/[.:]/);
time = (parseInt(time[0]) * 60 * 60) + (parseInt(time[1]) * 60) + parseInt(time[2]);
return time;
}
let overallStats = JSON.parse(window.localStorage.getItem(`overall-country-stats-${USER_ID}`));
let gameTime = timeToSeconds(end) - timeToSeconds(start);
//If there is a calculation error (end is smaller than start)
if(gameTime < 0){
gameTime = 0;
}
if(overallStats.totalSeconds < 0){
overallStats.totalSeconds = 0;
}
//Total Games Played
overallStats.totalGames++;
//Average Opponent Rating
overallStats.opponentRating = ((overallStats.opponentRating * (overallStats.totalGames - 1)) + opponentRating) / overallStats.totalGames;
//Win or Loss
let win = false;
if(userHealth > opponentHealth){
win = true;
}
//Game Length (Win)
if(win){
overallStats.totalWin++;
overallStats.averageGameLengthWin = ((overallStats.averageGameLengthWin * (overallStats.totalWin - 1)) + gameLength) / overallStats.totalWin;
}
//Game Length (Loss)
if(!win){
overallStats.totalLoss++;
overallStats.averageGameLengthLoss = ((overallStats.averageGameLengthLoss * (overallStats.totalLoss - 1)) + gameLength) / overallStats.totalLoss;
}
//Total Seconds Played
if(overallStats.totalSeconds){
overallStats.totalSeconds += gameTime;
}
else{
overallStats.totalSeconds = gameTime;
}
//Average Rating Change
if(userBefore != null && userAfter != null){
if(!overallStats.averageRatingChange){
overallStats.averageRatingChange = {
win: [0,0],
loss: [0,0],
};
}
if(win){
overallStats.averageRatingChange.win[0]++;
overallStats.averageRatingChange.win[1] = ((overallStats.averageRatingChange.win[1] * (overallStats.averageRatingChange.win[0] - 1)) + (userAfter-userBefore)) / overallStats.averageRatingChange.win[0];
}
else if(!win){
overallStats.averageRatingChange.loss[0]++;
overallStats.averageRatingChange.loss[1] = ((overallStats.averageRatingChange.loss[1] * (overallStats.averageRatingChange.loss[0] - 1)) + (userAfter-userBefore)) / overallStats.averageRatingChange.loss[0];
}
}
//Save Updated Stats
window.localStorage.setItem(`overall-country-stats-${USER_ID}`, JSON.stringify(overallStats));
};
//Check the country code
function checkGuessCountryCode(out){
let countryCode = 'NO CODE';
if(out.address.country_code){
countryCode = out.address.country_code;
}
//Check for US territories
if(countryCode == 'us'){
//Puerto Rico
if(out.address['ISO3166-2-lvl4'] == 'US-PR'){
countryCode = 'pr';
}
//Guam
if(out.address['ISO3166-2-lvl4'] == 'US-GU'){
countryCode = 'gu';
}
//Northern Mariana Islands
if(out.address['ISO3166-2-lvl4'] == 'US-MP'){
countryCode = 'mp';
}
//Virgin Islands
if(out.address['ISO3166-2-lvl4'] == 'US-VI'){
countryCode = 'vi';
}
//American Samoa
if(out.address['ISO3166-2-lvl4'] == 'US-AS'){
countryCode = 'as';
}
}
//US Minor Outlying Islands
if(countryCode == 'um'){
countryCode = 'us';
}
//Check for AU territories
if(countryCode == 'au'){
//Cocos Islands
if(out.address['territory'] == 'Cocos (Keeling) Islands'){
countryCode = 'cc';
}
//Christmas Island
if(out.address['territory'] == 'Christmas Island'){
countryCode = 'cx';
}
}
//Check for NL territories
if(countryCode == 'nl'){
//CuraƧao
if(out.address['ISO3166-2-lvl3'] == 'NL-CW'){
countryCode = 'cw';
}
}
//Check for Palestine
if(countryCode == 'ps'){
countryCode = 'il';
}
//Check for Hong Kong & Macau
if(countryCode == 'cn'){
if(out.address['ISO3166-2-lvl3']){
if(out.address['ISO3166-2-lvl3'] == 'CN-HK'){
countryCode = 'hk';
}
else if(out.address['ISO3166-2-lvl3'] == 'CN-MO'){
countryCode = 'mo';
}
}
}
return countryCode;
};
//Get the guess country code
async function getGuessCountryCode(location){
if(location[0] <= -85.05 || location == null){
return 'AQ';
}
else{
let api = "https://nominatim.openstreetmap.org/reverse.php?lat="+location[0]+"&lon="+location[1]+"&format=jsonv2";
let country_code = await fetch(api)
.then(res => res.json())
.then((out) => {
return checkGuessCountryCode(out);
}).catch(err => {return 'ERROR'});
return country_code.toUpperCase();
};
};
//Check if a new duels guess has been made
async function checkDuelsGuess(api){
return new Promise((resolve,reject) => {
fetch(api,{credentials: 'include'})
.then((res) => res.json())
.then((out) => {
if(out.teams[0].players[0].playerId === USER_ID){
player_index = 0;
}
else if(out.teams[1].players[0].playerId === USER_ID){
player_index = 1;
}
else{
return;
}
//If competitve only mode is on
let competitiveOnly = JSON.parse(window.localStorage.getItem('competitive-only-mode'));
if(competitiveOnly.include && !out.options.isRated){
return;
}
let last_guess_number = out.teams[player_index].players[0].guesses.length;
//If the game is finished
if(out.status == 'Finished'){
let opponentIndex = 0;
if(player_index == 0){
opponentIndex = 1;
}
let userRatingBefore = null;
let userRatingAfter = null;
if(out.options.isRated){
userRatingBefore = out.teams[player_index].players[0].progressChange.competitiveProgress.ratingBefore;
userRatingAfter = out.teams[player_index].players[0].progressChange.competitiveProgress.ratingAfter;
}
let opponentRatingBefore = out.teams[opponentIndex].players[0].rating;
let userHealth = out.teams[player_index].health;
let opponentHealth = out.teams[opponentIndex].health;
let gameLength = out.currentRoundNumber;
let gameStartTime = out.rounds[0].startTime;
let gameEndTime = out.rounds[gameLength-1].endTime;
duelsGameFinished(userHealth,opponentHealth,opponentRatingBefore,userRatingBefore,userRatingAfter,out.gameId,gameLength,gameStartTime,gameEndTime);
return;
}
//If the round's guesses haven't been sent
if(out.teams[player_index].roundResults.length != last_guess_number){
return;
}
//If a new guess hasn't been made
if(last_guess_number == previous_last_guess_number){
return;
}
else if(out.teams[player_index].players[0].guesses[last_guess_number-1].roundNumber !== out.currentRoundNumber){
return;
}
//If include healing toggle is off
let allowHealing = JSON.parse(window.localStorage.getItem('allow-healing-rounds'));
if(!allowHealing.include && out.rounds[out.currentRoundNumber-1].isHealingRound){
return;
}
let current_guess = [out.teams[player_index].players[0].guesses[last_guess_number-1].lat, out.teams[player_index].players[0].guesses[last_guess_number-1].lng];
//If the player refreshes during the 15 second countdown after making a guess
if(current_guess[0] == window.sessionStorage.getItem('last-guess-lat') && current_guess[1] == window.sessionStorage.getItem('last-guess-lng')){
return;
}
else{
window.sessionStorage.setItem('last-guess-lat', current_guess[0]);
window.sessionStorage.setItem('last-guess-lng', current_guess[1]);
}
//Everything is good
previous_last_guess_number = last_guess_number;
let current_round = out.currentRoundNumber;
getGuessCountryCode(current_guess)
.then((guess) => {
//Get all values
let location_code = out.rounds[current_round-1].panorama.countryCode.toUpperCase();
let distance = out.teams[player_index].players[0].guesses[last_guess_number-1].distance;
let opponent_index = 0;
if(player_index == 0){
opponent_index = 1;
}
let userScore = out.teams[player_index].roundResults[last_guess_number-1].score;
let opponentScore = out.teams[opponent_index].roundResults[last_guess_number-1].score;
let startTime = out.rounds[current_round-1].startTime;
let timerTime = out.rounds[current_round-1].timerStartTime;
let guessTime = out.teams[player_index].players[0].guesses[last_guess_number-1].created;
let opponentDistance = 'DNF';
let opponentGuess = out.teams[opponent_index].players[0];
if(opponentGuess.guesses[opponentGuess.guesses.length-1] && opponentGuess.guesses[opponentGuess.guesses.length-1].roundNumber == current_round){
opponentDistance = opponentGuess.guesses[opponentGuess.guesses.length-1].distance;
}
//If guess is good
if(guess != undefined){
//Check location code
location_code = checkGuessCountryCode({address:{country_code:location_code}});
//Evaluate all values and update stats
evaluateDuelsGuess(guess,location_code,distance,userScore,opponentScore,startTime,timerTime,guessTime,opponentDistance);
}
resolve(out);
});
})
.catch((error) => {
reject(error);
});
});
};
//Evaluate The Stats For The Finished Classic Game
function evaluateClassicGameFinished(out){
if(out.mode == 'streak'){
//Evaluate Country Streaks Game Stats
if(out.streakType == 'countrystreak'){
}
//Evaluate USA Streaks Game Stats
else if(out.streakType == 'usstatestreak'){
}
}
//Evaluate Standard Game Stats
else if(out.mode == 'standard'){
let overallStats = JSON.parse(window.localStorage.getItem(`geostats-overall-standard-stats-${USER_ID}`));
overallStats.totalGames++;
overallStats.totalSeconds += out.player.totalTime;
overallStats.averageGameTime = ((overallStats.averageGameTime * (overallStats.totalGames - 1)) + out.player.totalTime) / overallStats.totalGames;
//HIGHSCORE STUFF HERE
//Save Updated Stats
window.localStorage.setItem(`geostats-overall-standard-stats-${USER_ID}`, JSON.stringify(overallStats));
}
};
//Evaluate The Guess For The Finished Classic Round
function evaluateClassicRoundFinished(out){
if(out.mode == 'streak'){
//Evaluate Country Streaks Round Stats
if(out.streakType == 'countrystreak'){
}
//Evaluate USA Streaks Game Stats
else if(out.streakType == 'usstatestreak'){
}
}
else if(out.mode == 'standard'){
//Evaluate Standard Game Stats
let current_guess = [out.player.guesses[out.player.guesses.length-1].lat, out.player.guesses[out.player.guesses.length-1].lng];
let current_location = [out.rounds[out.player.guesses.length-1].lat, out.rounds[out.player.guesses.length-1].lng];
//If the guess timed out
if(out.player.guesses[out.player.guesses.length-1].timedOut){
return;
}
getGuessCountryCode(current_guess)
.then(guess => {
getGuessCountryCode(current_location)
.then(location => {
let standardCountryStats = {
//Total
totalCount: 0,
totalCorrect: 0,
totalSeconds: 0,
total5ks: 0,
//Average
averageScore: 0,
averageDistance: 0,
averageRoundTime: 0,
//Wrong country
incorrectCountry: {},
//Streak
countryCorrectStreak: 0,
countryCorrectMax: 0,
countryWrongStreak: 0,
countryWrongMax: 0,
};
let standardOverallStats = {
//Total
totalCount: 0,
totalCorrect: 0,
totalGames: 0,
totalSeconds: 0,
total5ks: 0,
//Average
averageScore: 0,
averageDistance: 0,
averageRoundTime: 0,
averageGameTime: 0,
//Wrong country
incorrectCountry: {},
//5k locations:
all5kLocations: [],
//Streak
countryCorrectStreak: 0,
countryCorrectMax: 0,
countryWrongStreak: 0,
countryWrongMax: 0,
};
//Get Exisiting Stats
if(window.localStorage.getItem(`geostats-${location}-all-standard-stats-${USER_ID}`)){
standardCountryStats = JSON.parse(window.localStorage.getItem(`geostats-${location}-all-standard-stats-${USER_ID}`));
}
if(window.localStorage.getItem(`geostats-overall-standard-stats-${USER_ID}`)){
standardOverallStats = JSON.parse(window.localStorage.getItem(`geostats-overall-standard-stats-${USER_ID}`));
}
//Useful variables
let score = out.player.guesses[out.player.guesses.length-1].roundScoreInPoints;
let distance = out.player.guesses[out.player.guesses.length-1].distanceInMeters;
let time = out.player.guesses[out.player.guesses.length-1].time;
//Country Total
standardCountryStats.totalCount++;
standardOverallStats.totalCount++;
//Country Correct
if(guess == location){
//Country
standardCountryStats.totalCorrect++;
standardCountryStats.countryWrongStreak = 0;
standardCountryStats.countryCorrectStreak++;
if(standardCountryStats.countryCorrectStreak > standardCountryStats.countryCorrectMax){
standardCountryStats.countryCorrectMax = standardCountryStats.countryCorrectStreak;
}
//Overall
standardOverallStats.totalCorrect++;
standardOverallStats.countryWrongStreak = 0;
standardOverallStats.countryCorrectStreak++;
if(standardOverallStats.countryCorrectStreak > standardOverallStats.countryCorrectMax){
standardOverallStats.countryCorrectMax = standardOverallStats.countryCorrectStreak;
}
}
//Country Incorrect
else{
//Country
standardCountryStats.countryCorrectStreak = 0;
standardCountryStats.countryWrongStreak++;
if(standardCountryStats.countryWrongStreak > standardCountryStats.countryWrongMax){
standardCountryStats.countryWrongMax = standardCountryStats.countryWrongStreak;
}
if(guess != 'ERROR'){
if(standardCountryStats.incorrectCountry[`${guess}`]){
standardCountryStats.incorrectCountry[`${guess}`]++;
}
else{
standardCountryStats.incorrectCountry[`${guess}`] = 1;
}
}
//Overall
standardOverallStats.countryCorrectStreak = 0;
standardOverallStats.countryWrongStreak++;
if(standardOverallStats.countryWrongStreak > standardOverallStats.countryWrongMax){
standardOverallStats.countryWrongMax = standardOverallStats.countryWrongStreak;
}
if(location != 'ERROR'){
if(standardOverallStats.incorrectCountry[`${location}`]){
standardOverallStats.incorrectCountry[`${location}`]++;
}
else{
standardOverallStats.incorrectCountry[`${location}`] = 1;
}
}
}
//Average Score
standardCountryStats.averageScore = ((standardCountryStats.averageScore * (standardCountryStats.totalCount - 1)) + score) / standardCountryStats.totalCount;
standardOverallStats.averageScore = ((standardOverallStats.averageScore * (standardOverallStats.totalCount - 1)) + score) / standardOverallStats.totalCount;
//Average Distance
standardCountryStats.averageDistance = ((standardCountryStats.averageDistance * (standardCountryStats.totalCount - 1)) + distance) / standardCountryStats.totalCount;
standardOverallStats.averageDistance = ((standardOverallStats.averageDistance * (standardOverallStats.totalCount - 1)) + distance) / standardOverallStats.totalCount;
//Average Round Time
standardCountryStats.averageRoundTime = ((standardCountryStats.averageRoundTime * (standardCountryStats.totalCount - 1)) + time) / standardCountryStats.totalCount;
standardOverallStats.averageRoundTime = ((standardOverallStats.averageRoundTime * (standardOverallStats.totalCount - 1)) + time) / standardOverallStats.totalCount;
//Total Seconds
standardCountryStats.totalSeconds += time;
//Total 5ks
if(score == 5000){
standardCountryStats.total5ks++;
standardOverallStats.total5ks++;
standardOverallStats.all5kLocations.push({lat: out.rounds[out.player.guesses.length-1].lat, lng: out.rounds[out.player.guesses.length-1].lng, id: out.token, map: out.map, round: out.player.guesses.length-1});
}
//Save Updated Stats
window.localStorage.setItem(`geostats-${location}-all-standard-stats-${USER_ID}`, JSON.stringify(standardCountryStats));
window.localStorage.setItem(`geostats-overall-standard-stats-${USER_ID}`, JSON.stringify(standardOverallStats));
});
});
}
};
//Add The Classic Game Id (if needed)
function addGameId(newId){
if(!window.localStorage.getItem(`geostats-unfinished-classic-games-${USER_ID}`)){
window.localStorage.setItem(`geostats-unfinished-classic-games-${USER_ID}`, JSON.stringify({gameIds:[]}));
}
let allGames = JSON.parse(window.localStorage.getItem(`geostats-unfinished-classic-games-${USER_ID}`));
if(!allGames.gameIds.includes(newId)){
allGames.gameIds.push(newId);
}
//Save Updated Ids
window.localStorage.setItem(`geostats-unfinished-classic-games-${USER_ID}`, JSON.stringify(allGames));
};
//Evaluate The Stats For The Finished Challenge Game
function evaluateChallengeGameFinished(out){
console.log("Game Finished");
let overallStats = JSON.parse(window.localStorage.getItem(`geostats-overall-challenge-stats-${USER_ID}`));
overallStats.totalGames++;
overallStats.totalSeconds += out.player.totalTime;
overallStats.averageGameTime = ((overallStats.averageGameTime * (overallStats.totalGames - 1)) + out.player.totalTime) / overallStats.totalGames;
//HIGHSCORE STUFF HERE
//Save Updated Stats
window.localStorage.setItem(`geostats-overall-challenge-stats-${USER_ID}`, JSON.stringify(overallStats));
};
//Evaluate The Guess For The Finished Round
function evaluateChallengeRoundFinished(out){
console.log("Round Finished");
console.log(out);
const currentRound = out.rounds[out.round-1];
const currentGuess = out.player.guesses[out.round-1];
//If User Timed Out
if(currentGuess.timedOut || currentGuess.timedOutWithGuess){
return;
}
getGuessCountryCode([currentGuess.lat, currentGuess.lng])
.then(guess => {
getGuessCountryCode([currentRound.lat, currentRound.lng])
.then(location => {
//Setup Stats For First Time
let challengeCountryStats = {
//Total
totalCount: 0,
totalCorrect: 0,
totalSeconds: 0,
total5ks: 0,
//Average
averageScore: 0,
averageDistance: 0,
averageRoundTime: 0,
//Wrong country
incorrectCountry: {},
//Streak
countryCorrectStreak: 0,
countryCorrectMax: 0,
countryWrongStreak: 0,
countryWrongMax: 0,
};
let challengeOverallStats = {
//Total
totalCount: 0,
totalCorrect: 0,
totalGames: 0,
totalSeconds: 0,
total5ks: 0,
//Average
averageScore: 0,
averageDistance: 0,
averageRoundTime: 0,
averageGameTime: 0,
//Wrong country
incorrectCountry: {},
//5k locations:
all5kLocations: [],
//Streak
countryCorrectStreak: 0,
countryCorrectMax: 0,
countryWrongStreak: 0,
countryWrongMax: 0,
};
//Get Exisiting Stats
if(window.localStorage.getItem(`geostats-${location}-all-challenge-stats-${USER_ID}`)){
challengeCountryStats = JSON.parse(window.localStorage.getItem(`geostats-${location}-all-challenge-stats-${USER_ID}`));
}
if(window.localStorage.getItem(`geostats-overall-challenge-stats-${USER_ID}`)){
challengeOverallStats = JSON.parse(window.localStorage.getItem(`geostats-overall-challenge-stats-${USER_ID}`));
}
//Useful variables
let score = currentGuess.roundScoreInPoints;
let distance = currentGuess.distanceInMeters;
let time = currentGuess.time;
//Country Total
challengeCountryStats.totalCount++;
challengeOverallStats.totalCount++;
//Country Correct
if(guess == location){
//Country
challengeCountryStats.totalCorrect++;
challengeCountryStats.countryWrongStreak = 0;
challengeCountryStats.countryCorrectStreak++;
if(challengeCountryStats.countryCorrectStreak > challengeCountryStats.countryCorrectMax){
challengeCountryStats.countryCorrectMax = challengeCountryStats.countryCorrectStreak;
}
//Overall
challengeOverallStats.totalCorrect++;
challengeOverallStats.countryWrongStreak = 0;
challengeOverallStats.countryCorrectStreak++;
if(challengeOverallStats.countryCorrectStreak > challengeOverallStats.countryCorrectMax){
challengeOverallStats.countryCorrectMax = challengeOverallStats.countryCorrectStreak;
}
}
//Country Incorrect
else{
//Country
challengeCountryStats.countryCorrectStreak = 0;
challengeCountryStats.countryWrongStreak++;
if(challengeCountryStats.countryWrongStreak > challengeCountryStats.countryWrongMax){
challengeCountryStats.countryWrongMax = challengeCountryStats.countryWrongStreak;
}
if(guess != 'ERROR'){
if(challengeCountryStats.incorrectCountry[`${guess}`]){
challengeCountryStats.incorrectCountry[`${guess}`]++;
}
else{
challengeCountryStats.incorrectCountry[`${guess}`] = 1;
}
}
//Overall
challengeOverallStats.countryCorrectStreak = 0;
challengeOverallStats.countryWrongStreak++;
if(challengeOverallStats.countryWrongStreak > challengeOverallStats.countryWrongMax){
challengeOverallStats.countryWrongMax = challengeOverallStats.countryWrongStreak;
}
if(location != 'ERROR'){
if(challengeOverallStats.incorrectCountry[`${location}`]){
challengeOverallStats.incorrectCountry[`${location}`]++;
}
else{
challengeOverallStats.incorrectCountry[`${location}`] = 1;
}
}
}
//Average Score
challengeCountryStats.averageScore = ((challengeCountryStats.averageScore * (challengeCountryStats.totalCount - 1)) + score) / challengeCountryStats.totalCount;
challengeOverallStats.averageScore = ((challengeOverallStats.averageScore * (challengeOverallStats.totalCount - 1)) + score) / challengeOverallStats.totalCount;
//Average Distance
challengeCountryStats.averageDistance = ((challengeCountryStats.averageDistance * (challengeCountryStats.totalCount - 1)) + distance) / challengeCountryStats.totalCount;
challengeOverallStats.averageDistance = ((challengeOverallStats.averageDistance * (challengeOverallStats.totalCount - 1)) + distance) / challengeOverallStats.totalCount;
//Average Round Time
challengeCountryStats.averageRoundTime = ((challengeCountryStats.averageRoundTime * (challengeCountryStats.totalCount - 1)) + time) / challengeCountryStats.totalCount;
challengeOverallStats.averageRoundTime = ((challengeOverallStats.averageRoundTime * (challengeOverallStats.totalCount - 1)) + time) / challengeOverallStats.totalCount;
//Total Seconds
challengeCountryStats.totalSeconds += time;
//Total 5ks
if(score == 5000){
challengeCountryStats.total5ks++;
challengeOverallStats.total5ks++;
challengeOverallStats.all5kLocations.push({lat: out.rounds[out.player.guesses.length-1].lat, lng: out.rounds[out.player.guesses.length-1].lng, id: out.token, map: out.map, round: out.player.guesses.length-1});
}
//Save Updated Stats
window.localStorage.setItem(`geostats-${location}-all-challenge-stats-${USER_ID}`, JSON.stringify(challengeCountryStats));
window.localStorage.setItem(`geostats-overall-challenge-stats-${USER_ID}`, JSON.stringify(challengeOverallStats));
})
});
};
//Check if the user is on the profile page
function profileCheck(){
if(location.pathname.endsWith('/profile')){
//If script is currently being updated and running it would cause more problems
if(window.localStorage.getItem(`geostats-fixing-bugs-${USER_ID}`) == 'tru'){
if(!fixingToProfile){
addFixingToProfile();
}
}
else if(window.localStorage.getItem(`geostats-setup-complete-${USER_ID}`)){
if(!window.localStorage.getItem(`geostats-update-2.2.8-complete-${USER_ID}`) && !updateToProfile){
addUpdateToProfile();
}
else if(window.localStorage.getItem(`geostats-update-2.2.8-complete-${USER_ID}`) && !statsToProfile){
USER_ID = JSON.parse(document.getElementById('__NEXT_DATA__').innerText).props.accountProps.account.user.userId;
addMapToProfile();
}
}
else if(!window.localStorage.getItem(`geostats-setup-complete-${USER_ID}`) && !setupToProfile){
addSetupPageToProfile();
}
}
else if(!location.pathname.endsWith('/profile')){
if(statsToProfile){
statsToProfile = false;
document.getElementById('map-wrapper-element').remove();
document.getElementById('css-for-map').remove();
}
else if(setupToProfile){
setupToProfile = false;
document.getElementById('geostats-setup-css').remove();
document.getElementById('geostats-setup-html').remove();
}
else if(updateToProfile){
updateToProfile = false;
document.getElementById('geostats-update-css').remove();
document.getElementById('geostats-update-html').remove();
}
else if(fixingToProfile){
fixingToProfile = false;
document.getElementById('geostats-fixing-bugs-css').remove();
document.getElementById('geostats-fixing-bugs-html').remove();
}
}
};
//Check if the user is playing a challenge
function challengeCheck(){
if(!location.pathname.startsWith('/challenge/')){
return;
}
//If A Round Has Finished
if(document.getElementsByClassName('result-layout_root__fRPgH').length > 0){
const game_tag = window.location.href.substring(window.location.href.lastIndexOf('/') + 1);
const api_url = 'https://www.geoguessr.com/api/v3/challenges/' + game_tag + '/game';
if(!checkingChallengeRoundOver){
checkingChallengeRoundOver = true;
fetch(api_url)
.then((res) => res.json())
.then((out) => {
//Set All Unfinished Challenge Games
if(!window.localStorage.getItem(`geostats-unfinished-challenge-games-${USER_ID}`)){window.localStorage.setItem(`geostats-unfinished-challenge-games-${USER_ID}`, JSON.stringify({games: []}))};
let allChallengeGames = JSON.parse(window.localStorage.getItem(`geostats-unfinished-challenge-games-${USER_ID}`));
//Check If First Round For Challenge
if(!allChallengeGames.games.includes(game_tag)){
if(out.round == 1){
allChallengeGames.games.push(game_tag);
}
else{
//Not First Round - Already Started Challenge
return;
}
}
//Don't Duplicate Round On Refresh
if(window.sessionStorage.getItem('last-challenge-guess')){
let lastGuess = JSON.parse(window.sessionStorage.getItem('last-challenge-guess'));
if(lastGuess.id == game_tag && lastGuess.round == out.round){
return;
}
}
window.sessionStorage.setItem('last-challenge-guess', JSON.stringify({id: game_tag, round: out.round}));
evaluateChallengeRoundFinished(out);
//If Game Is Finished
if(out.state == 'finished'){
evaluateChallengeGameFinished(out);
const index = allChallengeGames.games.indexOf(game_tag);
allChallengeGames.games.splice(index,1);
}
//Update Changed Stats
window.localStorage.setItem(`geostats-unfinished-challenge-games-${USER_ID}`, JSON.stringify(allChallengeGames));
})
.catch((error) => {
throw error;
});
}
}
else{
checkingChallengeRoundOver = false;
}
};
//Check if the user is playing a classic game
function classicCheck(){
if(!location.pathname.startsWith('/game/')){
return;
}
//If a round has finished
if(document.getElementsByClassName('result-layout_root__fRPgH').length > 0){
const game_tag = window.location.href.substring(window.location.href.lastIndexOf('/') + 1);
const api_url = 'https://www.geoguessr.com/api/v3/games/' + game_tag;
if(!checkingClassicRoundOver){
checkingClassicRoundOver = true;
fetch(api_url)
.then((res) => res.json())
.then((out) => {
//Check if the map is included in the selected maps
if(!window.localStorage.getItem(`geostats-classic-included-maps-${USER_ID}`)){
window.localStorage.setItem(`geostats-classic-included-maps-${USER_ID}`, JSON.stringify({ids: ['us-state-streak','country-streak']}));
}
let mapId = out.map;
let includedMaps = JSON.parse(window.localStorage.getItem(`geostats-classic-included-maps-${USER_ID}`));
if(!includedMaps.ids.includes(mapId)){
return;
}
//IF THE GAME IS FINISHED
if(out.state == 'finished'){
if(window.localStorage.getItem(`geostats-unfinished-classic-games-${USER_ID}`)){
let allGames = JSON.parse(window.localStorage.getItem(`geostats-unfinished-classic-games-${USER_ID}`));
if(allGames.gameIds.length > 0 && allGames.gameIds.includes(game_tag)){
//Remove the game id from unfinished games
const index = allGames.gameIds.indexOf(game_tag);
allGames.gameIds.splice(index,1);
window.localStorage.setItem(`geostats-unfinished-classic-games-${USER_ID}`, JSON.stringify(allGames));
evaluateClassicRoundFinished(out);
evaluateClassicGameFinished(out);
}
}
else{
return;
}
}
//IF THE GAME IS NOT FINISHED
else{
evaluateClassicRoundFinished(out);
addGameId(game_tag);
}
})
.catch((error) => {
throw error;
});
}
}
else{
checkingClassicRoundOver = false;
}
};
//Check if the user is playing a duels game
function duelsCheck(){
if(!location.pathname.startsWith('/duels/') || location.pathname.endsWith('/summary')){
return;
}
const game_tag = window.location.href.substring(window.location.href.lastIndexOf('/') + 1);
const api_url = "https://game-server.geoguessr.com/api/duels/"+game_tag;
//If a round has finished
if(document.getElementsByClassName('overlay_overlay__AR02x').length > 0){
//If the round is finished
if(document.getElementsByClassName('round-score_container__s6qNg').length > 0){
if(!checkingDuelsRoundOver){
checkingDuelsRoundOver = true;
checkDuelsGuess(api_url);
}
}
//If the game is finished
else if(document.getElementsByClassName('game-finished_container___UprU').length > 0){
if(!checkingDuelsGameOver){
checkingDuelsGameOver = true;
checkDuelsGuess(api_url);
}
}
}
else{
checkingDuelsRoundOver = false;
checkingDuelsGameOver = false;
}
};
//Check if the user is about to start a standard game
function startpageCheck(){
if(location.pathname.startsWith('/maps/') && location.pathname.endsWith('/play')){
if(!document.getElementById('geostats-standard-game-start-page-toggle-html')){
addToggleToStartPage();
}
}
};
//Run duelsCheck twice every second
setInterval(duelsCheck,500);
//Run classicCheck four times every second
setInterval(classicCheck,250);
//Run challengeCheck four times every second
setInterval(challengeCheck,250)
//Run profileCheck twice every second
setInterval(profileCheck,500);
//Console Log Script Name
console.log('*** Geoguessr GeoStats v2.2.8 by NappySlappy ***');
/************************************************** UPDATE STUFF HERE **************************************************/
function addFixingToProfile(){
fixingToProfile = true;
//Create The Fixing Elements
const cssForFixing = document.createElement('style');
cssForFixing.setAttribute('id','geostats-fixing-bugs-css');
const htmlForFixing = document.createElement('div');
htmlForFixing.setAttribute('id','geostats-fixing-bugs-html');
//CSS For Fixing
cssForFixing.textContent = `
#fixing-page-title-wrapper{
margin: 20px auto;
text-align: center;
}
#fixing-page-title{
font-family: var(--font-neo-sans);
font-size: 40px;
color: white;
}
#fixing-page-content-wrapper{
margin: 20px auto;
text-align: center;
width: 65%;
border: solid 2px white;
border-radius: 40px;
}
#fixing-page-content{
font-family: var(--font-neo-sans);
font-size: 20px;
color: white;
padding: 20px;
}
`;
//HTML For Fixing
htmlForFixing.innerHTML = `
GeoStats Fixing Bugs
Sorry, due to Geoguessr's recent update, some things in this script have broken. I'm working on fixing everything and will have the updates out shortly. Thanks! :)