Cookie Clicker Mod Menu

Mod menu for Cookie Clicker

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Cookie Clicker Mod Menu
// @namespace    https://github.com/qba210/cookie-clicker-mod-menu
// @version      1.1
// @description  Mod menu for Cookie Clicker
// @author       qba210
// @license      MIT
// @match        *://orteil.dashnet.org/cookieclicker/*
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAqCAMAAAD26OlUAAAAolBMVEU0DAIKBQhqIg8A3v9DLSpuV1URLT9FARDDkzh+ernp1nP///+QgHeMAACpdiE0FRfBqIyov5EAhZXwFwC7XzaDQBTUwbxBTn1NKiPg4OAXPiCaMxaauDTdtGaVXjnKEwr/fgAAWZ5Sb03Fh1D/ieeWABzUQQ3/zC86gSvtEG/cQWKgANXMs6z67bnhwJalQh9g/1B+WkBJDQPKa0G9jnkAAADDKDlIAAAANnRSTlP//////////////////////////////////////////////////////////////////////wChj04xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGx2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDIgNzkuMTY0NDg4LCAyMDIwLzA3LzEwLTIyOjA2OjUzICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIxLjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyMi0wOC0wN1QyMDo1NToxNCswMjowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjItMDgtMDdUMjE6MDA6MjgrMDI6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjItMDgtMDdUMjE6MDA6MjgrMDI6MDAiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OWRkM2RiMTEtNWViZS01MzQzLWEyOGEtYzAwZDE2MDY3MWFhIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6MGUxNzYxNGUtOTkxNy1hMzRmLTllYWYtYzM4ZTdlYjY2MWIwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6RjVDMTE2MERDQ0NDMTFFQzlBOThCQzEzREI1QkEzRjUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIyIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGNUMxMTYwQUNDQ0MxMUVDOUE5OEJDMTNEQjVCQTNGNSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGNUMxMTYwQkNDQ0MxMUVDOUE5OEJDMTNEQjVCQTNGNSIvPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpiNWRkNTg4NS1jNjA4LWEwNGEtYTYyMi04OTM4NjNlYjcwNGEiIHN0RXZ0OndoZW49IjIwMjItMDgtMDdUMjE6MDA6MjgrMDI6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMi4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6OWRkM2RiMTEtNWViZS01MzQzLWEyOGEtYzAwZDE2MDY3MWFhIiBzdEV2dDp3aGVuPSIyMDIyLTA4LTA3VDIxOjAwOjI4KzAyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjIuMCAoV2luZG93cykiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+nkWItQAAANxJREFUOMut1MEOgjAQBFDaY016bbJfsMde+P9fk3GyLg2obHESPcgjw2LbZd0isv7MEoKq/4Qiqr2rWn16Zxbuq3EZN5aCG0ccg6wFqlWVEAGOQ6+1UtWUVB9b9vQ6dAZEiG9Cp3HIy3glPgw4fotDGwSo95TKEJbPQlB8PPegDTPCWu9AG8AWBZf0DGyNfyGTEgAfRKS1OLRFa7U+ksjJ6rkIUT4uNnmFxXHotFY8hCVnMGyXOCRtDdBKWcvNPAN5pOSMSiKOwQ09B4/Hnh+K8/CYD9U34Mnr+QafGozjyeDrtEIAAAAASUVORK5CYII=
// @grant        none
// ==/UserScript==

