Greasy Fork

Geoguessr 36 Modded Emotes

Replaces the 6 default emotes with 36 emotes used by the various Geoguessr Twitch and Discord communities

目前为 2022-10-17 提交的版本。查看 最新版本

// ==UserScript==
// @name         Geoguessr 36 Modded Emotes
// @description  Replaces the 6 default emotes with 36 emotes used by the various Geoguessr Twitch and Discord communities
// @version      1.1.5
// @author       victheturtle#5159
// @license      MIT
// @match        https://www.geoguessr.com/*
// @run-at       document-start
// @grant        GM_addStyle
// @icon         https://www.geoguessr.com/_next/static/images/emote-gg-cf17a1f5d51d0ed53f01c65e941beb6d.png
// @namespace    https://greasyfork.org/users/967692-victheturtle
// ==/UserScript==

GM_addStyle ( `
    div[class^=emote-section_emoteContainer__] {
        scale: 0;
    }
    div[class*=emoteContainer_checked] {
        scale: 1;
    }
` );

function fixEmoteWheelZoom() {
    let zoomStyleElt = document.querySelector('style[data-name="big-screen-mode"]');
    if (!zoomStyleElt.innerHTML.includes("emote")) {
        zoomStyleElt.innerHTML = `
/* big-screen-mode 0:1 */
@media screen and (min-width: 0px) {
  html {
    font-size: 16px;
  }
}

/* big-screen-mode 2000:1.125 */
@media screen and (min-width: 2000px) {
  html :not(div[class^=emote-wheel_root__]) {
    font-size: 18px;
  }
}

/* big-screen-mode 4000:1.25 */
@media screen and (min-width: 4000px) {
  html :not(div[class^=emote-wheel_root__]) {
    font-size: 20px;
  }
}`;
    }
}

const GGemoteTypes = ["confused", "cry", "gg", "happy", "mindblown", "wave"];
const GGemotes = [
    "https://www.geoguessr.com/_next/static/images/emote-confused-e0cf85ababd0222d0a5afdd1e197643b.png",
    "https://www.geoguessr.com/_next/static/images/emote-cry-d6a31832e6fbb210bbc7f51a5a566b43.png",
    "https://www.geoguessr.com/_next/static/images/emote-gg-cf17a1f5d51d0ed53f01c65e941beb6d.png",
    "https://www.geoguessr.com/_next/static/images/emote-happy-072e991610e1235c10a134dac75b128c.png",
    "https://www.geoguessr.com/_next/static/images/emote-mindblown-d1f80fc9fd1cb031bbfb3de1240e03e5.png",
    "https://www.geoguessr.com/_next/static/images/emote-wave-da1dd3859051c109583d2f3cda5824f8.png",
]

let GGemotesIndex = {};
for (let i = 0; i < 6; i++) {
    GGemotesIndex[GGemoteTypes[i]] = i;
    for (let j = 0; j < 6; j++) {
        GGemotesIndex[GGemotes[i]+GGemotes[j]] = i*6+j;
    }
}

const customEmotes6 = [
    "https://www.geoguessr.com/images/auto/450/450/ce/0/plain/pin/9493e767efaca9a3c7a552c0d7227381.png", // A Confused/Sus
    "https://www.geoguessr.com/images/auto/450/450/ce/0/plain/pin/52e1282025886a7d9eb640454716ff4e.png", // B Sad/Mad
    "https://www.geoguessr.com/images/auto/450/450/ce/0/plain/pin/6632068240e1b83d9e93434369226277.png", // C GG
    "https://www.geoguessr.com/images/auto/450/450/ce/0/plain/pin/1b3cf7b7a99c358eed548661b64db014.png", // D Happy
    "https://www.geoguessr.com/images/auto/450/450/ce/0/plain/pin/b4d1960f7eb91d9908124371f0ff4c9a.png", // E Troll/Other
    "https://www.geoguessr.com/images/auto/450/450/ce/0/plain/pin/a55f9e12fde3a2f2850ed05c6532a5b9.png", // F Just say hi
];

