Zed City Tools

Tools for Zed City

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Zed City Tools
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Tools for Zed City
// @author       Bot7420 & Mountain Dewd
// @match        https://www.zed.city/*
// @connect      api.zed.city
// @icon         https://www.zed.city/icons/favicon.svg
// @grant        unsafeWindow
// @grant        GM_notification
// @grant        GM_xmlhttpRequest
// @license      CC-BY-NC-SA-4.0
// ==/UserScript==

(() => {
    'use strict';

 /* START */

// 🎯 Hook into XMLHttpRequest to Modify API Calls (Only Declare Once)
if (!unsafeWindow.XMLHttpRequest.prototype._open_modified) {
    const originalOpen = XMLHttpRequest.prototype.open;
    unsafeWindow.XMLHttpRequest.prototype._open_modified = true;

    unsafeWindow.XMLHttpRequest.prototype.open = function () {
        this.addEventListener("readystatechange", function () {
            if (this.readyState === 4) {
                const url = this.responseURL;

                const apiHandlers = {
                    "api.zed.city/getStats": handleGetStats,
                    "api.zed.city/skills": handleSkills,
                    "api.zed.city/getStore?store_id=junk": handleGetStoreJunkLimit,
                    "api.zed.city/startJob": handleStartJob,
                    "api.zed.city/completeJob": handleCompleteJob,
                    "api.zed.city/getStronghold": handleGetStronghold,
                    "api.zed.city/getRadioTower": handleGetRadioTower,
                    "api.zed.city/getFactionNotifications": handleGetFactionNotifications,
                    "api.zed.city/doFight": handleDoFight,
                    "https://api.zed.city/getFaction": handleGetFaction
                };

                if (url.includes("api.zed.city/getFight") && !url.includes("api.zed.city/getFightLog")) {
                    handleGetFight(this.response);
                } else {
                    for (const [key, handler] of Object.entries(apiHandlers)) {
                        if (url.includes(key)) {
                            handler(this.response);
                            break;
                        }
                    }
                }
            }
        });

        return originalOpen.apply(this, arguments);
    };
}


// ✅ Function to fetch updated player stats from the API
function fetchStats() {
    console.log("🔄 Fetching fresh stats from API...");

    GM_xmlhttpRequest({
        method: "GET",
        url: "https://api.zed.city/getStats",
        headers: {
            "Accept": "application/json",
            "Referer": "https://www.zed.city/"
        },
        onload: function(response) {
            try {
                const data = JSON.parse(response.responseText);
                localStorage.setItem("script_getStats", response.responseText);
                handleGetStats(response.responseText);
            } catch (error) {
                console.error("❌ JSON Parse Error:", error);
            }
        },
        onerror: function(error) {
            console.error("❌ API Fetch Failed:", error);
        }
    });
}

// ✅ Function to observe UI stat changes and fetch updated stats
function observeStatChanges(statClass) {
    // FIX 1: Use backticks for querySelector
    let statBar = document.querySelector(`.q-linear-progress.${statClass}`);
    if (!statBar) return;

    let observer = new MutationObserver(() => {
        // FIX 2: Use backticks for console.log
        console.log(`🔄 ${statClass.toUpperCase()} Bar Changed - Fetching Fresh Stats...`);
        fetchStats(); // ✅ Ensure fresh API data is retrieved
    });

    observer.observe(statBar, { childList: true, subtree: true, characterData: true });
}

// ✅ Start observing changes in UI
observeStatChanges("energy");
observeStatChanges("rad");
observeStatChanges("morale");
observeStatChanges("life");



        // ────────────────────────────────────────────────
    // Timer Container Toggle & Helper Functions
    // ────────────────────────────────────────────────

    // Returns the container element to use for timers based on the user's setting.
    function getTimerContainer() {
      // If draggable timer is NOT disabled, use the draggable container.
      if (localStorage.getItem("script_timerDraggable") !== "false") {
        let container = document.getElementById("script_timer_container");
        if (!container) {
          container = document.createElement("div");
          container.id = "script_timer_container";
          document.body.appendChild(container);
          updateContainerStyle(); // your existing function to style the draggable container
        }
        container.style.display = "flex";
        return container;
      } else {
        // Otherwise, use (or create) the fixed top container.
        let fixed = document.getElementById("script_countdowns_container");
        if (!fixed) {
          fixed = document.createElement("div");
          fixed.id = "script_countdowns_container";
          Object.assign(fixed.style, {
            position: "fixed",
            top: "0px",
            left: "0px",
            width: "100%",
            background: "rgba(0, 0, 0, 0.7)",
            padding: "10px",
            zIndex: "9999",
            display: "flex",
            justifyContent: "center",
            gap: "15px"
          });
          document.body.appendChild(fixed);
        }
        fixed.style.display = "flex";
        return fixed;
      }
    }

    // Updates which container is visible based on the draggable setting.
    function updateTimerContainerBasedOnSetting() {
      const draggableEnabled = localStorage.getItem("script_timerDraggable") !== "false";
      const draggable = document.getElementById("script_timer_container");
      const fixed = document.getElementById("script_countdowns_container");
      if (draggableEnabled) {
        if (fixed) fixed.style.display = "none";
        if (draggable) draggable.style.display = "flex";
        else createTimerContainer(); // creates #script_timer_container and applies styles
      } else {
        if (draggable) draggable.style.display = "none";
        if (!fixed) {
          const fixedContainer = document.createElement("div");
          fixedContainer.id = "script_countdowns_container";
          Object.assign(fixedContainer.style, {
            position: "fixed",
            top: "0px",
            left: "0px",
            width: "100%",
            background: "rgba(0, 0, 0, 0.7)",
            padding: "10px",
            zIndex: "9999",
            display: "flex",
            justifyContent: "center",
            gap: "15px"
          });
          document.body.appendChild(fixedContainer);
        } else {
          fixed.style.display = "flex";
        }
      }
    }

    // Adds a new toggle control to the settings UI so users can choose whether the timer container is draggable.
    function addDraggableToggleSetting() {
      if (!document.querySelector(".script_timerDraggable_toggle")) {
        const container = document.createElement("div");
        container.className = "script_timerDraggable_toggle";
        container.style.margin = "30px";
        const checkbox = document.createElement("input");
        checkbox.type = "checkbox";
        // Default is draggable enabled; if the stored value is "false", then it is disabled.
        checkbox.checked = localStorage.getItem("script_timerDraggable") !== "false";
        checkbox.style.marginRight = "10px";
        const label = document.createElement("label");
        label.style.fontSize = "20px";
        label.textContent = checkbox.checked
          ? "Draggable Timer Container: Enabled"
          : "Draggable Timer Container: Disabled";
        checkbox.addEventListener("change", () => {
          const enabled = checkbox.checked;
          localStorage.setItem("script_timerDraggable", enabled ? "true" : "false");
          label.textContent = enabled
            ? "Draggable Timer Container: Enabled"
            : "Draggable Timer Container: Disabled";
          updateTimerContainerBasedOnSetting();
        });
        container.appendChild(checkbox);
        container.appendChild(label);
        const settingsPanel = document.getElementById("script_settingsPanel");
        if (settingsPanel) {
          settingsPanel.appendChild(container);
        }
      }
    }

    // ────────────────────────────────────────────────
    // End Timer Container Toggle Helpers
    // ────────────────────────────────────────────────




// 🎯 Handle Faction Data
function handleGetFaction(response) {
    try {
        const data = JSON.parse(response);
        if (data.faction?.name) {
            localStorage.setItem("script_faction_id", data.faction.name);
            console.log("Faction Name Stored:", data.faction.name);
        }
    } catch (error) {
        console.error("Error parsing faction data:", error);
    }
}

// 📌 Initialize Faction Log Storage
if (!localStorage.getItem("script_faction_raid_logs")) {
    localStorage.setItem("script_faction_raid_logs", JSON.stringify({}));
}
if (!localStorage.getItem("script_faction_log_records")) {
    localStorage.setItem("script_faction_log_records", JSON.stringify({}));
}

// 🛡️ Handle Faction Notifications (Raid Logs)
function handleGetFactionNotifications(response) {
    const data = JSON.parse(response);
    if (!data?.notify) return;

    const raidLogs = JSON.parse(localStorage.getItem("script_faction_raid_logs"));

    for (const log of data.notify) {
        if (log.type === "faction_raid" && log.data?.users) {
            raidLogs[log.date] = log;
        }
    }
    localStorage.setItem("script_faction_raid_logs", JSON.stringify(raidLogs));
    updateFactionLogRecord();
}

// 📜 Update Faction Log Records
function updateFactionLogRecord() {
    const raidLogs = JSON.parse(localStorage.getItem("script_faction_raid_logs"));
    const result = {};

    for (const key in raidLogs) {
        const log = raidLogs[key];
        if (log.type === "faction_raid" && log.data?.users) {
            for (const user of log.data.users) {
                const userId = user.id;
                if (userId && !result[userId]) {
                    result[userId] = {
                        playerId: userId,
                        playerNames: [user.username],
                        respectFromRaids: 0,
                        lastRaid: null,
                    };
                }
                result[userId].respectFromRaids += Number(log.data.respect) / log.data.users.length;
                if (!result[userId].lastRaid || result[userId].lastRaid.timestamp < Number(log.date)) {
                    result[userId].lastRaid = { timestamp: Number(log.date), raidName: log.data.name };
                }
            }
        }
    }
    localStorage.setItem("script_faction_log_records", JSON.stringify(result));
}

// 📊 Rank Members by Respect
function rankByRespect() {
    const records = JSON.parse(localStorage.getItem("script_faction_log_records"));
    return Object.values(records)
        .sort((a, b) => b.respectFromRaids - a.respectFromRaids)
        .map(record => `${record.playerNames[0]} total Respect: ${record.respectFromRaids.toFixed(1)}`)
        .join("\n");
}

// ⏳ Calculate Raid Timings
function raidTimings() {
    const records = JSON.parse(localStorage.getItem("script_faction_log_records"));
    const result = [];

    for (const key in records) {
        const record = records[key];
        if (record.lastRaid) {
            let nextRaidInSec = null;
            const raidTimes = {
                "Raid a Farm": 1 * 60 * 60,
                "Raid a Hospital": 5 * 60 * 60,
                "Raid a Store": 20 * 60 * 60,
            };
            nextRaidInSec = Math.floor(record.lastRaid.timestamp + raidTimes[record.lastRaid.raidName] - Date.now() / 1000);

            if (nextRaidInSec > -172800) {
                const playerName = record.playerNames[0];
                const isReady = nextRaidInSec <= 0;
                const statusText = isReady
                    ? `✅ ${playerName} - Ready to Raid`
                    : `⏳ ${playerName} - Next Raid: ${timeReadable(nextRaidInSec)}`;

                result.push({ statusText, nextRaidInSec });
            }
        }
    }

    return result.sort((a, b) => a.nextRaidInSec - b.nextRaidInSec).map(r => r.statusText).join("\n");
}

// 🏆 Faction Raid Logs UI
function addFactionLogSearch() {
    if (!window.location.href.includes("zed.city/faction/activity") || !document.querySelector("div.q-infinite-scroll")) {
        return;
    }

    const insertToElem = document.querySelector("div.q-infinite-scroll");
    const searchElem = document.querySelector("#script_faction_log_container");

    if (!searchElem) {
        const container = document.createElement("div");
        container.id = "script_faction_log_container";
        container.className = "zed-grid has-title has-content";
        container.style.margin = "20px 0";
        container.innerHTML = `
            <div class="title"><div>Faction Raid Logs</div></div>
            <div class="grid-cont">
                <div class="q-pa-md">
                    <button id="script_rankRespect" class="q-btn q-btn-item non-selectable no-outline">
                        <span class="q-btn__content text-center">Members Total Respect</span>
                    </button>
                    <button id="script_raidCooldown" class="q-btn q-btn-item non-selectable no-outline">
                        <span class="q-btn__content text-center">Raid Cooldown Check</span>
                    </button>
                    <button id="script_clearLogs" class="q-btn q-btn-item non-selectable no-outline">
                        <span class="q-btn__content text-center">Clear Local History</span>
                    </button>
                </div>
                <div id="script_faction_log_output" class="q-pa-md q-field row no-wrap items-start q-field--outlined q-field--dark q-field--dense"
                     style="height: 200px; overflow-y: auto; background: rgba(0, 0, 0, 0.7); padding: 10px; border-radius: 5px; color: white; font-size: 14px;">
                    Faction logs are stored locally. Use the buttons to query data.<br>(First time use, manually scroll the faction activity)
                </div>
            </div>
        `;

        insertToElem.parentNode.insertBefore(container, insertToElem);

        document.getElementById("script_rankRespect").addEventListener("click", () => {
            document.getElementById("script_faction_log_output").innerHTML = rankByRespect().replace(/\n/g, "<br>");
        });

        document.getElementById("script_raidCooldown").addEventListener("click", () => {
            document.getElementById("script_faction_log_output").innerHTML = raidTimings().replace(/\n/g, "<br>");
        });

        document.getElementById("script_clearLogs").addEventListener("click", () => {
            if (window.confirm("⚠️ Are you sure you want to clear all raid logs?")) {
                console.log("Faction log cleared.");
                document.getElementById("script_faction_log_output").innerHTML = "History has been cleared.";
                localStorage.setItem("script_faction_raid_logs", JSON.stringify({}));
                localStorage.setItem("script_faction_log_records", JSON.stringify({}));
            } else {
                console.log("Clear history action canceled.");
            }
        });
    }
}

// 🔄 Run Faction Log Search Every 0.5s
setInterval(addFactionLogSearch, 500);



// 🎯 Ensure necessary localStorage variables exist
const defaultLocalStorageValues = {
    script_getStats: "{}",
    script_playerXp_previous: 0,
    script_playerXp_current: 0,
    script_playerXp_max: 0,
    script_energyFullAtTimestamp: 0,
    script_radFullAtTimestamp: 0,
    script_moraleFullAtTimestamp: 0,
    script_lifeFullAtTimestamp: 0,
    script_energy: 0,
    script_vehicle_max_weight: 0,
    script_vehicle_weight: 0,
    script_weapon_durability_threshold: 50
};

for (const [key, value] of Object.entries(defaultLocalStorageValues)) {
    if (!localStorage.getItem(key)) {
        localStorage.setItem(key, value);
    }
}


// 🎯 Handle Player Statistics Update
function handleGetStats(response) {
    localStorage.setItem("script_getStats", response);
    const data = JSON.parse(response);

    // 🟢 Player Data Tracking
    localStorage.setItem("script_playerXp_current", data.experience);
    localStorage.setItem("script_playerXp_max", data.xp_end);
    localStorage.setItem("script_playerName", data.username);
    showPlayerXpChangePopup(data.experience);

    // 🛡️ Calculate & Store Total Battle Score (BS)
    const totalBS = Number(data.skills.strength) + Number(data.skills.speed) +
                    Number(data.skills.defense) + Number(data.skills.agility);
    localStorage.setItem("script_totalBS", totalBS);

    // ⏳ Raid Cooldown Tracking
    if (data.raid_cooldown) {
        const previousTimestamp = Number(localStorage.getItem("script_raidTimestamp"));
        const timestamp = Date.now() + data.raid_cooldown * 1000;
        localStorage.setItem("script_raidTimestamp", timestamp);
        if (timestamp - previousTimestamp > 30000) {
            localStorage.setItem("script_raidIsAlreadyNotified", false);
        }
    }

    // 🚗 Vehicle Weight Tracking
    if (data.vehicle?.vars) {
        localStorage.setItem("script_vehicle_max_weight", data.vehicle.vars.max_weight);
        localStorage.setItem("script_vehicle_weight", data.vehicle.vars.weight);
    }

    // ================================
    //  🟢 Morale Regen Calculation
    // ================================
   const currentMorale = data.morale;
const maxMorale = data.skills.max_morale;
const moraleRegenInterval = 300; // 5 minutes = 300 seconds
const moraleRegenAmount = 5; // Fixed regen amount per cycle
const timeUntilNextMoraleRegen = data.morale_regen || 0; // Time left until next regen tick

if (currentMorale >= maxMorale) {
    localStorage.setItem("script_moraleFullAtTimestamp", -1);
} else {
    const neededMorale = maxMorale - currentMorale;

    // Calculate how many full 5-morale regen cycles are needed
    const fullCyclesNeeded = Math.floor(neededMorale / moraleRegenAmount);

    // Check if there's any remaining morale to regenerate after full cycles
    const remainingMorale = neededMorale % moraleRegenAmount;

    // Calculate total time needed to fully regenerate morale
    let timeLeftSec = (fullCyclesNeeded * moraleRegenInterval) + timeUntilNextMoraleRegen;

    // Set timestamp for when morale will be full
    const previousTimestamp = Number(localStorage.getItem("script_moraleFullAtTimestamp"));
    const timestamp = Date.now() + timeLeftSec * 1000;
    localStorage.setItem("script_moraleFullAtTimestamp", timestamp);

    // If the new full morale timestamp is more than 30 seconds different from the previous one, reset the notification flag
    if (timestamp - previousTimestamp > 30000) {
        localStorage.setItem("script_moraleFullAlreadyNotified", false);
    }
}

  // ================================
//  🟢 Life Regen Calculation
// ================================
const currentLife = data.life;
const maxLife = data.skills.max_life;
const lifeRegenAmount = data.skills.life_regen || 40; // ✅ Dynamic regen per player
const lifeRegenInterval = 900; // ✅ 15 min = 900 sec
const timeUntilNextLifeRegen = data.life_regen || 0; // ✅ Countdown to next regen

if (currentLife >= maxLife) {
    localStorage.setItem("script_lifeFullAtTimestamp", -1);
} else {
    const neededLife = maxLife - currentLife;

    // ✅ Calculate full regen cycles needed
    const fullCyclesNeeded = Math.floor(neededLife / lifeRegenAmount);

    // ✅ Calculate total time needed for full regen
    let timeLeftSec = (fullCyclesNeeded * lifeRegenInterval) + timeUntilNextLifeRegen;

    // ✅ Store full life timestamp
    const previousTimestamp = Number(localStorage.getItem("script_lifeFullAtTimestamp"));
    const timestamp = Date.now() + timeLeftSec * 1000;
    localStorage.setItem("script_lifeFullAtTimestamp", timestamp);

    // ✅ Reset notification flag if the new timestamp differs by more than 30s
    if (timestamp - previousTimestamp > 30000) {
        localStorage.setItem("script_lifeFullAlreadyNotified", false);
    }
}

    // ================================
    //  🟢 Radiation Regen Calculation
    // ================================
    const currentRad = data.rad;
    const maxRad = data.skills.max_rad;
    const radRegen = data.rad_regen || 0;
    const radRegenInterval = 300; // 5 min (300 sec)

    localStorage.setItem("script_rad", currentRad);

    if (currentRad < maxRad) {
        const timeLeftSec = ((maxRad - currentRad - 1) / 1) * radRegenInterval + radRegen;
        const previousTimestamp = Number(localStorage.getItem("script_radFullAtTimestamp"));
        const timestamp = Date.now() + timeLeftSec * 1000;
        localStorage.setItem("script_radFullAtTimestamp", timestamp);
        if (timestamp - previousTimestamp > 30000) {
            localStorage.setItem("script_radFullAlreadyNotified", false);
        }
    } else {
        localStorage.setItem("script_radFullAtTimestamp", -1);
    }

    // ================================
    //  🟢 Energy Calculation
    // ================================
    const currentEnergy = data.energy;
    const energyRegenIntervalMinute = data.membership ? 10 : 15;
    const maxEnergy = data.skills.max_energy + (data.membership ? 50 : 0);
    const energyRegen = data.energy_regen || 0;

    localStorage.setItem("script_energy", currentEnergy);

    if (currentEnergy < maxEnergy) {
        const timeLeftSec = ((maxEnergy - currentEnergy - 5) / 5) * energyRegenIntervalMinute * 60 + energyRegen;
        const previousTimestamp = Number(localStorage.getItem("script_energyFullAtTimestamp"));
        const timestamp = Date.now() + timeLeftSec * 1000;
        localStorage.setItem("script_energyFullAtTimestamp", timestamp);
        if (timestamp - previousTimestamp > 30000) {
            localStorage.setItem("script_energyFullAlreadyNotified", false);
        }
    }
}



// 🎯 Handle Skill XP Updates
function handleSkills(response) {
    const parsedResponse = JSON.parse(response);
    showSkillsXpChangePopup(parsedResponse.player_skills);
}

// 📈 Display XP Increase for Skills
function showSkillsXpChangePopup(skillsXp) {
    const insertElem = document.body.querySelector("#script_player_level");
    if (!insertElem) return;

    const skillsXp_previous = JSON.parse(localStorage.getItem("script_skillsXp_previous"));

    if (skillsXp_previous && skillsXp) {
        for (const skill of skillsXp) {
            for (const prevSkill of skillsXp_previous) {
                if (prevSkill.name === skill.name && skill.xp !== prevSkill.xp) {
                    const increase = Number(skill.xp) - Number(prevSkill.xp);
                    const div = document.createElement("span");
                    div.style.backgroundColor = "#0A748F";
                    div.style.marginLeft = "10px";
                    div.textContent = `${skill.name} +${increase}`;
                    insertElem.appendChild(div);
                    setTimeout(() => div.remove(), 6000);
                    break;
                }
            }
        }
    }
    localStorage.setItem("script_skillsXp_previous", JSON.stringify(skillsXp));
}

// 🔼 Display XP Increase for Player Level
function showPlayerXpChangePopup(playerXp) {
    const insertElem = document.body.querySelector("#script_player_level");
    if (!insertElem) return;

    const playerXp_previous = Number(localStorage.getItem("script_playerXp_previous"));
    if (playerXp_previous !== 0 && playerXp_previous !== playerXp) {
        const increase = playerXp - playerXp_previous;
        const div = document.createElement("span");
        div.style.backgroundColor = "#2e7d32";
        div.style.marginLeft = "10px";
        div.textContent = `XP +${increase}`;
        insertElem.appendChild(div);
        setTimeout(() => div.remove(), 6000);
    }
    localStorage.setItem("script_playerXp_previous", JSON.stringify(playerXp));
}

// ⚡ Enable Level-Up Time Estimation If Not Already Set
if (!localStorage.getItem("script_estimate_levelup_time_switch")) {
    localStorage.setItem("script_estimate_levelup_time_switch", "enabled");
}

// 📊 Update Player XP Display
function updatePlayerXpDisplay() {
    const playerXp = Number(localStorage.getItem("script_playerXp_current"));
    const currentLevelMaxXP = Number(localStorage.getItem("script_playerXp_max"));
    const xpLeft = Math.floor(currentLevelMaxXP - playerXp); // ✅ Whole number XP left

    // 🎯 Locate the currency stats row (contains Money, Points, Level, and Booster)
    const statsContainer = document.querySelector(".currency-stats");
    if (!statsContainer) return; // Prevents errors if not found

    // 🎯 Locate the user level element (trophy icon + level number)
    const levelElem = statsContainer.querySelector(".stat-level");
    if (!levelElem) return;

    let xpLeftElem = document.querySelector("#script_player_xp_left");

    if (!xpLeftElem) {
        // Insert XP Left right after the user level
        levelElem.insertAdjacentHTML(
            "afterend",
            `<div id="script_player_xp_left" style="margin-left: 8px; color: white; font-size: 12px; display: inline-flex; align-items: center;">
                XP Left: <strong>${xpLeft}</strong>
            </div>`
        );
    } else {
        xpLeftElem.innerHTML = `XP Left: <strong>${xpLeft}</strong>`;
    }

   // ✅ Insert countdown container as the last child of the parent of `.currency-stats`
let countdownContainer = document.querySelector("#script_countdowns_container");
if (!countdownContainer) {
    // Here we use the parent of statsContainer so it appears below all its content
    statsContainer.parentElement.insertAdjacentHTML(
        "beforeend",
        `<div id="script_countdowns_container" style="display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 15px; margin-top: 10px;"></div>`
    );
}
}

// ⏳ Update XP Display Every 0.5 Seconds
setInterval(updatePlayerXpDisplay, 500);




// 🔋 ENERGY TIMER
function updateEnergyDisplay() {
    const energyBar = document.querySelector(".q-linear-progress.energy");
    if (!energyBar) return;

    const energyFullAtTimestamp = Number(localStorage.getItem("script_energyFullAtTimestamp"));
    let timeLeftSec = Math.floor((energyFullAtTimestamp - Date.now()) / 1000);
    let energyElem = document.querySelector("#script_energyBar");

    const displayText = (energyFullAtTimestamp === -1 || timeLeftSec <= 0)
        ? "<span style='color: #28a745;'>FULL</span>"
        : timeReadable(timeLeftSec);

    if (!energyElem) {
        energyBar.insertAdjacentHTML(
            "afterend",
            `<div id="script_energyBar" class="script-timer" style="
                cursor: pointer; text-align: center; background: rgba(0, 0, 0, 0.7);
                padding: 5px; border-radius: 5px; color: white; font-size: 14px; margin-top: 5px;">
                Energy ${displayText}
            </div>`
        );

        // 🏋️ Navigate to Gym on Click
        document.querySelector("#script_energyBar").addEventListener("click", () => {
            const gymID = localStorage.getItem("script_stronghold_id_gym");
            if (gymID) {
                history.pushState(null, null, `https://www.zed.city/stronghold/${gymID}`);
                history.pushState(null, null, `https://www.zed.city/stronghold/${gymID}`);
                history.go(-1);
            } else {
                console.warn("❌ No Gym ID Found in LocalStorage.");
            }
        });

    } else {
        energyElem.innerHTML = `Energy ${displayText}`;
    }
}

// ⏳ Update Energy Display Every 0.5s
setInterval(updateEnergyDisplay, 500);


// ☢️ RAD TIMER FUNCTION (Accurately Tracks API Timing)
function updateRadDisplay() {
    const radBar = document.querySelector(".q-linear-progress.rad");
    if (!radBar) return;

    const radFullAtTimestamp = Number(localStorage.getItem("script_radFullAtTimestamp"));
    let timeLeftSec = Math.floor((radFullAtTimestamp - Date.now()) / 1000);
    let radElem = document.querySelector("#script_radBar");

    const displayText = (radFullAtTimestamp === -1 || timeLeftSec <= 0)
        ? "<span style='color: #28a745;'>FULL</span>"
        : timeReadable(timeLeftSec);

    if (!radElem) {
        radBar.insertAdjacentHTML(
            "afterend",
            `<div id="script_radBar" class="script-timer" style="
                cursor: pointer; text-align: center; background: rgba(0, 0, 0, 0.7);
                padding: 5px; border-radius: 5px; color: white; font-size: 14px; margin-top: 5px;">
                Rad ${displayText}
            </div>`
        );

        document.querySelector("#script_radBar").addEventListener("click", () => {
            history.pushState(null, null, "https://www.zed.city/scavenge");
            history.pushState(null, null, "https://www.zed.city/scavenge");
            history.go(0);
        });
    } else {
        radElem.innerHTML = `Rad ${displayText}`;
    }
}

// ⏳ Update Radiation Display Every 0.5s
setInterval(updateRadDisplay, 500);


// 🎭 MORALE TIMER FUNCTION (Syncs with API)
function updateMoraleDisplay() {
    const moraleBar = document.querySelector(".q-linear-progress.morale");
    if (!moraleBar) return;

    const moraleFullAtTimestamp = Number(localStorage.getItem("script_moraleFullAtTimestamp"));
    let timeLeftSec = Math.floor((moraleFullAtTimestamp - Date.now()) / 1000);
    let moraleElem = document.querySelector("#script_moraleBar");

    const displayText = (moraleFullAtTimestamp === -1 || timeLeftSec <= 0)
        ? "<span style='color: #28a745;'>FULL</span>"
        : timeReadable(timeLeftSec);

    if (!moraleElem) {
        moraleBar.insertAdjacentHTML(
            "afterend",
            `<div id="script_moraleBar" class="script-timer" style="
                text-align: center; background: rgba(0, 0, 0, 0.7);
                padding: 5px; border-radius: 5px; color: white; font-size: 14px; margin-top: 5px;">
                Morale ${displayText}
            </div>`
        );
    } else {
        moraleElem.innerHTML = `Morale ${displayText}`;
    }
}

// ⏳ Update Morale Display Every 0.5s
setInterval(updateMoraleDisplay, 500);


// ❤️ LIFE TIMER FUNCTION (Syncs with API)
function updateLifeDisplay() {
    const lifeBar = document.querySelector(".q-linear-progress.life");
    if (!lifeBar) return;

    const lifeFullAtTimestamp = Number(localStorage.getItem("script_lifeFullAtTimestamp"));
    let timeLeftSec = Math.floor((lifeFullAtTimestamp - Date.now()) / 1000);
    let lifeElem = document.querySelector("#script_lifeBar");

    const displayText = (lifeFullAtTimestamp === -1 || timeLeftSec <= 0)
        ? "<span style='color: #28a745;'>FULL</span>"
        : timeReadable(timeLeftSec);

    if (!lifeElem) {
        lifeBar.insertAdjacentHTML(
            "afterend",
            `<div id="script_lifeBar" class="script-timer" style="
                cursor: pointer; text-align: center; background: rgba(0, 0, 0, 0.7);
                padding: 5px; border-radius: 5px; color: white; font-size: 14px; margin-top: 5px;">
                Life ${displayText}
            </div>`
        );
    } else {
        lifeElem.innerHTML = `Life ${displayText}`;
    }
}

// ⏳ Update Life Display Every 0.5s
setInterval(updateLifeDisplay, 500);


// 🎯 Ensure necessary localStorage variables exist
const junkStoreDefaults = {
    script_junkStoreResetTimestamp: 0,
    script_junkStore_ironBarStock: 0,
    script_junkStore_logsStock: 0,
    script_junkStore_scrapStock: 0,
    script_junkStore_nailsStock: 0,
    script_junkStore_limit_left: 0
};

for (const [key, value] of Object.entries(junkStoreDefaults)) {
    if (!localStorage.getItem(key)) {
        localStorage.setItem(key, value);
    }
}

/**
 * Handles store limit data and updates local storage
 */
function handleGetStoreJunkLimit(response) {
    const data = JSON.parse(response);
    const secLeft = data?.limits?.reset_time;

    // ⏳ Store reset timestamp
    if (secLeft) {
        const previousTimestamp = Number(localStorage.getItem("script_junkStoreResetTimestamp"));
        const timestamp = Date.now() + secLeft * 1000;
        localStorage.setItem("script_junkStoreResetTimestamp", timestamp);

        if (timestamp - previousTimestamp > 30000) {
            localStorage.setItem("script_junkStoreIsAlreadyNotified", false);
        }
    }

    // 🛒 Update stock for buyable items in the Junk Store
    if (data?.storeItems) {
        const updateStock = (itemCode, storageKey) => {
            // Finds the item by its codename in the API response
            const item = data.storeItems.find(i => i.codename === itemCode);
            if (item) {
                localStorage.setItem(storageKey, item.quantity);
            }
        };

        // Use the *actual* codenames from your API (e.g. "craft_log", "craft_scrap", "craft_nails")
        updateStock("iron_bar", "script_junkStore_ironBarStock");
        updateStock("craft_log", "script_junkStore_logsStock");
        updateStock("craft_scrap", "script_junkStore_scrapStock");
        updateStock("craft_nails", "script_junkStore_nailsStock");
    }

    // 📈 Update purchase limit
    localStorage.setItem(
      "script_junkStore_limit_left",
      data?.limits?.limit_left ? data.limits.limit_left + 240 : 360
    );
}




// 🎯 Ensure necessary localStorage variables exist
const jobDefaults = {
    script_forgeTimestamp: 0,
    script_scavenge_records: "{}",
    script_hunting_records: "{}",
    script_stronghold_id_gym: "",
    script_stronghold_id_radio_tower: "",
    script_stronghold_id_furnace: ""
};

for (const [key, value] of Object.entries(jobDefaults)) {
    if (!localStorage.getItem(key)) {
        localStorage.setItem(key, value);
    }
}



    // Junk Store Buy Limit Display
function addBuyLimitInfo() {
  if (!window.location.href.includes("/store/junk")) return;
  const row = document.querySelector(".title > div.row.q-col-gutter-xs.q-px-xs");
  if (!row) return;
  const cols = row.querySelectorAll(".col");
  const target = cols[1];
  if (!target) return;
  let info = document.getElementById("script_buyLimitInfo");
  if (!info) {
      info = document.createElement("div");
      info.id = "script_buyLimitInfo";
      info.style.cssText = "text-align: center; color: white; font-size: 20px; margin-left: 130px;";
      target.appendChild(info);
  }
  const limitLeft = Number(localStorage.getItem("script_junkStore_limit_left")) || 0;
  const total = 360;
  let used = total - limitLeft;
  if (used < 0) used = 0;
  const resetTs = Number(localStorage.getItem("script_junkStoreResetTimestamp"));
  const secLeft = Math.floor((resetTs - Date.now()) / 1000);
  const resetText = secLeft > 0 ? timeReadable(secLeft) : "Refreshed";

  info.innerHTML =
      limitLeft === total
      ? `Buy Limit Remaining: ${limitLeft}`
      : limitLeft > 0
          ? `Buy Limit Remaining: ${limitLeft} (Resets in ${resetText})`
          : `<span style="color:red;">BUY LIMIT REACHED</span> — Next reset in ${resetText}`;
}
setInterval(addBuyLimitInfo, 500);


function addMaxBuySellButton() {
  const modal = document.querySelector(".small-modal");
  if (!modal) return;
  if (modal.querySelector(".script-store-btn")) return;
  const input = modal.querySelector("input");
  if (!input) return;

  const btns = modal.querySelectorAll("button");
  const buyBtn = Array.from(btns).find(b => b.textContent.trim().toLowerCase() === "buy");

  const maxBtn = document.createElement("button");
  maxBtn.className = "script-store-btn";
  maxBtn.textContent = "Max";
  maxBtn.style.cssText = `
    position: absolute;
    bottom: 10px;
    right: 10px;
    z-index: 1000;
    pointer-events: auto;
  `;

  const isJunk = window.location.href.includes("/store/junk");

  if (buyBtn && isJunk) {
    const imgSrc = modal.querySelector(".zed-item-img__content img")?.src || "";
    const mapping = {
      iron_bar: "script_junkStore_ironBarStock",
      logs: "script_junkStore_logsStock",
      scrap: "script_junkStore_scrapStock",
      nails: "script_junkStore_nailsStock"
    };
    // Note: This line uses a partial regex or placeholder
    const match = Object.keys(mapping).find(key => imgSrc.includes(`/${key}`));
    const storeKey = match ? mapping[match] : null;
    if (storeKey) {
      const stock = Number(localStorage.getItem(storeKey)) || 0;
      const limitLeft = Number(localStorage.getItem("script_junkStore_limit_left")) || 0;
      const buyNum = Math.max(0, Math.min(stock, limitLeft));
      if (buyNum > 0) {
        maxBtn.addEventListener("click", () => {
          input.value = buyNum;
          input.dispatchEvent(new Event("input", { bubbles: true }));
        });
      } else {
        maxBtn.disabled = true;
      }
    } else {
      maxBtn.addEventListener("click", () => {
        input.value = 999999;
        input.dispatchEvent(new Event("input", { bubbles: true }));
      });
    }
  } else {
    maxBtn.addEventListener("click", () => {
      input.value = 999999;
      input.dispatchEvent(new Event("input", { bubbles: true }));
    });
  }

  modal.style.position = "relative";
  modal.appendChild(maxBtn);
}
setInterval(addMaxBuySellButton, 500);



/**
 * Handles various jobs such as forging, scavenging, and hunting.
 */
function handleStartJob(response) {
    const data = JSON.parse(response);
    const jobName = data?.job?.codename;

    // 🔥 Furnace Activity Tracking
    const perActionTime = data?.job?.items?.["item_requirement-bp"]?.vars?.wait_time;
    const perActionConsumeItemNumber = data?.job?.items?.["item_requirement-bp"]?.vars?.items?.["item_requirement-1"]?.qty;
    const consumeItemNumber = data?.job?.items?.["item_requirement-1"]?.quantity;

    if (jobName === "furnace" && perActionTime && perActionConsumeItemNumber && consumeItemNumber) {
        const secLeft = perActionTime * (consumeItemNumber / perActionConsumeItemNumber);
        localStorage.setItem("script_forgeTimestamp", Date.now() + secLeft * 1000);
        localStorage.setItem("script_forgeIsAlreadyNotified", false);
        return;
    }

    // 🔍 Scavenging Activity Tracking
    if (jobName?.startsWith("job_scavenge_") || ["room_scavenge", "scavenge"].includes(data?.job?.layout)) {
        const records = JSON.parse(localStorage.getItem("script_scavenge_records")) || {};
        const mapName = data?.job?.name;

        if (!records[mapName]) {
            records[mapName] = { mapName, totalAttempts: 0, successCount: 0, itemRewards: {} };
        }

        records[mapName].totalAttempts += 1;

        // ✅ If scavenging was successful, track rewards
        if (data?.outcome?.rewards?.length > 0) {
            records[mapName].successCount += 1;

            for (const reward of data.outcome.rewards) {
                records[mapName].itemRewards[reward.name] = (records[mapName].itemRewards[reward.name] || 0) + Number(reward.posted_qty);
            }
        }

        localStorage.setItem("script_scavenge_records", JSON.stringify(records));
    }

    // 🎯 Hunting Activity Tracking
    handleHuntingStartJob(data);

    // 🏋️ Gym Training Tracking
    handleGymStartJob(data);

    // ⛽ Fuel Depot Trade Cooldown (3 hours)
    if (jobName?.startsWith("job_fuel_depot_fuel_trader_1")) {
        localStorage.setItem("script_exploration_fuelTrade_cooldown_at_ms", Date.now() + 10800 * 1000);
    }
}

/**
 * 🏋️ Handles Gym Training Details
 */
function handleGymStartJob(response) {
    if (response?.job?.codename !== "gym") return;

    const playerName = localStorage.getItem("script_playerName");
    const getStats = JSON.parse(localStorage.getItem("script_getStats"));

    const gymLevel = response?.job?.vars?.level;
    const gain = response?.outcome?.rewards?.gain;
    const stat = response?.outcome?.rewards?.skill;
    const energy = response?.outcome?.iterations * 5;
    const statBefore = Number(getStats.skills[stat]);
    const moralBefore = Number(getStats.morale);
    const moralAfter = response?.reactive_items_qty?.find(item => item.codename === "morale")?.quantity || 0;

    console.log(`${playerName} trained in the ${gymLevel}-star gym with ${energy} energy. Morale decreased from ${moralBefore} to ${moralAfter}, ${stat} increased from ${statBefore} by ${gain}.`);

    const insertToElem = document.body.querySelector(".q-page.q-layout-padding div");
    if (insertToElem) {
        insertToElem.insertAdjacentHTML(
            "beforeend",
            `<div class="script_do_not_translate" style="font-size: 12px;">
                ${playerName} trained in the ${gymLevel}-star gym with ${energy} energy.
                Morale decreased from ${moralBefore} to ${moralAfter}, ${stat} increased from ${statBefore} by ${gain}.
            </div>`
        );
    }
}

/**
 * 🔥 Handles Job Completion for the Furnace
 */
function handleCompleteJob(response) {
    if (JSON.parse(response)?.job?.codename !== "furnace") return;

    localStorage.setItem("script_forgeTimestamp", Date.now());
    localStorage.setItem("script_forgeIsAlreadyNotified", true);
}

/**
 * 🏰 Fetch Stronghold Room IDs
 */
function handleGetStronghold(response) {
    const data = JSON.parse(response);
    if (!data?.stronghold) return;

    const strongholdMapping = {
        gym: "script_stronghold_id_gym",
        radio_tower: "script_stronghold_id_radio_tower",
        furnace: "script_stronghold_id_furnace",
    };

    for (const key in data.stronghold) {
        const area = data.stronghold[key].codename;
        if (strongholdMapping[area]) {
            localStorage.setItem(strongholdMapping[area], String(key));
        }
    }
}

/**
 * 🔥 Handle Furnace Job Status
 */
function handleFurnaceJob(data) {
    for (const key in data.stronghold) {
        const area = data.stronghold[key];
        if (area.codename !== "furnace") continue;

        const perActionTime = area?.items?.["item_requirement-bp"]?.vars?.wait_time;
        const perActionConsumeItemNumber = area?.items?.["item_requirement-bp"]?.vars?.items?.["item_requirement-1"]?.qty;
        const consumeItemNumber = area?.items?.["item_requirement-1"]?.quantity;
        const iterationsPassed = area?.iterationsPassed;
        const timeLeft = area?.timeLeft;

        if (perActionTime && perActionConsumeItemNumber && consumeItemNumber && iterationsPassed && timeLeft) {
            const secLeft = perActionTime * (consumeItemNumber / perActionConsumeItemNumber - iterationsPassed) - (perActionTime - timeLeft);
            const previousTimestamp = Number(localStorage.getItem("script_forgeTimestamp"));
            const timestamp = Date.now() + secLeft * 1000;

            localStorage.setItem("script_forgeTimestamp", timestamp);
            if (timestamp - previousTimestamp > 30000) {
                localStorage.setItem("script_forgeIsAlreadyNotified", false);
            }
            break;
        }
    }
}


/**
 * 📡 Handle Radio Tower Trade Refresh
 */
if (!localStorage.getItem("script_radioTowerTradeTimestamp")) {
    localStorage.setItem("script_radioTowerTradeTimestamp", 0);
}

function handleGetRadioTower(response) {
    const data = JSON.parse(response);
    const expire = data?.expire;
    if (expire) {
        const previousTimestamp = Number(localStorage.getItem("script_radioTowerTradeTimestamp"));
        const timestamp = Date.now() + expire * 1000;
        localStorage.setItem("script_radioTowerTradeTimestamp", timestamp);
        if (timestamp - previousTimestamp > 30000) {
            localStorage.setItem("script_radioTowerIsAlreadyNotified", false);
        }
    }
}


    // Junk Store Timer UI
    function updateStoreResetDisplay() {
      const container = getTimerContainer();
      const resetTimestamp = Number(localStorage.getItem("script_junkStoreResetTimestamp"));
      if (!container || resetTimestamp === 0) return;
      const timeLeftSec = Math.floor((resetTimestamp - Date.now()) / 1000);
      const storeHTML = timeLeftSec > 0
        ? `<span style="font-size: 12px;">Store ${timeReadable(timeLeftSec)}</span>`
        : `<span style="background-color: #ef5350; font-size: 12px;">Store Refreshed</span>`;
      let storeElem = container.querySelector("#script_junk_store_limit_logo");
      if (!storeElem) {
        storeElem = document.createElement("div");
        storeElem.id = "script_junk_store_limit_logo";
        storeElem.style.order = "2";
        storeElem.style.cursor = "pointer";
        container.appendChild(storeElem);
        storeElem.addEventListener("click", () => {
          history.pushState(null, null, "https://www.zed.city/store/junk");
          history.pushState(null, null, "https://www.zed.city/store/junk");
          history.go(-1);
        });
      }
      storeElem.innerHTML = storeHTML;
    }
    setInterval(updateStoreResetDisplay, 500);

    // Radio Timer UI
    function updateRadioTowerDisplay() {
      const container = getTimerContainer();
      if (!container || localStorage.getItem("script_radioTowerTradeTimestamp") === "0") return;
      let radioElem = container.querySelector("#script_radio_tower_logo");
      const timeLeftSec = Math.floor((Number(localStorage.getItem("script_radioTowerTradeTimestamp")) - Date.now()) / 1000);
      const statusText = timeLeftSec > 0
        ? `Radio ${timeReadable(timeLeftSec)}`
        : `<span style="background-color: #ef5350;">Radio Ready</span>`;
      if (!radioElem) {
        radioElem = document.createElement("div");
        radioElem.id = "script_radio_tower_logo";
        radioElem.style.cursor = "pointer";
        radioElem.style.order = "4";
        radioElem.className = "script_do_not_translate";
        container.appendChild(radioElem);
        radioElem.addEventListener("click", () => {
          const radioId = localStorage.getItem("script_stronghold_id_radio_tower");
          if (radioId) {
            history.pushState(null, null, `https://www.zed.city/stronghold/${radioId}`);
            history.pushState(null, null, `https://www.zed.city/stronghold/${radioId}`);
            history.go(-1);
          }
        });
      }
      radioElem.innerHTML = `<span class="script_do_not_translate" style="font-size: 12px;">${statusText}</span>`;
    }
    setInterval(updateRadioTowerDisplay, 500);

    // Raid Timer UI
    function updateRaidDisplay() {
      const container = getTimerContainer();
      if (!container || localStorage.getItem("script_raidTimestamp") === "0") return;
      let raidElem = container.querySelector("#script_raidCooldown_logo");
      const timeLeftSec = Math.floor((Number(localStorage.getItem("script_raidTimestamp")) - Date.now()) / 1000);
      const statusText = timeLeftSec > 0
        ? `Raid ${timeReadable(timeLeftSec)}`
        : `<span style="background-color: #ef5350;">Raid Ready</span>`;
      if (!raidElem) {
        raidElem = document.createElement("div");
        raidElem.id = "script_raidCooldown_logo";
        raidElem.style.cursor = "pointer";
        raidElem.style.order = "1";
        raidElem.className = "script_do_not_translate";
        container.appendChild(raidElem);
        raidElem.addEventListener("click", () => {
          history.pushState(null, null, "https://www.zed.city/raids");
          history.pushState(null, null, "https://www.zed.city/raids");
          history.go(-1);
        });
      }
      raidElem.innerHTML = `<span class="script_do_not_translate" style="font-size: 12px;">${statusText}</span>`;
    }
    setInterval(updateRaidDisplay, 500);

    // Explore Timer UI
    function updateFuelTradeDisplay() {
      const container = getTimerContainer();
      if (!container) return;
      let fuelElem = container.querySelector("#script_fuelTrade_logo");
      const cooldownTimestamp = Number(localStorage.getItem("script_exploration_fuelTrade_cooldown_at_ms")) || 0;
      const timeLeftSec = Math.floor((cooldownTimestamp - Date.now()) / 1000);
      const statusText = timeLeftSec > 0
        ? `Explore: ${timeReadable(timeLeftSec)}`
        : `Explore: <span style="color: #28a745;">Ready</span>`;
      if (!fuelElem) {
        fuelElem = document.createElement("div");
        fuelElem.id = "script_fuelTrade_logo";
        fuelElem.style.cursor = "pointer";
        fuelElem.style.order = "5";
        fuelElem.className = "script_do_not_translate";
        container.appendChild(fuelElem);
        fuelElem.addEventListener("click", () => {
          history.pushState(null, null, "https://www.zed.city/explore");
          history.pushState(null, null, "https://www.zed.city/explore");
          history.go(-1);
        });
      }
      fuelElem.innerHTML = `<span class="script_do_not_translate" style="font-size: 12px;">${statusText}</span>`;
    }
    setInterval(updateFuelTradeDisplay, 500);

    // Battle Stats Timer UI
    function updateBSDisplay() {
      const container = getTimerContainer();
      if (!container) return;
      let bsElem = container.querySelector("#script_bs_logo");
      const totalBS = localStorage.getItem("script_totalBS") || 0;
      const statusText = `Total Battle Stats: ${numberFormatter(totalBS)}`;
      if (!bsElem) {
        bsElem = document.createElement("div");
        bsElem.id = "script_bs_logo";
        bsElem.style.cursor = "pointer";
        bsElem.style.order = "6";
        bsElem.className = "script_do_not_translate";
        container.appendChild(bsElem);
        bsElem.addEventListener("click", () => {
          const gymId = localStorage.getItem("script_stronghold_id_gym");
          if (gymId) {
            history.pushState(null, null, `https://www.zed.city/stronghold/${gymId}`);
            history.pushState(null, null, `https://www.zed.city/stronghold/${gymId}`);
            history.go(-1);
          }
        });
      }
      bsElem.innerHTML = `<span class="script_do_not_translate" style="font-size: 12px; color: green;">${statusText}</span>`;
    }
    setInterval(updateBSDisplay, 500);

    // Furnace Timer UI
    function updateForgeDisplay() {
      const container = getTimerContainer();
      if (!container || localStorage.getItem("script_forgeTimestamp") === "0") return;
      let forgeElem = container.querySelector("#script_forge_logo");
      const timeLeftSec = Math.floor((Number(localStorage.getItem("script_forgeTimestamp")) - Date.now()) / 1000);
      const statusText = timeLeftSec > 0
        ? `Furnace ${timeReadable(timeLeftSec)}`
        : `<span style="background-color: #ef5350;">Furnace Inactive</span>`;
      if (!forgeElem) {
        forgeElem = document.createElement("div");
        forgeElem.id = "script_forge_logo";
        forgeElem.style.cursor = "pointer";
        forgeElem.style.order = "3";
        forgeElem.className = "script_do_not_translate";
        container.appendChild(forgeElem);
        forgeElem.addEventListener("click", () => {
          const furnaceId = localStorage.getItem("script_stronghold_id_furnace");
          if (furnaceId) {
            history.pushState(null, null, `https://www.zed.city/stronghold/${furnaceId}`);
            history.pushState(null, null, `https://www.zed.city/stronghold/${furnaceId}`);
            history.go(-1);
          }
        });
      }
      forgeElem.innerHTML = `<span class="script_do_not_translate" style="font-size: 12px;">${statusText}</span>`;
    }
    setInterval(updateForgeDisplay, 500);



/**
 * 🏴 **Display Faction Raid Cooldown**
 */
if (!localStorage.getItem("script_raidTimestamp")) {
    localStorage.setItem("script_raidTimestamp", 0);
}





/**
 * 🔔 **Countdown Notification System**
 */
function pushSystemNotifications() {
    if (localStorage.getItem("script_settings_notifications") !== "enabled") return;

    const notificationItems = [
        { key: "script_forgeTimestamp", notifiedKey: "script_forgeIsAlreadyNotified", message: "Furnace is Idle", url: `https://www.zed.city/stronghold/${localStorage.getItem("script_stronghold_id_furnace")}` },
        { key: "script_radioTowerTradeTimestamp", notifiedKey: "script_radioTowerIsAlreadyNotified", message: "Radio Tower has refreshed", url: `https://www.zed.city/stronghold/${localStorage.getItem("script_stronghold_id_radio_tower")}` },
        { key: "script_raidTimestamp", notifiedKey: "script_raidIsAlreadyNotified", message: "Faction Raid Ready", url: "https://www.zed.city/raids" },
        { key: "script_junkStoreResetTimestamp", notifiedKey: "script_junkStoreIsAlreadyNotified", message: "The Junk Store has refreshed", url: "https://www.zed.city/store/junk" },
        { key: "script_energyFullAtTimestamp", notifiedKey: "script_energyFullAlreadyNotified", message: "Energy is Full", url: `https://www.zed.city/stronghold/${localStorage.getItem("script_stronghold_id_gym")}` },
        { key: "script_radFullAtTimestamp", notifiedKey: "script_radFullAlreadyNotified", message: "Rad is Full", url: "https://www.zed.city/scavenge" },
    ];

    notificationItems.forEach(({ key, notifiedKey, message, url }) => {
        const timestamp = Number(localStorage.getItem(key));
        const isAlreadyNotified = localStorage.getItem(notifiedKey);
        const timeLeftSec = Math.floor((timestamp - Date.now()) / 1000);

        if (timestamp > 0 && isAlreadyNotified !== "true" && timeLeftSec > -60 && timeLeftSec < 0) {
            console.log(`pushSystemNotification: ${message}`);
            localStorage.setItem(notifiedKey, true);
            GM_notification({ text: message, title: "Zed City Tools", url });
        }
    });
}
setInterval(pushSystemNotifications, 1000);


        // Function to create the timer container on all pages
    function createTimerContainer() {
        let timerContainer = document.querySelector("#script_timer_container");

        if (!timerContainer) {
            timerContainer = document.createElement("div");
            timerContainer.id = "script_timer_container";
            document.body.appendChild(timerContainer);
        }

        updateContainerStyle(); // Apply styles after creation
    }

    // Function to update the timer container style based on user settings
    function updateContainerStyle() {
        let timerContainer = document.querySelector("#script_timer_container");

        if (!timerContainer) {
            console.error("Timer container not found!");
            return;
        }

        let alignRight = localStorage.getItem("script_timerAlign") === "right"; // Retrieve user setting

        Object.assign(timerContainer.style, {
            position: "fixed",
            top: "230px", // Adjust vertical position
            left: alignRight ? "unset" : "400px", // Align left
            right: alignRight ? "400px" : "unset", // Align right if enabled
            width: "250px",
            background: "rgba(0, 0, 0, 0.7)",
            padding: "10px",
            borderRadius: "5px",
            color: "white",
            fontSize: "14px",
            textAlign: "center",
            display: "flex",
            flexDirection: "column",
            gap: "5px",
            zIndex: "1000",
        });


    }


    let lastFetchedTime = 0; // Prevents excessive API calls
    let dismissed = false; // Tracks if the warning was dismissed by clicking "Close"
    let doNotShow = false; // Tracks if the user selected "Do Not Show Again"
    let lastXP = null; // Stores the last XP value for change detection

    // Function to fetch XP data (only if enough time has passed)
    async function fetchXPData() {
        const now = Date.now();
        if (now - lastFetchedTime < 5000) return null;

        try {
            const response = await fetch("https://api.zed.city/getStats", { method: "GET", credentials: "include" });
            if (!response.ok) throw new Error("Failed to fetch XP data.");
            const data = await response.json();
            lastFetchedTime = now;
            return data;
        } catch (error) {
            console.error("❌ Error fetching XP data:", error);
            return null;
        }
    }

    // Function to check XP and display the warning if necessary
    async function checkAndDisplayWarning() {
        // If "Do Not Show Again" is active, reset when XP changes
        if (doNotShow) {
            const data = await fetchXPData();
            if (data && data.experience !== lastXP) {
                doNotShow = false;
                dismissed = false;
            }
            return;
        }

        const data = await fetchXPData();
        if (!data) return;

        const { experience, xp_end } = data;
        const xpNeeded = xp_end - experience;

        // Reset dismissal if XP changes
        if (dismissed && experience !== lastXP) dismissed = false;
        lastXP = experience;

        if (xpNeeded <= 25 && !dismissed) showLevelUpWarning(xpNeeded);
    }

    // Function to display the level-up warning with two options
    function showLevelUpWarning(xpNeeded) {
        if (document.getElementById("levelUpWarning")) return;

        const warning = document.createElement("div");
        warning.id = "levelUpWarning";
        Object.assign(warning.style, {
            position: "fixed",
            top: "10px",
            left: "50%",
            transform: "translateX(-50%)",
            backgroundColor: "red",
            color: "white",
            padding: "15px",
            fontSize: "18px",
            fontWeight: "bold",
            border: "2px solid black",
            borderRadius: "5px",
            zIndex: "9999",
            textAlign: "center"
        });

        // Warning message
        warning.innerHTML = `<div>⚠️ WARNING: You are ${xpNeeded} XP away from leveling up!</div>`;

        // Button container
        const btnContainer = document.createElement("div");
        btnContainer.style.marginTop = "10px";

        // "Close" button: dismisses the warning temporarily
        const closeBtn = document.createElement("button");
        closeBtn.innerText = "Close";
        closeBtn.style.marginRight = "10px";
        closeBtn.addEventListener("click", () => {
            warning.remove();
            dismissed = true;
        });

        // "Do Not Show Again" button: disables the warning until XP changes
        const dontShowBtn = document.createElement("button");
        dontShowBtn.innerText = "Do Not Show Again";
        dontShowBtn.addEventListener("click", () => {
            warning.remove();
            doNotShow = true;
        });

        btnContainer.appendChild(closeBtn);
        btnContainer.appendChild(dontShowBtn);
        warning.appendChild(btnContainer);
        document.body.appendChild(warning);

        // Auto-remove after 10 seconds
        setTimeout(() => {
            if (document.body.contains(warning)) warning.remove();
        }, 10000);
    }

    // Trigger an XP check on each click
    document.addEventListener("click", checkAndDisplayWarning);


// Function to add settings UI in https://www.zed.city/settings
function addSettingsUI() {
    if (!window.location.href.includes("/settings")) return; // Only run on the settings page

    let settingsContainer = document.querySelector(".zed-grid.has-title.has-content"); // Match game container

    if (!settingsContainer) {
        console.error("Settings container not found!");
        return;
    }

    // Prevent adding the settings UI multiple times
    if (document.querySelector("#script_settingsPanel")) return;

    // Create settings panel using game styling
    let settingsPanel = document.createElement("div");
    settingsPanel.id = "script_settingsPanel";
    settingsPanel.className = "zed-grid has-title has-content";
    settingsPanel.innerHTML = `

<!-- 🛠️ Durability Threshold Setting -->
<div class="title"><div>Hunting Durability Warning</div></div>
<div class="grid-cont">
    <div class="q-pa-md">
        <label class="q-field row no-wrap items-start q-field--outlined q-field--dark q-field--dense">
            <div class="q-field__inner relative-position col self-stretch">
                <div class="q-field__control relative-position row no-wrap">
                    <div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
                        <div style="display: flex; justify-content: space-between; align-items: center; width: 100%;">
                            <span class="q-field__label">Durability Warning Threshold:</span>
                            <strong id="script_durabilityValue" style="color: #4CAF50; font-size: 14px;">40%</strong>
                        </div>
                        <div style="display: flex; align-items: center; gap: 10px;">
                            <input type="range" id="script_durabilitySlider" min="0" max="100" step="1" value="40"
                                style="width: 100%; margin-top: 5px; accent-color: #2196F3;">
                        </div>
                    </div>
                </div>
            </div>
        </label>
    </div>
</div>


       <!-- 📌 Draggable Timer Toggle (Better UI) -->
       <div class="title"><div>Timer Container Settings</div></div>
       <div class="grid-cont">
           <div class="q-pa-md">
               <label class="q-field row no-wrap items-start q-field--outlined q-field--dark q-field--dense">
                   <div class="q-field__inner relative-position col self-stretch">
                       <div class="q-field__control relative-position row no-wrap">
                           <div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
                               <div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
                                   <span class="q-field__label">Draggable Timer Container:</span>
                                   <div style="display: flex; align-items: center; gap: 10px;">
                                       <strong id="script_timerDraggableText" style="color: #4CAF50; font-size: 14px;">Enabled</strong>
                                       <input type="checkbox" id="script_timerDraggableCheckbox" style="transform: scale(1.2); accent-color: #2196F3;">
                                   </div>
                               </div>
                           </div>
                       </div>
                   </div>
               </label>
           </div>
       </div>
    `;

    // Append the panel to the settings page
    settingsContainer.appendChild(settingsPanel);

    // 🎛️ Load stored durability threshold
    let savedThreshold = localStorage.getItem("script_durability_threshold") || 40;
    let slider = document.querySelector("#script_durabilitySlider");
    let valueDisplay = document.querySelector("#script_durabilityValue");
    slider.value = savedThreshold;
    valueDisplay.textContent = `${savedThreshold}%`;

    // 🎛️ Update value display & save setting on change
    slider.addEventListener("input", () => {
        valueDisplay.textContent = `${slider.value}%`;
    });

    slider.addEventListener("change", () => {
        localStorage.setItem("script_durability_threshold", slider.value);
    });

    // 📌 Load & Apply Draggable Timer Setting
    const draggableCheckbox = document.getElementById("script_timerDraggableCheckbox");
    const draggableText = document.getElementById("script_timerDraggableText");

    draggableCheckbox.checked = localStorage.getItem("script_timerDraggable") !== "false";
    draggableText.textContent = draggableCheckbox.checked ? "Enabled" : "Disabled";
    draggableText.style.color = draggableCheckbox.checked ? "#4CAF50" : "#F44336";

    draggableCheckbox.addEventListener("change", () => {
        const enabled = draggableCheckbox.checked;
        localStorage.setItem("script_timerDraggable", enabled ? "true" : "false");
        draggableText.textContent = enabled ? "Enabled" : "Disabled";
        draggableText.style.color = enabled ? "#4CAF50" : "#F44336";
        updateTimerContainerBasedOnSetting();
    });

    console.log("Settings panel added successfully.");
}


// Fix: Use MutationObserver to detect SPA page changes
function observePageChanges() {
    const observer = new MutationObserver(() => {
        if (window.location.href.includes("/settings")) {
            addSettingsUI(); // Add settings UI when on settings page
        }
    });
    observer.observe(document.body, { childList: true, subtree: true });
}

// Ensure the timer container is always visible
createTimerContainer();

// Run observer to detect SPA changes
observePageChanges();

    // ============================
    // End New Timer Container Code
    // ============================


    function makeTimerContainerDraggable() {
  const container = document.getElementById("script_timer_container");
  if (!container) return;

  // Create a handle bar at the top
  let dragHandle = document.createElement("div");
  dragHandle.id = "script_timer_dragHandle";
  dragHandle.style.background = "rgba(255,255,255,0.1)";
  dragHandle.style.cursor = "move";
  dragHandle.style.padding = "5px";
  dragHandle.style.fontWeight = "bold";
  dragHandle.style.textAlign = "center";
  dragHandle.textContent = "ZCTools (Drag me)";

  // Insert the handle as the first child of the container
  container.insertBefore(dragHandle, container.firstChild);

  let offsetX = 0, offsetY = 0;

  // When user clicks down on the handle, begin the drag
  dragHandle.addEventListener("mousedown", function(e) {
    e.preventDefault();
    // Calculate the mouse's offset from the container's top-left corner
    offsetX = e.clientX - container.offsetLeft;
    offsetY = e.clientY - container.offsetTop;

    // Listen for mousemove & mouseup on the entire document
    document.addEventListener("mousemove", onMouseMove);
    document.addEventListener("mouseup", onMouseUp);
  });

  // Move the container as the mouse moves
  function onMouseMove(e) {
    container.style.left = (e.clientX - offsetX) + "px";
    container.style.top = (e.clientY - offsetY) + "px";
  }

  // When user releases the mouse, stop dragging
  function onMouseUp() {
    document.removeEventListener("mousemove", onMouseMove);
    document.removeEventListener("mouseup", onMouseUp);
  }
}

/**
 * 📌 **Utility Functions**
 */
function getOriTextFromElement(elem) {
    if (!elem) {
        console.error("getTextFromElement: element is null");
        return "";
    }
    return elem.getAttribute("script_translated_from") || elem.textContent;
}

function numberFormatter(num, digits = 1) {
    if (num == null) return null;
    if (num < 0) return "-" + numberFormatter(-num);

    const units = [
        { value: 1, symbol: "" },
        { value: 1e3, symbol: "k" },
        { value: 1e6, symbol: "M" },
        { value: 1e9, symbol: "B" }
    ];

    const rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
    let unit = units.slice().reverse().find(u => num >= u.value);

    return unit ? (num / unit.value).toFixed(digits).replace(rx, "$1") + unit.symbol : "0";
}

function timeReadable(sec) {
    if (sec >= 86400) return `${(sec / 86400).toFixed(1)} Days`;

    const d = new Date(sec * 1000);
    const pad = (n) => ("0" + n).slice(-2);

    let hours = d.getUTCHours() ? d.getUTCHours() + ":" : "";
    return hours + pad(d.getUTCMinutes()) + ":" + pad(d.getUTCSeconds());
}

function timeReadableNoSec(sec) {
    return sec >= 86400 ? `${(sec / 86400).toFixed(1)} Days` : `${(sec / 3600).toFixed(1)} Hours`;
}

/**
 * 🏋️ **Gym Lock & Max Button System**
 */
const processedElements = new Set();

function lockElement(element, isLocked) {
    element.style.pointerEvents = isLocked ? "none" : "";
    element.style.opacity = isLocked ? "0.5" : "";
}

function getCheckboxStates() {
    return JSON.parse(localStorage.getItem("script_gymCheckboxes")) || {};
}

function saveCheckboxStates(states) {
    localStorage.setItem("script_gymCheckboxes", JSON.stringify(states));
}

function addGymLocks() {
    const gymElements = document.querySelectorAll(".grid-cont.text-center.gym-cont");
    const states = getCheckboxStates();

    gymElements.forEach((element, index) => {
        if (processedElements.has(element)) return;

        /* 🔒 Lock Checkbox */
        const checkbox = document.createElement("input");
        checkbox.type = "checkbox";
        checkbox.className = "lock-checkbox";
        checkbox.style.cssText = "position: absolute; bottom: 10px; left: 10px; z-index: 1000; pointer-events: auto;";

        const key = `checkbox-${element.dataset.id || index}`;
        checkbox.checked = states[key] || false;
        lockElement(element, checkbox.checked);

        checkbox.addEventListener("change", () => {
            states[key] = checkbox.checked;
            saveCheckboxStates(states);
            lockElement(element, checkbox.checked);
        });

        /* 🔼 Max Button */
        const maxBtn = document.createElement("button");
        maxBtn.textContent = "Max";
        maxBtn.style.cssText = "position: absolute; bottom: 10px; right: 10px; z-index: 1000; pointer-events: auto;";

        maxBtn.addEventListener("click", () => {
            const input = element.querySelector("input");
            let maxTrainTimes = Math.floor(Number(localStorage.getItem("script_energy")) / 5);
            maxTrainTimes = maxTrainTimes > 0 ? maxTrainTimes : 1;

            /* 🎯 React Input Hack */
            let lastValue = input.value;
            input.value = maxTrainTimes;

            let event = new Event("input", { bubbles: true });
            event.simulated = true;

            let tracker = input._valueTracker;
            if (tracker) tracker.setValue(lastValue);

            input.dispatchEvent(event);
        });

        /* 🏗️ Append Elements */
        element.style.position = "relative";
        element.appendChild(checkbox);
        element.appendChild(maxBtn);
        processedElements.add(element);
    });
}
setInterval(addGymLocks, 500);

//Junk Store Buy Limit Display



/**
 * 🔍 **Scavenger Records UI (Table Format)**
 */
function addScavengeRecords() {
    if (!window.location.href.match(/zed\.city\/(scavenge|exploring|outposts)/)) return;

    const insertToElem = document.body.querySelector(".q-page.q-layout-padding");
    if (!insertToElem) return;

    const isHidden = localStorage.getItem("script_scavenge_records_hidden") === "true";
    const records = JSON.parse(localStorage.getItem("script_scavenge_records")) || {};

    let htmlContent = `
        <div id="script_scavenge_records_container" style="margin: 20px 0;">
           <div style="display: flex; align-items: center; background: rgba(0, 0, 0, 0.85); padding: 10px; border-radius: 5px;">
    <!-- Empty div for centering trick -->
    <div style="flex: 1;"></div>

    <!-- Centered Title -->
    <span style="font-size: 20px; font-weight: bold; color: white; text-align: center;">Scavenger Records</span>

    <!-- Right-aligned controls -->
    <div style="flex: 1; display: flex; justify-content: flex-end; gap: 10px;">
        <label style="color: white; font-size: 14px; display: flex; align-items: center; cursor: pointer;">
            <input type="checkbox" id="script_toggle_scavenge_records" ${isHidden ? "" : "checked"} style="margin-right: 5px;">
            Show
        </label>
        <button id="script_clear_scavenge_history" class="q-btn q-btn-item non-selectable no-outline"
            style="background: rgba(160, 0, 0, 0.5); color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px;">
            Clear History
        </button>
    </div>
</div>
            <div id="script_scavenge_records" class="zed-grid has-title has-content" style="margin-top: 10px; ${isHidden ? "display: none;" : ""}">
                <div class="grid-cont" style="background: rgba(0, 0, 0, 0.7); padding: 15px; border-radius: 5px; color: white;">
    `;

    let hasData = false;

    for (const mapKey in records) {
        hasData = true;
        const map = records[mapKey];
        const successCount = map.successCount || 0;
        const failCount = map.totalAttempts ? map.totalAttempts - successCount : 0;
        const successRate = (successCount + failCount) > 0 ? ((successCount / (successCount + failCount)) * 100).toFixed(1) + "%" : "0.0%";
        const sortedItems = Object.entries(map.itemRewards || {}).sort((a, b) => b[1] - a[1]);

        htmlContent += `
            <div style="text-align: center; font-weight: bold; margin-top: 10px;">
                ${map.mapName} - ${successCount} Successes | ${failCount} Fails (${successRate})
            </div>
            <div style="display: flex; justify-content: center; margin-top: 10px;">
                <table style="width: 80%; border-collapse: collapse; color: white; text-align: center;">
                    <thead>
                        <tr style="background: rgba(0, 0, 0, 0.8);">
                            <th style="padding: 8px; border: 1px solid #555;">Item</th>
                            <th style="padding: 8px; border: 1px solid #555;">Quantity</th>
                            <th style="padding: 8px; border: 1px solid #555;">Drop Rate</th>
                        </tr>
                    </thead>
                    <tbody>
        `;

        let itemHtml = []; // Store rows separately
        sortedItems.forEach(([itemKey, itemQty]) => {
            const dropRate = successCount > 0 ? ((itemQty / successCount) * 100).toFixed(1) + "%" : "0.0%";
            itemHtml.push(`
                <tr>
                    <td style="padding: 8px; border: 1px solid #555;">${itemKey}</td>
                    <td style="padding: 8px; border: 1px solid #555;">x ${itemQty}</td>
                    <td style="padding: 8px; border: 1px solid #555;">${dropRate}</td>
                </tr>
            `);
        });

        htmlContent += itemHtml.join("") + "</tbody></table></div>";
    }

    if (!hasData) {
        htmlContent += `<div style="text-align: center; font-size: 14px; color: #888;">No data available.</div>`;
    }

    htmlContent += `</div></div>`;

    document.getElementById("script_scavenge_records_container")?.remove();
    insertToElem.insertAdjacentHTML("beforeend", htmlContent);

    // ✅ Toggle Visibility
    document.getElementById("script_toggle_scavenge_records").addEventListener("change", function () {
        const isChecked = this.checked;
        localStorage.setItem("script_scavenge_records_hidden", !isChecked);
        document.getElementById("script_scavenge_records").style.display = isChecked ? "block" : "none";
    });

    // ✅ Clear History
    document.getElementById("script_clear_scavenge_history").addEventListener("click", function () {
        if (confirm("⚠️ Are you sure you want to clear all scavenger records?")) {
            console.log("Scavenger history cleared.");
            localStorage.setItem("script_scavenge_records", JSON.stringify({}));
            addScavengeRecords(); // Refresh UI
        } else {
            console.log("Clear history action canceled.");
        }
    });
}

// 🔄 **Refresh scavenger records every 500ms**
setInterval(addScavengeRecords, 500);



    // HUNTING DURABILITY


// 🔍 Detect When User Enters Hunting Page
function observeHuntingPage() {
    let lastUrl = window.location.href;
    const observer = new MutationObserver(() => {
        if (window.location.href !== lastUrl) {
            lastUrl = window.location.href;
            if (window.location.href.match(/\/hunting\/[0-6]/)) {
                checkWeaponDurability();
            }
        }
    });
    observer.observe(document.body, { childList: true, subtree: true });
}

// 🛑 Create Warning Overlay Instantly
function createWarningOverlay(message) {
    // Remove any existing warning box
    const existingOverlay = document.getElementById("durabilityWarningOverlay");
    if (existingOverlay) existingOverlay.remove();

    // Create overlay
    const overlay = document.createElement("div");
    overlay.id = "durabilityWarningOverlay";
    overlay.style.position = "fixed";
    overlay.style.top = "0";
    overlay.style.left = "0";
    overlay.style.width = "100%";
    overlay.style.height = "100%";
    overlay.style.background = "rgba(0, 0, 0, 0.85)";
    overlay.style.color = "white";
    overlay.style.display = "flex";
    overlay.style.justifyContent = "center";
    overlay.style.alignItems = "center";
    overlay.style.flexDirection = "column";
    overlay.style.zIndex = "99999";
    overlay.style.fontSize = "20px";

    // Warning Box
    const warningBox = document.createElement("div");
    warningBox.style.background = "#222";
    warningBox.style.padding = "20px";
    warningBox.style.border = "2px solid red";
    warningBox.style.borderRadius = "10px";
    warningBox.style.textAlign = "center";

    // Warning Text
    const warningText = document.createElement("p");
    warningText.innerHTML = message;
    warningBox.appendChild(warningText);

    // OK Button
    const okButton = document.createElement("button");
    okButton.innerText = "I Understand";
    okButton.style.padding = "10px 20px";
    okButton.style.marginTop = "10px";
    okButton.style.background = "red";
    okButton.style.color = "white";
    okButton.style.border = "none";
    okButton.style.cursor = "pointer";
    okButton.style.fontSize = "16px";
    okButton.style.borderRadius = "5px";

    // Remove overlay when clicking OK
    okButton.addEventListener("click", () => {
        overlay.remove();
    });

    warningBox.appendChild(okButton);
    overlay.appendChild(warningBox);
    document.body.appendChild(overlay);
}

// 🔍 Fetch Equipped Weapon and Check Durability
async function checkWeaponDurability() {
    try {
        const response = await fetch("https://api.zed.city/loadItems", {
            method: "GET",
            credentials: "include",
            headers: { "Content-Type": "application/json" }
        });

        if (!response.ok) {
            throw new Error(`HTTP Error ${response.status}`);
        }

        const data = await response.json();

        if (data.error) {
            console.error("API Error:", data.error);
            return;
        }

        // 🎯 Get Equipped Primary Weapon
        const equippedWeapon = data.equip?.primary;

        if (!equippedWeapon || equippedWeapon.name === "Empty" || !equippedWeapon.vars?.condition) {
            console.warn("No valid weapon equipped or condition data missing.");
            return;
        }

        const weaponCondition = parseFloat(equippedWeapon.vars.condition);
        const userThreshold = parseFloat(localStorage.getItem("script_durability_threshold")) || 20;

        // 🛑 INSTANTLY Show Warning Box
        if (weaponCondition < userThreshold) {
            createWarningOverlay(`⚠️ Your <strong>${equippedWeapon.name}</strong> has low durability (<strong>${weaponCondition}%</strong>).<br><br>Your threshold is set to <strong>${userThreshold}%</strong>.<br><br>Consider repairing or switching weapons before hunting.`);
        }
    } catch (error) {
        console.error("Failed to fetch equipped weapon data:", error);
    }
}

// 🔄 Ensure Warning Loads Instantly
document.addEventListener("DOMContentLoaded", () => {
    if (window.location.href.match(/\/hunting\/[0-6]/)) {
        checkWeaponDurability();
    }
});

// 🔄 Start Monitoring Hunting Pages
observeHuntingPage();



    //HUNTING DURABILITY END


/**
 * 🎯 Hunting Statistics & Tracking
 * Combat Status:
 * 0 - No fight
 * 1 - Started Job (Got Map)
 * 2 - Got Fight (Got Monster)
 * 3 - Completed Fight (Got Loot)
 */
const pendingFight = {
  status: 0,
  mapName1: "",
  mapName2: "",
  monsterName: "",
  winner: "",
  lootItems: {}
};

/**
 * Called when a hunting job starts.
 * Simply parses the job codename to get a map identifier.
 */
function handleHuntingStartJob(response) {
  const job = response?.job?.codename;
  if (!job || !job.startsWith("job_hunting_")) return;
  // Simple approach: parse map from codename (e.g. "job_hunting_5_1" → "5")
  let map1 = job.replace("job_hunting_", "").slice(0, -2);
  Object.assign(pendingFight, {
    status: 1,
    mapName1: map1,
    mapName2: response?.job?.name || "",
    monsterName: "",
    winner: "",
    lootItems: {}
  });
}

/**
 * Called when fight data is received.
 * Sets the monster name from the victim's username.
 */
function handleGetFight(r) {
  const resp = JSON.parse(r);
  pendingFight.status = 2;
  pendingFight.monsterName = resp?.victim?.user?.username || "Unknown Monster";
}

/**
 * Called when a fight is completed.
 * Updates pendingFight with the winner and merges any loot.
 */
function handleDoFight(r) {
  const resp = JSON.parse(r);
  if (!resp?.winner) return;
  pendingFight.status = 3;
  pendingFight.winner = String(resp.winner);
  if (resp?.loot) {
    for (const item of resp.loot) {
      pendingFight.lootItems[item.name] = (pendingFight.lootItems[item.name] || 0) + item.quantity;
    }
  }
  saveFight();
}

/**
 * Saves the current pendingFight data into persistent localStorage records,
 * then resets pendingFight.
 */
function saveFight() {
  const records = JSON.parse(localStorage.getItem("script_hunting_records")) || {};
  if (!records[pendingFight.mapName1]) records[pendingFight.mapName1] = {};
  if (!records[pendingFight.mapName1][pendingFight.mapName2]) {
    records[pendingFight.mapName1][pendingFight.mapName2] = { wonTimes: 0, lostTimes: 0, monsters: {} };
  }
  const isWin = !pendingFight.winner.startsWith("npc_");
  if (isWin) {
    records[pendingFight.mapName1][pendingFight.mapName2].wonTimes++;
  } else {
    records[pendingFight.mapName1][pendingFight.mapName2].lostTimes++;
  }
  if (!records[pendingFight.mapName1][pendingFight.mapName2].monsters[pendingFight.monsterName]) {
    records[pendingFight.mapName1][pendingFight.mapName2].monsters[pendingFight.monsterName] = {
      wonTimes: 0,
      lostTimes: 0,
      itemLoots: {}
    };
  }
  if (isWin) {
    records[pendingFight.mapName1][pendingFight.mapName2].monsters[pendingFight.monsterName].wonTimes++;
  } else {
    records[pendingFight.mapName1][pendingFight.mapName2].monsters[pendingFight.monsterName].lostTimes++;
  }
  Object.entries(pendingFight.lootItems).forEach(([itemName, qty]) => {
    records[pendingFight.mapName1][pendingFight.mapName2].monsters[pendingFight.monsterName].itemLoots[itemName] =
      (records[pendingFight.mapName1][pendingFight.mapName2].monsters[pendingFight.monsterName].itemLoots[itemName] || 0) + qty;
  });
  localStorage.setItem("script_hunting_records", JSON.stringify(records));
  Object.assign(pendingFight, { status: 0, mapName1: "", mapName2: "", monsterName: "", winner: "", lootItems: {} });
}

/**
 * Builds and displays the Hunting Records UI on the /hunting page.
 */
function addHuntingRecordsToPage() {
  if (!window.location.href.includes("zed.city/hunting")) return;
  const container = document.querySelector(".q-page.q-layout-padding");
  if (!container) return;

  const hidden = localStorage.getItem("script_huntingRecords_hidden") === "true";
  let html = `
    <div id="script_hunting_records_container" style="margin: 20px 0;">
      <div style="display: flex; align-items: center; justify-content: center; position: relative; background: rgba(0,0,0,0.85); padding: 10px; border-radius: 5px;">
        <span style="font-size: 20px; font-weight: bold; color: white;">Hunting Records</span>
        <div style="position: absolute; right: 10px; display: flex; gap: 10px;">
          <label style="color: white; font-size: 14px; display: flex; align-items: center; cursor: pointer;">
            <input type="checkbox" id="script_toggle_hunting_records" ${hidden ? "" : "checked"} style="margin-right: 5px;"> Show
          </label>
          <button id="script_clear_hunting_history" class="q-btn q-btn-item non-selectable no-outline"
                  style="background: rgba(160,0,0,0.5); color: white; padding: 3px 8px; border-radius: 3px; font-size: 12px;">
            Clear History
          </button>
        </div>
      </div>
      <div id="script_hunting_records" class="zed-grid has-title has-content" style="margin-top: 10px; ${hidden ? "display: none;" : ""}">
        <div class="grid-cont" style="background: rgba(0,0,0,0.7); padding: 15px; border-radius: 5px; color: white;">
  `;
  const records = JSON.parse(localStorage.getItem("script_hunting_records")) || {};
  let hasData = false;
  Object.keys(records).forEach(map1 => {
    hasData = true;
    html += `<div style="font-size: 16px; font-weight: bold; text-align: center; border-bottom: 2px solid #555; padding: 8px 0; margin-top: 10px;">${map1}</div>`;
    Object.keys(records[map1]).forEach(map2 => {
      const data = records[map1][map2];
      html += `<div style="text-align: center; font-weight: bold; margin-top: 10px; font-size: 14px;">${map2} - ${data.wonTimes} Wins, ${data.lostTimes} Losses</div>`;
      html += `<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 10px; margin-top: 10px;">`;
      Object.entries(data.monsters)
        .sort((a, b) => (b[1].wonTimes + b[1].lostTimes) - (a[1].wonTimes + a[1].lostTimes))
        .forEach(([monster, stats]) => {
          html += `
            <div style="background: rgba(0,0,0,0.85); padding: 10px; border-radius: 5px; text-align: center;">
              <div style="font-weight: bold; font-size: 14px; margin-bottom: 5px;">${monster} (${stats.wonTimes} Kills)</div>
              <div style="display: flex; flex-direction: column; gap: 3px;">`;
          Object.entries(stats.itemLoots)
            .sort((a, b) => b[1] - a[1])
            .forEach(([item, qty]) => {
              const avg = stats.wonTimes > 0 ? (qty / stats.wonTimes).toFixed(2) : "0.00";
              html += `<div style="display: flex; justify-content: space-between; padding: 3px 5px; font-size: 13px;">
                <span>${item}</span>
                <span>x ${qty}</span>
                <span>Avg/Kill: ${avg}</span>
              </div>`;
            });
          html += `</div></div>`;
        });
      html += `</div>`;
    });
  });
  if (!hasData) {
    html += `<div style="text-align: center; font-size: 14px; color: #888;">No data available.</div>`;
  }
  html += `</div></div></div>`;
  const existing = document.getElementById("script_hunting_records_container");
  if (existing) existing.remove();
  container.insertAdjacentHTML("beforeend", html);

  document.getElementById("script_toggle_hunting_records").addEventListener("change", function (e) {
    const show = e.target.checked;
    localStorage.setItem("script_huntingRecords_hidden", !show);
    document.getElementById("script_hunting_records").style.display = show ? "block" : "none";
  });

  document.getElementById("script_clear_hunting_history").addEventListener("click", () => {
    if (confirm("⚠️ Are you sure you want to clear all hunting history?")) {
      localStorage.setItem("script_hunting_records", "{}");
      addHuntingRecordsToPage();
    }
  });
}
setInterval(addHuntingRecordsToPage, 500);

    // Ensure the timer container is created
createTimerContainer();
// Make it draggable
makeTimerContainerDraggable();

/* **END** */
})();