DH3 Fixed

Improve the experience of Diamond Hunt 3

Versión del día 16/06/2020. Echa un vistazo a la versión más reciente.

// ==UserScript==
// @name         DH3 Fixed
// @namespace    FileFace
// @version      1.0.0
// @description  Improve the experience of Diamond Hunt 3
// @author       kape142
// @match        dh3.diamondhunt.co
// @grant        none
// ==/UserScript==
/*jshint multistr: true */
/*jslint es5: true */


(function() {
    'use strict';

    var version = "1.0.0"

    var Program = {};

    //Main
    Program.Main = {};

    Program.Main.IDs = {}

    Program.Main.initialToggle = 1;

    Program.Main.dialogueID = "";

    Program.Main.toggle = (Module, key) => {
        if(window.localStorage.getItem(key) == 1){
            window.localStorage.setItem(key, 0);
            Module.destroy?Module.destroy():{};
            return false;
        }else{
            window.localStorage.setItem(key, 1);
            Module.init?Module.init():{};
            return true;
        }
    }

    Program.Main.decamelize = (str, separator) => {
        separator = typeof separator === 'undefined' ? ' ' : separator;

        return str
            .replace(/([a-z\d])([A-Z])/g, '$1' + separator + '$2')
            .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + separator + '$2')
            .toLowerCase()
            .replace(/^\w/, c => c.toUpperCase());
    }

    Program.Main.createSettingsDialogue = (Module, key, title, parentKey) => {
        let buttons = []
        let noSubmenus = true;
        for(let i in Module){
            let submodule = Module[i].Module
            if(submodule && submodule.Submenu){
                noSubmenus = false;
                break;
            }
        }
        for(let i in Module){
            let submodule = Module[i].Module
            if(submodule){
                if(i == "uniqueNameOfShortcut"){
                    continue;
                }
                buttons.push(Program.Main.createButtonModule(i, submodule, key, submodule.title, submodule.image, noSubmenus));
            }
        }
        let dialogue = Program.Main.createDialogue(key, title, buttons, undefined, parentKey)
        Program.Main.IDs[key] = dialogue.id;
        dialogue.style.display = "none"
        let dialogueDiv = document.getElementById("dh3-dialogues");
        dialogueDiv.appendChild(dialogue);
    }

    Program.Main.showSettingsDialogue = (parentID, ID) => {
        try{
            window.closeDialogue(parentID)
        }catch(error){
            console.warn(error)
        }
        document.getElementById(ID).style.display = "";
    }

    Program.Main.createDialogue = (key, title, elements, onClose, parentKey) =>{
        let div = document.createElement("div");
        let id = `dialogue-${key}`
        div.id = id;
        div.className = "dialogue";
        div.style.width = "400px";
        div.style.paddingBottom = "50px";
        div.style.top = "0px";

        if(parentKey){
            let backDiv = document.createElement("div");
            backDiv.onclick = () => Program.Main.showSettingsDialogue(Program.Main.IDs[key],Program.Main.IDs[parentKey]);
            backDiv.style.width = "60px";
            backDiv.style.heigth = "50px";
            backDiv.style.position = "absolute";
            backDiv.style.border = "1px solid gray";
            backDiv.style.padding = "3px";
            backDiv.style.textAlign = "center";
            backDiv.style.backgroundColor = "#1a1a1a";
            backDiv.style.color = "white";
            backDiv.style.borderRadius = "6px";
            backDiv.style.cursor = "pointer";
            let backImg = document.createElement("img");
            backImg.src = "images/back.png";
            backImg.className = "img-40";
            backDiv.appendChild(backImg);
            let backText = document.createTextNode("Back")
            backDiv.appendChild(backText);
            div.appendChild(backDiv);
        }

        let center = document.createElement("center");
        let h1 = document.createElement("h1");
        h1.textContent = title;
        center.appendChild(h1);
        div.appendChild(center);

        div.appendChild(document.createElement("hr"));

        for(let i in elements){
            div.appendChild(elements[i]);
        }

        div.appendChild(document.createElement("br"));
        div.appendChild(document.createElement("br"));

        let closeDiv = document.createElement("div");
        closeDiv.onclick = onClose?onClose:() => {div.style.display = "none"};
        closeDiv.className= "dialogue-button";
        closeDiv.appendChild(document.createTextNode("Close"));
        closeDiv.style.cursor = "pointer";
        div.appendChild(closeDiv);

        return div;
    }

    Program.Main.createButtonModule = (key, Parent, parentID, title, imageURL, noSubmenus) => {
        let imgId = `img-${key}`;
        let callback = undefined;
        if(Parent.Submenu){
            Program.Main.createSettingsDialogue(Parent.Submenu, key, title, parentID);
            callback = () => Program.Main.showSettingsDialogue(Program.Main.IDs[parentID],Program.Main.IDs[key]);
        }else{
            callback = () => {document.getElementById(imgId).src = Program.Main.toggle(Parent, key)?imageURL:"images/stone.png"};
        }
        return Program.Main.createButton(key, title, Parent.description, (Parent.Submenu || window.localStorage.getItem(key)==1)?imageURL:"images/stone.png", callback, !!Parent.Submenu, noSubmenus);
    }

    Program.Main.createButton = (key, title, description, imageURL, callback, hasSubmenu, noSubmenus) => {
        let div = document.createElement("div");
        let imgId = `img-${key}`;
        div.onclick = callback;
        div.className = "dialogue-button";
        div.style.cursor = "pointer";
        div.style.marginBottom = "10px";
        if(description){
            let hoverDiv = document.createElement("div");
            hoverDiv.className = "dialogue-button";
            hoverDiv.style.width = "384px";
            hoverDiv.style.position = "absolute";
            hoverDiv.style.left = "505px";
            hoverDiv.style.marginTop = "-10px";
            hoverDiv.style.display = "none";
            let hoverh6 = document.createElement("h6")
            let hoverTitle = document.createTextNode("Description");
            hoverh6.appendChild(hoverTitle)
            hoverh6.style.margin = "10px";
            hoverh6.style.fontSize = "20px";
            hoverDiv.appendChild(hoverh6);
            let hoverp = document.createElement("p");
            let hoverText = document.createTextNode(description);
            hoverp.appendChild(hoverText);
            hoverp.style.margin = "10px";
            hoverDiv.appendChild(hoverp);
            div.appendChild(hoverDiv);
            div.onmouseover = ()=>{hoverDiv.style.display = ""};
            div.onmouseout = ()=>{hoverDiv.style.display = "none"};
        }

        let table = document.createElement("table");
        let tbody = document.createElement("tbody");
        let tr = document.createElement("tr");

        let td1 = document.createElement("td");
        let img = document.createElement("img");
        img.src = imageURL;
        img.className = "img-100";
        img.id = imgId;
        td1.appendChild(img);
        tr.appendChild(td1);

        let td2 = document.createElement("td");
        td2.style.textAlign = "right";
        td2.style.paddingRight = "20px";
        td2.style.color = "black";
        td2.style.fontSize = "24pt";
        let textNode = document.createTextNode(title.toUpperCase());
        td2.appendChild(textNode);
        tr.appendChild(td2);

        let td3 = document.createElement("td");
        td3.style.textAlign = "center";
        td3.style.paddingRight = "10px";
        td3.style.fontSize = "40px";
        td3.style.color = "black";
        td3.appendChild(Program.Main.createIcon("caret-right", true));
        td3.style.visibility = hasSubmenu?"visible":"hidden";
        td3.style.display = (key==="dh3fixed" || noSubmenus)?"none":"";
        tr.appendChild(td3);

        tbody.appendChild(tr);
        table.appendChild(tbody);
        div.appendChild(table);
        return div;
    }

    Program.Main.addSettingsButton = () => {
        let homeDivItemSection = document.getElementById("item-section-home-1");
        let itemBox = document.createElement("div");
        itemBox.className = "item-box";
        itemBox.onclick = ()=>Program.Main.showSettingsDialogue(undefined, Program.Main.IDs.dh3fixed)
        itemBox.id = "item-box-dh3-fixed";
        itemBox.style.backgroundColor = "rgb(189, 20, 79)"
        itemBox.style.border = "1px solid rgb(94, 10, 39)"

        let itemBoxTitle = document.createElement("div");
        itemBoxTitle.style.textAlign = "center";
        itemBoxTitle.style.fontWeight = "bold";
        itemBoxTitle.style.fontSize = "12pt";
        itemBoxTitle.style.color = "silver";
        itemBoxTitle.appendChild(document.createTextNode("DH3 FIXED"));
        itemBox.appendChild(itemBoxTitle);

        let itemBoxImgDiv = document.createElement("div");
        itemBoxImgDiv.style.textAlign = "center";
        let itemBoxImg = document.createElement("img");
        itemBoxImg.src = "images/eventSigil2.png"
        itemBoxImg.className = "img-100";
        itemBoxImg.style.imageRendering = "pixelated";
        itemBoxImgDiv.appendChild(itemBoxImg);
        itemBox.appendChild(itemBoxImgDiv);

        let itemBoxSpan = document.createElement("span");
        itemBoxSpan.style.textAlign = "center";
        itemBoxSpan.style.display = "block";
        itemBoxSpan.style.fontWeight = "bold";
        itemBoxSpan.appendChild(document.createTextNode(version));
        itemBox.appendChild(itemBoxSpan);

        homeDivItemSection.insertBefore(itemBox, homeDivItemSection.firstElementChild);
    }

    Program.Main.createIcon = (type, solid) => {
        let icon = document.createElement("i");
        icon.className = "fa"+(solid?"s":"r")+" fa-"+type;
        return icon;
    }

    Program.Main.init = () => {
        let fontAwesome = document.createElement("script");
        fontAwesome.src = "https://kit.fontawesome.com/84465c291e.js"
        fontAwesome.crossorigin = "anonymous";
        fontAwesome.type = "text/javascript";
        document.head.appendChild(fontAwesome);

        let dh3DialogueDiv = document.createElement("div");
        dh3DialogueDiv.id = "dh3-dialogues";
        document.body.appendChild(dh3DialogueDiv)
        Program.Main.addSettingsButton();
        Program.Main.createSettingsDialogue(Program, "dh3fixed", "DH3 Fixed");
    }

    /* for reference
    chop tree: LOOT_DIALOGUE=TREE~images/logs.png~6 ~#ffcc99~#663300~images/woodcuttingSkill.png~1,000 xp~#99ff99~#006600~none
    farm red mush: LOOT_DIALOGUE=RED MUSHROOM SEEDS~images/redMushroom.png~12 ~#ffcc99~#663300~images/farmingSkill.png~25 xp~#99ff99~#006600~

    loot ent: LOOT_DIALOGUE=ENT~images/logs.png~8 ~#ffcc99~#663300~images/willowLogs.png~7 ~#ffcc99~#663300~images/combatSkill.png~70 xp~#99ff99~#006600~

    enter combat: SET_ITEMS=currentFighingArea~forest
    damage: HIT_SPLAT=monster~1~images/stinger.png~red~black~
    tanked: HIT_SPLAT=hero~0~images/tankIcon.png~red~black~

    */

    //ActivityLog

    Program.ActivityLog = {}

    Program.ActivityLog.visible = false;

    Program.ActivityLog.saveEntry = (data) => {
        console.log(data);
        if(!localStorage.getItem("ActivityLog")){
            return;
        }
        let historyString = localStorage.getItem("ActivityLog.history."+window.var_username)
        if(!historyString){
            historyString = "[]";
        }
        console.log(historyString)
        let history = JSON.parse(historyString);
        history.push(data);
        console.log(history)
        if(history.length > 200){
            history = history.slice(100,history.length);
        }
        let saved = false;
        while(!saved && history.length >= 1){
            saved = Program.ActivityLog.saveToLocalStorage(history);
            history = history.slice(history.length/2, history.length);
        }
    }

    Program.ActivityLog.saveToLocalStorage = history => {
        console.log(history);
        let historyString = JSON.stringify(history)
        try{
            localStorage.setItem("ActivityLog.history."+window.var_username, historyString);
            return true;
        }
        catch(error){
            console.warn(error);
            console.log(`Could not save Activity log with ${history.length} items and a total of ${historyString.length} characters. Retrying with half size.`)
            return false;
        }
    }

    Program.ActivityLog.saveLoot = (data) => {
        Program.ActivityLog.saveEntry(data);
        let div = Program.ActivityLog.dataToDiv(data);
        let parent = document.getElementById("dialogue-activityLogDisplay");
        let first = parent.children[2];
        parent.insertBefore(div,first);
    }

    Program.ActivityLog.dataToDiv = (data) => {
        let array = data.split("~");
        if(array.length<4){
            return document.createElement("div");
        }
        let title = array[0];
        let items = [];
        let startIndex = 1;
        if(!isNaN(array[startIndex])){
            startIndex++;
        }
        for(let i = startIndex; i+2 < array.length; i+=4){
            items.push({
                imageSrc: array[i],
                amount: array[i+1],
                backgroundColor: array[i+2],
                borderColor: array[i+3]
            });
        }

        let div = document.createElement("div");
        div.style.color = "black";
        div.style.border = "solid grey 1px";
        div.style.backgroundColor = "white";
        div.style.margin = "10px";

        let h1 = document.createElement("h1");
        h1.style.textAlign = "center";
        h1.textContent = title;
        div.appendChild(h1);

        for(let key in items){
            let item = items[key];
            let span = document.createElement("span");
            span.className = "loot-span";
            span.style.backgroundColor = item.backgroundColor;
            span.style.border = `1px solid ${item.borderColor}`;

            let img = document.createElement("img");
            img.className = "img-40";
            img.src = item.imageSrc;
            let decamName = Program.Main.decamelize(item.imageSrc.slice(7,-4))
            img.alt = decamName;
            img.title = decamName;
            span.appendChild(img);

            let text = document.createTextNode(item.amount);
            span.appendChild(text);
            div.appendChild(span);
        }
        return div;
    }



    Program.ActivityLog.toggleLog = (event) => {
        if(event.keyCode == 9 &&
           !event.altKey &&
           !event.ctrlKey &&
           window.var_username){
            event.preventDefault();
            if(Program.ActivityLog.visible){
                let combatLogClose = document.getElementById("combat-log-fullscreen-div")
                if(combatLogClose){
                    combatLogClose.click();
                }else{
                    Program.ActivityLog.hideLog()
                }
            }else{
                Program.ActivityLog.showLog();
            }
        }
    }

    Program.ActivityLog.createLog = () => {
        let history = JSON.parse(localStorage.getItem("ActivityLog.history."+window.var_username));
        if(!history){
            history = [];
        }
        let elements = [];
        for(let i = history.length-1; i >= 0; i--){
            elements.push(Program.ActivityLog.dataToDiv(history[i]))
        }
        let dialogue = Program.Main.createDialogue("activityLogDisplay", "Activity Log", elements, Program.ActivityLog.hideLog);
        dialogue.style.display = "none";
        Program.ActivityLog.dialogueId = dialogue.id;
        Program.ActivityLog.visible = false;
        document.getElementById("dh3-dialogues").appendChild(dialogue);
    }

    Program.ActivityLog.showLog = () => {
        Program.ActivityLog.visible = true;
        document.getElementById(Program.ActivityLog.dialogueId).style.display = "";
    }
    Program.ActivityLog.hideLog = () => {
        Program.ActivityLog.visible = false;
        document.getElementById(Program.ActivityLog.dialogueId).style.display = "none";
    }

    Program.ActivityLog.init = () => {
        Program.WindowExtensions.add("lootDialogue", {
            module: "ActivityLog",
            func: Program.ActivityLog.saveLoot,
            priority: 2
        });
        const func = () => {
            if(window.var_username){
                //console.log("ok");
                Program.ActivityLog.createLog();
            }else{
                //console.log("waiting");
                setTimeout(func,1000);
            }
        }
        func();
        document.addEventListener("keydown", Program.ActivityLog.toggleLog);
    }

    Program.ActivityLog.destroy = () => {
        document.removeEventListener("keydown", Program.ActivityLog.toggleLog);
        document.getElementById(Program.ActivityLog.dialogueId).remove();
    }

    Program.ActivityLog.Module = {
        title: "Activity Log",
        image: "images/titanium.png",
        init: Program.ActivityLog.init,
        destroy: Program.ActivityLog.destroy,
        description: `Keeps track of loot and combat data. Press "tab" to open`,
        initialToggle: 1,
    }

    //Fixes
    Program.Fixes = {}


    Program.Fixes.keepAlive = (data) => {
        if(localStorage.getItem("keepAlive")==1){
            if(data.includes("TAB_OFF")){
                return {error: "Keep connection alive"};
            }
        }
    }

    Program.Fixes.styleSheet = Array.from(document.styleSheets).find(a=>a.href && a.href.includes("diamondhunt.app/css/style.css"));

    Program.Fixes.adjustCss = (selectorText, property, newValue) => {
        if(Program.Fixes.styleSheet){
            let css = Array.from(document.styleSheets[0].cssRules)
            let cssClass = css.find(a=>a.selectorText == selectorText);
            if(cssClass && cssClass.style){
                cssClass.style[property] = newValue
            }
        }
    }

    Program.Fixes.init = ()=>{
        //non-toggleable fixes
        Program.WindowExtensions.add("closeDialogue", {
            module: "Fixes",
            func: data=>{document.activeElement.blur()},
            priority: -1,
        });

        //toggleable fixes
        Program.WindowExtensions.add("sendBytes", {
            module: "keepAlive",
            func: Program.Fixes.keepAlive,
            priority: 1
        });
    }

    Program.Fixes.destroy = ()=>{}

    Program.Fixes.Module = {
        title: "Fixes",
        image: "images/promethium.png",
        init: Program.Fixes.init,
        destroy: Program.Fixes.destroy,
        description: `Cointains smaller, individually toggleable, changes and fixes`,
        initialToggle: 1,
        Submenu: {
            keepAlive: {
                Module: {
                    title: "Keep alive",
                    image: "images/promethium.png",
                    description: `Keeps the connection to the server alive even if the game loses focus`,
                    initialToggle: 0
                }
            }
        }
    }

    //Shortcuts

    Program.Shortcuts = {}

    Program.Shortcuts.functions = {};
    Program.Shortcuts.keyheld = {};

    Program.Shortcuts.keyup = (event)=>{
        Program.Shortcuts.keyheld[event.keyCode] = false;
        return false;
    };

    Program.Shortcuts.keydown = (event)=>{
        console.log(event)
        if(event.keyCode === 27 && //27 = Esc
           ["input","textarea"].includes(document.activeElement.localName)){
            document.activeElement.blur();
        }
        if(!Program.Shortcuts.keyheld[event.keyCode] &&
           Program.Shortcuts.functions[event.keyCode] &&
           !["input","textarea"].includes(document.activeElement.localName) &&
           !event.ctrlKey &&
           window.var_username){
            if(Program.Shortcuts.tryInvoke(event.keyCode)){
                event.preventDefault();
                Program.Shortcuts.keyheld[event.keyCode] = true;
            }
        }
        return false;
    };

    Program.Shortcuts.tryInvoke = (keycode) => {
        let functionList = Program.Shortcuts.functions[keycode];
        console.log(keycode, functionList);
        for(let key in functionList){
            if(functionList[key].func()){
                console.log(key);
                return true;
            }
        }
    }

    Program.Shortcuts.init = ()=>{
        document.addEventListener("keyup", Program.Shortcuts.keyup);
        document.addEventListener("keydown", Program.Shortcuts.keydown);
        Program.Shortcuts.functions = {};
        for(let key in Program.Shortcuts.Module.Submenu){
            let shortcutCategory = Program.Shortcuts.Module.Submenu[key];
            if(!shortcutCategory.Module){
                continue;
            }
            for(let subKey in shortcutCategory.Module.Submenu){
                let shortcut = shortcutCategory.Module.Submenu[subKey];
                let func = ()=>{
                    if(localStorage.getItem(subKey) == 1){
                        return shortcut.Module.func();
                    }
                }
                let charCode = shortcut.Module.key.charCodeAt(0)
                if(!Program.Shortcuts.functions[charCode]){
                    Program.Shortcuts.functions[charCode] = [];
                }
                Program.Shortcuts.functions[charCode].push({
                    priority: shortcut.Module.priority,
                    func
                });
            }
        }
        for(let charCode in Program.Shortcuts.functions){
            Program.Shortcuts.functions[charCode].sort((a,b)=>b.priority-a.priority)
        }
    }

    Program.Shortcuts.destroy = ()=>{
        document.removeEventListener("keyup", Program.Shortcuts.keyup);
        document.removeEventListener("keydown", Program.Shortcuts.keydown);
    }

    Program.Shortcuts.Module = {
        title: "Shortcuts",
        image: "images/gold.png",
        init: Program.Shortcuts.init,
        destroy: Program.Shortcuts.destroy,
        description: `Shortcuts for many common actions. Each shortcut can be toggled individually`,
        initialToggle: 1,
        Submenu: {
            tabChangeShortcuts: {
                Module: {
                    title: "Change tabs",
                    image: "images/gold.png",
                    description: `Shortcuts to navigate between parts of the game`,
                    initialToggle: 1,
                    Submenu: {
                        homeTab: {
                            Module: {
                                title: "(T) Home",
                                image: "images/gold.png",
                                description: `Change to the home tab`,
                                initialToggle: 1,
                                key: "T",
                                priority: 1,
                                func: ()=>{window.navigate('right-home');return true;}
                            }
                        },
                        combatTab: {
                            Module: {
                                title: "(E) Combat",
                                image: "images/gold.png",
                                description: `Change to the combat tab`,
                                initialToggle: 1,
                                key: "E",
                                priority: 1,
                                func: ()=>{navigate('right-combat');return true;}
                            }
                        },
                        magicTab: {
                            Module: {
                                title: "Ma(G)ic",
                                image: "images/gold.png",
                                description: `Change to the magic tab`,
                                initialToggle: 1,
                                key: "G",
                                priority: 1,
                                func: ()=>{navigate('right-magic');return true;}
                            }
                        },
                        mineTab: {
                            Module: {
                                title: "(M)ining",
                                image: "images/gold.png",
                                description: `Change to the mining tab`,
                                initialToggle: 1,
                                key: "M",
                                priority: 1,
                                func: ()=>{navigate('right-mining');return true;}
                            }
                        },
                        craftingTab: {
                            Module: {
                                title: "(C)rafting",
                                image: "images/gold.png",
                                description: `Change to the crafting tab`,
                                initialToggle: 1,
                                key: "C",
                                priority: 1,
                                func: ()=>{navigate('right-crafting');return true;}
                            }
                        },
                        woodcuttingTab: {
                            Module: {
                                title: "(W)oodcutting",
                                image: "images/gold.png",
                                description: `Change to the woodcutting tab`,
                                initialToggle: 1,
                                key: "W",
                                priority: 1,
                                func: ()=>{navigate('right-woodcutting');return true;}
                            }
                        },
                        farmingTab: {
                            Module: {
                                title: "(F)arming",
                                image: "images/gold.png",
                                description: `Change to the farming tab`,
                                initialToggle: 1,
                                key: "F",
                                priority: 1,
                                func: ()=>{navigate('right-farming');return true;}
                            }
                        },
                        brewingTab: {
                            Module: {
                                title: "(B)rewing",
                                image: "images/gold.png",
                                description: `Change to the brewing tab`,
                                initialToggle: 1,
                                key: "B",
                                priority: 1,
                                func: ()=>{navigate('right-brewing');return true;}
                            }
                        },
                        fishingTab: {
                            Module: {
                                title: "F(I)shing",
                                image: "images/gold.png",
                                description: `Change to the fishing tab`,
                                initialToggle: 1,
                                key: "I",
                                priority: 1,
                                func: ()=>{navigate('right-fishing');return true;}
                            }
                        },
                        cookingTab: {
                            Module: {
                                title: "C(O)oking",
                                image: "images/gold.png",
                                description: `Change to the cooking tab`,
                                initialToggle: 1,
                                key: "O",
                                priority: 1,
                                func: ()=>{navigate('right-cooking');return true;}
                            }
                        }
                    }
                }
            },
            craftingShortcuts: {
                Module: {
                    title: "Crafting",
                    image: "images/gold.png",
                    description: `Shortcuts that work within the crafting tab`,
                    initialToggle: 1,
                    Submenu: {
                        smeltInFurnace: {
                            Module: {
                                title: "(S) Smelt",
                                image: "images/gold.png",
                                description: `Open the furnace to select an ore to smelt`,
                                initialToggle: 1,
                                key: "S",
                                priority: 2,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-crafting").style.display != "none"){
                                        window.clicksItem(window.getBestFurnace())
                                        return true;
                                    }
                                }
                            }
                        },
                        smeltSand: {
                            Module: {
                                title: "(S) Sand",
                                image: "images/gold.png",
                                description: `While the dialogue for selecting what to smelt is open, press 'S' to smelt sand into glass`,
                                initialToggle: 1,
                                key: "S",
                                priority: 3,
                                func: ()=>{
                                    if(document.getElementById("dialogue-furnace").style.display != "none"
                                       && document.getElementById("dialogue-furnace-selectOre").children.length>0){
                                        openFurnaceDialogue2(window.getBestFurnace(),"sand")
                                        return true;
                                    }
                                }
                            }
                        },
                        smeltBronze: {
                            Module: {
                                title: "(B) Bronze",
                                image: "images/gold.png",
                                description: `While the dialogue for selecting what to smelt is open, press 'B' to smelt copper into bronze`,
                                initialToggle: 1,
                                key: "B",
                                priority: 2,
                                func: ()=>{
                                    if(document.getElementById("dialogue-furnace").style.display != "none"
                                       && document.getElementById("dialogue-furnace-selectOre").children.length>0){
                                        openFurnaceDialogue2(window.getBestFurnace(),"copper")
                                        return true;
                                    }
                                }
                            }
                        },
                        smeltIron: {
                            Module: {
                                title: "(R) Iron",
                                image: "images/gold.png",
                                description: `While the dialogue for selecting what to smelt is open, press 'R' to select iron`,
                                initialToggle: 1,
                                key: "R",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("dialogue-furnace").style.display != "none"
                                       && document.getElementById("dialogue-furnace-selectOre").children.length>0){
                                        openFurnaceDialogue2(window.getBestFurnace(),"iron")
                                        return true;
                                    }
                                }
                            }
                        },
                        smeltSilver: {
                            Module: {
                                title: "(L) Silver",
                                image: "images/gold.png",
                                description: `While the dialogue for selecting what to smelt is open, press 'L' to select silver`,
                                initialToggle: 1,
                                key: "L",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("dialogue-furnace").style.display != "none"
                                       && document.getElementById("dialogue-furnace-selectOre").children.length>0){
                                        openFurnaceDialogue2(window.getBestFurnace(),"silver")
                                        return true;
                                    }
                                }
                            }
                        },
                        smeltGold: {
                            Module: {
                                title: "(D) Gold",
                                image: "images/gold.png",
                                description: `While the dialogue for selecting what to smelt is open, press 'D' to select gold`,
                                initialToggle: 1,
                                key: "D",
                                priority: 2,
                                func: ()=>{
                                    if(document.getElementById("dialogue-furnace").style.display != "none"
                                       && document.getElementById("dialogue-furnace-selectOre").children.length>0){
                                        openFurnaceDialogue2(window.getBestFurnace(),"gold")
                                        return true;
                                    }
                                }
                            }
                        },
                        startSmelting: {
                            Module: {
                                title: "(S) Start smelt",
                                image: "images/gold.png",
                                description: `After selecting what ore to smelt, press 'S' to confirm and start smelting`,
                                initialToggle: 1,
                                key: "S",
                                priority: 4,
                                func: ()=>{
                                    if(document.getElementById("dialogue-furnace").style.display != "none"
                                       && document.getElementById("dialogue-furnace-selectOre").children.length==0){
                                        document.querySelectorAll("#dialogue-furnace-buttons-area div.dialogue-button")[1].click()
                                        return true;
                                    }
                                }
                            }
                        }
                    }
                }
            },
            woodcuttingShortcuts: {
                Module: {
                    title: "Woodcutting",
                    image: "images/gold.png",
                    description: `Shortcuts that work within the woodcutting tab`,
                    initialToggle: 1,
                    Submenu: {
                        chopPlot1: {
                            Module: {
                                title: "(1) Chop 1",
                                image: "images/gold.png",
                                description: `While in the woodcutting tab, chop the tree in plot 1`,
                                initialToggle: 1,
                                key: "1",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-woodcutting").style.display != "none"){
                                        window.sendBytes('CHOP_TREE=1');
                                        return true;
                                    }
                                }
                            }
                        },
                        chopPlot2: {
                            Module: {
                                title: "(2) Chop 2",
                                image: "images/gold.png",
                                description: `While in the woodcutting tab, chop the tree in plot 2`,
                                initialToggle: 1,
                                key: "2",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-woodcutting").style.display != "none"){
                                        window.sendBytes('CHOP_TREE=2');
                                        return true;
                                    }
                                }
                            }
                        },
                        chopPlot3: {
                            Module: {
                                title: "(3) Chop 3",
                                image: "images/gold.png",
                                description: `While in the woodcutting tab, chop the tree in plot 3`,
                                initialToggle: 1,
                                key: "3",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-woodcutting").style.display != "none"){
                                        window.sendBytes('CHOP_TREE=3');
                                        return true;
                                    }
                                }
                            }
                        },
                        chopPlot4: {
                            Module: {
                                title: "(4) Chop 4",
                                image: "images/gold.png",
                                description: `While in the woodcutting tab, chop the tree in plot 4`,
                                initialToggle: 1,
                                key: "4",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-woodcutting").style.display != "none"){
                                        window.sendBytes('CHOP_TREE=4');
                                        return true;
                                    }
                                }
                            }
                        },
                        chopPlot5: {
                            Module: {
                                title: "(5) Chop 5",
                                image: "images/gold.png",
                                description: `While in the woodcutting tab, chop the tree in plot 5`,
                                initialToggle: 0,
                                key: "5",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-woodcutting").style.display != "none"){
                                        window.sendBytes('CHOP_TREE=5');
                                        return true;
                                    }
                                }
                            }
                        },
                        chopPlot6: {
                            Module: {
                                title: "(6) Chop 6",
                                image: "images/gold.png",
                                description: `While in the woodcutting tab, chop the tree in plot 6`,
                                initialToggle: 0,
                                key: "6",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-woodcutting").style.display != "none"){
                                        window.sendBytes('CHOP_TREE=6');
                                        return true;
                                    }
                                }
                            }
                        }
                    }
                }
            },
            farmingShortcuts: {
                Module: {
                    title: "Farming",
                    image: "images/gold.png",
                    description: `Shortcuts that work within the farming tab`,
                    initialToggle: 1,
                    Submenu: {
                        plantDottedLeaf: {
                            Module: {
                                title: "(D) Plant dot",
                                image: "images/gold.png",
                                description: `While in the farming tab, click dotted green leaf seeds to plant them in individual plots`,
                                initialToggle: 1,
                                key: "D",
                                priority: 3,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-farming").style.display != "none"){
                                        window.clicksItem('dottedGreenLeafSeeds');
                                        return true;
                                    }
                                }
                            }
                        },
                        plantRedMushroom: {
                            Module: {
                                title: "(R) Plant mush",
                                image: "images/gold.png",
                                description: `While in the farming tab, click red mushroom seeds to plant them in individual plots`,
                                initialToggle: 1,
                                key: "R",
                                priority: 2,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-farming").style.display != "none"){
                                        window.clicksItem('redMushroomSeeds');
                                        return true;
                                    }
                                }
                            }
                        },
                        plantGreenLeaf: {
                            Module: {
                                title: "(N) Plant gr.",
                                image: "images/gold.png",
                                description: `While in the farming tab, click green leaf seeds to plant them in individual plots`,
                                initialToggle: 1,
                                key: "N",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-farming").style.display != "none"){
                                        window.clicksItem('greenLeafSeeds');
                                        return true;
                                    }
                                }
                            }
                        },
                        plantLimeLeaf: {
                            Module: {
                                title: "(L) Plant lime",
                                image: "images/gold.png",
                                description: `While in the farming tab, click lime leaf seeds to plant them in individual plots`,
                                initialToggle: 1,
                                key: "L",
                                priority: 2,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-farming").style.display != "none"){
                                        window.clicksItem('limeLeafSeeds');
                                        return true;
                                    }
                                }
                            }
                        },
                        plantPlot1: {
                            Module: {
                                title: "(1) Plant 1",
                                image: "images/gold.png",
                                description: `While in the farming tab, plant the selected seed in plot 1`,
                                initialToggle: 1,
                                key: "1",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-farming").style.display != "none"){
                                        window.clicksFarmingPlot(1);
                                        return true;
                                    }
                                }
                            }
                        },
                        plantPlot2: {
                            Module: {
                                title: "(2) Plant 2",
                                image: "images/gold.png",
                                description: `While in the farming tab, plant the selected seed in plot 2`,
                                initialToggle: 1,
                                key: "2",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-farming").style.display != "none"){
                                        window.clicksFarmingPlot(2);
                                        return true;
                                    }
                                }
                            }
                        },
                        plantPlot3: {
                            Module: {
                                title: "(3) Plant 3",
                                image: "images/gold.png",
                                description: `While in the farming tab, plant the selected seed in plot 3`,
                                initialToggle: 1,
                                key: "3",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-farming").style.display != "none"){
                                        window.clicksFarmingPlot(3);
                                        return true;
                                    }
                                }
                            }
                        },
                        plantPlot4: {
                            Module: {
                                title: "(4) Plant 4",
                                image: "images/gold.png",
                                description: `While in the farming tab, plant the selected seed in plot 4`,
                                initialToggle: 1,
                                key: "4",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-farming").style.display != "none"){
                                        window.clicksFarmingPlot(4);
                                        return true;
                                    }
                                }
                            }
                        },
                        plantPlot5: {
                            Module: {
                                title: "(5) Plant 5",
                                image: "images/gold.png",
                                description: `While in the farming tab, plant the selected seed in plot 5`,
                                initialToggle: 0,
                                key: "5",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-farming").style.display != "none"){
                                        window.clicksFarmingPlot(5);
                                        return true;
                                    }
                                }
                            }
                        },
                        plantPlot6: {
                            Module: {
                                title: "(6) Plant 6",
                                image: "images/gold.png",
                                description: `While in the farming tab, plant the selected seed in plot 6`,
                                initialToggle: 0,
                                key: "6",
                                priority: 1,
                                func: ()=>{
                                    if(document.getElementById("navigation-right-farming").style.display != "none"){
                                        window.clicksFarmingPlot(6);
                                        return true;
                                    }
                                }
                            }
                        }
                    }
                }
            },
            menuShortcuts: {
                Module: {
                    title: "Menus",
                    image: "images/gold.png",
                    description: `Shortcuts that deal with menus and dialogues`,
                    initialToggle: 1,
                    Submenu: {
                        confirmDialogue: {
                            Module: {
                                title: "(Space) Yes/No",
                                image: "images/gold.png",
                                description: `Confirm a dialogue with only two options, by selecting the top button`,
                                initialToggle: 1,
                                key: " ",
                                priority: 7,
                                func: ()=>{
                                    if(document.getElementById("dialogue-confirm").style.display != "none"){
                                        document.getElementById("dialogue-confirm-yes").click();
                                        return true;
                                    }
                                }
                            }
                        },
                        confirmDialogue2: {
                            Module: {
                                title: "(Space) Yes/?/?",
                                image: "images/gold.png",
                                description: `Confirm a dialogue with three options, by selecting the top button`,
                                initialToggle: 1,
                                key: " ",
                                priority: 6,
                                func: ()=>{
                                    if(document.getElementById("dialogue-confirm2").style.display != "none"){
                                        document.getElementById("dialogue-confirm2-yes").click();
                                        return true;
                                    }
                                }
                            }
                        },
                        sellItem: {
                            Module: {
                                title: "(S) Sell",
                                image: "images/gold.png",
                                description: `Confirm a dialogue for selling an item`,
                                initialToggle: 1,
                                key: "S",
                                priority: 11,
                                func: ()=>{
                                    if(document.getElementById("wild-dialogue") && document.getElementById("wild-dialogue").style.display != "none"){
                                        document.getElementById("dialogue-wild-input-confirm").click();
                                        return true;
                                    }
                                }
                            }
                        }
                    }
                }
            },
            customShortcuts: {
                DisabledModule: { //rename this to just 'Module' to enable. Without enabling this the shortcuts will not work, nor appear in the DHMFixed Menu
                    title: "Custom",
                    image: "images/gold.png",
                    description: `Here you can add your own shortcuts. Your additions will be deleted if the script updates, so make sure to save a copy of them somewhere else`,
                    initialToggle: 1,
                    Submenu: {
                        //start of a single shortcut. This example shortcut will not be added to the menus in the game, so you can leave it here for reference
                        uniqueNameOfShortcut: { //this has to be unique from all other module names, or they will not work. Just make it long and specific and you should be good
                            Module: {
                                title: "(Key) short name", //the name should ideally be short enough to only take up one line in the menu, but it will work with any length
                                image: "images/gold.png", // you can change this, but the 'off' state will still be 'images/stone.png', so it might look weird
                                description: `A description of what your shortcut does`,
                                initialToggle: 1, //If set to 0, will have to be enabled in the dh3fixed menu before it works
                                key: "A", //Single capital letter for the key that activates shortcut. Replace with the letter you want to use
                                priority: 15, //Decides the order in which shortcuts on the same letter will be executed. No other shortcut is over 14. The value is not restricted to integers, so you can use e.g. 3.5 to make it trigger between to other with 3 and 4 respectively
                                func: ()=>{ //this is the code that runs when the shortcut is triggered. Any javascript code can be put here, but most of my shortcuts follow this template, probably most of yours will as well
                                    if(document.getElementById("example-id") && document.getElementById("example-id").style.display != "none"){ // Check that the part of the game you are interacting with is visible. Look at other shortcuts for examples
                                        document.getElementById("the-id-of-the-button-you-want-to-click").click();
                                        //alternatively
                                        window.nameOfMethodThatCanBeRanFromTheConsole()
                                        return true; //return whether or not the shortcut has been executed. If false is returned, the next shortcut bound to this key will try to execute. If true is returned, nothing more happens.
                                    }
                                }
                            }
                        }, //end of a single shortcut
                        //create your own shortcuts below this point
                    }
                }
            }
        }
    }

    //WindowExtensions

    Program.WindowExtensions = {}

    Program.WindowExtensions.functions = {}
    Program.WindowExtensions._functions = {}
    Program.WindowExtensions.added = {}
    Program.WindowExtensions.initiated = {}
    Program.WindowExtensions.first = true;


    Program.WindowExtensions.add = (key, funcData) => {
        if(Program.WindowExtensions.added[funcData.module+"."+key]){
            console.log(funcData, key, " added already");
            return;
        }
        Program.WindowExtensions.added[funcData.module+"."+key] = true;
        if(Program.WindowExtensions.functions[key]){
            Program.WindowExtensions.functions[key].push(funcData);
        }else{
            let oldFunc = {
                module: funcData.module,
                func: window[key],
                priority: 0
            }
            Program.WindowExtensions.functions[key] = [oldFunc, funcData];
        }
        if(!Program.WindowExtensions.first){
            Program.WindowExtensions.init();
        }
    }

    Program.WindowExtensions.init = () => {
        for(let key in Program.WindowExtensions.functions){
            let sorted = Program.WindowExtensions.functions[key].sort((a,b)=>b.priority-a.priority);
            let funcs = undefined
            for(let i in sorted){
                console.log(key, i, sorted[i], funcs?"":"first");
                if(funcs){
                    funcs.push((data) => {return sorted[i].func(data)})
                }else{
                    funcs = [sorted[i].func]
                }
            }
            window[key] = (a,b,c,d,e,f,g,h,i,j,k,l,m) => {
                let data = [a,b,c,d,e,f,g,h,i,j,k,l,m]
                for(let i in funcs){
                    let result = funcs[i](data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7],data[8],data[9],data[10],data[11],data[12])
                    if(result){
                        if(result.error){
                            if(result.error.length>3){
                                console.log(result.error);
                            }
                            break;
                        }
                        if(result.modifications){
                            for(let j in result.modifications){
                                data[j] = result.modifications[j];
                            }
                        }
                    }
                }
            }
        }
        Program.WindowExtensions.first = false;
    }

    function initiate(Module){
        for(let key in Module){
            let module = Module[key].Module
            if(!module){
                //Module[key].init?Module[key].init():{}
                continue;
            }
            if(module.Submenu){
                initiate(module.Submenu);
            }
            let stored = window.localStorage.getItem(key);
            if(stored == 1){
                forceInit(module, key)
                console.log(`Module ${key} initiated`);
            }else if(stored == 0){
                console.log(`Module ${key} not initiated, toggled off`);
            }else{
                window.localStorage.setItem(key, module.initialToggle);
                module.initialToggle?(module.init?module.init():{}):{};
                console.log(`Module ${key} initiated, first time`);
            }
        }
    }
    let attempts = {}
    function forceInit(module, key){
        try{
            module.init?module.init():{}
        }catch(error){
            attempts[key] = attempts[key]?attempts[key]+1:1
            try{module.destroy()}catch(e){}
            if(attempts[key]>5){
                return;
            }
            console.error(error);
            console.warn(`Initiating module ${key} failed, retrying in 5 seconds`)
            setTimeout(()=>forceInit(module, key),5000)
        }
    }
    initiate(Program);
    forceInit(Program.Main,"Main");
    forceInit(Program.WindowExtensions,"WindowExtensions");
})();