const customEmotes36 = [
    // A Confused/Sus
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/fa1d89146595e34773d9ec3a4436acf6.png", // skull
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/24543be764d1e9dff7abf05fc3e94350.png", // hmmm
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/585f4a75812097d174fad6589a4ba2e6.png", // monkaW
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/fe1231f811ea59e3cdaaaea979273067.png", // monkaS
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/40914674304a8344f1fa19ec67c4073d.png", // weirdChamp (ProjectBloom)
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/b68eeed38a52dbd980e868d1a272fe38.png", // ptryWeird
    // B Sad/Mad
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/2f119e3b084a7ff9782d4b4e88ee0507.png", // FeelsBadMan (oceanman)
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/fd7d1b2e055c8743076e77e3f8c537a5.png", // Sadge
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/57f6382c8cb89e4cbe6ffb0fed66efb8.png", // 3Head
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/0e565ee552aa347095d28f97b94fd917.png", // peepoLeave
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/eafdf8b1a60d66b31fcc5a9978254fb3.png", // SadYoshi
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/b6a49ed5ba15dc65d3ff3aec7d40dcf0.png", // WalterSad
    // C GG
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/3761e2b0f3f94cf89acd4e6e716c4f1d.png", // fatchamp
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/de707d6be7b4f62eefa5786e8adfef29.png", // goat (snorlax)
    "https://www.geoguessr.com/_next/static/images/emote-gg-cf17a1f5d51d0ed53f01c65e941beb6d.png", // gg (geoguessr)
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/b6243820695b8cbc1e607032d112e147.png", // EZ
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/f33319dba2f509c198160ce60b376d75.png", // POGGERS
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/a6118294b632c4e8008e1aa103466d4b.png", // 5Head
    // D Happy
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/4ab1f42e6477b6c5d850990b68b544da.png", // rainhappy
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/b27b9dc5e1a6dac1cf5fa7b786ace65f.png", // walterSmile
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/146322b17475d3c8f7910abada7b03fb.png", // plonk it smiley~1
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/9755b5a2579bb9427d559fa1b38834d9.png", // pepega
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/989a83392ba73afd733a3aed150614bb.png", // ptryHype
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/8c0a4f30215337b7e866027df80a9bf1.png", // daiionPog
    // E Troll/Other
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/1152d340c895e3f6f59cdf4e58702761.png", // walter
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/1d4139111a68e7a6ee90d50d33b503b8.png", // icant
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/ae7762e0db1f2647591d83c77fa5be4f.png", // tf
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/10ac7eec3834d4a9c7f8d1e944e1576d.png", // OMEGALUL
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/86cd9813e062ab885f3b543a306fc1b2.png", // ari_solussion
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/36d52ad83223f1525f7d473de44b3608.png", // BatChest
    // F Just say hi
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/0b15cfb262dc4b3ada9b6d4b982b6761.png", // havrd7
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/5f2abc71b54e1ac792eb9f4132eabd82.png", // ptryHi
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/00728abf2c7e22bca6bb677b884fcab8.png", // guiriHey
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/512b59a103f6e7af8d906ae0cfe4070c.png", // daiionHype
    "https://www.geoguessr.com/images/auto/128/128/ce/0/plain/pin/5e136fcb5604913f13deb2e8dce721df.png", // kodiak36Hello
    "https://www.geoguessr.com/_next/static/images/emote-wave-da1dd3859051c109583d2f3cda5824f8.png" // wave (geoguessr)
];

const verbose = true;

function forceSendEmote(i) {
    // only if not already automatically sent in the last 75ms by geoguessr
    if (verbose) console.log("Force-sending emote "+i+": "+(Date.now() - lastSent.time >= 75));
    if (Date.now() - lastSent.time < 75) return;
    let token = location.pathname.split("/")[2];
    let emoteType = GGemoteTypes[i];
    fetch(`https://www.geoguessr.com/api/v4/reactions/${token}/emote:${emoteType}`, {
        method: 'POST', headers: {'Content-type': 'application/json; charset=UTF-8'}
    }).catch(err => {console.log(err);});
}

function findSelected(event) {
    let pos_x = event.pageX;
	let pos_y = event.pageY;
    let pageSizeX = document.querySelector('[class^=version3-in-game_layout__]').clientWidth;
    let pageSizeY = document.querySelector('[class^=version3-in-game_layout__]').clientHeight;
    let freeAccountBanner = document.querySelector('[class^=ticket-bar_root__]');
    if (freeAccountBanner) pageSizeY += freeAccountBanner.clientHeight;
    let angle = Math.atan2(pos_y-pageSizeY/2, pos_x-pageSizeX/2) * 180 / Math.PI;
    return Math.floor(((angle+210)/60))%6;
}

function openWheel() {
    let wheel = document.querySelector('[class^=emote-wheel_root__]');
    if (!wheel) {
        document.querySelector('[class^=emote-button_button__]').click();
    }
    onMutation(); // not detected by the observer so we call onMutation manually
}

