· last year · Oct 26, 2023, 07:05 AM
1// ==UserScript==
2// @name Geohub Country Streaks United
3// @version 0.3.0
4// @author Jupaoqq
5// @license MIT
6// @description A modification to the geoguessr country streaks united created by Jupaoqq at: https://greasyfork.org/en/scripts/453710-geoguessr-country-streaks-united
7// @match https://www.geohub.gg/*
8// @icon https://www.geohub.gg/favicon.ico
9// ==/UserScript==
10
11
12
13// Credits: victheturtle, subsymmetry, slashP, emilyapocalypse
14
15// ------------------------------------------------- MUST READ BELOW -------------------------------------------------
16
17let ENABLED_ON_CHALLENGES = false; // Replace with true or false
18let API_Key = ''; // Replace ENTER_API_KEY_HERE with your API key (so keep the quote marks)
19let AUTOMATIC = true; // Replace with false for a manual counter. Without an API key, the counter will still be manual
20
21// Map number: e.g. Capitals of the World (Bing Satellite [20]), link https://www.geoguessr.com/maps/62062fcf0f38ba000190be65, has map number of 62062fcf0f38ba000190be65.
22
23/**
24 * Manually Save Locations:
25 * Press the z key (or change it to any other key) or click "save location" to save the location into a map of yours.
26 *
27 * You may replace manualKey with any key on your keyboard (by default it's key z).
28 * e.g. do "let manualKey = 'x'; " will remap the key to x instead.
29 * Press this key to save this location when the round result appears.
30 *
31 * You must replace MAP_LINK_HERE with your map number.
32 * e.g. do "let manualSave = "61a189a5531c7c4d38a6ae1"; " will save locations to map https://www.geoguessr.com/maps/61a189a5531c7c4d38a6ae1
33 * Such map must contain at least 5 unique locations.
34 *
35 */
36
37let manualSave = "MAP_LINK_HERE";
38let manualKey = 'z';
39
40// --------------------------------------------------------------------------------------------------------------------
41
42
43/**
44 * Advanced Options
45 */
46
47// More than one option below may be set to true, and multiple values may use the same map number.
48
49/**
50 * goodGuesses:
51 * For locations that you guessed the country correctly and received more points than the cutoff specified below.
52 *
53 * Replace MAP_LINK_HERE with your map number, e.g. do "let goodGuesses = "61a189a5531c7c4d38a6ae1"; "
54 * Such map must contain at least 5 unique locations.
55 *
56 * To turn in on, do "let collectGoodGuesses = true;" To turn it off, do "let collectGoodGuesses = false;"
57 * To change cutoff, do e.g. "let cutOffGood = 3500;" so every score higher than 3500 points (and you have to guess the country correctly) goes to this map.)
58 */
59
60let goodGuesses = "MAP_LINK_HERE";
61let collectGoodGuesses = false;
62let cutOffGood = 4000;
63
64/**
65 * okGuesses:
66 * For locations that you guessed the country correctly and received less points than the cutoff specified below.
67 *
68 * Replace MAP_LINK_HERE with your map number, e.g. do "let okGuesses = "61a189a5531c7c4d38a6ae1"; "
69 * Such map must contain at least 5 unique locations.
70 *
71 * To turn in on, do "let collectOkGuesses = true;" To turn it off, do "let collectOkGuesses = false;"
72 * To change cutoff, do e.g. "let cutOffOk = 3500;" so every score lower than 3500 points (and you have to guess the country correctly) goes to this map.)
73 */
74
75let okGuesses = "MAP_LINK_HERE";
76let collectOkGuesses = false;
77let cutoffOk = 4000;
78
79/**
80 * badGuesses:
81 * For locations that you guessed the country incorrectly.
82 *
83 * Replace MAP_LINK_HERE with your map number, e.g. do "let badGuesses = "61a189a5531c7c4d38a6ae1"; "
84 * Such map must contain at least 5 unique locations.
85 *
86 * To turn in on, do "let collectBadGuesses = true;" To turn it off, do "let collectBadGuesses = false;"
87 */
88
89let badGuesses = "MAP_LINK_HERE";
90let collectBadGuesses = false;
91
92/**
93 * GoodText: shows this text in result screen if you guess the country correctly with score exceeding your desired cutoff score.
94 * OkText: shows this text in result screen if you guess the country correctly with score below your desired cutoff score.
95 * BadText: shows this text in result screen if you guess the country incorrectly.
96 * SaveText: shows this text in result screen if you manually saved the location.
97 * defaultText: shows this text in result screen to remind you the manual option.
98 * All of these fields are customizable, you may replace it with your custom text.
99 */
100
101let GoodText = "Location has been saved to your Good Guesses Map.";
102let OkText = "Location has been saved to your Ok Guesses Map.";
103let BadText = "Location has been saved to your Bad Guesses Map.";
104let SaveText = "Location has been manually saved to your Map.";
105let defaultText = "";
106
107// Do not need to modify any code below.
108
109let global_loc;
110let LOC_SAVE = "save loc";
111
112if (sessionStorage.getItem("Streak") == null) {
113 sessionStorage.setItem("Streak", 0);
114};
115if (sessionStorage.getItem("StreakBackup") == null) {
116 sessionStorage.setItem("StreakBackup", 0);
117};
118if (sessionStorage.getItem("Checked") == null) {
119 sessionStorage.setItem("Checked", 0);
120};
121
122let streak = parseInt(sessionStorage.getItem("Streak"), 10);
123let last_guess = [0,0];
124const ERROR_RESP = -1000000;
125
126var CountryDict = {
127 AF: 'AF',
128 AX: 'FI', // Aland Islands
129 AL: 'AL',
130 DZ: 'DZ',
131 AS: 'US', // American Samoa
132 AD: 'AD',
133 AO: 'AO',
134 AI: 'GB', // Anguilla
135 AQ: 'AQ', // Antarctica
136 AG: 'AG',
137 AR: 'AR',
138 AM: 'AM',
139 AW: 'NL', // Aruba
140 AU: 'AU',
141 AT: 'AT',
142 AZ: 'AZ',
143 BS: 'BS',
144 BH: 'BH',
145 BD: 'BD',
146 BB: 'BB',
147 BY: 'BY',
148 BE: 'BE',
149 BZ: 'BZ',
150 BJ: 'BJ',
151 BM: 'GB', // Bermuda
152 BT: 'BT',
153 BO: 'BO',
154 BQ: 'NL', // Bonaire, Sint Eustatius, Saba
155 BA: 'BA',
156 BW: 'BW',
157 BV: 'NO', // Bouvet Island
158 BR: 'BR',
159 IO: 'GB', // British Indian Ocean Territory
160 BN: 'BN',
161 BG: 'BG',
162 BF: 'BF',
163 BI: 'BI',
164 KH: 'KH',
165 CM: 'CM',
166 CA: 'CA',
167 CV: 'CV',
168 KY: 'UK', // Cayman Islands
169 CF: 'CF',
170 TD: 'TD',
171 CL: 'CL',
172 CN: 'CN',
173 CX: 'AU', // Christmas Islands
174 CC: 'AU', // Cocos (Keeling) Islands
175 CO: 'CO',
176 KM: 'KM',
177 CG: 'CG',
178 CD: 'CD',
179 CK: 'NZ', // Cook Islands
180 CR: 'CR',
181 CI: 'CI',
182 HR: 'HR',
183 CU: 'CU',
184 CW: 'NL', // Curacao
185 CY: 'CY',
186 CZ: 'CZ',
187 DK: 'DK',
188 DJ: 'DJ',
189 DM: 'DM',
190 DO: 'DO',
191 EC: 'EC',
192 EG: 'EG',
193 SV: 'SV',
194 GQ: 'GQ',
195 ER: 'ER',
196 EE: 'EE',
197 ET: 'ET',
198 FK: 'GB', // Falkland Islands
199 FO: 'DK', // Faroe Islands
200 FJ: 'FJ',
201 FI: 'FI',
202 FR: 'FR',
203 GF: 'FR', // French Guiana
204 PF: 'FR', // French Polynesia
205 TF: 'FR', // French Southern Territories
206 GA: 'GA',
207 GM: 'GM',
208 GE: 'GE',
209 DE: 'DE',
210 GH: 'GH',
211 GI: 'UK', // Gibraltar
212 GR: 'GR',
213 GL: 'DK', // Greenland
214 GD: 'GD',
215 GP: 'FR', // Guadeloupe
216 GU: 'US', // Guam
217 GT: 'GT',
218 GG: 'GB', // Guernsey
219 GN: 'GN',
220 GW: 'GW',
221 GY: 'GY',
222 HT: 'HT',
223 HM: 'AU', // Heard Island and McDonald Islands
224 VA: 'VA',
225 HN: 'HN',
226 HK: 'CN', // Hong Kong
227 HU: 'HU',
228 IS: 'IS',
229 IN: 'IN',
230 ID: 'ID',
231 IR: 'IR',
232 IQ: 'IQ',
233 IE: 'IE',
234 IM: 'GB', // Isle of Man
235 IL: 'IL',
236 IT: 'IT',
237 JM: 'JM',
238 JP: 'JP',
239 JE: 'GB', // Jersey
240 JO: 'JO',
241 KZ: 'KZ',
242 KE: 'KE',
243 KI: 'KI',
244 KR: 'KR',
245 KW: 'KW',
246 KG: 'KG',
247 LA: 'LA',
248 LV: 'LV',
249 LB: 'LB',
250 LS: 'LS',
251 LR: 'LR',
252 LY: 'LY',
253 LI: 'LI',
254 LT: 'LT',
255 LU: 'LU',
256 MO: 'CN', // Macao
257 MK: 'MK',
258 MG: 'MG',
259 MW: 'MW',
260 MY: 'MY',
261 MV: 'MV',
262 ML: 'ML',
263 MT: 'MT',
264 MH: 'MH',
265 MQ: 'FR', // Martinique
266 MR: 'MR',
267 MU: 'MU',
268 YT: 'FR', // Mayotte
269 MX: 'MX',
270 FM: 'FM',
271 MD: 'MD',
272 MC: 'MC',
273 MN: 'MN',
274 ME: 'ME',
275 MS: 'GB', // Montserrat
276 MA: 'MA',
277 MZ: 'MZ',
278 MM: 'MM',
279 NA: 'NA',
280 NR: 'NR',
281 NP: 'NP',
282 NL: 'NL',
283 AN: 'NL', // Netherlands Antilles
284 NC: 'FR', // New Caledonia
285 NZ: 'NZ',
286 NI: 'NI',
287 NE: 'NE',
288 NG: 'NG',
289 NU: 'NZ', // Niue
290 NF: 'AU', // Norfolk Island
291 MP: 'US', // Northern Mariana Islands
292 NO: 'NO',
293 OM: 'OM',
294 PK: 'PK',
295 PW: 'PW',
296 PS: 'IL', // Palestine
297 PA: 'PA',
298 PG: 'PG',
299 PY: 'PY',
300 PE: 'PE',
301 PH: 'PH',
302 PN: 'GB', // Pitcairn
303 PL: 'PL',
304 PT: 'PT',
305 PR: 'US', // Puerto Rico
306 QA: 'QA',
307 RE: 'FR', // Reunion
308 RO: 'RO',
309 RU: 'RU',
310 RW: 'RW',
311 BL: 'FR', // Saint Barthelemy
312 SH: 'GB', // Saint Helena
313 KN: 'KN',
314 LC: 'LC',
315 MF: 'FR', // Saint Martin
316 PM: 'FR', // Saint Pierre and Miquelon
317 VC: 'VC',
318 WS: 'WS',
319 SM: 'SM',
320 ST: 'ST',
321 SA: 'SA',
322 SN: 'SN',
323 RS: 'RS',
324 SC: 'SC',
325 SL: 'SL',
326 SG: 'SG',
327 SX: 'NL', // Sint Maarten
328 SK: 'SK',
329 SI: 'SI',
330 SB: 'SB',
331 SO: 'SO',
332 ZA: 'ZA',
333 GS: 'GB', // South Georgia and the South Sandwich Islands
334 ES: 'ES',
335 LK: 'LK',
336 SD: 'SD',
337 SR: 'SR',
338 SJ: 'NO', // Svalbard and Jan Mayen
339 SZ: 'SZ',
340 SE: 'SE',
341 CH: 'CH',
342 SY: 'SY',
343 TW: 'TW', // Taiwan
344 TJ: 'TJ',
345 TZ: 'TZ',
346 TH: 'TH',
347 TL: 'TL',
348 TG: 'TG',
349 TK: 'NZ', // Tokelau
350 TO: 'TO',
351 TT: 'TT',
352 TN: 'TN',
353 TR: 'TR',
354 TM: 'TM',
355 TC: 'GB', // Turcs and Caicos Islands
356 TV: 'TV',
357 UG: 'UG',
358 UA: 'UA',
359 AE: 'AE',
360 GB: 'GB',
361 US: 'US',
362 UM: 'US', // US Minor Outlying Islands
363 UY: 'UY',
364 UZ: 'UZ',
365 VU: 'VU',
366 VE: 'VE',
367 VN: 'VN',
368 VG: 'GB', // British Virgin Islands
369 VI: 'US', // US Virgin Islands
370 WF: 'FR', // Wallis and Futuna
371 EH: 'MA', // Western Sahara
372 YE: 'YE',
373 ZM: 'ZM',
374 ZW: 'ZW'
375};
376
377function hex2a(hexx) {
378 var hex = hexx.toString();
379 var str = '';
380 for (var i = 0; i < hex.length; i += 2)
381 {
382 str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
383 }
384 return str;
385}
386
387
388if (AUTOMATIC && (API_Key.length <= 24 || API_Key.match("^[a-fA-F0-9_]*$") == null)) {
389 AUTOMATIC = false;
390};
391
392function checkGameMode() {
393 return (location.pathname.startsWith("/game/") || (ENABLED_ON_CHALLENGES && location.pathname.startsWith("/challenge/")));
394};
395
396let _cndic = {};
397function cn(classNameStart) { // cn("status_section__") -> "status_section__8uP8o"
398 let memorized = _cndic[classNameStart];
399 if (memorized != null) return memorized;
400 let selected = document.querySelector(`div[class*="${classNameStart}"]`);
401 if (selected == null) return classNameStart;
402 for (let className of selected.classList) {
403 if (className.startsWith(classNameStart)) {
404 _cndic[classNameStart] = className;
405 return className;
406 }
407 }
408}
409
410function geoguessrStyle(number) {
411 return `<div class="${cn("guess-description-distance_distanceLabel__")}">
412 <div class="${cn("slanted-wrapper_root__")} ${cn("slanted-wrapper_variantWhiteTransparent__")} ${cn("slanted-wrapper_roundnessSmall__")}">
413 <div class="${cn("slanted-wrapper_start__")} ${cn("slanted-wrapper_right__")}"></div>
414 <div class="${cn("guess-description-distance_distanceValue__")}">${number}</div>
415 <div class="${cn("slanted-wrapper_end__")} ${cn("slanted-wrapper_right__")}"></div>
416 </div>
417 </div>`;
418};
419
420function addCounter() {
421 if (!checkGameMode()) {
422 return;
423 };
424 /*
425 let status_length = document.getElementsByClassName(cn("status_section__")).length;
426 if (document.getElementById("country-streak") == null && status_length >= 3) {
427 let position = (status_length >= 4 && document.getElementsByClassName(cn("status_label__"))[3].innerText == "TIME LEFT") ? 4 : 3;
428 let newDiv0 = document.createElement("div");
429 newDiv0.className = cn('status_section__');
430 let statusBar = document.getElementsByClassName(cn("status_inner__"))[0];
431 statusBar.insertBefore(newDiv0, statusBar.children[position]);
432 newDiv0.innerHTML = `<div class="${cn("status_label__")}">Streak</div>
433 <div id="country-streak" class="${cn("status_value__")}">${streak}</div>`;
434 };
435 */
436
437 if (document.getElementById("country-streak") == null) {
438 let mapNameElement = document.getElementsByClassName("mapName")[0];
439 if (mapNameElement) {
440 let newDiv0 = document.createElement("div");
441 newDiv0.className = "infoSection";
442
443
444 let statusBar = mapNameElement.parentElement;
445
446 statusBar.insertBefore(newDiv0, mapNameElement);
447 newDiv0.innerHTML = `<div class="label"><span>Streak</span></div><div class="value"><span id="country-streak">${streak}</span></div>`;
448 }
449 }
450};
451
452function addStreakRoundResult() {
453 console.log("addstreakresult");
454 const wrapper = document.querySelector('div[class*="pointsWrapper"]');
455 if (document.getElementById("country-streak2") == null && !!wrapper && !document.querySelector('div[class="buttons-wrapper"]')) {
456 console.log("round result");
457 let pageProps = JSON.parse(document.getElementById("__NEXT_DATA__").innerHTML).props.pageProps;
458 if (pageProps.gamePlayedByCurrentUser != null && pageProps.gamePlayedByCurrentUser.mode == "streak") return;
459 let newDiv = document.createElement("div");
460
461 wrapper.parentElement.insertBefore(newDiv, wrapper.parentElement.children[3]);
462
463 //wrapper.parentElement.appendChild(newDiv);
464 //document.querySelector('div[data-qa="guess-description"]').appendChild(newDiv);
465 newDiv.innerHTML = `<div id="country-streak2" style="text-align:center;margin-top:10px;"><h2><i>Country Streak: ${streak}</i></h2></div>`;
466 };
467};
468
469function addStreakGameSummary() {
470 if (document.getElementById("country-streak2") == null && !!document.querySelector('div[class="buttons-wrapper"]')) {
471 console.log("game summary");
472 let newDiv = document.createElement("div");
473 //let progressSection = document.getElementsByClassName(cn("standard-final-result_progressSection__"))[0];
474 const wrapper = document.querySelector('div[class*="pointsWrapper"]');
475 wrapper.parentElement.insertBefore(newDiv, wrapper.parentElement.children[2]);
476
477 // progressSection.parentNode.insertBefore(newDiv, progressSection.parentNode.children[2]);
478 // progressSection.style.marginTop = "10px";
479 // progressSection.style.marginBottom = "10px";
480 newDiv.innerHTML = `<div id="country-streak2" style="text-align:center;margin-top:10px;"><h2><i>Country Streak: ${streak}</i></h2></div>`;
481 };
482};
483
484function updateStreak(newStreak, cond, guessType) {
485 if (newStreak === LOC_SAVE) {
486 if (document.getElementById("country-streak2") != null && (!!document.querySelector('div[data-qa="guess-description"]'))) {
487 document.getElementById("country-streak2").innerHTML = SaveText;
488 }
489 return;
490 }
491 else if (newStreak === ERROR_RESP) {
492 if (document.getElementById("country-streak2") != null && (!!document.querySelector('div[data-qa="guess-description"]'))) {
493 document.getElementById("country-streak2").innerHTML =
494 `<div><i>Country codes could not be fetched. If your API key is new, it should activate soon.</i></div>
495 <div><i>Check for typos in the API key. You might also see this message if bigdatacloud is down</i></div>
496 <div><i>or in the unlikely event that you have exceeded you quota limit of 50,000 requests.</i></div>
497 <div><i>In the meantime, you can press 1 to count the country as correct, or press 0 otherwise.</i></div>`;
498 }
499 return;
500 }
501 sessionStorage.setItem("Streak", newStreak);
502 console.log(newStreak);
503 if (!(streak > 0 && newStreak == 0)) {
504 sessionStorage.setItem("StreakBackup", newStreak);
505 };
506 if (document.getElementById("country-streak") != null) {
507 document.getElementById("country-streak").innerHTML = newStreak;
508 };
509 if (document.getElementById("country-streak2") != null
510 && (!!document.querySelector('div[data-qa="guess-description"]') || !!document.querySelector('div[class*="standard-final-result_section__"]'))) {
511
512 let moreText1 = "";
513 let moreText2 = "";
514 if (collectGoodGuesses && guessType === "PERFECT")
515 {
516 moreText1 = GoodText;
517 }
518
519 else if (collectOkGuesses && guessType === "BAD")
520 {
521 moreText1 = OkText;
522 }
523
524 if (collectBadGuesses && guessType === "MISS")
525 {
526 moreText2 = BadText;
527 }
528
529 if (manualSave !== "MAP_LINK_HERE")
530 {
531 defaultText = `You may press the ${manualKey} key on your keyboard to save this location.`
532 }
533
534 document.getElementById("country-streak2").innerHTML = `<h2><i>Country Streak: ${newStreak}</i></h2> <br> ${defaultText} <br> ${moreText1}`;
535 if (newStreak == 0 && !cond) {
536 if (streak >= 2) {
537 document.getElementById("country-streak2").innerHTML = `<h2><i>Country Streak: 0</i></h2>
538 Your streak ended after correctly guessing ${geoguessrStyle(streak)} countries in a row. <br> ${defaultText} <br> ${moreText2}`;
539 } else if (streak == 1) {
540 document.getElementById("country-streak2").innerHTML = `<h2><i>Country Streak: 0</i></h2>
541 Your streak ended after correctly guessing ${geoguessrStyle(1)} country. <br> ${defaultText} <br> ${moreText2}`;
542 }
543 else {
544 document.getElementById("country-streak2").innerHTML = `<br><h2><i>Country Streak: 0</i></h2>
545 Your streak ended after correctly guessing ${geoguessrStyle(0)} country. <br> ${defaultText} <br> ${moreText2}`;
546 };
547 };
548 };
549 streak = newStreak;
550};
551
552async function getUserAsync(coords) {
553 if (coords[0] <= -85.05) {
554 return 'AQ';
555 };
556 let api = "https://api.bigdatacloud.net/data/reverse-geocode?latitude="+coords[0]+"&longitude="+coords[1]+"&localityLanguage=en&key="+API_Key
557 let response = await fetch(api)
558 .then(res => (res.status !== 200) ? ERROR_RESP : res.json())
559 .then(out => (out === ERROR_RESP) ? ERROR_RESP : CountryDict[out.countryCode]);
560 return response;
561};
562
563function check() {
564 console.log("checking");
565 const game_tag = window.location.href.substring(window.location.href.lastIndexOf('/') + 1)
566 let api_url = ""
567 if (location.pathname.startsWith("/game/")) {
568 api_url = "https://geo-hub.vercel.app/api/games/"+game_tag;
569 } else if (location.pathname.startsWith("/challenge/")) {
570 api_url = "https://geo-hub.vercel.app/api/challenges/"+game_tag;
571 };
572 fetch(api_url)
573 .then(res => res.json())
574 .then((out) => {
575 let guess_counter = out.game.guesses.length;
576 let guess = [out.game.guesses[guess_counter-1].lat,out.game.guesses[guess_counter-1].lng];
577 if (guess[0] == last_guess[0] && guess[1] == last_guess[1]) {
578 return;
579 };
580 last_guess = guess;
581 let round = [out.game.rounds[guess_counter-1].lat,out.game.rounds[guess_counter-1].lng];
582 global_loc = out.game.rounds[guess_counter-1];
583 getUserAsync(guess)
584 .then(gue => {
585 getUserAsync(round)
586 .then(loc => {
587 if (loc == ERROR_RESP || gue == ERROR_RESP) {
588 updateStreak(ERROR_RESP, true, "");
589 } else if (loc == gue) {
590 let passStr = "";
591 if (out.game.guesses[guess_counter-1].points < cutoffOk)
592 {
593 if (collectOkGuesses && okGuesses !== "MAP_LINK_HERE")
594 {
595 toMap(global_loc, "BAD");
596 passStr = "BAD";
597 }
598 }
599 if (out.game.guesses[guess_counter-1].points > cutOffGood)
600 {
601 if (collectGoodGuesses && goodGuesses !== "MAP_LINK_HERE")
602 {
603 toMap(global_loc, "PERFECT");
604 passStr = "PERFECT";
605 }
606 }
607 updateStreak(streak + 1, true, passStr);
608 } else {
609 updateStreak(0, false, "MISS");
610 if (collectBadGuesses && badGuesses !== "MAP_LINK_HERE")
611 {
612 toMap(global_loc, "MISS");
613 }
614 };
615 });
616 });
617 }).catch(err => { throw err });
618};
619
620function doCheck() {
621 console.log("doCheck");
622 if (!document.querySelector('div[class*="pointsWrapper"]')) {
623 sessionStorage.setItem("Checked", 0);
624 } else if (sessionStorage.getItem("Checked") == 0) {
625 check();
626 sessionStorage.setItem("Checked", 1);
627 }
628};
629
630function tryAddCounter() {
631 addCounter();
632 for (let timeout of [400,1200,2000,3000,4000]) {
633 if (document.getElementsByClassName(cn("status_section__")).length == 0) {
634 setTimeout(addCounter, timeout);
635 };
636 }
637};
638
639function tryAddCounterOnRefresh() {
640 setTimeout(addCounter, 50);
641 setTimeout(addCounter, 300);
642};
643
644function tryAddStreak() {
645 if (!checkGameMode()) {
646 return;
647 };
648 if (AUTOMATIC) {
649 doCheck();
650 for (let timeout of [250,500,1200,2000]) {
651 setTimeout(doCheck, timeout);
652 }
653 };
654 for (let timeout of [250,500,1200,2000]) {
655 setTimeout(addStreakRoundResult, timeout);
656 setTimeout(addStreakGameSummary, timeout);
657 }
658};
659
660document.addEventListener('keypress', (e) => {
661 let streakBackup = parseInt(sessionStorage.getItem("StreakBackup"), 10);
662 switch (e.key) {
663 case '1':
664 updateStreak(streak + 1, true, "");
665 break;
666 case '2':
667 updateStreak(streak - 1, true, "");
668 break;
669 case '8':
670 updateStreak(streakBackup + 1, true, "");
671 break;
672 case manualKey:
673 toMap(global_loc, "SAVE");
674 updateStreak(LOC_SAVE, true, "");
675 break;
676 case '0':
677 updateStreak(0, true, "");
678 sessionStorage.setItem("StreakBackup", 0);
679 };
680});
681
682document.addEventListener('click', tryAddCounter, false);
683document.addEventListener('click', tryAddStreak, false);
684document.addEventListener('keyup', (e) => { if (e.key === " ") { tryAddStreak(); } });
685document.addEventListener('load', tryAddCounterOnRefresh(), false);
686
687function toMap(loc, type)
688{
689 let coordinates = [];
690 let pId;
691 if (loc.panoId)
692 {
693 pId = hex2a(loc.panoId);
694 }
695 const coordinate = {
696 heading: loc.heading,
697 pitch: loc.pitch,
698 zoom: loc.zoom,
699 panoId: pId,
700 countryCode: loc.streakLocationCode || null,
701 stateCode: null,
702 lat: loc.lat,
703 lng: loc.lng
704 };
705 coordinates.push(coordinate);
706
707
708 const mapText = JSON.stringify({
709 customCoordinates: coordinates
710 });
711 importLocations(mapText, type);
712}
713
714let mapDataFromClipboard = null;
715let existingMap = null;
716
717const getExistingMapData = (type) => {
718 let mId;
719 if (type == "PERFECT")
720 {
721 mId = goodGuesses;
722 }
723 else if (type == "BAD")
724 {
725 mId = okGuesses;
726 }
727 else if (type == "MISS")
728 {
729 mId = badGuesses;
730 }
731 else if (type == "SAVE")
732 {
733 mId = manualSave;
734 }
735 return fetch(`https://www.geoguessr.com/api/v3/profiles/maps/${mId}`)
736 .then(response => response.json())
737 .then(map => ({
738 id: map.id,
739 name: map.name,
740 description: map.description,
741 avatar: map.avatar,
742 highlighted: map.highlighted,
743 published: map.published,
744 customCoordinates: map.customCoordinates
745 }));
746}
747const uniqueBy = (arr, selector) => {
748 const flags = {};
749 return arr.filter(entry => {
750 if (flags[selector(entry)]) {
751 return false;
752 }
753 flags[selector(entry)] = true;
754 return true;
755 });
756};
757const intersectionCount = (arr1, arr2, selector) => {
758 var setB = new Set(arr2.map(selector));
759 var intersection = arr1.map(selector).filter(x => setB.has(x));
760 return intersection.length;
761}
762const exceptCount = (arr1, arr2, selector) => {
763 var setB = new Set(arr2.map(selector));
764 var except = arr1.map(selector).filter(x => !setB.has(x));
765 return except.length;
766}
767const latLngSelector = x => `${x.lat},${x.lng}`;
768const latLngHeadingPitchSelector = x => `${x.lat},${x.lng},${x.heading},${x.pitch}`;
769const pluralize = (text, count) => count === 1 ? text : text + "s";
770
771const importLocations = (text, type, mapAsObject) => {
772 try {
773 getExistingMapData(type)
774 .then(map => {
775 existingMap = map;
776 mapDataFromClipboard = mapAsObject ? mapAsObject : JSON.parse(text);
777 if (!mapDataFromClipboard?.customCoordinates?.length) {
778 return;
779 }
780 const uniqueExistingLocations = uniqueBy(existingMap.customCoordinates, latLngSelector);
781 const uniqueImportedLocations = uniqueBy(mapDataFromClipboard.customCoordinates, latLngSelector);
782 const uniqueLocations = uniqueBy([...uniqueExistingLocations, ...uniqueImportedLocations], latLngSelector);
783 const numberOfLocationsBeingAdded = uniqueLocations.length - uniqueExistingLocations.length;
784 const numberOfUniqueLocationsImported = uniqueImportedLocations.length;
785 const numberOfExactlyMatchingLocations = intersectionCount(uniqueExistingLocations, uniqueImportedLocations, latLngHeadingPitchSelector);
786 const numberOfLocationsWithSameLatLng = intersectionCount(uniqueExistingLocations, uniqueImportedLocations, latLngSelector);
787 const numberOfLocationEditions = numberOfLocationsWithSameLatLng - numberOfExactlyMatchingLocations;
788 const numberOfLocationsNotInImportedList = exceptCount(uniqueExistingLocations, uniqueImportedLocations, latLngSelector);
789 const numberOfLocationsNotInExistingMap = exceptCount(uniqueImportedLocations, uniqueExistingLocations, latLngSelector);
790
791 const uniqueLocations2 = uniqueBy([...existingMap.customCoordinates, ...mapDataFromClipboard.customCoordinates], latLngSelector);
792 const newMap = {
793 ...existingMap,
794 customCoordinates: uniqueLocations2
795 };
796 updateMap(newMap);
797
798 }).catch(error => console.log(error));
799 } catch (err) {
800 console.log(err);
801 }
802}
803
804
805function updateMap(newMap) {
806 fetch(`https://www.geoguessr.com/api/v4/user-maps/drafts/${existingMap.id}`, {
807 method: 'PUT',
808 credentials: 'same-origin',
809 headers: {
810 'Content-Type': 'application/json'
811 },
812 body: JSON.stringify(newMap)
813 }).then(response => {
814 if (!response.ok) {
815 console.log("Something went wrong when calling the server.");
816 return;
817 }
818 return response.json();
819 }).then(mapResponse => {
820 if (mapResponse.id) {
821 console.log(`Map updated.`);
822 }
823 });
824 fetch(`https://www.geoguessr.com/api/v3/profiles/maps/${existingMap.id}`, {
825 method: 'POST',
826 credentials: 'same-origin',
827 headers: {
828 'Content-Type': 'application/json'
829 },
830 body: JSON.stringify(newMap)
831 }).then(response => {
832 if (!response.ok) {
833 console.log("Something went wrong when calling the server.");
834 return;
835 }
836 return response.json();
837 }).then(mapResponse => {
838 if (mapResponse.id) {
839 console.log(`Map updated.`);
840 }
841 });
842}