Greasy Fork is available in English.

hwmAdvancedPlayerInfo

Добавляет в инфу персов и на домашнюю: ХП армии, очки и загрузку навыков, баланс рулетки и таверны, сумму умений, перекач, день рождения персонажа. Спойлеры. Статистика клана.

// ==UserScript==
// @name hwmAdvancedPlayerInfo
// @author alex_kocharin 2008-2012, Demin 2013-2015, Tamozhnya1 2024
// @namespace Tamozhnya1
// @version 18.8
// @description Добавляет в инфу персов и на домашнюю: ХП армии, очки и загрузку навыков, баланс рулетки и таверны, сумму умений, перекач, день рождения персонажа. Спойлеры. Статистика клана.
// @include     /^https{0,1}:\/\/(www|my)\.(heroeswm|lordswm)\.(ru|com)\/(home|pl_info|clan_info)\.php/
// @grant GM_deleteValue
// @grant GM_getValue
// @grant GM_setValue
// @grant GM.xmlHttpRequest
// @require https://update.greasyfork.org/scripts/490927/1360667/Tamozhnya1Lib.js
// @license MIT
// ==/UserScript==

// Включить/выключить загрузку навыков на домашней странице можно щелчком по надписи "Навыки". День рождения загружается щелчком по надписи. Стоимость боя показывается скриптом hwmOptimalRepairAtMarket. Туда же перенесены расчет ОА и крафта.
// Оригинальный скрипт https://userscripts-mirror.org/scripts/show/178809 (скрипт демина с greasyfork почему-то удален, как и таймеры)

