Zed City Stats Notifier (Bottom-Left Toggle UI + Per-Stat Enable)

Zed City notifier with collapsible bottom-left UI, per-stat toggles, sound, and persistent custom thresholds

2025-10-07 या दिनांकाला. सर्वात नवीन आवृत्ती पाहा.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

You will need to install an extension such as Tampermonkey to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name         Zed City Stats Notifier (Bottom-Left Toggle UI + Per-Stat Enable)
// @namespace    http://tampermonkey.net/
// @version      2.0.1
// @description  Zed City notifier with collapsible bottom-left UI, per-stat toggles, sound, and persistent custom thresholds
// @author       You
// @match        https://zed.city/*
// @match        https://*.zed.city/*
// @grant        GM_xmlhttpRequest
// @connect      zed.city
// @license      MIT
// ==/UserScript==


(function() {
    'use strict';

    const CHECK_INTERVAL = 60 * 1000;
    const STORAGE_KEY = 'zedCityNotifier';

    // --- Load saved config or set defaults ---
    const defaultConfig = {
        thresholds: { energy: 100, rad: 15, morale: 100, life: 100 },
        notified: { energy: false, rad: false, morale: false, life: false },
        enabled: { energy: true, rad: true, morale: true, life: true },
        uiVisible: false
    };

    let config = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
    // Merge with defaults to avoid undefined keys
    config = {
        thresholds: { ...defaultConfig.thresholds, ...(config.thresholds || {}) },
        notified: { ...defaultConfig.notified, ...(config.notified || {}) },
        enabled: { ...defaultConfig.enabled, ...(config.enabled || {}) },
        uiVisible: config.uiVisible ?? defaultConfig.uiVisible
    };

    function saveConfig() {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(config));
    }

    const audio = new Audio("https://actions.google.com/sounds/v1/cartoon/clang_and_wobble.ogg");

    if (Notification.permission !== "granted" && Notification.permission !== "denied") {
        Notification.requestPermission();
    }

    function sendNotification(title, text) {
        if (Notification.permission === "granted") {
            new Notification(title, { body: text });
            audio.play().catch(e => console.log("[ZedCityNotifier] Audio error:", e));
        }
    }

    function checkStats() {
        GM_xmlhttpRequest({
            method: "GET",
            url: "https://api.zed.city/getStats",
            headers: { "Accept": "application/json" },
            onload: function(response) {
                if (response.status === 200) {
                    try {
                        const data = JSON.parse(response.responseText);
                        const stats = {
                            energy: data.energy,
                            rad: data.rad,
                            morale: data.morale,
                            life: data.life
                        };
                        console.log("[ZedCityNotifier] Stats:", stats);

                        for (const stat in stats) {
                            if (!config.enabled[stat]) continue; // skip disabled stats
                            const value = stats[stat];
                            const limit = config.thresholds[stat];
                            if (value >= limit && !config.notified[stat]) {
                                sendNotification("Zed City Alert", `${stat} reached ${value}!`);
                                config.notified[stat] = true;
                                saveConfig();
                            } else if (value < limit && config.notified[stat]) {
                                config.notified[stat] = false;
                                saveConfig();
                            }
                        }
                    } catch (e) {
                        console.error("[ZedCityNotifier] Error parsing stats:", e);
                    }
                }
            },
            onerror: function(err) { console.error("[ZedCityNotifier] Request error:", err); }
        });
    }

    checkStats();
    setInterval(checkStats, CHECK_INTERVAL);

    // --- PANEL CONTAINER ---
    const panel = document.createElement("div");
    Object.assign(panel.style, {
        position: "fixed",
        bottom: "60px",
        left: "20px",
        width: "210px",
        background: "rgba(20,20,20,0.9)",
        color: "#fff",
        borderRadius: "10px",
        fontFamily: "Arial,sans-serif",
        fontSize: "12px",
        zIndex: "9999",
        boxShadow: "0 0 10px rgba(0,0,0,0.5)",
        transition: "all 0.3s ease",
        opacity: config.uiVisible ? "1" : "0",
        transform: config.uiVisible ? "translateY(0)" : "translateY(10px)",
        display: config.uiVisible ? "block" : "none",
        backdropFilter: "blur(4px)"
    });

    const header = document.createElement("div");
    header.textContent = "⚙️ Zed City Notifier";
    Object.assign(header.style, {
        padding: "6px",
        fontWeight: "bold",
        textAlign: "center",
        background: "#2c2c2c",
        borderRadius: "10px 10px 0 0"
    });
    panel.appendChild(header);

    const content = document.createElement("div");
    content.style.padding = "6px";

    const fields = [
        { id: "energy", label: "Energy", value: config.thresholds.energy },
        { id: "rad", label: "Rad", value: config.thresholds.rad },
        { id: "morale", label: "Morale", value: config.thresholds.morale },
        { id: "life", label: "Life", value: config.thresholds.life }
    ];

    fields.forEach(f => {
        const row = document.createElement("div");
        Object.assign(row.style, {
            display: "flex",
            justifyContent: "space-between",
            alignItems: "center",
            marginBottom: "6px"
        });

        const label = document.createElement("label");
        label.textContent = f.label;
        label.style.flex = "1";

        const input = document.createElement("input");
        input.type = "number";
        input.value = f.value;
        input.id = "zc_" + f.id;
        Object.assign(input.style, {
            width: "50px",
            marginRight: "4px"
        });

        const checkbox = document.createElement("input");
        checkbox.type = "checkbox";
        checkbox.checked = config.enabled[f.id];
        checkbox.id = "zc_enable_" + f.id;

        const checkboxLabel = document.createElement("span");
        checkboxLabel.textContent = "🔔";
        checkboxLabel.title = "Enable/disable notifications";

        row.appendChild(label);
        row.appendChild(input);
        row.appendChild(checkboxLabel);
        row.appendChild(checkbox);
        content.appendChild(row);
    });

    const saveButton = document.createElement("button");
    saveButton.id = "zc_save";
    saveButton.textContent = "💾 Save";
    Object.assign(saveButton.style, {
        marginTop: "5px",
        width: "100%",
        borderRadius: "6px",
        border: "none",
        background: "#0078d7",
        color: "white",
        padding: "5px 0",
        cursor: "pointer"
    });
    content.appendChild(saveButton);
    panel.appendChild(content);
    document.body.appendChild(panel);

    // --- TOGGLE BUTTON ---
    const toggleButton = document.createElement("button");
    toggleButton.id = "zcToggleBtn";
    toggleButton.textContent = "⚙️ Notifier";
    Object.assign(toggleButton.style, {
        position: "fixed",
        bottom: "20px",
        left: "20px",
        zIndex: "9999",
        padding: "6px 12px",
        borderRadius: "8px",
        border: "none",
        background: "#2c2c2c",
        color: "#fff",
        cursor: "pointer",
        boxShadow: "0 0 5px rgba(0,0,0,0.3)"
    });
    document.body.appendChild(toggleButton);

    // --- Toggle logic ---
    let visible = config.uiVisible;
    function updatePanelVisibility() {
        if (visible) {
            panel.style.display = "block";
            setTimeout(() => {
                panel.style.opacity = "1";
                panel.style.transform = "translateY(0)";
            }, 10);
        } else {
            panel.style.opacity = "0";
            panel.style.transform = "translateY(10px)";
            setTimeout(() => { panel.style.display = "none"; }, 300);
        }
        config.uiVisible = visible;
        saveConfig();
    }
    toggleButton.addEventListener("click", () => {
        visible = !visible;
        updatePanelVisibility();
    });

    // --- Save handler ---
    saveButton.addEventListener("click", () => {
        fields.forEach(f => {
            config.thresholds[f.id] = parseInt(document.getElementById("zc_" + f.id).value) || 100;
            config.enabled[f.id] = document.getElementById("zc_enable_" + f.id).checked;
        });
        saveConfig();
        alert("Settings saved!");
    });
})();