Greasy Fork

来自缓存

Greasy Fork is available in English.

Geoguessr百度街景脚本

支持Geoguessr百度街景

当前为 2022-01-20 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name          Geoguessr百度街景脚本
// @description   支持Geoguessr百度街景
// @version       0.1.0
// @include       https://www.geoguessr.com/*
// @run-at        document-start
// @license       MIT
// @namespace     http://greasyfork.icu/users/838374
// ==/UserScript==

myLog("Geoguessr百度街景脚本");

// Store each player instance

let BAIDU_INJECTED = false;

// Game mode detection

let isBattleRoyale = false;
let isDuel = false;

// Player detection and coordinate conversion

let nextPlayer = "Google";
let global_lat = 0;
let global_lng = 0;
let global_panoID = null;
let global_BDID, global_BDAh, global_BDBh;
let global_heading = null;
let global_pitch = null;

let global_radi = 100

// Callback variables

let playerLoaded = false;

let defaultPanoIdChange = true;

// Round check

let ROUND = 0;
let CURRENT_ROUND_DATA = null;

let switch_call = true;
let one_reset = false;
// let cnt = 0;

let cn_tips = false;
var isFirefox = typeof InstallTrigger !== 'undefined';

/**
 * Helper Functions
 */

// Pretty print

function myLog(...args) {
    console.log(...args);
}
function myHighlight(...args) {
    console.log(`%c${[...args]}`, "color: dodgerblue; font-size: 24px;");
}

// Hex to number conversion for Baidu coordinate conversion

function hex2a(hexx) {
    var hex = hexx.toString();
    var str = '';
    for (var i = 0; i < hex.length; i += 2)
        str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
    return str;
}

// Coordinate computation given heading, distance and current coordinates for teleport

function FindPointAtDistanceFrom(lat, lng, initialBearingRadians, distanceKilometres) {
    const radiusEarthKilometres = 6371.01;
    var distRatio = distanceKilometres / radiusEarthKilometres;
    var distRatioSine = Math.sin(distRatio);
    var distRatioCosine = Math.cos(distRatio);

    var startLatRad = DegreesToRadians(lat);
    var startLonRad = DegreesToRadians(lng);

    var startLatCos = Math.cos(startLatRad);
    var startLatSin = Math.sin(startLatRad);

    var endLatRads = Math.asin((startLatSin * distRatioCosine) + (startLatCos * distRatioSine * Math.cos(initialBearingRadians)));

    var endLonRads = startLonRad
    + Math.atan2(
        Math.sin(initialBearingRadians) * distRatioSine * startLatCos,
        distRatioCosine - startLatSin * Math.sin(endLatRads));

    return { lat: RadiansToDegrees(endLatRads), lng: RadiansToDegrees(endLonRads) };
}

function DegreesToRadians(degrees) {
    const degToRadFactor = Math.PI / 180;
    return degrees * degToRadFactor;
}

function RadiansToDegrees(radians) {
    const radToDegFactor = 180 / Math.PI;
    return radians * radToDegFactor;
}

// Check if two floating point numbers are really really really really close to each other (to 10 decimal points)
function almostEqual (a, b) {
    return a.toFixed(10) === b.toFixed(10)
}

function almostEqual2 (a, b) {
    return a.toFixed(3) === b.toFixed(3)
}

// Script injection, extracted from extenssr:
// https://gitlab.com/nonreviad/extenssr/-/blob/main/src/injected_scripts/maps_api_injecter.ts

