hwmSetsMaster

Меню наборов армии, навыков и оружия. Смена фракции.

当前为 2024-09-30 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name           hwmSetsMaster
// @author         Tamozhnya1
// @namespace      Tamozhnya1
// @description    Меню наборов армии, навыков и оружия. Смена фракции.
// @version        12.0
// @include        *heroeswm.ru/*
// @include        *lordswm.com/*
// @exclude        */rightcol.php*
// @exclude        */ch_box.php*
// @exclude        */chat*
// @exclude        */ticker.html*
// @exclude        */frames*
// @exclude        */brd.php*
// @require        https://update.greasyfork.org/scripts/490927/1360667/Tamozhnya1Lib.js
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_deleteValue
// @grant 		   GM.xmlHttpRequest
// @license        MIT
// ==/UserScript==

if(!PlayerId) {
    return;
}
const Strings = {
        "ru": {
            Army: "Армия",
            Save: "Сохранить",
            Add: "Добавить",
            AddCurrent: "Добавить текущий",
            SetName: "Наименование набора",
            Delete: "Удалить",
            Talents: "Навыки",
            Weapon: "Оружие",
            RemoveAll: "Снять все",
            EnterJpg: "enter0.jpg",
            SignInTitle: "Войти",
            Castle: "Замок",
            Task: "Задание",
            Apply: "Применить",
            AvailablePoints: "Свободных очков",
            AvailableTalentPoints: "Свободных очков от навыка",
            IncreaseManyPointsTooltip: "Введите число от ${minValue} до ${maxValue}. Нажмите Tab."
        },
        "en": {
            Army: "Army",
            Save: "Save",
            Add: "Add",
            AddCurrent: "Add current",
            SetName: "Set name",
            Delete: "Delete",
            Talents: "Talents",
            Weapon: "Weapon",
            RemoveAll: "Un-equip all",
            EnterJpg: "enter0_eng.jpg",
            SignInTitle: "Sign in",
            Castle: "Castle",
            Task: "Task",
            Apply: "Apply",
            AvailablePoints: "Available points",
            AvailableTalentPoints: "Available talent points",
            IncreaseManyPointsTooltip: "Enter number from ${minValue} to ${maxValue}. Press Tab."
        }
    };
const LocalizedString = Strings[lang];
let Fraction;
getFraction();

//const homeArtsPanelSelector = doc => isNewPersonPage ? doc.querySelector("div#inv_doll_stats") : getParent(doc.querySelector("div.arts_info.shop_art_info"), "table");
const homeArtsPanelSelector = doc => isNewPersonPage ? doc.querySelector("div#inv_doll_stats") : doc.querySelector("body > center table.wb > tbody > tr:nth-child(3) > td > table:nth-child(2)");
const homeStatsPanelSelector = doc => isNewPersonPage ? doc.getElementById("home_css_stats_wrap_div") : getParent(doc.querySelector("img[src*='attr_attack']"), "table", 2);
const homeArmyPanelSelector = doc => isNewPersonPage ? doc.querySelector("div.home_pers_army") : doc.querySelector("center > div > div.cre_creature72").parentNode;
const playerInfoArtsPanelSelector = doc => getParent(doc.querySelector("div[class^='slot']"), "div");
const playerInfoStatsPanelSelector = doc => getParent(doc.querySelector("img[src*='attr_attack']"), "table");
const playerInfoArmyPanelSelector = doc => doc.querySelector("center > div > div.cre_creature72").parentNode;
const playerInfoPerksPanelSelector = doc => getParent(doc.querySelector("a[href^='showperkinfo.php']"), "table", 2);
const mapHuntButtonsPanelSelector = doc => doc.querySelector("div#neut_right_block div.map_buttons_container");
const mapHuntButtons2PanelSelector = doc => doc.querySelector("div#neut_right_block2 div.map_buttons_container");
const mapMercenaryTaskPanelSelector = doc => getParent(doc.querySelector("div#map_right_block_inside > table.wbwhite.rounded_table.map_table_margin center a[href='mercenary_guild.php']"), "center");
const inventoryStatsPanelSelector = doc => doc.querySelector("div.inventory_stats");

