GeoGuessr High Level Ranks

Replace 1500+ levels with special ranks

当前为 2025-08-27 提交的版本,查看 最新版本

// ==UserScript==
// @name         GeoGuessr High Level Ranks
// @version      1.2
// @description  Replace 1500+ levels with special ranks
// @match        https://www.geoguessr.com/*
// @icon         https://i.imgur.com/wHQjX4m.png
// @license      MIT
// @run-at       document-idle
// @grant        GM_addStyle
// @namespace https://example.com/
// ==/UserScript==

(function () {
  'use strict';

  // --------------------

  const RECOLOR_TOGGLE = "on";   // CHANGE THIS: "on" = Enable Rank Background, "off" = Disable Rank Background

  // --------------------

  const BADGES_DIVISION = [
    { min: 1500, max: 1649, url: 'https://i.imgur.com/aR6fova.png' },
    { min: 1650, max: 1799, url: 'https://i.imgur.com/No26QT6.png' },
    { min: 1800, max: 1999, url: 'https://i.imgur.com/DH3XBSr.png' },
    { min: 2000, max: 2199, url: 'https://i.imgur.com/mTCZKHg.png' },
    { min: 2200, max: Infinity, url: 'https://i.imgur.com/wHQjX4m.png' },
  ];

  const BADGES_MULTIPLAYER = [...BADGES_DIVISION];

  const TITLES = [
    { min: 1500, max: 1649, label: 'Grand Champion 3' },
    { min: 1650, max: 1799, label: 'Grand Champion 2' },
    { min: 1800, max: 1999, label: 'Grand Champion 1' },
    { min: 2000, max: 2199, label: 'Legend' },
    { min: 2200, max: Infinity, label: 'Eternal' },
  ];

  // --------------------
  // Utilities
  // --------------------
  function extractFirstInteger(text) {
    if (!text) return null;
    const cleaned = String(text).replace(/,/g, '').trim();
    const m = cleaned.match(/(\d{2,5})/);
    if (!m) return null;
    const n = parseInt(m[1], 10);
    return Number.isFinite(n) ? n : null;
  }

  function pickForRating(arr, rating) {
    if (rating == null) return null;
    for (const e of arr) {
      if (rating >= e.min && rating <= e.max) return e.url || e.label || null;
    }
    return null;
  }

  function pickTitleForRating(rating) {
    if (rating == null) return null;
    for (const t of TITLES) {
      if (rating >= t.min && rating <= t.max) return t.label;
    }
    return null;
  }

  // --------------------
  // Header recolor
  // --------------------
  function recolorHeader(rating) {
    if (RECOLOR_TOGGLE.toLowerCase() !== "on") return; // disabled

    let background = null;
    let overlay = null;
    let overlayOpacity = 1.0;

    if (rating >= 1500 && rating <= 1999) {
      background = "linear-gradient(179deg, #8b0000 -3.95%, #ff0000 95.2%)"; // red
      overlay    = "linear-gradient(41deg, #330613, #bf1755)";
      overlayOpacity = 0.75;
    } else if (rating >= 2000 && rating <= 2199) {
      background = "linear-gradient(179deg, #b8860b -3.95%, #ffd700 95.2%)"; // gold
      overlay    = "linear-gradient(41deg, #2b1900, #d68940)";
      overlayOpacity = 0.8;
    } else if (rating >= 2200) {
      background = "linear-gradient(179deg, #ffdee3 -3.95%, #ffdbe2 95.2%)"; // light pink
      overlay    = "linear-gradient(41deg, #5e4d5b, #c2089a)";
      overlayOpacity = 0.65;
    }

    if (background && overlay) {
      GM_addStyle(`
        .division-header_background__1gzPy {
          background: ${background} !important;
        }
        .division-header_pattern__VYb42::before {
          opacity: 0 !important;
        }
        .division-header_overlay__ohEaU {
          background: ${overlay} !important;
          opacity: ${overlayOpacity} !important;
        }
      `);
    }
  }

  // --------------------
  // Division area (page 1)
  // --------------------
  function updateDivisionArea() {
    const ratingEl = document.querySelector('.division-header_rating__CQOgo');
    const badgeEl = document.querySelector('.division-header_badge__i1zzd');
    const titleEl = document.querySelector('.division-header_title__3YYUS');

    if (!ratingEl) return false;

    const rating = extractFirstInteger(ratingEl.textContent || ratingEl.innerText || '');
    const badgeUrl = pickForRating(BADGES_DIVISION, rating);
    const titleStr = pickTitleForRating(rating);

    // Recolor header (only if toggle = "on")
    if (!isNaN(rating)) {
      recolorHeader(rating);
    }

    // Badge handling
    if (badgeEl) {
      if (!('origSrc' in badgeEl.dataset)) {
        badgeEl.dataset.origSrc = badgeEl.getAttribute('src') || '';
        badgeEl.dataset.origSrcset = badgeEl.getAttribute('srcset') || '';
      }
      if (!badgeUrl) {
        if (badgeEl.dataset.replaced === 'true') {
          if (badgeEl.dataset.origSrc) badgeEl.setAttribute('src', badgeEl.dataset.origSrc);
          if (badgeEl.dataset.origSrcset) badgeEl.setAttribute('srcset', badgeEl.dataset.origSrcset);
          else badgeEl.removeAttribute('srcset');
          delete badgeEl.dataset.replaced;
          console.log('[GG Division] Restored division badge (rating:', rating, ')');
        }
      } else {
        const cur = badgeEl.getAttribute('src') || '';
        if (!cur.includes(badgeUrl)) {
          badgeEl.setAttribute('src', badgeUrl);
          badgeEl.setAttribute('srcset', `${badgeUrl} 1x, ${badgeUrl} 2x`);
          badgeEl.dataset.replaced = 'true';
          console.log('[GG Division] Replaced division badge ->', badgeUrl, 'rating:', rating);
        }
      }
    }

    // Title handling
    if (titleEl) {
      if (!('origTitle' in titleEl.dataset)) {
        titleEl.dataset.origTitle = (titleEl.textContent || '').trim();
      }
      if (!titleStr) {
        if (titleEl.dataset.replacedTitle === 'true') {
          titleEl.textContent = titleEl.dataset.origTitle || '';
          delete titleEl.dataset.replacedTitle;
          console.log('[GG Division] Restored division title (rating:', rating, ')');
        }
      } else {
        const cur = (titleEl.textContent || '').trim();
        if (cur !== titleStr) {
          titleEl.textContent = titleStr;
          titleEl.dataset.replacedTitle = 'true';
          console.log('[GG Division] Set division title ->', titleStr, 'rating:', rating);
        }
      }
    }

    return true;
  }

  // --------------------
  // Multiplayer boxes (page 2) — unchanged
  // --------------------
  function findMultiplayerBoxes() {
    let boxes = Array.from(document.querySelectorAll('.multiplayer_ratingBox__05Gko'));
    if (boxes.length) return boxes;
    const root = document.querySelector('.multiplayer_root__jmpXA');
    if (root) {
      const candidates = Array.from(root.querySelectorAll('div,section,article')).filter(el =>
        el.querySelector('label.shared_yellowVariant__XONv8')
      );
      boxes = candidates.filter((el, i, arr) => !arr.some(other => other !== el && other.contains(el)));
    }
    return boxes;
  }

  function updateMultiplayerBox(box, idx) {
    const ratingLabel = box.querySelector('label.shared_yellowVariant__XONv8');
    let titleLabel = box.querySelector('label[data-original-title]');
    if (!titleLabel) titleLabel = box.querySelector('label.label_label__9xkbh:not(.shared_yellowVariant__XONv8)');

    const imgEl = box.querySelector('img.multiplayer_icon__hRbEa') || box.querySelector('img');
    const rating = ratingLabel ? extractFirstInteger(ratingLabel.textContent || '') : null;
    const badgeUrl = pickForRating(BADGES_MULTIPLAYER, rating);
    const titleStr = pickTitleForRating(rating);

    // Image handling
    if (imgEl) {
      if (!('origSrc' in imgEl.dataset)) {
        imgEl.dataset.origSrc = imgEl.getAttribute('src') || '';
        imgEl.dataset.origSrcset = imgEl.getAttribute('srcset') || '';
      }
      if (!badgeUrl) {
        if (imgEl.dataset.replaced === 'true') {
          if (imgEl.dataset.origSrc) imgEl.setAttribute('src', imgEl.dataset.origSrc);
          if (imgEl.dataset.origSrcset) imgEl.setAttribute('srcset', imgEl.dataset.origSrcset);
          else imgEl.removeAttribute('srcset');
          delete imgEl.dataset.replaced;
          console.log('[GG MP] Restored image for box', idx, 'rating:', rating);
        }
      } else {
        const cur = imgEl.getAttribute('src') || '';
        if (!cur.includes(badgeUrl)) {
          imgEl.setAttribute('src', badgeUrl);
          imgEl.setAttribute('srcset', `${badgeUrl} 1x, ${badgeUrl} 2x`);
          imgEl.dataset.replaced = 'true';
          console.log('[GG MP] Replaced image for box', idx, '->', badgeUrl, 'rating:', rating);
        }
      }
    }

    // Title handling
    if (titleLabel) {
      if (!('origTitle' in titleLabel.dataset)) {
        titleLabel.dataset.origTitle = (titleLabel.textContent || '').trim();
        titleLabel.dataset.origDataOriginalTitle = titleLabel.getAttribute('data-original-title') || '';
      }
      if (!titleStr) {
        if (titleLabel.dataset.replacedTitle === 'true') {
          titleLabel.textContent = titleLabel.dataset.origTitle || '';
          if (titleLabel.dataset.origDataOriginalTitle) titleLabel.setAttribute('data-original-title', titleLabel.dataset.origDataOriginalTitle);
          else titleLabel.removeAttribute('data-original-title');
          delete titleLabel.dataset.replacedTitle;
          console.log('[GG MP] Restored title for box', idx, 'rating:', rating);
        }
      } else {
        const cur = (titleLabel.textContent || '').trim();
        if (cur !== titleStr) {
          titleLabel.textContent = titleStr;
          titleLabel.setAttribute('data-original-title', titleStr);
          titleLabel.dataset.replacedTitle = 'true';
          console.log('[GG MP] Set title for box', idx, '->', titleStr, 'rating:', rating);
        }
      }
    }
  }

  function updateMultiplayerAll() {
    const boxes = findMultiplayerBoxes();
    if (!boxes || !boxes.length) return false;
    boxes.forEach((b, i) => {
      try { updateMultiplayerBox(b, i); } catch (e) { console.error('updateMultiplayerBox error', e); }
    });
    return true;
  }

  // --------------------
  // Combined update
  // --------------------
  function updateAllOnce() {
    let changed = false;
    changed = updateDivisionArea() || changed;
    changed = updateMultiplayerAll() || changed;
    return changed;
  }

  // --------------------
  // Observer + debounce + fallback
  // --------------------
  let scheduled = null;
  function scheduleUpdate() {
    if (scheduled) return;
    scheduled = setTimeout(() => {
      scheduled = null;
      updateAllOnce();
    }, 150);
  }

  const observer = new MutationObserver((mutations) => {
    for (const m of mutations) {
      if (m.type === 'childList' && (m.addedNodes.length || m.removedNodes.length)) { scheduleUpdate(); break; }
      if (m.type === 'characterData') { scheduleUpdate(); break; }
      if (m.type === 'attributes') { scheduleUpdate(); break; }
    }
  });

  function startObserving() {
    if (!document.body) return;
    observer.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true });
    scheduleUpdate();
  }

  if (document.readyState === 'loading') {
    window.addEventListener('DOMContentLoaded', startObserving, { once: true });
  } else startObserving();

  const fallbackInterval = setInterval(() => updateAllOnce(), 5000);

  // cleanup
  window.addEventListener('beforeunload', () => {
    observer.disconnect();
    clearInterval(fallbackInterval);
    if (scheduled) clearTimeout(scheduled);
  });

  // initial run
  setTimeout(updateAllOnce, 300);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址