(async function() {
    'use strict';

    class Logger {
        /**
         * 
         * @param {object} msg 
         */
        static LogInfo(msg) {
            console.log("\n[Mod Menu] ", msg);
        }

        /**
         * 
         * @param {object} msg 
         */
        static LogDebug(msg) {
            Logger.LogInfo(msg);
        }
    }

    Logger.LogInfo("Waiting for Game...")
    let gamePromise =  new Promise((res) => {var int = setInterval(() => {if (Game.Achievements) {clearInterval(int); res(Game);}}, 10)});
    await gamePromise;
    Logger.LogInfo("Game loaded!")


    let cheatMenu = document.createElement("div");

    const translations = [
        {
            lang: "pl",
            langName: "Polski",
            hacks: {
                "silent-mode": {
                    name: "Tryb cichy",
                    desc: "Unika wykrycia przez grę hackowania (np. osiągnięcie 'Oszukane ciastka smakują najgorzej')"
                },
                "cookie-spam": {
                    name: "Spamowanie ciastka",
                    desc: "Po przyciśnięciu ciastka samoczynnie zaczyna na nie klikać (kończy po odciśnięciu)"
                },
                "autoclicker": {
                    name: "Autokliker",
                    desc: "Auttomatycznie klika w ciastko"
                },
                "dev-tools": {
                    name: "Menu dewelop.",
                    desc: "Otwiera menu deweloperskie"
                },
                "set-cookies": {
                    name: "Ustaw ilość ciastek",
                    desc: "Pozwala na zmianę ilości ciastek"
                },
                "delete-save": {
                    name: "Usuń zapis"
                },
                "earn-achievement": {
                    name: "Odblokuj osiągnięcie",
                    desc: "Wybierz osiągnięcie z listy i kliknij przycisk aby je zdobyć!"
                },
                "revoke-achievement": {
                    name: "Usuń osiągnięcie",
                    desc: "Wybierz osiągnięcie z listy i kliknij przycisk aby je usunąć!"
                },
                "finish-game": {
                    name: "Ukończ grę",
                    desc: "Kończy grę i odblokowywuje wszystko"
                }
            },
            strings: {
                "dev-tools-confirm": "Jesteś pewien? Tryb cichy nie uchroni Cię od hacków które włączycz w menu deweloperskim.",
                "changes-as-you-type": "Wartość zmienia się jak piszesz",
                "confirm-save-delete": "Jesteś pewien?",
                "set-object-amount": "Ustaw ilość %s"
            }
        },
        {
            lang: "en",
            langName: "English",
            hacks: {
                "silent-mode": {
                    name: "Silent mode",
                    desc: "Avoids detecting hacks by game (ex.: achievement 'Cheated cookies tastes awful')"
                },
                "cookie-spam": {
                    name: "Cookie spam",
                    desc: "After holding the cookie, starts to click it automatically (ends after releasing)"
                },
                "autoclicker": {
                    name: "Autoclicker",
                    desc: "Automatically clicks cookie"
                },
                "dev-tools": {
                    name: "Dev menu",
                    desc: "Opens developer menu"
                },
                "set-cookies": {
                    name: "Set cookie count",
                    desc: "Allows you to change cookie count"
                },
                "delete-save": {
                    name: "Delete save"
                },
                "earn-achievement": {
                    name: "Earn achievement",
                    desc: "Select achievement from list, then click button to get it!"
                },
                "revoke-achievement": {
                    name: "Remove achievement",
                    desc: "Select achievement from list, then click button to remove it!"
                },
                "finish-game": {
                    name: "Finish game",
                    desc: "Finishes game and unlocks everything"
                }
            },
            strings: {
                "dev-tools-confirm": "Are you sure? Silent mode cannot prevent detecting cheats you activate in developer menu.",
                "changes-as-you-type": "Changes as you type",
                "confirm-save-delete": "Are you sure?",
                "set-object-amount": "Set %s amount"
            }
        }
    ]

    let lang = translations.find((lng) => lng.lang === (localStorage.getItem("cheats_lang") ?? "en"));

    document.body.append(cheatMenu);

    cheatMenu.outerHTML = `
    <div id="hack-menu" style="left: 50%; top: 35px;">
        <div id="hack-popup">Hack</div>
        <br>
        <div id="hacks" style="display: none;">
            <div class="hack-list" id="main-hacks">
                <select id="hack-lang-select"></select>
                <div class="hack hack-bool" id="hack-silent-mode"></div>
                <div class="hack hack-bool" id="hack-cookie-spam"></div>
                <div class="hack hack-bool" id="hack-autoclicker"></div>
                <div class="hack hack-btn" id="hack-dev-tools"></div>
                <div class="hack hack-btn" id="hack-set-cookies"></div>
                <div class="hack hack-btn" id="hack-delete-save"></div>
                <div class="hack hack-btn" id="hack-finish-game"></div>
                <div class="hack hack-select" id="hack-earn-achievement"></div>
                <div class="hack hack-select" id="hack-revoke-achievement"></div>
            </div>
            <div class="hack-list" id="objects-hacks">
            </div>
        </div>
    </div>
    <div id="hack-tooltip" style="opacity: 0;left:0;top:0"></div>
    <div id="hack-alert-input" style="opacity: 0;display: none;">
        <div id="hack-alert-input-popup">
            <h1 id="hack-alert-input-popup-title"></h1>
            <div id="hack-alert-input-popup-desc"></div><br>
            <input type="number" id="hack-alert-input-popup-input"/><br>
            <input type="button" id="hack-alert-input-popup-ok" value="OK"/>
        </div>
    </div>
    `;
    
    let styles = document.createElement("style");
    styles.innerText = `
        #hack-menu, #hack-tooltip {
            z-index: 9000000000;
            position: absolute;
            box-sizing: border-box;
        }
        #hack-menu, #hack-menu *:not(#hacks){
            box-sizing: border-box;
        }
        #hack-menu {
            display: flex;
            flex-direction: column;
            align-items: center;
            width: clamp(150px, 225px, 25vw);
        }
        #hacks {
            backdrop-filter: blur(5px);
            display: flex;
            flex-direction: row;
            gap: 10px;
            padding: 10px;
            background-color: rgba(0, 0, 0, .5);
            border-radius: 13px;
            width: 200%;
            box-sizing: content-box;
        }

        .hack-list {
            display: flex;
            flex-direction: column;
            gap: 10px;
            padding: 10px;
            width: 100%;
            box-sizing: content-box;
            overflow-y: auto;
            overflow-x: hidden;
            max-height: 70vh;
        }
        .hack-list::-webkit-scrollbar-thumb {
            border: 4px solid rgb(10, 87, 242);
            background-color: rgba(10, 87, 242, .2); 
            border-radius: 5px;
            backdrop-filter: blur(3px);
            box-shadow: none;
            transition: background-color .3s linear, border .3s linear
        }
        .hack-list::-webkit-scrollbar-thumb:hover {
            border: 4px solid #00a2ff;
            background-color: rgba(0, 162, 255, .4); 
        }
        .hack-list::-webkit-scrollbar-track {
            border: 4px solid black;
            background-color: transparent;
            border-radius: 5px;
        }

        #hack-popup, .hack {
            padding: 10px;
            font-size: 20px;
            background-color: #00a2ff;
            border: 7px solid black;
            text-align: center;
            width: 100%;
        }
        
        .hack {
            cursor: pointer;
        }
        .hack:active {
            filter: brightness(0.85);
        }

        .hack-bool {
            transition: background-color .3s ease-in;
            background-color: red;
        }
        .hack-bool[on] {
            background-color: green;
        }
        #hack-tooltip {
            position: absolute;
            backdrop-filter: blur(7px);
            filter: blur(0px);
            transition: all .3s ease-out, left 0s ease, top 0s ease;
            background-color: rgba(0, 0, 0, .5);
            color: white;
            transform: translate(15px, 9.5px);
            width: 200px;
            padding: 10px;
            border-radius: 13px;
            pointer-events: none;
        }
        #hack-lang-select {
            z-index: 90000000001;
            background-color: black;
            color: white;
            border-color: white;
            width: 100%
        }
        #hack-lang-select > option {
            color: white;
        }

        #hack-popup {
            transition: opacity .35s ease-out;
        }
        #hack-popup:hover, #hack-popup[open] {
            opacity: 1;
        }
        #hack-popup:not(:hover):not([open]) {
            opacity: .8;
        }

        #hack-alert-input {
            z-index: 9500000000;
            background-color: rgba(0, 0, 0, .5);
            transition: opacity .3s ease-out;
            position: absolute;
            left: 0;
            top: 0;
            right: 0;
            bottom: 0;
            backdrop-filter: blur(5px);
        }
        #hack-alert-input-popup {
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            width: 60%;
            height: 50%;
            background-color: black;
            color: white;
            display: flex;
            flex-direction: column;
            justify-content: center;
            font-family: verdana;
            text-align: center;
            padding: 20px;
            border: 3px solid white;
            border-radius: 10px;
        }
        #hack-alert-input-popup-title {
            font-size: 2.5em;
        }
        #hack-alert-input-popup-desc {
            font-size: 1.5em;
        }
        #hack-alert-input-popup-ok {
            background-color: #00a2ff;
            border: 4px solid white;
            text-align: center;
            width: 100%;
            height: 50px;
            font-weight: bold;
            cursor: pointer;
        }
        #hack-alert-input-popup-input {
            background-color: black;
            color: white;
            border-color: white;
        }
        .select-in-hack {
            width: 100%;
            border-color: black;
            color: white;
            background-color: black;
        }

    `;
    document.head.append(styles);

    if (!localStorage.getItem("cheats_lang")) {
        localStorage.setItem("cheats_lang", "en");
    }
    
    let $hackmenu = document.getElementById("hack-menu");
    let $hacks = document.getElementById("hacks");
    let $popup = document.getElementById("hack-popup");
    let $tooltip = document.getElementById("hack-tooltip");
    /**@type {HTMLSelectElement} */
    let $langselect = document.getElementById("hack-lang-select");
    let $hack_earnachievement =  document.getElementById("hack-earn-achievement")
    let $hack_revokeachievement =  document.getElementById("hack-revoke-achievement")

    let $inputalert = {
        alert: document.getElementById("hack-alert-input"),
        popup: document.getElementById("hack-alert-input-popup"),
        title: document.getElementById("hack-alert-input-popup-title"),
        desc: document.getElementById("hack-alert-input-popup-desc"),
        input: document.getElementById("hack-alert-input-popup-input"),
        ok: document.getElementById("hack-alert-input-popup-ok")
    }

    let $objecthacks = document.getElementById("objects-hacks")

    let $cookie = document.getElementById("bigCookie");

    dragElement($hackmenu, $popup);

    //boolean hack manager
    $hacks.querySelectorAll(".hack-bool").forEach(node => {
        node.addEventListener("click", (e) => {
            if (node.hasAttribute("on")) {
                node.removeAttribute("on");
            } else {
                node.setAttribute("on", "");
            }
        }) 
    })

    //populate object hacks
    gamePromise.then(() => {
        Object.entries(Game.Objects).forEach(entry => {
            let [key, value] = entry;
            let hack = document.createElement("div");
            hack.className = "hack hack-btn";
            hack.id = `hack-object-amount-${value.bsingle}`;
            hack.setAttribute("lang-string", "set-object-amount");
            hack.setAttribute("supply-val", value.dname);
            hack.setAttribute("key", key);

            hack.onclick = (e) => {
                showInputAlert(hack.innerText, lang.strings["changes-as-you-type"], "number", value.amount, (val) => {
                    value.getFree(+val - value.amount)
                    value.free = 0;
                })
            }

            $objecthacks.append(hack);
        })
        // update language after
        reloadLangs();
    })

    reloadLangs();

    //class loop
    $hackmenu.querySelectorAll(".hack").forEach(_node => {
        /**@type {HTMLElement} */
        let node = _node;

        //tooltip
        node.addEventListener("mousemove", function (e) {
            if (e.target === this) {
                showTooltip(node.id, e.pageX, e.pageY);
            } else if (e.target instanceof HTMLSelectElement) {
                if (e.target.value && e.target.value !== "") {
                    showTooltip(Array.from(this.querySelectorAll("option")).find(opt => opt.value === e.target.value).innerHTML, e.pageX, e.pageY, false);
                }
            } else {
                hideTooltip();
            }
        })
        node.addEventListener("mouseleave", (e) => {
            hideTooltip();
        })
    })

    //menu hide/show
    $popup.addEventListener("click", (e) => {
        if ($hacks.style.display === "none") {
            $hacks.style.display = "";
            $popup.setAttribute("open", "");
        } else {
            $hacks.style.display = "none";
            $popup.removeAttribute("open");
        }
    })

    //add event listener to language select
    $langselect.addEventListener("change", (e) => {
        setLang($langselect.value);
    })

    //select language
    translations.forEach((translation) => {
        let option = document.createElement("option");
        option.value = translation.lang;
        option.innerText = translation.langName;
        $langselect.append(option);
        Logger.LogInfo(`Loaded language ${translation.lang}: ${translation.langName}`);
    })

    //set current language
    $langselect.value = lang.lang;

    // Debug menu hack
    document.getElementById("hack-dev-tools").addEventListener("click", (e) => {
        if (isSilentMode()) {
            if (confirm(lang.strings["dev-tools-confirm"])) {
                Game.OpenSesame();
            }
        } else {
            Game.OpenSesame();
        }
    })

    // Cookie spam hack
    let cookieSpamWorker;

    $cookie.addEventListener("mousedown", (e) => 
    {
        if (document.getElementById("hack-cookie-spam").hasAttribute("on"))
            cookieSpamWorker = setInterval(() => $cookie.dispatchEvent(new Event("click")));
    })

    $cookie.addEventListener("mouseup", (e) => {
        clearInterval(cookieSpamWorker);
    })

    // Autoclicker hack
    let autoclickerWorker;

    document.getElementById("hack-autoclicker").addEventListener("click", (e) => 
    {
        let cookrect = $cookie.getBoundingClientRect();
        if (document.getElementById("hack-autoclicker").hasAttribute("on"))
            autoclickerWorker = setInterval(() => $cookie.dispatchEvent(new Event("click")), 0);
        else 
            clearInterval(autoclickerWorker);
    })

    // Set cookies hack
    document.getElementById("hack-set-cookies").addEventListener("click", (e) => {
        //show input to user
        showInputAlert(lang.hacks["set-cookies"].name, lang.strings["changes-as-you-type"], "number", Game.cookies.toString(), (val) => {
            //earn mode decided by silent mode
            if (isSilentMode()) {
                Game.Earn(-Game.cookies + +val);
            } else {
                Game.cookies = +val;
            }
        })
    });

    // Delete save hack
    document.getElementById("hack-delete-save").addEventListener("click", (e) => {
        //ask user to confirm
        if (confirm(lang.strings["confirm-save-delete"])) {
            //delete save from localstorage
            localStorage.removeItem("CookieClickerGame");
            //after this reload to confirm 
            window.location.reload();
        }
    });
    
    //earn achievement hack
    $hack_earnachievement.addEventListener("click", function (e) {
        //if clicked on select dont gain achevement
        if (e.target !== this) return;
        //gain selected achievement
        Game.Win($hack_earnachievement.querySelector("select").value)
    })
    //revoke achievement hack (simillar to earn)
    $hack_revokeachievement.addEventListener("click", function (e) {
        if (e.target !== this) return;
        Game.RemoveAchiev($hack_revokeachievement.querySelector("select").value)
    })

    gamePromise.then(() => {
        document.getElementById("hack-finish-game").addEventListener("click", (e) => {
            Game.RuinTheFun(true);
        })
    })

    /**
     * @param {string} lang 
     */

    function setLang(lang) {
        localStorage.setItem("cheats_lang", lang);
        reloadLangs();
    }

    function reloadLangs() {
        lang = translations.find((lng) => lng.lang === (localStorage.getItem("cheats_lang") ?? "en"))

        //for every hack
        $hackmenu.querySelectorAll(".hack").forEach(node => {
            //add value
            /**@type {string} */
            let transval = (lang.hacks[node.id.replace("hack-", "")] ?? {name: node.id} ).name;
            if (node.hasAttribute("lang-string")) {
                transval = lang.strings[node.getAttribute("lang-string")];
            }
            if (node.hasAttribute("supply-val")) {
                transval = transval.replace("%s", node.getAttribute("supply-val"));
            }
            node.innerText = (transval ?? node.id ) ?? node.id
        })

        //support for select class
        $hackmenu.querySelectorAll(".hack-select").forEach(node => {
            let select = document.createElement("select");
            select.className = "select-in-hack";
            node.append(select);
        })

        // Earn achevement hack

        //when Game is avaible do
       gamePromise.then(() => {
            // foreach achevement ingame
            Object.entries(Game.Achievements).forEach((entry) => {
                const [key, value] = entry;
                //create option with achievement
                let option = document.createElement("option");
                option.value = key;
                option.innerHTML = value.dname;
                //add it to hack
                $hack_earnachievement.querySelector("select").append(option);
                $hack_revokeachievement.querySelector("select").append(option.cloneNode(true));
            });
        })

        //set current language
        $langselect.value = lang.lang;
    }

    function isSilentMode() {
        return document.getElementById("hack-silent-mode").hasAttribute("on");
    }


    /**
     * 
     * @param {string} id 
     * @param {number} x 
     * @param {number} y 
     */
    function showTooltip(id, x, y, translatable = true) {
        //show only if id have description
        if (translatable) {
            if (!lang.hacks[id.replace("hack-", "")]) return;
            if (!lang.hacks[id.replace("hack-", "")].desc) return;
        }

        $tooltip.innerText = translatable ? lang.hacks[id.replace("hack-", "")].desc ?? "" : id;
        $tooltip.style.left = x.toString() + "px";
        $tooltip.style.top = y.toString() + "px";
        $tooltip.style.opacity = 1;
    }

    function hideTooltip() {
        //$tooltip.innerText = ""; //its needed to be comented because transistion
        $tooltip.style.opacity = 0;
    }

    /**
     * Shows input alert.
     * 
     * @param {string} title 
     * @param {string} desc 
     * @param {string} type 
     * @param {string} value 
     * @param {(value: string) => void} setter 
     */

    function showInputAlert(title, desc, type, value, setter) {
        $inputalert.ok.onclick = () => {
            setTimeout(() => $inputalert.alert.style.display = "none", 301); 
            setter($inputalert.input.value);
            $inputalert.alert.style.opacity = "0";
        }

        $inputalert.title.innerText = title;
        $inputalert.desc.innerText = desc;

        $inputalert.input.type = type;
        $inputalert.input.value = value.toString();
        $inputalert.input.onchange = (e) => setter($inputalert.input.value);

        $inputalert.alert.style.display = "";
        $inputalert.alert.style.opacity = "1";
    }

    /**
     * 
     * @param {HTMLElement} elmnt element to drag 
     * @param {HTMLElement?} dragger element dragging elmnt
     */
    function dragElement(elmnt, dragger) {
        var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        if (document.getElementById(elmnt.id + "header") || dragger) {
          // if present, the header is where you move the DIV from:
          (document.getElementById(elmnt.id + "header") ?? dragger).onmousedown = dragMouseDown;
        } else {
          // otherwise, move the DIV from anywhere inside the DIV:
          elmnt.onmousedown = dragMouseDown;
        }
      
        function dragMouseDown(e) {
          e = e || window.event;
          e.preventDefault();
          // get the mouse cursor position at startup:
          pos3 = e.clientX;
          pos4 = e.clientY;
          document.onmouseup = closeDragElement;
          // call a function whenever the cursor moves:
          document.onmousemove = elementDrag;
          if (document.getElementById(elmnt.id + "header") || dragger) {
            (document.getElementById(elmnt.id + "header") ?? dragger).setAttribute("dragging", "");
          }else {
            elmnt.setAttribute("dragging", "");
          }
        }
      
        function elementDrag(e) {
          e = e || window.event;
          e.preventDefault();
          // calculate the new cursor position:
          pos1 = pos3 - e.clientX;
          pos2 = pos4 - e.clientY;
          pos3 = e.clientX;
          pos4 = e.clientY;
          // set the element's new position:
          elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
          elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
        }
      
        function closeDragElement() {
          // stop moving when mouse button is released:
          document.onmouseup = null;
          document.onmousemove = null;

          if (document.getElementById(elmnt.id + "header") || dragger) {
            (document.getElementById(elmnt.id + "header") ?? dragger).removeAttribute("dragging");
          }else {
            elmnt.removeAttribute("dragging");
          }
        }
      }
})();