/************************************************************************************************************/
const weaponSetsPreferences = {
    menuTitle: LocalizedString.Weapon,
    menuImage: "https://dcdn3.heroeswm.ru/i/mobile_view_ny/icons/_panelInventory.png?v=3.23de65",
    setReferencePage: "inventory.php",
    sets: new Array(),
    menuItems: {},
    currentMenuItem: undefined,
    initSetsApplyAction: function() {
        this.sets.length = 0;
        this.sets.push({ number: 0, name: LocalizedString.RemoveAll, method: "GET", url: "/inventory.php?all_off=100" });
        const weaponSets = JSON.parse(getPlayerValue("WeaponSets", "{}"));
        for(const setNumber in weaponSets) {
            this.sets.push({ number: setNumber, name: weaponSets[setNumber], method: "GET", url: `/inventory.php?all_on=${setNumber}` });
        }
    },
    name: "WeaponSet",
    getCurrentSetName: function() { return "WeaponSet"; },
    onPageLoad: function() {
        if(/inventory.php/.test(location.href)) {
            const undressDiv = document.querySelector("div[id ='undress_all_div']");
            addSetChangerListener(undressDiv, this, 0);

            const setDivs = document.querySelectorAll("div[set_div_id]"); // Если setDivs.length = 0, то ничего не делаем - мы в заявке на бой
            if(setDivs.length > 0) {
                const weaponSets = {};
                for(const setDiv of setDivs) {
                    const setNumber = setDiv.getAttribute("set_div_id");
                    if(setDiv.hasAttribute("onclick")) {
                        weaponSets[setNumber] = setDiv.innerText;
                        addSetChangerListener(setDiv, this, setNumber);
                    }
                }
                setPlayerValue("WeaponSets", JSON.stringify(weaponSets));
            }
        }
        this.findSetChangersAndAddListener();
    },
    findSetChangersAndAddListener: function() {
        let setRefs = document.querySelectorAll("a[href^='inventory.php?all_off=100']");
        for(const setRef of setRefs) {
            addSetChangerListener(setRef, this, 0);
        }
        setRefs = document.querySelectorAll("a[href*='inventory.php?all_on=']");
        for(const weaponSetReference of setRefs) {
            let setNumber = weaponSetReference.getAttribute("href").split("all_on=")[1];
            addSetChangerListener(weaponSetReference, this, setNumber);
        }
    }
};
/***********************************************************************************************************/
const skillSetsPreferences = {
    menuTitle: LocalizedString.Talents,
    menuImage: "https://dcdn.heroeswm.ru/i/perks/2xleadership1.png",
    setReferencePage: "skillwheel.php",
    sets: new Array(),
    menuItems: {},
    currentMenuItem: undefined,
    onPageLoad: function() {
        const setRefs = document.querySelectorAll("a[href^='skillwheel.php?setuserperk']"); // skillwheel.php?setuserperk=1&prace=4&buildid=5 // skillwheel.php?rand=1&setstats=1&param0=20&param1=8&param2=0&param3=2
        const pageSets = {};
        for(const setRef of setRefs) {
            pageSets[setRef.innerHTML] = getUrlParamValue(setRef.href, "buildid");
            addSetChangerListener(setRef, this, parseInt(getUrlParamValue(setRef.href, "buildid")));
        }
        setPlayerFractionValue("SkillSets", JSON.stringify(pageSets));
    },
    initSetsApplyAction: function () {
        const skillSets = JSON.parse(getPlayerFractionValue("SkillSets", "{}"));
        this.sets = Object.keys(skillSets).map(x => { return { number: parseInt(skillSets[x]), name: x, method: "GET", url: `/skillwheel.php?setuserperk=1&prace=${Fraction}&buildid=${skillSets[x]}` }; });
    },
    name: "SkillSet",
    getCurrentSetName: function() { return getFractionKey("SkillSet"); },
};
/************************************************************************************************************/
const armySetsPreferences = {
    menuTitle: LocalizedString.Army,
    menuImage: "https://dcdn.heroeswm.ru/i/castle_im/btn_recruit.png",
    setReferencePage: "army.php",
    sets: new Array(),
    menuItems: {},
    currentMenuItem: undefined,
    setsTable: null,
    name: "ArmySet",
    getCurrentSetName: function() { return getFractionKey("ArmySet"); },
    initSetsApplyAction: function () {
        const armySetsData = JSON.parse(getPlayerFractionValue("ArmySets", "{}"));
        const armySets = Object.keys(armySetsData).map(x => { const data = armySetsData[x].split("|"); return { Id: x, Name: data[0], Army: data.slice(1).map(y => Number(y)) } });
        this.sets = armySets.map(x => { return { number: parseInt(x.Id), name: x.Name, title: x.Army.join("+"), method: "POST", url: "/army_apply.php", data: x.Army.map((x, i) => `countv${i + 1}=${x}`).join("&") }; });
        if(/\/army.php/.test(location.href)) {
            this.drawSetsTable(armySets);
        }
    },
    drawSetsTable: function(armySets) {
        const isInit = this.setsTable ? false : true;
        let container;
        if(isInit) {
            const hwm_for_zoom = document.getElementById("hwm_for_zoom");
            container = document.body;
            if(!isMobileInterface) {
                container = addElement("center", null, hwm_for_zoom);
            }
            const tableStyle = "background-color: #959595; border: 1px solid #f5c137; table-layout: fixed;" + (isMobileInterface ? "margin: 0 0 0 50px;" : "");
            this.setsTable = addElement("table", { style: tableStyle }, container);
        } else {
            this.setsTable.innerHTML = "";
            container = this.setsTable.parentNode;
        }

        const cellWidths = [100, 60, 60, 60, 60, 60, 60, 60, 10, 10];
        for(const cellWidth of cellWidths) {
            this.setsTable.innerHTML += `<col style="width: ${cellWidth}px;" />`;
        }
        this.drawTableHeader();
        for(const armySet of armySets) {
            this.drawSetRow(armySet);
        }
        if(isInit) {
            const saveButton = addElement("input", { type: "button", value: LocalizedString.Save, style: isMobileInterface ? "margin: 0 0 0 50px;" : "" }, container);
            saveButton.addEventListener("click", this.saveSets);
            const addCurrentButton = addElement("input", { type: "button", value: LocalizedString.AddCurrent }, container);
            addCurrentButton.addEventListener("click", function() { armySetsPreferences.drawSetRow(); });
        }
    },
    drawTableHeader: function () {
        if(!this.setsTable) {
            return;
        }
        const cellStyle = "border: 1px solid #f5c137; overflow: hidden;";
        const units = win.obj;
        //console.log(units)
        const tr = addElement("tr", null, this.setsTable);
        addElement("td", { innerHTML: LocalizedString.SetName.replace(/ /g, "<br>").replace(/-/g, "-<br>"), style: "font-weight: bold; font-size: 8pt; text-align: center; border: 1px solid #f5c137; overflow: hidden;" }, tr);
        for(let i = 1; i <= 7; i++) {
            addElement("td", { innerHTML: (units[i]?.name || "").replace(/ /g, "<br>").replace(/-/g, "-<br>"), style: "font-weight: bold; font-size: 8pt; text-align: center; border: 1px solid #f5c137; overflow: hidden;", onClick: `ChangeSlider(event, ${i}, 0);` }, tr);
        }
        addElement("td", { style: cellStyle }, tr);
        addElement("td", { style: cellStyle }, tr);
    },
    drawSetRow: function(armySet) {
        const isNew = armySet ? false : true;
        const units = win.obj;
        //console.log(`armySet: ${armySet}, units: ${units}`);
        //console.log(units.slice(1));
        armySet = armySet || { Id: (new Date()).getTime(), Name: "", Army: [units[1], units[2], units[3], units[4], units[5], units[6], units[7]].map(x => x?.nownumberd || 0) };
        const cellStyle = "border: 1px solid #f5c137; overflow: hidden;";
        if(!this.setsTable) {
            return;
        }
        const tr = addElement("tr", { setId: armySet.Id }, this.setsTable);
        let td = addElement("td", { style: cellStyle }, tr);
        let input = addElement("input", { value: armySet.Name, onfocus: `this.select();`, style: "width: 97px;" }, td);
        for(let i = 0; i < armySet.Army.length; i++) {
            td = addElement("td", { style: cellStyle }, tr);
            input = addElement("input", { value: armySet.Army[i], onfocus: `ChangeSlider(event, ${i + 1}, 0); this.select();`, type: "number", style: "min-width: 47px; width: 98%; text-align: right;" }, td);
        }
        td = addElement("td", { style: cellStyle }, tr);
        let delButton = addElement("input", { type: "button", value: "x", title: LocalizedString.Delete }, td);
        delButton.addEventListener("click", this.deleteSet);

        td = addElement("td", { style: cellStyle }, tr);
        if(!isNew) {
            let applyButton = addElement("input", { type : "button", value : "v", title : LocalizedString.Apply }, td);
            applyButton.addEventListener("click", function() { armySetsPreferences.saveSets(); applySet(null, armySetsPreferences, { number: parseInt(armySet.Id), name: armySet.Name, title: armySet.Army.join("+"), method: "POST", url: "/army_apply.php", data: armySet.Army.reduce((x, y, i) => `${x}&countv${i + 1}=${y}`, "") }); });
        }
    },
    saveSets: function () {
        const rows = Array.from(armySetsPreferences.setsTable.rows).slice(1);
        const armySetsData = rows.reduce((t, x) => ({ ...t, [x.getAttribute("setId")]: Array.from(x.cells).slice(0, 8).map(y => y.firstChild.value).join("|") }), {});
        setPlayerFractionValue("ArmySets", JSON.stringify(armySetsData));
    },
    deleteSet: function () {
        const table = this.parentNode.parentNode.parentNode;
        const row = this.parentNode.parentNode;
        table.removeChild(row);
    },
};
/************************************************************************************************************/
const fractionsPreferences = {
    menuTitle: LocalizedString.Castle,
    menuImage: "https://dcdn.heroeswm.ru/i/castle_im/btn_fraction.png",
    setReferencePage: "castle.php",
    sets: [],
    menuItems: {},
    currentMenuItem: undefined,
    initSetsApplyAction: function() {
        const fractions = JSON.parse(getPlayerValue("Fractions", "{}"));
        this.sets = Object.keys(fractions).map(x => { return { number: fractions[x], name: x, method: "GET", url: `/castle.php?change_clr_to=${fractions[x]}&sign=${getPlayerValue("Sign")}` }; });
    },
    name: "Fraction",
    getCurrentSetName: function() { return "Fraction"; },
    onPageLoad: async function () {
        await this.initCastlesList();
        this.findSetChangersAndAddListener();
    },
    initCastlesList: async function () {
        if(location.pathname == '/castle.php' || !getPlayerValue("Fractions")) {
            const doc = location.pathname == '/castle.php' ? document : await getRequest("/castle.php");
            const fractions = Array.from(doc.querySelectorAll("div.castle_faction_div_inside")).reduce((t, x) => ({...t, [x.getAttribute("hint")]: getUrlParamValue(x.firstChild.href, "show_castle_f") }), {});
            setPlayerValue("Fractions", JSON.stringify(fractions));
            const changeCastleRef = doc.querySelector("div.castle_yes_no_dialog a[href*='castle.php?change_clr_to']");
            if(changeCastleRef) {
                setPlayerValue("Sign", getUrlParamValue(changeCastleRef.href, "sign"));
            }
        }
    },
    findSetChangersAndAddListener: function() {
        const setRefs = document.querySelectorAll("a[href*='castle.php?change_clr_to']");
        for(const setRef of setRefs) {
            addSetChangerListener(setRef, this, getUrlParamValue(setRef.href, "change_clr_to"));
        }
    },
    setChanged: function(newSetNumber) {
        Fraction = newSetNumber;
        setPlayerValue("Fraction", Fraction);
        createMenu();
    }
};