if(!PlayerId) {
    return;
}
const unitsHealth = {
    peasant: [1, 4],
    conscript: [1, 6],
    archer: [1, 7],
    marksman: [1, 10],
    footman: [1, 16],
    squire: [1, 26],
    griffon: [1, 30],
    impergriffin: [1, 35],
    priest: [1, 54],
    inquisitor: [1, 80],
    cavalier: [1, 90],
    paladin: [1, 100],
    angel: [1, 180],
    archangel: [1, 220],

    brute: [101, 8],
    crossman: [101, 8],
    vindicator: [101, 23],
    battlegriffon: [101, 52],
    zealot: [101, 80],
    champion: [101, 100],
    seraph2: [101, 220],

    skeleton: [2, 4],
    skeletonarcher: [2, 4],
    zombie: [2, 17],
    plaguezombie: [2, 17],
    ghost: [2, 8],
    spectre: [2, 19],
    vampire: [2, 30],
    vampirelord: [2, 35],
    lich: [2, 50],
    archlich: [2, 55],
    wight: [2, 95],
    wraith: [2, 100],
    bonedragon: [2, 150],
    spectraldragon: [2, 160],

    sceletonwar: [102, 5],
    rotzombie: [102, 23],
    poltergeist: [102, 20],
    vampireprince: [102, 40],
    masterlich: [102, 55],
    banshee: [102, 110],
    ghostdragon: [102, 150],

    gremlin: [3, 5],
    mastergremlin: [3, 6],
    stone_gargoyle: [3, 15],
    obsgargoyle: [3, 20],
    iron_golem: [3, 18],
    steelgolem: [3, 24],
    mage: [3, 18],
    archmage: [3, 30],
    djinn: [3, 40],
    djinn_sultan: [3, 45],
    rakshasa_rani: [3, 120],
    rakshasa_raja: [3, 140],
    colossus: [3, 175],
    titan: [3, 190],

    saboteurgremlin: [103, 6],
    elgargoly: [103, 16],
    magneticgolem: [103, 28],
    battlemage: [103, 29],
    djinn_vizier: [103, 50],
    rakshasa_kshatra: [103, 135],
    stormtitan: [103, 190],

    pixel: [4, 5],
    sprite: [4, 6],
    dancer: [4, 12],
    wardancer: [4, 12],
    elf: [4, 10],
    masterhunter: [4, 14],
    druid: [4, 34],
    druideld: [4, 38],
    unicorn: [4, 57],
    silverunicorn: [4, 77],
    treant: [4, 175],
    ancienent: [4, 181],
    greendragon: [4, 200],
    emeralddragon: [4, 200],

    dryad: [104, 6],
    wdancer: [104, 14],
    arcaneelf: [104, 12],
    ddhigh: [104, 34],
    pristineunicorn: [104, 80],
    savageent: [104, 175],
    crystaldragon: [104, 200],

    goblin: [5, 3],
    hobgoblin: [5, 4],
    wolfrider: [5, 10],
    wolfraider: [5, 12],
    orc: [5, 12],
    orcchief: [5, 18],
    ogre: [5, 50],
    ogremagi: [5, 65],
    rocbird: [5, 55],
    thunderbird: [5, 65],
    cyclop: [5, 85],
    cyclopking: [5, 95],
    behemoth: [5, 210],
    ancientbehemoth: [5, 250],

    goblinarcher: [105, 3],
    boarrider: [105, 14],
    orcrubak: [105, 20],
    ogrebrutal: [105, 70],
    firebird: [105, 65],
    cyclopod: [105, 100],
    dbehemoth: [105, 280],

    goblinmag: [205, 3],
    orcshaman: [205, 13],
    darkbird: [205, 60],
    hyenarider: [205, 13],
    ogreshaman: [205, 55],
    shamancyclop: [205, 105],
    cursedbehemoth: [205, 250],

    scout: [6, 10],
    assassin: [6, 14],
    stalker: [6, 15],
    maiden: [6, 16],
    fury: [6, 16],
    bloodsister: [6, 24],
    minotaur: [6, 31],
    minotaurguard: [6, 35],
    taskmaster: [6, 40],
    darkrider: [6, 40],
    grimrider: [6, 50],
    briskrider: [6, 50],
    hydra: [6, 80],
    deephydra: [6, 125],
    foulhydra: [6, 125],
    shadow_witch: [6, 80],
    matriarch: [6, 90],
    mistress: [6, 100],
    shadowdragon: [6, 200],
    blackdragon: [6, 240],
    reddragon: [6, 235],

    imp: [7, 4],
    familiar: [7, 6],
    horneddemon: [7, 13],
    hornedoverseer: [7, 13],
    hellhound: [7, 15],
    cerberus: [7, 15],
    succubus: [7, 20],
    succubusmis: [7, 30],
    hellcharger: [7, 50],
    nightmare: [7, 66],
    pitfiend: [7, 110],
    pitlord: [7, 120],
    devil: [7, 166],
    archdevil: [7, 199],

    vermin: [107, 6],
    jdemon: [107, 13],
    hotdog: [107, 15],
    seducer: [107, 26],
    hellkon: [107, 66],
    pity: [107, 140],
    archdemon: [107, 211],

    defender: [8, 7],
    shieldguard: [8, 12],
    spearwielder: [8, 10],
    skirmesher: [8, 12],
    bearrider: [8, 25],
    blackbearrider: [8, 30],
    brawler: [8, 20],
    berserker: [8, 25],
    runepriest: [8, 60],
    runepatriarch: [8, 70],
    thane: [8, 100],
    thunderlord: [8, 120],
    firedragon: [8, 230],
    magmadragon: [8, 280],

    mountaingr: [108, 12],
    harpooner: [108, 10],
    whitebearrider: [108, 30],
    battlerager: [108, 30],
    runekeeper: [108, 65],
    flamelord: [108, 120],
    lavadragon: [108, 275],

    goblinus: [9, 3],
    trapper: [9, 4],
    fcentaur: [9, 6],
    ncentaur: [9, 9],
    warrior: [9, 12],
    mauler: [9, 12],
    shamaness: [9, 30],
    sdaughter: [9, 35],
    slayer: [9, 34],
    executioner: [9, 40],
    wyvern: [9, 90],
    foulwyvern: [9, 105],
    cyclopus: [9, 220],
    untamedcyc: [9, 225],

    goblinshaman: [109, 5],
    mcentaur: [109, 10],
    warmong: [109, 20],
    eadaughter: [109, 35],
    chieftain: [109, 48],
    poukai: [109, 120],
    bloodeyecyc: [109, 235],

    scorp: [10, 4],
    scorpup: [10, 5],
    duneraider: [10, 12],
    duneraiderup: [10, 12],
    shakal: [10, 24],
    shakalup: [10, 30],
    dromad: [10, 40],
    dromadup: [10, 45],
    priestmoon: [10, 50],
    priestsun: [10, 55],
    slon: [10, 100],
    slonup: [10, 110],
    anubis: [10, 160],
    anubisup: [10, 200]
};
const perkBranchCosts = {
    1: {
        knight_mark: 5,
        attack: 9,
        defense: 8,
        luck: 10,
        leadership: 7,
        dark: 8,
        light: 7
    },
    101: {
        benediction: 7,
        defense: 10,
        leadership: 10,
        enlightenment: 8,
        light: 6,
        summon: 10,
        sorcery: 7
    },
    2: {
        necr_soul: 5,
        defense: 11,
        enlightenment: 9,
        dark: 7,
        summon: 8,
        sorcery: 8
    },
    102: {
        powerraise: 6,
        attack: 9,
        luck: 10,
        enlightenment: 8,
        summon: 6,
        sorcery: 7
    },
    3: {
        magic_mirror: 7,
        enlightenment: 9,
        light: 10,
        summon: 8,
        destructive: 10,
        sorcery: 7
    },
    103: {
        nomagicdamage: 7,
        attack: 8,
        luck: 10,
        leadership: 9,
        enlightenment: 8,
        destructive: 9,
        sorcery: 8
    },
    4: {
        elf_shot: 7,
        attack: 10,
        defense: 9,
        luck: 7,
        leadership: 10,
        enlightenment: 10,
        light: 7,
        summon: 9
    },
    104: {
        zakarrow: 4,
        attack: 9,
        defense: 8,
        luck: 10,
        leadership: 8,
        enlightenment: 12
    },
    5: {
        barb_skill: 7,
        attack: 7,
        defense: 9,
        luck: 9,
        leadership: 10
    },
    105: {
        save_rage: 6,
        attack: 7,
        defense: 8,
        luck: 7,
        leadership: 9
    },
    205: {
        dark_blood: 5,
        defense: 9,
        luck: 10,
        leadership: 10,
        enlightenment: 8,
        dark: 6,
        sorcery: 7
    },
    6: {
        dark_power: 7,
        attack: 8,
        luck: 9,
        leadership: 10,
        enlightenment: 10,
        dark: 8,
        destructive: 9,
        sorcery: 7
    },
    106: {
        cre_master: 5,
        attack: 8,
        defense: 11,
        leadership: 8,
        luck: 10,
        enlightenment: 8,
        dark: 8
    },
    7: {
        hellfire: 6,
        attack: 7,
        defense: 9,
        luck: 10,
        dark: 8,
        destructive: 10,
        sorcery: 8
    },
    107: {
        consumecorpse: 5,
        defense: 9,
        leadership: 9,
        enlightenment: 9,
        dark: 6,
        sorcery: 7
    },
    8: {
        runeadv: 7,
        defense: 9,
        destructive: 11,
        light: 7,
        leadership: 9,
        luck: 10
    },
    108: {
        firelord: 5,
        attack: 7,
        defense: 10,
        leadership: 8,
        luck: 8,
        destructive: 6,
    },
    9: {
        memoryblood: 6,
        attack: 7,
        enlightenment: 9,
        leadership: 9,
        luck: 10,
        defense: 9
    },
    10: {
        dayandnight: 7,
        attack: 8,
        defense: 10,
        leadership: 10,
        light: 6,
        dark: 6,
        sorcery: 9,
        enlightenment: 9
    }
};
addStyle(`
.bar_wrap {
    width: 120px;
    /*margin: 3px 0 3px 9px;*/
    border: 1px solid #1C1C1C;
    background-color: #8C7526;
    box-shadow: 0 0 1px #666, inset 0 1px 1px #222;
    background-image: linear-gradient(#65541B, #8C7526 50%, #65541B);
    display: inline-block;
}
.bar {
    height: 5px;
    background-color: #f9e37e;
    border-right: 1px solid #282828;
    box-shadow: inset 0 0 1px #ddd;
    background-image: linear-gradient(#e7ae6b, #be8d55 50%, #a57b4b 51%, #ae804c);
    transition: all 1s ease;
    max-width: 150px;
}
.bar:hover {
    animation: animate-stripes 3s linear infinite;
}
@keyframes animate-stripes {
    0% {background-position: 0 0;}
    100% {background-position: 0 22px;}
}
.htooltip, .htooltip: visited, .tooltip: active {
    color: #0077AA;
    text-decoration: none;
}
.htooltip:hover {
    color: #0099CC;
}
.htooltip > span {
    background-color: rgba(0,0,0, 0.8);
    border-radius: 5px 5px 0px 0px;
    box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.5);
    color: #fff;
    margin-left: -1px;
    margin-top: -24px;
    opacity: 0;
    padding: 2px 5px;
    position: absolute;
    text-decoration: none;
    visibility: hidden;
    z-index: 10;
    transition: opacity 0.4s ease-in-out, visibility 0.4s ease-in-out;
}
.htooltip:hover > span {
    position: absolute;
    opacity: 1;
    visibility: visible;
}
.htooltip1 > span:first-child {
    background-color: rgba(0,0,0, 0.8);
    border-radius: 5px 5px 0px 0px;
    box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.5);
    color: #fff;
    margin-left: -1px;
    margin-top: -24px;
    opacity: 0;
    padding: 2px 5px;
    position: absolute;
    text-decoration: none;
    visibility: hidden;
    z-index: 10;
    transition: opacity 0.4s ease-in-out, visibility 0.4s ease-in-out;
}
.htooltip1:hover > span:first-child {
    position: absolute;
    opacity: 1;
    visibility: visible;
}
`);
var lang_en = {
    'Knight'               : 'Knight',
    'Necromancer'          : 'Necromancer',
    'Wizard'               : 'Wizard',
    'Elf'                  : 'Elf',
    'Barbarian'            : 'Barbarian',
    'Dark elf'             : 'Dark elf',
    'Demon'                : 'Demon',
    'Dwarf'                : 'Dwarf',
    'Steepe barbarian'     : 'Steepe barbarian',
    'Pharaoh'              : 'Pharaoh',
    'Combat level'         : 'Combat level',
    'Hunters\' guild'      : 'Hunters\' guild',
    'Laborers\' guild'     : 'Laborers\' guild',
    'Gamblers\' guild'     : 'Gamblers\' guild',
    'Thieves\' guild'      : 'Thieves\' guild',
    'Rangers\' guild'      : 'Rangers\' guild',
    'Mercenaries\' guild'  : 'Mercenaries\' guild',
    'Commanders\' guild'   : 'Commanders\' guild',
    'Watchers\' guild'     : 'Watchers\' guild',
    'Adventurers\' guild'  : 'Adventurers\' guild',
    'Leaders\' Guild'      : 'Leaders\' Guild',
    'Smiths\' guild'       : 'Smiths\' guild',
    'Enchanters\' guild'   : 'Enchanters\' guild',
    'Enchanters'   : 'Enchanters'
};
var lang_ru = {
    'Knight'               : 'Рыцарь',
    'Necromancer'          : 'Некромант',
    'Wizard'               : 'Маг',
    'Elf'                  : 'Эльф',
    'Barbarian'            : 'Варвар',
    'Dark elf'             : 'Темный эльф',
    'Demon'                : 'Демон',
    'Dwarf'                : 'Гном',
    'Steepe barbarian'     : 'Степной варвар',
    'Pharaoh'              : 'Фараон',
    'Combat level'         : 'Боевой уровень',
    'Hunters\' guild'      : 'Гильдия Охотников',
    'Laborers\' guild'     : 'Гильдия Рабочих',
    'Gamblers\' guild'     : 'Гильдия Картежников',
    'Thieves\' guild'      : 'Гильдия Воров',
    'Rangers\' guild'      : 'Гильдия Рейнджеров',
    'Mercenaries\' guild'  : 'Гильдия Наемников',
    'Commanders\' guild'   : 'Гильдия Тактиков',
    'Watchers\' guild'     : 'Гильдия Стражей',
    'Adventurers\' guild'  : 'Гильдия Искателей',
    'Leaders\' Guild'      : 'Гильдия Лидеров',
    'Smiths\' guild'       : 'Гильдия Кузнецов',
    'Enchanters\' guild'   : 'Гильдия Оружейников',
    'Enchanters'   : 'Оружейников'
};
const text = isEn ? lang_en : lang_ru;
const fractions = isEn ? ["Knight", "Necromancer", "Wizard", "Elf", "Barbarian", "Dark elf", "Demon", "Dwarf", "Tribal", "Pharaoh"]
 : ["Рыцарь", "Некромант", "Маг", "Эльф", "Варвар", "Темный эльф", "Демон", "Гном", "Степной варвар", "Фараон", "Кавалер", "Зомби-призыватель", "Волшебник", "Лесной", "Дикарь", "Зловещая тень", "Адский хулиган", "Карлик", "Кочевник", "Кот-фараон"];

