// ==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);