Greasy Fork is available in English.
Display guess times and rating changes to the summary page of duels, and a list of players in team duels to be able to check their profile
当前为
// ==UserScript==
// @name Geoguessr duel guess times & team duels player list
// @version 1.2.1
// @description Display guess times and rating changes to the summary page of duels, and a list of players in team duels to be able to check their profile
// @match https://www.geoguessr.com/*
// @author victheturtle#5159
// @grant none
// @license MIT
// @icon https://www.svgrepo.com/show/139928/katana.svg
// @namespace http://greasyfork.icu/users/967692-victheturtle
// ==/UserScript==
let game = {};
let done = false;
let green = "game-summary_healing__qWCZd";
let red = "game-summary_damage__XQS1o";
let grey = "game-summary_smallText__SBEuX";
let big_white = "game-summary_text__AMDNt";
let summary_table = "game-summary_playedRounds__QLFcJ";
let summary_line = "game-summary_playedRound__zFmlo";
let color = (diff) => (diff>=0) ? ((diff==0)?grey:green) : red;
function checkURL() {
return location.pathname.startsWith("/duels") && location.pathname.endsWith("/summary") && document.getElementsByClassName(summary_table)[0] != null;
};
function addGuessTimesSummary() {
if (checkURL() && game != {} && !done) {
let result_lines = document.getElementsByClassName(summary_line);
let player2_link = document.getElementsByClassName("game-summary_playedRoundsHeader__86HiG")[0].children[2].firstChild.href;
if (player2_link == null) return false
done = true;
let player2_id = player2_link.slice(player2_link.lastIndexOf("/")+1);
let inversion = game.teams[1].players[0].playerId != player2_id;
let player1 = game.teams[inversion ? 1 : 0].players[0];
let player2 = game.teams[inversion ? 0 : 1].players[0];
let guesses1 = player1.guesses;
let guesses2 = player2.guesses;
for (let i=0; i<result_lines.length; i++) {
let time0 = game.rounds[i].startTime;
if (guesses1.length <= i || guesses1[i].roundNumber != i+1) guesses1.splice(i, 0, {created:NaN});
let time1 = guesses1[i].created;
if (guesses2.length <= i || guesses2[i].roundNumber != i+1) guesses2.splice(i, 0, {created:NaN});
let time2 = guesses2[i].created;
let text1 = document.createElement("div");
text1.classList.add(isNaN(time2) ? green : color(time2-time1));
text1.innerText = isNaN(time1) ? "-" : (time1-time0)/1000. + " s";
result_lines[i].childNodes[1].appendChild(text1);
let text2 = document.createElement("div");
text2.classList.add(isNaN(time1) ? green : color(time1-time2));
text2.innerText = isNaN(time2) ? "-" : (time2-time0)/1000. + " s";
result_lines[i].childNodes[2].appendChild(text2);
}
let summary = document.getElementsByClassName(summary_table)[0];
let newRatingLine = document.createElement("div");
newRatingLine.classList.add(summary_line);
try {
let oldRating1 = player1.progressChange.competitiveProgress.ratingBefore;
let newRating1 = player1.progressChange.competitiveProgress.ratingAfter;
let oldRating2 = player2.progressChange.competitiveProgress.ratingBefore;
let newRating2 = player2.progressChange.competitiveProgress.ratingAfter;
newRatingLine.innerHTML = `
<div><span><div class="${grey}">Rating change</div><div class="${big_white}">New rating</div></span></div>
<div><div class="${color(newRating1-oldRating1)}">${newRating1-oldRating1}</div><div class="${big_white}">${newRating1}</div></div>
<div><div class="${color(newRating2-oldRating2)}">${newRating2-oldRating2}</div><div class="${big_white}">${newRating2}</div></div>
<div><div class="${big_white}"> </div></div>
<div><div class="${big_white}"> </div></div>`;
} catch {
let oldRating1 = player1.rating;
let oldRating2 = player2.rating;
newRatingLine.innerHTML = `
<div><span><div class="${grey}">Rating change</div><div class="${big_white}">New rating</div></span></div>
<div><div class="${grey}">0</div><div class="${big_white}">${oldRating1}</div></div>
<div><div class="${grey}">0</div><div class="${big_white}">${oldRating2}</div></div>
<div><div class="${big_white}"> </div></div>
<div><div class="${big_white}"> </div></div>`;
};
summary.appendChild(newRatingLine);
};
return true;
};
function addProfileLinks() {
if (checkURL() && game != {} && !done) {
done = true;
const nameMap = {};
const teamMap = {};
const verifiedMap = {};
__NEXT_DATA__.props.pageProps.game.teams[0].players.map(y => {
nameMap[y.playerId]=y.nick; verifiedMap[y.playerId] = y.isVerified; teamMap[y.playerId] = "red";
});
__NEXT_DATA__.props.pageProps.game.teams[1].players.map(y => {
nameMap[y.playerId]=y.nick; verifiedMap[y.playerId] = y.isVerified; teamMap[y.playerId] = "blue";
});
const playerTemplate = (playerId) => `<span class="game-summary_bestGuessValue__H9wt3" style="margin:2px"><div class="user-nick_root__DUfvc">
<div class="user-nick_nickWrapper__8Tnk4">
<div class="user-nick_nick__y4VIt"><a href="https://www.geoguessr.com/user/${playerId}" style="color:white">${nameMap[playerId]} </a></div>
${verifiedMap[playerId] ? '<div class="user-nick_verifiedWrapper__yocOV"><img class="user-nick_verified__WdndT" src="/_next/static/images/verified-badge-566f0efd4d90928c6e044cbe588456dc.svg" alt="Verified user"></div>' : ''}
</div>
</div></span>`;
const teamTemplate = (team) => {
let s = "";
for (let playerId in nameMap) {
if (teamMap[playerId] == team && playerId != "633a8a81af04a94fb02d8b1b" && playerId != "633c8040723d43ea09977ea2") {
s = s + playerTemplate(playerId);
}
}
return s;
}
const mapTemplate = (mapId, mapName) => `<span class="game-summary_bestGuessValue__H9wt3" style="margin:2px"><div class="user-nick_root__DUfvc">
<div class="user-nick_nickWrapper__8Tnk4">
<div class="user-nick_nick__y4VIt"><a href="https://www.geoguessr.com/maps/${mapId}" style="color:white">${mapName} </a></div>
</div>
</div></span>`;
const options = __NEXT_DATA__.props.pageProps.game.options;
let playersLine = document.createElement("div");
playersLine.classList.add(summary_line);
const inversion = document.querySelector("#__next div.game-summary_playedRoundsHeader__86HiG img").alt.includes("blue");
playersLine.innerHTML = `
<div><span><div class="game-summary_text__AMDNt">Players</div></span></div>
<div>${teamTemplate((inversion) ? "blue" : "red")}</div>
<div>${teamTemplate((inversion) ? "red" : "blue")}</div>
<div><span><div class="game-summary_text__AMDNt">Map</div></span></div>
<div>${mapTemplate(options.map.slug, options.map.name)}</div>`;
let summary = document.getElementsByClassName(summary_table)[0];
summary.insertBefore(playersLine, summary.firstChild);
};
return true;
};
function check() {
let game_url = window.location.href;
fetch(game_url)
.then(res => res.text())
.then(str => {
let parser = new DOMParser();
let html = parser.parseFromString(str, "text/html");
let dataHTML = html.getElementById("__NEXT_DATA__");
let dataJson = JSON.parse(dataHTML.innerHTML);
game = dataJson.props.pageProps.game;
const isDuel = addGuessTimesSummary();
if (!isDuel) { // it's a team duel
addProfileLinks();
}
}).catch(err => {throw(err);});
};
function doCheck() {
if (!checkURL()) {
done = false;
} else if (game != {} && !done) {
check();
}
};
function tryAddGuessTimesOnRefresh() {
setTimeout(doCheck, 50);
setTimeout(doCheck, 300);
};
function tryAddGuessTimes() {
doCheck();
for (let timeout of [250,500,1200,2000]) {
setTimeout(doCheck, timeout);
}
};
document.addEventListener('click', tryAddGuessTimes, false);
document.addEventListener('load', tryAddGuessTimesOnRefresh(), false);
window.addEventListener('popstate', tryAddGuessTimes, false);