/************************************************************************************************************/
const preferences = [weaponSetsPreferences, skillSetsPreferences, armySetsPreferences, fractionsPreferences];
const visibleMunuItems = [[], [], [], []];
main();
function main() {
    if(isHeartOnPage && Fraction) {
        update();
        for(const preference of preferences) {
            if(preference.onPageLoad) {
                preference.onPageLoad();
            }
        }
        createMenu();
        window.addEventListener("resize", function() { createMenu(true); });
        drowSkillChangers();
        if(location.pathname == '/home.php' && isNewPersonPage) {
            observe(document.querySelector("div#home_css_stats_wrap_div"), drowSkillChangers);
        }
    }
}
function update() {
    const fractionNumbers = [1, 101, 2, 102, 3, 103, 4, 104, 5, 105, 205, 6, 106, 7, 107, 8, 108, 9, 10];
    for(const fractionNumber of fractionNumbers) {
        //deletePlayerFractionValue("SkillSets", fractionNumber);
        const skillSetsOld = getValue(`SkillSets${PlayerId}Fraction${fractionNumber}`);
        const skillSets = getPlayerFractionValue("SkillSets", undefined, fractionNumber);
        // if(fractionNumber == 8) {
            // console.log(`fractionNumber: ${fractionNumber}, skillSetsOld: ${skillSetsOld}, skillSets: ${skillSets}, CurrentSkillSet: ${getValue(`CurrentSkillSet${PlayerId}Fraction${fractionNumber}`)}`);
        // }
        if(skillSetsOld && !skillSets) {
            setPlayerFractionValue("SkillSets", skillSetsOld, fractionNumber);
            setPlayerFractionValue("SkillSet", getValue(`CurrentSkillSet${PlayerId}Fraction${fractionNumber}`), fractionNumber);
        }
        //deletePlayerFractionValue("ArmySets", fractionNumber);
        const armySetsOld = getValue(`ArmySets${PlayerId}Fraction${fractionNumber}`);
        const armySets = getPlayerFractionValue("ArmySets", undefined, fractionNumber);
        // if(fractionNumber == 8) {
            // console.log(`fractionNumber: ${fractionNumber}, armySetsOld: ${armySetsOld}, armySets: ${armySets}, ArmySet: ${getValue(`ArmySet${PlayerId}Fraction${fractionNumber}`)}`);
        // }
        if(armySetsOld && !armySets) {
            setPlayerFractionValue("ArmySets", armySetsOld, fractionNumber);
            setPlayerFractionValue("ArmySet", getValue(`ArmySet${PlayerId}Fraction${fractionNumber}`), fractionNumber);
        }
        // if(fractionNumber == 8) {
            // console.log(`ArmySet: ${getPlayerFractionValue("ArmySet", undefined, fractionNumber)}`);
        // }
    }
}
function getTextWidth(text, font) {
    const canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas")); // re-use canvas object for better performance
    const context = canvas.getContext("2d");
    context.font = font;
    const metrics = context.measureText(text);
    return metrics.width;
}
function getCssStyle(element, prop) { return window.getComputedStyle(element, null).getPropertyValue(prop); }
function getCanvasFont(el = document.body) {
    const fontWeight = getCssStyle(el, 'font-weight') || 'normal';
    const fontSize = getCssStyle(el, 'font-size') || '16px';
    const fontFamily = getCssStyle(el, 'font-family') || 'Times New Roman';
    return `${fontWeight} ${fontSize} ${fontFamily}`;
}
function getOrCreateAndResizeDropdown(branchIndex, baseElement, style, toRight = false) {
    const dropdownId = `${baseElement.id}Dropdown`;
    let dropdown = document.getElementById(dropdownId);
    if(!dropdown) {
        dropdown = addElement("div", { id: dropdownId, style: `position: absolute; box-shadow: 3px 3px 5px #333;` + (style || "") }, document.body);
        if(isMobileDevice) {
            baseElement.addEventListener("click", function() {
                dropdown.style.display = dropdown.style.display == "none" ? "block" : "none";
                if(dropdown.style.display == "block") {
                    const dropdowns = document.querySelectorAll("div[id$=Dropdown]");
                    for(const dropdown of dropdowns) {
                        if(dropdown.id != dropdownId) {
                            dropdown.style.display = "none";
                        }
                    }
                }
            });
        } else {
            let hideTimer;
            const dropdownTimeout = 100;
            baseElement.addEventListener("mouseenter", function() { clearTimeout(hideTimer); if(dropdown.style.display == "none") { setTimeout(function() {
                dropdown.style.display = "block"; 
                baseElement.setAttribute("menuShown", "");
            }, dropdownTimeout); } });
            baseElement.addEventListener("mouseleave", function() { hideTimer = setTimeout(function() {
                dropdown.style.display = "none";
                baseElement.removeAttribute("menuShown");
            }, dropdownTimeout); });
            dropdown.addEventListener("mouseenter", function() {
                clearTimeout(hideTimer); 
                //console.log(`mouseenter dropdownId: ${dropdownId}`);
            });
            dropdown.addEventListener("mouseleave", function() {
                //console.log(`mouseleave dropdownId: ${dropdownId}`);
                const menuShown = dropdown.querySelector("[menuShown]");
                //console.log(menuShown)
                if(menuShown) {
                    return;
                }
                hideTimer = setTimeout(function() {
                    dropdown.style.display = "none";
                    baseElement.removeAttribute("menuShown");
                }, dropdownTimeout); 
            });
        }
    }
    const baseElementRect = baseElement.getBoundingClientRect();
    dropdown.style.top = `${baseElementRect.bottom + window.scrollY + 1}px`;
    dropdown.style.left = `${toRight ? baseElementRect.right : baseElementRect.left}px`;
    return dropdown;
}
function createMenu(isResize) {
    const menuPanel = document.querySelector("div#hwm_header") || document.querySelector("#main_top_table") || document.querySelector("body > table");
    if(!menuPanel) {
        armySetsPreferences.initSetsApplyAction();
        return;
    }
    const setsMenuPosition = getPlayerBool("ShowMenyAtRight") ? "right" : "left"; //const setsMenuPosition = isNewInterface && isMobileDevice || getPlayerBool("ShowMenyAtRight") ? "right" : "left";
    const homeRef = menuPanel.querySelector("a[href='home.php']");
    const framesRef = menuPanel.querySelector("a[href='frames.php']");
    const menuAnchor = setsMenuPosition == "left" ? (isNewInterface ? homeRef.parentNode : getParent(homeRef, "td", 3)) : (isNewInterface ? framesRef.parentNode : getParent(framesRef, "td", 3));
    if(!menuAnchor) {
        return;
    }
    const superSets = JSON.parse(getPlayerValue("SuperSets", "[]"));
    const anchorRect = menuAnchor.getBoundingClientRect();
    //console.log(anchorRect);
    const borderWidth = isNewInterface ? 1 : 2;
    const menuItemHeight = anchorRect.height - borderWidth * 2;
    const menuItemLineHeight = menuItemHeight - borderWidth;
    const menuItemTop = anchorRect.top + window.scrollY;
    const foreColor = "#f5c137";
    const backgroundColor = isNewInterface ? "linear-gradient(to top, #09203f 0%, #537895 100%)" : (document.querySelector("img[src*='i/top_ny']") ? "#003399" : "#6b6b69");
    const zIndex = location.pathname == "/photo_pl_photos.php" ? "0" : "100";
    let currentMenuItemLeft = setsMenuPosition == "left" ? (anchorRect.left - borderWidth - 1) : (anchorRect.right + borderWidth + 1);
    let previousWidth = 0;
    for(let i = preferences.length - 1; i >= 0; i--) {
        const currentPreferences = preferences[i];
        const mainMenuItemId = `SetsMenuItem${i}`;
        let mainMenuItem = document.getElementById(mainMenuItemId);
        let selectedValueHidden = document.getElementById(`hwmSetsMaster${currentPreferences.name}SelectedValue`);
        if(!mainMenuItem) {
            const menuHeaderStyle = `min-width: ${menuItemHeight}px; font-size: 9pt; position: absolute; border-radius: 5px; background: ${backgroundColor}; color: ${foreColor}; border: ${borderWidth}px solid ${foreColor}; padding: 0 3px 0 3px; font-weight: bold; text-align: center; z-index: ${zIndex};`;
            mainMenuItem = addElement("div", { id: mainMenuItemId, style: menuHeaderStyle }, document.body);
            let itemContent = currentPreferences.menuTitle;
            let itemTitle = "";
            if(currentPreferences.menuImage && isNewInterface) {
                itemTitle = currentPreferences.menuTitle;
                itemContent = `<img src="${currentPreferences.menuImage}" alt="${itemTitle}" style="height: 90%; margin-top: 2px; border-radius: 50%;">`;
            }
            let itemChild;
            if(isMobileDevice) {
                itemChild = addElement("span", { innerHTML: itemContent, style: `color: ${foreColor}; text-decoration: none;` }, mainMenuItem);
            } else {
                itemChild = addElement("a", { innerHTML: itemContent, href: currentPreferences.setReferencePage, style: `color: ${foreColor}; text-decoration: none;` }, mainMenuItem);
            }
            if(itemTitle != "") {
                itemChild.title = itemTitle;
            }
            selectedValueHidden = addElement("div", { id: `hwmSetsMaster${currentPreferences.name}SelectedValue`, hidden: "hidden" }, mainMenuItem);
            if(i == 3) {
                createBuildButton();
            }
            visibleMunuItems[i] = [mainMenuItem];
        }
        mainMenuItem.style.height = `${menuItemHeight}px`;
        mainMenuItem.style.lineHeight = `${menuItemLineHeight}px`;
        mainMenuItem.style.top = `${menuItemTop}px`;

        const mainMenuItemRect = mainMenuItem.getBoundingClientRect();
        currentMenuItemLeft = currentMenuItemLeft + (setsMenuPosition == "left" ? (- mainMenuItemRect.width) : previousWidth);
        previousWidth = mainMenuItemRect.width;
        mainMenuItem.style.left = `${currentMenuItemLeft}px`;

        const menuContent = getOrCreateAndResizeDropdown(i, mainMenuItem, ` z-index: ${zIndex}; list-style-position: inside; color: ${foreColor}; padding: 2px 3px 2px 3px; white-space: nowrap; background: ${backgroundColor};`);
        if(isResize) {
            continue;
        }
        currentPreferences.initSetsApplyAction();
        menuContent.style.display = "block"; // Перед заполнением покажем див для правильного определения его размеров (нужно, если он установлен в none)
        menuContent.innerHTML = '';
        const currentSetNumber = getPlayerValue(currentPreferences.getCurrentSetName(), -1);
        let maxClientWidth = menuContent.clientWidth;
        for(const currentSet of currentPreferences.sets) {
            const dropDownMenuItem = addElement("li", { type: "disc", style: "text-align: left;" }, menuContent);
            const currentSetReference = addElement("b", { id: `${currentPreferences.getCurrentSetName()}SetReference${currentSet.number}`, innerHTML: currentSet.name, title: currentSet.title || "", style: `color: ${foreColor}; cursor: pointer;` }, dropDownMenuItem);
            if(currentSet.url) {
                currentSetReference.addEventListener("click", function() { applySet(currentSetReference, currentPreferences, currentSet); });
            }
            if(currentSet.number == currentSetNumber) {
                selectedValueHidden.innerHTML = currentSet.number;
                markCurrent(currentSetReference, currentPreferences, currentSet.number);
            }
            currentPreferences.menuItems[currentSet.number] = currentSetReference;

            if(i == 3) {
                const fractionBuilds = superSets.filter(x => x.Fraction == currentSet.number);
                if(fractionBuilds.length > 0) {
                    const superDropdown = getOrCreateAndResizeDropdown(i, currentSetReference, ` z-index: ${Number(zIndex) + 1}; list-style-position: inside; color: ${foreColor}; padding: 2px 3px 2px 3px; white-space: nowrap; background: ${backgroundColor};`, true);
                    for(const build of fractionBuilds) {
                        const superDropdownMenuItem = addElement("li", { type: "disc", style: "text-align: left;" }, superDropdown);
                        const html = `${build.Name} <span id="deleteSuperSet${build.Id}Button" title='${isEn ? "Delete" : "Удалить"}' style="cursor: pointer; display: inline; color: yellow;">&times;</span>`
                        const superDropdownText = addElement("b", { name: "superDropdownText",  innerHTML: html, style: `color: ${foreColor}; cursor: pointer;` }, superDropdownMenuItem);
                        document.getElementById(`deleteSuperSet${build.Id}Button`).addEventListener("click", function(e) { e.stopPropagation(); deleteSuperSet(build.Id, superDropdownMenuItem); });
                        superDropdownMenuItem.addEventListener("click", function() { applyBuild(superDropdownText, build); });
                        if(build.Id == getPlayerValue("SuperSet")) {
                            superDropdownText.style.color = '#0f0';
                        }
                    }
                    superDropdown.style.display = "none";
                }
            }
            let currentWidth = getTextWidth(currentSet.name, getCanvasFont(currentSetReference));
            if(maxClientWidth < currentWidth) {
                maxClientWidth = currentWidth;
            }
        }
        if(i == 3) {
            const showAddSuperSetButtonLable = addElement("label", { for: "showAddSuperSetButtonCheckbox", innerText: (isEn ? 'Show button "Create super set"' : 'Показать кнопку "Создать билд"') + "\t" }, menuContent);
            const showAddSuperSetButtonCheckbox = addElement("input", { id: "showAddSuperSetButtonCheckbox", type: "checkbox" }, menuContent);
            showAddSuperSetButtonCheckbox.checked = getPlayerBool("ShowAddSuperSetButton", true);
            showAddSuperSetButtonCheckbox.addEventListener("change", function() { setPlayerValue("ShowAddSuperSetButton", this.checked); }, false);
            
            addElement("br", { }, menuContent);

            const showMenyAtRightLable = addElement("label", { for: "showMenyAtRightCheckbox", innerText: (isEn ? 'Show menu at right' : 'Показать меню справа') + "\t" }, menuContent);
            const showMenyAtRightCheckbox = addElement("input", { id: "showMenyAtRightCheckbox", type: "checkbox" }, menuContent);
            showMenyAtRightCheckbox.checked = getPlayerBool("ShowMenyAtRight");
            showMenyAtRightCheckbox.addEventListener("change", function() { setPlayerValue("ShowMenyAtRight", this.checked); }, false);
        }
        menuContent.style.minWidth = `${(maxClientWidth + 25)}px`;
        menuContent.style.display = "none";
    }
}
function createBuildButton() {
    if(!getPlayerBool("ShowAddSuperSetButton", true)) {
        return;
    }
    const fractionsMainMenuItem = document.getElementById(`SetsMenuItem3`);
    let addBuildButton = document.getElementById("addBuildButton");
    if(addBuildButton) {
        return;
    }
    if(isNewInterface) {
        addBuildButton = addElement("div", { id: "addBuildButton", class: "position_tr", innerHTML: `<img class="NotificationIcon" src="https://dcdn.heroeswm.ru/i/new_top/_panelBattles.png" style="width: 16px; height: 16px;" title="${isEn ? "Create build" : "Создать билд"}" >` }, fractionsMainMenuItem);
    } else {
        addBuildButton = addElement("a", { id: "addBuildButton", href: "javascript:void(0);", style: "text-decoration: none; vertical-align: bottom;", innerHTML: `<img src="https://dcdn.heroeswm.ru/i/new_top/_panelBattles.png" style="width: 16px; height: 16px; border-radius: 50%;" title="${isEn ? "Create build" : "Создать билд"}" >` }, fractionsMainMenuItem);
    }
    addBuildButton.addEventListener("click", function(e) { e.stopPropagation(); addBuild(); });
}
function addBuild() {
    const superSetName = prompt(isEn ? "Enter build name" : "Введите название билда");
    if(superSetName) {
        //deletePlayerValue("SuperSets");
        const superSets = JSON.parse(getPlayerValue("SuperSets", "[]"));
        superSets.push({
            Id: Date.now(),
            Name: superSetName,
            Fraction: Fraction,
            WeaponSet: getPlayerValue(weaponSetsPreferences.getCurrentSetName(), -1),
            SkillSet: getPlayerValue(skillSetsPreferences.getCurrentSetName(), -1),
            ArmySet: getPlayerValue(armySetsPreferences.getCurrentSetName(), -1)
        });
        setPlayerValue("SuperSets", JSON.stringify(superSets));
    }
}
function deleteSuperSet(id, menuItem) {
    const superSets = JSON.parse(getPlayerValue("SuperSets", "[]"));
    const deletingIndex = superSets.findIndex(x => x.Id == id);
    if(deletingIndex > -1) {
        superSets.splice(deletingIndex, 1);
        setPlayerValue("SuperSets", JSON.stringify(superSets));
        if(menuItem) {
            menuItem.remove();
        }
    }
}
async function applyBuild(superDropdownText, build) {
    const sets = [weaponSetsPreferences.name, skillSetsPreferences.name, armySetsPreferences.name];
    const originalText = superDropdownText ? superDropdownText.innerHTML : "";
    if(superDropdownText) {
        superDropdownText.innerHTML += " " + getWheelImage();
    }
    if(build.Fraction != Fraction) {
        await applySet(document.getElementById(`${fractionsPreferences.getCurrentSetName()}SetReference${build.Fraction}`), fractionsPreferences, fractionsPreferences.sets.find(x => x.number == build.Fraction), false);
        sets.push(fractionsPreferences.name);
    }
    await applySet(document.getElementById(`${weaponSetsPreferences.getCurrentSetName()}SetReference${build.WeaponSet}`), weaponSetsPreferences, weaponSetsPreferences.sets.find(x => x.number == build.WeaponSet), false);
    await applySet(document.getElementById(`${skillSetsPreferences.getCurrentSetName()}SetReference${build.SkillSet}`), skillSetsPreferences, skillSetsPreferences.sets.find(x => x.number == build.SkillSet), false);
    await applySet(document.getElementById(`${armySetsPreferences.getCurrentSetName()}SetReference${build.ArmySet}`), armySetsPreferences, armySetsPreferences.sets.find(x => x.number == build.ArmySet), false);
    await updatePanels(sets);
    setPlayerValue("SuperSet", build.Id);
    if(superDropdownText) {
        Array.from(document.querySelectorAll("b[name=superDropdownText]")).forEach(x => x.style.color = "#f5c137");
        superDropdownText.innerHTML = originalText;
        superDropdownText.style.color = '#0f0';
    }
}
function markCurrent(selectedMenuItem, currentPreferences, currentSetNumber) {
    setPlayerValue(currentPreferences.getCurrentSetName(), currentSetNumber);
    if(selectedMenuItem) {
        selectedMenuItem.style.color = '#0f0';
        if(currentPreferences.currentMenuItem && currentPreferences.currentMenuItem != selectedMenuItem) {
            currentPreferences.currentMenuItem.style.color = "#f5c137";
        }
        currentPreferences.currentMenuItem = selectedMenuItem;
    }
}
function addSetChangerListener(htmlElement, currentPreferences, setNumber) {
    htmlElement.addEventListener("click", function() { markCurrent(currentPreferences.menuItems[setNumber], currentPreferences, setNumber); });
}
async function applySet(selectedMenuItem, currentPreferences, currentSet, callPageRefreshFunction = true) {
    markCurrent(selectedMenuItem, currentPreferences, currentSet.number);
    const originalText = selectedMenuItem ? selectedMenuItem.innerHTML : "";
    if(selectedMenuItem) {
        selectedMenuItem.innerHTML += " " + getWheelImage();
    }
    if(currentSet.method == "POST") {
        await postRequest(currentSet.url, currentSet.data);
    } else {
        await getRequest(currentSet.url);
    }
    if(selectedMenuItem) {
        selectedMenuItem.innerHTML = originalText;
        if(isMobileDevice) {
            selectedMenuItem.parentNode.parentNode.style.display = "none";
        }
    }
    const selectedValueDiv = document.getElementById(`hwmSetsMaster${currentPreferences.name}SelectedValue`);
    if(selectedValueDiv) {
        selectedValueDiv.innerHTML = currentSet.number;
    }
    if(typeof(currentPreferences.setChanged) == "function") {
        await currentPreferences.setChanged(currentSet.number);
    }
    if(callPageRefreshFunction) {
        updatePanels([currentPreferences.name]);
    }
}
function drowSkillChangers() {
    if(location.pathname=='/home.php' && !document.querySelector(`#increaseattackAmountInput`)) {
        let skillsCount = 0;
        let re = new RegExp(isNewPersonPage ? `>${LocalizedString.AvailablePoints}:\\s(\\d+)<` : `<b>${LocalizedString.AvailablePoints}:</b>\\s(\\d+)`);
        const skillsExec = re.exec(document.body.innerHTML);
        if(skillsExec) {
            skillsCount += parseInt(skillsExec[1]);
        }
        re = new RegExp(isNewPersonPage ? `>${LocalizedString.AvailableTalentPoints}:\\s(\\d+)<` : `<b>${LocalizedString.AvailableTalentPoints}:</b>\\s(\\d+)`);
        const perksSkillsExec = re.exec(document.body.innerHTML);
        if(perksSkillsExec) {
            skillsCount += parseInt(perksSkillsExec[1]);
        }
        //console.log(`skillsCount: ${skillsCount}`);
        if(skillsCount == 0) {
            return;
        }
        const skillValueContainers = [];
        if(isNewPersonPage) {
            const container = document.querySelector("div#home_css_stats_wrap_div");
            const inv_stat_dataDivs = container.querySelectorAll("div.inv_stat_data.home_stat_data.show_hint");
            for(const inv_stat_dataDiv of inv_stat_dataDivs) {
                const increaseButton = inv_stat_dataDiv.querySelector("div.home_button2.btn_hover2");
                if(!increaseButton) {
                    continue;
                }
                const skillValueContainer = inv_stat_dataDiv.querySelector("div.inv_stat_text.home_stat_text");
                skillValueContainers.push(skillValueContainer);
            }
        } else {
            const increaseRefs = document.querySelectorAll("a[href^='home.php?increase=']"); //home.php?increase=defence
            for(const increaseRef of increaseRefs) {
                const sklilIncreaseCell = getParent(increaseRef, "td");
                const skillValueContainer = sklilIncreaseCell.previousSibling;
                skillValueContainers.push(skillValueContainer);
            }
        }
        const skillNames = ["attack", "defence", "power", "knowledge"];
        let i = 0;
        for(const skillValueContainer of skillValueContainers) {
            const skillValue = Number((skillValueContainer.querySelector("b") || skillValueContainer).innerText);
            skillValueContainer.innerHTML = "";
            const skill = skillNames[i];
            const increaseAmountInput = addElement("input", { id: `increase${skill}AmountInput`, name: "increaseAmountInput", value: skillValue, type: "number", min: skillValue, max: skillValue + skillsCount, size: 4, onfocus: "this.select();", title: LocalizedString.IncreaseManyPointsTooltip.replace("${minValue}", skillValue + 1).replace("${maxValue}", skillValue + skillsCount) }, skillValueContainer);
            increaseAmountInput.addEventListener("change", function() { const targetValue = Number(increaseAmountInput.value); if(targetValue > skillValue && targetValue <= skillValue + skillsCount) { changeSkill(increaseAmountInput, isNewPersonPage, skill, skillValue, targetValue); } });
            i++;
        }
    }
}
async function changeSkill(increaseAmountInput, isNewPersonPage, skill, currentValue, targetValue) {
    while(currentValue < targetValue) {
        const url = `/home.php?increase=${skill}` + (isNewPersonPage ? `&info=1&js_output=1&rand=${Math.random() * 1000000}` : "");
        const txt = await getRequestText(url, isNewPersonPage ? "text/html; charset=UTF-8" : "text/html; charset=windows-1251");
        currentValue++;
        if(isNewPersonPage) {
            if (txt.substring(0, 7) != 'HCSS_OK') {
                window.location = '/home.php?info';
                return;
            }
            const data = txt.split('@');
            const home_css_stats_wrap_div = document.getElementById('home_css_stats_wrap_div');
            if(data && data[1] && home_css_stats_wrap_div) {
                home_css_stats_wrap_div.innerHTML = data[1];
                if(data.length > 2 && document.getElementById('home_css_mana_count')) {
                    document.getElementById('home_css_mana_count').innerHTML = parseInt(data[2]);
                }
                if(typeof win.hwm_hints_init === 'function') win.hwm_hints_init();
            }
        } else {
            increaseAmountInput.value = currentValue;
        }
    }
    if(!isNewPersonPage) {
        location.reload();
    }
}
function getWheelImage() { return '<img border="0" align="absmiddle" height="11" src="https://dcdn.heroeswm.ru/css/loading.gif">'; }
function getFraction() {
    let currentFractionNumber;
    if(location.pathname == '/home.php') {
        // for new home page
        let currentFractionIconContainer = document.querySelector("div.home_css_pl_fract.show_hint");
        if(!currentFractionIconContainer) {
            currentFractionIconContainer = document.querySelector("a[href^='castle.php?change_faction_dialog']");
        }
        if(currentFractionIconContainer) {
            const currentFractionIconImg = currentFractionIconContainer.querySelector("img");
            currentFractionNumber = currentFractionIconImg.src.split("i/f/r")[1].split(".png")[0];
        }
    } else if(location.pathname=='/pl_info.php' && getUrlParamValue(location.href, "id") == PlayerId) {
        const fractionImage = document.querySelector("img[src*='i/f/r']");
        const regExp = new RegExp('\\/i\\/f\\/r(\\d+)\\.png');
        const regExpExec = regExp.exec(fractionImage.src);
        if(regExpExec) {
            currentFractionNumber = regExpExec[1];
        }
    } else if(location.pathname=='/castle.php') {
        const selectedFractionImg = document.querySelector("div.castle_faction_div_inside2 img");
        const selectedFractionImgName = selectedFractionImg.getAttribute("src");
        const selectedFractionNumber = selectedFractionImgName.split("kukla_png/kukla")[1].split(".")[0]; //dcdn.heroeswm.ru/i/kukla_png/kukla5.png

        const fractionsDiv = document.querySelector("div[id='faction_list']");
        if(fractionsDiv.getAttribute("style").includes("display:none;")) {
            currentFractionNumber = selectedFractionNumber;
        }
    }
    if(currentFractionNumber) {
        setPlayerValue("Fraction", currentFractionNumber);
    }
    Fraction = parseInt(getPlayerValue("Fraction"));
}
async function updatePanels(sets) {
    let pageReloadNeeded = false;
    let panels = [];
    if(sets.includes(weaponSetsPreferences.name) && ["/home.php", "/inventory.php", "/pl_info.php", "/map.php"].includes(location.pathname)) {
        pageReloadNeeded = true;
        if(location.pathname == '/home.php') {
            pushNew(panels, homeArtsPanelSelector);
            pushNew(panels, homeStatsPanelSelector);
            pageReloadNeeded = false;
        }
        if(location.pathname == '/pl_info.php') {
            if(getUrlParamValue(location.href, "id") == PlayerId) {
                pushNew(panels, playerInfoArtsPanelSelector);
                pushNew(panels, playerInfoStatsPanelSelector);
            }
            pageReloadNeeded = false;
        }
        if(location.pathname == '/map.php') {
            pushNew(panels, mapHuntButtons2PanelSelector);
            pushNew(panels, mapMercenaryTaskPanelSelector);
            pageReloadNeeded = false;
        }
    }
    if(sets.includes(skillSetsPreferences.name) && ["/skillwheel.php", "/pl_info.php", "/home.php", "/inventory.php"].includes(location.pathname)) {
        pageReloadNeeded = true;
        if(location.pathname == '/home.php') {
            pushNew(panels, homeStatsPanelSelector);
            pageReloadNeeded = false;
        }
        if(location.pathname == '/pl_info.php') {
            if(getUrlParamValue(location.href, "id") == PlayerId) {
                pushNew(panels, playerInfoStatsPanelSelector);
                pushNew(panels, playerInfoPerksPanelSelector);
            }
            pageReloadNeeded = false;
        }
        if(location.pathname == '/inventory.php') {
            pushNew(panels, inventoryStatsPanelSelector);
            pageReloadNeeded = false;
        }
    }
    if(sets.includes(armySetsPreferences.name) && ["/home.php", "/army.php", "/pl_info.php"].includes(location.pathname)) {
        pageReloadNeeded = true;
        if(location.pathname == '/home.php') {
            pushNew(panels, homeArmyPanelSelector);
            pageReloadNeeded = false;
        }
        if(location.pathname == '/pl_info.php') {
            if(getUrlParamValue(location.href, "id") == PlayerId) {
                pushNew(panels, playerInfoArmyPanelSelector);
            }
            pageReloadNeeded = false;
        }
    }
    if(sets.includes(fractionsPreferences.name) && ["/home.php", "/army.php", "/pl_info.php", "/castle.php", "/inventory.php", "/skillwheel.php"].includes(location.pathname)) {
        pageReloadNeeded = true;
    }
    if(pageReloadNeeded) {
        window.location.reload();
    } else {
        await refreshUpdatePanels(panels);
    }
}
// API
function getPlayerFractionValue(key, defaultValue, fraction = Fraction) { return getPlayerValue(getFractionKey(key, fraction), defaultValue); };
function setPlayerFractionValue(key, value, fraction = Fraction) { setPlayerValue(getFractionKey(key, fraction), value); };
function deletePlayerFractionValue(key, fraction = Fraction) { return deletePlayerValue(getFractionKey(key, fraction)); };
function getFractionKey(key, fraction = Fraction) { return `${key}${fraction}f`; }