Greasy Fork

Greasy Fork is available in English.

HWHNewCharacterExt

Extension for HeroWarsHelper script

当前为 2025-11-18 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name             HWHNewCharacterExt
// @name:en          HWHNewCharacterExt
// @name:ru          HWHNewCharacterExt
// @namespace        HWHNewCharacterExt
// @version          2.8
// @description      Extension for HeroWarsHelper script
// @description:en   Extension for HeroWarsHelper script
// @description:ru   Расширение для скрипта HeroWarsHelper
// @author           ZingerY, Green
// @license          Copyright Green
// @icon             https://i.ibb.co/xtmhK7zS/icon.png
// @match            https://www.hero-wars.com/*
// @match            https://apps-1701433570146040.apps.fbsbx.com/*
// @run-at           document-start
// ==/UserScript==

(function () {
    if (!this.HWHClasses) {
        console.log('%cObject for extension not found', 'color: red');
        return;
    }

    console.log('%cStart Extension ' + GM_info.script.name + ', v' + GM_info.script.version + ' by ' + GM_info.script.author, 'color: red');
    const { addExtentionName } = HWHFuncs;
    addExtentionName(GM_info.script.name, GM_info.script.version, GM_info.script.author);

    const { popup, confShow, setProgress, I18N, countdownTimer } = HWHFuncs;

    const { i18nLangData } = HWHData;

    const { WinFixBattle } = HWHClasses;

    const i18nLangDataEn = {
        NEW_CHARACTER: 'New Character',
        NEW_CHARACTER_TITLE: 'Complete quests for a new hero or titan',
        NEW_CHARACTER_NO_EVENT: 'The event is not active',
        NEW_CHARACTER_SOMETHING_WENT_WRONG: '<span style="color: red;"> Something went wrong </span> <br> Please try again',
        NEW_CHARACTER_SELECT_ACTION: 'Select an action',
        NT_TITAN_EVENT: '<span style="color: Lime; font-size: 35px;"> The Titan Awakens </span> <br>',
        NT_COLLECT_TITANS: 'Сollect the Titans',
        NT_COLLECT_TITANS_TITLE: 'Сollecting the Titans of the maximum rank by purchasing fragments in the store',
        NT_COLLECT_HEROES: 'Сollect the Heroes',
        NT_COLLECT_HEROES_TITLE: 'Сollecting the Heroes of the maximum rank by purchasing fragments in the store',
        NT_COLLECT_TOTEM_SKILLS: 'Сollect totem skills',
        NT_COLLECT_TOTEM_SKILLS_TITLE: 'Get the influence skill of the maximum rank by purchasing fragments in the store',
        NT_COLLECT_TITANS_PROGRESS: '<span style="color: green;"> {counter} </span> titans left to collect',
        NT_TITANS_COLLECTED: 'All titans have been collected',
        NT_COLLECT_TOTEM_SKILLS_PROGRESS: '<span style="color: green;"> {counter} </span> influence skills left to collect <br> Collecting...',
        NT_TOTEM_SKILLS_COLLECTED: 'All influence skills have been collected',
        NT_CHAPTER_NOT_AVAILABLE: '<span style="color: red;"> Chapter unavailable </span> <br> Complete the previous chapter',
        NT_COLLECT_HEROES_PROGRESS: '<span style="color: green;"> {counter} </span> heroes left to collect <br> Collecting...',
        NT_HEROES_COLLECTED: '<span style="color: LimeGreen;">All heroes have been collected</span>',
        NT_COLLECT_EVERYTHING: 'Collect everything',
        NT_COLLECT_EVERYTHING_TITLE: 'Collect heroes, titans, totems, pets',
        NT_BOSS_WAS_KILLED: '<span style="color: green;"> The boss was killed </span>',
        NT_BOSS_WAS_KILLED_SET_PROGRESS_1: 'All done. <span style="color: LimeGreen;">Boss defeated</span>. Collecting trophies',
        NT_BOSS_WAS_KILLED_SET_PROGRESS_2: '<br>Oh no, our haul is too big for the bag.',
        NT_BOSS_WAS_KILLED_SET_PROGRESS_3: '<br>Calling the porters guild.',
        NT_BOSS_WAS_KILLED_SET_PROGRESS_4: '<br>All of it!',
        NT_BOSS_WAS_KILLED_SET_PROGRESS_5: '<br><span style="color: LimeGreen;">Stand by.</span> Arrival in approximately seconds ',
        NT_BOSS_WAS_NOT_KILLED: '<span style="color: green;"> The boss was not killed </span> <br> Restart the game and kill the boss',
        NT_COMPLETE_CHAPTER: 'Complete the chapter',
        NT_COMPLETE_CHAPTER_TITLE: 'Complete an available chapter',
        NT_ALL_CHAPTERS_COMPLETED: 'All chapters completed',
        NT_NOT_ENOUGH_BUFF:
          'Not enough buff to complete Chapter <span style="color: green;"> {chapterNumber} </span>'+
          'You have: <span style="color: red;"> {buffAmount} </span> <br>' +
          'You need: <span style="color: green;"> {invasionBuff} </span>',
        NT_ENTER_TITAN_IDS:
          'Chapter <span style="color: green;"> {chapterNumber} </span> is available for completion <br>' +
          'Enter <span style="color: red;"> 5 </span> titan IDs separated by commas or dashes',
        NT_ENTER_HERO_IDS:
          'Chapter <span style="color: green;"> {chapterNumber} </span> is available for completion <br>' +
          'Enter <span style="color: red;"> 5 </span> hero IDs separated by commas or dashes',
        NT_MUST_FIVE_TITANS: 'There must be <span style="color: red;"> 5 </span> titans',
        NT_MUST_FIVE_HEROES: 'There must be <span style="color: red;"> 5 </span> heroes',
        NT_MUST_ONLY_NUMBERS: 'The list must contain only numbers and commas',
        NT_LETS_START: 'Let\'s start...',
        NT_OUTDATED_VERSION_OF_SCRIPT:
          '<span style="color: red;"> Outdated version of HeroWarsHelper </span><br>' +
          'Please update the script',
        NT_MISSION_PROGRESS: 'Taking out <span style="color: LimeGreen;"> {missionNumber} </span> enemy team',
        NT_MISSION_PROGRESS_BOSS: 'Let\'s wipe <span style="color: LimeGreen;">the boss</span> as a team!',
        NHR_NOTHING_HERE: 'There\'s nothing here yet. Please wait.',
        NHR_HERO_EVENT: '<span style="color: White; font-size: 35px;">Rise of a New Hero</span> <br>',
        NHR_COMPLETE_TASKS: 'Complete the tasks',
        NHR_COMPLETE_TASKS_TITLE: 'Complete event tasks: collect heroes, buy pets, spend coins',
        NHR_TASKS_COMPLETED: 'The tasks have been completed',
        NHR_LIVES_ARE_OVER: 'Lives are over. Try again',
        NHR_SHOPPING: 'Make purchases...',
        NHR_NOTHING_HERE_1: 'What\'s this? Where is it? When? Booooooooooosss...',
        NHR_NOTHING_HERE_2: 'Nobody\'s heeere! ',
        NHR_NOTHING_HERE_3: 'And nobody here either.',
        NHR_NOTHING_HERE_4: 'If he\'s not there, then what\'s supposed to be here?',
        NHR_COMPLETE_CHAPTER_N1: 'Raid for Chapter I',
        NHR_COMPLETE_CHAPTER_N1_TITLE: 'Complete chapter I one time',
        NHR_COMPLETE_CHAPTER_N1_COMPLETED: 'Chapter <span style="color: green;">I</span> raid completed',

        NHR_CHAPTER_N1_RAID: 'Starting <span style="color: LimeGreen;">{raidNumber}</span> raid of first chapter',
        NHR_MAKE_OTHER_TASKS: '<br>Moving on to other quests',
        NHR_GET_HERO_IDS: 'Hero IDs',
        NHR_GET_HERO_IDS_TITLE: 'Get a list of hero IDs',
        NHR_GET_HERO_IDS_MESSAGE: '<span style="color: LimeGreen; font-size: 25px;">ID - Name</span>',
    };

    i18nLangData['en'] = Object.assign(i18nLangData['en'], i18nLangDataEn);

    const i18nLangDataRu = {
        NEW_CHARACTER: 'Новый персонаж',
        NEW_CHARACTER_TITLE: 'Выполнять задания для нового героя или титана',
        NEW_CHARACTER_NO_EVENT: 'Ивент не активен',
        NEW_CHARACTER_SOMETHING_WENT_WRONG: '<span style="color: red;"> Что-то пошло не так </span> <br> Попробуйте заново',
        NEW_CHARACTER_SELECT_ACTION: 'Выберите действие',
        NT_TITAN_EVENT: '<span style="color: Lime; font-size: 35px;"> Пробуждение Титана </span> <br>',
        NT_COLLECT_TITANS: 'Собрать титанов',
        NT_COLLECT_TITANS_TITLE: 'Собрать титанов максимального ранга, покупая фрагменты в магазине',
        NT_COLLECT_HEROES: 'Собрать героев',
        NT_COLLECT_HEROES_TITLE: 'Собрать героев максимального ранга, покупая фрагменты в магазине',
        NT_COLLECT_TOTEM_SKILLS: 'Собрать тотемы',
        NT_COLLECT_TOTEM_SKILLS_TITLE: 'Собрать навыки тотемов максимального ранга, покупая фрагменты в магазине',
        NT_COLLECT_TITANS_PROGRESS: 'Осталось собрать титанов: <span style="color: green;"> {counter} </span> шт.',
        NT_TITANS_COLLECTED: 'Все титаны собраны',
        NT_COLLECT_TOTEM_SKILLS_PROGRESS: 'Осталось собрать навыков тотемов: <span style="color: green;"> {counter} </span> шт. <br> Собираем...',
        NT_TOTEM_SKILLS_COLLECTED: 'Все навыки влияния собраны',
        NT_CHAPTER_NOT_AVAILABLE: '<span style="color: red;"> Глава не доступна </span> <br> Завершите предыдущую главу',
        NT_COLLECT_HEROES_PROGRESS: 'Осталось собрать героев: <span style="color: green;"> {counter} </span> шт. <br> Собираем...',
        NT_HEROES_COLLECTED: '<span style="color: LimeGreen;">Все герои собраны</span>',
        NT_COLLECT_EVERYTHING: 'Собрать все',
        NT_COLLECT_EVERYTHING_TITLE: 'Собрать героев, титанов, тотемы, питомцев',
        NT_BOSS_WAS_KILLED: '<span style="color: green;"> Убили босса </span>',
        NT_BOSS_WAS_KILLED_SET_PROGRESS_1: 'Готовенько. <span style="color: LimeGreen;">Босс повержен</span>. Собираем трофеи',
        NT_BOSS_WAS_KILLED_SET_PROGRESS_2: '<br>О нет, наш улов слишком велик для пакета. ',
        NT_BOSS_WAS_KILLED_SET_PROGRESS_3: '<br>Вызываем гильдию носильщиков.',
        NT_BOSS_WAS_KILLED_SET_PROGRESS_4: '<br>Всю!',
        NT_BOSS_WAS_KILLED_SET_PROGRESS_5: '<br><span style="color: LimeGreen;">Ожидайте.</span> Прибытие, примерно секунд через ',
        NT_BOSS_WAS_NOT_KILLED: '<span style="color: green;"> Босса не убили </span> <br> Перезагрузите игру, и убейте босса',
        NT_COMPLETE_CHAPTER: 'Пройти главу',
        NT_COMPLETE_CHAPTER_TITLE: 'Пройти доступную главу',
        NT_ALL_CHAPTERS_COMPLETED: 'Все главы пройдены',
        NT_NOT_ENOUGH_BUFF:
          'Недостаточно усиления для прохождения <span style="color: green;"> {chapterNumber} </span> главы <br>' +
          'У вас: <span style="color: red;"> {buffAmount} </span> <br>' +
          'Необходимо: <span style="color: green;"> {invasionBuff} </span>',
        NT_ENTER_TITAN_IDS:
          'Для прохождения доступна <span style="color: green;"> {chapterNumber} </span> глава <br>' +
          'Введите <span style="color: red;"> 5 </span> id <span style="color: green;"> титанов </span> через запятые или дефисы',
        NT_ENTER_HERO_IDS:
          'Для прохождения доступна <span style="color: green;"> {chapterNumber} </span> глава <br>' +
          'Введите <span style="color: red;"> 5 </span> id <span style="color: green;"> героев </span> через запятые или дефисы',
        NT_MUST_FIVE_TITANS:'Должно быть <span style="color: red;"> 5 </span> титанов',
        NT_MUST_FIVE_HEROES:'Должно быть <span style="color: red;"> 5 </span> героев',
        NT_MUST_ONLY_NUMBERS: 'Список должен содержать только цифры и запятые',
        NT_LETS_START: 'Начинаем начинать...',
        NT_OUTDATED_VERSION_OF_SCRIPT:
          '<span style="color: red;"> Устаревшая версия HeroWarsHelper </span><br>' +
          'Пожалуйста, обновите скрипт',
        NT_MISSION_PROGRESS: 'Сносим <span style="color: LimeGreen;"> {missionNumber} </span> команду противника',
        NT_MISSION_PROGRESS_BOSS: 'Пакуем <span style="color: LimeGreen;">босса</span> всем коллективом!',
        NHR_NOTHING_HERE: 'Здесь пока ничего нет. Ожидайте',
        NHR_HERO_EVENT: '<span style="color: White; font-size: 35px;">Восхождение Нового Героя</span> <br>',
        NHR_COMPLETE_TASKS: 'Выполнить задания',
        NHR_COMPLETE_TASKS_TITLE: 'Выполнить задания ивента: собрать героев, купить питомцев, потратить монеты',
        NHR_TASKS_COMPLETED: 'Задания выполнены',
        NHR_LIVES_ARE_OVER: 'Закончились жизни. Попробуйте снова',
        NHR_SHOPPING: 'Шопимся, шопимся, шопимся...',
        NHR_NOTHING_HERE_1: 'А чё это? А где? А когда? Хазяяяяяяяииинн...',
        NHR_NOTHING_HERE_2: 'Здеся никого нет!',
        NHR_NOTHING_HERE_3: 'И здесь тоже нет.',
        NHR_NOTHING_HERE_4: 'Если там нет, то чё здесь должно быть?',
        NHR_COMPLETE_CHAPTER_N1: 'Рейд I главы',
        NHR_COMPLETE_CHAPTER_N1_TITLE: 'Пройти I главу 1 раз',
        NHR_COMPLETE_CHAPTER_N1_COMPLETED: 'Рейд <span style="color: green;">I</span> главы выполнен',

        NHR_CHAPTER_N1_RAID: 'Выполняем <span style="color: LimeGreen;">{raidNumber}</span> рейд первой главы',
        NHR_MAKE_OTHER_TASKS: '<br> Приступаем к выполнению других заданий',
        NHR_GET_HERO_IDS: 'Id героев',
        NHR_GET_HERO_IDS_TITLE: 'Получить список Id героев',
        NHR_GET_HERO_IDS_MESSAGE: '<span style="color: LimeGreen; font-size: 25px;">ID - Имя</span>',

    };

    i18nLangData['ru'] = Object.assign(i18nLangData['ru'], i18nLangDataRu);

    const romanNumerals = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X'];

    ///////////////////////////////////////////////////////////////////////////////////////////////

    // Добавление кнопоки в окно Разное
    const { othersPopupButtons } = HWHData;
    othersPopupButtons.push({
        get msg() {
            return I18N('NEW_CHARACTER');
        },
        get title() {
            return I18N('NEW_CHARACTER_TITLE');
        },
        result: async function () {
            if (compareVersions(scriptInfo.version, '2.390') < 0) {
                confShow(`${I18N('NT_OUTDATED_VERSION_OF_SCRIPT')}`);
                return;
            }
            console.log(scriptInfo.version);
            let invasionInfo = await Caller.send('invasion_getInfo');
            if (invasionInfo) {
                let invasionInfoId = invasionInfo.id;
                let chapters = Object.values(lib.data.invasion.chapter).filter((e) => e.invasionId === invasionInfoId);
                //const startDate = new Date("2025-11-14 14:00:00".replace(' ', 'T') + 'Z');
                const startDate = new Date((chapters[0].startDate).replace(' ', 'T') + 'Z');
                const todayDate = Date.now();
                console.log(startDate);
                console.log(todayDate);

                if (todayDate < startDate) {
                    confShow(`${I18N('NEW_CHARACTER_NO_EVENT')}`);
                    return;
                }
                let titanIvent = false;
                for (let chapter of chapters) {
                    if (chapter.settings.unitType === 'titan') {
                        titanIvent = true;
                        break;
                    }
                }
                if (titanIvent) {
                    await onClickNewTitanButton();
                } else {
                    await onClickNewHeroButton();
                }
            } else {
                confShow(`${I18N('NEW_CHARACTER_NO_EVENT')}`);
            }
        },
        color: 'pink',
    });

    async function onClickNewHeroButton() {
        const popupButtons = [
            {
                get msg() {
                    return I18N('NHR_COMPLETE_TASKS');
                },
                get title() {
                    return I18N('NHR_COMPLETE_TASKS_TITLE');
                },
                result: async function () {
                    //confShow(I18N('NHR_NOTHING_HERE'));
                    //Получить список героев, которых нужно собрать
                    let heroIdsToBuy = await getHeroIdsToBuy();
                    //let heroIdsToBuy = [60]
                    if (heroIdsToBuy.length != 0) {
                        let spendCoins = true;
                        await collectHeroes(spendCoins);
                        setProgress(`${I18N('NT_HEROES_COLLECTED')} ${I18N('NHR_MAKE_OTHER_TASKS')}`, false);
                        await new Promise((e) => setTimeout(e, 2000));
                        for (let i = 1; i <= 4; i++) {
                            setProgress(I18N('NHR_CHAPTER_N1_RAID', {raidNumber:i}), false);
                            await new Promise((e) => setTimeout(e, 2000));
                            await firstHeroicChapterRaid();
                        }
                    }
                    setProgress('', true);
                    confShow(I18N('NHR_TASKS_COMPLETED'));
                },
                color: 'green',
            },
            {
                get msg() {
                    return I18N('NT_COMPLETE_CHAPTER');
                },
                get title() {
                    return I18N('NT_COMPLETE_CHAPTER_TITLE');
                },
                result: async function () {
                    //confShow(I18N('NHR_NOTHING_HERE'));
                    await completeChapter();
                },
                color: 'blue',
            },
            {
                get msg() {
                    return I18N('NHR_COMPLETE_CHAPTER_N1');
                },
                get title() {
                    return I18N('NHR_COMPLETE_CHAPTER_N1_TITLE');
                },
                result: async function () {
                    await firstHeroicChapterRaid();
                    setProgress('', true);
                    confShow(I18N('NHR_COMPLETE_CHAPTER_N1_COMPLETED'));
                },
                color: 'pink',
            },
            {
                get msg() {
                    return I18N('NHR_GET_HERO_IDS');
                },
                get title() {
                    return I18N('NHR_GET_HERO_IDS_TITLE');
                },
                result: function () {
                    getAllHeroIDs();
                },
                color: 'pink',
            },
        ];
        popupButtons.push({ result: false, isClose: true });
        const answer = await popup.confirm(I18N('NHR_HERO_EVENT'), popupButtons);
        if (typeof answer === 'function') {
            answer();
        }
    }

    async function onClickNewTitanButton() {
        const popupButtons = [
            {
                get msg() {
                    return I18N('NT_COLLECT_TITANS');
                },
                get title() {
                    return I18N('NT_COLLECT_TITANS_TITLE');
                },
                result: async function () {
                    confShow(I18N('NHR_NOTHING_HERE_1'));
                    //await collectTitans();
                },
                color: 'green',
            },
            /*{
                get msg() {
                    return I18N('NT_COLLECT_TOTEM_SKILLS');
                },
                get title() {
                    return I18N('NT_COLLECT_TOTEM_SKILLS_TITLE');
                },
                result: async function () {
                    await collectTotemFragments();
                },
                color: 'green',
            },*/
            {
                get msg() {
                    return I18N('NT_COLLECT_HEROES');
                },
                get title() {
                    return I18N('NT_COLLECT_HEROES_TITLE');
                },
                result: async function () {
                    confShow(I18N('NHR_NOTHING_HERE_2'));
                    /*let farmedChapters = await Caller.send('invasion_getInfo').then((e) => e.farmedChapters);
                    if (farmedChapters.length >= 1) {
                        await collectHeroes();
                    } else {
                        confShow(`${I18N('NT_CHAPTER_NOT_AVAILABLE')}`);
                    }*/
                },
                color: 'green',
            },
            {
                get msg() {
                    return I18N('NT_COLLECT_EVERYTHING');
                },
                get title() {
                    return I18N('NT_COLLECT_EVERYTHING_TITLE');
                },
                result: async function () {
                    confShow(I18N('NHR_NOTHING_HERE_3'));
                    /*await collectTitans();
                    await collectTotemFragments();
                    let farmedChapters = await Caller.send('invasion_getInfo').then((e) => e.farmedChapters);
                    if (farmedChapters.length >= 1) {
                        await collectHeroes();
                    } else {
                        confShow(`${I18N('NT_CHAPTER_NOT_AVAILABLE')}`);
                    }*/
                },
                color: 'pink',
            },
            {
                get msg() {
                    return I18N('NT_COMPLETE_CHAPTER');
                },
                get title() {
                    return I18N('NT_COMPLETE_CHAPTER_TITLE');
                },
                result: async function () {
                    confShow(I18N('NHR_NOTHING_HERE_4'));
                    //await completeChapter();
                },
                color: 'blue',
            },
        ];
        popupButtons.push({ result: false, isClose: true });
        const answer = await popup.confirm(I18N('NT_TITAN_EVENT'), popupButtons);
        if (typeof answer === 'function') {
            answer();
        }
    }
    //****************************************************************************************************
    //****************************************************************************************************
    //****************************************************************************************************
    //****************************************************************************************************

    function getAllHeroIDs() {
        let heroIds = Object.values(lib.data.hero).filter(e => e.type === 'hero' && !e.roleExtended.includes('boss'));
        heroIds = heroIds.filter((e) => e.id != 63 && e.id != 65);
        const heroIdsConsole = heroIds.map(e => `${e.id} - ` + cheats.translate(`LIB_HERO_NAME_${e.id}`)).join('\n');
        console.log(heroIdsConsole);
        popup.customPopup(async (complete) => {
            popup.custom.insertAdjacentHTML(
                'beforeend',
                '<div class="PopUp_text" style="text-align: left;">' + heroIds.map((e) => `<div>${e.id} - ` + cheats.translate(`LIB_HERO_NAME_${e.id}`) + '</div>').join('') + '</div>'
            );
            popup.setMsgText(I18N('NHR_GET_HERO_IDS_MESSAGE'));
            popup.addButton({ isClose: true }, () => {
                complete(false);
                popup.hide();
            });
            popup.show();
        });
    }

    async function firstHeroicChapterRaid() {
        let spendCoins = true;
        let missionRaid = true;
        await completeChapter(spendCoins, missionRaid);
    }

    async function completeChapter(spendCoins = false, missionRaid = false) {
        //Получить состояние на карте
        let invasionInfo = await Caller.send('invasion_getInfo');
        let invasionInfoId = invasionInfo.id;
        let farmedChapters = invasionInfo.farmedChapters;
        let buffAmount = invasionInfo.buffAmount;
        console.log('invasionInfoId ' + invasionInfoId);
        console.log('farmedChapters ', JSON.stringify(farmedChapters));
        console.log('buffAmount ' + buffAmount);

        //Получить id главы для атаки
        let chapters = Object.values(lib.data.invasion.chapter).filter((e) => e.invasionId === invasionInfoId);
        console.log(chapters);
        let chapterId = 0;
        let invasionBuff = 0;
        let titanOrHero = '';
        let chapterNumber = 0;

        if (missionRaid == false) {
            if (chapters.length == farmedChapters.length) {
                confShow(I18N('NT_ALL_CHAPTERS_COMPLETED'));
            }
            for (let chapter of chapters) {
                if (!farmedChapters.includes(chapter.id)) {
                    chapterId = chapter.id;
                    if (chapter.requirements?.invasionBuff) {
                        invasionBuff = chapter.requirements.invasionBuff;
                    }
                    titanOrHero = chapter.settings.unitType;
                    chapterNumber = chapters.indexOf(chapter)+1;
                    break;
                }
            }
        }
        //Рейд первой мисси
        if (missionRaid == true) {
            chapterId = chapters[0].id;
            titanOrHero = chapters[0].settings.unitType;
            chapterNumber = 1;
        }

        console.log('chapterId ' + chapterId);
        console.log('invasionBuff ' + invasionBuff);
        console.log('titanOrHero ' + titanOrHero);
        console.log('chapterNumber ' + chapterNumber);

        if (buffAmount < invasionBuff) {
            confShow(I18N('NT_NOT_ENOUGH_BUFF', { chapterNumber: romanNumerals[chapterNumber], buffAmount, invasionBuff}));
            return;
        }
        if (titanOrHero === 'hero' ) {
            await completeHeroesChapter(chapters, chapterId, chapterNumber, spendCoins, missionRaid);
        }
        if (titanOrHero === 'titan' ) {
            await completeTitansChapter(chapters, chapterId, chapterNumber);
        }
    }

    async function completeHeroesChapter(chapters, chapterId, chapterNumber, spendCoins = false, missionRaid = false) {
        /*Питомцы
        6000 - Фенрис   //6005 - Альбрус
        6001 - Оливер	//6006 - Аксель
        6002 - Мерлин   //6007 - Бисквит
        6003 - Мара	    //6008 - Хорус
        6004 - Каин	    //6009 - Векс*/
        //Атакующие герои: Галахад, Тристан, Лирия, Кира, Себастьян.
        //let heroAttackingTeams = {heroes: [2, 54, 67, 3, 48], pets: [6005,6000,6001,6006]};

        //Атакующие герои: Электра, Каскад + Тея, Криста + Ларс.
        //let heroAttackingTeams = {heroes: [70, 69, 7, 33, 34], pets: [6005, 6001, 6002, 6003, 6006]};

        //Атакующие герои: Электра + Орион, Каскад, Криста + Ларс.
        //let heroAttackingTeams = {heroes: [70, 13, 69, 33, 34], pets: [6005, 6001, 6002, 6003, 6006]};

        //Атакующие герои: Электра, Каскад, Криста + Ларс, Хайди.
        let heroAttackingTeams = {heroes: [70, 69, 33, 34, 9], pets: [6005, 6001, 6002, 6003, 6006]};

        //Атакующие герои: Электра, Каскад, Айрис, Хайди, Дориан.
        //let heroAttackingTeams = {heroes: [70, 69, 55, 9, 29], pets: [6005, 6001, 6002, 6003, 6006]};

        let titanOrHero = 'hero';
        let heroIds = heroAttackingTeams.heroes;

        if (missionRaid == false) {
            //Кнопка ввод Id героев, что необходимо собрать
            console.log("chapterNumber chapterNumber" + chapterNumber);
            heroIds = await getTeamButton(heroAttackingTeams.heroes, chapterNumber, titanOrHero);
            console.log('heroIds ', JSON.stringify(heroIds));
            if (!heroIds) {
                return;
            }
        }

        setProgress(I18N('NT_LETS_START'), false);
        await new Promise((e) => setTimeout(e, 3000));

        //Активировать главу
        let chapterInfo = await Caller.send({ name: 'invasion_setActiveChapter', args: { chapterId: chapterId } });
        let haveHeroFragments = chapterInfo.invasion.fragments;
        console.log('haveHeroFragments ', JSON.stringify(haveHeroFragments));

        //Получить id магазина
        let shopId = await getShopId(chapters, titanOrHero); //1078; // Магазин титанов
        console.log('Id магазина ' + shopId);

        //Id миссии
        let firstMissionId = chapterInfo.invasion.actions[0].payload.id;
        let missionId = firstMissionId;

        //Жизни
        let lives = chapterInfo.invasion.lives;
        console.log('firstMissionId ' + firstMissionId);
        console.log('missionId ' + missionId);
        console.log('lives ' + lives);

        //Фрагменты героев
        let heroFragments = [0, 0, 0, 0, 0];
        for (let i = 0; i < 5; i++) {
            if (haveHeroFragments[heroIds[i]]) {
                heroFragments[i] = haveHeroFragments[heroIds[i]];
            }
        }
        console.log('shopId ' + shopId);
        console.log(heroIds);
        console.log('heroFragments ', JSON.stringify(heroFragments));

        //Питомцы, что необходимо купить
        let pets = heroAttackingTeams.pets;

        while (lives > 0 && missionId <= firstMissionId + 7) {
            //Купить героев
            setProgress(I18N('NHR_SHOPPING'), false);
            let result = await buyHeroesAndPets(shopId, heroIds, heroFragments, pets, spendCoins);

            //Текущая миссия босс или нет
            let boss = false;

            //Произвести атаку босса
            if (missionId == firstMissionId + 7) {
                if (missionRaid == true) {
                    return;
                }
                boss = true;
            }

            //Получить атакующую команду
            let heroes = [];
            let havePets = [];
            let pet;
            let allHeroes = [];
            let petsFavor = {};

            await getAttackingTeam(heroes, havePets, allHeroes);

            const haveAllAttackingTeams = (arr, values) => {
                return values.every(v => arr.includes(v));
            };
            if (haveAllAttackingTeams(allHeroes, heroIds)) {
                heroes = heroIds;
            }
            console.log('allHeroes ', JSON.stringify(allHeroes));
            console.log('heroes ', JSON.stringify(heroes));

            if (havePets.length > 0) {
                //Основной питомец первый в списке питомцев
                let mainPet = heroAttackingTeams.pets[0];
                if (havePets.includes(mainPet)) {
                    pet = mainPet;
                } else {
                    pet = havePets[0];
                }
                //Покровительство
                const petLib = lib.getData('pet');
                for (let heroId of heroes) {
                    /** Поиск питомца для героя */
                    for (let petId of havePets) {
                        if (petLib[petId].favorHeroes.includes(heroId)) {
                            petsFavor[heroId] = petId;
                            havePets = havePets.filter((e) => e != petId);
                            break;
                        };
                    }
                }
            }

            //Проходим миссию
            if (!boss) {
                setProgress(I18N('NT_MISSION_PROGRESS', {missionNumber: 1 + missionId - firstMissionId}), false);
            } else {
                setProgress(I18N('NT_MISSION_PROGRESS_BOSS'), false);
            }
            await new Promise((e) => setTimeout(e, 2000));
            await attackHeroMission(missionId, chapterId, heroes, pet, boss, petsFavor);

            //Результат атаки
            let invasionInfo = await Caller.send('invasion_getInfo');
            let missions = Object.values(invasionInfo.actions);
            for (let mission of missions) {
                if (mission.payload.wins == 0) {
                    missionId = mission.payload.id;
                    break;
                }
            }

            //Результат атаки босса
            if (boss) {
                if (invasionInfo.farmedChapters.includes(chapterId)) {
                    confShow(I18N('NT_BOSS_WAS_KILLED'));
                } else {
                    confShow(I18N('NT_BOSS_WAS_NOT_KILLED'));
                }
                return;
            }
            lives = invasionInfo.lives;
            console.log('missionId ' + missionId);
            console.log('lives ' + lives);
        }
        if (lives == 0) {
            confShow(I18N('NHR_LIVES_ARE_OVER'));
        }
    }



    async function completeTitansChapter(chapters, chapterId, chapterNumber) {
        //Атакующие титаны: Ияри, Солярис, Молох, Игнис, Араджи
        /*Навыки тотемов:
        elemental                  primal
        4500 - Последний Всполох   4506 - Пульс Древних
        4502 - Ледниковый Период   4507 - Первородное Рвение
        4503 - Гнев Недр           4508 - Эгида Эха
        4509 - Танец Пламени       4514 - Тройной Круговорот
        4510 - Шепот Глубин        4515 - Зов Стихий
        4511 - Гул Скал */

        const elementalSkils = [4500,4502,4503,4509,4510,4511];
        const primalSkils = [4506,4507,4508,4514,4515];
        let titanAttackingTeams = {heroes: [4042, 4043, 4010, 4012, 4013], titanSkilsIds: [4500, 4507]};
        let titanOrHero = 'titan';

        //Id титанов, что необходимо собрать
        let titanIds = await getTeamButton(titanAttackingTeams.heroes, chapterNumber, titanOrHero);
        console.log('titanIds ', JSON.stringify(titanIds));
        if (!titanIds) {
            return;
        }
        setProgress(I18N('NT_LETS_START'), false);
        await new Promise((e) => setTimeout(e, 3000));

        //Активировать главу
        let chapterInfo = await Caller.send({ name: 'invasion_setActiveChapter', args: { chapterId: chapterId } });
        let haveTitanFragments = chapterInfo.invasion.fragments;
        console.log('haveTitanFragments ', JSON.stringify(haveTitanFragments));

        //Получить id магазина
        let shopId = await getShopId(chapters, titanOrHero); //1073; // Магазин титанов
        console.log('Id магазина ' + shopId);

        //Id миссии
        let firstMissionId = chapterInfo.invasion.actions[0].payload.id;
        let missionId = firstMissionId;

        //Жизни
        let lives = chapterInfo.invasion.lives;
        console.log('firstMissionId ' + firstMissionId);
        console.log('missionId ' + missionId);
        console.log('lives ' + lives);

        //Фрагменты титанов
        let titanFragments = [0, 0, 0, 0, 0];
        for (let i = 0; i < 5; i++) {
            if (haveTitanFragments[titanIds[i]]) {
                titanFragments[i] = haveTitanFragments[titanIds[i]];
            }
        }
        console.log('shopId ' + shopId);
        console.log(titanIds);
        console.log('titanFragments ', JSON.stringify(titanFragments));

        //Навыки тотемов, что необходимо собрать
        let titanSkilsIds = titanAttackingTeams.titanSkilsIds;

        //Фрагменты навыков тотемов
        let titanSkilFragments = [];
        for (let t of titanSkilsIds){
            titanSkilFragments.push(0);
        }


        while (lives > 0 && missionId <= firstMissionId + 7) {
            setProgress(I18N('NHR_SHOPPING'), false);
            //Купить титанов
            let result = await buyTitansAndTotemSkils(shopId, titanIds, titanFragments, titanSkilsIds, titanSkilFragments);

            //Текущая миссия босс или нет
            let boss = false;

            //Произвести атаку босса
            if (missionId == firstMissionId + 7) {
                boss = true;
            }

            //Получить атакующую команду
            let heroes = [];
            let titanSkil = [];
            await getAttackingTeam(heroes, titanSkil);
            console.log('heroes ', JSON.stringify(heroes));
            let spiritSkills = new Array();
            if (titanSkil.length > 0) {
                for (let ts of titanSkil) {
                    if (elementalSkils.includes(ts)) {
                        spiritSkills.push(['elemental', ts]);
                    }
                    if (primalSkils.includes(ts)) {
                        spiritSkills.push(['primalSkils', ts]);
                    }
                }
            }
            let firstSpiritSkills = Object.fromEntries(spiritSkills);

            //Проходим миссию
            if (!boss) {
                setProgress(I18N('NT_MISSION_PROGRESS', {missionNumber: 1 + missionId - firstMissionId}), false);
            } else {
                setProgress(I18N('NT_MISSION_PROGRESS_BOSS'), false);
            }
            await new Promise((e) => setTimeout(e, 2000));
            await attackTitanMission(missionId, chapterId, heroes, firstSpiritSkills, boss);

            //Результат атаки
            let invasionInfo = await Caller.send('invasion_getInfo');
            let missions = Object.values(invasionInfo.actions);
            for (let mission of missions) {
                if (mission.payload.wins == 0) {
                    missionId = mission.payload.id;
                    break;
                }
            }

            //Результат атаки босса
            if (boss) {
                if (invasionInfo.farmedChapters.includes(chapterId)) {
                    confShow(I18N('NT_BOSS_WAS_KILLED'));
                } else {
                    confShow(I18N('NT_BOSS_WAS_NOT_KILLED'));
                }
                return;
            }
            lives = invasionInfo.lives;
            console.log('missionId ' + missionId);
            console.log('lives ' + lives);
        }
        if (lives == 0) {
            confShow(I18N('NHR_LIVES_ARE_OVER'));
        }
    }

    async function collectTitans() {
        //Получить состояние на карте
        let invasionInfo = await Caller.send('invasion_getInfo');
        let invasionInfoId = invasionInfo.id;
        let farmedChapters = invasionInfo.farmedChapters;
        console.log('invasionInfoId ' + invasionInfoId);
        console.log('farmedChapters ', JSON.stringify(farmedChapters));

        //Получить id первой главый
        let chapters = Object.values(lib.data.invasion.chapter).filter((e) => e.invasionId === invasionInfoId);
        let chapterId = getChapterId(chapters, 'titan');
        console.log('chapterId ' + chapterId);

        let cycle = true;
        while (cycle) {
            //Получить титанов, которых нужно собрать
            let allTitanIdsToBuy = await getTitanIdsToBuy();
            if (allTitanIdsToBuy.length == 0) {
                setProgress('', true);
                //await new Promise((e) => setTimeout(e, 4000));
                confShow(I18N('NT_TITANS_COLLECTED'));
                cycle = false;
                return;
            }

            //Активировать главу
            let chapterInfo = await Caller.send({ name: 'invasion_setActiveChapter', args: { chapterId: chapterId } });
            let haveTitanFragments = chapterInfo.invasion.fragments;

            //Получить id магазина
            let titanShopId = await getShopId(chapters, 'titan'); //1073; // Магазин титанов
            console.log('Id магазина титанов ' + titanShopId);

            //Id миссии
            let firstMissionId = chapterInfo.invasion.actions[0].payload.id;
            let missionId = firstMissionId;

            //Жизни
            let lives = chapterInfo.invasion.lives;
            console.log('firstMissionId ' + firstMissionId);
            console.log('missionId ' + missionId);
            console.log('lives ' + lives);

            //Id титанов, что необходимо собрать
            let titanIds = [0, 0];
            if (allTitanIdsToBuy.length >= 2) {
                titanIds = [allTitanIdsToBuy[0], allTitanIdsToBuy[1]];
            } else {
                titanIds[0] = allTitanIdsToBuy[0];
                if (titanIds[0] != Number(Object.keys(haveTitanFragments)[0])) {
                    titanIds[1] = Number(Object.keys(haveTitanFragments)[0]);
                } else {
                    titanIds[1] = Number(Object.keys(haveTitanFragments)[1]);
                }
            }
            //Фрагменты титанов
            let titanFragments = [0, 0];
            if (haveTitanFragments[titanIds[0]]) {
                titanFragments[0] = haveTitanFragments[titanIds[0]];
            }
            if (haveTitanFragments[titanIds[1]]) {
                titanFragments[1] = haveTitanFragments[titanIds[1]];
            }

            console.log('titanShopId ' + titanShopId);
            console.log(titanIds);
            console.log(titanFragments);

            //Навыки тотемов, что необходимо собрать
            let titanSkilsIds = await getTitanSkillIdsToBuy();

            //Фрагменты навыков тотемов
            let titanSkilFragments = [0, 0];
            if (titanSkilsIds.length <= 1) {
                titanSkilFragments = [0];
            }

            setProgress(
                I18N('NT_COLLECT_TITANS_PROGRESS', { counter: allTitanIdsToBuy.length }) +
                    '<br>' +
                    I18N('NT_COLLECT_TOTEM_SKILLS_PROGRESS', { counter: titanSkilsIds.length }),
                false
            );

            while (lives > 0 && missionId <= firstMissionId + 7) {
                setProgress(I18N('NHR_SHOPPING'), false);
                //Купить титанов
                let result = await buyTitanInTheStore(titanShopId, titanIds, titanFragments, titanSkilsIds, titanSkilFragments);
                //Выйти, если босс побежден и полностью собрали двух титанов.
                if (result && farmedChapters.includes(chapterId)) {
                    break;
                }
                //Текущая миссия босс или нет
                let boss = false;

                //Произвести атаку босса, если его ни разу не убили
                if (!farmedChapters.includes(chapterId)) {
                    if (missionId == firstMissionId + 7) {
                        boss = true;
                        console.log('%cАтакуем босса ', 'color: green; font-weight: bold;');
                    }
                } else {
                    //Не атаковать босса, если его уже убили
                    if (missionId == firstMissionId + 7) {
                        break;
                    }
                }
                //Получить атакующую команду
                let heroes = [];
                let titanSkil = [];
                await getAttackingTeam(heroes, titanSkil);
                let firstSpiritSkills = {};

                //Проходим миссию
                setProgress(I18N('NT_MISSION_PROGRESS', {missionNumber: 1 + missionId - firstMissionId}), false);
                await attackTitanMission(missionId, chapterId, heroes, firstSpiritSkills, boss);

                //Убили / не убили босса, вышли с "while"
                if (boss == true) {
                    break;
                }
                //Результат атаки
                let invasionInfo = await Caller.send('invasion_getInfo');
                farmedChapters = invasionInfo.farmedChapters;
                let missions = Object.values(invasionInfo.actions);
                for (let mission of missions) {
                    if (mission.payload.wins == 0) {
                        missionId = mission.payload.id;
                        break;
                    }
                }
                lives = invasionInfo.lives;
                console.log('missionId ' + missionId);
                console.log('lives ' + lives);
            }
        }
    }
    async function collectTotemFragments() {
        //Получить состояние на карте
        let invasionInfo = await Caller.send('invasion_getInfo');
        let invasionInfoId = invasionInfo.id;
        let farmedChapters = invasionInfo.farmedChapters;
        console.log('invasionInfoId ' + invasionInfoId);
        console.log('farmedChapters ', JSON.stringify(farmedChapters));

        //Получить id первой главый
        let chapters = Object.values(lib.data.invasion.chapter).filter((e) => e.invasionId === invasionInfoId);
        let chapterId = getChapterId(chapters, 'titan');
        console.log('chapterId ' + chapterId);

        let cycle = true;
        while (cycle) {
            //Получить навыки тотемов, которые нужно собрать
            let titanSkilsIds = await getTitanSkillIdsToBuy();
            if (titanSkilsIds.length == 0) {
                setProgress('', true);
                confShow(I18N('NT_TOTEM_SKILLS_COLLECTED'));
                cycle = false;
                return;
            }
            console.log(titanSkilsIds);
            setProgress(I18N('NT_COLLECT_TOTEM_SKILLS_PROGRESS', { counter: titanSkilsIds.length }), false);
            await new Promise((e) => setTimeout(e, 3000));

            //Фрагменты навыков тотемов
            let titanSkilFragments = [0, 0];
            if (titanSkilsIds.length == 1) {
                titanSkilFragments = [0];
            }

            //Активировать главу
            let chapterInfo = await Caller.send({ name: 'invasion_setActiveChapter', args: { chapterId: chapterId } });
            let haveTitanFragments = chapterInfo.invasion.fragments;

            //Получить id магазина
            let titanShopId = await getShopId(chapters, 'titan'); //1073; // Магазин титанов
            console.log('Id магазина титанов ' + titanShopId);

            //Id миссии
            let firstMissionId = chapterInfo.invasion.actions[0].payload.id;
            let missionId = firstMissionId;

            //Жизни
            let lives = chapterInfo.invasion.lives;
            console.log('firstMissionId ' + firstMissionId);
            console.log('missionId ' + missionId);
            console.log('lives ' + lives);

            //Id титанов, что необходимо купить
            //Ияри и Солярис
            let titanIds = [4042, 4043];

            //Фрагменты титанов
            let titanFragments = [0, 0];
            if (haveTitanFragments[titanIds[0]]) {
                titanFragments[0] = haveTitanFragments[titanIds[0]];
            }
            if (haveTitanFragments[titanIds[1]]) {
                titanFragments[1] = haveTitanFragments[titanIds[1]];
            }

            console.log('titanShopId ' + titanShopId);
            console.log(titanIds);
            console.log(titanFragments);

            while (lives > 0 && missionId <= firstMissionId + 7) {
                setProgress(I18N('NHR_SHOPPING'), false);
                //Купить титанов и навыки тотемов
                let result = await buyTotemSkilsInTheStore(titanShopId, titanIds, titanFragments, titanSkilsIds, titanSkilFragments);
                //Выйти, если босс побежден и полностью собрали два навыка тотема
                if (result && farmedChapters.includes(chapterId)) {
                    break;
                }

                //Произвести атаку босса, если его ни разу не убили
                if (!farmedChapters.includes(chapterId)) {
                    if (missionId == firstMissionId + 7) {
                        console.log('%cАтакуем босса ', 'color: green; font-weight: bold;');
                    }
                } else {
                    //Не атаковать босса, если его уже убили
                    if (missionId == firstMissionId + 7) {
                        break;
                    }
                }

                //Получить атакующую команду
                let heroes = [];
                let titanSkil = [];
                await getAttackingTeam(heroes, titanSkil);
                console.log(heroes);

                let firstSpiritSkills = {};

                //Проходим миссию
                setProgress(I18N('NT_MISSION_PROGRESS', {missionNumber: 1 + missionId - firstMissionId}), false);
                await attackTitanMission(missionId, chapterId, heroes, firstSpiritSkills);

                //Результат атаки
                let invasionInfo = await Caller.send('invasion_getInfo');
                farmedChapters = invasionInfo.farmedChapters;
                let missions = Object.values(invasionInfo.actions);
                for (let mission of missions) {
                    if (mission.payload.wins == 0) {
                        missionId = mission.payload.id;
                        break;
                    }
                }
                lives = invasionInfo.lives;
                console.log('missionId ' + missionId);
                console.log('lives ' + lives);
            }
        }
    }

    async function collectHeroes(spendCoins = false) {
        //Получить состояние на карте
        let invasionInfo = await Caller.send('invasion_getInfo');

        let invasionInfoId = invasionInfo.id;
        let farmedChapters = invasionInfo.farmedChapters;
        console.log('invasionInfoId ' + invasionInfoId);
        console.log('farmedChapters ', JSON.stringify(farmedChapters));

        //Получить id главы
        let chapters = Object.values(lib.data.invasion.chapter).filter((e) => e.invasionId === invasionInfoId);
        let chapterId = getChapterId(chapters, 'hero');
        console.log('chapterId ' + chapterId);

        //Питомцы, что необходимо купить
        let pets = [6008, 6005];// [6000, 6001, 6002, 6003, 6004, 6005, 6006, 6007, 6008, 6009];

        let cycle = true;
        while (cycle) {
            //Получить героев, которых нужно собрать
            let heroIdsToBuy = await getHeroIdsToBuy();
            if (heroIdsToBuy.length == 0) {
                if (spendCoins == false) {
                    setProgress('', true);
                    confShow(I18N('NT_HEROES_COLLECTED'));
                }
                cycle = false;
                return;
            }
            console.log(heroIdsToBuy);
            setProgress(I18N('NT_COLLECT_HEROES_PROGRESS', { counter: heroIdsToBuy.length }), false);
            await new Promise((e) => setTimeout(e, 3000));

            //Активировать главу
            let chapterInfo = await Caller.send({ name: 'invasion_setActiveChapter', args: { chapterId: chapterId } });
            let haveHeroFragments = chapterInfo.invasion.fragments;

            //Получить id магазина
            let shopId = await getShopId(chapters, 'hero'); //1078; // Магазин героев
            console.log('Id магазина героев ' + shopId);

            //Id миссии
            let firstMissionId = chapterInfo.invasion.actions[0].payload.id;
            let missionId = firstMissionId;

            //Жизни
            let lives = chapterInfo.invasion.lives;
            console.log('firstMissionId ' + firstMissionId);
            console.log('missionId ' + missionId);
            console.log('lives ' + lives);

            //Id героев, что будем покупить
            let heroIds = [0, 0, 0];
            //Резервные герои, для добавления в покупки: Галахад, Тристан, Лирия
            //let reserveHeroes = [2, 54, 67];

            //Резервные герои, для добавления в покупки: Электра, Каскад + Тея
            let reserveHeroes = [70, 69, 7];

            //Собирать по 3 нужных героя
            if (heroIdsToBuy.length >= 3) {
                heroIds = [heroIdsToBuy[0], heroIdsToBuy[1], heroIdsToBuy[2]];
            }
            if (heroIdsToBuy.length == 2) {
                heroIds[0] = heroIdsToBuy[0];
                heroIds[1] = heroIdsToBuy[1];
                for (let r of reserveHeroes) {
                    if (r != heroIds[0] && r != heroIds[1]) {
                        heroIds[2] = r;
                        break;
                    }
                }
            }

            if (heroIdsToBuy.length == 1) {
                if (reserveHeroes.includes(heroIdsToBuy[0])) {
                    heroIds = reserveHeroes;
                } else {
                    heroIds[0] = heroIdsToBuy[0];
                    heroIds[1] = reserveHeroes[0];
                    heroIds[2] = reserveHeroes[1];
                }
            }

            //Фрагменты героев
            let heroFragments = [0, 0, 0];
            for (let i = 0; i < 3; i++){
                if (haveHeroFragments[heroIds[i]]) {
                    heroFragments[i] = haveHeroFragments[heroIds[i]];
                }
            }

            console.log(heroIds);
            console.log(heroFragments);

            while (lives > 0 && missionId <= firstMissionId + 7) {
                setProgress(I18N('NHR_SHOPPING'), false);
                //Купить героев
                let result = await buyHeroesAndPets(shopId, heroIds, heroFragments, pets, spendCoins);

                //Выйти, если босс побежден и полностью собрали три героя
                if (result && farmedChapters.includes(chapterId)) {
                    break;
                }

                //Текущая миссия босс или нет
                let boss = false;

                //Произвести атаку босса, если его ни разу не убили
                if (!farmedChapters.includes(chapterId)) {
                    if (missionId == firstMissionId + 7) {
                        boss = true;
                        console.log('%cАтакуем босса ', 'color: green; font-weight: bold;');
                    }
                } else {
                    //Не атаковать босса, если его уже убили
                    if (missionId == firstMissionId + 7) {
                        break;
                    }
                }
                //Получить атакующую команду
                let heroes = [];
                let havePets = [];
                let pet;
                let petsFavor = {};
                await getAttackingTeam(heroes, havePets);
                console.log('heroes ', JSON.stringify(heroes));
                if (havePets.length > 0) {
                    //Основной питомец 6005 - Альбрус
                    let mainPet = 6005;
                    if (havePets.includes(mainPet)) {
                        pet = mainPet;
                    } else {
                        pet = havePets[0];
                    }
                    //Покровительство
                    const petLib = lib.getData('pet');
                    for (let heroId of heroes) {
                        /** Поиск питомца для героя */
                        for (let petId of havePets) {
                            if (petLib[petId].favorHeroes.includes(heroId)) {
                                petsFavor[heroId] = petId;
                                havePets = havePets.filter((e) => e != petId);
                                break;
                            };
                        }
                    }
                }
                console.log('Атакующие герои ' + heroes);

                //Проходим миссию
                if (!boss) {
                    setProgress(I18N('NT_MISSION_PROGRESS', {missionNumber: 1 + missionId - firstMissionId}), false);
                } else {
                    setProgress(I18N('NT_MISSION_PROGRESS_BOSS'), false);
                }
                await new Promise((e) => setTimeout(e, 2000));

                await attackHeroMission(missionId, chapterId, heroes, pet, boss, petsFavor);
                //Убили / не убили босса, вышли с "while"
                if (boss == true) {
                    break;
                }
                //Результат атаки
                let invasionInfo = await Caller.send('invasion_getInfo');
                farmedChapters = invasionInfo.farmedChapters;
                let missions = Object.values(invasionInfo.actions);
                for (let mission of missions) {
                    if (mission.payload.wins == 0) {
                        missionId = mission.payload.id;
                        break;
                    }
                }
                lives = invasionInfo.lives;
                console.log('missionId ' + missionId);
                console.log('lives ' + lives);
            }
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    async function getHeroIdsToBuy() {
        const quest = await Caller.send('questGetAll');
        const heroIds = quest
            .filter((e) => e.state == 1 && lib.data.quest.special[e.id]?.translationMethod === 'invasionStallHeroFragments')
            .map((e) => lib.data.quest.special[e.id].farmCondition.eventFunc.args.fragmentId);
        return heroIds;
    }

    async function getTitanIdsToBuy() {
        const quest = await Caller.send('questGetAll');
        const titanIds = quest
            .filter((e) => e.state == 1 && lib.data.quest.special[e.id]?.translationMethod === 'invasionStallFragmentsTitans')
            .map((e) => lib.data.quest.special[e.id].farmCondition.eventFunc.args.fragmentId);
        return titanIds;
    }

    async function getTitanSkillIdsToBuy() {
        const quest = await Caller.send('questGetAll');
        const titanSkillIds = quest
            .filter((e) => e.state == 1 && lib.data.quest.special[e.id]?.translationMethod === 'invasionStallFragmentsTitanSkills')
            .map((e) => lib.data.quest.special[e.id].farmCondition.eventFunc.args.fragmentId);
        return titanSkillIds;
    }

    async function getShopId(chapters, titanOrHero) {
        if (chapters[0].settings?.stallShopId) {
            for (let chapter of chapters) {
                if (chapter.settings.unitType === titanOrHero) {
                    return chapter.settings.stallShopId;
                }
            }
        }
        const result = await Caller.send('shopGetAll');
        const shops = Object.values(result).filter(e => e.slots[1].cost?.coin);
        for (let shop of shops){
            if (shop.slots[1].cost.coin[1080]){
                return shop.id;
            }
        }
        return false;
    }

    function getChapterId(chapters, titanOrHero) {
        for (let chapter of chapters) {
            if (chapter.settings.unitType === titanOrHero) {
                return chapter.id;
            }
        }
        return false;
    }

    function compareVersions(version1, version2) {
        const v1 = version1.split('.').map(Number);
        const v2 = version2.split('.').map(Number);

        const maxLength = Math.max(v1.length, v2.length);

        for (let i = 0; i < maxLength; i++) {
            const num1 = v1[i] || 0;
            const num2 = v2[i] || 0;

            if (num1 > num2) return 1;
            if (num1 < num2) return -1;
        }

        return 0;
    }

    async function getAttackingTeam (heroes, other = [], allHeroes = []) {
        let haveFragments = Object.entries(await Caller.send('invasion_getInfo').then((e) => e.fragments)).map(e => ({id:e[0],count:e[1]})).sort((a, b) => b.count - a.count);
        console.log(haveFragments);
        for (let m of haveFragments) {
            //Отделяем питомцев и фрагменты тотемов
            if (Number(m.id) < 4400) {
                allHeroes.push(Number(m.id));
                if (heroes.length < 5) {
                    heroes.push(Number(m.id));
                }
            }
            if (Number(m.id) > 4400) {
                other.push(Number(m.id));
            }
        }
    }

    async function getTeamButton (titanAttackingTeams, chapterNumber, titanOrHero) {
        let message = '';
        if (titanOrHero === 'titan' ) {
            message = I18N('NT_ENTER_TITAN_IDS', { chapterNumber: romanNumerals[chapterNumber] });
        }
        if (titanOrHero === 'hero' ) {
            message = I18N('NT_ENTER_HERO_IDS', { chapterNumber: romanNumerals[chapterNumber] });
        }
        const answer = await popup.confirm(message, [
            {
                msg: I18N('NT_COMPLETE_CHAPTER'),
                placeholder: '1,2,3,4,5,6',
                isInput: true,
                default: titanAttackingTeams,
                color: 'green',
            },
            {
                msg: I18N('BTN_CANCEL'),
                result: false,
                isCancel: true,
                color: 'red',
            },
        ]);
        if (!answer) {
            return false;
        }

        let team = answer.split(',');
        if (team.length != 5) {
            team = answer.split('-');
        }

        if (team.length != 5) {
            if (titanOrHero === 'hero' ) {
                confShow(I18N('NT_MUST_FIVE_HEROES'));
            }
            if (titanOrHero === 'titan' ) {
                confShow(I18N('NT_MUST_FIVE_TITANS'));
            }
            return false;
        }

        for (let p in team) {
            team[p] = +team[p].trim()
            if (Number.isNaN(team[p])) {
                confShow(I18N('NT_MUST_ONLY_NUMBERS'));
                return false;
            }
        }
        return team;
    }

    async function buyTitanInTheStore(titanShopId, titanIds, titanFragments, titanSkilsIds, titanSkilFragments) {
        console.log('Зашли в магазин');
        console.log('titanIds ', JSON.stringify(titanIds));
        console.log('titanFragments ', JSON.stringify(titanFragments));
        console.log('titanSkilsIds ', JSON.stringify(titanSkilsIds));
        console.log('titanSkilFragments ', JSON.stringify(titanSkilFragments));

        let coins = await Caller.send('inventoryGet').then((e) => e.coin[1080]);
        console.log('Монеты: ' + coins);

        let shopSlots = null;

        while (coins >= 9) {
            //Получить состояние магазина
            if (!shopSlots) {
                shopSlots = await Caller.send([{ name: 'shopGet', args: { shopId: titanShopId } }]).then((e) => Object.values(e.slots));
            }
            for (let slot of shopSlots) {
                //Пропустить скрытые лоты
                if (slot.reward.invasionFragmentTitanRand || slot.reward.invasionFragmentSkillRand) {
                    continue;
                }
                //Купить первого титана
                if (slot.reward.invasionFragmentTitan?.[titanIds[0]] && titanFragments[0] < 7) {
                    if (coins >= slot.cost.coin[1080]) {
                        let shopBuy = await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: titanShopId, slot: slot.id } });
                        if (shopBuy.error) {
                            await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                            coins = 0;
                            continue;
                        }
                        console.log('%cКуплен титан ', 'color: green; font-weight: bold;');
                        coins -= slot.cost.coin[1080];
                        titanFragments[0] += slot.reward.invasionFragmentTitan?.[titanIds[0]];

                        //Если c первым титаном есть второй
                        if (slot.reward.invasionFragmentTitan?.[titanIds[1]]) {
                            console.log('%cВторой титан в комплекте ', 'color: green; font-weight: bold;');
                            titanFragments[1] += slot.reward.invasionFragmentTitan?.[titanIds[1]];
                        }
                    } else {
                        await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                    }
                    continue;
                }

                //Купить второго титана
                if (slot.reward.invasionFragmentTitan?.[titanIds[1]] && titanFragments[1] < 7) {
                    if (coins >= slot.cost.coin[1080]) {
                        let shopBuy = await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: titanShopId, slot: slot.id } });
                        if (shopBuy.error) {
                            await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                            coins = 0;
                            continue;
                        }
                        console.log('%cКуплен титан ', 'color: green; font-weight: bold;');
                        coins -= slot.cost.coin[1080];
                        titanFragments[1] += slot.reward.invasionFragmentTitan?.[titanIds[1]];
                    } else {
                        await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                    }
                    continue;
                }

                //Купить первый навык тотема
                if (slot.reward.invasionFragmentSkill?.[titanSkilsIds[0]] && titanSkilFragments[0] < 7) {
                    if (coins >= slot.cost.coin[1080]) {
                        let shopBuy = await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: titanShopId, slot: slot.id } });
                        if (shopBuy.error) {
                            await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                            coins = 0;
                            continue;
                        }
                        console.log('%cКуплена часть навыка тотема ', 'color: green; font-weight: bold;');
                        coins -= slot.cost.coin[1080];
                        titanSkilFragments[0] += slot.reward.invasionFragmentSkill?.[titanSkilsIds[0]];
                    } else {
                        await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                    }
                    continue;
                }

                //Купить второй навык тотема
                if (titanSkilsIds.length >= 2 && slot.reward.invasionFragmentSkill?.[titanSkilsIds[1]] && titanSkilFragments[1] < 7) {
                    if (coins >= slot.cost.coin[1080]) {
                        let shopBuy = await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: titanShopId, slot: slot.id } });
                        if (shopBuy.error) {
                            await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                            coins = 0;
                            continue;
                        }
                        console.log('%cКуплена часть навыка тотема ', 'color: green; font-weight: bold;');
                        coins -= slot.cost.coin[1080];
                        titanSkilFragments[1] += slot.reward.invasionFragmentSkill?.[titanSkilsIds[1]];
                    } else {
                        await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                    }
                    continue;
                }
                //Если ТИТАНЫ собраны
                if (titanFragments[0] >= 7 && titanFragments[1] >= 7) {
                    //Обновляем навыки
                    if (titanSkilsIds.length >= 2 && titanSkilFragments[0] >= 7 && titanSkilFragments[1] >= 7) {
                        console.log('%cМеняем навыки навыки тотемов', 'color: green; font-weight: bold;');
                        titanSkilsIds = await getTitanSkillIdsToBuy();
                        titanSkilFragments = [0, 0];
                        if (titanSkilsIds.length == 1) {
                            titanSkilFragments = [0];
                        }
                    }
                    if ((titanSkilsIds.length == 1 && titanSkilFragments[0] >= 7) || titanSkilsIds.length == 0) {
                        console.log('%cВсе навыки тотемов куплены, переходим к покупке следующих титанов', 'color: green; font-weight: bold;');
                        return true;
                    }
                }
            }
            //Обновить магазин
            if (coins >= 12) {
                shopSlots = await Caller.send([{ name: 'shopRefresh', args: { shopId: titanShopId } }]).then((e) => Object.values(e.slots));
                coins -= 3;
                console.log('Обновили магазин. Осталось монет: ' + coins);
                if (shopSlots.error) {
                    coins = 0;
                }
            } else {
                break;
            }
        }
        return false;
    }

    async function buyTotemSkilsInTheStore(titanShopId, titanIds, titanFragments, titanSkilsIds, titanSkilFragments) {
        console.log('Зашли в магазин');
        console.log('titanIds ', JSON.stringify(titanIds));
        console.log('titanFragments ', JSON.stringify(titanFragments));
        console.log('titanSkilsIds ', JSON.stringify(titanSkilsIds));
        console.log('titanSkilFragments ', JSON.stringify(titanSkilFragments));

        let coins = await Caller.send('inventoryGet').then((e) => e.coin[1080]);
        console.log('Монеты: ' + coins);

        let shopSlots = null;
        while (coins >= 9) {
            //Получить состояние магазина
            if (!shopSlots) {
                shopSlots = await Caller.send([{ name: 'shopGet', args: { shopId: titanShopId } }]).then((e) => Object.values(e.slots));
            }
            for (let slot of shopSlots) {
                //Пропустить скрытые лоты
                if (slot.reward.invasionFragmentTitanRand || slot.reward.invasionFragmentSkillRand) {
                    continue;
                }
                //Купить первый навык тотема
                if (slot.reward.invasionFragmentSkill?.[titanSkilsIds[0]] && titanSkilFragments[0] < 7) {
                    if (coins >= slot.cost.coin[1080]) {
                        let shopBuy = await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: titanShopId, slot: slot.id } });
                        if (shopBuy.error) {
                            await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                            coins = 0;
                            continue;
                        }
                        console.log('%cКуплена часть навыка тотема ', 'color: green; font-weight: bold;');
                        coins -= slot.cost.coin[1080];
                        titanSkilFragments[0] += slot.reward.invasionFragmentSkill?.[titanSkilsIds[0]];
                    } else {
                        await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                    }
                    continue;
                }

                //Купить второй навык тотема
                if (titanSkilsIds.length >= 2 && slot.reward.invasionFragmentSkill?.[titanSkilsIds[1]] && titanSkilFragments[1] < 7) {
                    if (coins >= slot.cost.coin[1080]) {
                        let shopBuy = await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: titanShopId, slot: slot.id } });
                        if (shopBuy.error) {
                            await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                            coins = 0;
                            continue;
                        }
                        console.log('%cКуплена часть навыка тотема ', 'color: green; font-weight: bold;');
                        coins -= slot.cost.coin[1080];
                        titanSkilFragments[1] += slot.reward.invasionFragmentSkill?.[titanSkilsIds[1]];
                    } else {
                        await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                    }
                    continue;
                }
                //Если навыки собраны, обновляем навыки
                if (titanSkilsIds.length >= 2 && titanSkilFragments[0] >= 7 && titanSkilFragments[1] >= 7) {
                    titanSkilsIds = await getTitanSkillIdsToBuy();
                    titanSkilFragments = [0, 0];
                    if (titanSkilsIds.length == 1) {
                        titanSkilFragments = [0];
                    }
                }

                if ((titanSkilsIds.length == 1 && titanSkilFragments[0] >= 7) || titanSkilsIds.length == 0) {
                    //Тратим оставшиеся монеты (пусть будет, вдруг пригодиццо)
                    /*if (coins >= slot.cost.coin[1080]){
                        let shopBuy = await Caller.send({name: 'shopBuy', args: {cost: {}, reward: {}, shopId: titanShopId, slot: slot.id}});
                        coins -= slot.cost.coin[1080];
                        if(shopBuy.error){
                            coins = 0;
                            continue;
                        }
                    }*/
                    //Купили все навыки, идем на выход
                    return true;
                }

                //Купить первого титана
                if (slot.reward.invasionFragmentTitan?.[titanIds[0]] && titanFragments[0] < 7) {
                    if (coins >= slot.cost.coin[1080]) {
                        let shopBuy = await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: titanShopId, slot: slot.id } });
                        if (shopBuy.error) {
                            await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                            coins = 0;
                            continue;
                        }
                        console.log('%cКуплен титан ', 'color: green; font-weight: bold;');
                        coins -= slot.cost.coin[1080];
                        titanFragments[0] += slot.reward.invasionFragmentTitan?.[titanIds[0]];

                        //Если c первым титаном есть второй
                        if (slot.reward.invasionFragmentTitan?.[titanIds[1]]) {
                            console.log('%cВторой титан в комплекте ', 'color: green; font-weight: bold;');
                            titanFragments[1] += slot.reward.invasionFragmentTitan?.[titanIds[1]];
                        }
                    } else {
                        await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                    }
                    continue;
                }

                //Купить второго титана
                if (slot.reward.invasionFragmentTitan?.[titanIds[1]] && titanFragments[1] < 7) {
                    if (coins >= slot.cost.coin[1080]) {
                        let shopBuy = await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: titanShopId, slot: slot.id } });
                        if (shopBuy.error) {
                            await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                            coins = 0;
                            continue;
                        }
                        console.log('%cКуплен титан ', 'color: green; font-weight: bold;');
                        coins -= slot.cost.coin[1080];
                        titanFragments[1] += slot.reward.invasionFragmentTitan?.[titanIds[1]];
                    } else {
                        await Caller.send({ name: 'shop_pinSlot', args: { shopId: titanShopId, slotId: slot.id } });
                    }
                    continue;
                }
            }
            //Обновить магазин
            if (coins >= 12) {
                shopSlots = await Caller.send({ name: 'shopRefresh', args: { shopId: titanShopId } }).then((e) => Object.values(e.slots));
                coins -= 3;
                console.log('Обновили магазин. Осталось монет ' + coins);
                if (shopSlots.error) {
                    coins = 0;
                }
            } else {
                break;
            }
        }
        return false;
    }


    async function buyTitansAndTotemSkils (shopId, titanIds, titanFragments, titanSkilsIds, titanSkilFragments) {
        console.log('Зашли в магазин');
        console.log('titanIds ', JSON.stringify(titanIds));
        console.log('titanFragments ', JSON.stringify(titanFragments));
        console.log('titanSkilsIds ', JSON.stringify(titanSkilsIds));
        console.log('titanSkilFragments ', JSON.stringify(titanSkilFragments));

        let coins = await Caller.send('inventoryGet').then((e) => e.coin[1080]);
        console.log('Монеты: ' + coins);

        let shopSlots = null;

        while (coins >= 9) {
            //Получить состояние магазина
            if (!shopSlots) {
                shopSlots = await Caller.send([{ name: 'shopGet', args: { shopId: shopId } }]).then((e) => Object.values(e.slots));
            }
            for (let slot of shopSlots) {
                //Пропустить скрытые лоты
                if (slot.reward.invasionFragmentTitanRand || slot.reward.invasionFragmentSkillRand) {
                    continue;
                }
                let boughtTitan = false;
                //Купить титанов
                for (let t = 0; t < titanIds.length; t++) {
                    if (slot.reward.invasionFragmentTitan?.[titanIds[t]] && titanFragments[t] < 7) {
                        if (coins >= slot.cost.coin[1080]) {
                            let shopBuy = await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: shopId, slot: slot.id } });
                            console.log('%cКуплен титан ', 'color: green; font-weight: bold;');
                            coins -= slot.cost.coin[1080];
                            titanFragments[t] += slot.reward.invasionFragmentTitan?.[titanIds[t]];

                            //Если c первым титаном есть другой
                            for (let i = t+1; i < titanIds.length; i++) {
                                if (slot.reward.invasionFragmentTitan?.[titanIds[i]]) {
                                    console.log('%cВторой титан в комплекте ', 'color: green; font-weight: bold;');
                                    titanFragments[i] += slot.reward.invasionFragmentTitan?.[titanIds[i]];
                                    break;
                                }
                            }
                        } else {
                            await Caller.send({ name: 'shop_pinSlot', args: { shopId: shopId, slotId: slot.id } });
                        }
                        boughtTitan = true;
                        break;
                    }
                }

                //Купить навыки тотема
                if (!boughtTitan) {
                    for (let s = 0; s < titanSkilsIds.length; s++) {
                        if (slot.reward.invasionFragmentSkill?.[titanSkilsIds[s]] && titanSkilFragments[s] < 7) {
                            if (coins >= slot.cost.coin[1080]) {
                                let shopBuy = await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: shopId, slot: slot.id } });
                                console.log('%cКуплена часть навыка тотема ', 'color: green; font-weight: bold;');
                                coins -= slot.cost.coin[1080];
                                titanSkilFragments[s] += slot.reward.invasionFragmentSkill?.[titanSkilsIds[s]];
                            } else {
                                await Caller.send({ name: 'shop_pinSlot', args: { shopId: shopId, slotId: slot.id } });
                            }
                            break;
                        }
                    }
                }

                //Если ТИТАНЫ и тотемы собраны
                let titanF = false;
                let titanS = false;
                for (let tf of titanFragments) {
                    if (tf < 7) {
                        titanF = false;
                        break;
                    }
                    titanF = true;
                }
                for (let ts of titanSkilFragments) {
                    if (ts < 7) {
                        titanS = false;
                        break;
                    }
                    titanS = true;
                }
                if ( titanF && titanS) {
                    return;
                }
            }
            //Обновить магазин
            if (coins >= 12) {
                shopSlots = await Caller.send([{ name: 'shopRefresh', args: { shopId: shopId } }]).then((e) => Object.values(e.slots));
                coins -= 3;
                console.log('Обновили магазин. Осталось монет: ' + coins);
                if (shopSlots.error) {
                    coins = 0;
                }
            } else {
                break;
                return;
            }
        }
    }

    async function buyHeroesAndPets (shopId, heroIds, heroFragments, pets, spendCoins = false) {
        console.log('Зашли в магазин');
        console.log('heroIds ', JSON.stringify(heroIds));
        console.log('heroFragments ', JSON.stringify(heroFragments));
        console.log('pets ', JSON.stringify(pets));


        let coins = await Caller.send('inventoryGet').then((e) => e.coin[1080]);
        console.log('Монеты: ' + coins);

        let shopSlots = null;
        let boughtAllHeroes = false;

        while (coins >= 12) {
            try {
                //Получить состояние магазина
                if (!shopSlots) {
                    shopSlots = await Caller.send([{ name: 'shopGet', args: { shopId: shopId } }]).then((e) => Object.values(e.slots));
                }
                //Если куплены все герои, питомци, и нет задания на трату монет, выйти и заменить героев для покупки
                if (boughtAllHeroes && pets.length == 0 && !spendCoins ) {
                    return true;
                }

                //Если куплены все герои, питомци, и есть задание на трату монет
                if (boughtAllHeroes && pets.length == 0 && spendCoins ) {
                    console.log('%cТратим монеты ', 'color: red; font-weight: bold;');
                    for (let slot of shopSlots) {
                        if (coins >= slot.cost.coin[1080]) {
                            await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: shopId, slot: slot.id } });
                            coins -= slot.cost.coin[1080];
                        }
                    }
                }
                //Купить героев
                if (!boughtAllHeroes) {
                    for (let slot of shopSlots) {
                        //Пропустить скрытые лоты и питомцев
                        if (slot.reward.invasionFragmentHeroRand || slot.reward.invasionFragmentPet) {
                            continue;
                        }
                        for (let t = 0; t < heroIds.length; t++) {
                            if (slot.reward.invasionFragmentHero?.[heroIds[t]] && heroFragments[t] < 7) {
                                if (coins >= slot.cost.coin[1080]) {
                                    let shopBuy = await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: shopId, slot: slot.id } });
                                    console.log('%cКуплен герой ', 'color: green; font-weight: bold;');
                                    coins -= slot.cost.coin[1080];
                                    heroFragments[t] += slot.reward.invasionFragmentHero?.[heroIds[t]];

                                    //Если c первым героем есть другой
                                    for (let i = t+1; i < heroIds.length; i++) {
                                        if (slot.reward.invasionFragmentHero?.[heroIds[i]]) {
                                            console.log('%cВторой герой в комплекте ', 'color: green; font-weight: bold;');
                                            heroFragments[i] += slot.reward.invasionFragmentHero?.[heroIds[i]];
                                            break;
                                        }
                                    }
                                } else {
                                    await Caller.send({ name: 'shop_pinSlot', args: { shopId: shopId, slotId: slot.id } });
                                }
                                break;
                            }
                        }
                    }
                    //Куплены все герои или нет
                    for (let fragments of heroFragments) {
                        if (fragments < 7) {
                            boughtAllHeroes = false;
                            break;
                        }
                        boughtAllHeroes = true;
                    }
                }

                //Купить питомцев
                if (pets.length > 0) {
                    for (let slot of shopSlots) {
                        //Пропустить скрытые лоты и героев
                        if (slot.reward.invasionFragmentHeroRand || slot.reward.invasionFragmentHero) {
                            continue;
                        }
                        for (let s = 0; s < pets.length; s++) {
                            if (slot.reward.invasionFragmentPet?.[pets[s]] ) {
                                if (coins >= slot.cost.coin[1080]) {
                                    let shopBuy = await Caller.send({ name: 'shopBuy', args: { cost: {}, reward: {}, shopId: shopId, slot: slot.id } });
                                    console.log('%cКуплен питомец ', 'color: green; font-weight: bold;');
                                    coins -= slot.cost.coin[1080];
                                    pets.splice(pets.indexOf(pets[s]), 1)
                                } else {
                                    await Caller.send({ name: 'shop_pinSlot', args: { shopId: shopId, slotId: slot.id } });
                                }
                                break;
                            }
                        }
                        if (pets.length == 0) {
                            break;
                        }
                    }
                }
            } catch (e) {
                console.log('%cПроизошла ошибка', 'color: red; font-weight: bold;');
                coins = await Caller.send('inventoryGet').then((e) => e.coin[1080]);
            }

            //Обновить магазин
            if (coins >= 15) {
                try {
                    shopSlots = await Caller.send([{ name: 'shopRefresh', args: { shopId: shopId } }]).then((e) => Object.values(e.slots));
                    coins -= 3;
                    console.log('Обновили магазин. Осталось монет: ' + coins);
                } catch (e) {
                    shopSlots = await Caller.send([{ name: 'shopRefresh', args: { shopId: shopId } }]).then((e) => Object.values(e.slots));
                    coins = await Caller.send('inventoryGet').then((e) => e.coin[1080]);
                }
            } else {
                break;
            }
        }
        return false;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    async function attackTitanMission(missionId, chapterId, heroes, firstSpiritSkills, boss = false) {
        if (boss == true) {
            console.log('%cАтакуем босса ', 'color: green; font-weight: bold;');
        }
        const startBattle = await Caller.send({
            name: 'invasion_bossStart',
            args: {
                id: missionId,
                chapterId: chapterId,
                heroes: heroes,
                //firstSpiritElement: "earth",
                firstSpiritSkills: firstSpiritSkills,
                favor: {},
            },
        });
        const calcBattle = await Calc(startBattle);

        if (!calcBattle.result.win) {
            const cloneBattle = structuredClone(startBattle);
            const bFix = new WinFixBattle(cloneBattle);
            let result = await bFix.start(cloneBattle.endTime, Infinity);
            if (result.result?.win) {
                calcBattle.result = result.result;
                calcBattle.progress = result.progress;
                calcBattle.battleTimer = result.battleTimer;
            }
        }

        if (!calcBattle.result.win && boss == true) {
            //Босса не убили
            console.log('%cБосса не убили ', 'color: red; font-weight: bold;');
            return;
        }
        if (boss == true) {
            let timer = calcBattle.battleTimer;
            console.log('%cУбили босса', 'color: green; font-weight: bold;');
            let message = I18N('NT_BOSS_WAS_KILLED_SET_PROGRESS_1');
            setProgress(message, false);
            await new Promise((e) => setTimeout(e, 1000));
            message += I18N('NT_BOSS_WAS_KILLED_SET_PROGRESS_2');
            setProgress(message, false);
            await new Promise((e) => setTimeout(e, 1000));
            message += I18N('NT_BOSS_WAS_KILLED_SET_PROGRESS_3');
            setProgress(message, false);
            await new Promise((e) => setTimeout(e, 1000));
            message += I18N('NT_BOSS_WAS_KILLED_SET_PROGRESS_4');
            setProgress(message, false);
            await new Promise((e) => setTimeout(e, 1000));
            message += I18N('NT_BOSS_WAS_KILLED_SET_PROGRESS_5');
            await countdownTimer(timer, message);
        }

        const endBattle = await Caller.send({
            name: 'invasion_bossEnd',
            args: {
                id: missionId,
                result: calcBattle.result,
                progress: calcBattle.progress,
            },
        });
    }

    async function attackHeroMission(missionId, chapterId, heroes, pet, boss = false, petsFavor) {
        if (boss == true) {
            console.log('%cАтакуем босса ', 'color: green; font-weight: bold;');
        }
        const startBattle = await Caller.send({
            name: 'invasion_bossStart',
            args: {
                id: missionId,
                chapterId: chapterId,
                heroes: heroes,
                pet: pet,
                favor: petsFavor,
            },
        });
        const calcBattle = await Calc(startBattle);

        if (!calcBattle.result.win) {
            const cloneBattle = structuredClone(startBattle);
            const bFix = new WinFixBattle(cloneBattle);
            let result = await bFix.start(cloneBattle.endTime, Infinity);
            if (result.result?.win) {
                calcBattle.result = result.result;
                calcBattle.progress = result.progress;
                calcBattle.battleTimer = result.battleTimer;
            }
        }

        if (!calcBattle.result.win && boss == true) {
            //Босса не убили
            console.log('%cБосса не убили ', 'color: red; font-weight: bold;');
            return;
        }
        if (boss == true) {
            let timer = calcBattle.battleTimer;
            console.log('%cУбили босса', 'color: green; font-weight: bold;');
            let message = I18N('NT_BOSS_WAS_KILLED_SET_PROGRESS_1');
            setProgress(message, false);
            await new Promise((e) => setTimeout(e, 1000));
            message += I18N('NT_BOSS_WAS_KILLED_SET_PROGRESS_2');
            setProgress(message, false);
            await new Promise((e) => setTimeout(e, 1000));
            message += I18N('NT_BOSS_WAS_KILLED_SET_PROGRESS_3');
            setProgress(message, false);
            await new Promise((e) => setTimeout(e, 1000));
            message += I18N('NT_BOSS_WAS_KILLED_SET_PROGRESS_4');
            setProgress(message, false);
            await new Promise((e) => setTimeout(e, 1000));
            message += I18N('NT_BOSS_WAS_KILLED_SET_PROGRESS_5');
            await countdownTimer(timer, message);
        }

        await Caller.send({
            name: 'invasion_bossEnd',
            args: {
                id: missionId,
                result: calcBattle.result,
                progress: calcBattle.progress,
            },
        });
    }
})();