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