Geoguessr Resolver Release

Features: Automatically score 5000 Points | Score randomly between 4500 and 5000 points | Open in Google Maps | See enemy guess Distance

当前为 2023-09-14 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Geoguessr Resolver Release
  3. // @namespace http://tampermonkey.net/
  4. // @version 10
  5. // @description Features: Automatically score 5000 Points | Score randomly between 4500 and 5000 points | Open in Google Maps | See enemy guess Distance
  6. // @author 0x978
  7. // @match https://www.geoguessr.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=geoguessr.com
  9. // @grant GM_webRequest
  10. // ==/UserScript==
  11.  
  12. window.alert = function (message) { // Devs tried to overwrite alert to detect script. I had already stopped using alert, but i'd rather they didn't override this anyway.
  13. nativeAlert(message)
  14. };
  15.  
  16. const originalFetch = window.fetch;
  17. window.fetch = function (url, options) {
  18. if (url === "https://www.geoguessr.com/api/v4/cd0d1298-a3aa-4bd0-be09-ccf513ad14b1") { // devs using this endpoint for Anticheat. Block all calls to it.
  19. return
  20. }
  21. return originalFetch.call(this, url, options);
  22. };
  23.  
  24. async function getAddress(lat, lon) {
  25. const response = await fetch(`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=json`)
  26. return await response.json();
  27. }
  28.  
  29. function displayLocationInfo() {
  30. const coordinates = coordinateClimber()
  31. // api call with the lat lon to retrieve data.
  32. getAddress(coordinates[0], coordinates[1]).then(data => {
  33. alert(`
  34. Country: ${data.address.country}
  35. County: ${data.address.county}
  36. City: ${data.address.city}
  37. Road: ${data.address.road}
  38. State: ${data.address.state}
  39. Postcode: ${data.address.postcode}
  40. Village/Suburb: ${(data.address.village || data.address.suburb)}
  41.  
  42. Postal Address: ${data.display_name}
  43. `)
  44. });
  45.  
  46. }
  47.  
  48. function placeMarker(safeMode, skipGet, coords) {
  49. const isStreaks = document.getElementsByClassName("guess-map__canvas-container")[0] === undefined
  50. let location = skipGet ? coords : coordinateClimber(isStreaks)
  51. if (isStreaks) {
  52. placeMarkerStreaksMode(location)
  53. return;
  54. }
  55. let [lat, lng] = location
  56.  
  57. if (safeMode) {
  58. const sway = [Math.random() > 0.5,Math.random() > 0.5]
  59. const multiplier = Math.random() * 4
  60. const horizontalAmount = Math.random() * multiplier
  61. const verticalAmount = Math.random() * multiplier
  62. sway[0] ? lat += verticalAmount : lat -= verticalAmount
  63. sway[1] ? lng += horizontalAmount : lat -= horizontalAmount
  64. }
  65.  
  66. const element = document.getElementsByClassName("guess-map__canvas-container")[0] // html element containing needed props.
  67. const keys = Object.keys(element) // all keys
  68. const key = keys.find(key => key.startsWith("__reactFiber$")) // the React key I need to access props
  69. const place = element[key].return.memoizedProps.onMarkerLocationChanged // getting the function which will allow me to place a marker on the map
  70.  
  71. flag = false;
  72. place({lat: lat, lng: lng}) // placing the marker on the map at the correct coordinates given by getCoordinates(). Must be passed as an Object.
  73. toggleClick(({lat: lat, lng: lng}))
  74. displayDistanceFromCorrect({lat: lat, lng: lng})
  75. injectOverride()
  76. }
  77.  
  78. function placeMarkerStreaksMode(code) {
  79. let element = document.getElementsByClassName("region-map_map__5e4h8")[0] // this map is unique to streaks mode, however, is similar to that found in normal modes.
  80. if(!element){
  81. element = document.getElementsByClassName("region-map_map__7jxcD")[0]
  82. }
  83. const keys = Object.keys(element)
  84. const reactKey = keys.find(key => key.startsWith("__reactFiber$"))
  85. const placeMarkerFunction = element[reactKey].return.memoizedProps.onRegionSelected // This map works by selecting regions, not exact coordinates on a map, which is handles by the "onRegionSelected" function.
  86.  
  87. if(typeof code !== "string"){
  88. let [lat,lng] = code
  89. getAddress(lat, lng).then(data => { // using API to retrieve the country code at the current coordinates.
  90. const countryCode = data.address.country_code
  91. placeMarkerFunction(countryCode) // passing the country code into the onRegionSelected method.
  92. })
  93. return
  94. }
  95.  
  96. placeMarkerFunction(code)
  97.  
  98. }
  99.  
  100. function coordinateClimber(isStreaks){
  101. let timeout = 10
  102. let path = document.querySelector('div[data-qa="panorama"]');
  103. while (timeout > 0){
  104. const props = path[Object.keys(path).find(key => key.startsWith("__reactFiber$"))]
  105. const checkReturns = iterateReturns(props,isStreaks)
  106. if(checkReturns){
  107. return checkReturns
  108. }
  109. path = path.parentNode
  110. timeout--
  111. }
  112. alert("Failed to find co-ordinates. Please make an issue on GitHub or GreasyFork. " +
  113. "Please make sure you mention the game mode in your report.")
  114. }
  115.  
  116. function iterateReturns(element,isStreaks){
  117. let timeout = 10
  118. let path = element
  119. while(timeout > 0){
  120. const coords = checkProps(path.memoizedProps,isStreaks)
  121. if(coords){
  122. return coords
  123. }
  124. path = element.return
  125. timeout--
  126. }
  127. }
  128.  
  129. function checkProps(props,isStreaks){
  130. if(props?.panoramaRef){
  131. const found = props.panoramaRef.current.location.latLng
  132. return [found.lat(),found.lng()]
  133. }
  134. if(props.streakLocationCode && isStreaks){
  135. return props.streakLocationCode
  136. }
  137. if(props.lat){
  138. return [props.lat,props.lng]
  139. }
  140. }
  141.  
  142. function mapsFromCoords() { // opens new Google Maps location using coords.
  143. const [lat, lon] = coordinateClimber()
  144. if (!lat || !lon) {
  145. return;
  146. }
  147. window.open(`https://www.google.com/maps/place/${lat},${lon}`);
  148. }
  149.  
  150. function getGuessDistance(manual) {
  151. const coords = coordinateClimber()
  152. const clat = coords[0] * (Math.PI / 180)
  153. const clng = coords[1] * (Math.PI / 180)
  154. const y = document.getElementsByClassName("guess-map__canvas-container")[0]
  155. const keys = Object.keys(y)
  156. const key = keys.find(key => key.startsWith("__reactFiber$"))
  157. const props = y[key]
  158. const user = manual ?? props.return.memoizedProps.markers[0]
  159. if (!coords || !user) {
  160. return null
  161. }
  162. const ulat = user.lat * (Math.PI / 180)
  163. const ulng = user.lng * (Math.PI / 180)
  164.  
  165. const distance = Math.acos(Math.sin(clat) * Math.sin(ulat) + Math.cos(clat) * Math.cos(ulat) * Math.cos(ulng - clng)) * 6371
  166. return distance
  167. }
  168.  
  169. function displayDistanceFromCorrect(manual) {
  170. let unRoundedDistance = getGuessDistance(manual) // need unrounded distance for precise calculations later.
  171. let distance = Math.round(unRoundedDistance)
  172. if (distance === null) {
  173. return
  174. }
  175. let text = `${distance} km (${Math.round(distance * 0.621371)} miles)`
  176. setGuessButtonText(text)
  177. }
  178.  
  179. function setGuessButtonText(text) {
  180. let x = document.querySelector('button[data-qa="perform-guess"]');
  181. if(!x){
  182. return null}
  183. x.innerText = text
  184. }
  185.  
  186. function toggleClick(coords) { // prevents user from making 5k guess to prevent bans.
  187. const disableSpaceBar = (e) => {
  188. if (e.keyCode === 32) {
  189. const distance = getGuessDistance()
  190. if ((distance < 1 || isNaN(distance)) && !flag) {
  191. e.stopImmediatePropagation();
  192. preventedActionPopup()
  193. document.removeEventListener("keyup", disableSpaceBar);
  194. flag = true
  195. }
  196. }
  197. };
  198. document.addEventListener("keyup", disableSpaceBar);
  199. setTimeout(() => {
  200. const distance = getGuessDistance()
  201. if ((distance < 1 || isNaN(distance)) && !flag) {
  202. let old = document.getElementsByClassName("button_button__CnARx button_variantPrimary__xc8Hp")[0][Object.keys(document.getElementsByClassName("button_button__CnARx button_variantPrimary__xc8Hp")[0])[1]].onClick
  203. document.getElementsByClassName("button_button__CnARx button_variantPrimary__xc8Hp")[0][Object.keys(document.getElementsByClassName("button_button__CnARx button_variantPrimary__xc8Hp")[0])[1]].onClick = (() => {
  204. flag = true
  205. preventedActionPopup()
  206. document.getElementsByClassName("button_button__CnARx button_variantPrimary__xc8Hp")[0][Object.keys(document.getElementsByClassName("button_button__CnARx button_variantPrimary__xc8Hp")[0])[1]].onClick = (() => old())
  207. })
  208. }
  209. }, 500)
  210. }
  211.  
  212. function preventedActionPopup() {
  213. alert(`Geoguessr Resolver has prevented you from making a perfect guess.
  214.  
  215. Making perfect guesses will very likely result in a ban from competitive.
  216.  
  217. Press "guess" again to proceed anyway.`)
  218. }
  219.  
  220. function injectOverride() {
  221. document.getElementsByClassName("guess-map__canvas-container")[0].onpointermove = (() => { // this is called wayyyyy too many times (thousands) but fixes a lot of issues over using onClick.
  222. displayDistanceFromCorrect()
  223. })
  224. }
  225.  
  226. function getBRGuesses() {
  227. let gameRoot = document.getElementsByClassName("game_root__2vV1H")[0]
  228. if(!gameRoot){
  229. return null
  230. }
  231. let keys = gameRoot[Object.keys(document.getElementsByClassName("game_root__2vV1H")[0])[0]]
  232. let gameState = keys.return.memoizedProps.gameState
  233. let players = gameState.players
  234. let bestGuessDistance = Number.MAX_SAFE_INTEGER
  235. players.forEach(player => {
  236. let currGuess = player.coordinateGuesses[player.coordinateGuesses.length - 1]
  237. if(currGuess){
  238. let currDistance = currGuess.distance
  239. if ((currDistance < bestGuessDistance) && currGuess.roundNumber === gameState.currentRoundNumber && player.playerId !== gameRoot.return.memoizedProps.currentPlayer.playerId) {
  240. bestGuessDistance = currDistance
  241. }
  242. }
  243. })
  244. if (bestGuessDistance === Number.MAX_SAFE_INTEGER) {
  245. return null;
  246. }
  247. return Math.round(bestGuessDistance / 1000)
  248. }
  249.  
  250. function displayBRGuesses(){
  251. const distance = getBRGuesses()
  252. if (distance === null) {
  253. alert("There have been no guesses this round")
  254. return;
  255. }
  256. alert(`The best guess this round is ${distance} km from the correct location. (Not including your guess)`)
  257. }
  258.  
  259. function setInnerText(){
  260. const text = `
  261. Geoguessr Resolver Loaded Successfully
  262.  
  263. IMPORTANT GEOGUESSR RESOLVER UPDATE INFORMATION: https://text.0x978.com/geoGuessr
  264.  
  265. Please read the above update to GeoGuessr anticheat
  266. `
  267. if(document.getElementsByClassName("header_logo__vV0HK")[0]){
  268. document.getElementsByClassName("header_logo__vV0HK")[0].innerText = text
  269. }
  270. }
  271.  
  272. GM_webRequest([
  273. { selector: 'https://www.geoguessr.com/api/v4/trails', action: 'cancel' },
  274. ]);
  275.  
  276. let onKeyDown = (e) => {
  277. if (e.keyCode === 49) {
  278. placeMarker(true, false, undefined)
  279. }
  280. if (e.keyCode === 50) {
  281. placeMarker(false, false, undefined)
  282. }
  283. if (e.keyCode === 51) {
  284. displayLocationInfo()
  285. }
  286. if (e.keyCode === 52) {
  287. mapsFromCoords()
  288. }
  289. if (e.keyCode === 53) {
  290. displayBRGuesses()
  291. }
  292. }
  293. setInnerText()
  294. document.addEventListener("keydown", onKeyDown);
  295. let flag = false
  296.