main();
async function main() {
    tavernAndRouletteBalances();
    birthday();
    await getTalentsToHome();
    if(location.pathname == "/pl_info.php") {
        talentsStatistics();
    }
    calcArmyHealth(); // После загрузки навыков, если она есть, чтоб обработать навык "vitality"
    showExpBar();
    setTimeout(function() {
        const talentsChanger = document.getElementById("hwmSetsMasterSkillSetSelectedValue");
        const armyChanger = document.getElementById("hwmSetsMasterArmySetSelectedValue");
        if(talentsChanger) {
            if(location.pathname == "/pl_info.php" && getUrlParamValue(location.href, "id") == PlayerId) {
                observe([talentsChanger], talentsStatistics);
            }
            if(location.pathname == "/home.php" || location.pathname == "/pl_info.php" && getUrlParamValue(location.href, "id") == PlayerId) {
                const panels = [armyChanger];
                if(location.pathname == "/pl_info.php" && getUrlParamValue(location.href, "id") == PlayerId) {
                    panels.push(talentsChanger);
                }
                observe(panels, calcArmyHealth);
            }
        }
    }, 1500); // Здесь ждем загрузки hwmSetsMaster, чтоб подписаться на элемент hwmSetsMasterSkillSetSelectedValue и hwmSetsMasterArmySetSelectedValue
    addPlayerInfoSpoilers();
    clanStatistics();
}
async function getTalentsToHome() {
    if(location.pathname == '/home.php') {
        const playerLevel = getViewingPlayerLevel();
        if(playerLevel < 5) {
            return;
        }
        let hwmAdvancedPlayerInfoTalentsContainer = document.getElementById("hwmAdvancedPlayerInfoTalentsContainer");
        if(!hwmAdvancedPlayerInfoTalentsContainer) {
            let homePageTalentsContainer;
            if(isNewPersonPage) {
                homePageTalentsContainer = document.querySelector("div.home_friends_block");
            } else {
                homePageTalentsContainer = document.querySelector("body > center > table:nth-child(2) > tbody > tr:nth-child(1) > td:nth-child(1) > table > tbody > tr:nth-child(2) > td:nth-child(1) > table > tbody > tr > td > table > tbody > tr > td");
            }
            addElement("br", {}, homePageTalentsContainer);
            const ShowTalentsTitleSpan = addElement("span", { id: "hwmAdvancedPlayerInfoTalentsCaptionContainer", innerHTML: `&nbsp;&raquo;&nbsp;<b>${isEn ? "Talents" : "Навыки"}:</b><b id="hwmAdvancedPlayerInfoTalentsPointsContainer"></b>`, title: getShowTalentsTitleSpanTitle() }, homePageTalentsContainer);
            ShowTalentsTitleSpan.addEventListener("click", function() { setValue("ShowTalents", !getBool("ShowTalents")); this.title = getShowTalentsTitleSpanTitle(); getTalentsToHomeCore(); }, false);
            addElement("br", {}, homePageTalentsContainer);
            hwmAdvancedPlayerInfoTalentsContainer = addElement("div", { id: "hwmAdvancedPlayerInfoTalentsContainer" }, homePageTalentsContainer);
        }
        await getTalentsToHomeCore();
        const talentsChanger = document.getElementById("hwmSetsMasterSkillSetSelectedValue");
        if(talentsChanger) {
            observe([talentsChanger], getTalentsToHomeCore);
            //setTimeout(function() { observe([talentsChanger], getTalentsToHomeCore); }, 3000);
        }
    }
}
async function getTalentsToHomeCore() {
    if(location.pathname == '/home.php' && getBool("ShowTalents")) {
        const doc = await getRequest(`/pl_info.php?id=${PlayerId}`);
        const perksTable = getParent(doc.querySelector("a[href^='showperkinfo.php']"), "table", 2);
        if(perksTable) {
            Array.from(perksTable.querySelectorAll("a[href^='showperkinfo.php'] > img")).forEach(x => { x.style.width = `48px`; x.style.height = "auto"; });
            //Array.from(perksTable.querySelectorAll("a[href^='showperkinfo.php'] > img")).forEach(x => { x.setAttribute("title", x.getAttribute("hint")); x.style.width = `48px`; x.style.height = "auto"; });
            document.getElementById("hwmAdvancedPlayerInfoTalentsContainer").innerHTML = perksTable.outerHTML;
            if(typeof win.hwm_hints_init === 'function') win.hwm_hints_init();
            talentsStatistics();
            calcArmyHealth();
        }
    }
}
function getShowTalentsTitleSpanTitle() { return isEn ? `Click for ${getBool("ShowTalents") ? "disable" : "enable"} talents loading` : `Нажмите для ${getBool("ShowTalents") ? "выключения" : "включения"} загрузки навыков`; }
function talentsStatistics() {
    const playerLevel = getViewingPlayerLevel();
    if(playerLevel < 5) {
        return;
    }
    let talentsContainer;
    let talentsCaptionContainer;
    let talentsPointsContainer;
    if(location.pathname == "/home.php") {
        talentsContainer = document.getElementById("hwmAdvancedPlayerInfoTalentsContainer");
        talentsCaptionContainer = document.getElementById("hwmAdvancedPlayerInfoTalentsCaptionContainer");
        talentsPointsContainer = document.getElementById("hwmAdvancedPlayerInfoTalentsPointsContainer");
    }
    if(location.pathname == "/pl_info.php") {
        talentsContainer = getParent(document.querySelector("a[href^='showperkinfo.php?name=']"), "td", 3);
        const layerTable = getParent(talentsContainer, "table");
        talentsCaptionContainer = Array.from(layerTable.rows[0].cells).find(x => x.innerHTML.includes(isEn ? "Talents" : "Навыки")).querySelector("b");
        talentsPointsContainer = document.getElementById("hwmAdvancedPlayerInfoTalentsPointsContainer") || addElement("span", { id: "hwmAdvancedPlayerInfoTalentsPointsContainer" }, talentsCaptionContainer);
    }
    if(!talentsContainer || !talentsCaptionContainer) {
        return;
    }
    const talentBranchRows = talentsContainer.querySelectorAll(":scope > table > tbody > tr");
    const playerTalentBranches = Array.from(talentBranchRows).map(x => Array.from(x.querySelectorAll("a[href^='showperkinfo.php?name=']")).map(y => getUrlParamValue(y.href, "name")));
    //console.log(playerTalentBranches);
    let talentPoints = 0;
    const playerFraction = getPlayerFraction();
    const playerPerkBranchCosts = perkBranchCosts[playerFraction];
    //console.log(playerPerkBranchCosts);
    for(const playerPerkBranche of playerTalentBranches) {
        let branchName = playerPerkBranche[0];
        let firstPerkPromout = 1;
        if(["1", "2", "3"].includes(branchName.slice(-1))) {
            firstPerkPromout = parseInt(branchName.slice(-1));
            branchName = branchName.slice(0, -1);
        }
        //console.log(`branchName: ${branchName}, branchCost: ${playerPerkBranchCosts[branchName]}, branchePerks: ${playerPerkBranche.length}, firstPerkPromout: ${firstPerkPromout}`);
        talentPoints += playerPerkBranchCosts[branchName] * (playerPerkBranche.length + firstPerkPromout - 1);
    }
    talentsPointsContainer.innerText = ` (${isEn ? "points" : "очки"} ${talentPoints} ${isEn ? "from" : "из"} ${10 + 5 * (playerLevel - 5)})`;
}
function calcArmyHealth() {
    let armyContainer;
    if(location.pathname == "/pl_info.php") {
        armyContainer = getParent(document.querySelector(".cre_creature72"), "center");
    }
    if(location.pathname == "/home.php") {
        if(isNewPersonPage) {
            armyContainer = document.querySelector("div.home_css_creature_list");
        } else {
            armyContainer = getParent(document.querySelector(".cre_creature72"), "center");
        }
    }
    if(!armyContainer) {
        return;
    }
    const creatures = Array.from(document.querySelectorAll(`div.${isNewPersonPage ? "castle_creature54" : "cre_creature72"}`)).filter(x => x.querySelector("div#add_now_count")).map(x => ({ Name: getUrlParamValue(x.querySelector("a[href^='army_info.php?name=']").href, "name"),  Amount: parseInt(x.querySelector("div#add_now_count").innerText) }));
    const hasVitality = document.querySelector("a[href='showperkinfo.php?name=vitality']") ? true : false;
    const sum = creatures.reduce((t, x) => {
        const army = unitsHealth[x.Name];
        if(army) {
            t += x.Amount * (army[1] + (hasVitality ? 2 : 0));
        }
        return t;
    }, 0);
    const unknownCreatures = creatures.filter(x => !unitsHealth[x.Name]);
    let unknownCreaturesMessage = "";
    if(unknownCreatures.length > 0) {
        unknownCreaturesMessage = isEn ? `Creatures not found: ${unknownCreatures.map(x => x.Name).join(', ')}. Please, contact developer.` : `Существа не найдены: ${unknownCreatures.map(x => x.Name).join(', ')}. Пожалуйста, сообщите разработчику.`;
    }
    let hwmAdvancedPlayerInfoArmyHealthContainer = document.getElementById("hwmAdvancedPlayerInfoArmyHealthContainer");
    if(!hwmAdvancedPlayerInfoArmyHealthContainer) {
        armyContainer.insertAdjacentHTML("afterend", `&nbsp;»&nbsp;<b>${isEn ? "Total HP" : "Общее HP"}:&nbsp;</b><span id=hwmAdvancedPlayerInfoArmyHealthContainer>${sum}</span>
<a id=hwmAdvancedPlayerInfouUknownCreaturesMessageContainer href="javascript:alert('${unknownCreaturesMessage}');" title="${unknownCreaturesMessage}"> (?)</a>`);
        hwmAdvancedPlayerInfoArmyHealthContainer = document.getElementById("hwmAdvancedPlayerInfoArmyHealthContainer");
    }
    hwmAdvancedPlayerInfoArmyHealthContainer.innerText = sum;
    hwmAdvancedPlayerInfoArmyHealthContainer.style.color = hasVitality ? "#ff0000" : "";
    const hwmAdvancedPlayerInfouUknownCreaturesMessageContainer = document.getElementById("hwmAdvancedPlayerInfouUknownCreaturesMessageContainer");
    hwmAdvancedPlayerInfouUknownCreaturesMessageContainer.style.display = unknownCreaturesMessage ? "" : "none";
    hwmAdvancedPlayerInfouUknownCreaturesMessageContainer.title = unknownCreaturesMessage;
    hwmAdvancedPlayerInfouUknownCreaturesMessageContainer.href = `javascript:alert('${unknownCreaturesMessage}');`;
}
function tavernAndRouletteBalances() {
    if(location.pathname != "/pl_info.php") {
        return;
    }
    // Баланс рулетки
    const rouletteDebitText = findChildrenTextContainsValue("td", isEn ? "Roulette bets total" : "Поставлено в рулетке");
    const rouletteCreditText = findChildrenTextContainsValue("td", isEn ? "Roulette winnings total" : "Выиграно в рулетке");
    if(rouletteDebitText.length == 1 && rouletteCreditText.length == 1 && rouletteDebitText[0].nextSibling.tagName.toLowerCase() == "b" && rouletteCreditText[0].nextSibling.tagName.toLowerCase() == "b") {
        const rouletteBalance = rouletteCreditText[0].nextSibling.innerText.replace(/,/g, "") - rouletteDebitText[0].nextSibling.innerText.replace(/,/g, "");
        rouletteCreditText[0].nextSibling.insertAdjacentHTML("afterend", `<br>&nbsp;&nbsp;${isEn ? 'Balance' : 'Баланс'}: <b>${rouletteBalance.toLocaleString()}</b>`);
    }
    // Баланс таверны
    var statisticsSecondCell = getParent(document.querySelector("td.wb > a[href^='pl_transfers.php?id']"), "td").nextSibling;
    var tavern_parent = statisticsSecondCell.querySelector("tr").parentNode.childNodes[0].childNodes[1].firstChild.firstChild.childNodes;
    const winsRow = tavern_parent[1];
    const failsRow = tavern_parent[2];
    tavern_parent[0].childNodes[1].setAttribute('style', 'white-space: nowrap;');
    winsRow.childNodes[1].setAttribute('style', 'white-space: nowrap;');
    //console.log(winsRow)
    failsRow.childNodes[1].setAttribute('style', 'white-space: nowrap;');
    var tavern_0bal = winsRow.querySelector("tr");
    var tavern_1bal = failsRow.querySelector("tr");
    if(!tavern_0bal && !tavern_1bal) return;
    let wins = 0;
    let fails = 0;
    if(tavern_0bal) {
        wins = parseInt(winsRow.cells[1].querySelector("b").innerText.replace(/,/g, ""));
        tavern_0bal.childNodes[1].setAttribute('style', 'text-align: right; padding-right: 5px;');
        tavern_0bal = tavern_0bal.childNodes[1].innerHTML.replace(/,/g, "");
        winsRow.childNodes[3].firstChild.width = "100%";
        var totalRow = winsRow.cloneNode(true);
    } else {
        tavern_0bal = 0;
    }
    if(tavern_1bal) {
        fails = parseInt(failsRow.cells[1].querySelector("b").innerText.replace(/,/g, ""));
        tavern_1bal.childNodes[1].setAttribute('style', 'text-align: right; padding-right: 5px;');
        tavern_1bal = tavern_1bal.childNodes[1].innerHTML.replace(/,/g, "");
        failsRow.childNodes[3].firstChild.width = "100%";
        var totalRow = totalRow || failsRow.cloneNode(true);
    } else {
        tavern_1bal = 0;
    }
    var tavern_bal = tavern_0bal - tavern_1bal;
    totalRow.firstChild.innerHTML = `&nbsp;&nbsp;${isEn ? 'Balance:' : 'Баланс:'}`;
    totalRow.childNodes[1].innerHTML = `<b>${(wins - fails).toLocaleString()}</b>`;
    totalRow.childNodes[2].innerHTML = "&nbsp;";
    totalRow.childNodes[3].innerHTML = `
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td>
                <img class="rs" width="24" height="24" src="https://dcdn2.heroeswm.ru/i/r/48/gold.png?v=3.23de65" border="0" title="${isEn ? "Gold" : "Золото"}" alt="">
            </td>
            <td style="text-align: right; padding-right: 5px;">
            ${tavern_bal.toLocaleString()}
            </td>
        </tr>
    </tbody>
</table>`;
    failsRow.insertAdjacentElement("afterend", totalRow);
}
function birthday() {
    if(location.pathname == "/pl_info.php") {
        const playerId = getUrlParamValue(location.href, "id");
        let bifthday = GM_getValue(`PlayerBifthday${playerId}`, "");
        const districtBold = Array.from(document.querySelectorAll("b")).find(x => x.innerHTML.includes(isEn ? "Location" : "Район"));
        districtBold.nextElementSibling.nextElementSibling.insertAdjacentHTML("afterend", `&nbsp;&raquo;&nbsp;<b id=hwmAdvancedPlayerInfoBirthdayCaption title="${isEn ? "Click to download birthday" : "Нажмите для загрузки дня рождения"}">${isEn ? "Birthday" : "День рождения"}: </b><span id=hwmAdvancedPlayerInfoBirthday>${bifthday}</span>`);
        document.getElementById("hwmAdvancedPlayerInfoBirthdayCaption").addEventListener("click", loadBirthday);
    }
}
async function loadBirthday() {
    const playerId = getUrlParamValue(location.href, "id");
    const firstProtocolPage = await getRequest(`/pl_transfers.php?id=${playerId}&page=50000`);
    const lines = firstProtocolPage.querySelector(`div#set_mobile_max_width > div > div`).innerHTML.split("<br>").filter(x => x != "");
    bifthday = lines[lines.length - 2];
    bifthday = bifthday.replace(/&nbsp;/g, "");
    GM_setValue(`PlayerBifthday${playerId}`, bifthday);
    document.getElementById("hwmAdvancedPlayerInfoBirthday").innerHTML = bifthday;
}
function showExpBar() {
    if(location.pathname != '/home.php' && location.pathname != '/pl_info.php') {
        return;
    }
    const player = { SkillLevel: [], SkillNumber: []};
    let skillInfoCell;
    if(isNewPersonPage) {
        const levelInfoCell = Array.from(document.querySelectorAll("div.home_pers_info")).find(x => x.innerHTML.includes(text['Combat level']));
        player.Level = parseInt(levelInfoCell.querySelector("div[id=bartext] > span").innerText);
        player.Expirience = parseInt(levelInfoCell.querySelector("div.home_text_exp").firstChild.textContent.replace(/,/g, ""));

        const homeContainerBlocks = Array.from(document.querySelectorAll("div#set_mobile_max_width > div > div.home_container_block"));
        //console.log(homeContainerBlocks)
        skillInfoCell = homeContainerBlocks.find(x => x.innerHTML.includes(text['Knight']) || x.innerHTML.includes('Кавалер'));
        Array.from(skillInfoCell.querySelectorAll("div[id=row]")).forEach((x, i) => {
            player.SkillLevel[i] = parseInt(x.querySelector("div#bartext span").innerText);
            player.SkillNumber[i] = parseFloat(x.querySelector("div.home_text_exp").firstChild.textContent);
        });
        const guildsDiv = homeContainerBlocks.find(x => x.innerHTML.includes(text['Enchanters']));
        Array.from(guildsDiv.querySelectorAll("div[id=row]")).forEach(x => {
            const key = findKey(text, y => text[y].includes(x.querySelector("span.home_guild_text").innerText));
            if(key) {
                player[key] = parseInt(x.querySelector("div#bartext span").innerText);
            }
        });
    } else {
        const levelInfoBold = Array.from(document.querySelectorAll("td > b")).find(x => x.innerHTML.includes(text['Combat level']));
        const levelRegex = new RegExp(`${text['Combat level']}: (\\d{1,2})`);
        player.Level = parseInt(levelRegex.exec(levelInfoBold.innerHTML)[1]);
        const expirienceRegex = /\(([\d\.\,]+)\)/g;
        const expirienceRegexExec = expirienceRegex.exec(levelInfoBold.parentNode.innerHTML);
        if(expirienceRegexExec) {
            player.Expirience = parseInt(expirienceRegexExec[1].replace(/,/g, ""));
        }
        skillInfoCell = Array.from(document.querySelectorAll("td")).find(x => (x.innerHTML.includes(text['Knight']) || x.innerHTML.includes('Кавалер')) && x.innerHTML.includes(text['Enchanters\' guild']) && !x.innerHTML.includes("<td"));
        const regex = /\(([\d\.\,]+)\)/g;
        for(const fraction of fractions) {
            const skillRegex = new RegExp(`${fraction}: (\\d{1,2})`);
            const skillsData = skillRegex.exec(skillInfoCell.innerHTML);
            if(!skillsData) {
                continue;
            }
            player.SkillLevel.push(parseInt(skillsData[1]));
            const skillsNumberData = regex.exec(skillInfoCell.innerHTML);
            let skillNumber = 0;
            if(skillsNumberData) {
                skillNumber = parseFloat(skillsNumberData[1]);
            }
            player.SkillNumber.push(skillNumber);
        }
        for(const key of Object.keys(text).filter(x => x.includes("guild") || x.includes("Guild"))) {
            const guildName = text[key];
            let guildRegex = new RegExp(`&nbsp;&nbsp;${guildName}: (\\d{1,2}) \\(`, "g");
            if(key == 'Hunters\' guild') {
                guildRegex = new RegExp(`&nbsp;&nbsp;${guildName}: .+>(\\d{1,2})<`, "g");
            }
            player[key] = parseInt(guildRegex.exec(skillInfoCell.innerHTML)[1]);
        }
    }
    player.GuildStatsNumber = getGuildStatsNumber(player);
    player.MainSkillStatsNumber = getMainSkillStatsNumber(player);
    player.SkillSum = Math.floor(player.SkillLevel.reduce((t, x) => t + x, 0) * 0.25);
    player.TotalPoints = player.GuildStatsNumber + player.MainSkillStatsNumber + player.SkillSum;
    player.TotalSkills = round00(player.SkillNumber.reduce((t, x) => t + x, 0));
    player.Ratio = player.TotalSkills > 0 ? Math.round(player.Expirience / player.TotalSkills) : 0;
    //console.log(player);
    if(player.Level > 2) {
        //console.log(`isNewPersonPage: ${isNewPersonPage}`)
        const playerPersents = getPlayerPercents(player);
        let resumeText =  "В норме!";
        let resumeTitle =  "";
        const prevEdgeSpan = getSpanHtml(playerPersents.prevEdge, playerPersents.prevEdgeClarification);
        const totalPointsSpan = getSpanHtml(player.TotalPoints, isEn ? "Player points" : "Очков игрока");
        const guildStatsNumberSpan = getSpanHtml(player.GuildStatsNumber, isEn ? "total guilds parameters" : "сумма параметров от всех гильдий");
        const skillSumSpan = getSpanHtml(player.SkillSum, isEn ? "total factions skill levels*0.25" : "сумма уровней умений фракций*0.25");
        const mainSkillStatsNumberSpan = getSpanHtml(player.MainSkillStatsNumber, isEn ? "max faction skill level parameters (with possible faction potion)" : "сумма параметров от максимального умения фракции(с учётом возможного применения \"зелья фракции\")");
        const nextEdgeSpan = getSpanHtml(playerPersents.nextEdge, playerPersents.nextEdgeClarification);
        const progressText = `${isEn ? "Main progress" : "Основной прогресс"}: ${playerPersents.normalPercent}%. ${prevEdgeSpan}->${totalPointsSpan}=(${guildStatsNumberSpan}+${skillSumSpan}+${mainSkillStatsNumberSpan})->${nextEdgeSpan}`;
        if(playerPersents.advacedPersent > 0){
            resumeText = `Перекач! ${isEn ? "Exp" : "Опыт"}: +${playerPersents.advacedPersent}%`;
            //resumeTitle = `${isEn ? "More experience" : "Опыта больше на"}: ${playerPersents.advacedPersent}%`;
        }
        if(playerPersents.advacedPersent < 0) {
            resumeText = `Недокач!`;
            resumeTitle = `${isEn ? "more skill points will be awarded in proportion to the missing points" : "будет начисляться больше очков умений в соотношении к недостающим очкам"}`;
        }
        const resumeSpan = getSpanHtml(resumeText, resumeTitle);
        if(isNewPersonPage) {
            const ratioSpan = getSpanHtml(player.Ratio.toLocaleString(), isEn ? "Expirience points to one skill point" : "Очков опыта на одно очко умения");
            skillInfoCell.querySelector("div.home_inside_margins").insertAdjacentHTML("beforeend", `
<div class="home_scroll_content" id="row" onclick="show_all_hwm_exp();">
    <span class="home_guild_text">${isEn ? "Total skills" : "Сумма умений"}</span> <div class="home_text_exp" style="display: block;">${player.TotalSkills.toLocaleString()} (${ratioSpan})</div>
</div>
<div class="home_scroll_content htooltip1" id="row" onclick="show_all_hwm_exp();">
    <span>${progressText}</span>
    <span class="home_guild_text">${isEn ? "Progress" : "Прогресс"}
    <div id="bar" class="home_bar_exp" style="opacity: 1;"><div id="barprogress" style="width: ${playerPersents.normalPercent}%;"></div></div>
    <div class="home_text_exp" style="display: block;">${playerPersents.normalPercent}% ${resumeSpan}</div>
`);
        } else {
            const levelInfoBold = getParent(Array.from(document.querySelectorAll("td > b")).find(x => x.innerHTML.includes(isEn ? "Combat level:" : "Боевой уровень:")), "td").querySelector("br");
            //levelInfoBold.insertAdjacentHTML("afterend", `<meter id="playerPointsMeter" min="0" max="147" low="${playerPersents.beginNormalPoint}" high="${playerPersents.endNormalPoint}" optimum="${playerPersents.averagePoint}" value="${player.TotalPoints}"></meter>`);
            levelInfoBold.insertAdjacentHTML("afterend", `
<span id=hwmAdvancedPlayerInfoTotalSkillsSpan>
    &nbsp;&raquo;&nbsp;<b>${isEn ? "Skills count" : "Сумма умений"}: </b>${player.TotalSkills.toLocaleString()}, ${isEn ? "ratio" : "соотношение"}: <span title="${isEn ? "Expirience points to one skill point" : "Очков опыта на одно очко умения"}">${ player.Ratio.toLocaleString() }</span>
</span>
<br>&nbsp;&raquo;&nbsp;<b>${isEn ? "Progress" : "Прогресс"}:</b> <div class="bar_wrap htooltip">
    <div class="bar" style="width: ${playerPersents.normalPercent}%"></div>
    <span>${progressText}</span>
</div>
<div style='font-size: 8px; font-weight: bold; display: inline-block;'>${playerPersents.normalPercent}% ${resumeSpan}</div>`);
        }
    }
}
function getSpanHtml(value, title) { return `<span title="${title}">${value}</span>`; }
function getGuildStatsNumber(player) {
    const mercStats = [0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11];
    const commandersStats = [0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
    const watchersStats = [0, 1, 2, 3, 4, 5, 6, 7, 7.5, 8.5];
    const leadersStats = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5, 0.5, 1];
    const hunters = player['Hunters\' guild'];
    const laborers = Math.floor(player['Laborers\' guild'] / 2);
    const thieves = Math.max(player['Thieves\' guild'], player['Rangers\' guild']) / 2;
    const mercenaries = mercStats[player['Mercenaries\' guild']];
    const commanders = commandersStats[player['Commanders\' guild']];
    const watchers = watchersStats[player['Watchers\' guild']];
    const adventurers = player['Adventurers\' guild'];
    const leaders = leadersStats[player['Leaders\' Guild']];
    //console.log(`Hunters: ${hunters}, Laborers: ${laborers}, Thieves/Rangers: ${thieves}, Mercenaries: ${mercenaries}, Commanders: ${commanders}, Watchers: ${watchers}, Adventurers: ${adventurers}, Leaders: ${leaders}`);
    return hunters + laborers + thieves + mercenaries + commanders + watchers + adventurers + leaders;
}
function getMainSkillStatsNumber(player) {
    let skillLevel = player.SkillLevel.reduce((t, x) => Math.max(t, x), 0);
    const averageSkill = [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 10, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14];
    skillLevel = Math.max(skillLevel, averageSkill[player.Level]);
    const skillStats = [0, 1, 2, 2.5, 4, 5.5, 6.5, 9, 11.5, 16, 21, 27, 33, 35, 36.5];
    return skillStats[skillLevel];
}
function getPlayerPercents(player) {
    //player.Level++; //для тестов
    const averagePoints = [0, 0, 0, 3, 5, 7, 10, 13, 17, 20, 25, 29, 35, 41, 48, 58, 68, 77, 84, 90, 96, 101, 108, 114,       125, 136, 147];
    const penaltyProcents = [0, 25, 500, 1000];
    // Ищем перекач // 101, 108, 114, 125
    let advacedPersent = 0;
    const nextLevels = averagePoints.slice(player.Level + 1, player.Level + 5);
    const prevLevels = averagePoints.slice(player.Level - 4, player.Level).reverse();
    //console.log(prevLevels)
    const averagePoint = averagePoints[player.Level];
    let beginNormalPoint = prevLevels[0];
    let endNormalPoint = nextLevels[0] + 2;
    //console.log(`beginNormalPoint: ${beginNormalPoint}, endNormalPoint: ${endNormalPoint}, player.TotalPoints: ${player.TotalPoints}, player.Level: ${player.Level}`)
    let prevEdge = beginNormalPoint;
    let nextEdge = endNormalPoint;
    let prevEdgeClarification = isEn ? `Average points on ${player.Level - 1} level` : `Средние очки на ${player.Level - 1} уровне`;
    let nextEdgeClarification = isEn ? `Average points + 2 on ${player.Level + 1} level` : `Средние очки + 2 на ${player.Level + 1} уровне`;
    if(player.TotalPoints <= endNormalPoint) {
        if(player.TotalPoints < beginNormalPoint) {
            // Недокач - будет начисляться больше очков умений в соотношении к недостающим очкам
        }
        if(player.TotalPoints < beginNormalPoint) {
            prevLevels.forEach((x, i) => {
                const add = i == 0 ? 0 : 0;
                if(player.TotalPoints < (x - add) && player.TotalPoints >= prevLevels[i + 1]) {
                    const beginPoint = x - add;
                    const endPoint = prevLevels[i + 1];
                    const beginProcent = -penaltyProcents[i] - 1;
                    const endProcent = penaltyProcents[i + 1];
                    const advacedPoints = beginPoint - player.TotalPoints;
                    const percentStep = Math.round((endProcent - beginProcent) / (endPoint - beginPoint));
                    advacedPersent = beginProcent + percentStep * advacedPoints;
                    //console.log(`excessLevel: ${i}, player.TotalPoints: ${player.TotalPoints}, levelAveragePoints: ${x}, add: ${add}, beginPoint: ${beginPoint}, endPoint: ${endPoint}, beginProcent: ${beginProcent}, endProcent: ${endProcent}, advacedPoints: ${advacedPoints}, percentStep: ${percentStep}, advacedPersent: ${advacedPersent}`);
                    prevEdge = endPoint;
                    nextEdge = beginPoint;
                    prevEdgeClarification = isEn ? `Average points on ${player.Level - (i + 2)} level` : `Средние очки на ${player.Level - (i + 2)} уровне`;
                    nextEdgeClarification = isEn ? `Average points on ${player.Level - (i + 1)} level` : `Средние очки на ${player.Level - (i + 1)} уровне`;
                }
                if(i == prevLevels.length - 1 && player.TotalPoints < prevLevels[i + 1]) {
                    advacedPersent = penaltyProcents[i + 1];
                    //console.log(`excessLevel: ${i}, player.TotalPoints: ${player.TotalPoints}, levelAveragePoints: ${x}, add: ${add}, advacedPersent: ${advacedPersent}`);
                }
            });
        }
    } else {
        nextLevels.forEach((x, i) => {
            const add = i == 0 ? 2 : 0;
            if(player.TotalPoints > (x + add) && player.TotalPoints <= nextLevels[i + 1]) {
                const beginPoint = x + add;
                const endPoint = nextLevels[i + 1];
                const beginProcent = penaltyProcents[i] + 1;
                const endProcent = penaltyProcents[i + 1];
                const advacedPoints = player.TotalPoints - beginPoint;
                const percentStep = Math.round((endProcent - beginProcent) / (endPoint - beginPoint));
                advacedPersent = beginProcent + percentStep * advacedPoints;
                //console.log(`excessLevel: ${i}, player.TotalPoints: ${player.TotalPoints}, levelAveragePoints: ${x}, add: ${add}, beginPoint: ${beginPoint}, endPoint: ${endPoint}, beginProcent: ${beginProcent}, endProcent: ${endProcent}, advacedPoints: ${advacedPoints}, percentStep: ${percentStep}, advacedPersent: ${advacedPersent}`);
                prevEdge = x + add;
                nextEdge = endPoint;
                prevEdgeClarification = isEn ? `Average points${add > 0 ? ` + ${add}` : ""} on ${player.Level + 1 + i} level` : `Средние очки${add > 0 ? ` + ${add}` : ""} на ${player.Level + 1 + i} уровне`;
                nextEdgeClarification = isEn ? `Average points on ${player.Level + 1 + i + 1} level` : `Средние очки на ${player.Level + 1 + i + 1} уровне`;
            }
            if(i == nextLevels.length - 1 && player.TotalPoints > nextLevels[i + 1]) {
                advacedPersent = penaltyProcents[i + 1];
                //console.log(`excessLevel: ${i}, player.TotalPoints: ${player.TotalPoints}, levelAveragePoints: ${x}, add: ${add}, advacedPersent: ${advacedPersent}`);
            }
        });
    }
    return {
        averagePoint: averagePoint,
        beginNormalPoint: beginNormalPoint,
        endNormalPoint: endNormalPoint,
        normalPercent: Math.max(Math.min(Math.round((player.TotalPoints - beginNormalPoint) * 100 / (endNormalPoint - beginNormalPoint)), 100), 0),
        advacedPersent: advacedPersent,
        prevEdge: prevEdge,
        nextEdge: nextEdge,
        prevEdgeClarification: prevEdgeClarification,
        nextEdgeClarification: nextEdgeClarification
    };
}
function addPlayerInfoSpoilers() {
    if(location.pathname == "/pl_info.php") {
        const panelNames = isEn ? ["Statistics", "Clans", "Resources", "Best stacks in the Leaders' Guild", "Achievements", "Personal info"] : ["Статистика", "Кланы", "Ресурсы", "Лучшие отряды Гильдии Лидеров", "Достижения", "Личная информация"];
        const bolds = Array.from(document.querySelectorAll("td > b"));
        for(const panelName of panelNames) {
            const panelTitleBold = bolds.find(x => x.innerText == panelName);
            if(panelTitleBold) {
                const panelTitle = panelTitleBold.closest("td");
                const spoiler = addElement("div", { id: `${panelName.replace(/\s/g, "").replace(/'/g, "")}Spoiler`,  style: "display: inline-block; cursor: pointer;", innerHTML: `<img id="pl_info_parts_open_img" src="https://dcdn.heroeswm.ru/i/inv_im/btn_expand.svg" style="vertical-align: middle;">` }, panelTitle);
                spoiler.addEventListener("click", function() { setPlayerValue(this.id, !getPlayerBool(this.id)); bindPlayerInfoSpolers(); });
            }
        }
        bindPlayerInfoSpolers();
    }
}
function bindPlayerInfoSpolers() {
    const panelNames = isEn ? ["Statistics", "Clans", "Resources", "Best stacks in the Leaders' Guild", "Achievements", "Personal info"] : ["Статистика", "Кланы", "Ресурсы", "Лучшие отряды Гильдии Лидеров", "Достижения", "Личная информация"];
    for(const panelName of panelNames) {
        const spoilerId = `${panelName.replace(/\s/g, "").replace(/'/g, "")}Spoiler`;
        const spoiler = document.getElementById(spoilerId);
        if(spoiler) {
            const spoiled = getPlayerBool(spoilerId);
            spoiler.querySelector("img").style.transform = spoiled ? 'rotate(0deg)' : 'rotate(90deg)';
            spoiler.closest("tr").nextElementSibling.style.display = spoiled ? "none" : "";
        }
    }
}
function clanStatistics() {
    if(location.pathname == "/clan_info.php") {
        const clanActivity = document.querySelector("img[src$='clans/online.gif']") || document.querySelector("img[src$='clans/offline.gif']");
        if(!clanActivity) {
            return;
        }
        const clanHeroesTable = clanActivity.closest("table");
        
        const onlineAmount = clanHeroesTable.querySelectorAll("img[src$='clans/online.gif']").length;
        const offlineAmount = clanHeroesTable.querySelectorAll("img[src$='clans/offline.gif']").length;
        const inBattleAmount = clanHeroesTable.querySelectorAll("img[src$='clans/battle.gif']").length;
        const playArcomagAmount = clanHeroesTable.querySelectorAll("img[src$='clans/arcomag.gif']").length;

        
        const clanInfoTable = document.querySelector("a[href^='clan_log.php?id']").closest("table");
        const firstRow = clanInfoTable.rows[0];
        firstRow.cells[0].insertAdjacentHTML("beforeend", `
<span id=shortClanActivityInfoSpan>
    <span style="${onlineAmount > 0 ? "" : "display: none;"}"><img src="https://dcdn.heroeswm.ru/i/clans/online.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "Online" : "В игре"}:</b> ${onlineAmount}&nbsp;&nbsp;&nbsp;</span>
    <span style="${inBattleAmount > 0 ? "" : "display: none;"}"><img src="https://dcdn.heroeswm.ru/i/clans/battle.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "In combat" : "В бою"}:</b> ${inBattleAmount}&nbsp;&nbsp;&nbsp;</span>
    <span style="${playArcomagAmount > 0 ? "" : "display: none;"}"><img src="https://dcdn.heroeswm.ru/i/clans/arcomag.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "In card match" : "В таверне"}:</b> ${playArcomagAmount}&nbsp;&nbsp;&nbsp;</span>
</span>`);
        const spoiler = addElement("div", { id: `clanStatisticsSpoiler`,  style: "display: inline-block; cursor: pointer;", innerHTML: `<img id="pl_info_parts_open_img" src="https://dcdn.heroeswm.ru/i/inv_im/btn_expand.svg" style="vertical-align: middle;">` }, document.getElementById("shortClanActivityInfoSpan"));
        spoiler.addEventListener("click", function() { setPlayerValue("clanStatisticsSpoiled", !getPlayerBool("clanStatisticsSpoiled")); clanStatisticsRowBind(); });
        
        
        const clanHeroes = Array.from(clanHeroesTable.rows).filter(x => x.cells[2].querySelector("a[href^='pl_info.php?id=']")).map(x => {
            const level = parseInt(x.cells[3].innerText);
            const fractionImage = x.cells[2].querySelector("img");
            const fractionNumber = parseInt(fractionImage.src.match(/\/i\/f\/r(\d+).png/)[1]);
            return { level: level, fractionImage: fractionImage, fraction: fractionImage.title, fractionNumber: fractionNumber };
        });
        const clanHeroesAmount = clanHeroes.length;
        const heroesLevels = groupBy(clanHeroes, "level");
        const clanLevels = Object.keys(heroesLevels).map(x => ({ level: parseInt(x), amount: heroesLevels[x].length }));
        clanLevels.sort((a, b) => b.level - a.level);

        const heroesFractions = groupBy(clanHeroes, "fractionNumber"); // console.log(heroesFractions);
        const clanFractions = Object.keys(heroesFractions).map(x => ({
            fractionNumber: parseInt(x) % 100,
            amount: heroesFractions[x].length,
            fractionImage: heroesFractions[x][0].fractionImage,
            fraction: heroesFractions[x][0].fraction,
            altNumber: parseInt(x) >= 100 ? Math.round(parseInt(x) / 100) : 0
        }));
        clanFractions.sort((a, b) => a.fractionNumber == b.fractionNumber ? a.altNumber - b.altNumber : a.fractionNumber - b.fractionNumber); // console.log(clanFractions);
        
        
        const clanStatisticsRow = addElement('tr', { id: "clanStatisticsRow" }, firstRow, "afterend");
        clanStatisticsRow.innerHTML = `
<td colspan="2" class="wbwhite">
    <table width="100%" height="100%">
        <tr>
            <td width="60%" valign="top" style="border-right:1px #5D413A solid;">
                <table width="100%" cellpadding="5">
                    <tr>
                        <td align="center">
                            <b>${isEn ? 'All' : 'Всего'}:</b> ${clanHeroesAmount}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>${isEn ? 'Online' : 'В сети'}:</b> ${clanHeroesAmount - offlineAmount} (${Math.round((clanHeroesAmount - offlineAmount) / clanHeroesAmount * 100)}%)                
                        </td>
                    </tr>
                    <tr>
                        <td align="center" style="white-space:nowrap">
                            <img src="https://dcdn.heroeswm.ru/i/clans/online.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "Online" : "В игре"}:</b> ${onlineAmount}&nbsp;&nbsp;&nbsp;
                            <img src="https://dcdn.heroeswm.ru/i/clans/battle.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "In combat" : "В бою"}:</b> ${inBattleAmount}&nbsp;&nbsp;&nbsp;
                            <img src="https://dcdn.heroeswm.ru/i/clans/arcomag.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "In card match" : "В таверне"}:</b> ${playArcomagAmount}&nbsp;&nbsp;&nbsp;
                            <img src="https://dcdn.heroeswm.ru/i/clans/offline.gif" align="absmiddle" border="0" height="15" width="15"> <b>${isEn ? "Offline" : "Не в игре"}:</b> ${offlineAmount}
                        </td>
                    </tr>
                    <tr>
                        <td>
                        ${clanFractions.map(x => `<img src="${x.fractionImage.src}" align="absmiddle" border="0" height="15" width="15"> <b>${x.fraction}:</b> ${x.amount} (${Math.round(x.amount / clanHeroesAmount * 100)}%)`).join("<br>")}
                        </td>
                    </tr>
                </table>
            </td>
            <td valign="top" height="100%">
                <table width="100%" height="100%" cellpadding="5">
                    <tr>
                        <td align="center">
                            <b>${isEn ? 'The average level of heroes' : 'Средний уровень героев'}:</b> ${round0(clanHeroes.reduce((t, x) => t + x.level, 0) / clanHeroesAmount)}
                        </td>
                    </tr>
                    <tr>
                        <td height="100%" valign="middle">
                            ${clanLevels.map(x => `<b>${x.level} ${isEn ? 'level' : 'уровней'}:</b> ${x.amount} (${Math.round(x.amount / clanHeroesAmount * 100)}%)`).join("<br>")}
                        </td>
                    </tr>
                    <tr>
                        <td align="right" valign="bottom">
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
    </table>
</td>`;
        clanStatisticsRowBind();
    }
}
function clanStatisticsRowBind() {
    const spoiled = getPlayerBool("clanStatisticsSpoiled");
    const clanStatisticsSpoiler = document.getElementById("clanStatisticsSpoiler");
    clanStatisticsSpoiler.querySelector("img").style.transform = spoiled ? 'rotate(0deg)' : 'rotate(90deg)';
    clanStatisticsSpoiler.title = isEn ? (spoiled ? "Show clan statistics" : "Hide clan statistics") : (spoiled ? "Показать статистику клана" : "Скрыть статистику клана");
    document.getElementById("clanStatisticsRow").style.display = spoiled ? "none" : "";
}
function getViewingPlayerLevel() {
    if(location.pathname == "/home.php" || location.pathname == "/pl_info.php") {
        if(isNewPersonPage) {
            const levelInfoCell = Array.from(document.querySelectorAll("div.home_pers_info")).find(x => x.innerHTML.includes(isEn ? "Combat level" : "Боевой уровень"));
            return parseInt(levelInfoCell.querySelector("div[id=bartext] > span").innerText);
        } else {
            const playerLevelExec = new RegExp(`<b>${isEn ? "Combat level" : "Боевой уровень"}: (\\d+?)<\\/b>`).exec(document.documentElement.innerHTML);
            if(playerLevelExec) {
                return parseInt(playerLevelExec[1]);
            }
        }
    }
    return 0;
}
function getPlayerFraction() {
    let fractionSourceText;
    if(location.pathname == '/home.php') {
        const fractionImage = isNewPersonPage ? document.querySelector("div#hwm_no_zoom div.home_main_pers_block center a[href^='castle.php'] img") : document.querySelector("body > center table.wb > tbody > tr:nth-child(2) center a[href^='castle.php'] img");
        fractionSourceText = fractionImage?.src;
    }
    const playerFractionExec = /\/i\/f\/r(\d{1,3})\.png/.exec(fractionSourceText || document.querySelector("body").innerHTML);
    if(playerFractionExec) {
        return playerFractionExec[1];
    }
}

