Greasy Fork

Greasy Fork is available in English.

Geoguessr duel guess times & team duels player list

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

当前为 2022-12-08 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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]}&nbsp;</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}&nbsp;</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);