Greasy Fork

Greasy Fork is available in English.

Geoguessr 36 Modded Emotes

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

当前为 2022-10-09 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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    http://greasyfork.icu/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);