function overrideOnLoad(googleScript, observer, overrider) {
    const oldOnload = googleScript.onload
    googleScript.onload = (event) => {
        const google = unsafeWindow.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) {
    new MutationObserver((mutations, observer) => {
        const googleScript = grabGoogleScript(mutations)
        if (googleScript) {
            overrideOnLoad(googleScript, observer, overrider)
        }
    }).observe(document.documentElement, { childList: true, subtree: true })
}

/**
 * This observer stays alive while the script is running
 */

function launchObserver() {
    myHighlight("Main Observer");
    const OBSERVER = new MutationObserver((mutations, observer) => {
        detectGamePage();
    });
    OBSERVER.observe(document.head, { attributes: true, childList: true, subtree: true });
}

/**
 * Once the Google Maps API was loaded we can do more stuff
 */

injecter(() => {
    launchObserver();
})

/**
 * Check whether the current page is a game, if so which game mode
 */

function detectGamePage() {
    if (document.querySelector(".game-layout__panorama-message") !== null && !one_reset)
    {
        one_reset = true;
        myLog("Hide fail to load panorama canvas");
        document.querySelector(".game-layout__panorama-message").style.visibility = "hidden";
    }
    let toLoad = !playerLoaded 
    const PATHNAME = window.location.pathname;
    if (PATHNAME.startsWith("/game/") || PATHNAME.startsWith("/challenge/")) {
        // myLog("Game page");
        isBattleRoyale = false;
        isDuel = false;
        if (toLoad) {
            loadPlayers();
        }
        waitLoad();
    }
    else if (PATHNAME.startsWith("/battle-royale/")) {
        if (document.querySelector(".br-game-layout") == null) {
            // myLog("Battle Royale Lobby");
            rstValues();
        }
        else {
            // myLog("Battle Royale");
            isBattleRoyale = true;
            isDuel = false;
            if (toLoad) {
                loadPlayers();
            }
            waitLoad();
        }
    }
    else if (PATHNAME.startsWith("/duels/") || PATHNAME.startsWith("/team-duels/")) {
        if (document.querySelector(".game_layout__TO_jf") == null) {
            // myLog("Battle Royale Lobby");
            rstValues();
        }
        else {
            // myLog("Duels");
            isBattleRoyale = true;
            isDuel = true;
            if (toLoad) {
                loadPlayers();
            }
            waitLoad();
        }
    }
    else {
        rstValues();
        //myLog("Not a Game page");
    }
}

function rstValues()
{
    ROUND = 0;

    BAIDU_INJECTED = false;

    nextPlayer = "Google"
    global_lat = 0;
    global_lng = 0;
    global_panoID = null;
    global_BDAh = null;
    global_BDBh = null;
    global_BDID = null;

    playerLoaded = false;
    one_reset = false;
    CURRENT_ROUND_DATA = null;
}

/**
 * Wait for various players to load
 */

function waitLoad() {
    checkRound();
}

/**
 * Checks for round changes
 */

function checkRound() {
    if (!isBattleRoyale) {
        let currentRound = getRoundFromPage();
        if (ROUND != currentRound) {
            myHighlight("New round");
            ROUND = currentRound;
            one_reset = false;
            getMapData();
        }
    }
    else {
        getMapData();
    }
}

function loadPlayers() {
    playerLoaded = true;
    if (!isBattleRoyale)
    {
        getSeed().then((data) => {
            if (!data.mapName.includes("China Tips for each province"))
            {
                cn_tips = false;
            }
            else
            {
                cn_tips = true;
                guaranteeUI();
            }
        }).catch((error) => {
            myLog(error);
        });
    }
    initializeCanvas();

}

function guaranteeUI()
{
    // myLog("UI")
    if (document.getElementById("GH-ui") !== null)
    {
        document.getElementById("GH-ui").style.display = "block";
    }
    else
    {
        setTimeout(guaranteeUI, 500);
    }
}

/**
 * Handles Return to start and undo
 */

function handleReturnToStart() {
    let rtsButton = document.querySelector("button[data-qa='return-to-start']");
    if (rtsButton != null) {
        myLog("handleReturnToStart listener attached");
        rtsButton.addEventListener("click", (e) => {
            if (nextPlayer != "Baidu")
            {
                goToLocation();
            }
            else
            {
                document.getElementById("PanoramaMap").src = "https://map.baidu.com/?panotype=street&pid=" + global_BDID + "&panoid=" + global_BDID + "&from=api";
            }
            const elementClicked = e.target;
            elementClicked.setAttribute('listener', 'true');
            myLog("Return to start");
        });
    }
    else
    {
        setTimeout(handleReturnToStart, 500);
    }
}

/**
 * Load game information
 */

function getMapData() {
    // myHighlight("Seed data");
    getSeed().then((data) => {
        // myHighlight("Seed data");
        // myLog(data);
        if (isBattleRoyale) {
            if ((document.querySelector(".br-game-layout") == null && document.querySelector(".version3-in-game_layout__Hi_Iw") == null) || typeof data.gameId == typeof undefined) {
                // myLog("Battle Royale Lobby");
            }
            else
            {
                let origin = false;
                if (!CURRENT_ROUND_DATA) {
                    CURRENT_ROUND_DATA = data
                    origin = true;
                }

                if (origin || !(data.currentRoundNumber === CURRENT_ROUND_DATA.currentRoundNumber)) {
                    // myHighlight("Battle Royale New round");
                    // NEW_ROUND_LOADED = true;
                    one_reset = false;
                    if (!origin) {
                        CURRENT_ROUND_DATA = data;
                    }
                    locationCheck(data);
                    // myLog(data);
                    goToLocation();
                    handleReturnToStart();
                    if (isDuel)
                    {
                        hideButtons();
                    }

                }
            }
        }
        else {
            locationCheck(data);
            goToLocation();
            handleReturnToStart();
            hideButtons();
        }
    }).catch((error) => {
        myLog(error);
    });
}

/**
 * Hide unnecessary buttons for non-Google coverages
 */

function hideButtons() {
    let CHECKPOINT = document.querySelector("button[data-qa='set-checkpoint']");
    let ZOOM_IN = document.querySelector("button[data-qa='pano-zoom-in']");
    let ZOOM_OUT = document.querySelector("button[data-qa='pano-zoom-out']");

    if (CHECKPOINT != null)
    {
        if (nextPlayer === "Google") {

            CHECKPOINT.style.visibility = "";
            ZOOM_IN.style.visibility = "";
            ZOOM_OUT.style.visibility = "";
            myLog("Buttons Unhidden");

        }
        else {

            CHECKPOINT.style.visibility = "hidden";
            ZOOM_IN.style.visibility = "hidden";
            ZOOM_OUT.style.visibility = "hidden";
            myLog("Buttons Hidden");

        }
    }
    else
    {
        setTimeout(hideButtons, 250);
    }
}

/**
 * Check which player to use for the next location
 */

function locationCheck(data) {
    // console.log(data);
    let round;
    if (isBattleRoyale) {
        if (isDuel)
        {
            round = data.rounds[data.currentRoundNumber - 1].panorama;
        }
        else
        {
            round = data.rounds[data.currentRoundNumber - 1];
        }
    }
    else {
        round = data.rounds[data.round - 1];
    }
    global_lat = round.lat;
    global_lng = round.lng;
    global_panoID = round.panoId;
    global_heading = round.heading;
    global_pitch = round.pitch;

    nextPlayer = "Google";

    if (global_panoID) {
        let locInfo = hex2a(global_panoID);
        // myLog(locInfo)
        let mapType = locInfo.substring(0, 5);
        if (mapType === "BDMAP") {
            nextPlayer = "Baidu";
            let coord = locInfo.substring(5);

            if(coord.includes('BDAh'))
            {
                global_BDID = coord.split('BDAh')[0].replace("panoId","");
                let tem = coord.split('BDAh')[1];
                global_BDAh = tem.split('BDBh')[0];
                global_BDBh = tem.split('BDBh')[1];
            }
            else
            {
                global_BDID = coord.replace("panoId","");
            }
        }
    }



    myLog(nextPlayer);
    injectCanvas();
}


/**
 * setID for canvas
 */

function initializeCanvas() {
    let GAME_CANVAS = "";
    let DUEL_CANVAS = "";
    //myLog("Is duels");
    //myLog(duels);

    if (isBattleRoyale) {
        if (isDuel) {
            GAME_CANVAS = document.querySelector(".game-panorama_panorama__rdhFg");
            DUEL_CANVAS = document.querySelector(".game-panorama_panoramaCanvas__PNKve");
        }
        else
        {
            GAME_CANVAS = document.querySelector(".br-game-layout__panorama-wrapper");
            DUEL_CANVAS = "dummy";
        }
    }
    else {
        GAME_CANVAS = document.querySelector(".game-layout__canvas");
        DUEL_CANVAS = "dummy";
    }
    if (GAME_CANVAS && DUEL_CANVAS)
    {
        myLog("Canvas injected");
        GAME_CANVAS.id = "player";
        if (isDuel) {
            DUEL_CANVAS.id = "default_player";
        }
        injectBaiduScript();
    }
    else
    {
        setTimeout(initializeCanvas, 250);
    }

}

/**
 * Hide or show players based on where the next location is
 */

function injectCanvas() {
    if (isDuel)
    {
        canvasSwitch();
    }
    else
    {
        Google();
        Baidu();
    }
}

// for duels (class ID change)

function canvasSwitch()
{
    if (document.querySelector(".compass") !== null && document.querySelector("button[data-qa='undo-move']") !== null)
    {
        let GOOGLE_MAPS_CANVAS = document.querySelector(".game-panorama_panoramaCanvas__PNKve");
        if (nextPlayer === "Google") {
            document.getElementById("default_player").className = "game-panorama_panoramaCanvas__PNKve";
            document.getElementById("PanoramaMap").className = "inactive";
            document.getElementById("default_player").style.position = "absolute";
            document.querySelector(".compass").style.visibility = "";
            document.querySelector("button[data-qa='undo-move']").visibility = "";
            myLog("Google Duel Canvas loaded");
        }
        else if (nextPlayer === "Baidu")
        {
            document.getElementById("default_player").className = "inactive";
            document.getElementById("PanoramaMap").className = "game-panorama_panorama__rdhFg";
            document.getElementById("PanoramaMap").style.position = "absolute";
            document.querySelector(".compass").style.visibility = "hidden";
            document.querySelector("button[data-qa='undo-move']").visibility = "hidden";
            myLog("Baidu Duel Canvas loaded");
        }
    }
    else
    {
        setTimeout(canvasSwitch, 250);
    }
}

// for Battle Royale and classic (change visibility)

function Google() {
    let GOOGLE_MAPS_CANVAS = ""
    if (isBattleRoyale) {
        GOOGLE_MAPS_CANVAS = document.querySelector(".br-game-layout__panorama-canvas");
    }
    else {
        GOOGLE_MAPS_CANVAS = document.querySelector(".game-layout__panorama-canvas");
    }
    if (nextPlayer === "Google") {
        GOOGLE_MAPS_CANVAS.style.visibility = "";
        myLog("Google Canvas loaded");
    }
    else {
        GOOGLE_MAPS_CANVAS.style.visibility = "hidden";
        myLog("Google Canvas hidden");
    }

}

function Baidu() {
    let BAIDU_MAPS_CANVAS = document.getElementById("PanoramaMap");
    // myLog("Baidu canvas");
    document.getElementById("PanoramaMap").style.position = "absolute";
    if (BAIDU_MAPS_CANVAS !== null && document.querySelector(".compass") !== null && document.querySelector("button[data-qa='undo-move']") !== null)
    {
        if (nextPlayer === "Baidu") {
            BAIDU_MAPS_CANVAS.style.visibility = "";
            document.querySelector(".compass").style.visibility = "hidden";
            document.querySelector("button[data-qa='undo-move']").style.visibility = "hidden";
            myLog("Baidu Canvas loaded");
        }
        else {
            document.querySelector(".compass").style.visibility = "";
            document.querySelector("button[data-qa='undo-move']").style.visibility = "";
            BAIDU_MAPS_CANVAS.style.visibility = "hidden";
            myLog("Baidu Canvas hidden");
        }
    }
    else
    {
        setTimeout(Baidu, 250);
    }

}

/**
 * Open next location in streetview player given next player and next coordinate
 */

function goToLocation() {
    myLog("Going to location");
    if (nextPlayer === "Baidu") {
        if (document.getElementById("PanoramaMap") !== null)
        {
            let urlStr2 = "https://map.baidu.com/?panotype=street&pid=" + global_BDID + "&panoid=" + global_BDID + "&from=api";
            let urlStr = "https://map.baidu.com/@" + global_BDAh + "," + global_BDBh + "#panoid=" + global_BDID + "&panotype=street&l=12&tn=B_NORMAL_MAP&sc=0&newmap=1&shareurl=1&pid=" + global_BDID;
            if (global_BDAh != null)
            {
                document.getElementById("PanoramaMap").src = urlStr;
            }
            else
            {
                document.getElementById("PanoramaMap").src = urlStr2;
            }
        }
        else
        {
            setTimeout(goToLocation, 250);
        }
    }
}

/**
 * Gets the seed data for the current game
 *
 * @returns Promise with seed data as object
 */
function getSeed() {
    // myLog("getSeed called");
    return new Promise((resolve, reject) => {
        let token = getToken();
        let URL;
        let cred = ""

        const PATHNAME = window.location.pathname;

        if (PATHNAME.startsWith("/game/")) {
            URL = `https://www.geoguessr.com/api/v3/games/${token}`;
        }
        else if (PATHNAME.startsWith("/challenge/")) {
            URL = `https://www.geoguessr.com/api/v3/challenges/${token}/game`;
        }
        else if (PATHNAME.startsWith("/battle-royale/")) {
            URL = `https://game-server.geoguessr.com/api/battle-royale/${token}`;
        }
        else if (PATHNAME.startsWith("/duels/") || PATHNAME.startsWith("/team-duels/")) {
            URL = `https://game-server.geoguessr.com/api/duels/${token}`;
        }
        if (isBattleRoyale) {
            fetch(URL, {
                // Include credentials to GET from the endpoint
                credentials: 'include'
            })
                .then((response) => response.json())
                .then((data) => {
                resolve(data);
            })
                .catch((error) => {
                reject(error);
            });
        }
        else {
            fetch(URL)
                .then((response) => response.json())
                .then((data) => {
                resolve(data);
            })
                .catch((error) => {
                reject(error);
            });
        }
    });
}

/**
 * Gets the token from the current URL
 *
 * @returns token
 */
function getToken() {
    const PATHNAME = window.location.pathname;
    if (PATHNAME.startsWith("/game/")) {
        return PATHNAME.replace("/game/", "");
    }
    else if (PATHNAME.startsWith("/challenge/")) {
        return PATHNAME.replace("/challenge/", "");
    }
    else if (PATHNAME.startsWith("/battle-royale/")) {
        return PATHNAME.replace("/battle-royale/", "");
    }
    else if (PATHNAME.startsWith("/duels/")) {
        return PATHNAME.replace("/duels/", "");
    }
    else if (PATHNAME.startsWith("/team-duels/")) {
        return PATHNAME.replace("/team-duels/", "");
    }
}

/**
 * Gets the round number from the ongoing game from the page itself
 *
 * @returns Round number
 */
function getRoundFromPage() {
    const roundData = document.querySelector("div[data-qa='round-number']");
    if (roundData) {
        let roundElement = roundData.querySelector("div:last-child");
        if (roundElement) {
            let round = parseInt(roundElement.innerText.charAt(0));
            if (!isNaN(round) && round >= 1 && round <= 5) {
                return round;
            }
        }
    }
    else {
        return ROUND;
    }
}

/**
 * Injects Baidu script
 */

function reportWindowSize() {
    let iframeC = document.getElementById("PanoramaMap");
    iframeC.style.top = '-60px';
    iframeC.style.height = (window.innerHeight + 200) + 'px';
    iframeC.style.right = '-55px';
    iframeC.style.width = (window.innerWidth + 55) + 'px';
}

window.onresize = reportWindowSize;

function injectBaiduScript() {
    //     return new Promise((resolve, reject) => {
    //         if (!BAIDU_INJECTED) {
    //             if (BAIDU_API_KEY === "") {
    //                 let canvas = document.getElementById("player");
    //                 myLog("No Baidu Key")
    //             }
    //             else {
    myLog("Iframe")
    const iframe = document.createElement('iframe');

    // iframe.src = "https://map.baidu.com/"
    iframe.frameBorder = 0;
    iframe.style.position = "absolute";
    iframe.id = "PanoramaMap";
    if (!isFirefox)
    {
        iframe.style.top = '-60px';
        iframe.style.height = (window.innerHeight + 200) + 'px';
    }
    else
    {
        iframe.style.top = '-60px';
        iframe.style.height = (window.innerHeight + 219) + 'px';
    }

    if (!isFirefox)
    {
        iframe.style.right = '-55px';
        iframe.style.width = (window.innerWidth + 55) + 'px';
    }
    else
    {
        iframe.style.right = '-15px';
        iframe.style.width = (window.innerWidth + 15) + 'px';
    }

    if (isBattleRoyale) {
        if (isDuel)
        {
            iframe.className = "inactive";
        }
        else
        {
            iframe.className = "br-game-layout__panorama";
        }
    }
    else {
        iframe.className = "game-layout__panorama";
    }
    var div = document.getElementById("player");
    div.style.overflow = "hidden";
    div.appendChild(iframe);
}