function showEmoteFolder(selected) {
    let emotes_wheel = document.querySelectorAll('div[class^=emote-wheel_emote__]');
    fixEmoteWheelZoom();
    for (let i = 0; i < emotes_wheel.length; i++) {
        emotes_wheel[i].firstChild.src = (selected < 0) ? customEmotes6[i] : customEmotes36[6 * selected + i];
        if (selected < 0) emotes_wheel[i].style.scale = 1.9;
    }
}

function onWheelClick1(event) {
    setTimeout(() => {
        if (verbose) console.log("onWheelClick1: time ..."+(Date.now()%100000)/1000);
        if (Date.now() - lastSent.time > 100) selected = -2;
        if (selected != -2) {
            selected = lastSent.index;//findSelected(event);
        }
        if (verbose) console.log("Selected on wheel click 1: " + ((selected == -2) ? "none" : selected));
        if (selected != -2) {
            openWheel();
            showEmoteFolder(selected);
            wheel_state = 2;
        }
    }, 50);
}

function onWheelClick2(event) {
    setTimeout(() => {
        if (verbose) console.log("onWheelClick2: time ..."+(Date.now()%100000)/1000);
        if (selected != -2) {
            let selected2 = findSelected(event);
            selected = selected * 6 + selected2;
            forceSendEmote(selected2);
        }
        if (verbose) console.log("Selected on wheel click 2: " + ((selected == -2) ? "none" : selected));
    }, 50);
}

let wheel_state = 1;
let selected = -1;
function onMutation(mutation) {
    let wheel = document.querySelector('[class^=emote-wheel_root__]');
    if (wheel) {
        if (wheel_state == 1) {
            selected = -1;
            showEmoteFolder(selected);
            wheel.onclick = (event) => onWheelClick1(event);
        } else { // wheel_state == 2
            showEmoteFolder(selected);
            let closeButton = document.querySelector('[class^=emote-wheel_closeButton__]');
            if (closeButton) {
                closeButton.remove();
            }
            wheel.onclick = (event) => onWheelClick2(event);
        }
    } else {
        wheel_state = 1;
    }
}
let wheelObserver = new MutationObserver(onMutation);

let lastEmote = {}
function checkIncomingEmotes() {
    let incomingEmotesList = document.querySelectorAll('[class^=emote-section_emoteContainer__]');
    let toRemove = [];
    let toCheck = []
    for (let i = incomingEmotesList.length-1; i >= 0; i--) {
        let incomingEmote = incomingEmotesList[i];
        let classList = incomingEmote.classList;
        if (!classList.contains("emoteContainer_checked")) {
            let emoteImg = incomingEmote.firstChild.children[1];
            let sender = incomingEmote.firstChild.children[2].firstChild.firstChild.firstChild.firstChild.firstChild;
            let t = Date.now();
            if (sender.src == null) continue; // We need to know who it comes from. Wait until the animation reveals it.
            toCheck.push(incomingEmote);
            if (lastEmote[sender.src] != null && t-lastEmote[sender.src].time < 4000) {
                let combination = lastEmote[sender.src].src + emoteImg.src;
                let index36 = GGemotesIndex[combination];
                emoteImg.src = customEmotes36[index36];
                lastEmote[sender.src] = null;
                if (verbose) console.log("Left drawer: 2nd emote by "+sender.src);
            } else {
                lastEmote[sender.src] = {src: emoteImg.src, time: t};
                toRemove.push(incomingEmote);
                if (verbose) console.log("Left drawer: 1st emote by "+sender.src);
            }
        }
    }
    for (let incomingEmote of toRemove) {
        //incomingEmote.style.scale = 0.8;
        incomingEmote.style.visibility = "hidden"; // only hide it (geo needs to delete it by itself)
    }
    for (let incomingEmote of toCheck) {
        let classList = incomingEmote.classList;
        classList.add("emoteContainer_checked");
    }
}

let lastSent = {src: null, index: -1, time: 0};
let original_fetch = unsafeWindow.fetch;
unsafeWindow.fetch = async (url, init) => {
    if (typeof url === 'string' || url instanceof String) {
        if (url.includes('api/v4/reactions')) {
            let reactType = GGemotesIndex[url.split(":")[2]];
            lastSent = {src: GGemotes[reactType], index: reactType, time: Date.now()}
            if (verbose) console.log("Detected sent emote: type "+reactType+", time ..."+(lastSent.time%100000)/1000);
        }
    }
    let response = await original_fetch(url, init);
    return response;
};


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

setInterval(checkIncomingEmotes, 100);