// ==UserScript==
// @name WG Cheat Panel
// @namespace wg-cheat-panel
// @version 1.0.0
// @description Clean cheat overlay for WorldGuessr — reads coords from Street View iframe, pins the location on the guess map
// @author RandomAccount
// @match https://www.worldguessr.com/*
// @license MIT
// @grant none
// @run-at document-idle
// @downloadURL https://update.greasyfork.icu/scripts/570414/WG%20Cheat%20Panel.user.js
// @updateURL https://update.greasyfork.icu/scripts/570414/WG%20Cheat%20Panel.meta.js
// ==/UserScript==
(() => {
"use strict";
// ─────────────────────────────────────────────
// State
// ─────────────────────────────────────────────
let lat = null;
let lng = null;
let panelOpen = false;
let isDragging = false;
let dragOffX = 0;
let dragOffY = 0;
let lastSrc = "";
let leafletMarker = null;
let pinActive = false;
// ─────────────────────────────────────────────
// Coord extraction — parses Google Maps iframe src
// ─────────────────────────────────────────────
function parseCoords(src) {
if (!src) return null;
let m;
m = src.match(/[?&]location=(-?\d+\.\d+),(-?\d+\.\d+)/);
if (m) return { lat: parseFloat(m[1]), lng: parseFloat(m[2]) };
m = src.match(/[?&]q=(-?\d+\.\d+),(-?\d+\.\d+)/);
if (m) return { lat: parseFloat(m[1]), lng: parseFloat(m[2]) };
m = src.match(/@(-?\d+\.\d+),(-?\d+\.\d+)/);
if (m) return { lat: parseFloat(m[1]), lng: parseFloat(m[2]) };
m = src.match(/!2d(-?\d+\.\d+)!3d(-?\d+\.\d+)/);
if (m) return { lat: parseFloat(m[2]), lng: parseFloat(m[1]) };
m = src.match(/cbll=(-?\d+\.\d+),(-?\d+\.\d+)/);
if (m) return { lat: parseFloat(m[1]), lng: parseFloat(m[2]) };
return null;
}
function findCoords() {
for (const iframe of document.querySelectorAll("iframe")) {
const src = iframe.src || iframe.getAttribute("src") || "";
if (!src.includes("google.com/maps")) continue;
const c = parseCoords(src);
if (c) return c;
}
return null;
}
// ─────────────────────────────────────────────
// Leaflet map finder
// Uses the same deep recursive scan as the
// original working script: walks object properties
// up to depth 4 looking for a Leaflet map instance
// whose _container matches the leaflet-container el.
// ─────────────────────────────────────────────
function isLeafletCandidate(obj, container) {
return Boolean(
obj &&
typeof obj === "object" &&
typeof obj.latLngToContainerPoint === "function" &&
typeof obj.containerPointToLatLng === "function" &&
typeof obj.getZoom === "function" &&
obj._container === container
);
}
function findLeafletMapForContainer(container) {
if (!container) return null;
const seen = new WeakSet();
function scan(obj, depth) {
if (!obj || typeof obj !== "object" || seen.has(obj) || depth < 0) return null;
seen.add(obj);
if (isLeafletCandidate(obj, container)) return obj;
let keys = [];
try { keys = Object.getOwnPropertyNames(obj); } catch { return null; }
for (const key of keys) {
if (key === "parentNode" || key === "children" || key === "childNodes") continue;
let val;
try { val = obj[key]; } catch { continue; }
if (!val || typeof val !== "object") continue;
if (isLeafletCandidate(val, container)) return val;
const found = scan(val, depth - 1);
if (found) return found;
}
return null;
}
// Search roots: the container itself, its parent, any #miniMapArea ancestor, then window
const roots = [
container,
container.parentElement,
container.closest("#miniMapArea"),
window,
];
for (const root of roots) {
if (!root) continue;
const found = scan(root, root === window ? 2 : 4);
if (found) return found;
}
return null;
}
function getLeafletMap() {
const containers = document.querySelectorAll(".leaflet-container");
for (const el of containers) {
const map = findLeafletMapForContainer(el);
if (map) return map;
}
return null;
}
// ─────────────────────────────────────────────
// Pin management
// ─────────────────────────────────────────────
// Extract the Leaflet constructor (L) from the map instance itself.
// WorldGuessr bundles Leaflet as a module so window.L is often undefined.
// But the map object's prototype chain leads back to L.Map, and L.Map
// has .addInitHook on it — we can walk up to find the L namespace by
// looking for divIcon / marker on the map's constructor exports,
// or we can use the map's own addLayer to place a raw marker directly.
function getLFromMap(map) {
if (window.L) return window.L;
// Try to get L from the map's options or internal refs
try {
// Leaflet attaches _leaflet_id to instances; the constructor is L.Map
// Walk the prototype to find the namespace
let proto = Object.getPrototypeOf(map);
while (proto) {
const ctor = proto.constructor;
if (ctor && ctor.version && typeof ctor.marker === "function") return ctor;
proto = Object.getPrototypeOf(proto);
}
} catch {}
return null;
}
function placePin(coordLat, coordLng) {
const map = getLeafletMap();
if (!map) return false;
// Remove existing marker
if (leafletMarker) {
try { map.removeLayer(leafletMarker); } catch {}
leafletMarker = null;
}
// Build the pin as a raw positioned div injected into the map's pane,
// positioned via Leaflet's own latLngToLayerPoint — no L namespace needed.
try {
const L = getLFromMap(map);
if (L) {
// Full Leaflet path — proper marker with custom icon
const icon = L.divIcon({
className: "",
html: `