// https://dcdn3.heroeswm.ru/i/april2023/gn_face.png
// https://dcdn.heroeswm.ru/i/april2023/pvp_gerb2.png
// https://dcdn3.heroeswm.ru/i/april2023/gv_face.png
// https://dcdn1.heroeswm.ru/i/april2023/gr_face.png
// https://dcdn1.heroeswm.ru/i/april2023/go_face.png
// https://dcdn.heroeswm.ru/i/kukla_png/premium/1april2023/p8ap.png?v=10.d20e1e
// https://dcdn1.heroeswm.ru/i/kukla_png/premium/1april2023/p1ap.png?v=10.d20e1e
// https://dcdn.heroeswm.ru/i/kukla_png/premium/1april2023/p2ap.png?v=10.d20e1e
// https://dcdn.heroeswm.ru/i/kukla_png/premium/1april2023/p3ap.png?v=10.d20e1e
// https://dcdn3.heroeswm.ru/i/kukla_png/premium/1april2023/p4ap.png?v=10.d20e1e
// https://dcdn3.heroeswm.ru/i/kukla_png/premium/1april2023/p5ap.png?v=10.d20e1e
// https://dcdn2.heroeswm.ru/i/kukla_png/premium/1april2023/p6ap.png?v=10.d20e1e
// https://dcdn2.heroeswm.ru/i/kukla_png/premium/1april2023/p7ap.png?v=10.d20e1e
// https://dcdn.heroeswm.ru/i/kukla_png/premium/1april2023/p9ap.png?v=10.d20e1e
// https://dcdn.heroeswm.ru/i/kukla_png/premium/1april2023/p10ap.png?v=10.d20e1e
// https://dcdn.heroeswm.ru/i/transport/1april/150/2.png
// https://dcdn.heroeswm.ru/i/transport/1april/150/108.png
// https://dcdn.heroeswm.ru/i/transport/1april/150/150.png