Internet Roadtrip: Dark Souls Location Popup

Show Dark Souls style location name popup on https://neal.fun/internet-roadtrip/

// ==UserScript==
// @name         Internet Roadtrip: Dark Souls Location Popup
// @namespace    http://tampermonkey.net/
// @version      1.1.2
// @author       joawatte19
// @description  Show Dark Souls style location name popup on https://neal.fun/internet-roadtrip/
// @match        https://neal.fun/internet-roadtrip/
// @license      MIT
// @grant        none
// @run-at       document-end
// @require      https://cdn.jsdelivr.net/npm/[email protected]
// ==/UserScript==

(async () => {
  if (!IRF?.isInternetRoadtrip) return;

  const container = await IRF.vdom.container;
  const originalUpdateData = container.methods.updateData;

  let lastLoc = {};

  container.state.updateData = new Proxy(originalUpdateData, {
    apply: (target, thisArg, args) => {
      const result = Reflect.apply(target, thisArg, args);

      const loc = container.state.currentLocation;
      const fields = ["neighborhood", "county", "state", "country"];

      let changedField = null;
      for (const field of fields) {
        if (loc?.[field] && loc[field] !== lastLoc[field]) {
          changedField = field;
          break;
        }
      }

      const changedValue = changedField ? loc[changedField] : null;
      lastLoc = { ...loc };

      if (changedValue) {
        showPopup(changedValue);
      }

      return result;
    }
  });

  function showPopup(text) {
    const existing = document.getElementById("ds-location-popup");
    if (existing) existing.remove();

    const overlay = document.createElement("div");
    overlay.id = "ds-overlay";
    Object.assign(overlay.style, {
      position: "fixed",
      top: "0",
      left: "0",
      width: "100vw",
      height: "100vh",
      backgroundColor: "#000",
      opacity: "0",
      zIndex: 9998,
      pointerEvents: "none",
      transition: "opacity 1.2s ease-in-out",
    });
    document.body.appendChild(overlay);

    requestAnimationFrame(() => {
      overlay.style.opacity = "0.4";
    });

    const popup = document.createElement("div");
    popup.id = "ds-location-popup";
    popup.textContent = text;

    Object.assign(popup.style, {
      position: "fixed",
      top: "40%",
      left: "50%",
      transform: "translate(-50%, -50%) scale(0.95)",
      color: "#e3e2e0",
      fontFamily: "adobe-garamond-pro, Georgia, serif",
      fontSize: "4rem",
      whiteSpace: "nowrap",
      maxWidth: "90vw",
      userSelect: "none",
      zIndex: 9999,
      opacity: "0",
      textAlign: "center",
      lineHeight: "1.2",
      textShadow: "0 0 8px rgba(0,0,0,0.6), 0 0 2px #ccc",
      transition: "opacity 1.2s ease-in-out, transform 1.2s ease-in-out",
      pointerEvents: "none",
      padding: "1rem",
      paddingBottom: "0.05rem",
      borderBottom: "4px solid rgba(227,226,224,0.5)",
    });

    document.body.appendChild(popup);

    // play sound
    const audio = new Audio("https://media.vocaroo.com/mp3/1f7euqkLrXJs");
    audio.volume = 0.8;
    audio.play().catch(() => {});

    // fade in
    requestAnimationFrame(() => {
      popup.style.opacity = "1";
      popup.style.transform = "translate(-50%, -50%) scale(1)";
    });

    // fade out
    setTimeout(() => {
      popup.style.opacity = "0";
      popup.style.transform = "translate(-50%, -50%) scale(1.05)";
      overlay.style.opacity = "0";
      setTimeout(() => {
        popup.remove();
        overlay.remove();
      }, 1500);
    }, 3000);
  }

  // manual trigger with console
  window.showDSLocationPopup = () => {
    const loc = container.state.currentLocation;
    if (!loc) {
      console.warn("No location data available.");
      return;
    }

    const fields = ["neighborhood", "county", "state", "country"];
    for (const field of fields) {
      if (loc[field]) {
        showPopup(loc[field]);
        break;
      }
    }
  };
})();