Zed City Tools

Tools for Zed City

// ==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** */
})();