Automation of actions for the game Hero Wars
// ==UserScript== // @name HeroWarsHelper // @name:en HeroWarsHelper // @name:ru HeroWarsHelper // @namespace HeroWarsHelper // @version 2.327 // @description Automation of actions for the game Hero Wars // @description:en Automation of actions for the game Hero Wars // @description:ru Автоматизация действий для игры Хроники Хаоса // @author ZingerY // @license Copyright ZingerY // @homepage https://zingery.ru/scripts/HeroWarsHelper.user.js // @icon https://zingery.ru/scripts/VaultBoyIco16.ico // @icon64 https://zingery.ru/scripts/VaultBoyIco64.png // @match https://www.hero-wars.com/* // @match https://apps-1701433570146040.apps.fbsbx.com/* // @run-at document-start // ==/UserScript== (function() { /** * Start script * * Стартуем скрипт */ console.log('%cStart ' + GM_info.script.name + ', v' + GM_info.script.version + ' by ' + GM_info.script.author, 'color: red'); /** * Script info * * Информация о скрипте */ this.scriptInfo = (({name, version, author, homepage, lastModified}, updateUrl) => ({name, version, author, homepage, lastModified, updateUrl})) (GM_info.script, GM_info.scriptUpdateURL); this.GM_info = GM_info; /** * Information for completing daily quests * * Информация для выполнения ежендевных квестов */ const questsInfo = {}; /** * Is the game data loaded * * Загружены ли данные игры */ let isLoadGame = false; /** * Headers of the last request * * Заголовки последнего запроса */ let lastHeaders = {}; /** * Information about sent gifts * * Информация об отправленных подарках */ let freebieCheckInfo = null; /** * missionTimer * * missionTimer */ let missionBattle = null; /** * User data * * Данные пользователя */ let userInfo; this.isTimeBetweenNewDays = function () { if (userInfo.timeZone <= 3) { return false; } const nextDayTs = new Date(userInfo.nextDayTs * 1e3); const nextServerDayTs = new Date(userInfo.nextServerDayTs * 1e3); if (nextDayTs > nextServerDayTs) { nextDayTs.setDate(nextDayTs.getDate() - 1); } const now = Date.now(); if (now > nextDayTs && now < nextServerDayTs) { return true; } return false; }; function getUserInfo() { return userInfo; } /** * Original methods for working with AJAX * * Оригинальные методы для работы с AJAX */ const original = { open: XMLHttpRequest.prototype.open, send: XMLHttpRequest.prototype.send, setRequestHeader: XMLHttpRequest.prototype.setRequestHeader, SendWebSocket: WebSocket.prototype.send, fetch: fetch, }; // Sentry blocking // Блокировка наблюдателя this.fetch = function (url, options) { /** * Checking URL for blocking * Проверяем URL на блокировку */ if (url.includes('sentry.io')) { console.log('%cFetch blocked', 'color: red'); console.log(url, options); const body = { id: md5(Date.now()), }; let info = {}; try { info = JSON.parse(options.body); } catch (e) {} if (info.event_id) { body.id = info.event_id; } /** * Mock response for blocked URL * * Мокаем ответ для заблокированного URL */ const mockResponse = new Response('Custom blocked response', { status: 200, headers: { 'Content-Type': 'application/json' }, body, }); return Promise.resolve(mockResponse); } else { /** * Call the original fetch function for all other URLs * Вызываем оригинальную функцию fetch для всех других URL */ return original.fetch.apply(this, arguments); } }; /** * Decoder for converting byte data to JSON string * * Декодер для перобразования байтовых данных в JSON строку */ const decoder = new TextDecoder("utf-8"); /** * Stores a history of requests * * Хранит историю запросов */ let requestHistory = {}; /** * URL for API requests * * URL для запросов к API */ let apiUrl = ''; /** * Connecting to the game code * * Подключение к коду игры */ this.cheats = new hackGame(); /** * The function of calculating the results of the battle * * Функция расчета результатов боя */ this.BattleCalc = cheats.BattleCalc; /** * Sending a request available through the console * * Отправка запроса доступная через консоль */ this.SendRequest = send; /** * Simple combat calculation available through the console * * Простой расчет боя доступный через консоль */ this.Calc = function (data) { const type = getBattleType(data?.type); return new Promise((resolve, reject) => { try { BattleCalc(data, type, resolve); } catch (e) { reject(e); } }) } /** * Short asynchronous request * Usage example (returns information about a character): * const userInfo = await Send('{"calls":[{"name":"userGetInfo","args":{},"ident":"body"}]}') * * Короткий асинхронный запрос * Пример использования (возвращает информацию о персонаже): * const userInfo = await Send('{"calls":[{"name":"userGetInfo","args":{},"ident":"body"}]}') */ this.Send = function (json, pr) { return new Promise((resolve, reject) => { try { send(json, resolve, pr); } catch (e) { reject(e); } }) } this.xyz = (({ name, version, author }) => ({ name, version, author }))(GM_info.script); const i18nLangData = { /* English translation by BaBa */ en: { /* Checkboxes */ SKIP_FIGHTS: 'Skip battle', SKIP_FIGHTS_TITLE: 'Skip battle in Outland and the arena of the titans, auto-pass in the tower and campaign', ENDLESS_CARDS: 'Infinite cards', ENDLESS_CARDS_TITLE: 'Disable Divination Cards wasting', AUTO_EXPEDITION: 'Auto Expedition', AUTO_EXPEDITION_TITLE: 'Auto-sending expeditions', CANCEL_FIGHT: 'Cancel battle', CANCEL_FIGHT_TITLE: 'Ability to cancel manual combat on GW, CoW and Asgard', GIFTS: 'Gifts', GIFTS_TITLE: 'Collect gifts automatically', BATTLE_RECALCULATION: 'Battle recalculation', BATTLE_RECALCULATION_TITLE: 'Preliminary calculation of the battle', QUANTITY_CONTROL: 'Quantity control', QUANTITY_CONTROL_TITLE: 'Ability to specify the number of opened "lootboxes"', REPEAT_CAMPAIGN: 'Repeat missions', REPEAT_CAMPAIGN_TITLE: 'Auto-repeat battles in the campaign', DISABLE_DONAT: 'Disable donation', DISABLE_DONAT_TITLE: 'Removes all donation offers', DAILY_QUESTS: 'Quests', DAILY_QUESTS_TITLE: 'Complete daily quests', AUTO_QUIZ: 'AutoQuiz', AUTO_QUIZ_TITLE: 'Automatically receive correct answers to quiz questions', SECRET_WEALTH_CHECKBOX: 'Automatic purchase in the store "Secret Wealth" when entering the game', HIDE_SERVERS: 'Collapse servers', HIDE_SERVERS_TITLE: 'Hide unused servers', /* Input fields */ HOW_MUCH_TITANITE: 'How much titanite to farm', COMBAT_SPEED: 'Combat Speed Multiplier', NUMBER_OF_TEST: 'Number of test fights', NUMBER_OF_AUTO_BATTLE: 'Number of auto-battle attempts', /* Buttons */ RUN_SCRIPT: 'Run the', TO_DO_EVERYTHING: 'Do All', TO_DO_EVERYTHING_TITLE: 'Perform multiple actions of your choice', OUTLAND: 'Outland', OUTLAND_TITLE: 'Collect Outland', TITAN_ARENA: 'ToE', TITAN_ARENA_TITLE: 'Complete the titan arena', DUNGEON: 'Dungeon', DUNGEON_TITLE: 'Go through the dungeon', SEER: 'Seer', SEER_TITLE: 'Roll the Seer', TOWER: 'Tower', TOWER_TITLE: 'Pass the tower', EXPEDITIONS: 'Expeditions', EXPEDITIONS_TITLE: 'Sending and collecting expeditions', SYNC: 'Sync', SYNC_TITLE: 'Partial synchronization of game data without reloading the page', ARCHDEMON: 'Archdemon', FURNACE_OF_SOULS: 'Furnace of souls', ARCHDEMON_TITLE: 'Hitting kills and collecting rewards', ESTER_EGGS: 'Easter eggs', ESTER_EGGS_TITLE: 'Collect all Easter eggs or rewards', REWARDS: 'Rewards', REWARDS_TITLE: 'Collect all quest rewards', MAIL: 'Mail', MAIL_TITLE: 'Collect all mail, except letters with energy and charges of the portal', MINIONS: 'Minions', MINIONS_TITLE: 'Attack minions with saved packs', ADVENTURE: 'Adv.', ADVENTURE_TITLE: 'Passes the adventure along the specified route', STORM: 'Storm', STORM_TITLE: 'Passes the Storm along the specified route', SANCTUARY: 'Sanctuary', SANCTUARY_TITLE: 'Fast travel to Sanctuary', GUILD_WAR: 'Guild War', GUILD_WAR_TITLE: 'Fast travel to Guild War', SECRET_WEALTH: 'Secret Wealth', SECRET_WEALTH_TITLE: 'Buy something in the store "Secret Wealth"', /* Misc */ BOTTOM_URLS: '<a href="https://t.me/+0oMwICyV1aQ1MDAy" target="_blank" title="Telegram"><svg width="20" height="20" style="margin:2px" viewBox="0 0 1e3 1e3" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a" x1="50%" x2="50%" y2="99.258%"><stop stop-color="#2AABEE" offset="0"/><stop stop-color="#229ED9" offset="1"/></linearGradient></defs><g fill-rule="evenodd"><circle cx="500" cy="500" r="500" fill="url(#a)"/><path d="m226.33 494.72c145.76-63.505 242.96-105.37 291.59-125.6 138.86-57.755 167.71-67.787 186.51-68.119 4.1362-0.072862 13.384 0.95221 19.375 5.8132 5.0584 4.1045 6.4501 9.6491 7.1161 13.541 0.666 3.8915 1.4953 12.756 0.83608 19.683-7.5246 79.062-40.084 270.92-56.648 359.47-7.0089 37.469-20.81 50.032-34.17 51.262-29.036 2.6719-51.085-19.189-79.207-37.624-44.007-28.847-68.867-46.804-111.58-74.953-49.366-32.531-17.364-50.411 10.769-79.631 7.3626-7.6471 135.3-124.01 137.77-134.57 0.30968-1.3202 0.59708-6.2414-2.3265-8.8399s-7.2385-1.7099-10.352-1.0032c-4.4137 1.0017-74.715 47.468-210.9 139.4-19.955 13.702-38.029 20.379-54.223 20.029-17.853-0.3857-52.194-10.094-77.723-18.393-31.313-10.178-56.199-15.56-54.032-32.846 1.1287-9.0037 13.528-18.212 37.197-27.624z" fill="#fff"/></g></svg></a><a href="https://www.patreon.com/HeroWarsUserScripts" target="_blank" title="Patreon"><svg width="20" height="20" viewBox="0 0 1080 1080" xmlns="http://www.w3.org/2000/svg"><g fill="#FFF" stroke="None"><path d="m1033 324.45c-0.19-137.9-107.59-250.92-233.6-291.7-156.48-50.64-362.86-43.3-512.28 27.2-181.1 85.46-237.99 272.66-240.11 459.36-1.74 153.5 13.58 557.79 241.62 560.67 169.44 2.15 194.67-216.18 273.07-321.33 55.78-74.81 127.6-95.94 216.01-117.82 151.95-37.61 255.51-157.53 255.29-316.38z"/></g></svg></a>', GIFTS_SENT: 'Gifts sent!', DO_YOU_WANT: 'Do you really want to do this?', BTN_RUN: 'Run', BTN_CANCEL: 'Cancel', BTN_ACCEPT: 'Accept', BTN_OK: 'OK', MSG_HAVE_BEEN_DEFEATED: 'You have been defeated!', BTN_AUTO: 'Auto', MSG_YOU_APPLIED: 'You applied', MSG_DAMAGE: 'damage', MSG_CANCEL_AND_STAT: 'Auto (F5) and show statistic', MSG_REPEAT_MISSION: 'Repeat the mission?', BTN_REPEAT: 'Repeat', BTN_NO: 'No', MSG_SPECIFY_QUANT: 'Specify Quantity:', BTN_OPEN: 'Open', QUESTION_COPY: 'Question copied to clipboard', ANSWER_KNOWN: 'The answer is known', ANSWER_NOT_KNOWN: 'ATTENTION THE ANSWER IS NOT KNOWN', BEING_RECALC: 'The battle is being recalculated', THIS_TIME: 'This time', VICTORY: '<span style="color:green;">VICTORY</span>', DEFEAT: '<span style="color:red;">DEFEAT</span>', CHANCE_TO_WIN: 'Chance to win <span style="color: red;">based on pre-calculation</span>', OPEN_DOLLS: 'nesting dolls recursively', SENT_QUESTION: 'Question sent', SETTINGS: 'Settings', MSG_BAN_ATTENTION: '<p style="color:red;">Using this feature may result in a ban.</p> Continue?', BTN_YES_I_AGREE: 'Yes, I understand the risks!', BTN_NO_I_AM_AGAINST: 'No, I refuse it!', VALUES: 'Values', EXPEDITIONS_SENT: 'Expeditions:<br>Collected: {countGet}<br>Sent: {countSend}', EXPEDITIONS_NOTHING: 'Nothing to collect/send', EXPEDITIONS_NOTTIME: 'It is not time for expeditions', TITANIT: 'Titanit', COMPLETED: 'completed', FLOOR: 'Floor', LEVEL: 'Level', BATTLES: 'battles', EVENT: 'Event', NOT_AVAILABLE: 'not available', NO_HEROES: 'No heroes', DAMAGE_AMOUNT: 'Damage amount', NOTHING_TO_COLLECT: 'Nothing to collect', COLLECTED: 'Collected', REWARD: 'rewards', REMAINING_ATTEMPTS: 'Remaining attempts', BATTLES_CANCELED: 'Battles canceled', MINION_RAID: 'Minion Raid', STOPPED: 'Stopped', REPETITIONS: 'Repetitions', MISSIONS_PASSED: 'Missions passed', STOP: 'stop', TOTAL_OPEN: 'Total open', OPEN: 'Open', ROUND_STAT: 'Damage statistics for ', BATTLE: 'battles', MINIMUM: 'Minimum', MAXIMUM: 'Maximum', AVERAGE: 'Average', NOT_THIS_TIME: 'Not this time', RETRY_LIMIT_EXCEEDED: 'Retry limit exceeded', SUCCESS: 'Success', RECEIVED: 'Received', LETTERS: 'letters', PORTALS: 'portals', ATTEMPTS: 'attempts', /* Quests */ QUEST_10001: 'Upgrade the skills of heroes 3 times', QUEST_10002: 'Complete 10 missions', QUEST_10003: 'Complete 3 heroic missions', QUEST_10004: 'Fight 3 times in the Arena or Grand Arena', QUEST_10006: 'Use the exchange of emeralds 1 time', QUEST_10007: 'Perform 1 summon in the Solu Atrium', QUEST_10016: 'Send gifts to guildmates', QUEST_10018: 'Use an experience potion', QUEST_10019: 'Open 1 chest in the Tower', QUEST_10020: 'Open 3 chests in Outland', QUEST_10021: 'Collect 75 Titanite in the Guild Dungeon', QUEST_10021: 'Collect 150 Titanite in the Guild Dungeon', QUEST_10023: 'Upgrade Gift of the Elements by 1 level', QUEST_10024: 'Level up any artifact once', QUEST_10025: 'Start Expedition 1', QUEST_10026: 'Start 4 Expeditions', QUEST_10027: 'Win 1 battle of the Tournament of Elements', QUEST_10028: 'Level up any titan artifact', QUEST_10029: 'Unlock the Orb of Titan Artifacts', QUEST_10030: 'Upgrade any Skin of any hero 1 time', QUEST_10031: 'Win 6 battles of the Tournament of Elements', QUEST_10043: 'Start or Join an Adventure', QUEST_10044: 'Use Summon Pets 1 time', QUEST_10046: 'Open 3 chests in Adventure', QUEST_10047: 'Get 150 Guild Activity Points', NOTHING_TO_DO: 'Nothing to do', YOU_CAN_COMPLETE: 'You can complete quests', BTN_DO_IT: 'Do it', NOT_QUEST_COMPLETED: 'Not a single quest completed', COMPLETED_QUESTS: 'Completed quests', /* everything button */ ASSEMBLE_OUTLAND: 'Assemble Outland', PASS_THE_TOWER: 'Pass the tower', CHECK_EXPEDITIONS: 'Check Expeditions', COMPLETE_TOE: 'Complete ToE', COMPLETE_DUNGEON: 'Complete the dungeon', COLLECT_MAIL: 'Collect mail', COLLECT_MISC: 'Collect some bullshit', COLLECT_MISC_TITLE: 'Collect Easter Eggs, Skin Gems, Keys, Arena Coins and Soul Crystal', COLLECT_QUEST_REWARDS: 'Collect quest rewards', MAKE_A_SYNC: 'Make a sync', RUN_FUNCTION: 'Run the following functions?', BTN_GO: 'Go!', PERFORMED: 'Performed', DONE: 'Done', ERRORS_OCCURRES: 'Errors occurred while executing', COPY_ERROR: 'Copy error information to clipboard', BTN_YES: 'Yes', ALL_TASK_COMPLETED: 'All tasks completed', UNKNOWN: 'unknown', ENTER_THE_PATH: 'Enter the path of adventure using commas or dashes', START_ADVENTURE: 'Start your adventure along this path!', INCORRECT_WAY: 'Incorrect path in adventure: {from} -> {to}', BTN_CANCELED: 'Canceled', MUST_TWO_POINTS: 'The path must contain at least 2 points.', MUST_ONLY_NUMBERS: 'The path must contain only numbers and commas', NOT_ON_AN_ADVENTURE: 'You are not on an adventure', YOU_IN_NOT_ON_THE_WAY: 'Your location is not on the way', ATTEMPTS_NOT_ENOUGH: 'Your attempts are not enough to complete the path, continue?', YES_CONTINUE: 'Yes, continue!', NOT_ENOUGH_AP: 'Not enough action points', ATTEMPTS_ARE_OVER: 'The attempts are over', MOVES: 'Moves', BUFF_GET_ERROR: 'Buff getting error', BATTLE_END_ERROR: 'Battle end error', AUTOBOT: 'Autobot', FAILED_TO_WIN_AUTO: 'Failed to win the auto battle', ERROR_OF_THE_BATTLE_COPY: 'An error occurred during the passage of the battle<br>Copy the error to the clipboard?', ERROR_DURING_THE_BATTLE: 'Error during the battle', NO_CHANCE_WIN: 'No chance of winning this fight: 0/', LOST_HEROES: 'You have won, but you have lost one or several heroes', VICTORY_IMPOSSIBLE: 'Is victory impossible, should we focus on the result?', FIND_COEFF: 'Find the coefficient greater than', BTN_PASS: 'PASS', BRAWLS: 'Brawls', BRAWLS_TITLE: 'Activates the ability to auto-brawl', START_AUTO_BRAWLS: 'Start Auto Brawls?', LOSSES: 'Losses', WINS: 'Wins', FIGHTS: 'Fights', STAGE: 'Stage', DONT_HAVE_LIVES: "You don't have lives", LIVES: 'Lives', SECRET_WEALTH_ALREADY: 'Item for Pet Potions already purchased', SECRET_WEALTH_NOT_ENOUGH: 'Not Enough Pet Potion, You Have {available}, Need {need}', SECRET_WEALTH_UPGRADE_NEW_PET: 'After purchasing the Pet Potion, it will not be enough to upgrade a new pet', SECRET_WEALTH_PURCHASED: 'Purchased {count} {name}', SECRET_WEALTH_CANCELED: 'Secret Wealth: Purchase Canceled', SECRET_WEALTH_BUY: 'You have {available} Pet Potion.<br>Do you want to buy {countBuy} {name} for {price} Pet Potion?', DAILY_BONUS: 'Daily bonus', DO_DAILY_QUESTS: 'Do daily quests', ACTIONS: 'Actions', ACTIONS_TITLE: 'Dialog box with various actions', OTHERS: 'Others', OTHERS_TITLE: 'Others', CHOOSE_ACTION: 'Choose an action', OPEN_LOOTBOX: 'You have {lootBox} boxes, should we open them?', STAMINA: 'Energy', BOXES_OVER: 'The boxes are over', NO_BOXES: 'No boxes', NO_MORE_ACTIVITY: 'No more activity for items today', EXCHANGE_ITEMS: 'Exchange items for activity points (max {maxActive})?', GET_ACTIVITY: 'Get Activity', NOT_ENOUGH_ITEMS: 'Not enough items', ACTIVITY_RECEIVED: 'Activity received', NO_PURCHASABLE_HERO_SOULS: 'No purchasable Hero Souls', PURCHASED_HERO_SOULS: 'Purchased {countHeroSouls} Hero Souls', NOT_ENOUGH_EMERALDS_540: 'Not enough emeralds, you need {imgEmerald}540 you have {imgEmerald}{currentStarMoney}', BUY_OUTLAND_BTN: 'Buy {count} chests {imgEmerald}{countEmerald}', CHESTS_NOT_AVAILABLE: 'Chests not available', OUTLAND_CHESTS_RECEIVED: 'Outland chests received', RAID_NOT_AVAILABLE: 'The raid is not available or there are no spheres', RAID_ADVENTURE: 'Raid {adventureId} adventure!', SOMETHING_WENT_WRONG: 'Something went wrong', ADVENTURE_COMPLETED: 'Adventure {adventureId} completed {times} times', CLAN_STAT_COPY: 'Clan statistics copied to clipboard', GET_ENERGY: 'Get Energy', GET_ENERGY_TITLE: 'Opens platinum boxes one at a time until you get 250 energy', ITEM_EXCHANGE: 'Item Exchange', ITEM_EXCHANGE_TITLE: 'Exchanges items for the specified amount of activity', BUY_SOULS: 'Buy souls', BUY_SOULS_TITLE: 'Buy hero souls from all available shops', BUY_OUTLAND: 'Buy Outland', BUY_OUTLAND_TITLE: 'Buy 9 chests in Outland for 540 emeralds', RAID: 'Raid', AUTO_RAID_ADVENTURE: 'Raid', AUTO_RAID_ADVENTURE_TITLE: 'Raid adventure set number of times', CLAN_STAT: 'Clan statistics', CLAN_STAT_TITLE: 'Copies clan statistics to the clipboard', BTN_AUTO_F5: 'Auto (F5)', BOSS_DAMAGE: 'Boss Damage: ', NOTHING_BUY: 'Nothing to buy', LOTS_BOUGHT: '{countBuy} lots bought for gold', BUY_FOR_GOLD: 'Buy for gold', BUY_FOR_GOLD_TITLE: 'Buy items for gold in the Town Shop and in the Pet Soul Stone Shop', REWARDS_AND_MAIL: 'Rewards and Mail', REWARDS_AND_MAIL_TITLE: 'Collects rewards and mail', COLLECT_REWARDS_AND_MAIL: 'Collected {countQuests} rewards and {countMail} letters', TIMER_ALREADY: 'Timer already started {time}', NO_ATTEMPTS_TIMER_START: 'No attempts, timer started {time}', EPIC_BRAWL_RESULT: 'Wins: {wins}/{attempts}, Coins: {coins}, Streak: {progress}/{nextStage} [Close]{end}', ATTEMPT_ENDED: '<br>Attempts ended, timer started {time}', EPIC_BRAWL: 'Cosmic Battle', EPIC_BRAWL_TITLE: 'Spends attempts in the Cosmic Battle', RELOAD_GAME: 'Reload game', TIMER: 'Timer:', SHOW_ERRORS: 'Show errors', SHOW_ERRORS_TITLE: 'Show server request errors', ERROR_MSG: 'Error: {name}<br>{description}', EVENT_AUTO_BOSS: 'Maximum number of battles for calculation:</br>{length} ∗ {countTestBattle} = {maxCalcBattle}</br>If you have a weak computer, it may take a long time for this, click on the cross to cancel.</br>Should I search for the best pack from all or the first suitable one?', BEST_SLOW: 'Best (slower)', FIRST_FAST: 'First (faster)', FREEZE_INTERFACE: 'Calculating... <br>The interface may freeze.', ERROR_F12: 'Error, details in the console (F12)', FAILED_FIND_WIN_PACK: 'Failed to find a winning pack', BEST_PACK: 'Best pack:', BOSS_HAS_BEEN_DEF: 'Boss {bossLvl} has been defeated.', NOT_ENOUGH_ATTEMPTS_BOSS: 'Not enough attempts to defeat boss {bossLvl}, retry?', BOSS_VICTORY_IMPOSSIBLE: 'Based on the recalculation of {battles} battles, victory has not been achieved. Would you like to continue the search for a winning battle in real battles?', BOSS_HAS_BEEN_DEF_TEXT: 'Boss {bossLvl} defeated in<br>{countBattle}/{countMaxBattle} attempts{winTimer}<br>(Please synchronize or restart the game to update the data)', MAP: 'Map: ', PLAYER_POS: 'Player positions:', NY_GIFTS: 'Gifts', NY_GIFTS_TITLE: "Open all New Year's gifts", NY_NO_GIFTS: 'No gifts not received', NY_GIFTS_COLLECTED: '{count} gifts collected', CHANGE_MAP: 'Island map', CHANGE_MAP_TITLE: 'Change island map', SELECT_ISLAND_MAP: 'Select an island map:', MAP_NUM: 'Map {num}', SECRET_WEALTH_SHOP: 'Secret Wealth {name}: ', SHOPS: 'Shops', SHOPS_DEFAULT: 'Default', SHOPS_DEFAULT_TITLE: 'Default stores', SHOPS_LIST: 'Shops {number}', SHOPS_LIST_TITLE: 'List of shops {number}', SHOPS_WARNING: 'Stores<br><span style="color:red">If you buy brawl store coins for emeralds, you must use them immediately, otherwise they will disappear after restarting the game!</span>', MINIONS_WARNING: 'The hero packs for attacking minions are incomplete, should I continue?', FAST_SEASON: 'Fast season', FAST_SEASON_TITLE: 'Skip the map selection screen in a season', SET_NUMBER_LEVELS: 'Specify the number of levels:', POSSIBLE_IMPROVE_LEVELS: 'It is possible to improve only {count} levels.<br>Improving?', NOT_ENOUGH_RESOURECES: 'Not enough resources', IMPROVED_LEVELS: 'Improved levels: {count}', ARTIFACTS_UPGRADE: 'Artifacts Upgrade', ARTIFACTS_UPGRADE_TITLE: 'Upgrades the specified amount of the cheapest hero artifacts', SKINS_UPGRADE: 'Skins Upgrade', SKINS_UPGRADE_TITLE: 'Upgrades the specified amount of the cheapest hero skins', HINT: '<br>Hint: ', PICTURE: '<br>Picture: ', ANSWER: '<br>Answer: ', NO_HEROES_PACK: 'Fight at least one battle to save the attacking team', BRAWL_AUTO_PACK: 'Automatic selection of packs', BRAWL_AUTO_PACK_NOT_CUR_HERO: 'Automatic pack selection is not suitable for the current hero', BRAWL_DAILY_TASK_COMPLETED: 'Daily task completed, continue attacking?', CALC_STAT: 'Calculate statistics', ELEMENT_TOURNAMENT_REWARD: 'Unclaimed bonus for Elemental Tournament', BTN_TRY_FIX_IT: 'Fix it', BTN_TRY_FIX_IT_TITLE: 'Enable auto attack combat correction', DAMAGE_FIXED: 'Damage fixed from {lastDamage} to {maxDamage}!', DAMAGE_NO_FIXED: 'Failed to fix damage: {lastDamage}', LETS_FIX: "Let's fix", COUNT_FIXED: 'For {count} attempts', DEFEAT_TURN_TIMER: 'Defeat! Turn on the timer to complete the mission?', SEASON_REWARD: 'Season Rewards', SEASON_REWARD_TITLE: 'Collects available free rewards from all current seasons', SEASON_REWARD_COLLECTED: 'Collected {count} season rewards', SELL_HERO_SOULS: 'Sell souls', SELL_HERO_SOULS_TITLE: 'Exchanges all absolute star hero souls for gold', GOLD_RECEIVED: 'Gold received: {gold}', OPEN_ALL_EQUIP_BOXES: 'Open all Equipment Fragment Box?', SERVER_NOT_ACCEPT: 'The server did not accept the result', INVASION_BOSS_BUFF: 'For {bossLvl} boss need buff {needBuff} you have {haveBuff}}', HERO_POWER: 'Hero Power', HERO_POWER_TITLE: 'Displays the current and maximum power of heroes', MAX_POWER_REACHED: 'Maximum power reached: {power}', CURRENT_POWER: 'Current power: {power}', POWER_TO_MAX: 'Power left to reach maximum: <span style="color:{color};">{power}</span><br>', BEST_RESULT: 'Best result: {value}%', GUILD_ISLAND_TITLE: 'Fast travel to Guild Island', TITAN_VALLEY_TITLE: 'Fast travel to Titan Valley', }, ru: { /* Чекбоксы */ SKIP_FIGHTS: 'Пропуск боев', SKIP_FIGHTS_TITLE: 'Пропуск боев в запределье и арене титанов, автопропуск в башне и кампании', ENDLESS_CARDS: 'Бесконечные карты', ENDLESS_CARDS_TITLE: 'Отключить трату карт предсказаний', AUTO_EXPEDITION: 'АвтоЭкспедиции', AUTO_EXPEDITION_TITLE: 'Автоотправка экспедиций', CANCEL_FIGHT: 'Отмена боя', CANCEL_FIGHT_TITLE: 'Возможность отмены ручного боя на ВГ, СМ и в Асгарде', GIFTS: 'Подарки', GIFTS_TITLE: 'Собирать подарки автоматически', BATTLE_RECALCULATION: 'Прерасчет боя', BATTLE_RECALCULATION_TITLE: 'Предварительный расчет боя', QUANTITY_CONTROL: 'Контроль кол-ва', QUANTITY_CONTROL_TITLE: 'Возможность указывать количество открываемых "лутбоксов"', REPEAT_CAMPAIGN: 'Повтор в кампании', REPEAT_CAMPAIGN_TITLE: 'Автоповтор боев в кампании', DISABLE_DONAT: 'Отключить донат', DISABLE_DONAT_TITLE: 'Убирает все предложения доната', DAILY_QUESTS: 'Квесты', DAILY_QUESTS_TITLE: 'Выполнять ежедневные квесты', AUTO_QUIZ: 'АвтоВикторина', AUTO_QUIZ_TITLE: 'Автоматическое получение правильных ответов на вопросы викторины', SECRET_WEALTH_CHECKBOX: 'Автоматическая покупка в магазине "Тайное Богатство" при заходе в игру', HIDE_SERVERS: 'Свернуть сервера', HIDE_SERVERS_TITLE: 'Скрывать неиспользуемые сервера', /* Поля ввода */ HOW_MUCH_TITANITE: 'Сколько фармим титанита', COMBAT_SPEED: 'Множитель ускорения боя', NUMBER_OF_TEST: 'Количество тестовых боев', NUMBER_OF_AUTO_BATTLE: 'Количество попыток автобоев', /* Кнопки */ RUN_SCRIPT: 'Запустить скрипт', TO_DO_EVERYTHING: 'Сделать все', TO_DO_EVERYTHING_TITLE: 'Выполнить несколько действий', OUTLAND: 'Запределье', OUTLAND_TITLE: 'Собрать Запределье', TITAN_ARENA: 'Турн.Стихий', TITAN_ARENA_TITLE: 'Автопрохождение Турнира Стихий', DUNGEON: 'Подземелье', DUNGEON_TITLE: 'Автопрохождение подземелья', SEER: 'Провидец', SEER_TITLE: 'Покрутить Провидца', TOWER: 'Башня', TOWER_TITLE: 'Автопрохождение башни', EXPEDITIONS: 'Экспедиции', EXPEDITIONS_TITLE: 'Отправка и сбор экспедиций', SYNC: 'Синхронизация', SYNC_TITLE: 'Частичная синхронизация данных игры без перезагрузки сатраницы', ARCHDEMON: 'Архидемон', FURNACE_OF_SOULS: 'Горнило душ', ARCHDEMON_TITLE: 'Набивает килы и собирает награду', ESTER_EGGS: 'Пасхалки', ESTER_EGGS_TITLE: 'Собрать все пасхалки или награды', REWARDS: 'Награды', REWARDS_TITLE: 'Собрать все награды за задания', MAIL: 'Почта', MAIL_TITLE: 'Собрать всю почту, кроме писем с энергией и зарядами портала', MINIONS: 'Прислужники', MINIONS_TITLE: 'Атакует прислужников сохраннеными пачками', ADVENTURE: 'Прикл', ADVENTURE_TITLE: 'Проходит приключение по указанному маршруту', STORM: 'Буря', STORM_TITLE: 'Проходит бурю по указанному маршруту', SANCTUARY: 'Святилище', SANCTUARY_TITLE: 'Быстрый переход к Святилищу', GUILD_WAR: 'Война гильдий', GUILD_WAR_TITLE: 'Быстрый переход к Войне гильдий', SECRET_WEALTH: 'Тайное богатство', SECRET_WEALTH_TITLE: 'Купить что-то в магазине "Тайное богатство"', /* Разное */ BOTTOM_URLS: '<a href="https://t.me/+q6gAGCRpwyFkNTYy" target="_blank" title="Telegram"><svg width="20" height="20" style="margin:2px" viewBox="0 0 1e3 1e3" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a" x1="50%" x2="50%" y2="99.258%"><stop stop-color="#2AABEE" offset="0"/><stop stop-color="#229ED9" offset="1"/></linearGradient></defs><g fill-rule="evenodd"><circle cx="500" cy="500" r="500" fill="url(#a)"/><path d="m226.33 494.72c145.76-63.505 242.96-105.37 291.59-125.6 138.86-57.755 167.71-67.787 186.51-68.119 4.1362-0.072862 13.384 0.95221 19.375 5.8132 5.0584 4.1045 6.4501 9.6491 7.1161 13.541 0.666 3.8915 1.4953 12.756 0.83608 19.683-7.5246 79.062-40.084 270.92-56.648 359.47-7.0089 37.469-20.81 50.032-34.17 51.262-29.036 2.6719-51.085-19.189-79.207-37.624-44.007-28.847-68.867-46.804-111.58-74.953-49.366-32.531-17.364-50.411 10.769-79.631 7.3626-7.6471 135.3-124.01 137.77-134.57 0.30968-1.3202 0.59708-6.2414-2.3265-8.8399s-7.2385-1.7099-10.352-1.0032c-4.4137 1.0017-74.715 47.468-210.9 139.4-19.955 13.702-38.029 20.379-54.223 20.029-17.853-0.3857-52.194-10.094-77.723-18.393-31.313-10.178-56.199-15.56-54.032-32.846 1.1287-9.0037 13.528-18.212 37.197-27.624z" fill="#fff"/></g></svg></a><a href="https://vk.com/invite/YNPxKGX" target="_blank" title="Вконтакте"><svg width="20" height="20" style="margin:2px" viewBox="0 0 101 100" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path d="M0.5 48C0.5 25.3726 0.5 14.0589 7.52944 7.02944C14.5589 0 25.8726 0 48.5 0H52.5C75.1274 0 86.4411 0 93.4706 7.02944C100.5 14.0589 100.5 25.3726 100.5 48V52C100.5 74.6274 100.5 85.9411 93.4706 92.9706C86.4411 100 75.1274 100 52.5 100H48.5C25.8726 100 14.5589 100 7.52944 92.9706C0.5 85.9411 0.5 74.6274 0.5 52V48Z" fill="#07f"/><path d="m53.708 72.042c-22.792 0-35.792-15.625-36.333-41.625h11.417c0.375 19.083 8.7915 27.167 15.458 28.833v-28.833h10.75v16.458c6.5833-0.7083 13.499-8.2082 15.832-16.458h10.75c-1.7917 10.167-9.2917 17.667-14.625 20.75 5.3333 2.5 13.875 9.0417 17.125 20.875h-11.834c-2.5417-7.9167-8.8745-14.042-17.25-14.875v14.875h-1.2919z" fill="#fff"/></g><defs><clipPath id="a"><rect transform="translate(.5)" width="100" height="100" fill="#fff"/></clipPath></defs></svg></a>', GIFTS_SENT: 'Подарки отправлены!', DO_YOU_WANT: 'Вы действительно хотите это сделать?', BTN_RUN: 'Запускай', BTN_CANCEL: 'Отмена', BTN_ACCEPT: 'Принять', BTN_OK: 'Ок', MSG_HAVE_BEEN_DEFEATED: 'Вы потерпели поражение!', BTN_AUTO: 'Авто', MSG_YOU_APPLIED: 'Вы нанесли', MSG_DAMAGE: 'урона', MSG_CANCEL_AND_STAT: 'Авто (F5) и показать Статистику', MSG_REPEAT_MISSION: 'Повторить миссию?', BTN_REPEAT: 'Повторить', BTN_NO: 'Нет', MSG_SPECIFY_QUANT: 'Указать количество:', BTN_OPEN: 'Открыть', QUESTION_COPY: 'Вопрос скопирован в буфер обмена', ANSWER_KNOWN: 'Ответ известен', ANSWER_NOT_KNOWN: 'ВНИМАНИЕ ОТВЕТ НЕ ИЗВЕСТЕН', BEING_RECALC: 'Идет прерасчет боя', THIS_TIME: 'На этот раз', VICTORY: '<span style="color:green;">ПОБЕДА</span>', DEFEAT: '<span style="color:red;">ПОРАЖЕНИЕ</span>', CHANCE_TO_WIN: 'Шансы на победу <span style="color:red;">на основе прерасчета</span>', OPEN_DOLLS: 'матрешек рекурсивно', SENT_QUESTION: 'Вопрос отправлен', SETTINGS: 'Настройки', MSG_BAN_ATTENTION: '<p style="color:red;">Использование этой функции может привести к бану.</p> Продолжить?', BTN_YES_I_AGREE: 'Да, я беру на себя все риски!', BTN_NO_I_AM_AGAINST: 'Нет, я отказываюсь от этого!', VALUES: 'Значения', EXPEDITIONS_SENT: 'Экспедиции:<br>Собрано: {countGet}<br>Отправлено: {countSend}', EXPEDITIONS_NOTHING: 'Нечего собирать/отправлять', EXPEDITIONS_NOTTIME: 'Не время для экспедиций', TITANIT: 'Титанит', COMPLETED: 'завершено', FLOOR: 'Этаж', LEVEL: 'Уровень', BATTLES: 'бои', EVENT: 'Эвент', NOT_AVAILABLE: 'недоступен', NO_HEROES: 'Нет героев', DAMAGE_AMOUNT: 'Количество урона', NOTHING_TO_COLLECT: 'Нечего собирать', COLLECTED: 'Собрано', REWARD: 'наград', REMAINING_ATTEMPTS: 'Осталось попыток', BATTLES_CANCELED: 'Битв отменено', MINION_RAID: 'Рейд прислужников', STOPPED: 'Остановлено', REPETITIONS: 'Повторений', MISSIONS_PASSED: 'Миссий пройдено', STOP: 'остановить', TOTAL_OPEN: 'Всего открыто', OPEN: 'Открыто', ROUND_STAT: 'Статистика урона за', BATTLE: 'боев', MINIMUM: 'Минимальный', MAXIMUM: 'Максимальный', AVERAGE: 'Средний', NOT_THIS_TIME: 'Не в этот раз', RETRY_LIMIT_EXCEEDED: 'Превышен лимит попыток', SUCCESS: 'Успех', RECEIVED: 'Получено', LETTERS: 'писем', PORTALS: 'порталов', ATTEMPTS: 'попыток', QUEST_10001: 'Улучши умения героев 3 раза', QUEST_10002: 'Пройди 10 миссий', QUEST_10003: 'Пройди 3 героические миссии', QUEST_10004: 'Сразись 3 раза на Арене или Гранд Арене', QUEST_10006: 'Используй обмен изумрудов 1 раз', QUEST_10007: 'Соверши 1 призыв в Атриуме Душ', QUEST_10016: 'Отправь подарки согильдийцам', QUEST_10018: 'Используй зелье опыта', QUEST_10019: 'Открой 1 сундук в Башне', QUEST_10020: 'Открой 3 сундука в Запределье', QUEST_10021: 'Собери 75 Титанита в Подземелье Гильдии', QUEST_10021: 'Собери 150 Титанита в Подземелье Гильдии', QUEST_10023: 'Прокачай Дар Стихий на 1 уровень', QUEST_10024: 'Повысь уровень любого артефакта один раз', QUEST_10025: 'Начни 1 Экспедицию', QUEST_10026: 'Начни 4 Экспедиции', QUEST_10027: 'Победи в 1 бою Турнира Стихий', QUEST_10028: 'Повысь уровень любого артефакта титанов', QUEST_10029: 'Открой сферу артефактов титанов', QUEST_10030: 'Улучши облик любого героя 1 раз', QUEST_10031: 'Победи в 6 боях Турнира Стихий', QUEST_10043: 'Начни или присоеденись к Приключению', QUEST_10044: 'Воспользуйся призывом питомцев 1 раз', QUEST_10046: 'Открой 3 сундука в Приключениях', QUEST_10047: 'Набери 150 очков активности в Гильдии', NOTHING_TO_DO: 'Нечего выполнять', YOU_CAN_COMPLETE: 'Можно выполнить квесты', BTN_DO_IT: 'Выполняй', NOT_QUEST_COMPLETED: 'Ни одного квеста не выполенно', COMPLETED_QUESTS: 'Выполнено квестов', /* everything button */ ASSEMBLE_OUTLAND: 'Собрать Запределье', PASS_THE_TOWER: 'Пройти башню', CHECK_EXPEDITIONS: 'Проверить экспедиции', COMPLETE_TOE: 'Пройти Турнир Стихий', COMPLETE_DUNGEON: 'Пройти подземелье', COLLECT_MAIL: 'Собрать почту', COLLECT_MISC: 'Собрать всякую херню', COLLECT_MISC_TITLE: 'Собрать пасхалки, камни облика, ключи, монеты арены и Хрусталь души', COLLECT_QUEST_REWARDS: 'Собрать награды за квесты', MAKE_A_SYNC: 'Сделать синхронизацию', RUN_FUNCTION: 'Выполнить следующие функции?', BTN_GO: 'Погнали!', PERFORMED: 'Выполняется', DONE: 'Выполнено', ERRORS_OCCURRES: 'Призошли ошибки при выполнении', COPY_ERROR: 'Скопировать в буфер информацию об ошибке', BTN_YES: 'Да', ALL_TASK_COMPLETED: 'Все задачи выполнены', UNKNOWN: 'Неизвестно', ENTER_THE_PATH: 'Введите путь приключения через запятые или дефисы', START_ADVENTURE: 'Начать приключение по этому пути!', INCORRECT_WAY: 'Неверный путь в приключении: {from} -> {to}', BTN_CANCELED: 'Отменено', MUST_TWO_POINTS: 'Путь должен состоять минимум из 2х точек', MUST_ONLY_NUMBERS: 'Путь должен содержать только цифры и запятые', NOT_ON_AN_ADVENTURE: 'Вы не в приключении', YOU_IN_NOT_ON_THE_WAY: 'Указанный путь должен включать точку вашего положения', ATTEMPTS_NOT_ENOUGH: 'Ваших попыток не достаточно для завершения пути, продолжить?', YES_CONTINUE: 'Да, продолжай!', NOT_ENOUGH_AP: 'Попыток не достаточно', ATTEMPTS_ARE_OVER: 'Попытки закончились', MOVES: 'Ходы', BUFF_GET_ERROR: 'Ошибка при получении бафа', BATTLE_END_ERROR: 'Ошибка завершения боя', AUTOBOT: 'АвтоБой', FAILED_TO_WIN_AUTO: 'Не удалось победить в автобою', ERROR_OF_THE_BATTLE_COPY: 'Призошли ошибка в процессе прохождения боя<br>Скопировать ошибку в буфер обмена?', ERROR_DURING_THE_BATTLE: 'Ошибка в процессе прохождения боя', NO_CHANCE_WIN: 'Нет шансов победить в этом бою: 0/', LOST_HEROES: 'Вы победили, но потеряли одного или несколько героев!', VICTORY_IMPOSSIBLE: 'Победа не возможна, бъем на результат?', FIND_COEFF: 'Поиск коэффициента больше чем', BTN_PASS: 'ПРОПУСК', BRAWLS: 'Потасовки', BRAWLS_TITLE: 'Включает возможность автопотасовок', START_AUTO_BRAWLS: 'Запустить Автопотасовки?', LOSSES: 'Поражений', WINS: 'Побед', FIGHTS: 'Боев', STAGE: 'Стадия', DONT_HAVE_LIVES: 'У Вас нет жизней', LIVES: 'Жизни', SECRET_WEALTH_ALREADY: 'товар за Зелья питомцев уже куплен', SECRET_WEALTH_NOT_ENOUGH: 'Не достаточно Зелье Питомца, у Вас {available}, нужно {need}', SECRET_WEALTH_UPGRADE_NEW_PET: 'После покупки Зелье Питомца будет не достаточно для прокачки нового питомца', SECRET_WEALTH_PURCHASED: 'Куплено {count} {name}', SECRET_WEALTH_CANCELED: 'Тайное богатство: покупка отменена', SECRET_WEALTH_BUY: 'У вас {available} Зелье Питомца.<br>Вы хотите купить {countBuy} {name} за {price} Зелье Питомца?', DAILY_BONUS: 'Ежедневная награда', DO_DAILY_QUESTS: 'Сделать ежедневные квесты', ACTIONS: 'Действия', ACTIONS_TITLE: 'Диалоговое окно с различными действиями', OTHERS: 'Разное', OTHERS_TITLE: 'Диалоговое окно с дополнительными различными действиями', CHOOSE_ACTION: 'Выберите действие', OPEN_LOOTBOX: 'У Вас {lootBox} ящиков, откываем?', STAMINA: 'Энергия', BOXES_OVER: 'Ящики закончились', NO_BOXES: 'Нет ящиков', NO_MORE_ACTIVITY: 'Больше активности за предметы сегодня не получить', EXCHANGE_ITEMS: 'Обменять предметы на очки активности (не более {maxActive})?', GET_ACTIVITY: 'Получить активность', NOT_ENOUGH_ITEMS: 'Предметов недостаточно', ACTIVITY_RECEIVED: 'Получено активности', NO_PURCHASABLE_HERO_SOULS: 'Нет доступных для покупки душ героев', PURCHASED_HERO_SOULS: 'Куплено {countHeroSouls} душ героев', NOT_ENOUGH_EMERALDS_540: 'Недостаточно изюма, нужно {imgEmerald}540 у Вас {imgEmerald}{currentStarMoney}', BUY_OUTLAND_BTN: 'Купить {count} сундуков {imgEmerald}{countEmerald}', CHESTS_NOT_AVAILABLE: 'Сундуки не доступны', OUTLAND_CHESTS_RECEIVED: 'Получено сундуков Запределья', RAID_NOT_AVAILABLE: 'Рейд не доступен или сфер нет', RAID_ADVENTURE: 'Рейд {adventureId} приключения!', SOMETHING_WENT_WRONG: 'Что-то пошло не так', ADVENTURE_COMPLETED: 'Приключение {adventureId} пройдено {times} раз', CLAN_STAT_COPY: 'Клановая статистика скопирована в буфер обмена', GET_ENERGY: 'Получить энергию', GET_ENERGY_TITLE: 'Открывает платиновые шкатулки по одной до получения 250 энергии', ITEM_EXCHANGE: 'Обмен предметов', ITEM_EXCHANGE_TITLE: 'Обменивает предметы на указанное количество активности', BUY_SOULS: 'Купить души', BUY_SOULS_TITLE: 'Купить души героев из всех доступных магазинов', BUY_OUTLAND: 'Купить Запределье', BUY_OUTLAND_TITLE: 'Купить 9 сундуков в Запределье за 540 изумрудов', RAID: 'Рейд', AUTO_RAID_ADVENTURE: 'Рейд', AUTO_RAID_ADVENTURE_TITLE: 'Рейд приключения заданное количество раз', CLAN_STAT: 'Клановая статистика', CLAN_STAT_TITLE: 'Копирует клановую статистику в буфер обмена', BTN_AUTO_F5: 'Авто (F5)', BOSS_DAMAGE: 'Урон по боссу: ', NOTHING_BUY: 'Нечего покупать', LOTS_BOUGHT: 'За золото куплено {countBuy} лотов', BUY_FOR_GOLD: 'Скупить за золото', BUY_FOR_GOLD_TITLE: 'Скупить предметы за золото в Городской лавке и в магазине Камней Душ Питомцев', REWARDS_AND_MAIL: 'Награды и почта', REWARDS_AND_MAIL_TITLE: 'Собирает награды и почту', COLLECT_REWARDS_AND_MAIL: 'Собрано {countQuests} наград и {countMail} писем', TIMER_ALREADY: 'Таймер уже запущен {time}', NO_ATTEMPTS_TIMER_START: 'Попыток нет, запущен таймер {time}', EPIC_BRAWL_RESULT: '{i} Победы: {wins}/{attempts}, Монеты: {coins}, Серия: {progress}/{nextStage} [Закрыть]{end}', ATTEMPT_ENDED: '<br>Попытки закончились, запущен таймер {time}', EPIC_BRAWL: 'Вселенская битва', EPIC_BRAWL_TITLE: 'Тратит попытки во Вселенской битве', RELOAD_GAME: 'Перезагрузить игру', TIMER: 'Таймер:', SHOW_ERRORS: 'Отображать ошибки', SHOW_ERRORS_TITLE: 'Отображать ошибки запросов к серверу', ERROR_MSG: 'Ошибка: {name}<br>{description}', EVENT_AUTO_BOSS: 'Максимальное количество боев для расчета:</br>{length} * {countTestBattle} = {maxCalcBattle}</br>Если у Вас слабый компьютер на это может потребоваться много времени, нажмите крестик для отмены.</br>Искать лучший пак из всех или первый подходящий?', BEST_SLOW: 'Лучший (медленее)', FIRST_FAST: 'Первый (быстрее)', FREEZE_INTERFACE: 'Идет расчет... <br> Интерфейс может зависнуть.', ERROR_F12: 'Ошибка, подробности в консоли (F12)', FAILED_FIND_WIN_PACK: 'Победный пак найти не удалось', BEST_PACK: 'Наилучший пак: ', BOSS_HAS_BEEN_DEF: 'Босс {bossLvl} побежден', NOT_ENOUGH_ATTEMPTS_BOSS: 'Для победы босса ${bossLvl} не хватило попыток, повторить?', BOSS_VICTORY_IMPOSSIBLE: 'По результатам прерасчета {battles} боев победу получить не удалось. Вы хотите продолжить поиск победного боя на реальных боях?', BOSS_HAS_BEEN_DEF_TEXT: 'Босс {bossLvl} побежден за<br>{countBattle}/{countMaxBattle} попыток{winTimer}<br>(Сделайте синхронизацию или перезагрузите игру для обновления данных)', MAP: 'Карта: ', PLAYER_POS: 'Позиции игроков:', NY_GIFTS: 'Подарки', NY_GIFTS_TITLE: 'Открыть все новогодние подарки', NY_NO_GIFTS: 'Нет не полученных подарков', NY_GIFTS_COLLECTED: 'Собрано {count} подарков', CHANGE_MAP: 'Карта острова', CHANGE_MAP_TITLE: 'Сменить карту острова', SELECT_ISLAND_MAP: 'Выберите карту острова:', MAP_NUM: 'Карта {num}', SECRET_WEALTH_SHOP: 'Тайное богатство {name}: ', SHOPS: 'Магазины', SHOPS_DEFAULT: 'Стандартные', SHOPS_DEFAULT_TITLE: 'Стандартные магазины', SHOPS_LIST: 'Магазины {number}', SHOPS_LIST_TITLE: 'Список магазинов {number}', SHOPS_WARNING: 'Магазины<br><span style="color:red">Если Вы купите монеты магазинов потасовок за изумруды, то их надо использовать сразу, иначе после перезагрузки игры они пропадут!</span>', MINIONS_WARNING: 'Пачки героев для атаки приспешников неполные, продолжить?', FAST_SEASON: 'Быстрый сезон', FAST_SEASON_TITLE: 'Пропуск экрана с выбором карты в сезоне', SET_NUMBER_LEVELS: 'Указать колличество уровней:', POSSIBLE_IMPROVE_LEVELS: 'Возможно улучшить только {count} уровней.<br>Улучшаем?', NOT_ENOUGH_RESOURECES: 'Не хватает ресурсов', IMPROVED_LEVELS: 'Улучшено уровней: {count}', ARTIFACTS_UPGRADE: 'Улучшение артефактов', ARTIFACTS_UPGRADE_TITLE: 'Улучшает указанное количество самых дешевых артефактов героев', SKINS_UPGRADE: 'Улучшение обликов', SKINS_UPGRADE_TITLE: 'Улучшает указанное количество самых дешевых обликов героев', HINT: '<br>Подсказка: ', PICTURE: '<br>На картинке: ', ANSWER: '<br>Ответ: ', NO_HEROES_PACK: 'Проведите хотя бы один бой для сохранения атакующей команды', BRAWL_AUTO_PACK: 'Автоподбор пачки', BRAWL_AUTO_PACK_NOT_CUR_HERO: 'Автоматический подбор пачки не подходит для текущего героя', BRAWL_DAILY_TASK_COMPLETED: 'Ежедневное задание выполнено, продолжить атаку?', CALC_STAT: 'Посчитать статистику', ELEMENT_TOURNAMENT_REWARD: 'Несобранная награда за Турнир Стихий', BTN_TRY_FIX_IT: 'Исправить', BTN_TRY_FIX_IT_TITLE: 'Включить исправление боев при автоатаке', DAMAGE_FIXED: 'Урон исправлен с {lastDamage} до {maxDamage}!', DAMAGE_NO_FIXED: 'Не удалось исправить урон: {lastDamage}', LETS_FIX: 'Исправляем', COUNT_FIXED: 'За {count} попыток', DEFEAT_TURN_TIMER: 'Поражение! Включить таймер для завершения миссии?', SEASON_REWARD: 'Награды сезонов', SEASON_REWARD_TITLE: 'Собирает доступные бесплатные награды со всех текущих сезонов', SEASON_REWARD_COLLECTED: 'Собрано {count} наград сезонов', SELL_HERO_SOULS: 'Продать души', SELL_HERO_SOULS_TITLE: 'Обменивает все души героев с абсолютной звездой на золото', GOLD_RECEIVED: 'Получено золота: {gold}', OPEN_ALL_EQUIP_BOXES: 'Открыть все ящики фрагментов экипировки?', SERVER_NOT_ACCEPT: 'Сервер не принял результат', INVASION_BOSS_BUFF: 'Для {bossLvl} босса нужен баф {needBuff} у вас {haveBuff}', HERO_POWER: 'Сила героев', HERO_POWER_TITLE: 'Отображает текущую и максимальную силу героев', MAX_POWER_REACHED: 'Максимальная достигнутая мощь: {power}', CURRENT_POWER: 'Текущая мощь: {power}', POWER_TO_MAX: 'До максимума мощи осталось: <span style="color:{color};">{power}</span><br>', BEST_RESULT: 'Лучший результат: {value}%', GUILD_ISLAND_TITLE: 'Перейти к Острову гильдии', TITAN_VALLEY_TITLE: 'Перейти к Долине титанов', }, }; function getLang() { let lang = ''; if (typeof NXFlashVars !== 'undefined') { lang = NXFlashVars.interface_lang } if (!lang) { lang = (navigator.language || navigator.userLanguage).substr(0, 2); } if (lang == 'ru') { return lang; } return 'en'; } this.I18N = function (constant, replace) { const selectLang = getLang(); if (constant && constant in i18nLangData[selectLang]) { const result = i18nLangData[selectLang][constant]; if (replace) { return result.sprintf(replace); } return result; } return `% ${constant} %`; }; String.prototype.sprintf = String.prototype.sprintf || function () { "use strict"; var str = this.toString(); if (arguments.length) { var t = typeof arguments[0]; var key; var args = ("string" === t || "number" === t) ? Array.prototype.slice.call(arguments) : arguments[0]; for (key in args) { str = str.replace(new RegExp("\\{" + key + "\\}", "gi"), args[key]); } } return str; }; /** * Checkboxes * * Чекбоксы */ const checkboxes = { passBattle: { label: I18N('SKIP_FIGHTS'), cbox: null, title: I18N('SKIP_FIGHTS_TITLE'), default: false, }, sendExpedition: { label: I18N('AUTO_EXPEDITION'), cbox: null, title: I18N('AUTO_EXPEDITION_TITLE'), default: false, }, cancelBattle: { label: I18N('CANCEL_FIGHT'), cbox: null, title: I18N('CANCEL_FIGHT_TITLE'), default: false, }, preCalcBattle: { label: I18N('BATTLE_RECALCULATION'), cbox: null, title: I18N('BATTLE_RECALCULATION_TITLE'), default: false, }, countControl: { label: I18N('QUANTITY_CONTROL'), cbox: null, title: I18N('QUANTITY_CONTROL_TITLE'), default: true, }, repeatMission: { label: I18N('REPEAT_CAMPAIGN'), cbox: null, title: I18N('REPEAT_CAMPAIGN_TITLE'), default: false, }, noOfferDonat: { label: I18N('DISABLE_DONAT'), cbox: null, title: I18N('DISABLE_DONAT_TITLE'), /** * A crutch to get the field before getting the character id * * Костыль чтоб получать поле до получения id персонажа */ default: (() => { $result = false; try { $result = JSON.parse(localStorage[GM_info.script.name + ':noOfferDonat']); } catch (e) { $result = false; } return $result || false; })(), }, dailyQuests: { label: I18N('DAILY_QUESTS'), cbox: null, title: I18N('DAILY_QUESTS_TITLE'), default: false, }, // Потасовки autoBrawls: { label: I18N('BRAWLS'), cbox: null, title: I18N('BRAWLS_TITLE'), default: (() => { $result = false; try { $result = JSON.parse(localStorage[GM_info.script.name + ':autoBrawls']); } catch (e) { $result = false; } return $result || false; })(), hide: false, }, getAnswer: { label: I18N('AUTO_QUIZ'), cbox: null, title: I18N('AUTO_QUIZ_TITLE'), default: false, hide: true, }, tryFixIt_v2: { label: I18N('BTN_TRY_FIX_IT'), cbox: null, title: I18N('BTN_TRY_FIX_IT_TITLE'), default: false, hide: false, }, showErrors: { label: I18N('SHOW_ERRORS'), cbox: null, title: I18N('SHOW_ERRORS_TITLE'), default: true, }, buyForGold: { label: I18N('BUY_FOR_GOLD'), cbox: null, title: I18N('BUY_FOR_GOLD_TITLE'), default: false, }, hideServers: { label: I18N('HIDE_SERVERS'), cbox: null, title: I18N('HIDE_SERVERS_TITLE'), default: false, }, fastSeason: { label: I18N('FAST_SEASON'), cbox: null, title: I18N('FAST_SEASON_TITLE'), default: false, }, }; /** * Get checkbox state * * Получить состояние чекбокса */ function isChecked(checkBox) { if (!(checkBox in checkboxes)) { return false; } return checkboxes[checkBox].cbox?.checked; } /** * Input fields * * Поля ввода */ const inputs = { countTitanit: { input: null, title: I18N('HOW_MUCH_TITANITE'), default: 150, }, speedBattle: { input: null, title: I18N('COMBAT_SPEED'), default: 5, }, countTestBattle: { input: null, title: I18N('NUMBER_OF_TEST'), default: 10, }, countAutoBattle: { input: null, title: I18N('NUMBER_OF_AUTO_BATTLE'), default: 10, }, FPS: { input: null, title: 'FPS', default: 60, } } /** * Checks the checkbox * * Поплучить данные поля ввода */ function getInput(inputName) { return inputs[inputName]?.input?.value; } /** * Control FPS * * Контроль FPS */ let nextAnimationFrame = Date.now(); const oldRequestAnimationFrame = this.requestAnimationFrame; this.requestAnimationFrame = async function (e) { const FPS = Number(getInput('FPS')) || -1; const now = Date.now(); const delay = nextAnimationFrame - now; nextAnimationFrame = Math.max(now, nextAnimationFrame) + Math.min(1e3 / FPS, 1e3); if (delay > 0) { await new Promise((e) => setTimeout(e, delay)); } oldRequestAnimationFrame(e); }; /** * Button List * * Список кнопочек */ const buttons = { getOutland: { name: I18N('TO_DO_EVERYTHING'), title: I18N('TO_DO_EVERYTHING_TITLE'), onClick: testDoYourBest, }, doActions: { name: I18N('ACTIONS'), title: I18N('ACTIONS_TITLE'), onClick: async function () { const popupButtons = [ { msg: I18N('OUTLAND'), result: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('OUTLAND')}?`, getOutland); }, title: I18N('OUTLAND_TITLE'), }, { msg: I18N('TOWER'), result: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('TOWER')}?`, testTower); }, title: I18N('TOWER_TITLE'), }, { msg: I18N('EXPEDITIONS'), result: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('EXPEDITIONS')}?`, checkExpedition); }, title: I18N('EXPEDITIONS_TITLE'), }, { msg: I18N('MINIONS'), result: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('MINIONS')}?`, testRaidNodes); }, title: I18N('MINIONS_TITLE'), }, { msg: I18N('ESTER_EGGS'), result: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('ESTER_EGGS')}?`, offerFarmAllReward); }, title: I18N('ESTER_EGGS_TITLE'), }, { msg: I18N('STORM'), result: function () { testAdventure('solo'); }, title: I18N('STORM_TITLE'), }, { msg: I18N('REWARDS'), result: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('REWARDS')}?`, questAllFarm); }, title: I18N('REWARDS_TITLE'), }, { msg: I18N('MAIL'), result: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('MAIL')}?`, mailGetAll); }, title: I18N('MAIL_TITLE'), }, { msg: I18N('SEER'), result: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('SEER')}?`, rollAscension); }, title: I18N('SEER_TITLE'), }, /* { msg: I18N('NY_GIFTS'), result: getGiftNewYear, title: I18N('NY_GIFTS_TITLE'), }, */ ]; popupButtons.push({ result: false, isClose: true }); const answer = await popup.confirm(`${I18N('CHOOSE_ACTION')}:`, popupButtons); if (typeof answer === 'function') { answer(); } }, }, doOthers: { name: I18N('OTHERS'), title: I18N('OTHERS_TITLE'), onClick: async function () { const popupButtons = [ { msg: I18N('GET_ENERGY'), result: farmStamina, title: I18N('GET_ENERGY_TITLE'), }, { msg: I18N('ITEM_EXCHANGE'), result: fillActive, title: I18N('ITEM_EXCHANGE_TITLE'), }, { msg: I18N('BUY_SOULS'), result: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('BUY_SOULS')}?`, buyHeroFragments); }, title: I18N('BUY_SOULS_TITLE'), }, { msg: I18N('BUY_FOR_GOLD'), result: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('BUY_FOR_GOLD')}?`, buyInStoreForGold); }, title: I18N('BUY_FOR_GOLD_TITLE'), }, { msg: I18N('BUY_OUTLAND'), result: bossOpenChestPay, title: I18N('BUY_OUTLAND_TITLE'), }, { msg: I18N('CLAN_STAT'), result: clanStatistic, title: I18N('CLAN_STAT_TITLE'), }, { msg: I18N('EPIC_BRAWL'), result: async function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('EPIC_BRAWL')}?`, () => { const brawl = new epicBrawl(); brawl.start(); }); }, title: I18N('EPIC_BRAWL_TITLE'), }, { msg: I18N('ARTIFACTS_UPGRADE'), result: updateArtifacts, title: I18N('ARTIFACTS_UPGRADE_TITLE'), }, { msg: I18N('SKINS_UPGRADE'), result: updateSkins, title: I18N('SKINS_UPGRADE_TITLE'), }, { msg: I18N('SEASON_REWARD'), result: farmBattlePass, title: I18N('SEASON_REWARD_TITLE'), }, { msg: I18N('SELL_HERO_SOULS'), result: sellHeroSoulsForGold, title: I18N('SELL_HERO_SOULS_TITLE'), }, { msg: I18N('CHANGE_MAP'), result: async function () { const maps = Object.values(lib.data.seasonAdventure.list) .filter((e) => e.map.cells.length > 2) .map((i) => ({ msg: I18N('MAP_NUM', { num: i.id }), result: i.id, })); const result = await popup.confirm(I18N('SELECT_ISLAND_MAP'), [...maps, { result: false, isClose: true }]); if (result) { cheats.changeIslandMap(result); } }, title: I18N('CHANGE_MAP_TITLE'), }, { msg: I18N('HERO_POWER'), result: async () => { const calls = ['userGetInfo', 'heroGetAll'].map((name) => ({ name, args: {}, ident: name, })); const [maxHeroSumPower, heroSumPower] = await Send({ calls }).then((e) => [ e.results[0].result.response.maxSumPower.heroes, Object.values(e.results[1].result.response).reduce((a, e) => a + e.power, 0), ]); const power = maxHeroSumPower - heroSumPower; let msg = I18N('MAX_POWER_REACHED', { power: maxHeroSumPower.toLocaleString() }) + '<br>' + I18N('CURRENT_POWER', { power: heroSumPower.toLocaleString() }) + '<br>' + I18N('POWER_TO_MAX', { power: power.toLocaleString(), color: power >= 4000 ? 'green' : 'red' }); await popup.confirm(msg, [{ msg: I18N('BTN_OK'), result: 0 }]); }, title: I18N('HERO_POWER_TITLE'), }, ]; popupButtons.push({ result: false, isClose: true }); const answer = await popup.confirm(`${I18N('CHOOSE_ACTION')}:`, popupButtons); if (typeof answer === 'function') { answer(); } }, }, testTitanArena: { isCombine: true, combineList: [ { name: I18N('TITAN_ARENA'), title: I18N('TITAN_ARENA_TITLE'), onClick: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('TITAN_ARENA')}?`, testTitanArena); }, }, { name: '>>', onClick: cheats.goTitanValley, title: I18N('TITAN_VALLEY_TITLE'), color: 'green', }, ], }, testDungeon: { isCombine: true, combineList: [ { name: I18N('DUNGEON'), onClick: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('DUNGEON')}?`, testDungeon); }, title: I18N('DUNGEON_TITLE'), }, { name: '>>', onClick: cheats.goClanIsland, title: I18N('GUILD_ISLAND_TITLE'), color: 'green', }, ], }, testAdventure: { isCombine: true, combineList: [ { name: I18N('ADVENTURE'), onClick: () => { testAdventure(); }, title: I18N('ADVENTURE_TITLE'), }, { name: I18N('AUTO_RAID_ADVENTURE'), onClick: autoRaidAdventure, title: I18N('AUTO_RAID_ADVENTURE_TITLE'), }, { name: '>>', onClick: cheats.goSanctuary, title: I18N('SANCTUARY_TITLE'), color: 'green', }, ], }, // Архидемон bossRatingEvent: { name: I18N('ARCHDEMON'), title: I18N('ARCHDEMON_TITLE'), onClick: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('ARCHDEMON')}?`, bossRatingEvent); }, hide: true, }, // Горнило душ bossRatingEvent: { name: I18N('FURNACE_OF_SOULS'), title: I18N('ARCHDEMON_TITLE'), onClick: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('FURNACE_OF_SOULS')}?`, bossRatingEventSouls); }, hide: true, }, rewardsAndMailFarm: { name: I18N('REWARDS_AND_MAIL'), title: I18N('REWARDS_AND_MAIL_TITLE'), onClick: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('REWARDS_AND_MAIL')}?`, rewardsAndMailFarm); }, }, goToClanWar: { name: I18N('GUILD_WAR'), title: I18N('GUILD_WAR_TITLE'), onClick: cheats.goClanWar, dot: true, }, dailyQuests: { name: I18N('DAILY_QUESTS'), title: I18N('DAILY_QUESTS_TITLE'), onClick: async function () { const quests = new dailyQuests( () => {}, () => {} ); await quests.autoInit(); quests.start(); }, }, newDay: { name: I18N('SYNC'), title: I18N('SYNC_TITLE'), onClick: function () { confShow(`${I18N('RUN_SCRIPT')} ${I18N('SYNC')}?`, cheats.refreshGame); }, }, }; /** * Display buttons * * Вывести кнопочки */ function addControlButtons() { for (let name in buttons) { button = buttons[name]; if (button.hide) { continue; } if (button.isCombine) { button['button'] = scriptMenu.addCombinedButton(button.combineList); continue; } button['button'] = scriptMenu.addButton(button); } } /** * Adds links * * Добавляет ссылки */ function addBottomUrls() { scriptMenu.addHeader(I18N('BOTTOM_URLS')); } /** * Stop repetition of the mission * * Остановить повтор миссии */ let isStopSendMission = false; /** * There is a repetition of the mission * * Идет повтор миссии */ let isSendsMission = false; /** * Data on the past mission * * Данные о прошедшей мисии */ let lastMissionStart = {} /** * Start time of the last battle in the company * * Время начала последнего боя в кампании */ let lastMissionBattleStart = 0; /** * Data for calculating the last battle with the boss * * Данные для расчете последнего боя с боссом */ let lastBossBattle = null; /** * Information about the last battle * * Данные о прошедшей битве */ let lastBattleArg = {} let lastBossBattleStart = null; this.addBattleTimer = 4; this.invasionTimer = 2500; const invasionInfo = { id: 225, buff: 0, bossLvl: 130, }; const invasionDataPacks = { 130: { buff: 0, pet: 6005, heroes: [9, 62, 10, 1, 66], favor: { 9: 6006 } }, 140: { buff: 0, pet: 6005, heroes: [9, 62, 10, 1, 66], favor: {} }, 150: { buff: 0, pet: 6005, heroes: [9, 62, 10, 1, 66], favor: {} }, 160: { buff: 0, pet: 6005, heroes: [64, 66, 13, 9, 4], favor: { 4: 6006, 9: 6004, 13: 6003, 64: 6005, 66: 6002 } }, 170: { buff: 0, pet: 6005, heroes: [9, 62, 10, 1, 66], favor: { 1: 6006, 9: 6005, 10: 6008, 62: 6003, 66: 6002 } }, 180: { buff: 0, pet: 6006, heroes: [62, 10, 2, 4, 66], favor: { 2: 6005, 4: 6001, 10: 6006, 62: 6003 } }, 190: { buff: 40, pet: 6005, heroes: [9, 2, 43, 45, 66], favor: { 9: 6005, 45: 6002, 66: 6006 } }, 200: { buff: 20, pet: 6005, heroes: [9, 62, 1, 48, 66], favor: { 9: 6007, 62: 6003 } }, 210: { buff: 10, pet: 6008, heroes: [9, 10, 4, 32, 66], favor: { 9: 6005, 10: 6003, 32: 6007, 66: 6006 } }, 220: { buff: 20, pet: 6004, heroes: [9, 1, 48, 43, 66], favor: { 9: 6005, 43: 6006, 48: 6000, 66: 6002 } }, 230: { buff: 45, pet: 6001, heroes: [9, 7, 40, 43, 66], favor: { 7: 6006, 9: 6005, 40: 6004, 43: 6006, 66: 6006 } }, 240: { buff: 50, pet: 6009, heroes: [9, 40, 43, 51, 66], favor: { 9: 6005, 40: 6004, 43: 6002, 66: 6007 } }, 250: { buff: 70, pet: 6005, heroes: [9, 10, 13, 43, 66], favor: { 9: 6005, 10: 6002, 13: 6002, 43: 6006, 66: 6006 } }, 260: { buff: 80, pet: 6008, heroes: [9, 40, 43, 4, 66], favor: { 4: 6001, 9: 6006, 43: 6006 } }, 270: { buff: 115, pet: 6001, heroes: [9, 13, 43, 51, 66], favor: { 9: 6006, 43: 6006, 51: 6001 } }, 280: { buff: 80, pet: 6008, heroes: [9, 13, 43, 56, 66], favor: { 9: 6004, 13: 6006, 43: 6006, 66: 6006 } }, 290: { buff: 60, pet: 6005, heroes: [9, 10, 43, 56, 66], favor: { 9: 6005, 10: 6002, 43: 6006 } }, 300: { buff: 75, pet: 6006, heroes: [9, 62, 1, 45, 66], favor: { 1: 6006, 9: 6005, 45: 6002, 66: 6007 } }, }; /** * The name of the function of the beginning of the battle * * Имя функции начала боя */ let nameFuncStartBattle = ''; /** * The name of the function of the end of the battle * * Имя функции конца боя */ let nameFuncEndBattle = ''; /** * Data for calculating the last battle * * Данные для расчета последнего боя */ let lastBattleInfo = null; /** * The ability to cancel the battle * * Возможность отменить бой */ let isCancalBattle = true; function setIsCancalBattle(value) { isCancalBattle = value; } /** * Certificator of the last open nesting doll * * Идетификатор последней открытой матрешки */ let lastRussianDollId = null; /** * Cancel the training guide * * Отменить обучающее руководство */ this.isCanceledTutorial = false; /** * Data from the last question of the quiz * * Данные последнего вопроса викторины */ let lastQuestion = null; /** * Answer to the last question of the quiz * * Ответ на последний вопрос викторины */ let lastAnswer = null; /** * Flag for opening keys or titan artifact spheres * * Флаг открытия ключей или сфер артефактов титанов */ let artifactChestOpen = false; /** * The name of the function to open keys or orbs of titan artifacts * * Имя функции открытия ключей или сфер артефактов титанов */ let artifactChestOpenCallName = ''; let correctShowOpenArtifact = 0; /** * Data for the last battle in the dungeon * (Fix endless cards) * * Данные для последнего боя в подземке * (Исправление бесконечных карт) */ let lastDungeonBattleData = null; /** * Start time of the last battle in the dungeon * * Время начала последнего боя в подземелье */ let lastDungeonBattleStart = 0; /** * Subscription end time * * Время окончания подписки */ let subEndTime = 0; /** * Number of prediction cards * * Количество карт предсказаний */ let countPredictionCard = 0; /** * Brawl pack * * Пачка для потасовок */ let brawlsPack = null; /** * Autobrawl started * * Автопотасовка запущена */ let isBrawlsAutoStart = false; let clanDominationGetInfo = null; /** * Copies the text to the clipboard * * Копирует тест в буфер обмена * @param {*} text copied text // копируемый текст */ function copyText(text) { let copyTextarea = document.createElement("textarea"); copyTextarea.style.opacity = "0"; copyTextarea.textContent = text; document.body.appendChild(copyTextarea); copyTextarea.select(); document.execCommand("copy"); document.body.removeChild(copyTextarea); delete copyTextarea; } /** * Returns the history of requests * * Возвращает историю запросов */ this.getRequestHistory = function() { return requestHistory; } /** * Generates a random integer from min to max * * Гененирует случайное целое число от min до max */ const random = function (min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } const randf = function (min, max) { return Math.random() * (max - min + 1) + min; }; /** * Clearing the request history * * Очистка истоии запросов */ setInterval(function () { let now = Date.now(); for (let i in requestHistory) { const time = +i.split('_')[0]; if (now - time > 300000) { delete requestHistory[i]; } } }, 300000); /** * Displays the dialog box * * Отображает диалоговое окно */ function confShow(message, yesCallback, noCallback) { let buts = []; message = message || I18N('DO_YOU_WANT'); noCallback = noCallback || (() => {}); if (yesCallback) { buts = [ { msg: I18N('BTN_RUN'), result: true}, { msg: I18N('BTN_CANCEL'), result: false, isCancel: true}, ] } else { yesCallback = () => {}; buts = [ { msg: I18N('BTN_OK'), result: true}, ]; } popup.confirm(message, buts).then((e) => { // dialogPromice = null; if (e) { yesCallback(); } else { noCallback(); } }); } /** * Override/proxy the method for creating a WS package send * * Переопределяем/проксируем метод создания отправки WS пакета */ WebSocket.prototype.send = function (data) { if (!this.isSetOnMessage) { const oldOnmessage = this.onmessage; this.onmessage = function (event) { try { const data = JSON.parse(event.data); if (!this.isWebSocketLogin && data.result.type == "iframeEvent.login") { this.isWebSocketLogin = true; } else if (data.result.type == "iframeEvent.login") { return; } } catch (e) { } return oldOnmessage.apply(this, arguments); } this.isSetOnMessage = true; } original.SendWebSocket.call(this, data); } /** * Overriding/Proxying the Ajax Request Creation Method * * Переопределяем/проксируем метод создания Ajax запроса */ XMLHttpRequest.prototype.open = function (method, url, async, user, password) { this.uniqid = Date.now() + '_' + random(1000000, 10000000); this.errorRequest = false; if (method == 'POST' && url.includes('.nextersglobal.com/api/') && /api\/$/.test(url)) { if (!apiUrl) { apiUrl = url; const socialInfo = /heroes-(.+?)\./.exec(apiUrl); console.log(socialInfo); } requestHistory[this.uniqid] = { method, url, error: [], headers: {}, request: null, response: null, signature: [], calls: {}, }; } else if (method == 'POST' && url.includes('error.nextersglobal.com/client/')) { this.errorRequest = true; } return original.open.call(this, method, url, async, user, password); }; /** * Overriding/Proxying the header setting method for the AJAX request * * Переопределяем/проксируем метод установки заголовков для AJAX запроса */ XMLHttpRequest.prototype.setRequestHeader = function (name, value, check) { if (this.uniqid in requestHistory) { requestHistory[this.uniqid].headers[name] = value; } else { check = true; } if (name == 'X-Auth-Signature') { requestHistory[this.uniqid].signature.push(value); if (!check) { return; } } return original.setRequestHeader.call(this, name, value); }; /** * Overriding/Proxying the AJAX Request Sending Method * * Переопределяем/проксируем метод отправки AJAX запроса */ XMLHttpRequest.prototype.send = async function (sourceData) { if (this.uniqid in requestHistory) { let tempData = null; if (getClass(sourceData) == "ArrayBuffer") { tempData = decoder.decode(sourceData); } else { tempData = sourceData; } requestHistory[this.uniqid].request = tempData; let headers = requestHistory[this.uniqid].headers; lastHeaders = Object.assign({}, headers); /** * Game loading event * * Событие загрузки игры */ if (headers["X-Request-Id"] > 2 && !isLoadGame) { isLoadGame = true; if (cheats.libGame) { lib.setData(cheats.libGame); } else { lib.setData(await cheats.LibLoad()); } addControls(); addControlButtons(); addBottomUrls(); if (isChecked('sendExpedition')) { const isTimeBetweenDays = isTimeBetweenNewDays(); if (!isTimeBetweenDays) { checkExpedition(); } else { setProgress(I18N('EXPEDITIONS_NOTTIME'), true); } } getAutoGifts(); cheats.activateHacks(); justInfo(); if (isChecked('dailyQuests')) { testDailyQuests(); } if (isChecked('buyForGold')) { buyInStoreForGold(); } } /** * Outgoing request data processing * * Обработка данных исходящего запроса */ sourceData = await checkChangeSend.call(this, sourceData, tempData); /** * Handling incoming request data * * Обработка данных входящего запроса */ const oldReady = this.onreadystatechange; this.onreadystatechange = async function (e) { if (this.errorRequest) { return oldReady.apply(this, arguments); } if(this.readyState == 4 && this.status == 200) { isTextResponse = this.responseType === "text" || this.responseType === ""; let response = isTextResponse ? this.responseText : this.response; requestHistory[this.uniqid].response = response; /** * Replacing incoming request data * * Заменна данных входящего запроса */ if (isTextResponse) { await checkChangeResponse.call(this, response); } /** * A function to run after the request is executed * * Функция запускаемая после выполения запроса */ if (typeof this.onReadySuccess == 'function') { setTimeout(this.onReadySuccess, 500); } /** Удаляем из истории запросов битвы с боссом */ if ('invasion_bossStart' in requestHistory[this.uniqid].calls) delete requestHistory[this.uniqid]; } if (oldReady) { try { return oldReady.apply(this, arguments); } catch(e) { console.log(oldReady); console.error('Error in oldReady:', e); } } } } if (this.errorRequest) { const oldReady = this.onreadystatechange; this.onreadystatechange = function () { Object.defineProperty(this, 'status', { writable: true }); this.status = 200; Object.defineProperty(this, 'readyState', { writable: true }); this.readyState = 4; Object.defineProperty(this, 'responseText', { writable: true }); this.responseText = JSON.stringify({ "result": true }); if (typeof this.onReadySuccess == 'function') { setTimeout(this.onReadySuccess, 200); } return oldReady.apply(this, arguments); } this.onreadystatechange(); } else { try { return original.send.call(this, sourceData); } catch(e) { debugger; } } }; /** * Processing and substitution of outgoing data * * Обработка и подмена исходящих данных */ async function checkChangeSend(sourceData, tempData) { try { /** * A function that replaces battle data with incorrect ones to cancel combatя * * Функция заменяющая данные боя на неверные для отмены боя */ const fixBattle = function (heroes) { for (const ids in heroes) { hero = heroes[ids]; hero.energy = random(1, 999); if (hero.hp > 0) { hero.hp = random(1, hero.hp); } } } /** * Dialog window 2 * * Диалоговое окно 2 */ const showMsg = async function (msg, ansF, ansS) { if (typeof popup == 'object') { return await popup.confirm(msg, [ {msg: ansF, result: false}, {msg: ansS, result: true}, ]); } else { return !confirm(`${msg}\n ${ansF} (${I18N('BTN_OK')})\n ${ansS} (${I18N('BTN_CANCEL')})`); } } /** * Dialog window 3 * * Диалоговое окно 3 */ const showMsgs = async function (msg, ansF, ansS, ansT) { return await popup.confirm(msg, [ {msg: ansF, result: 0}, {msg: ansS, result: 1}, {msg: ansT, result: 2}, ]); } let changeRequest = false; testData = JSON.parse(tempData); for (const call of testData.calls) { if (!artifactChestOpen) { requestHistory[this.uniqid].calls[call.name] = call.ident; } /** * Cancellation of the battle in adventures, on VG and with minions of Asgard * Отмена боя в приключениях, на ВГ и с прислужниками Асгарда */ if ((call.name == 'adventure_endBattle' || call.name == 'adventureSolo_endBattle' || call.name == 'clanWarEndBattle' && isChecked('cancelBattle') || call.name == 'crossClanWar_endBattle' && isChecked('cancelBattle') || call.name == 'brawl_endBattle' || call.name == 'towerEndBattle' || call.name == 'invasion_bossEnd' || call.name == 'titanArenaEndBattle' || call.name == 'bossEndBattle' || call.name == 'clanRaid_endNodeBattle') && isCancalBattle) { nameFuncEndBattle = call.name; if (isChecked('tryFixIt_v2') && !call.args.result.win && (call.name == 'brawl_endBattle' || //call.name == 'crossClanWar_endBattle' || call.name == 'epicBrawl_endBattle' || //call.name == 'clanWarEndBattle' || call.name == 'adventure_endBattle' || call.name == 'titanArenaEndBattle' || call.name == 'bossEndBattle' || call.name == 'adventureSolo_endBattle') && lastBattleInfo) { const noFixWin = call.name == 'clanWarEndBattle' || call.name == 'crossClanWar_endBattle'; const cloneBattle = structuredClone(lastBattleInfo); lastBattleInfo = null; try { const { BestOrWinFixBattle } = HWHClasses; const bFix = new BestOrWinFixBattle(cloneBattle); bFix.setNoMakeWin(noFixWin); let endTime = Date.now() + 3e4; if (endTime < cloneBattle.endTime) { endTime = cloneBattle.endTime; } const result = await bFix.start(cloneBattle.endTime, 150); if (result.result.win) { call.args.result = result.result; call.args.progress = result.progress; changeRequest = true; } else if (result.value) { if ( await popup.confirm(I18N('DEFEAT') + '<br>' + I18N('BEST_RESULT', { value: result.value }), [ { msg: I18N('BTN_CANCEL'), result: 0 }, { msg: I18N('BTN_ACCEPT'), result: 1 }, ]) ) { call.args.result = result.result; call.args.progress = result.progress; changeRequest = true; } } } catch (error) { console.error(error); } } if (!call.args.result.win) { let resultPopup = false; if (call.name == 'adventure_endBattle' || //call.name == 'invasion_bossEnd' || call.name == 'bossEndBattle' || call.name == 'adventureSolo_endBattle') { resultPopup = await showMsgs(I18N('MSG_HAVE_BEEN_DEFEATED'), I18N('BTN_OK'), I18N('BTN_CANCEL'), I18N('BTN_AUTO')); } else if (call.name == 'clanWarEndBattle' || call.name == 'crossClanWar_endBattle') { resultPopup = await showMsg(I18N('MSG_HAVE_BEEN_DEFEATED'), I18N('BTN_OK'), I18N('BTN_AUTO_F5')); } else if (call.name !== 'epicBrawl_endBattle' && call.name !== 'titanArenaEndBattle') { resultPopup = await showMsg(I18N('MSG_HAVE_BEEN_DEFEATED'), I18N('BTN_OK'), I18N('BTN_CANCEL')); } if (resultPopup) { if (call.name == 'invasion_bossEnd') { this.errorRequest = true; } fixBattle(call.args.progress[0].attackers.heroes); fixBattle(call.args.progress[0].defenders.heroes); changeRequest = true; if (resultPopup > 1) { this.onReadySuccess = testAutoBattle; // setTimeout(bossBattle, 1000); } } } else if (call.args.result.stars < 3 && call.name == 'towerEndBattle') { resultPopup = await showMsg(I18N('LOST_HEROES'), I18N('BTN_OK'), I18N('BTN_CANCEL'), I18N('BTN_AUTO')); if (resultPopup) { fixBattle(call.args.progress[0].attackers.heroes); fixBattle(call.args.progress[0].defenders.heroes); changeRequest = true; if (resultPopup > 1) { this.onReadySuccess = testAutoBattle; } } } // Потасовки if (isChecked('autoBrawls') && !isBrawlsAutoStart && call.name == 'brawl_endBattle') {} } /** * Save pack for Brawls * * Сохраняем пачку для потасовок */ if (isChecked('autoBrawls') && !isBrawlsAutoStart && call.name == 'brawl_startBattle') { console.log(JSON.stringify(call.args)); brawlsPack = call.args; if ( await popup.confirm( I18N('START_AUTO_BRAWLS'), [ { msg: I18N('BTN_NO'), result: false }, { msg: I18N('BTN_YES'), result: true }, ], [ { name: 'isAuto', label: I18N('BRAWL_AUTO_PACK'), checked: false, }, ] ) ) { isBrawlsAutoStart = true; const isAuto = popup.getCheckBoxes().find((e) => e.name === 'isAuto'); this.errorRequest = true; testBrawls(isAuto.checked); } } /** * Canceled fight in Asgard * Отмена боя в Асгарде */ if (call.name == 'clanRaid_endBossBattle' && isChecked('cancelBattle')) { const bossDamage = call.args.progress[0].defenders.heroes[1].extra; let maxDamage = bossDamage.damageTaken + bossDamage.damageTakenNextLevel; const lastDamage = maxDamage; const testFunc = []; if (testFuntions.masterFix) { testFunc.push({ msg: 'masterFix', isInput: true, default: 100 }); } const resultPopup = await popup.confirm( `${I18N('MSG_YOU_APPLIED')} ${lastDamage.toLocaleString()} ${I18N('MSG_DAMAGE')}.`, [ { msg: I18N('BTN_OK'), result: false }, { msg: I18N('BTN_AUTO_F5'), result: 1 }, { msg: I18N('BTN_TRY_FIX_IT'), result: 2 }, ...testFunc, ], [ { name: 'isStat', label: I18N('CALC_STAT'), checked: false, }, ] ); if (resultPopup) { if (resultPopup == 2) { setProgress(I18N('LETS_FIX'), false); await new Promise((e) => setTimeout(e, 0)); const cloneBattle = structuredClone(lastBossBattle); const endTime = cloneBattle.endTime - 1e4; console.log('fixBossBattleStart'); const { BossFixBattle } = HWHClasses; const bFix = new BossFixBattle(cloneBattle); const result = await bFix.start(endTime, 300); console.log(result); let msgResult = I18N('DAMAGE_NO_FIXED', { lastDamage: lastDamage.toLocaleString() }); if (result.value > lastDamage) { call.args.result = result.result; call.args.progress = result.progress; msgResult = I18N('DAMAGE_FIXED', { lastDamage: lastDamage.toLocaleString(), maxDamage: result.value.toLocaleString(), }); } console.log(lastDamage, '>', result.value); setProgress( msgResult + '<br/>' + I18N('COUNT_FIXED', { count: result.maxCount, }), false, hideProgress ); } else if (resultPopup > 3) { const cloneBattle = structuredClone(lastBossBattle); const { masterFixBattle } = HWHClasses; const mFix = new masterFixBattle(cloneBattle); const result = await mFix.start(cloneBattle.endTime, resultPopup); console.log(result); let msgResult = I18N('DAMAGE_NO_FIXED', { lastDamage: lastDamage.toLocaleString(), }); if (result.value > lastDamage) { maxDamage = result.value; call.args.result = result.result; call.args.progress = result.progress; msgResult = I18N('DAMAGE_FIXED', { lastDamage: lastDamage.toLocaleString(), maxDamage: maxDamage.toLocaleString(), }); } console.log('Урон:', lastDamage, maxDamage); setProgress(msgResult, false, hideProgress); } else { fixBattle(call.args.progress[0].attackers.heroes); fixBattle(call.args.progress[0].defenders.heroes); } changeRequest = true; } const isStat = popup.getCheckBoxes().find((e) => e.name === 'isStat'); if (isStat.checked) { this.onReadySuccess = testBossBattle; } } /** * Save the Asgard Boss Attack Pack * Сохраняем пачку для атаки босса Асгарда */ if (call.name == 'clanRaid_startBossBattle') { console.log(JSON.stringify(call.args)); } /** * Saving the request to start the last battle * Сохранение запроса начала последнего боя */ if ( call.name == 'clanWarAttack' || call.name == 'crossClanWar_startBattle' || call.name == 'adventure_turnStartBattle' || call.name == 'adventureSolo_turnStartBattle' || call.name == 'bossAttack' || call.name == 'invasion_bossStart' || call.name == 'towerStartBattle' ) { nameFuncStartBattle = call.name; lastBattleArg = call.args; if (call.name == 'invasion_bossStart') { const timePassed = Date.now() - lastBossBattleStart; if (timePassed < invasionTimer) { await new Promise((e) => setTimeout(e, invasionTimer - timePassed)); } invasionTimer -= 1; } lastBossBattleStart = Date.now(); } if (call.name == 'invasion_bossEnd') { const lastBattle = lastBattleInfo; if (lastBattle && call.args.result.win) { lastBattle.progress = call.args.progress; const result = await Calc(lastBattle); let timer = getTimer(result.battleTime, 1) + addBattleTimer; const period = Math.ceil((Date.now() - lastBossBattleStart) / 1000); console.log(timer, period); if (period < timer) { timer = timer - period; await countdownTimer(timer); } } } /** * Disable spending divination cards * Отключить трату карт предсказаний */ if (call.name == 'dungeonEndBattle') { if (call.args.isRaid) { if (countPredictionCard <= 0) { delete call.args.isRaid; changeRequest = true; } else if (countPredictionCard > 0) { countPredictionCard--; } } console.log(`Cards: ${countPredictionCard}`); /** * Fix endless cards * Исправление бесконечных карт */ const lastBattle = lastDungeonBattleData; if (lastBattle && !call.args.isRaid) { if (changeRequest) { lastBattle.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }]; } else { lastBattle.progress = call.args.progress; } const result = await Calc(lastBattle); if (changeRequest) { call.args.progress = result.progress; call.args.result = result.result; } let timer = result.battleTimer + addBattleTimer; const period = Math.ceil((Date.now() - lastDungeonBattleStart) / 1000); console.log(timer, period); if (period < timer) { timer = timer - period; await countdownTimer(timer); } } } /** * Quiz Answer * Ответ на викторину */ if (call.name == 'quizAnswer') { /** * Automatically changes the answer to the correct one if there is one. * Автоматически меняет ответ на правильный если он есть */ if (lastAnswer && isChecked('getAnswer')) { call.args.answerId = lastAnswer; lastAnswer = null; changeRequest = true; } } /** * Present * Подарки */ if (call.name == 'freebieCheck') { freebieCheckInfo = call; } /** missionTimer */ if (call.name == 'missionEnd' && missionBattle) { let startTimer = false; if (!call.args.result.win) { startTimer = await popup.confirm(I18N('DEFEAT_TURN_TIMER'), [ { msg: I18N('BTN_NO'), result: false }, { msg: I18N('BTN_YES'), result: true }, ]); } if (call.args.result.win || startTimer) { missionBattle.progress = call.args.progress; missionBattle.result = call.args.result; const result = await Calc(missionBattle); let timer = result.battleTimer + addBattleTimer; const period = Math.ceil((Date.now() - lastMissionBattleStart) / 1000); if (period < timer) { timer = timer - period; await countdownTimer(timer); } missionBattle = null; } else { this.errorRequest = true; } } /** * Getting mission data for auto-repeat * Получение данных миссии для автоповтора */ if (isChecked('repeatMission') && call.name == 'missionEnd') { let missionInfo = { id: call.args.id, result: call.args.result, heroes: call.args.progress[0].attackers.heroes, count: 0, } setTimeout(async () => { if (!isSendsMission && await popup.confirm(I18N('MSG_REPEAT_MISSION'), [ { msg: I18N('BTN_REPEAT'), result: true}, { msg: I18N('BTN_NO'), result: false}, ])) { isStopSendMission = false; isSendsMission = true; sendsMission(missionInfo); } }, 0); } /** * Getting mission data * Получение данных миссии * missionTimer */ if (call.name == 'missionStart') { lastMissionStart = call.args; lastMissionBattleStart = Date.now(); } /** * Specify the quantity for Titan Orbs and Pet Eggs * Указать количество для сфер титанов и яиц петов */ if (isChecked('countControl') && (call.name == 'pet_chestOpen' || call.name == 'titanUseSummonCircle') && call.args.amount > 1) { const startAmount = call.args.amount; const result = await popup.confirm(I18N('MSG_SPECIFY_QUANT'), [ { msg: I18N('BTN_OPEN'), isInput: true, default: 1}, ]); if (result) { const item = call.name == 'pet_chestOpen' ? { id: 90, type: 'consumable' } : { id: 13, type: 'coin' }; cheats.updateInventory({ [item.type]: { [item.id]: -(result - startAmount), }, }); call.args.amount = result; changeRequest = true; } } /** * Specify the amount for keys and spheres of titan artifacts * Указать колличество для ключей и сфер артефактов титанов */ if (isChecked('countControl') && (call.name == 'artifactChestOpen' || call.name == 'titanArtifactChestOpen') && call.args.amount > 1 && call.args.free && !changeRequest) { artifactChestOpenCallName = call.name; const startAmount = call.args.amount; let result = await popup.confirm(I18N('MSG_SPECIFY_QUANT'), [ { msg: I18N('BTN_OPEN'), isInput: true, default: 1 }, ]); if (result) { const openChests = result; let sphere = result < 10 ? 1 : 10; call.args.amount = sphere; for (let count = openChests - sphere; count > 0; count -= sphere) { if (count < 10) sphere = 1; const ident = artifactChestOpenCallName + "_" + count; testData.calls.push({ name: artifactChestOpenCallName, args: { amount: sphere, free: true, }, ident: ident }); if (!Array.isArray(requestHistory[this.uniqid].calls[call.name])) { requestHistory[this.uniqid].calls[call.name] = [requestHistory[this.uniqid].calls[call.name]]; } requestHistory[this.uniqid].calls[call.name].push(ident); } const consumableId = call.name == 'artifactChestOpen' ? 45 : 55; cheats.updateInventory({ consumable: { [consumableId]: -(openChests - startAmount), }, }); artifactChestOpen = true; changeRequest = true; } } if (call.name == 'consumableUseLootBox') { lastRussianDollId = call.args.libId; /** * Specify quantity for gold caskets * Указать количество для золотых шкатулок */ if (isChecked('countControl') && call.args.libId == 148 && call.args.amount > 1) { const result = await popup.confirm(I18N('MSG_SPECIFY_QUANT'), [ { msg: I18N('BTN_OPEN'), isInput: true, default: call.args.amount}, ]); call.args.amount = result; changeRequest = true; } if (isChecked('countControl') && call.args.libId >= 362 && call.args.libId <= 389) { this.massOpen = call.args.libId; } } if (call.name == 'invasion_bossStart' && isChecked('tryFixIt_v2') && call.args.id == invasionInfo.id) { const pack = invasionDataPacks[invasionInfo.bossLvl]; if (pack) { if (pack.buff != invasionInfo.buff) { setProgress( I18N('INVASION_BOSS_BUFF', { bossLvl: invasionInfo.bossLvl, needBuff: pack.buff, haveBuff: invasionInfo.buff, }), false ); } else { call.args.pet = pack.pet; call.args.heroes = pack.heroes; call.args.favor = pack.favor; changeRequest = true; } } } if (call.name == 'workshopBuff_create') { const pack = invasionDataPacks[invasionInfo.bossLvl]; if (pack) { const addBuff = call.args.amount * 5; if (pack.buff < addBuff + invasionInfo.buff) { this.errorRequest = true; } setProgress( I18N('INVASION_BOSS_BUFF', { bossLvl: invasionInfo.bossLvl, needBuff: pack.buff, haveBuff: invasionInfo.buff, }), false ); } } /** * Changing the maximum number of raids in the campaign * Изменение максимального количества рейдов в кампании */ // if (call.name == 'missionRaid') { // if (isChecked('countControl') && call.args.times > 1) { // const result = +(await popup.confirm(I18N('MSG_SPECIFY_QUANT'), [ // { msg: I18N('BTN_RUN'), isInput: true, default: call.args.times }, // ])); // call.args.times = result > call.args.times ? call.args.times : result; // changeRequest = true; // } // } } let headers = requestHistory[this.uniqid].headers; if (changeRequest) { sourceData = JSON.stringify(testData); headers['X-Auth-Signature'] = getSignature(headers, sourceData); } let signature = headers['X-Auth-Signature']; if (signature) { original.setRequestHeader.call(this, 'X-Auth-Signature', signature); } } catch (err) { console.log("Request(send, " + this.uniqid + "):\n", sourceData, "Error:\n", err); } return sourceData; } /** * Processing and substitution of incoming data * * Обработка и подмена входящих данных */ async function checkChangeResponse(response) { try { isChange = false; let nowTime = Math.round(Date.now() / 1000); callsIdent = requestHistory[this.uniqid].calls; respond = JSON.parse(response); /** * If the request returned an error removes the error (removes synchronization errors) * Если запрос вернул ошибку удаляет ошибку (убирает ошибки синхронизации) */ if (respond.error) { isChange = true; console.error(respond.error); if (isChecked('showErrors')) { popup.confirm(I18N('ERROR_MSG', { name: respond.error.name, description: respond.error.description, })); } if (respond.error.name != 'AccountBan') { delete respond.error; respond.results = []; } } let mainReward = null; const allReward = {}; let countTypeReward = 0; let readQuestInfo = false; for (const call of respond.results) { /** * Obtaining initial data for completing quests * Получение исходных данных для выполнения квестов */ if (readQuestInfo) { questsInfo[call.ident] = call.result.response; } /** * Getting a user ID * Получение идетификатора пользователя */ if (call.ident == callsIdent['registration']) { userId = call.result.response.userId; if (localStorage['userId'] != userId) { localStorage['newGiftSendIds'] = ''; localStorage['userId'] = userId; } await openOrMigrateDatabase(userId); readQuestInfo = true; } /** * Hiding donation offers 1 * Скрываем предложения доната 1 */ if (call.ident == callsIdent['billingGetAll'] && getSaveVal('noOfferDonat')) { const billings = call.result.response?.billings; const bundle = call.result.response?.bundle; if (billings && bundle) { call.result.response.billings = call.result.response.billings.filter((e) => ['repeatableOffer'].includes(e.type)); call.result.response.bundle = []; isChange = true; } } /** * Hiding donation offers 2 * Скрываем предложения доната 2 */ if (getSaveVal('noOfferDonat') && (call.ident == callsIdent['offerGetAll'] || call.ident == callsIdent['specialOffer_getAll'])) { let offers = call.result.response; if (offers) { call.result.response = offers.filter( (e) => !['addBilling', 'bundleCarousel'].includes(e.type) || ['idleResource', 'stagesOffer'].includes(e.offerType) ); isChange = true; } } /** * Hiding donation offers 3 * Скрываем предложения доната 3 */ if (getSaveVal('noOfferDonat') && call.result?.bundleUpdate) { delete call.result.bundleUpdate; isChange = true; } /** * Hiding donation offers 4 * Скрываем предложения доната 4 */ if (call.result?.specialOffers) { const offers = call.result.specialOffers; call.result.specialOffers = offers.filter( (e) => !['addBilling', 'bundleCarousel'].includes(e.type) || ['idleResource', 'stagesOffer'].includes(e.offerType) ); isChange = true; } /** * Copies a quiz question to the clipboard * Копирует вопрос викторины в буфер обмена и получает на него ответ если есть */ if (call.ident == callsIdent['quizGetNewQuestion']) { let quest = call.result.response; console.log(quest.question); copyText(quest.question); setProgress(I18N('QUESTION_COPY'), true); quest.lang = null; if (typeof NXFlashVars !== 'undefined') { quest.lang = NXFlashVars.interface_lang; } lastQuestion = quest; if (isChecked('getAnswer')) { const answer = await getAnswer(lastQuestion); let showText = ''; if (answer) { lastAnswer = answer; console.log(answer); showText = `${I18N('ANSWER_KNOWN')}: ${answer}`; } else { showText = I18N('ANSWER_NOT_KNOWN'); } try { const hint = hintQuest(quest); if (hint) { showText += I18N('HINT') + hint; } } catch(e) {} setProgress(showText, true); } } /** * Submits a question with an answer to the database * Отправляет вопрос с ответом в базу данных */ if (call.ident == callsIdent['quizAnswer']) { const answer = call.result.response; if (lastQuestion) { const answerInfo = { answer, question: lastQuestion, lang: null, } if (typeof NXFlashVars !== 'undefined') { answerInfo.lang = NXFlashVars.interface_lang; } lastQuestion = null; setTimeout(sendAnswerInfo, 0, answerInfo); } } /** * Get user data * Получить даныне пользователя */ if (call.ident == callsIdent['userGetInfo']) { let user = call.result.response; document.title = user.name; userInfo = Object.assign({}, user); delete userInfo.refillable; if (!questsInfo['userGetInfo']) { questsInfo['userGetInfo'] = user; } } /** * Start of the battle for recalculation * Начало боя для прерасчета */ if (call.ident == callsIdent['clanWarAttack'] || call.ident == callsIdent['crossClanWar_startBattle'] || call.ident == callsIdent['bossAttack'] || call.ident == callsIdent['battleGetReplay'] || call.ident == callsIdent['brawl_startBattle'] || call.ident == callsIdent['adventureSolo_turnStartBattle'] || call.ident == callsIdent['invasion_bossStart'] || call.ident == callsIdent['titanArenaStartBattle'] || call.ident == callsIdent['towerStartBattle'] || call.ident == callsIdent['epicBrawl_startBattle'] || call.ident == callsIdent['adventure_turnStartBattle']) { let battle = call.result.response.battle || call.result.response.replay; if (call.ident == callsIdent['brawl_startBattle'] || call.ident == callsIdent['bossAttack'] || call.ident == callsIdent['towerStartBattle'] || call.ident == callsIdent['invasion_bossStart']) { battle = call.result.response; } lastBattleInfo = battle; if (call.ident == callsIdent['battleGetReplay'] && call.result.response.replay.type === "clan_raid") { if (call?.result?.response?.replay?.result?.damage) { const damages = Object.values(call.result.response.replay.result.damage); const bossDamage = damages.reduce((a, v) => a + v, 0); setProgress(I18N('BOSS_DAMAGE') + bossDamage.toLocaleString(), false, hideProgress); continue; } } if (!isChecked('preCalcBattle')) { continue; } const preCalcBattle = structuredClone(battle); setProgress(I18N('BEING_RECALC')); let battleDuration = 120; try { const typeBattle = getBattleType(preCalcBattle.type); battleDuration = +lib.data.battleConfig[typeBattle.split('_')[1]].config.battleDuration; } catch (e) { } //console.log(battle.type); function getBattleInfo(battle, isRandSeed) { return new Promise(function (resolve) { if (isRandSeed) { battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3); } BattleCalc(battle, getBattleType(battle.type), e => resolve(e)); }); } let actions = [getBattleInfo(preCalcBattle, false)]; let countTestBattle = getInput('countTestBattle'); if (call.ident == callsIdent['invasion_bossStart'] ) { countTestBattle = 0; } if (call.ident == callsIdent['battleGetReplay']) { preCalcBattle.progress = [{ attackers: { input: ['auto', 0, 0, 'auto', 0, 0] } }]; } for (let i = 0; i < countTestBattle; i++) { actions.push(getBattleInfo(preCalcBattle, true)); } Promise.all(actions) .then(e => { e = e.map(n => ({win: n.result.win, time: n.battleTime})); let firstBattle = e.shift(); const timer = Math.floor(battleDuration - firstBattle.time); const min = ('00' + Math.floor(timer / 60)).slice(-2); const sec = ('00' + Math.floor(timer - min * 60)).slice(-2); let msg = `${I18N('THIS_TIME')} ${firstBattle.win ? I18N('VICTORY') : I18N('DEFEAT')}`; if (e.length) { const countWin = e.reduce((w, s) => w + s.win, 0); msg += ` ${I18N('CHANCE_TO_WIN')}: ${Math.floor((countWin / e.length) * 100)}% (${e.length})`; } msg += `, ${min}:${sec}` setProgress(msg, false, hideProgress) }); } /** * Start of the Asgard boss fight * Начало боя с боссом Асгарда */ if (call.ident == callsIdent['clanRaid_startBossBattle']) { lastBossBattle = call.result.response.battle; lastBossBattle.endTime = Date.now() + 160 * 1000; if (isChecked('preCalcBattle')) { const result = await Calc(lastBossBattle).then(e => e.progress[0].defenders.heroes[1].extra); const bossDamage = result.damageTaken + result.damageTakenNextLevel; setProgress(I18N('BOSS_DAMAGE') + bossDamage.toLocaleString(), false, hideProgress); } } /** * Cancel tutorial * Отмена туториала */ if (isCanceledTutorial && call.ident == callsIdent['tutorialGetInfo']) { let chains = call.result.response.chains; for (let n in chains) { chains[n] = 9999; } isChange = true; } /** * Opening keys and spheres of titan artifacts * Открытие ключей и сфер артефактов титанов */ if (artifactChestOpen && (call.ident == callsIdent[artifactChestOpenCallName] || (callsIdent[artifactChestOpenCallName] && callsIdent[artifactChestOpenCallName].includes(call.ident)))) { let reward = call.result.response[artifactChestOpenCallName == 'artifactChestOpen' ? 'chestReward' : 'reward']; reward.forEach(e => { for (let f in e) { if (!allReward[f]) { allReward[f] = {}; } for (let o in e[f]) { if (!allReward[f][o]) { allReward[f][o] = e[f][o]; countTypeReward++; } else { allReward[f][o] += e[f][o]; } } } }); if (!call.ident.includes(artifactChestOpenCallName)) { mainReward = call.result.response; } } if (countTypeReward > 20) { correctShowOpenArtifact = 3; } else { correctShowOpenArtifact = 0; } /** * Sum the result of opening Pet Eggs * Суммирование результата открытия яиц питомцев */ if (isChecked('countControl') && call.ident == callsIdent['pet_chestOpen']) { const rewards = call.result.response.rewards; if (rewards.length > 10) { /** * Removing pet cards * Убираем карточки петов */ for (const reward of rewards) { if (reward.petCard) { delete reward.petCard; } } } rewards.forEach(e => { for (let f in e) { if (!allReward[f]) { allReward[f] = {}; } for (let o in e[f]) { if (!allReward[f][o]) { allReward[f][o] = e[f][o]; } else { allReward[f][o] += e[f][o]; } } } }); call.result.response.rewards = [allReward]; isChange = true; } /** * Removing titan cards * Убираем карточки титанов */ if (call.ident == callsIdent['titanUseSummonCircle']) { if (call.result.response.rewards.length > 10) { for (const reward of call.result.response.rewards) { if (reward.titanCard) { delete reward.titanCard; } } isChange = true; } } /** * Auto-repeat opening matryoshkas * АвтоПовтор открытия матрешек */ if (isChecked('countControl') && call.ident == callsIdent['consumableUseLootBox']) { let [countLootBox, lootBox] = Object.entries(call.result.response).pop(); countLootBox = +countLootBox; let newCount = 0; if (lootBox?.consumable && lootBox.consumable[lastRussianDollId]) { newCount += lootBox.consumable[lastRussianDollId]; delete lootBox.consumable[lastRussianDollId]; } if ( newCount && (await popup.confirm(`${I18N('BTN_OPEN')} ${newCount} ${I18N('OPEN_DOLLS')}?`, [ { msg: I18N('BTN_OPEN'), result: true }, { msg: I18N('BTN_NO'), result: false, isClose: true }, ])) ) { const [count, recursionResult] = await openRussianDolls(lastRussianDollId, newCount); countLootBox += +count; mergeItemsObj(lootBox, recursionResult); isChange = true; } if (this.massOpen) { if ( await popup.confirm(I18N('OPEN_ALL_EQUIP_BOXES'), [ { msg: I18N('BTN_OPEN'), result: true }, { msg: I18N('BTN_NO'), result: false, isClose: true }, ]) ) { const consumable = await Send({ calls: [{ name: 'inventoryGet', args: {}, ident: 'inventoryGet' }] }).then((e) => Object.entries(e.results[0].result.response.consumable) ); const calls = []; const deleteItems = {}; for (const [libId, amount] of consumable) { if (libId != this.massOpen && libId >= 362 && libId <= 389) { calls.push({ name: 'consumableUseLootBox', args: { libId, amount }, ident: 'consumableUseLootBox_' + libId, }); deleteItems[libId] = -amount; } } const responses = await Send({ calls }).then((e) => e.results.map((r) => r.result.response).flat()); for (const loot of responses) { const [count, result] = Object.entries(loot).pop(); countLootBox += +count; mergeItemsObj(lootBox, result); } isChange = true; this.onReadySuccess = () => { cheats.updateInventory({ consumable: deleteItems }); cheats.refreshInventory(); }; } } if (isChange) { call.result.response = { [countLootBox]: lootBox, }; } } /** * Dungeon recalculation (fix endless cards) * Прерасчет подземки (исправление бесконечных карт) */ if (call.ident == callsIdent['dungeonStartBattle']) { lastDungeonBattleData = call.result.response; lastDungeonBattleStart = Date.now(); } /** * Getting the number of prediction cards * Получение количества карт предсказаний */ if (call.ident == callsIdent['inventoryGet']) { countPredictionCard = call.result.response.consumable[81] || 0; } /** * Getting subscription status * Получение состояния подписки */ if (call.ident == callsIdent['subscriptionGetInfo']) { const subscription = call.result.response.subscription; if (subscription) { subEndTime = subscription.endTime * 1000; } } /** * Getting prediction cards * Получение карт предсказаний */ if (call.ident == callsIdent['questFarm']) { const consumable = call.result.response?.consumable; if (consumable && consumable[81]) { countPredictionCard += consumable[81]; console.log(`Cards: ${countPredictionCard}`); } } /** * Hiding extra servers * Скрытие лишних серверов */ if (call.ident == callsIdent['serverGetAll'] && isChecked('hideServers')) { let servers = call.result.response.users.map(s => s.serverId) call.result.response.servers = call.result.response.servers.filter(s => servers.includes(s.id)); isChange = true; } /** * Displays player positions in the adventure * Отображает позиции игроков в приключении */ if (call.ident == callsIdent['adventure_getLobbyInfo']) { const users = Object.values(call.result.response.users); const mapIdent = call.result.response.mapIdent; const adventureId = call.result.response.adventureId; const maps = { adv_strongford_3pl_hell: 9, adv_valley_3pl_hell: 10, adv_ghirwil_3pl_hell: 11, adv_angels_3pl_hell: 12, } let msg = I18N('MAP') + (mapIdent in maps ? maps[mapIdent] : adventureId); msg += '<br>' + I18N('PLAYER_POS'); for (const user of users) { msg += `<br>${user.user.name} - ${user.currentNode}`; } setProgress(msg, false, hideProgress); } /** * Automatic launch of a raid at the end of the adventure * Автоматический запуск рейда при окончании приключения */ if (call.ident == callsIdent['adventure_end']) { autoRaidAdventure() } /** Удаление лавки редкостей */ if (call.ident == callsIdent['missionRaid']) { if (call.result?.heroesMerchant) { delete call.result.heroesMerchant; isChange = true; } } /** missionTimer */ if (call.ident == callsIdent['missionStart']) { missionBattle = call.result.response; } /** Награды турнира стихий */ if (call.ident == callsIdent['hallOfFameGetTrophies']) { const trophys = call.result.response; const calls = []; for (const week in trophys) { const trophy = trophys[week]; if (!trophy.championRewardFarmed) { calls.push({ name: 'hallOfFameFarmTrophyReward', args: { trophyId: week, rewardType: 'champion' }, ident: 'body_champion_' + week, }); } if (Object.keys(trophy.clanReward).length && !trophy.clanRewardFarmed) { calls.push({ name: 'hallOfFameFarmTrophyReward', args: { trophyId: week, rewardType: 'clan' }, ident: 'body_clan_' + week, }); } } if (calls.length) { Send({ calls }) .then((e) => e.results.map((e) => e.result.response)) .then(async results => { let coin18 = 0, coin19 = 0, gold = 0, starmoney = 0; for (const r of results) { coin18 += r?.coin ? +r.coin[18] : 0; coin19 += r?.coin ? +r.coin[19] : 0; gold += r?.gold ? +r.gold : 0; starmoney += r?.starmoney ? +r.starmoney : 0; } let msg = I18N('ELEMENT_TOURNAMENT_REWARD') + '<br>'; if (coin18) { msg += cheats.translate('LIB_COIN_NAME_18') + `: ${coin18}<br>`; } if (coin19) { msg += cheats.translate('LIB_COIN_NAME_19') + `: ${coin19}<br>`; } if (gold) { msg += cheats.translate('LIB_PSEUDO_COIN') + `: ${gold}<br>`; } if (starmoney) { msg += cheats.translate('LIB_PSEUDO_STARMONEY') + `: ${starmoney}<br>`; } await popup.confirm(msg, [{ msg: I18N('BTN_OK'), result: 0 }]); }); } } if (call.ident == callsIdent['clanDomination_getInfo']) { clanDominationGetInfo = call.result.response; } if (call.ident == callsIdent['clanRaid_endBossBattle']) { console.log(call.result.response); const damage = Object.values(call.result.response.damage).reduce((a, e) => a + e); if (call.result.response.result.afterInvalid) { addProgress('<br>' + I18N('SERVER_NOT_ACCEPT')); } addProgress('<br>Server > ' + I18N('BOSS_DAMAGE') + damage.toLocaleString()); } if (call.ident == callsIdent['invasion_getInfo']) { const r = call.result.response; if (r?.actions?.length) { const boss = r.actions.find((e) => e.payload.id === invasionInfo.id); if (boss) { invasionInfo.buff = r.buffAmount; invasionInfo.bossLvl = boss.payload.level; if (isChecked('tryFixIt_v2')) { const pack = invasionDataPacks[invasionInfo.bossLvl]; if (pack) { setProgress( I18N('INVASION_BOSS_BUFF', { bossLvl: invasionInfo.bossLvl, needBuff: pack.buff, haveBuff: invasionInfo.buff, }), false ); } } } } } if (call.ident == callsIdent['workshopBuff_create']) { const r = call.result.response; if (r.id == 1) { invasionInfo.buff = r.amount; if (isChecked('tryFixIt_v2')) { const pack = invasionDataPacks[invasionInfo.bossLvl]; if (pack) { setProgress( I18N('INVASION_BOSS_BUFF', { bossLvl: invasionInfo.bossLvl, needBuff: pack.buff, haveBuff: invasionInfo.buff, }), false ); } } } } /* if (call.ident == callsIdent['chatGetAll'] && call.args.chatType == 'clanDomination' && !callsIdent['clanDomination_mapState']) { this.onReadySuccess = async function () { const result = await Send({ calls: [ { name: 'clanDomination_mapState', args: {}, ident: 'clanDomination_mapState', }, ], }).then((e) => e.results[0].result.response); let townPositions = result.townPositions; let positions = {}; for (let pos in townPositions) { let townPosition = townPositions[pos]; positions[townPosition.position] = townPosition; } Object.assign(clanDominationGetInfo, { townPositions: positions, }); let userPositions = result.userPositions; for (let pos in clanDominationGetInfo.townPositions) { let townPosition = clanDominationGetInfo.townPositions[pos]; if (townPosition.status) { userPositions[townPosition.userId] = +pos; } } cheats.updateMap(result); }; } if (call.ident == callsIdent['clanDomination_mapState']) { const townPositions = call.result.response.townPositions; const userPositions = call.result.response.userPositions; for (let pos in townPositions) { let townPos = townPositions[pos]; if (townPos.status) { userPositions[townPos.userId] = townPos.position; } } isChange = true; } */ } if (mainReward && artifactChestOpen) { console.log(allReward); mainReward[artifactChestOpenCallName == 'artifactChestOpen' ? 'chestReward' : 'reward'] = [allReward]; artifactChestOpen = false; artifactChestOpenCallName = ''; isChange = true; } } catch(err) { console.log("Request(response, " + this.uniqid + "):\n", "Error:\n", response, err); } if (isChange) { Object.defineProperty(this, 'responseText', { writable: true }); this.responseText = JSON.stringify(respond); } } /** * Request an answer to a question * * Запрос ответа на вопрос */ async function getAnswer(question) { // c29tZSBzdHJhbmdlIHN5bWJvbHM= const quizAPI = new ZingerYWebsiteAPI('getAnswer.php', arguments, { question }); return new Promise((resolve, reject) => { quizAPI.request().then((data) => { if (data.result) { resolve(data.result); } else { resolve(false); } }).catch((error) => { console.error(error); resolve(false); }); }) } /** * Submitting a question and answer to a database * * Отправка вопроса и ответа в базу данных */ function sendAnswerInfo(answerInfo) { // c29tZSBub25zZW5zZQ== const quizAPI = new ZingerYWebsiteAPI('setAnswer.php', arguments, { answerInfo }); quizAPI.request().then((data) => { if (data.result) { console.log(I18N('SENT_QUESTION')); } }); } /** * Returns the battle type by preset type * * Возвращает тип боя по типу пресета */ function getBattleType(strBattleType) { if (!strBattleType) { return null; } switch (strBattleType) { case 'titan_pvp': return 'get_titanPvp'; case 'titan_pvp_manual': case 'titan_clan_pvp': case 'clan_pvp_titan': case 'clan_global_pvp_titan': case 'brawl_titan': case 'challenge_titan': case 'titan_mission': return 'get_titanPvpManual'; case 'clan_raid': // Asgard Boss // Босс асгарда case 'adventure': // Adventures // Приключения case 'clan_global_pvp': case 'epic_brawl': case 'clan_pvp': return 'get_clanPvp'; case 'dungeon_titan': case 'titan_tower': return 'get_titan'; case 'tower': case 'clan_dungeon': return 'get_tower'; case 'pve': case 'mission': return 'get_pve'; case 'mission_boss': return 'get_missionBoss'; case 'challenge': case 'pvp_manual': return 'get_pvpManual'; case 'grand': case 'arena': case 'pvp': case 'clan_domination': return 'get_pvp'; case 'core': return 'get_core'; default: { if (strBattleType.includes('invasion')) { return 'get_invasion'; } if (strBattleType.includes('boss')) { return 'get_boss'; } if (strBattleType.includes('titan_arena')) { return 'get_titanPvpManual'; } return 'get_clanPvp'; } } } /** * Returns the class name of the passed object * * Возвращает название класса переданного объекта */ function getClass(obj) { return {}.toString.call(obj).slice(8, -1); } /** * Calculates the request signature * * Расчитывает сигнатуру запроса */ this.getSignature = function(headers, data) { const sign = { signature: '', length: 0, add: function (text) { this.signature += text; if (this.length < this.signature.length) { this.length = 3 * (this.signature.length + 1) >> 1; } }, } sign.add(headers["X-Request-Id"]); sign.add(':'); sign.add(headers["X-Auth-Token"]); sign.add(':'); sign.add(headers["X-Auth-Session-Id"]); sign.add(':'); sign.add(data); sign.add(':'); sign.add('LIBRARY-VERSION=1'); sign.add('UNIQUE-SESSION-ID=' + headers["X-Env-Unique-Session-Id"]); return md5(sign.signature); } class HotkeyManager { constructor() { if (HotkeyManager.instance) { return HotkeyManager.instance; } this.hotkeys = []; document.addEventListener('keydown', this.handleKeyDown.bind(this)); HotkeyManager.instance = this; } handleKeyDown(event) { const key = event.key.toLowerCase(); const mods = { ctrl: event.ctrlKey, alt: event.altKey, shift: event.shiftKey, }; this.hotkeys.forEach((hotkey) => { if (hotkey.key === key && hotkey.ctrl === mods.ctrl && hotkey.alt === mods.alt && hotkey.shift === mods.shift) { hotkey.callback(hotkey); } }); } add(key, opt = {}, callback) { this.hotkeys.push({ key: key.toLowerCase(), callback, ctrl: opt.ctrl || false, alt: opt.alt || false, shift: opt.shift || false, }); } remove(key, opt = {}) { this.hotkeys = this.hotkeys.filter((hotkey) => { return !( hotkey.key === key.toLowerCase() && hotkey.ctrl === (opt.ctrl || false) && hotkey.alt === (opt.alt || false) && hotkey.shift === (opt.shift || false) ); }); } static getInst() { if (!HotkeyManager.instance) { new HotkeyManager(); } return HotkeyManager.instance; } } class MouseClicker { constructor(element) { if (MouseClicker.instance) { return MouseClicker.instance; } this.element = element; this.mouse = { bubbles: true, cancelable: true, clientX: 0, clientY: 0, }; this.element.addEventListener('mousemove', this.handleMouseMove.bind(this)); this.clickInfo = {}; this.nextTimeoutId = 1; MouseClicker.instance = this; } handleMouseMove(event) { this.mouse.clientX = event.clientX; this.mouse.clientY = event.clientY; } click(options) { options = options || this.mouse; this.element.dispatchEvent(new MouseEvent('mousedown', options)); this.element.dispatchEvent(new MouseEvent('mouseup', options)); } start(interval = 1000, clickCount = Infinity) { const currentMouse = { ...this.mouse }; const timeoutId = this.nextTimeoutId++; let count = 0; const clickTimeout = () => { this.click(currentMouse); count++; if (count < clickCount) { this.clickInfo[timeoutId].timeout = setTimeout(clickTimeout, interval); } else { delete this.clickInfo[timeoutId]; } }; this.clickInfo[timeoutId] = { timeout: setTimeout(clickTimeout, interval), count: clickCount, }; return timeoutId; } stop(timeoutId) { if (this.clickInfo[timeoutId]) { clearTimeout(this.clickInfo[timeoutId].timeout); delete this.clickInfo[timeoutId]; } } stopAll() { for (const timeoutId in this.clickInfo) { clearTimeout(this.clickInfo[timeoutId].timeout); } this.clickInfo = {}; } static getInst(element) { if (!MouseClicker.instance) { new MouseClicker(element); } return MouseClicker.instance; } } let extintionsList = []; /** * Creates an interface * * Создает интерфейс */ function createInterface() { popup.init(); scriptMenu.init({ showMenu: true }); scriptMenu.addHeader(GM_info.script.name, justInfo); const versionHeader = scriptMenu.addHeader('v' + GM_info.script.version); if (extintionsList.length) { versionHeader.title = ''; versionHeader.style.color = 'red'; for (const extintion of extintionsList) { const { name, ver, author } = extintion; versionHeader.title += name + ', v' + ver + ' by ' + author + '\n'; } } // AutoClicker const hkm = new HotkeyManager(); const fc = document.getElementById('flash-content') || document.getElementById('game'); const mc = new MouseClicker(fc); function toggleClicker(self, timeout) { if (self.onClick) { console.log('Останавливаем клики'); mc.stop(self.onClick); self.onClick = false; } else { console.log('Стартуем клики'); self.onClick = mc.start(timeout); } } hkm.add('C', { ctrl: true, alt: true }, (self) => { console.log('"Ctrl + Alt + C"'); toggleClicker(self, 20); }); hkm.add('V', { ctrl: true, alt: true }, (self) => { console.log('"Ctrl + Alt + V"'); toggleClicker(self, 100); }); } function addExtentionName(name, ver, author) { extintionsList.push({ name, ver, author, }); } function addControls() { createInterface(); const checkboxDetails = scriptMenu.addDetails(I18N('SETTINGS')); for (let name in checkboxes) { if (checkboxes[name].hide) { continue; } checkboxes[name].cbox = scriptMenu.addCheckbox(checkboxes[name].label, checkboxes[name].title, checkboxDetails); /** * Getting the state of checkboxes from storage * Получаем состояние чекбоксов из storage */ let val = storage.get(name, null); if (val != null) { checkboxes[name].cbox.checked = val; } else { storage.set(name, checkboxes[name].default); checkboxes[name].cbox.checked = checkboxes[name].default; } /** * Tracing the change event of the checkbox for writing to storage * Отсеживание события изменения чекбокса для записи в storage */ checkboxes[name].cbox.dataset['name'] = name; checkboxes[name].cbox.addEventListener('change', async function (event) { const nameCheckbox = this.dataset['name']; /* if (this.checked && nameCheckbox == 'cancelBattle') { this.checked = false; if (await popup.confirm(I18N('MSG_BAN_ATTENTION'), [ { msg: I18N('BTN_NO_I_AM_AGAINST'), result: true }, { msg: I18N('BTN_YES_I_AGREE'), result: false }, ])) { return; } this.checked = true; } */ storage.set(nameCheckbox, this.checked); }) } const inputDetails = scriptMenu.addDetails(I18N('VALUES')); for (let name in inputs) { inputs[name].input = scriptMenu.addInputText(inputs[name].title, false, inputDetails); /** * Get inputText state from storage * Получаем состояние inputText из storage */ let val = storage.get(name, null); if (val != null) { inputs[name].input.value = val; } else { storage.set(name, inputs[name].default); inputs[name].input.value = inputs[name].default; } /** * Tracing a field change event for a record in storage * Отсеживание события изменения поля для записи в storage */ inputs[name].input.dataset['name'] = name; inputs[name].input.addEventListener('input', function () { const inputName = this.dataset['name']; let value = +this.value; if (!value || Number.isNaN(value)) { value = storage.get(inputName, inputs[inputName].default); inputs[name].input.value = value; } storage.set(inputName, value); }) } } /** * Sending a request * * Отправка запроса */ function send(json, callback, pr) { if (typeof json == 'string') { json = JSON.parse(json); } for (const call of json.calls) { if (!call?.context?.actionTs) { call.context = { actionTs: Math.floor(performance.now()) } } } json = JSON.stringify(json); /** * We get the headlines of the previous intercepted request * Получаем заголовки предыдущего перехваченого запроса */ let headers = lastHeaders; /** * We increase the header of the query Certifier by 1 * Увеличиваем заголовок идетификатора запроса на 1 */ headers["X-Request-Id"]++; /** * We calculate the title with the signature * Расчитываем заголовок с сигнатурой */ headers["X-Auth-Signature"] = getSignature(headers, json); /** * Create a new ajax request * Создаем новый AJAX запрос */ let xhr = new XMLHttpRequest; /** * Indicate the previously saved URL for API queries * Указываем ранее сохраненный URL для API запросов */ xhr.open('POST', apiUrl, true); /** * Add the function to the event change event * Добавляем функцию к событию смены статуса запроса */ xhr.onreadystatechange = function() { /** * If the result of the request is obtained, we call the flask function * Если результат запроса получен вызываем колбек функцию */ if(xhr.readyState == 4) { callback(xhr.response, pr); } }; /** * Indicate the type of request * Указываем тип запроса */ xhr.responseType = 'json'; /** * We set the request headers * Задаем заголовки запроса */ for(let nameHeader in headers) { let head = headers[nameHeader]; xhr.setRequestHeader(nameHeader, head); } /** * Sending a request * Отправляем запрос */ xhr.send(json); } let hideTimeoutProgress = 0; /** * Hide progress * * Скрыть прогресс */ function hideProgress(timeout) { timeout = timeout || 0; clearTimeout(hideTimeoutProgress); hideTimeoutProgress = setTimeout(function () { scriptMenu.setStatus(''); }, timeout); } /** * Progress display * * Отображение прогресса */ function setProgress(text, hide, onclick) { scriptMenu.setStatus(text, onclick); hide = hide || false; if (hide) { hideProgress(3000); } } /** * Progress added * * Дополнение прогресса */ function addProgress(text) { scriptMenu.addStatus(text); } /** * Returns the timer value depending on the subscription * * Возвращает значение таймера в зависимости от подписки */ function getTimer(time, div) { let speedDiv = 5; if (subEndTime < Date.now()) { speedDiv = div || 1.5; } return Math.max(Math.ceil(time / speedDiv + 1.5), 4); } function startSlave() { const { slaveFixBattle } = HWHClasses; const sFix = new slaveFixBattle(); sFix.wsStart(); } this.testFuntions = { hideProgress, setProgress, addProgress, masterFix: false, startSlave, }; this.HWHFuncs = { send, I18N, isChecked, getInput, copyText, confShow, hideProgress, setProgress, addProgress, getTimer, addExtentionName, getUserInfo, setIsCancalBattle, random, }; this.HWHClasses = { checkChangeSend, checkChangeResponse, }; /** * Calculates HASH MD5 from string * * Расчитывает HASH MD5 из строки * * [js-md5]{@link https://github.com/emn178/js-md5} * * @namespace md5 * @version 0.7.3 * @author Chen, Yi-Cyuan [[email protected]] * @copyright Chen, Yi-Cyuan 2014-2017 * @license MIT */ !function(){"use strict";function t(t){if(t)d[0]=d[16]=d[1]=d[2]=d[3]=d[4]=d[5]=d[6]=d[7]=d[8]=d[9]=d[10]=d[11]=d[12]=d[13]=d[14]=d[15]=0,this.blocks=d,this.buffer8=l;else if(a){var r=new ArrayBuffer(68);this.buffer8=new Uint8Array(r),this.blocks=new Uint32Array(r)}else this.blocks=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];this.h0=this.h1=this.h2=this.h3=this.start=this.bytes=this.hBytes=0,this.finalized=this.hashed=!1,this.first=!0}var r="input is invalid type",e="object"==typeof window,i=e?window:{};i.JS_MD5_NO_WINDOW&&(e=!1);var s=!e&&"object"==typeof self,h=!i.JS_MD5_NO_NODE_JS&&"object"==typeof process&&process.versions&&process.versions.node;h?i=global:s&&(i=self);var f=!i.JS_MD5_NO_COMMON_JS&&"object"==typeof module&&module.exports,o="function"==typeof define&&define.amd,a=!i.JS_MD5_NO_ARRAY_BUFFER&&"undefined"!=typeof ArrayBuffer,n="0123456789abcdef".split(""),u=[128,32768,8388608,-2147483648],y=[0,8,16,24],c=["hex","array","digest","buffer","arrayBuffer","base64"],p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""),d=[],l;if(a){var A=new ArrayBuffer(68);l=new Uint8Array(A),d=new Uint32Array(A)}!i.JS_MD5_NO_NODE_JS&&Array.isArray||(Array.isArray=function(t){return"[object Array]"===Object.prototype.toString.call(t)}),!a||!i.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW&&ArrayBuffer.isView||(ArrayBuffer.isView=function(t){return"object"==typeof t&&t.buffer&&t.buffer.constructor===ArrayBuffer});var b=function(r){return function(e){return new t(!0).update(e)[r]()}},v=function(){var r=b("hex");h&&(r=w(r)),r.create=function(){return new t},r.update=function(t){return r.create().update(t)};for(var e=0;e<c.length;++e){var i=c[e];r[i]=b(i)}return r},w=function(t){var e=eval("require('crypto')"),i=eval("require('buffer').Buffer"),s=function(s){if("string"==typeof s)return e.createHash("md5").update(s,"utf8").digest("hex");if(null===s||void 0===s)throw r;return s.constructor===ArrayBuffer&&(s=new Uint8Array(s)),Array.isArray(s)||ArrayBuffer.isView(s)||s.constructor===i?e.createHash("md5").update(new i(s)).digest("hex"):t(s)};return s};t.prototype.update=function(t){if(!this.finalized){var e,i=typeof t;if("string"!==i){if("object"!==i)throw r;if(null===t)throw r;if(a&&t.constructor===ArrayBuffer)t=new Uint8Array(t);else if(!(Array.isArray(t)||a&&ArrayBuffer.isView(t)))throw r;e=!0}for(var s,h,f=0,o=t.length,n=this.blocks,u=this.buffer8;f<o;){if(this.hashed&&(this.hashed=!1,n[0]=n[16],n[16]=n[1]=n[2]=n[3]=n[4]=n[5]=n[6]=n[7]=n[8]=n[9]=n[10]=n[11]=n[12]=n[13]=n[14]=n[15]=0),e)if(a)for(h=this.start;f<o&&h<64;++f)u[h++]=t[f];else for(h=this.start;f<o&&h<64;++f)n[h>>2]|=t[f]<<y[3&h++];else if(a)for(h=this.start;f<o&&h<64;++f)(s=t.charCodeAt(f))<128?u[h++]=s:s<2048?(u[h++]=192|s>>6,u[h++]=128|63&s):s<55296||s>=57344?(u[h++]=224|s>>12,u[h++]=128|s>>6&63,u[h++]=128|63&s):(s=65536+((1023&s)<<10|1023&t.charCodeAt(++f)),u[h++]=240|s>>18,u[h++]=128|s>>12&63,u[h++]=128|s>>6&63,u[h++]=128|63&s);else for(h=this.start;f<o&&h<64;++f)(s=t.charCodeAt(f))<128?n[h>>2]|=s<<y[3&h++]:s<2048?(n[h>>2]|=(192|s>>6)<<y[3&h++],n[h>>2]|=(128|63&s)<<y[3&h++]):s<55296||s>=57344?(n[h>>2]|=(224|s>>12)<<y[3&h++],n[h>>2]|=(128|s>>6&63)<<y[3&h++],n[h>>2]|=(128|63&s)<<y[3&h++]):(s=65536+((1023&s)<<10|1023&t.charCodeAt(++f)),n[h>>2]|=(240|s>>18)<<y[3&h++],n[h>>2]|=(128|s>>12&63)<<y[3&h++],n[h>>2]|=(128|s>>6&63)<<y[3&h++],n[h>>2]|=(128|63&s)<<y[3&h++]);this.lastByteIndex=h,this.bytes+=h-this.start,h>=64?(this.start=h-64,this.hash(),this.hashed=!0):this.start=h}return this.bytes>4294967295&&(this.hBytes+=this.bytes/4294967296<<0,this.bytes=this.bytes%4294967296),this}},t.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var t=this.blocks,r=this.lastByteIndex;t[r>>2]|=u[3&r],r>=56&&(this.hashed||this.hash(),t[0]=t[16],t[16]=t[1]=t[2]=t[3]=t[4]=t[5]=t[6]=t[7]=t[8]=t[9]=t[10]=t[11]=t[12]=t[13]=t[14]=t[15]=0),t[14]=this.bytes<<3,t[15]=this.hBytes<<3|this.bytes>>>29,this.hash()}},t.prototype.hash=function(){var t,r,e,i,s,h,f=this.blocks;this.first?r=((r=((t=((t=f[0]-680876937)<<7|t>>>25)-271733879<<0)^(e=((e=(-271733879^(i=((i=(-1732584194^2004318071&t)+f[1]-117830708)<<12|i>>>20)+t<<0)&(-271733879^t))+f[2]-1126478375)<<17|e>>>15)+i<<0)&(i^t))+f[3]-1316259209)<<22|r>>>10)+e<<0:(t=this.h0,r=this.h1,e=this.h2,r=((r+=((t=((t+=((i=this.h3)^r&(e^i))+f[0]-680876936)<<7|t>>>25)+r<<0)^(e=((e+=(r^(i=((i+=(e^t&(r^e))+f[1]-389564586)<<12|i>>>20)+t<<0)&(t^r))+f[2]+606105819)<<17|e>>>15)+i<<0)&(i^t))+f[3]-1044525330)<<22|r>>>10)+e<<0),r=((r+=((t=((t+=(i^r&(e^i))+f[4]-176418897)<<7|t>>>25)+r<<0)^(e=((e+=(r^(i=((i+=(e^t&(r^e))+f[5]+1200080426)<<12|i>>>20)+t<<0)&(t^r))+f[6]-1473231341)<<17|e>>>15)+i<<0)&(i^t))+f[7]-45705983)<<22|r>>>10)+e<<0,r=((r+=((t=((t+=(i^r&(e^i))+f[8]+1770035416)<<7|t>>>25)+r<<0)^(e=((e+=(r^(i=((i+=(e^t&(r^e))+f[9]-1958414417)<<12|i>>>20)+t<<0)&(t^r))+f[10]-42063)<<17|e>>>15)+i<<0)&(i^t))+f[11]-1990404162)<<22|r>>>10)+e<<0,r=((r+=((t=((t+=(i^r&(e^i))+f[12]+1804603682)<<7|t>>>25)+r<<0)^(e=((e+=(r^(i=((i+=(e^t&(r^e))+f[13]-40341101)<<12|i>>>20)+t<<0)&(t^r))+f[14]-1502002290)<<17|e>>>15)+i<<0)&(i^t))+f[15]+1236535329)<<22|r>>>10)+e<<0,r=((r+=((i=((i+=(r^e&((t=((t+=(e^i&(r^e))+f[1]-165796510)<<5|t>>>27)+r<<0)^r))+f[6]-1069501632)<<9|i>>>23)+t<<0)^t&((e=((e+=(t^r&(i^t))+f[11]+643717713)<<14|e>>>18)+i<<0)^i))+f[0]-373897302)<<20|r>>>12)+e<<0,r=((r+=((i=((i+=(r^e&((t=((t+=(e^i&(r^e))+f[5]-701558691)<<5|t>>>27)+r<<0)^r))+f[10]+38016083)<<9|i>>>23)+t<<0)^t&((e=((e+=(t^r&(i^t))+f[15]-660478335)<<14|e>>>18)+i<<0)^i))+f[4]-405537848)<<20|r>>>12)+e<<0,r=((r+=((i=((i+=(r^e&((t=((t+=(e^i&(r^e))+f[9]+568446438)<<5|t>>>27)+r<<0)^r))+f[14]-1019803690)<<9|i>>>23)+t<<0)^t&((e=((e+=(t^r&(i^t))+f[3]-187363961)<<14|e>>>18)+i<<0)^i))+f[8]+1163531501)<<20|r>>>12)+e<<0,r=((r+=((i=((i+=(r^e&((t=((t+=(e^i&(r^e))+f[13]-1444681467)<<5|t>>>27)+r<<0)^r))+f[2]-51403784)<<9|i>>>23)+t<<0)^t&((e=((e+=(t^r&(i^t))+f[7]+1735328473)<<14|e>>>18)+i<<0)^i))+f[12]-1926607734)<<20|r>>>12)+e<<0,r=((r+=((h=(i=((i+=((s=r^e)^(t=((t+=(s^i)+f[5]-378558)<<4|t>>>28)+r<<0))+f[8]-2022574463)<<11|i>>>21)+t<<0)^t)^(e=((e+=(h^r)+f[11]+1839030562)<<16|e>>>16)+i<<0))+f[14]-35309556)<<23|r>>>9)+e<<0,r=((r+=((h=(i=((i+=((s=r^e)^(t=((t+=(s^i)+f[1]-1530992060)<<4|t>>>28)+r<<0))+f[4]+1272893353)<<11|i>>>21)+t<<0)^t)^(e=((e+=(h^r)+f[7]-155497632)<<16|e>>>16)+i<<0))+f[10]-1094730640)<<23|r>>>9)+e<<0,r=((r+=((h=(i=((i+=((s=r^e)^(t=((t+=(s^i)+f[13]+681279174)<<4|t>>>28)+r<<0))+f[0]-358537222)<<11|i>>>21)+t<<0)^t)^(e=((e+=(h^r)+f[3]-722521979)<<16|e>>>16)+i<<0))+f[6]+76029189)<<23|r>>>9)+e<<0,r=((r+=((h=(i=((i+=((s=r^e)^(t=((t+=(s^i)+f[9]-640364487)<<4|t>>>28)+r<<0))+f[12]-421815835)<<11|i>>>21)+t<<0)^t)^(e=((e+=(h^r)+f[15]+530742520)<<16|e>>>16)+i<<0))+f[2]-995338651)<<23|r>>>9)+e<<0,r=((r+=((i=((i+=(r^((t=((t+=(e^(r|~i))+f[0]-198630844)<<6|t>>>26)+r<<0)|~e))+f[7]+1126891415)<<10|i>>>22)+t<<0)^((e=((e+=(t^(i|~r))+f[14]-1416354905)<<15|e>>>17)+i<<0)|~t))+f[5]-57434055)<<21|r>>>11)+e<<0,r=((r+=((i=((i+=(r^((t=((t+=(e^(r|~i))+f[12]+1700485571)<<6|t>>>26)+r<<0)|~e))+f[3]-1894986606)<<10|i>>>22)+t<<0)^((e=((e+=(t^(i|~r))+f[10]-1051523)<<15|e>>>17)+i<<0)|~t))+f[1]-2054922799)<<21|r>>>11)+e<<0,r=((r+=((i=((i+=(r^((t=((t+=(e^(r|~i))+f[8]+1873313359)<<6|t>>>26)+r<<0)|~e))+f[15]-30611744)<<10|i>>>22)+t<<0)^((e=((e+=(t^(i|~r))+f[6]-1560198380)<<15|e>>>17)+i<<0)|~t))+f[13]+1309151649)<<21|r>>>11)+e<<0,r=((r+=((i=((i+=(r^((t=((t+=(e^(r|~i))+f[4]-145523070)<<6|t>>>26)+r<<0)|~e))+f[11]-1120210379)<<10|i>>>22)+t<<0)^((e=((e+=(t^(i|~r))+f[2]+718787259)<<15|e>>>17)+i<<0)|~t))+f[9]-343485551)<<21|r>>>11)+e<<0,this.first?(this.h0=t+1732584193<<0,this.h1=r-271733879<<0,this.h2=e-1732584194<<0,this.h3=i+271733878<<0,this.first=!1):(this.h0=this.h0+t<<0,this.h1=this.h1+r<<0,this.h2=this.h2+e<<0,this.h3=this.h3+i<<0)},t.prototype.hex=function(){this.finalize();var t=this.h0,r=this.h1,e=this.h2,i=this.h3;return n[t>>4&15]+n[15&t]+n[t>>12&15]+n[t>>8&15]+n[t>>20&15]+n[t>>16&15]+n[t>>28&15]+n[t>>24&15]+n[r>>4&15]+n[15&r]+n[r>>12&15]+n[r>>8&15]+n[r>>20&15]+n[r>>16&15]+n[r>>28&15]+n[r>>24&15]+n[e>>4&15]+n[15&e]+n[e>>12&15]+n[e>>8&15]+n[e>>20&15]+n[e>>16&15]+n[e>>28&15]+n[e>>24&15]+n[i>>4&15]+n[15&i]+n[i>>12&15]+n[i>>8&15]+n[i>>20&15]+n[i>>16&15]+n[i>>28&15]+n[i>>24&15]},t.prototype.toString=t.prototype.hex,t.prototype.digest=function(){this.finalize();var t=this.h0,r=this.h1,e=this.h2,i=this.h3;return[255&t,t>>8&255,t>>16&255,t>>24&255,255&r,r>>8&255,r>>16&255,r>>24&255,255&e,e>>8&255,e>>16&255,e>>24&255,255&i,i>>8&255,i>>16&255,i>>24&255]},t.prototype.array=t.prototype.digest,t.prototype.arrayBuffer=function(){this.finalize();var t=new ArrayBuffer(16),r=new Uint32Array(t);return r[0]=this.h0,r[1]=this.h1,r[2]=this.h2,r[3]=this.h3,t},t.prototype.buffer=t.prototype.arrayBuffer,t.prototype.base64=function(){for(var t,r,e,i="",s=this.array(),h=0;h<15;)t=s[h++],r=s[h++],e=s[h++],i+=p[t>>>2]+p[63&(t<<4|r>>>4)]+p[63&(r<<2|e>>>6)]+p[63&e];return t=s[h],i+=p[t>>>2]+p[t<<4&63]+"=="};var _=v();f?module.exports=_:(i.md5=_,o&&define(function(){return _}))}(); class Caller { static globalHooks = { onError: null, }; constructor(calls = null) { this.calls = []; this.results = {}; if (calls) { this.add(calls); } } static setGlobalHook(event, callback) { if (this.globalHooks[event] !== undefined) { this.globalHooks[event] = callback; } else { throw new Error(`Unknown event: ${event}`); } } addCall(call) { const { name = call, args = {} } = typeof call === 'object' ? call : { name: call }; this.calls.push({ name, args }); return this; } add(name) { if (Array.isArray(name)) { name.forEach((call) => this.addCall(call)); } else { this.addCall(name); } return this; } handleError(error) { const errorName = error.name; const errorDescription = error.description; if (Caller.globalHooks.onError) { const shouldThrow = Caller.globalHooks.onError(error); if (shouldThrow === false) { return; } } if (error.call) { const callInfo = error.call; throw new Error(`${errorName} in ${callInfo.name}: ${errorDescription}\n` + `Args: ${JSON.stringify(callInfo.args)}\n`); } else if (errorName === 'common\\rpc\\exception\\InvalidRequest') { throw new Error(`Invalid request: ${errorDescription}`); } else { throw new Error(`Unknown error: ${errorName} - ${errorDescription}`); } } async send() { if (!this.calls.length) { throw new Error('No calls to send.'); } const identToNameMap = {}; const callsWithIdent = this.calls.map((call, index) => { const ident = this.calls.length === 1 ? 'body' : `group_${index}_body`; identToNameMap[ident] = call.name; return { ...call, ident }; }); try { const response = await Send({ calls: callsWithIdent }); if (response.error) { this.handleError(response.error); } if (!response.results) { throw new Error('Invalid response format: missing "results" field'); } response.results.forEach((result) => { const name = identToNameMap[result.ident]; if (!this.results[name]) { this.results[name] = []; } this.results[name].push(result.result.response); }); } catch (error) { throw error; } return this; } result(name, forceArray = false) { const results = name ? this.results[name] || [] : Object.values(this.results).flat(); return forceArray || results.length !== 1 ? results : results[0]; } async execute(name) { try { await this.send(); return this.result(name); } catch (error) { throw error; } } clear() { this.calls = []; this.results = {}; return this; } isEmpty() { return this.calls.length === 0 && Object.keys(this.results).length === 0; } } this.Caller = Caller; /* // Примеры использования (async () => { // Короткий вызов await new Caller('inventoryGet').execute(); // Простой вызов let result = await new Caller().add('inventoryGet').execute(); console.log('Inventory Get Result:', result); // Сложный вызов let caller = new Caller(); await caller .add([ { name: 'inventoryGet', args: {}, }, { name: 'heroGetAll', args: {}, }, ]) .send(); console.log('Inventory Get Result:', caller.result('inventoryGet')); console.log('Hero Get All Result:', caller.result('heroGetAll')); // Очистка всех данных caller.clear(); })(); */ /** * Script for beautiful dialog boxes * * Скрипт для красивых диалоговых окошек */ const popup = new (function () { this.popUp, this.downer, this.middle, this.msgText, this.buttons = []; this.checkboxes = []; this.dialogPromice = null; this.isInit = false; this.init = function () { if (this.isInit) { return; } addStyle(); addBlocks(); addEventListeners(); this.isInit = true; } const addEventListeners = () => { document.addEventListener('keyup', (e) => { if (e.key == 'Escape') { if (this.dialogPromice) { const { func, result } = this.dialogPromice; this.dialogPromice = null; popup.hide(); func(result); } } }); } const addStyle = () => { let style = document.createElement('style'); style.innerText = ` .PopUp_ { position: absolute; min-width: 300px; max-width: 500px; max-height: 600px; background-color: #190e08e6; z-index: 10001; top: 169px; left: 345px; border: 3px #ce9767 solid; border-radius: 10px; display: flex; flex-direction: column; justify-content: space-around; padding: 15px 9px; box-sizing: border-box; } .PopUp_back { position: absolute; background-color: #00000066; width: 100%; height: 100%; z-index: 10000; top: 0; left: 0; } .PopUp_close { width: 40px; height: 40px; position: absolute; right: -18px; top: -18px; border: 3px solid #c18550; border-radius: 20px; background: radial-gradient(circle, rgba(190,30,35,1) 0%, rgba(0,0,0,1) 100%); background-position-y: 3px; box-shadow: -1px 1px 3px black; cursor: pointer; box-sizing: border-box; } .PopUp_close:hover { filter: brightness(1.2); } .PopUp_crossClose { width: 100%; height: 100%; background-size: 65%; background-position: center; background-repeat: no-repeat; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%23f4cd73' d='M 0.826 12.559 C 0.431 12.963 3.346 15.374 3.74 14.97 C 4.215 15.173 8.167 10.457 7.804 10.302 C 7.893 10.376 11.454 14.64 11.525 14.372 C 12.134 15.042 15.118 12.086 14.638 11.689 C 14.416 11.21 10.263 7.477 10.402 7.832 C 10.358 7.815 11.731 7.101 14.872 3.114 C 14.698 2.145 13.024 1.074 12.093 1.019 C 11.438 0.861 8.014 5.259 8.035 5.531 C 7.86 5.082 3.61 1.186 3.522 1.59 C 2.973 1.027 0.916 4.611 1.17 4.873 C 0.728 4.914 5.088 7.961 5.61 7.995 C 5.225 7.532 0.622 12.315 0.826 12.559 Z'/%3e%3c/svg%3e") } .PopUp_blocks { width: 100%; height: 50%; display: flex; justify-content: space-evenly; align-items: center; flex-wrap: wrap; justify-content: center; } .PopUp_blocks:last-child { margin-top: 25px; } .PopUp_buttons { display: flex; margin: 7px 10px; flex-direction: column; } .PopUp_button { background-color: #52A81C; border-radius: 5px; box-shadow: inset 0px -4px 10px, inset 0px 3px 2px #99fe20, 0px 0px 4px, 0px -3px 1px #d7b275, 0px 0px 0px 3px #ce9767; cursor: pointer; padding: 4px 12px 6px; } .PopUp_input { text-align: center; font-size: 16px; height: 27px; border: 1px solid #cf9250; border-radius: 9px 9px 0px 0px; background: transparent; color: #fce1ac; padding: 1px 10px; box-sizing: border-box; box-shadow: 0px 0px 4px, 0px 0px 0px 3px #ce9767; } .PopUp_checkboxes { display: flex; flex-direction: column; margin: 15px 15px -5px 15px; align-items: flex-start; } .PopUp_ContCheckbox { margin: 2px 0px; } .PopUp_checkbox { position: absolute; z-index: -1; opacity: 0; } .PopUp_checkbox+label { display: inline-flex; align-items: center; user-select: none; font-size: 15px; font-family: sans-serif; font-weight: 600; font-stretch: condensed; letter-spacing: 1px; color: #fce1ac; text-shadow: 0px 0px 1px; } .PopUp_checkbox+label::before { content: ''; display: inline-block; width: 20px; height: 20px; border: 1px solid #cf9250; border-radius: 7px; margin-right: 7px; } .PopUp_checkbox:checked+label::before { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2388cb13' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e"); } .PopUp_input::placeholder { color: #fce1ac75; } .PopUp_input:focus { outline: 0; } .PopUp_input + .PopUp_button { border-radius: 0px 0px 5px 5px; padding: 2px 18px 5px; } .PopUp_button:hover { filter: brightness(1.2); } .PopUp_button:active { box-shadow: inset 0px 5px 10px, inset 0px 1px 2px #99fe20, 0px 0px 4px, 0px -3px 1px #d7b275, 0px 0px 0px 3px #ce9767; } .PopUp_text { font-size: 22px; font-family: sans-serif; font-weight: 600; font-stretch: condensed; letter-spacing: 1px; text-align: center; } .PopUp_buttonText { color: #E4FF4C; text-shadow: 0px 1px 2px black; } .PopUp_msgText { color: #FDE5B6; text-shadow: 0px 0px 2px; } .PopUp_hideBlock { display: none; } `; document.head.appendChild(style); } const addBlocks = () => { this.back = document.createElement('div'); this.back.classList.add('PopUp_back'); this.back.classList.add('PopUp_hideBlock'); document.body.append(this.back); this.popUp = document.createElement('div'); this.popUp.classList.add('PopUp_'); this.back.append(this.popUp); let upper = document.createElement('div') upper.classList.add('PopUp_blocks'); this.popUp.append(upper); this.middle = document.createElement('div') this.middle.classList.add('PopUp_blocks'); this.middle.classList.add('PopUp_checkboxes'); this.popUp.append(this.middle); this.downer = document.createElement('div') this.downer.classList.add('PopUp_blocks'); this.popUp.append(this.downer); this.msgText = document.createElement('div'); this.msgText.classList.add('PopUp_text', 'PopUp_msgText'); upper.append(this.msgText); } this.showBack = function () { this.back.classList.remove('PopUp_hideBlock'); } this.hideBack = function () { this.back.classList.add('PopUp_hideBlock'); } this.show = function () { if (this.checkboxes.length) { this.middle.classList.remove('PopUp_hideBlock'); } this.showBack(); this.popUp.classList.remove('PopUp_hideBlock'); this.popUp.style.left = (window.innerWidth - this.popUp.offsetWidth) / 2 + 'px'; this.popUp.style.top = (window.innerHeight - this.popUp.offsetHeight) / 3 + 'px'; } this.hide = function () { this.hideBack(); this.popUp.classList.add('PopUp_hideBlock'); } this.addAnyButton = (option) => { const contButton = document.createElement('div'); contButton.classList.add('PopUp_buttons'); this.downer.append(contButton); let inputField = { value: option.result || option.default } if (option.isInput) { inputField = document.createElement('input'); inputField.type = 'text'; if (option.placeholder) { inputField.placeholder = option.placeholder; } if (option.default) { inputField.value = option.default; } inputField.classList.add('PopUp_input'); contButton.append(inputField); } const button = document.createElement('div'); button.classList.add('PopUp_button'); button.title = option.title || ''; contButton.append(button); const buttonText = document.createElement('div'); buttonText.classList.add('PopUp_text', 'PopUp_buttonText'); buttonText.innerHTML = option.msg; button.append(buttonText); return { button, contButton, inputField }; } this.addCloseButton = () => { let button = document.createElement('div') button.classList.add('PopUp_close'); this.popUp.append(button); let crossClose = document.createElement('div') crossClose.classList.add('PopUp_crossClose'); button.append(crossClose); return { button, contButton: button }; } this.addButton = (option, buttonClick) => { const { button, contButton, inputField } = option.isClose ? this.addCloseButton() : this.addAnyButton(option); if (option.isClose) { this.dialogPromice = { func: buttonClick, result: option.result }; } button.addEventListener('click', () => { let result = ''; if (option.isInput) { result = inputField.value; } if (option.isClose || option.isCancel) { this.dialogPromice = null; } buttonClick(result); }); this.buttons.push(contButton); } this.clearButtons = () => { while (this.buttons.length) { this.buttons.pop().remove(); } } this.addCheckBox = (checkBox) => { const contCheckbox = document.createElement('div'); contCheckbox.classList.add('PopUp_ContCheckbox'); this.middle.append(contCheckbox); const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.id = 'PopUpCheckbox' + this.checkboxes.length; checkbox.dataset.name = checkBox.name; checkbox.checked = checkBox.checked; checkbox.label = checkBox.label; checkbox.title = checkBox.title || ''; checkbox.classList.add('PopUp_checkbox'); contCheckbox.appendChild(checkbox) const checkboxLabel = document.createElement('label'); checkboxLabel.innerText = checkBox.label; checkboxLabel.title = checkBox.title || ''; checkboxLabel.setAttribute('for', checkbox.id); contCheckbox.appendChild(checkboxLabel); this.checkboxes.push(checkbox); } this.clearCheckBox = () => { this.middle.classList.add('PopUp_hideBlock'); while (this.checkboxes.length) { this.checkboxes.pop().parentNode.remove(); } } this.setMsgText = (text) => { this.msgText.innerHTML = text; } this.getCheckBoxes = () => { const checkBoxes = []; for (const checkBox of this.checkboxes) { checkBoxes.push({ name: checkBox.dataset.name, label: checkBox.label, checked: checkBox.checked }); } return checkBoxes; } this.confirm = async (msg, buttOpt, checkBoxes = []) => { if (!this.isInit) { this.init(); } this.clearButtons(); this.clearCheckBox(); return new Promise((complete, failed) => { this.setMsgText(msg); if (!buttOpt) { buttOpt = [{ msg: 'Ok', result: true, isInput: false }]; } for (const checkBox of checkBoxes) { this.addCheckBox(checkBox); } for (let butt of buttOpt) { this.addButton(butt, (result) => { result = result || butt.result; complete(result); popup.hide(); }); if (butt.isCancel) { this.dialogPromice = { func: complete, result: butt.result }; } } this.show(); }); } }); this.HWHFuncs.popup = popup; /** * Script control panel * * Панель управления скриптом */ class ScriptMenu { constructor() { this.mainMenu = null; this.buttons = []; this.checkboxes = []; this.option = { showMenu: false, showDetails: {}, }; } init(option = {}) { this.option = Object.assign(this.option, option); this.option.showDetails = this.loadShowDetails(); this.addStyle(); this.addBlocks(); } addStyle() { const style = document.createElement('style'); style.innerText = ` .scriptMenu_status { position: absolute; z-index: 10001; top: -1px; left: 30%; cursor: pointer; border-radius: 0px 0px 10px 10px; background: #190e08e6; border: 1px #ce9767 solid; font-size: 18px; font-family: sans-serif; font-weight: 600; font-stretch: condensed; letter-spacing: 1px; color: #fce1ac; text-shadow: 0px 0px 1px; transition: 0.5s; padding: 2px 10px 3px; } .scriptMenu_statusHide { top: -35px; height: 30px; overflow: hidden; } .scriptMenu_label { position: absolute; top: 30%; left: -4px; z-index: 9999; cursor: pointer; width: 30px; height: 30px; background: radial-gradient(circle, #47a41b 0%, #1a2f04 100%); border: 1px solid #1a2f04; border-radius: 5px; box-shadow: inset 0px 2px 4px #83ce26, inset 0px -4px 6px #1a2f04, 0px 0px 2px black, 0px 0px 0px 2px #ce9767; } .scriptMenu_label:hover { filter: brightness(1.2); } .scriptMenu_arrowLabel { width: 100%; height: 100%; background-size: 75%; background-position: center; background-repeat: no-repeat; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%2388cb13' d='M7.596 7.304a.802.802 0 0 1 0 1.392l-6.363 3.692C.713 12.69 0 12.345 0 11.692V4.308c0-.653.713-.998 1.233-.696l6.363 3.692Z'/%3e%3cpath fill='%2388cb13' d='M15.596 7.304a.802.802 0 0 1 0 1.392l-6.363 3.692C8.713 12.69 8 12.345 8 11.692V4.308c0-.653.713-.998 1.233-.696l6.363 3.692Z'/%3e%3c/svg%3e"); box-shadow: 0px 1px 2px #000; border-radius: 5px; filter: drop-shadow(0px 1px 2px #000D); } .scriptMenu_main { position: absolute; max-width: 285px; z-index: 9999; top: 50%; transform: translateY(-40%); background: #190e08e6; border: 1px #ce9767 solid; border-radius: 0px 10px 10px 0px; border-left: none; padding: 5px 10px 5px 5px; box-sizing: border-box; font-size: 15px; font-family: sans-serif; font-weight: 600; font-stretch: condensed; letter-spacing: 1px; color: #fce1ac; text-shadow: 0px 0px 1px; transition: 1s; display: flex; flex-direction: column; flex-wrap: nowrap; } .scriptMenu_showMenu { display: none; } .scriptMenu_showMenu:checked~.scriptMenu_main { left: 0px; } .scriptMenu_showMenu:not(:checked)~.scriptMenu_main { left: -300px; } .scriptMenu_divInput { margin: 2px; } .scriptMenu_divInputText { margin: 2px; align-self: center; display: flex; } .scriptMenu_checkbox { position: absolute; z-index: -1; opacity: 0; } .scriptMenu_checkbox+label { display: inline-flex; align-items: center; user-select: none; } .scriptMenu_checkbox+label::before { content: ''; display: inline-block; width: 20px; height: 20px; border: 1px solid #cf9250; border-radius: 7px; margin-right: 7px; } .scriptMenu_checkbox:checked+label::before { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2388cb13' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e"); } .scriptMenu_close { width: 40px; height: 40px; position: absolute; right: -18px; top: -18px; border: 3px solid #c18550; border-radius: 20px; background: radial-gradient(circle, rgba(190,30,35,1) 0%, rgba(0,0,0,1) 100%); background-position-y: 3px; box-shadow: -1px 1px 3px black; cursor: pointer; box-sizing: border-box; } .scriptMenu_close:hover { filter: brightness(1.2); } .scriptMenu_crossClose { width: 100%; height: 100%; background-size: 65%; background-position: center; background-repeat: no-repeat; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%23f4cd73' d='M 0.826 12.559 C 0.431 12.963 3.346 15.374 3.74 14.97 C 4.215 15.173 8.167 10.457 7.804 10.302 C 7.893 10.376 11.454 14.64 11.525 14.372 C 12.134 15.042 15.118 12.086 14.638 11.689 C 14.416 11.21 10.263 7.477 10.402 7.832 C 10.358 7.815 11.731 7.101 14.872 3.114 C 14.698 2.145 13.024 1.074 12.093 1.019 C 11.438 0.861 8.014 5.259 8.035 5.531 C 7.86 5.082 3.61 1.186 3.522 1.59 C 2.973 1.027 0.916 4.611 1.17 4.873 C 0.728 4.914 5.088 7.961 5.61 7.995 C 5.225 7.532 0.622 12.315 0.826 12.559 Z'/%3e%3c/svg%3e") } .scriptMenu_button { user-select: none; cursor: pointer; padding: 5px 14px 8px; } .scriptMenu_button:hover { filter: brightness(1.2); } .scriptMenu_buttonText { color: #fce5b7; text-shadow: 0px 1px 2px black; text-align: center; } .scriptMenu_header { text-align: center; align-self: center; font-size: 15px; margin: 0px 15px; } .scriptMenu_header a { color: #fce5b7; text-decoration: none; } .scriptMenu_InputText { text-align: center; width: 130px; height: 24px; border: 1px solid #cf9250; border-radius: 9px; background: transparent; color: #fce1ac; padding: 0px 10px; box-sizing: border-box; } .scriptMenu_InputText:focus { filter: brightness(1.2); outline: 0; } .scriptMenu_InputText::placeholder { color: #fce1ac75; } .scriptMenu_Summary { cursor: pointer; margin-left: 7px; } .scriptMenu_Details { align-self: center; } .scriptMenu_buttonGroup { display: flex; justify-content: center; user-select: none; cursor: pointer; padding: 0; margin: 3px 0; } .scriptMenu_buttonGroup .scriptMenu_button { width: 100%; padding: 5px 8px 8px; } .scriptMenu_mainButton { border-radius: 5px; margin: 3px 0; } .scriptMenu_combineButtonLeft { border-top-left-radius: 5px; border-bottom-left-radius: 5px; margin-right: 2px; } .scriptMenu_combineButtonCenter { border-radius: 0px; margin-right: 2px; } .scriptMenu_combineButtonRight { border-top-right-radius: 5px; border-bottom-right-radius: 5px; } .scriptMenu_beigeButton { border: 1px solid #442901; background: radial-gradient(circle, rgba(165,120,56,1) 80%, rgba(0,0,0,1) 110%); box-shadow: inset 0px 2px 4px #e9b282, inset 0px -4px 6px #442901, inset 0px 1px 6px #442901, inset 0px 0px 6px, 0px 0px 2px black, 0px 0px 0px 1px #ce9767; } .scriptMenu_beigeButton:active { box-shadow: inset 0px 4px 6px #442901, inset 0px 4px 6px #442901, inset 0px 0px 6px, 0px 0px 4px, 0px 0px 0px 1px #ce9767; } .scriptMenu_greenButton { border: 1px solid #1a2f04; background: radial-gradient(circle, #47a41b 0%, #1a2f04 150%); box-shadow: inset 0px 2px 4px #83ce26, inset 0px -4px 6px #1a2f04, 0px 0px 2px black, 0px 0px 0px 1px #ce9767; } .scriptMenu_greenButton:active { box-shadow: inset 0px 4px 6px #1a2f04, inset 0px 4px 6px #1a2f04, inset 0px 0px 6px, 0px 0px 4px, 0px 0px 0px 1px #ce9767; } .scriptMenu_redButton { border: 1px solid #440101; background: radial-gradient(circle, rgb(198, 34, 34) 80%, rgb(0, 0, 0) 110%); box-shadow: inset 0px 2px 4px #e98282, inset 0px -4px 6px #440101, inset 0px 1px 6px #440101, inset 0px 0px 6px, 0px 0px 2px black, 0px 0px 0px 1px #ce9767; } .scriptMenu_redButton:active { box-shadow: inset 0px 4px 6px #440101, inset 0px 4px 6px #440101, inset 0px 0px 6px, 0px 0px 4px, 0px 0px 0px 1px #ce9767; } .scriptMenu_attention { position: relative; } .scriptMenu_attention .scriptMenu_dot { display: block; } .scriptMenu_dot { position: absolute; top: -7px; right: -7px; width: 20px; height: 20px; border-radius: 50%; border: 1px solid #c18550; background: radial-gradient(circle, #f000 25%, black 100%); box-shadow: 0px 0px 2px black; background-position: 0px -1px; font-size: 10px; text-align: center; color: white; text-shadow: 1px 1px 1px black; padding: 2px 0px; box-sizing: border-box; display: none; } `; document.head.appendChild(style); } addBlocks() { const main = document.createElement('div'); document.body.appendChild(main); this.status = document.createElement('div'); this.status.classList.add('scriptMenu_status'); this.setStatus(''); main.appendChild(this.status); const label = document.createElement('label'); label.classList.add('scriptMenu_label'); label.setAttribute('for', 'checkbox_showMenu'); main.appendChild(label); const arrowLabel = document.createElement('div'); arrowLabel.classList.add('scriptMenu_arrowLabel'); label.appendChild(arrowLabel); const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.id = 'checkbox_showMenu'; checkbox.checked = this.option.showMenu; checkbox.classList.add('scriptMenu_showMenu'); main.appendChild(checkbox); this.mainMenu = document.createElement('div'); this.mainMenu.classList.add('scriptMenu_main'); main.appendChild(this.mainMenu); const closeButton = document.createElement('label'); closeButton.classList.add('scriptMenu_close'); closeButton.setAttribute('for', 'checkbox_showMenu'); this.mainMenu.appendChild(closeButton); const crossClose = document.createElement('div'); crossClose.classList.add('scriptMenu_crossClose'); closeButton.appendChild(crossClose); } getButtonColor(color) { const buttonColors = { green: 'scriptMenu_greenButton', red: 'scriptMenu_redButton', beige: 'scriptMenu_beigeButton', }; return buttonColors[color] || buttonColors['beige']; } setStatus(text, onclick) { if (!text) { this.status.classList.add('scriptMenu_statusHide'); this.status.innerHTML = ''; } else { this.status.classList.remove('scriptMenu_statusHide'); this.status.innerHTML = text; } if (typeof onclick === 'function') { this.status.addEventListener('click', onclick, { once: true }); } } addStatus(text) { if (!this.status.innerHTML) { this.status.classList.remove('scriptMenu_statusHide'); } this.status.innerHTML += text; } addHeader(text, onClick, main = this.mainMenu) { const header = document.createElement('div'); header.classList.add('scriptMenu_header'); header.innerHTML = text; if (typeof onClick === 'function') { header.addEventListener('click', onClick); } main.appendChild(header); return header; } addButton(btn, main = this.mainMenu) { const { name, onClick, title, color, dot, classes = [], isCombine } = btn; const button = document.createElement('div'); if (!isCombine) { classes.push('scriptMenu_mainButton'); } button.classList.add('scriptMenu_button', this.getButtonColor(color), ...classes); button.title = title; button.addEventListener('click', onClick); main.appendChild(button); const buttonText = document.createElement('div'); buttonText.classList.add('scriptMenu_buttonText'); buttonText.innerText = name; button.appendChild(buttonText); if (dot) { const dotAtention = document.createElement('div'); dotAtention.classList.add('scriptMenu_dot'); dotAtention.title = dot; button.appendChild(dotAtention); } this.buttons.push(button); return button; } addCombinedButton(buttonList, main = this.mainMenu) { const buttonGroup = document.createElement('div'); buttonGroup.classList.add('scriptMenu_buttonGroup'); let count = 0; for (const btn of buttonList) { btn.isCombine = true; btn.classes ??= []; if (count === 0) { btn.classes.push('scriptMenu_combineButtonLeft'); } else if (count === buttonList.length - 1) { btn.classes.push('scriptMenu_combineButtonRight'); } else { btn.classes.push('scriptMenu_combineButtonCenter'); } this.addButton(btn, buttonGroup); count++; } const dotAtention = document.createElement('div'); dotAtention.classList.add('scriptMenu_dot'); buttonGroup.appendChild(dotAtention); main.appendChild(buttonGroup); return buttonGroup; } addCheckbox(label, title, main = this.mainMenu) { const divCheckbox = document.createElement('div'); divCheckbox.classList.add('scriptMenu_divInput'); divCheckbox.title = title; main.appendChild(divCheckbox); const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.id = 'scriptMenuCheckbox' + this.checkboxes.length; checkbox.classList.add('scriptMenu_checkbox'); divCheckbox.appendChild(checkbox); const checkboxLabel = document.createElement('label'); checkboxLabel.innerText = label; checkboxLabel.setAttribute('for', checkbox.id); divCheckbox.appendChild(checkboxLabel); this.checkboxes.push(checkbox); return checkbox; } addInputText(title, placeholder, main = this.mainMenu) { const divInputText = document.createElement('div'); divInputText.classList.add('scriptMenu_divInputText'); divInputText.title = title; main.appendChild(divInputText); const newInputText = document.createElement('input'); newInputText.type = 'text'; if (placeholder) { newInputText.placeholder = placeholder; } newInputText.classList.add('scriptMenu_InputText'); divInputText.appendChild(newInputText); return newInputText; } addDetails(summaryText, name = null) { const details = document.createElement('details'); details.classList.add('scriptMenu_Details'); this.mainMenu.appendChild(details); const summary = document.createElement('summary'); summary.classList.add('scriptMenu_Summary'); summary.innerText = summaryText; if (name) { const self = this; details.open = this.option.showDetails[name]; details.dataset.name = name; summary.addEventListener('click', () => { self.option.showDetails[details.dataset.name] = !details.open; self.saveShowDetails(self.option.showDetails); }); } details.appendChild(summary); return details; } saveShowDetails(value) { try { localStorage.setItem('scriptMenu_showDetails', JSON.stringify(value)); } catch (e) { console.log('¯\\_(ツ)_/¯'); } } loadShowDetails() { let showDetails = null; try { showDetails = localStorage.getItem('scriptMenu_showDetails'); } catch (e) { console.log('¯\\_(ツ)_/¯'); } if (!showDetails) { return {}; } try { showDetails = JSON.parse(showDetails); } catch (e) { return {}; } return showDetails; } } const scriptMenu = new ScriptMenu(); /** * Пример использования const scriptMenu = new ScriptMenu(); scriptMenu.init(); scriptMenu.addHeader('v1.508'); scriptMenu.addCheckbox('testHack', 'Тестовый взлом игры!'); scriptMenu.addButton({ text: 'Запуск!', onClick: () => console.log('click'), title: 'подсказака', }); scriptMenu.addInputText('input подсказака'); */ /** * Game Library * * Игровая библиотека */ class Library { defaultLibUrl = 'https://heroesru-a.akamaihd.net/vk/v1101/lib/lib.json'; constructor() { if (!Library.instance) { Library.instance = this; } return Library.instance; } async load() { try { await this.getUrlLib(); console.log(this.defaultLibUrl); this.data = await fetch(this.defaultLibUrl).then(e => e.json()) } catch (error) { console.error('Не удалось загрузить библиотеку', error) } } async getUrlLib() { try { const db = new Database('hw_cache', 'cache'); await db.open(); const cacheLibFullUrl = await db.get('lib/lib.json.gz', false); this.defaultLibUrl = cacheLibFullUrl.fullUrl.split('.gz').shift(); } catch(e) {} } getData(id) { return this.data[id]; } setData(data) { this.data = data; } } this.lib = new Library(); /** * Database * * База данных */ class Database { constructor(dbName, storeName) { this.dbName = dbName; this.storeName = storeName; this.db = null; } async open() { return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName); request.onerror = () => { reject(new Error(`Failed to open database ${this.dbName}`)); }; request.onsuccess = () => { this.db = request.result; resolve(); }; request.onupgradeneeded = (event) => { const db = event.target.result; if (!db.objectStoreNames.contains(this.storeName)) { db.createObjectStore(this.storeName); } }; }); } async set(key, value) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); const request = store.put(value, key); request.onerror = () => { reject(new Error(`Failed to save value with key ${key}`)); }; request.onsuccess = () => { resolve(); }; }); } async get(key, def) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([this.storeName], 'readonly'); const store = transaction.objectStore(this.storeName); const request = store.get(key); request.onerror = () => { resolve(def); }; request.onsuccess = () => { resolve(request.result); }; }); } async delete(key) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); const request = store.delete(key); request.onerror = () => { reject(new Error(`Failed to delete value with key ${key}`)); }; request.onsuccess = () => { resolve(); }; }); } } /** * Returns the stored value * * Возвращает сохраненное значение */ function getSaveVal(saveName, def) { const result = storage.get(saveName, def); return result; } this.HWHFuncs.getSaveVal = getSaveVal; /** * Stores value * * Сохраняет значение */ function setSaveVal(saveName, value) { storage.set(saveName, value); } this.HWHFuncs.setSaveVal = setSaveVal; /** * Database initialization * * Инициализация базы данных */ const db = new Database(GM_info.script.name, 'settings'); /** * Data store * * Хранилище данных */ const storage = { userId: 0, /** * Default values * * Значения по умолчанию */ values: [ ...Object.entries(checkboxes).map(e => ({ [e[0]]: e[1].default })), ...Object.entries(inputs).map(e => ({ [e[0]]: e[1].default })), ].reduce((acc, obj) => ({ ...acc, ...obj }), {}), name: GM_info.script.name, get: function (key, def) { if (key in this.values) { return this.values[key]; } return def; }, set: function (key, value) { this.values[key] = value; db.set(this.userId, this.values).catch( e => null ); localStorage[this.name + ':' + key] = value; }, delete: function (key) { delete this.values[key]; db.set(this.userId, this.values); delete localStorage[this.name + ':' + key]; } } /** * Returns all keys from localStorage that start with prefix (for migration) * * Возвращает все ключи из localStorage которые начинаются с prefix (для миграции) */ function getAllValuesStartingWith(prefix) { const values = []; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key.startsWith(prefix)) { const val = localStorage.getItem(key); const keyValue = key.split(':')[1]; values.push({ key: keyValue, val }); } } return values; } /** * Opens or migrates to a database * * Открывает или мигрирует в базу данных */ async function openOrMigrateDatabase(userId) { storage.userId = userId; try { await db.open(); } catch(e) { return; } let settings = await db.get(userId, false); if (settings) { storage.values = settings; return; } const values = getAllValuesStartingWith(GM_info.script.name); for (const value of values) { let val = null; try { val = JSON.parse(value.val); } catch { break; } storage.values[value.key] = val; } await db.set(userId, storage.values); } class ZingerYWebsiteAPI { /** * Class for interaction with the API of the zingery.ru website * Intended only for use with the HeroWarsHelper script: * https://greasyfork.org/ru/scripts/450693-herowarshelper * Copyright ZingerY */ url = 'https://zingery.ru/heroes/'; // YWJzb2x1dGVseSB1c2VsZXNzIGxpbmU= constructor(urn, env, data = {}) { this.urn = urn; this.fd = { now: Date.now(), fp: this.constructor.toString().replaceAll(/\s/g, ''), env: env.callee.toString().replaceAll(/\s/g, ''), info: (({ name, version, author }) => [name, version, author])(GM_info.script), ...data, }; } sign() { return md5([...this.fd.info, ~(this.fd.now % 1e3), this.fd.fp].join('_')); } encode(data) { return btoa(encodeURIComponent(JSON.stringify(data))); } decode(data) { return JSON.parse(decodeURIComponent(atob(data))); } headers() { return { 'X-Request-Signature': this.sign(), 'X-Script-Name': GM_info.script.name, 'X-Script-Version': GM_info.script.version, 'X-Script-Author': GM_info.script.author, 'X-Script-ZingerY': 42, }; } async request() { try { const response = await fetch(this.url + this.urn, { method: 'POST', headers: this.headers(), body: this.encode(this.fd), }); const text = await response.text(); return this.decode(text); } catch (e) { console.error(e); return []; } } /** * Класс для взаимодействия с API сайта zingery.ru * Предназначен только для использования со скриптом HeroWarsHelper: * https://greasyfork.org/ru/scripts/450693-herowarshelper * Copyright ZingerY */ } /** * Sending expeditions * * Отправка экспедиций */ function checkExpedition() { const { Expedition } = HWHClasses; return new Promise((resolve, reject) => { const expedition = new Expedition(resolve, reject); expedition.start(); }); } class Expedition { checkExpedInfo = { calls: [ { name: 'expeditionGet', args: {}, ident: 'expeditionGet', }, { name: 'heroGetAll', args: {}, ident: 'heroGetAll', }, ], }; constructor(resolve, reject) { this.resolve = resolve; this.reject = reject; } async start() { const data = await Send(JSON.stringify(this.checkExpedInfo)); const expedInfo = data.results[0].result.response; const dataHeroes = data.results[1].result.response; const dataExped = { useHeroes: [], exped: [] }; const calls = []; /** * Adding expeditions to collect * Добавляем экспедиции для сбора */ let countGet = 0; for (var n in expedInfo) { const exped = expedInfo[n]; const dateNow = Date.now() / 1000; if (exped.status == 2 && exped.endTime != 0 && dateNow > exped.endTime) { countGet++; calls.push({ name: 'expeditionFarm', args: { expeditionId: exped.id }, ident: 'expeditionFarm_' + exped.id, }); } else { dataExped.useHeroes = dataExped.useHeroes.concat(exped.heroes); } if (exped.status == 1) { dataExped.exped.push({ id: exped.id, power: exped.power }); } } dataExped.exped = dataExped.exped.sort((a, b) => b.power - a.power); /** * Putting together a list of heroes * Собираем список героев */ const heroesArr = []; for (let n in dataHeroes) { const hero = dataHeroes[n]; if (hero.power > 0 && !dataExped.useHeroes.includes(hero.id)) { let heroPower = hero.power; // Лара Крофт * 3 if (hero.id == 63 && hero.color >= 16) { heroPower *= 3; } heroesArr.push({ id: hero.id, power: heroPower }); } } /** * Adding expeditions to send * Добавляем экспедиции для отправки */ let countSend = 0; heroesArr.sort((a, b) => a.power - b.power); for (const exped of dataExped.exped) { let heroesIds = this.selectionHeroes(heroesArr, exped.power); if (heroesIds && heroesIds.length > 4) { for (let q in heroesArr) { if (heroesIds.includes(heroesArr[q].id)) { delete heroesArr[q]; } } countSend++; calls.push({ name: 'expeditionSendHeroes', args: { expeditionId: exped.id, heroes: heroesIds, }, ident: 'expeditionSendHeroes_' + exped.id, }); } } if (calls.length) { await Send({ calls }); this.end(I18N('EXPEDITIONS_SENT', {countGet, countSend})); return; } this.end(I18N('EXPEDITIONS_NOTHING')); } /** * Selection of heroes for expeditions * * Подбор героев для экспедиций */ selectionHeroes(heroes, power) { const resultHeroers = []; const heroesIds = []; for (let q = 0; q < 5; q++) { for (let i in heroes) { let hero = heroes[i]; if (heroesIds.includes(hero.id)) { continue; } const summ = resultHeroers.reduce((acc, hero) => acc + hero.power, 0); const need = Math.round((power - summ) / (5 - resultHeroers.length)); if (hero.power > need) { resultHeroers.push(hero); heroesIds.push(hero.id); break; } } } const summ = resultHeroers.reduce((acc, hero) => acc + hero.power, 0); if (summ < power) { return false; } return heroesIds; } /** * Ends expedition script * * Завершает скрипт экспедиции */ end(msg) { setProgress(msg, true); this.resolve(); } } this.HWHClasses.Expedition = Expedition; /** * Walkthrough of the dungeon * * Прохождение подземелья */ function testDungeon() { const { executeDungeon } = HWHClasses; return new Promise((resolve, reject) => { const dung = new executeDungeon(resolve, reject); const titanit = getInput('countTitanit'); dung.start(titanit); }); } /** * Walkthrough of the dungeon * * Прохождение подземелья */ function executeDungeon(resolve, reject) { dungeonActivity = 0; maxDungeonActivity = 150; titanGetAll = []; teams = { heroes: [], earth: [], fire: [], neutral: [], water: [], } titanStats = []; titansStates = {}; let talentMsg = ''; let talentMsgReward = ''; callsExecuteDungeon = { calls: [{ name: "dungeonGetInfo", args: {}, ident: "dungeonGetInfo" }, { name: "teamGetAll", args: {}, ident: "teamGetAll" }, { name: "teamGetFavor", args: {}, ident: "teamGetFavor" }, { name: "clanGetInfo", args: {}, ident: "clanGetInfo" }, { name: "titanGetAll", args: {}, ident: "titanGetAll" }, { name: "inventoryGet", args: {}, ident: "inventoryGet" }] } this.start = function(titanit) { maxDungeonActivity = titanit || getInput('countTitanit'); send(JSON.stringify(callsExecuteDungeon), startDungeon); } /** * Getting data on the dungeon * * Получаем данные по подземелью */ function startDungeon(e) { res = e.results; dungeonGetInfo = res[0].result.response; if (!dungeonGetInfo) { endDungeon('noDungeon', res); return; } teamGetAll = res[1].result.response; teamGetFavor = res[2].result.response; dungeonActivity = res[3].result.response.stat.todayDungeonActivity; titanGetAll = Object.values(res[4].result.response); countPredictionCard = res[5].result.response.consumable[81]; teams.hero = { favor: teamGetFavor.dungeon_hero, heroes: teamGetAll.dungeon_hero.filter(id => id < 6000), teamNum: 0, } heroPet = teamGetAll.dungeon_hero.filter(id => id >= 6000).pop(); if (heroPet) { teams.hero.pet = heroPet; } teams.neutral = { favor: {}, heroes: getTitanTeam(titanGetAll, 'neutral'), teamNum: 0, }; teams.water = { favor: {}, heroes: getTitanTeam(titanGetAll, 'water'), teamNum: 0, }; teams.fire = { favor: {}, heroes: getTitanTeam(titanGetAll, 'fire'), teamNum: 0, }; teams.earth = { favor: {}, heroes: getTitanTeam(titanGetAll, 'earth'), teamNum: 0, }; checkFloor(dungeonGetInfo); } function getTitanTeam(titans, type) { switch (type) { case 'neutral': return titans.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id); case 'water': return titans.filter(e => e.id.toString().slice(2, 3) == '0').map(e => e.id); case 'fire': return titans.filter(e => e.id.toString().slice(2, 3) == '1').map(e => e.id); case 'earth': return titans.filter(e => e.id.toString().slice(2, 3) == '2').map(e => e.id); } } function getNeutralTeam() { const titans = titanGetAll.filter(e => !titansStates[e.id]?.isDead) return titans.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id); } function fixTitanTeam(titans) { titans.heroes = titans.heroes.filter(e => !titansStates[e]?.isDead); return titans; } /** * Checking the floor * * Проверяем этаж */ async function checkFloor(dungeonInfo) { if (!('floor' in dungeonInfo) || dungeonInfo.floor?.state == 2) { saveProgress(); return; } checkTalent(dungeonInfo); // console.log(dungeonInfo, dungeonActivity); setProgress(`${I18N('DUNGEON')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity} ${talentMsg}`); if (dungeonActivity >= maxDungeonActivity) { endDungeon('endDungeon', 'maxActive ' + dungeonActivity + '/' + maxDungeonActivity); return; } titansStates = dungeonInfo.states.titans; titanStats = titanObjToArray(titansStates); const floorChoices = dungeonInfo.floor.userData; const floorType = dungeonInfo.floorType; //const primeElement = dungeonInfo.elements.prime; if (floorType == "battle") { const calls = []; for (let teamNum in floorChoices) { attackerType = floorChoices[teamNum].attackerType; const args = fixTitanTeam(teams[attackerType]); if (attackerType == 'neutral') { args.heroes = getNeutralTeam(); } if (!args.heroes.length) { continue; } args.teamNum = teamNum; calls.push({ name: "dungeonStartBattle", args, ident: "body_" + teamNum }) } if (!calls.length) { endDungeon('endDungeon', 'All Dead'); return; } const battleDatas = await Send(JSON.stringify({ calls })) .then(e => e.results.map(n => n.result.response)) const battleResults = []; for (n in battleDatas) { battleData = battleDatas[n] battleData.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }]; battleResults.push(await Calc(battleData).then(result => { result.teamNum = n; result.attackerType = floorChoices[n].attackerType; return result; })); } processingPromises(battleResults) } } async function checkTalent(dungeonInfo) { const talent = dungeonInfo.talent; if (!talent) { return; } const dungeonFloor = +dungeonInfo.floorNumber; const talentFloor = +talent.floorRandValue; let doorsAmount = 3 - talent.conditions.doorsAmount; if (dungeonFloor === talentFloor && (!doorsAmount || !talent.conditions?.farmedDoors[dungeonFloor])) { const reward = await Send({ calls: [ { name: 'heroTalent_getReward', args: { talentType: 'tmntDungeonTalent', reroll: false }, ident: 'group_0_body' }, { name: 'heroTalent_farmReward', args: { talentType: 'tmntDungeonTalent' }, ident: 'group_1_body' }, ], }).then((e) => e.results[0].result.response); const type = Object.keys(reward).pop(); const itemId = Object.keys(reward[type]).pop(); const count = reward[type][itemId]; const itemName = cheats.translate(`LIB_${type.toUpperCase()}_NAME_${itemId}`); talentMsgReward += `<br> ${count} ${itemName}`; doorsAmount++; } talentMsg = `<br>TMNT Talent: ${doorsAmount}/3 ${talentMsgReward}<br>`; } function processingPromises(results) { let selectBattle = results[0]; if (results.length < 2) { // console.log(selectBattle); if (!selectBattle.result.win) { endDungeon('dungeonEndBattle\n', selectBattle); return; } endBattle(selectBattle); return; } selectBattle = false; let bestState = -1000; for (const result of results) { const recovery = getState(result); if (recovery > bestState) { bestState = recovery; selectBattle = result } } // console.log(selectBattle.teamNum, results); if (!selectBattle || bestState <= -1000) { endDungeon('dungeonEndBattle\n', results); return; } startBattle(selectBattle.teamNum, selectBattle.attackerType) .then(endBattle); } /** * Let's start the fight * * Начинаем бой */ function startBattle(teamNum, attackerType) { return new Promise(function (resolve, reject) { args = fixTitanTeam(teams[attackerType]); args.teamNum = teamNum; if (attackerType == 'neutral') { const titans = titanGetAll.filter(e => !titansStates[e.id]?.isDead) args.heroes = titans.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id); } startBattleCall = { calls: [{ name: "dungeonStartBattle", args, ident: "body" }] } send(JSON.stringify(startBattleCall), resultBattle, { resolve, teamNum, attackerType }); }); } /** * Returns the result of the battle in a promise * * Возращает резульат боя в промис */ function resultBattle(resultBattles, args) { battleData = resultBattles.results[0].result.response; battleType = "get_tower"; if (battleData.type == "dungeon_titan") { battleType = "get_titan"; } battleData.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }]; BattleCalc(battleData, battleType, function (result) { result.teamNum = args.teamNum; result.attackerType = args.attackerType; args.resolve(result); }); } /** * Finishing the fight * * Заканчиваем бой */ async function endBattle(battleInfo) { if (battleInfo.result.win) { const args = { result: battleInfo.result, progress: battleInfo.progress, } if (countPredictionCard > 0) { args.isRaid = true; } else { const timer = getTimer(battleInfo.battleTime); console.log(timer); await countdownTimer(timer, `${I18N('DUNGEON')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity} ${talentMsg}`); } const calls = [{ name: "dungeonEndBattle", args, ident: "body" }]; lastDungeonBattleData = null; send(JSON.stringify({ calls }), resultEndBattle); } else { endDungeon('dungeonEndBattle win: false\n', battleInfo); } } /** * Getting and processing battle results * * Получаем и обрабатываем результаты боя */ function resultEndBattle(e) { if ('error' in e) { popup.confirm(I18N('ERROR_MSG', { name: e.error.name, description: e.error.description, })); endDungeon('errorRequest', e); return; } battleResult = e.results[0].result.response; if ('error' in battleResult) { endDungeon('errorBattleResult', battleResult); return; } dungeonGetInfo = battleResult.dungeon ?? battleResult; dungeonActivity += battleResult.reward.dungeonActivity ?? 0; checkFloor(dungeonGetInfo); } /** * Returns the coefficient of condition of the * difference in titanium before and after the battle * * Возвращает коэффициент состояния титанов после боя */ function getState(result) { if (!result.result.win) { return -1000; } let beforeSumFactor = 0; const beforeTitans = result.battleData.attackers; for (let titanId in beforeTitans) { const titan = beforeTitans[titanId]; const state = titan.state; let factor = 1; if (state) { const hp = state.hp / titan.hp; const energy = state.energy / 1e3; factor = hp + energy / 20 } beforeSumFactor += factor; } let afterSumFactor = 0; const afterTitans = result.progress[0].attackers.heroes; for (let titanId in afterTitans) { const titan = afterTitans[titanId]; const hp = titan.hp / beforeTitans[titanId].hp; const energy = titan.energy / 1e3; const factor = hp + energy / 20; afterSumFactor += factor; } return afterSumFactor - beforeSumFactor; } /** * Converts an object with IDs to an array with IDs * * Преобразует объект с идетификаторами в массив с идетификаторами */ function titanObjToArray(obj) { let titans = []; for (let id in obj) { obj[id].id = id; titans.push(obj[id]); } return titans; } function saveProgress() { let saveProgressCall = { calls: [{ name: "dungeonSaveProgress", args: {}, ident: "body" }] } send(JSON.stringify(saveProgressCall), resultEndBattle); } function endDungeon(reason, info) { console.warn(reason, info); setProgress(`${I18N('DUNGEON')} ${I18N('COMPLETED')}`, true); resolve(); } } this.HWHClasses.executeDungeon = executeDungeon; /** * Passing the tower * * Прохождение башни */ function testTower() { const { executeTower } = HWHClasses; return new Promise((resolve, reject) => { tower = new executeTower(resolve, reject); tower.start(); }); } /** * Passing the tower * * Прохождение башни */ function executeTower(resolve, reject) { lastTowerInfo = {}; scullCoin = 0; heroGetAll = []; heroesStates = {}; argsBattle = { heroes: [], favor: {}, }; callsExecuteTower = { calls: [{ name: "towerGetInfo", args: {}, ident: "towerGetInfo" }, { name: "teamGetAll", args: {}, ident: "teamGetAll" }, { name: "teamGetFavor", args: {}, ident: "teamGetFavor" }, { name: "inventoryGet", args: {}, ident: "inventoryGet" }, { name: "heroGetAll", args: {}, ident: "heroGetAll" }] } buffIds = [ {id: 0, cost: 0, isBuy: false}, // plug // заглушка {id: 1, cost: 1, isBuy: true}, // 3% attack // 3% атака {id: 2, cost: 6, isBuy: true}, // 2% attack // 2% атака {id: 3, cost: 16, isBuy: true}, // 4% attack // 4% атака {id: 4, cost: 40, isBuy: true}, // 8% attack // 8% атака {id: 5, cost: 1, isBuy: true}, // 10% armor // 10% броня {id: 6, cost: 6, isBuy: true}, // 5% armor // 5% броня {id: 7, cost: 16, isBuy: true}, // 10% armor // 10% броня {id: 8, cost: 40, isBuy: true}, // 20% armor // 20% броня { id: 9, cost: 1, isBuy: true }, // 10% protection from magic // 10% защита от магии { id: 10, cost: 6, isBuy: true }, // 5% protection from magic // 5% защита от магии { id: 11, cost: 16, isBuy: true }, // 10% protection from magic // 10% защита от магии { id: 12, cost: 40, isBuy: true }, // 20% protection from magic // 20% защита от магии { id: 13, cost: 1, isBuy: false }, // 40% health hero // 40% здоровья герою { id: 14, cost: 6, isBuy: false }, // 40% health hero // 40% здоровья герою { id: 15, cost: 16, isBuy: false }, // 80% health hero // 80% здоровья герою { id: 16, cost: 40, isBuy: false }, // 40% health to all heroes // 40% здоровья всем героям { id: 17, cost: 1, isBuy: false }, // 40% energy to the hero // 40% энергии герою { id: 18, cost: 3, isBuy: false }, // 40% energy to the hero // 40% энергии герою { id: 19, cost: 8, isBuy: false }, // 80% energy to the hero // 80% энергии герою { id: 20, cost: 20, isBuy: false }, // 40% energy to all heroes // 40% энергии всем героям { id: 21, cost: 40, isBuy: false }, // Hero Resurrection // Воскрешение героя ] this.start = function () { send(JSON.stringify(callsExecuteTower), startTower); } /** * Getting data on the Tower * * Получаем данные по башне */ function startTower(e) { res = e.results; towerGetInfo = res[0].result.response; if (!towerGetInfo) { endTower('noTower', res); return; } teamGetAll = res[1].result.response; teamGetFavor = res[2].result.response; inventoryGet = res[3].result.response; heroGetAll = Object.values(res[4].result.response); scullCoin = inventoryGet.coin[7] ?? 0; argsBattle.favor = teamGetFavor.tower; argsBattle.heroes = heroGetAll.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id); pet = teamGetAll.tower.filter(id => id >= 6000).pop(); if (pet) { argsBattle.pet = pet; } checkFloor(towerGetInfo); } function fixHeroesTeam(argsBattle) { let fixHeroes = argsBattle.heroes.filter(e => !heroesStates[e]?.isDead); if (fixHeroes.length < 5) { heroGetAll = heroGetAll.filter(e => !heroesStates[e.id]?.isDead); fixHeroes = heroGetAll.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id); Object.keys(argsBattle.favor).forEach(e => { if (!fixHeroes.includes(+e)) { delete argsBattle.favor[e]; } }) } argsBattle.heroes = fixHeroes; return argsBattle; } /** * Check the floor * * Проверяем этаж */ function checkFloor(towerInfo) { lastTowerInfo = towerInfo; maySkipFloor = +towerInfo.maySkipFloor; floorNumber = +towerInfo.floorNumber; heroesStates = towerInfo.states.heroes; floorInfo = towerInfo.floor; /** * Is there at least one chest open on the floor * Открыт ли на этаже хоть один сундук */ isOpenChest = false; if (towerInfo.floorType == "chest") { isOpenChest = towerInfo.floor.chests.reduce((n, e) => n + e.opened, 0); } setProgress(`${I18N('TOWER')}: ${I18N('FLOOR')} ${floorNumber}`); if (floorNumber > 49) { if (isOpenChest) { endTower('alreadyOpenChest 50 floor', floorNumber); return; } } /** * If the chest is open and you can skip floors, then move on * Если сундук открыт и можно скипать этажи, то переходим дальше */ if (towerInfo.mayFullSkip && +towerInfo.teamLevel == 130) { if (floorNumber == 1) { fullSkipTower(); return; } if (isOpenChest) { nextOpenChest(floorNumber); } else { nextChestOpen(floorNumber); } return; } // console.log(towerInfo, scullCoin); switch (towerInfo.floorType) { case "battle": if (floorNumber <= maySkipFloor) { skipFloor(); return; } if (floorInfo.state == 2) { nextFloor(); return; } startBattle().then(endBattle); return; case "buff": checkBuff(towerInfo); return; case "chest": openChest(floorNumber); return; default: console.log('!', towerInfo.floorType, towerInfo); break; } } /** * Let's start the fight * * Начинаем бой */ function startBattle() { return new Promise(function (resolve, reject) { towerStartBattle = { calls: [{ name: "towerStartBattle", args: fixHeroesTeam(argsBattle), ident: "body" }] } send(JSON.stringify(towerStartBattle), resultBattle, resolve); }); } /** * Returns the result of the battle in a promise * * Возращает резульат боя в промис */ function resultBattle(resultBattles, resolve) { battleData = resultBattles.results[0].result.response; battleType = "get_tower"; BattleCalc(battleData, battleType, function (result) { resolve(result); }); } /** * Finishing the fight * * Заканчиваем бой */ function endBattle(battleInfo) { if (battleInfo.result.stars >= 3) { endBattleCall = { calls: [{ name: "towerEndBattle", args: { result: battleInfo.result, progress: battleInfo.progress, }, ident: "body" }] } send(JSON.stringify(endBattleCall), resultEndBattle); } else { endTower('towerEndBattle win: false\n', battleInfo); } } /** * Getting and processing battle results * * Получаем и обрабатываем результаты боя */ function resultEndBattle(e) { battleResult = e.results[0].result.response; if ('error' in battleResult) { endTower('errorBattleResult', battleResult); return; } if ('reward' in battleResult) { scullCoin += battleResult.reward?.coin[7] ?? 0; } nextFloor(); } function nextFloor() { nextFloorCall = { calls: [{ name: "towerNextFloor", args: {}, ident: "body" }] } send(JSON.stringify(nextFloorCall), checkDataFloor); } function openChest(floorNumber) { floorNumber = floorNumber || 0; openChestCall = { calls: [{ name: "towerOpenChest", args: { num: 2 }, ident: "body" }] } send(JSON.stringify(openChestCall), floorNumber < 50 ? nextFloor : lastChest); } function lastChest() { endTower('openChest 50 floor', floorNumber); } function skipFloor() { skipFloorCall = { calls: [{ name: "towerSkipFloor", args: {}, ident: "body" }] } send(JSON.stringify(skipFloorCall), checkDataFloor); } function checkBuff(towerInfo) { buffArr = towerInfo.floor; promises = []; for (let buff of buffArr) { buffInfo = buffIds[buff.id]; if (buffInfo.isBuy && buffInfo.cost <= scullCoin) { scullCoin -= buffInfo.cost; promises.push(buyBuff(buff.id)); } } Promise.all(promises).then(nextFloor); } function buyBuff(buffId) { return new Promise(function (resolve, reject) { buyBuffCall = { calls: [{ name: "towerBuyBuff", args: { buffId }, ident: "body" }] } send(JSON.stringify(buyBuffCall), resolve); }); } function checkDataFloor(result) { towerInfo = result.results[0].result.response; if ('reward' in towerInfo && towerInfo.reward?.coin) { scullCoin += towerInfo.reward?.coin[7] ?? 0; } if ('tower' in towerInfo) { towerInfo = towerInfo.tower; } if ('skullReward' in towerInfo) { scullCoin += towerInfo.skullReward?.coin[7] ?? 0; } checkFloor(towerInfo); } /** * Getting tower rewards * * Получаем награды башни */ function farmTowerRewards(reason) { let { pointRewards, points } = lastTowerInfo; let pointsAll = Object.getOwnPropertyNames(pointRewards); let farmPoints = pointsAll.filter(e => +e <= +points && !pointRewards[e]); if (!farmPoints.length) { return; } let farmTowerRewardsCall = { calls: [{ name: "tower_farmPointRewards", args: { points: farmPoints }, ident: "tower_farmPointRewards" }] } if (scullCoin > 0) { farmTowerRewardsCall.calls.push({ name: "tower_farmSkullReward", args: {}, ident: "tower_farmSkullReward" }); } send(JSON.stringify(farmTowerRewardsCall), () => { }); } function fullSkipTower() { /** * Next chest * * Следующий сундук */ function nextChest(n) { return { name: "towerNextChest", args: {}, ident: "group_" + n + "_body" } } /** * Open chest * * Открыть сундук */ function openChest(n) { return { name: "towerOpenChest", args: { "num": 2 }, ident: "group_" + n + "_body" } } const fullSkipTowerCall = { calls: [] } let n = 0; for (let i = 0; i < 15; i++) { // 15 сундуков fullSkipTowerCall.calls.push(nextChest(++n)); fullSkipTowerCall.calls.push(openChest(++n)); // +5 сундуков, 250 изюма // towerOpenChest // if (i < 5) { // fullSkipTowerCall.calls.push(openChest(++n, 2)); // } } fullSkipTowerCall.calls.push({ name: 'towerGetInfo', args: {}, ident: 'group_' + ++n + '_body', }); send(JSON.stringify(fullSkipTowerCall), data => { for (const r of data.results) { const towerInfo = r?.result?.response; if (towerInfo && 'skullReward' in towerInfo) { scullCoin += towerInfo.skullReward?.coin[7] ?? 0; } } data.results[0] = data.results[data.results.length - 1]; checkDataFloor(data); }); } function nextChestOpen(floorNumber) { const calls = [{ name: "towerOpenChest", args: { num: 2 }, ident: "towerOpenChest" }]; Send(JSON.stringify({ calls })).then(e => { nextOpenChest(floorNumber); }); } function nextOpenChest(floorNumber) { if (floorNumber > 49) { endTower('openChest 50 floor', floorNumber); return; } let nextOpenChestCall = { calls: [{ name: "towerNextChest", args: {}, ident: "towerNextChest" }, { name: "towerOpenChest", args: { num: 2 }, ident: "towerOpenChest" }] } send(JSON.stringify(nextOpenChestCall), checkDataFloor); } function endTower(reason, info) { console.log(reason, info); if (reason != 'noTower') { farmTowerRewards(reason); } setProgress(`${I18N('TOWER')} ${I18N('COMPLETED')}!`, true); resolve(); } } this.HWHClasses.executeTower = executeTower; /** * Passage of the arena of the titans * * Прохождение арены титанов */ function testTitanArena() { const { executeTitanArena } = HWHClasses; return new Promise((resolve, reject) => { titAren = new executeTitanArena(resolve, reject); titAren.start(); }); } /** * Passage of the arena of the titans * * Прохождение арены титанов */ function executeTitanArena(resolve, reject) { let titan_arena = []; let finishListBattle = []; /** * ID of the current batch * * Идетификатор текущей пачки */ let currentRival = 0; /** * Number of attempts to finish off the pack * * Количество попыток добития пачки */ let attempts = 0; /** * Was there an attempt to finish off the current shooting range * * Была ли попытка добития текущего тира */ let isCheckCurrentTier = false; /** * Current shooting range * * Текущий тир */ let currTier = 0; /** * Number of battles on the current dash * * Количество битв на текущем тире */ let countRivalsTier = 0; let callsStart = { calls: [{ name: "titanArenaGetStatus", args: {}, ident: "titanArenaGetStatus" }, { name: "teamGetAll", args: {}, ident: "teamGetAll" }] } this.start = function () { send(JSON.stringify(callsStart), startTitanArena); } function startTitanArena(data) { let titanArena = data.results[0].result.response; if (titanArena.status == 'disabled') { endTitanArena('disabled', titanArena); return; } let teamGetAll = data.results[1].result.response; titan_arena = teamGetAll.titan_arena; checkTier(titanArena) } function checkTier(titanArena) { if (titanArena.status == "peace_time") { endTitanArena('Peace_time', titanArena); return; } currTier = titanArena.tier; if (currTier) { setProgress(`${I18N('TITAN_ARENA')}: ${I18N('LEVEL')} ${currTier}`); } if (titanArena.status == "completed_tier") { titanArenaCompleteTier(); return; } /** * Checking for the possibility of a raid * Проверка на возможность рейда */ if (titanArena.canRaid) { titanArenaStartRaid(); return; } /** * Check was an attempt to achieve the current shooting range * Проверка была ли попытка добития текущего тира */ if (!isCheckCurrentTier) { checkRivals(titanArena.rivals); return; } endTitanArena('Done or not canRaid', titanArena); } /** * Submit dash information for verification * * Отправка информации о тире на проверку */ function checkResultInfo(data) { let titanArena = data.results[0].result.response; checkTier(titanArena); } /** * Finish the current tier * * Завершить текущий тир */ function titanArenaCompleteTier() { isCheckCurrentTier = false; let calls = [{ name: "titanArenaCompleteTier", args: {}, ident: "body" }]; send(JSON.stringify({calls}), checkResultInfo); } /** * Gathering points to be completed * * Собираем точки которые нужно добить */ function checkRivals(rivals) { finishListBattle = []; for (let n in rivals) { if (rivals[n].attackScore < 250) { finishListBattle.push(n); } } console.log('checkRivals', finishListBattle); countRivalsTier = finishListBattle.length; roundRivals(); } /** * Selecting the next point to finish off * * Выбор следующей точки для добития */ function roundRivals() { let countRivals = finishListBattle.length; if (!countRivals) { /** * Whole range checked * * Весь тир проверен */ isCheckCurrentTier = true; titanArenaGetStatus(); return; } // setProgress('TitanArena: Уровень ' + currTier + ' Бои: ' + (countRivalsTier - countRivals + 1) + '/' + countRivalsTier); currentRival = finishListBattle.pop(); attempts = +currentRival; // console.log('roundRivals', currentRival); titanArenaStartBattle(currentRival); } /** * The start of a solo battle * * Начало одиночной битвы */ function titanArenaStartBattle(rivalId) { let calls = [{ name: "titanArenaStartBattle", args: { rivalId: rivalId, titans: titan_arena }, ident: "body" }]; send(JSON.stringify({calls}), calcResult); } /** * Calculation of the results of the battle * * Расчет результатов боя */ function calcResult(data) { let battlesInfo = data.results[0].result.response.battle; /** * If attempts are equal to the current battle number we make * Если попытки равны номеру текущего боя делаем прерасчет */ if (attempts == currentRival) { preCalcBattle(battlesInfo); return; } /** * If there are still attempts, we calculate a new battle * Если попытки еще есть делаем расчет нового боя */ if (attempts > 0) { attempts--; calcBattleResult(battlesInfo) .then(resultCalcBattle); return; } /** * Otherwise, go to the next opponent * Иначе переходим к следующему сопернику */ roundRivals(); } /** * Processing the results of the battle calculation * * Обработка результатов расчета битвы */ function resultCalcBattle(resultBattle) { // console.log('resultCalcBattle', currentRival, attempts, resultBattle.result.win); /** * If the current calculation of victory is not a chance or the attempt ended with the finish the battle * Если текущий расчет победа или шансов нет или попытки кончились завершаем бой */ if (resultBattle.result.win || !attempts) { titanArenaEndBattle({ progress: resultBattle.progress, result: resultBattle.result, rivalId: resultBattle.battleData.typeId }); return; } /** * If not victory and there are attempts we start a new battle * Если не победа и есть попытки начинаем новый бой */ titanArenaStartBattle(resultBattle.battleData.typeId); } /** * Returns the promise of calculating the results of the battle * * Возращает промис расчета результатов битвы */ function getBattleInfo(battle, isRandSeed) { return new Promise(function (resolve) { if (isRandSeed) { battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3); } // console.log(battle.seed); BattleCalc(battle, "get_titanClanPvp", e => resolve(e)); }); } /** * Recalculate battles * * Прерасчтет битвы */ function preCalcBattle(battle) { let actions = [getBattleInfo(battle, false)]; const countTestBattle = getInput('countTestBattle'); for (let i = 0; i < countTestBattle; i++) { actions.push(getBattleInfo(battle, true)); } Promise.all(actions) .then(resultPreCalcBattle); } /** * Processing the results of the battle recalculation * * Обработка результатов прерасчета битвы */ function resultPreCalcBattle(e) { let wins = e.map(n => n.result.win); let firstBattle = e.shift(); let countWin = wins.reduce((w, s) => w + s); const countTestBattle = getInput('countTestBattle'); console.log('resultPreCalcBattle', `${countWin}/${countTestBattle}`) if (countWin > 0) { attempts = getInput('countAutoBattle'); } else { attempts = 0; } resultCalcBattle(firstBattle); } /** * Complete an arena battle * * Завершить битву на арене */ function titanArenaEndBattle(args) { let calls = [{ name: "titanArenaEndBattle", args, ident: "body" }]; send(JSON.stringify({calls}), resultTitanArenaEndBattle); } function resultTitanArenaEndBattle(e) { let attackScore = e.results[0].result.response.attackScore; let numReval = countRivalsTier - finishListBattle.length; setProgress(`${I18N('TITAN_ARENA')}: ${I18N('LEVEL')} ${currTier} </br>${I18N('BATTLES')}: ${numReval}/${countRivalsTier} - ${attackScore}`); /** * TODO: Might need to improve the results. * TODO: Возможно стоит сделать улучшение результатов */ // console.log('resultTitanArenaEndBattle', e) console.log('resultTitanArenaEndBattle', numReval + '/' + countRivalsTier, attempts) roundRivals(); } /** * Arena State * * Состояние арены */ function titanArenaGetStatus() { let calls = [{ name: "titanArenaGetStatus", args: {}, ident: "body" }]; send(JSON.stringify({calls}), checkResultInfo); } /** * Arena Raid Request * * Запрос рейда арены */ function titanArenaStartRaid() { let calls = [{ name: "titanArenaStartRaid", args: { titans: titan_arena }, ident: "body" }]; send(JSON.stringify({calls}), calcResults); } function calcResults(data) { let battlesInfo = data.results[0].result.response; let {attackers, rivals} = battlesInfo; let promises = []; for (let n in rivals) { rival = rivals[n]; promises.push(calcBattleResult({ attackers: attackers, defenders: [rival.team], seed: rival.seed, typeId: n, })); } Promise.all(promises) .then(results => { const endResults = {}; for (let info of results) { let id = info.battleData.typeId; endResults[id] = { progress: info.progress, result: info.result, } } titanArenaEndRaid(endResults); }); } function calcBattleResult(battleData) { return new Promise(function (resolve, reject) { BattleCalc(battleData, "get_titanClanPvp", resolve); }); } /** * Sending Raid Results * * Отправка результатов рейда */ function titanArenaEndRaid(results) { titanArenaEndRaidCall = { calls: [{ name: "titanArenaEndRaid", args: { results }, ident: "body" }] } send(JSON.stringify(titanArenaEndRaidCall), checkRaidResults); } function checkRaidResults(data) { results = data.results[0].result.response.results; isSucsesRaid = true; for (let i in results) { isSucsesRaid &&= (results[i].attackScore >= 250); } if (isSucsesRaid) { titanArenaCompleteTier(); } else { titanArenaGetStatus(); } } function titanArenaFarmDailyReward() { titanArenaFarmDailyRewardCall = { calls: [{ name: "titanArenaFarmDailyReward", args: {}, ident: "body" }] } send(JSON.stringify(titanArenaFarmDailyRewardCall), () => {console.log('Done farm daily reward')}); } function endTitanArena(reason, info) { if (!['Peace_time', 'disabled'].includes(reason)) { titanArenaFarmDailyReward(); } console.log(reason, info); setProgress(`${I18N('TITAN_ARENA')} ${I18N('COMPLETED')}!`, true); resolve(); } } this.HWHClasses.executeTitanArena = executeTitanArena; function hackGame() { const self = this; selfGame = null; bindId = 1e9; this.libGame = null; this.doneLibLoad = () => {}; /** * List of correspondence of used classes to their names * * Список соответствия используемых классов их названиям */ ObjectsList = [ { name: 'BattlePresets', prop: 'game.battle.controller.thread.BattlePresets' }, { name: 'DataStorage', prop: 'game.data.storage.DataStorage' }, { name: 'BattleConfigStorage', prop: 'game.data.storage.battle.BattleConfigStorage' }, { name: 'BattleInstantPlay', prop: 'game.battle.controller.instant.BattleInstantPlay' }, { name: 'MultiBattleInstantReplay', prop: 'game.battle.controller.instant.MultiBattleInstantReplay' }, { name: 'MultiBattleResult', prop: 'game.battle.controller.MultiBattleResult' }, { name: 'PlayerMissionData', prop: 'game.model.user.mission.PlayerMissionData' }, { name: 'PlayerMissionBattle', prop: 'game.model.user.mission.PlayerMissionBattle' }, { name: 'GameModel', prop: 'game.model.GameModel' }, { name: 'CommandManager', prop: 'game.command.CommandManager' }, { name: 'MissionCommandList', prop: 'game.command.rpc.mission.MissionCommandList' }, { name: 'RPCCommandBase', prop: 'game.command.rpc.RPCCommandBase' }, { name: 'PlayerTowerData', prop: 'game.model.user.tower.PlayerTowerData' }, { name: 'TowerCommandList', prop: 'game.command.tower.TowerCommandList' }, { name: 'PlayerHeroTeamResolver', prop: 'game.model.user.hero.PlayerHeroTeamResolver' }, { name: 'BattlePausePopup', prop: 'game.view.popup.battle.BattlePausePopup' }, { name: 'BattlePopup', prop: 'game.view.popup.battle.BattlePopup' }, { name: 'DisplayObjectContainer', prop: 'starling.display.DisplayObjectContainer' }, { name: 'GuiClipContainer', prop: 'engine.core.clipgui.GuiClipContainer' }, { name: 'BattlePausePopupClip', prop: 'game.view.popup.battle.BattlePausePopupClip' }, { name: 'ClipLabel', prop: 'game.view.gui.components.ClipLabel' }, { name: 'ClipLabelBase', prop: 'game.view.gui.components.ClipLabelBase' }, { name: 'Translate', prop: 'com.progrestar.common.lang.Translate' }, { name: 'ClipButtonLabeledCentered', prop: 'game.view.gui.components.ClipButtonLabeledCentered' }, { name: 'BattlePausePopupMediator', prop: 'game.mediator.gui.popup.battle.BattlePausePopupMediator' }, { name: 'SettingToggleButton', prop: 'game.mechanics.settings.popup.view.SettingToggleButton' }, { name: 'PlayerDungeonData', prop: 'game.mechanics.dungeon.model.PlayerDungeonData' }, { name: 'NextDayUpdatedManager', prop: 'game.model.user.NextDayUpdatedManager' }, { name: 'BattleController', prop: 'game.battle.controller.BattleController' }, { name: 'BattleSettingsModel', prop: 'game.battle.controller.BattleSettingsModel' }, { name: 'BooleanProperty', prop: 'engine.core.utils.property.BooleanProperty' }, { name: 'RuleStorage', prop: 'game.data.storage.rule.RuleStorage' }, { name: 'BattleConfig', prop: 'battle.BattleConfig' }, { name: 'BattleGuiMediator', prop: 'game.battle.gui.BattleGuiMediator' }, { name: 'BooleanPropertyWriteable', prop: 'engine.core.utils.property.BooleanPropertyWriteable' }, { name: 'BattleLogEncoder', prop: 'battle.log.BattleLogEncoder' }, { name: 'BattleLogReader', prop: 'battle.log.BattleLogReader' }, { name: 'PlayerSubscriptionInfoValueObject', prop: 'game.model.user.subscription.PlayerSubscriptionInfoValueObject' }, { name: 'AdventureMapCamera', prop: 'game.mechanics.adventure.popup.map.AdventureMapCamera' }, ]; /** * Contains the game classes needed to write and override game methods * * Содержит классы игры необходимые для написания и подмены методов игры */ Game = { /** * Function 'e' * Функция 'e' */ bindFunc: function (a, b) { if (null == b) return null; null == b.__id__ && (b.__id__ = bindId++); var c; null == a.hx__closures__ ? (a.hx__closures__ = {}) : (c = a.hx__closures__[b.__id__]); null == c && ((c = b.bind(a)), (a.hx__closures__[b.__id__] = c)); return c; }, }; /** * Connects to game objects via the object creation event * * Подключается к объектам игры через событие создания объекта */ function connectGame() { for (let obj of ObjectsList) { /** * https: //stackoverflow.com/questions/42611719/how-to-intercept-and-modify-a-specific-property-for-any-object */ Object.defineProperty(Object.prototype, obj.prop, { set: function (value) { if (!selfGame) { selfGame = this; } if (!Game[obj.name]) { Game[obj.name] = value; } // console.log('set ' + obj.prop, this, value); this[obj.prop + '_'] = value; }, get: function () { // console.log('get ' + obj.prop, this); return this[obj.prop + '_']; }, }); } } /** * Game.BattlePresets * @param {bool} a isReplay * @param {bool} b autoToggleable * @param {bool} c auto On Start * @param {object} d config * @param {bool} f showBothTeams */ /** * Returns the results of the battle to the callback function * Возвращает в функцию callback результаты боя * @param {*} battleData battle data данные боя * @param {*} battleConfig combat configuration type options: * * тип конфигурации боя варианты: * * "get_invasion", "get_titanPvpManual", "get_titanPvp", * "get_titanClanPvp","get_clanPvp","get_titan","get_boss", * "get_tower","get_pve","get_pvpManual","get_pvp","get_core" * * You can specify the xYc function in the game.assets.storage.BattleAssetStorage class * * Можно уточнить в классе game.assets.storage.BattleAssetStorage функция xYc * @param {*} callback функция в которую вернуться результаты боя */ this.BattleCalc = function (battleData, battleConfig, callback) { // battleConfig = battleConfig || getBattleType(battleData.type) if (!Game.BattlePresets) throw Error('Use connectGame'); battlePresets = new Game.BattlePresets( battleData.progress, !1, !0, Game.DataStorage[getFn(Game.DataStorage, 24)][getF(Game.BattleConfigStorage, battleConfig)](), !1 ); let battleInstantPlay; if (battleData.progress?.length > 1) { battleInstantPlay = new Game.MultiBattleInstantReplay(battleData, battlePresets); } else { battleInstantPlay = new Game.BattleInstantPlay(battleData, battlePresets); } battleInstantPlay[getProtoFn(Game.BattleInstantPlay, 9)].add((battleInstant) => { const MBR_2 = getProtoFn(Game.MultiBattleResult, 2); const battleResults = battleInstant[getF(Game.BattleInstantPlay, 'get_result')](); const battleData = battleInstant[getF(Game.BattleInstantPlay, 'get_rawBattleInfo')](); const battleLogs = []; const timeLimit = battlePresets[getF(Game.BattlePresets, 'get_timeLimit')](); let battleTime = 0; let battleTimer = 0; for (const battleResult of battleResults[MBR_2]) { const battleLog = Game.BattleLogEncoder.read(new Game.BattleLogReader(battleResult)); battleLogs.push(battleLog); const maxTime = Math.max(...battleLog.map((e) => (e.time < timeLimit && e.time !== 168.8 ? e.time : 0))); battleTimer += getTimer(maxTime); battleTime += maxTime; } callback({ battleLogs, battleTime, battleTimer, battleData, progress: battleResults[getF(Game.MultiBattleResult, 'get_progress')](), result: battleResults[getF(Game.MultiBattleResult, 'get_result')](), }); }); battleInstantPlay.start(); }; /** * Returns a function with the specified name from the class * * Возвращает из класса функцию с указанным именем * @param {Object} classF Class // класс * @param {String} nameF function name // имя функции * @param {String} pos name and alias order // порядок имени и псевдонима * @returns */ function getF(classF, nameF, pos) { pos = pos || false; let prop = Object.entries(classF.prototype.__properties__); if (!pos) { return prop.filter((e) => e[1] == nameF).pop()[0]; } else { return prop.filter((e) => e[0] == nameF).pop()[1]; } } /** * Returns a function with the specified name from the class * * Возвращает из класса функцию с указанным именем * @param {Object} classF Class // класс * @param {String} nameF function name // имя функции * @returns */ function getFnP(classF, nameF) { let prop = Object.entries(classF.__properties__); return prop.filter((e) => e[1] == nameF).pop()[0]; } /** * Returns the function name with the specified ordinal from the class * * Возвращает имя функции с указаным порядковым номером из класса * @param {Object} classF Class // класс * @param {Number} nF Order number of function // порядковый номер функции * @returns */ function getFn(classF, nF) { let prop = Object.keys(classF); return prop[nF]; } /** * Returns the name of the function with the specified serial number from the prototype of the class * * Возвращает имя функции с указаным порядковым номером из прототипа класса * @param {Object} classF Class // класс * @param {Number} nF Order number of function // порядковый номер функции * @returns */ function getProtoFn(classF, nF) { let prop = Object.keys(classF.prototype); return prop[nF]; } /** * Description of replaced functions * * Описание подменяемых функций */ replaceFunction = { company: function () { let PMD_12 = getProtoFn(Game.PlayerMissionData, 12); let oldSkipMisson = Game.PlayerMissionData.prototype[PMD_12]; Game.PlayerMissionData.prototype[PMD_12] = function (a, b, c) { if (!isChecked('passBattle')) { oldSkipMisson.call(this, a, b, c); return; } try { this[getProtoFn(Game.PlayerMissionData, 9)] = new Game.PlayerMissionBattle(a, b, c); var a = new Game.BattlePresets( !1, !1, !0, Game.DataStorage[getFn(Game.DataStorage, 24)][getProtoFn(Game.BattleConfigStorage, 20)](), !1 ); a = new Game.BattleInstantPlay(c, a); a[getProtoFn(Game.BattleInstantPlay, 9)].add(Game.bindFunc(this, this.P$h)); a.start(); } catch (error) { console.error('company', error); oldSkipMisson.call(this, a, b, c); } }; Game.PlayerMissionData.prototype.P$h = function (a) { let GM_2 = getFn(Game.GameModel, 2); let GM_P2 = getProtoFn(Game.GameModel, 2); let CM_20 = getProtoFn(Game.CommandManager, 20); let MCL_2 = getProtoFn(Game.MissionCommandList, 2); let MBR_15 = getF(Game.MultiBattleResult, 'get_result'); let RPCCB_15 = getProtoFn(Game.RPCCommandBase, 16); let PMD_32 = getProtoFn(Game.PlayerMissionData, 32); Game.GameModel[GM_2]()[GM_P2][CM_20][MCL_2](a[MBR_15]())[RPCCB_15](Game.bindFunc(this, this[PMD_32])); }; }, /* tower: function () { let PTD_67 = getProtoFn(Game.PlayerTowerData, 67); let oldSkipTower = Game.PlayerTowerData.prototype[PTD_67]; Game.PlayerTowerData.prototype[PTD_67] = function (a) { if (!isChecked('passBattle')) { oldSkipTower.call(this, a); return; } try { var p = new Game.BattlePresets( !1, !1, !0, Game.DataStorage[getFn(Game.DataStorage, 24)][getProtoFn(Game.BattleConfigStorage, 20)](), !1 ); a = new Game.BattleInstantPlay(a, p); a[getProtoFn(Game.BattleInstantPlay, 9)].add(Game.bindFunc(this, this.P$h)); a.start(); } catch (error) { console.error('tower', error); oldSkipMisson.call(this, a, b, c); } }; Game.PlayerTowerData.prototype.P$h = function (a) { const GM_2 = getFnP(Game.GameModel, 'get_instance'); const GM_P2 = getProtoFn(Game.GameModel, 2); const CM_29 = getProtoFn(Game.CommandManager, 29); const TCL_5 = getProtoFn(Game.TowerCommandList, 5); const MBR_15 = getF(Game.MultiBattleResult, 'get_result'); const RPCCB_15 = getProtoFn(Game.RPCCommandBase, 17); const PTD_78 = getProtoFn(Game.PlayerTowerData, 78); Game.GameModel[GM_2]()[GM_P2][CM_29][TCL_5](a[MBR_15]())[RPCCB_15](Game.bindFunc(this, this[PTD_78])); }; }, */ // skipSelectHero: function() { // if (!HOST) throw Error('Use connectGame'); // Game.PlayerHeroTeamResolver.prototype[getProtoFn(Game.PlayerHeroTeamResolver, 3)] = () => false; // }, passBattle: function () { let BPP_4 = getProtoFn(Game.BattlePausePopup, 4); let oldPassBattle = Game.BattlePausePopup.prototype[BPP_4]; Game.BattlePausePopup.prototype[BPP_4] = function (a) { if (!isChecked('passBattle')) { oldPassBattle.call(this, a); return; } try { Game.BattlePopup.prototype[getProtoFn(Game.BattlePausePopup, 4)].call(this, a); this[getProtoFn(Game.BattlePausePopup, 3)](); this[getProtoFn(Game.DisplayObjectContainer, 3)](this.clip[getProtoFn(Game.GuiClipContainer, 2)]()); this.clip[getProtoFn(Game.BattlePausePopupClip, 1)][getProtoFn(Game.ClipLabelBase, 9)]( Game.Translate.translate('UI_POPUP_BATTLE_PAUSE') ); this.clip[getProtoFn(Game.BattlePausePopupClip, 2)][getProtoFn(Game.ClipButtonLabeledCentered, 2)]( Game.Translate.translate('UI_POPUP_BATTLE_RETREAT'), ((q = this[getProtoFn(Game.BattlePausePopup, 1)]), Game.bindFunc(q, q[getProtoFn(Game.BattlePausePopupMediator, 17)])) ); this.clip[getProtoFn(Game.BattlePausePopupClip, 5)][getProtoFn(Game.ClipButtonLabeledCentered, 2)]( this[getProtoFn(Game.BattlePausePopup, 1)][getProtoFn(Game.BattlePausePopupMediator, 14)](), this[getProtoFn(Game.BattlePausePopup, 1)][getProtoFn(Game.BattlePausePopupMediator, 13)]() ? ((q = this[getProtoFn(Game.BattlePausePopup, 1)]), Game.bindFunc(q, q[getProtoFn(Game.BattlePausePopupMediator, 18)])) : ((q = this[getProtoFn(Game.BattlePausePopup, 1)]), Game.bindFunc(q, q[getProtoFn(Game.BattlePausePopupMediator, 18)])) ); this.clip[getProtoFn(Game.BattlePausePopupClip, 5)][getProtoFn(Game.ClipButtonLabeledCentered, 0)][ getProtoFn(Game.ClipLabelBase, 24) ](); this.clip[getProtoFn(Game.BattlePausePopupClip, 3)][getProtoFn(Game.SettingToggleButton, 3)]( this[getProtoFn(Game.BattlePausePopup, 1)][getProtoFn(Game.BattlePausePopupMediator, 9)]() ); this.clip[getProtoFn(Game.BattlePausePopupClip, 4)][getProtoFn(Game.SettingToggleButton, 3)]( this[getProtoFn(Game.BattlePausePopup, 1)][getProtoFn(Game.BattlePausePopupMediator, 10)]() ); this.clip[getProtoFn(Game.BattlePausePopupClip, 6)][getProtoFn(Game.SettingToggleButton, 3)]( this[getProtoFn(Game.BattlePausePopup, 1)][getProtoFn(Game.BattlePausePopupMediator, 11)]() ); } catch (error) { console.error('passBattle', error); oldPassBattle.call(this, a); } }; let retreatButtonLabel = getF(Game.BattlePausePopupMediator, 'get_retreatButtonLabel'); let oldFunc = Game.BattlePausePopupMediator.prototype[retreatButtonLabel]; Game.BattlePausePopupMediator.prototype[retreatButtonLabel] = function () { if (isChecked('passBattle')) { return I18N('BTN_PASS'); } else { return oldFunc.call(this); } }; }, endlessCards: function () { let PDD_21 = getProtoFn(Game.PlayerDungeonData, 21); let oldEndlessCards = Game.PlayerDungeonData.prototype[PDD_21]; Game.PlayerDungeonData.prototype[PDD_21] = function () { if (countPredictionCard <= 0) { return true; } else { return oldEndlessCards.call(this); } }; }, speedBattle: function () { const get_timeScale = getF(Game.BattleController, 'get_timeScale'); const oldSpeedBattle = Game.BattleController.prototype[get_timeScale]; Game.BattleController.prototype[get_timeScale] = function () { const speedBattle = Number.parseFloat(getInput('speedBattle')); if (!speedBattle) { return oldSpeedBattle.call(this); } try { const BC_12 = getProtoFn(Game.BattleController, 12); const BSM_12 = getProtoFn(Game.BattleSettingsModel, 12); const BP_get_value = getF(Game.BooleanProperty, 'get_value'); if (this[BC_12][BSM_12][BP_get_value]()) { return 0; } const BSM_2 = getProtoFn(Game.BattleSettingsModel, 2); const BC_49 = getProtoFn(Game.BattleController, 49); const BSM_1 = getProtoFn(Game.BattleSettingsModel, 1); const BC_14 = getProtoFn(Game.BattleController, 14); const BC_3 = getFn(Game.BattleController, 3); if (this[BC_12][BSM_2][BP_get_value]()) { var a = speedBattle * this[BC_49](); } else { a = this[BC_12][BSM_1][BP_get_value](); const maxSpeed = Math.max(...this[BC_14]); const multiple = a == this[BC_14].indexOf(maxSpeed) ? (maxSpeed >= 4 ? speedBattle : this[BC_14][a]) : this[BC_14][a]; a = multiple * Game.BattleController[BC_3][BP_get_value]() * this[BC_49](); } const BSM_24 = getProtoFn(Game.BattleSettingsModel, 24); a > this[BC_12][BSM_24][BP_get_value]() && (a = this[BC_12][BSM_24][BP_get_value]()); const DS_23 = getFn(Game.DataStorage, 23); const get_battleSpeedMultiplier = getF(Game.RuleStorage, 'get_battleSpeedMultiplier', true); var b = Game.DataStorage[DS_23][get_battleSpeedMultiplier](); const R_1 = getFn(selfGame.Reflect, 1); const BC_1 = getFn(Game.BattleController, 1); const get_config = getF(Game.BattlePresets, 'get_config'); null != b && (a = selfGame.Reflect[R_1](b, this[BC_1][get_config]().ident) ? a * selfGame.Reflect[R_1](b, this[BC_1][get_config]().ident) : a * selfGame.Reflect[R_1](b, 'default')); return a; } catch (error) { console.error('passBatspeedBattletle', error); return oldSpeedBattle.call(this); } }; }, /** * Acceleration button without Valkyries favor * * Кнопка ускорения без Покровительства Валькирий */ battleFastKey: function () { const BGM_44 = getProtoFn(Game.BattleGuiMediator, 44); const oldBattleFastKey = Game.BattleGuiMediator.prototype[BGM_44]; Game.BattleGuiMediator.prototype[BGM_44] = function () { let flag = true; //console.log(flag) if (!flag) { return oldBattleFastKey.call(this); } try { const BGM_9 = getProtoFn(Game.BattleGuiMediator, 9); const BGM_10 = getProtoFn(Game.BattleGuiMediator, 10); const BPW_0 = getProtoFn(Game.BooleanPropertyWriteable, 0); this[BGM_9][BPW_0](true); this[BGM_10][BPW_0](true); } catch (error) { console.error(error); return oldBattleFastKey.call(this); } }; }, fastSeason: function () { const GameNavigator = selfGame['game.screen.navigator.GameNavigator']; const oldFuncName = getProtoFn(GameNavigator, 18); const newFuncName = getProtoFn(GameNavigator, 16); const oldFastSeason = GameNavigator.prototype[oldFuncName]; const newFastSeason = GameNavigator.prototype[newFuncName]; GameNavigator.prototype[oldFuncName] = function (a, b) { if (isChecked('fastSeason')) { return newFastSeason.apply(this, [a]); } else { return oldFastSeason.apply(this, [a, b]); } }; }, ShowChestReward: function () { const TitanArtifactChest = selfGame['game.mechanics.titan_arena.mediator.chest.TitanArtifactChestRewardPopupMediator']; const getOpenAmountTitan = getF(TitanArtifactChest, 'get_openAmount'); const oldGetOpenAmountTitan = TitanArtifactChest.prototype[getOpenAmountTitan]; TitanArtifactChest.prototype[getOpenAmountTitan] = function () { if (correctShowOpenArtifact) { correctShowOpenArtifact--; return 100; } return oldGetOpenAmountTitan.call(this); }; const ArtifactChest = selfGame['game.view.popup.artifactchest.rewardpopup.ArtifactChestRewardPopupMediator']; const getOpenAmount = getF(ArtifactChest, 'get_openAmount'); const oldGetOpenAmount = ArtifactChest.prototype[getOpenAmount]; ArtifactChest.prototype[getOpenAmount] = function () { if (correctShowOpenArtifact) { correctShowOpenArtifact--; return 100; } return oldGetOpenAmount.call(this); }; }, fixCompany: function () { const GameBattleView = selfGame['game.mediator.gui.popup.battle.GameBattleView']; const BattleThread = selfGame['game.battle.controller.thread.BattleThread']; const getOnViewDisposed = getF(BattleThread, 'get_onViewDisposed'); const getThread = getF(GameBattleView, 'get_thread'); const oldFunc = GameBattleView.prototype[getThread]; GameBattleView.prototype[getThread] = function () { return ( oldFunc.call(this) || { [getOnViewDisposed]: async () => {}, } ); }; }, BuyTitanArtifact: function () { const BIP_4 = getProtoFn(selfGame['game.view.popup.shop.buy.BuyItemPopup'], 4); const BuyItemPopup = selfGame['game.view.popup.shop.buy.BuyItemPopup']; const oldFunc = BuyItemPopup.prototype[BIP_4]; BuyItemPopup.prototype[BIP_4] = function () { if (isChecked('countControl')) { const BuyTitanArtifactItemPopup = selfGame['game.view.popup.shop.buy.BuyTitanArtifactItemPopup']; const BTAP_0 = getProtoFn(BuyTitanArtifactItemPopup, 0); if (this[BTAP_0]) { const BuyTitanArtifactPopupMediator = selfGame['game.mediator.gui.popup.shop.buy.BuyTitanArtifactItemPopupMediator']; const BTAM_1 = getProtoFn(BuyTitanArtifactPopupMediator, 1); const BuyItemPopupMediator = selfGame['game.mediator.gui.popup.shop.buy.BuyItemPopupMediator']; const BIPM_5 = getProtoFn(BuyItemPopupMediator, 5); const BIPM_7 = getProtoFn(BuyItemPopupMediator, 7); const BIPM_9 = getProtoFn(BuyItemPopupMediator, 9); let need = Math.min(this[BTAP_0][BTAM_1](), this[BTAP_0][BIPM_7]); need = need ? need : 60; this[BTAP_0][BIPM_9] = need; this[BTAP_0][BIPM_5] = 10; } } oldFunc.call(this); }; }, ClanQuestsFastFarm: function () { const VipRuleValueObject = selfGame['game.data.storage.rule.VipRuleValueObject']; const getClanQuestsFastFarm = getF(VipRuleValueObject, 'get_clanQuestsFastFarm', 1); VipRuleValueObject.prototype[getClanQuestsFastFarm] = function () { return 0; }; }, adventureCamera: function () { const AMC_40 = getProtoFn(Game.AdventureMapCamera, 40); const AMC_5 = getProtoFn(Game.AdventureMapCamera, 5); const oldFunc = Game.AdventureMapCamera.prototype[AMC_40]; Game.AdventureMapCamera.prototype[AMC_40] = function (a) { this[AMC_5] = 0.4; oldFunc.bind(this)(a); }; }, unlockMission: function () { const WorldMapStoryDrommerHelper = selfGame['game.mediator.gui.worldmap.WorldMapStoryDrommerHelper']; const WMSDH_4 = getFn(WorldMapStoryDrommerHelper, 4); const WMSDH_7 = getFn(WorldMapStoryDrommerHelper, 7); WorldMapStoryDrommerHelper[WMSDH_4] = function () { return true; }; WorldMapStoryDrommerHelper[WMSDH_7] = function () { return true; }; }, }; /** * Starts replacing recorded functions * * Запускает замену записанных функций */ this.activateHacks = function () { if (!selfGame) throw Error('Use connectGame'); for (let func in replaceFunction) { try { replaceFunction[func](); } catch (error) { console.error(error); } } }; /** * Returns the game object * * Возвращает объект игры */ this.getSelfGame = function () { return selfGame; }; /** * Updates game data * * Обновляет данные игры */ this.refreshGame = function () { new Game.NextDayUpdatedManager()[getProtoFn(Game.NextDayUpdatedManager, 5)](); try { cheats.refreshInventory(); } catch (e) {} }; /** * Update inventory * * Обновляет инвентарь */ this.refreshInventory = async function () { const GM_INST = getFnP(Game.GameModel, 'get_instance'); const GM_0 = getProtoFn(Game.GameModel, 0); const P_24 = getProtoFn(selfGame['game.model.user.Player'], 24); const Player = Game.GameModel[GM_INST]()[GM_0]; Player[P_24] = new selfGame['game.model.user.inventory.PlayerInventory'](); Player[P_24].init(await Send({ calls: [{ name: 'inventoryGet', args: {}, ident: 'body' }] }).then((e) => e.results[0].result.response)); }; this.updateInventory = function (reward) { const GM_INST = getFnP(Game.GameModel, 'get_instance'); const GM_0 = getProtoFn(Game.GameModel, 0); const P_24 = getProtoFn(selfGame['game.model.user.Player'], 24); const Player = Game.GameModel[GM_INST]()[GM_0]; Player[P_24].init(reward); }; this.updateMap = function (data) { const PCDD_21 = getProtoFn(selfGame['game.mechanics.clanDomination.model.PlayerClanDominationData'], 21); const P_60 = getProtoFn(selfGame['game.model.user.Player'], 60); const GM_0 = getProtoFn(Game.GameModel, 0); const getInstance = getFnP(selfGame['Game'], 'get_instance'); const PlayerClanDominationData = Game.GameModel[getInstance]()[GM_0]; PlayerClanDominationData[P_60][PCDD_21].update(data); }; /** * Change the play screen on windowName * * Сменить экран игры на windowName * * Possible options: * * Возможные варианты: * * MISSION, ARENA, GRAND, CHEST, SKILLS, SOCIAL_GIFT, CLAN, ENCHANT, TOWER, RATING, CHALLENGE, BOSS, CHAT, CLAN_DUNGEON, CLAN_CHEST, TITAN_GIFT, CLAN_RAID, ASGARD, HERO_ASCENSION, ROLE_ASCENSION, ASCENSION_CHEST, TITAN_MISSION, TITAN_ARENA, TITAN_ARTIFACT, TITAN_ARTIFACT_CHEST, TITAN_VALLEY, TITAN_SPIRITS, TITAN_ARTIFACT_MERCHANT, TITAN_ARENA_HALL_OF_FAME, CLAN_PVP, CLAN_PVP_MERCHANT, CLAN_GLOBAL_PVP, CLAN_GLOBAL_PVP_TITAN, ARTIFACT, ZEPPELIN, ARTIFACT_CHEST, ARTIFACT_MERCHANT, EXPEDITIONS, SUBSCRIPTION, NY2018_GIFTS, NY2018_TREE, NY2018_WELCOME, ADVENTURE, ADVENTURESOLO, SANCTUARY, PET_MERCHANT, PET_LIST, PET_SUMMON, BOSS_RATING_EVENT, BRAWL */ this.goNavigtor = function (windowName) { let mechanicStorage = selfGame['game.data.storage.mechanic.MechanicStorage']; let window = mechanicStorage[windowName]; let event = new selfGame['game.mediator.gui.popup.PopupStashEventParams'](); let Game = selfGame['Game']; let navigator = getF(Game, 'get_navigator'); let navigate = getProtoFn(selfGame['game.screen.navigator.GameNavigator'], 20); let instance = getFnP(Game, 'get_instance'); Game[instance]()[navigator]()[navigate](window, event); }; /** * Move to the sanctuary cheats.goSanctuary() * * Переместиться в святилище cheats.goSanctuary() */ this.goSanctuary = () => { this.goNavigtor('SANCTUARY'); }; /** Перейти в Долину титанов */ this.goTitanValley = () => { this.goNavigtor('TITAN_VALLEY'); }; /** * Go to Guild War * * Перейти к Войне Гильдий */ this.goClanWar = function () { let instance = getFnP(Game.GameModel, 'get_instance'); let player = Game.GameModel[instance]().A; let clanWarSelect = selfGame['game.mechanics.cross_clan_war.popup.selectMode.CrossClanWarSelectModeMediator']; new clanWarSelect(player).open(); }; /** Перейти к Острову гильдии */ this.goClanIsland = function () { let instance = getFnP(Game.GameModel, 'get_instance'); let player = Game.GameModel[instance]().A; let clanIslandSelect = selfGame['game.view.gui.ClanIslandPopupMediator']; new clanIslandSelect(player).open(); }; /** * Go to BrawlShop * * Переместиться в BrawlShop */ this.goBrawlShop = () => { const instance = getFnP(Game.GameModel, 'get_instance'); const P_36 = getProtoFn(selfGame['game.model.user.Player'], 36); const PSD_0 = getProtoFn(selfGame['game.model.user.shop.PlayerShopData'], 0); const IM_0 = getProtoFn(selfGame['haxe.ds.IntMap'], 0); const PSDE_4 = getProtoFn(selfGame['game.model.user.shop.PlayerShopDataEntry'], 4); const player = Game.GameModel[instance]().A; const shop = player[P_36][PSD_0][IM_0][1038][PSDE_4]; const shopPopup = new selfGame['game.mechanics.brawl.mediator.BrawlShopPopupMediator'](player, shop); shopPopup.open(new selfGame['game.mediator.gui.popup.PopupStashEventParams']()); }; /** * Returns all stores from game data * * Возвращает все магазины из данных игры */ this.getShops = () => { const instance = getFnP(Game.GameModel, 'get_instance'); const P_36 = getProtoFn(selfGame['game.model.user.Player'], 36); const PSD_0 = getProtoFn(selfGame['game.model.user.shop.PlayerShopData'], 0); const IM_0 = getProtoFn(selfGame['haxe.ds.IntMap'], 0); const player = Game.GameModel[instance]().A; return player[P_36][PSD_0][IM_0]; }; /** * Returns the store from the game data by ID * * Возвращает магазин из данных игры по идетификатору */ this.getShop = (id) => { const PSDE_4 = getProtoFn(selfGame['game.model.user.shop.PlayerShopDataEntry'], 4); const shops = this.getShops(); const shop = shops[id]?.[PSDE_4]; return shop; }; /** * Change island map * * Сменить карту острова */ this.changeIslandMap = (mapId = 2) => { const GameInst = getFnP(selfGame['Game'], 'get_instance'); const GM_0 = getProtoFn(Game.GameModel, 0); const P_59 = getProtoFn(selfGame['game.model.user.Player'], 60); const PSAD_31 = getProtoFn(selfGame['game.mechanics.season_adventure.model.PlayerSeasonAdventureData'], 31); const Player = Game.GameModel[GameInst]()[GM_0]; Player[P_59][PSAD_31]({ id: mapId, seasonAdventure: { id: mapId, startDate: 1701914400, endDate: 1709690400, closed: false } }); const GN_15 = getProtoFn(selfGame['game.screen.navigator.GameNavigator'], 17); const navigator = getF(selfGame['Game'], 'get_navigator'); selfGame['Game'][GameInst]()[navigator]()[GN_15](new selfGame['game.mediator.gui.popup.PopupStashEventParams']()); }; /** * Game library availability tracker * * Отслеживание доступности игровой библиотеки */ function checkLibLoad() { timeout = setTimeout(() => { if (Game.GameModel) { changeLib(); } else { checkLibLoad(); } }, 100); } /** * Game library data spoofing * * Подмена данных игровой библиотеки */ function changeLib() { console.log('lib connect'); const originalStartFunc = Game.GameModel.prototype.start; Game.GameModel.prototype.start = function (a, b, c) { self.libGame = b.raw; self.doneLibLoad(self.libGame); try { const levels = b.raw.seasonAdventure.level; for (const id in levels) { const level = levels[id]; level.clientData.graphics.fogged = level.clientData.graphics.visible; } const adv = b.raw.seasonAdventure.list[1]; adv.clientData.asset = 'dialog_season_adventure_tiles'; } catch (e) { console.warn(e); } originalStartFunc.call(this, a, b, c); }; } this.LibLoad = function () { return new Promise((e) => { this.doneLibLoad = e; }); }; /** * Returns the value of a language constant * * Возвращает значение языковой константы * @param {*} langConst language constant // языковая константа * @returns */ this.translate = function (langConst) { return Game.Translate.translate(langConst); }; connectGame(); checkLibLoad(); } /** * Auto collection of gifts * * Автосбор подарков */ function getAutoGifts() { // c3ltYm9scyB0aGF0IG1lYW4gbm90aGluZw== let valName = 'giftSendIds_' + userInfo.id; if (!localStorage['clearGift' + userInfo.id]) { localStorage[valName] = ''; localStorage['clearGift' + userInfo.id] = '+'; } if (!localStorage[valName]) { localStorage[valName] = ''; } const giftsAPI = new ZingerYWebsiteAPI('getGifts.php', arguments); /** * Submit a request to receive gift codes * * Отправка запроса для получения кодов подарков */ giftsAPI.request().then((data) => { let freebieCheckCalls = { calls: [], }; data.forEach((giftId, n) => { if (localStorage[valName].includes(giftId)) return; freebieCheckCalls.calls.push({ name: 'registration', args: { user: { referrer: {} }, giftId, }, context: { actionTs: Math.floor(performance.now()), cookie: window?.NXAppInfo?.session_id || null, }, ident: giftId, }); }); if (!freebieCheckCalls.calls.length) { return; } send(JSON.stringify(freebieCheckCalls), (e) => { let countGetGifts = 0; const gifts = []; for (check of e.results) { gifts.push(check.ident); if (check.result.response != null) { countGetGifts++; } } const saveGifts = localStorage[valName].split(';'); localStorage[valName] = [...saveGifts, ...gifts].slice(-50).join(';'); console.log(`${I18N('GIFTS')}: ${countGetGifts}`); }); }); } /** * To fill the kills in the Forge of Souls * * Набить килов в горниле душ */ async function bossRatingEvent() { const topGet = await Send(JSON.stringify({ calls: [{ name: "topGet", args: { type: "bossRatingTop", extraId: 0 }, ident: "body" }] })); if (!topGet || !topGet.results[0].result.response[0]) { setProgress(`${I18N('EVENT')} ${I18N('NOT_AVAILABLE')}`, true); return; } const replayId = topGet.results[0].result.response[0].userData.replayId; const result = await Send(JSON.stringify({ calls: [ { name: "battleGetReplay", args: { id: replayId }, ident: "battleGetReplay" }, { name: "heroGetAll", args: {}, ident: "heroGetAll" }, { name: "pet_getAll", args: {}, ident: "pet_getAll" }, { name: "offerGetAll", args: {}, ident: "offerGetAll" } ] })); const bossEventInfo = result.results[3].result.response.find(e => e.offerType == "bossEvent"); if (!bossEventInfo) { setProgress(`${I18N('EVENT')} ${I18N('NOT_AVAILABLE')}`, true); return; } const usedHeroes = bossEventInfo.progress.usedHeroes; const party = Object.values(result.results[0].result.response.replay.attackers); const availableHeroes = Object.values(result.results[1].result.response).map(e => e.id); const availablePets = Object.values(result.results[2].result.response).map(e => e.id); const calls = []; /** * First pack * * Первая пачка */ const args = { heroes: [], favor: {} } for (let hero of party) { if (hero.id >= 6000 && availablePets.includes(hero.id)) { args.pet = hero.id; continue; } if (!availableHeroes.includes(hero.id) || usedHeroes.includes(hero.id)) { continue; } args.heroes.push(hero.id); if (hero.favorPetId) { args.favor[hero.id] = hero.favorPetId; } } if (args.heroes.length) { calls.push({ name: "bossRatingEvent_startBattle", args, ident: "body_0" }); } /** * Other packs * * Другие пачки */ let heroes = []; let count = 1; while (heroId = availableHeroes.pop()) { if (args.heroes.includes(heroId) || usedHeroes.includes(heroId)) { continue; } heroes.push(heroId); if (heroes.length == 5) { calls.push({ name: "bossRatingEvent_startBattle", args: { heroes: [...heroes], pet: availablePets[Math.floor(Math.random() * availablePets.length)] }, ident: "body_" + count }); heroes = []; count++; } } if (!calls.length) { setProgress(`${I18N('NO_HEROES')}`, true); return; } const resultBattles = await Send(JSON.stringify({ calls })); console.log(resultBattles); rewardBossRatingEvent(); } /** * Collecting Rewards from the Forge of Souls * * Сбор награды из Горнила Душ */ function rewardBossRatingEvent() { let rewardBossRatingCall = '{"calls":[{"name":"offerGetAll","args":{},"ident":"offerGetAll"}]}'; send(rewardBossRatingCall, function (data) { let bossEventInfo = data.results[0].result.response.find(e => e.offerType == "bossEvent"); if (!bossEventInfo) { setProgress(`${I18N('EVENT')} ${I18N('NOT_AVAILABLE')}`, true); return; } let farmedChests = bossEventInfo.progress.farmedChests; let score = bossEventInfo.progress.score; setProgress(`${I18N('DAMAGE_AMOUNT')}: ${score}`); let revard = bossEventInfo.reward; let getRewardCall = { calls: [] } let count = 0; for (let i = 1; i < 10; i++) { if (farmedChests.includes(i)) { continue; } if (score < revard[i].score) { break; } getRewardCall.calls.push({ name: "bossRatingEvent_getReward", args: { rewardId: i }, ident: "body_" + i }); count++; } if (!count) { setProgress(`${I18N('NOTHING_TO_COLLECT')}`, true); return; } send(JSON.stringify(getRewardCall), e => { console.log(e); setProgress(`${I18N('COLLECTED')} ${e?.results?.length} ${I18N('REWARD')}`, true); }); }); } /** * Collect Easter eggs and event rewards * * Собрать пасхалки и награды событий */ function offerFarmAllReward() { const offerGetAllCall = '{"calls":[{"name":"offerGetAll","args":{},"ident":"offerGetAll"}]}'; return Send(offerGetAllCall).then((data) => { const offerGetAll = data.results[0].result.response.filter(e => e.type == "reward" && !e?.freeRewardObtained && e.reward); if (!offerGetAll.length) { setProgress(`${I18N('NOTHING_TO_COLLECT')}`, true); return; } const calls = []; for (let reward of offerGetAll) { calls.push({ name: "offerFarmReward", args: { offerId: reward.id }, ident: "offerFarmReward_" + reward.id }); } return Send(JSON.stringify({ calls })).then(e => { console.log(e); setProgress(`${I18N('COLLECTED')} ${e?.results?.length} ${I18N('REWARD')}`, true); }); }); } /** * Assemble Outland * * Собрать запределье */ function getOutland() { return new Promise(function (resolve, reject) { send('{"calls":[{"name":"bossGetAll","args":{},"ident":"bossGetAll"}]}', e => { let bosses = e.results[0].result.response; let bossRaidOpenChestCall = { calls: [] }; for (let boss of bosses) { if (boss.mayRaid) { bossRaidOpenChestCall.calls.push({ name: "bossRaid", args: { bossId: boss.id }, ident: "bossRaid_" + boss.id }); bossRaidOpenChestCall.calls.push({ name: "bossOpenChest", args: { bossId: boss.id, amount: 1, starmoney: 0 }, ident: "bossOpenChest_" + boss.id }); } else if (boss.chestId == 1) { bossRaidOpenChestCall.calls.push({ name: "bossOpenChest", args: { bossId: boss.id, amount: 1, starmoney: 0 }, ident: "bossOpenChest_" + boss.id }); } } if (!bossRaidOpenChestCall.calls.length) { setProgress(`${I18N('OUTLAND')} ${I18N('NOTHING_TO_COLLECT')}`, true); resolve(); return; } send(JSON.stringify(bossRaidOpenChestCall), e => { setProgress(`${I18N('OUTLAND')} ${I18N('COLLECTED')}`, true); resolve(); }); }); }); } /** * Collect all rewards * * Собрать все награды */ function questAllFarm() { return new Promise(function (resolve, reject) { let questGetAllCall = { calls: [{ name: "questGetAll", args: {}, ident: "body" }] } send(JSON.stringify(questGetAllCall), function (data) { let questGetAll = data.results[0].result.response; const questAllFarmCall = { calls: [] } let number = 0; for (let quest of questGetAll) { if (quest.id < 1e6 && quest.state == 2) { questAllFarmCall.calls.push({ name: "questFarm", args: { questId: quest.id }, ident: `group_${number}_body` }); number++; } } if (!questAllFarmCall.calls.length) { setProgress(`${I18N('COLLECTED')} ${number} ${I18N('REWARD')}`, true); resolve(); return; } send(JSON.stringify(questAllFarmCall), function (res) { console.log(res); setProgress(`${I18N('COLLECTED')} ${number} ${I18N('REWARD')}`, true); resolve(); }); }); }) } /** * Mission auto repeat * * Автоповтор миссии * isStopSendMission = false; * isSendsMission = true; **/ this.sendsMission = async function (param) { if (isStopSendMission) { isSendsMission = false; console.log(I18N('STOPPED')); setProgress(''); await popup.confirm(`${I18N('STOPPED')}<br>${I18N('REPETITIONS')}: ${param.count}`, [{ msg: 'Ok', result: true }, ]) return; } lastMissionBattleStart = Date.now(); let missionStartCall = { "calls": [{ "name": "missionStart", "args": lastMissionStart, "ident": "body" }] } /** * Mission Request * * Запрос на выполнение мисии */ SendRequest(JSON.stringify(missionStartCall), async e => { if (e['error']) { isSendsMission = false; console.log(e['error']); setProgress(''); let msg = e['error'].name + ' ' + e['error'].description + `<br>${I18N('REPETITIONS')}: ${param.count}`; await popup.confirm(msg, [ {msg: 'Ok', result: true}, ]) return; } /** * Mission data calculation * * Расчет данных мисии */ BattleCalc(e.results[0].result.response, 'get_tower', async r => { /** missionTimer */ let timer = getTimer(r.battleTime) + 5; const period = Math.ceil((Date.now() - lastMissionBattleStart) / 1000); if (period < timer) { timer = timer - period; await countdownTimer(timer, `${I18N('MISSIONS_PASSED')}: ${param.count}`); } let missionEndCall = { "calls": [{ "name": "missionEnd", "args": { "id": param.id, "result": r.result, "progress": r.progress }, "ident": "body" }] } /** * Mission Completion Request * * Запрос на завершение миссии */ SendRequest(JSON.stringify(missionEndCall), async (e) => { if (e['error']) { isSendsMission = false; console.log(e['error']); setProgress(''); let msg = e['error'].name + ' ' + e['error'].description + `<br>${I18N('REPETITIONS')}: ${param.count}`; await popup.confirm(msg, [ {msg: 'Ok', result: true}, ]) return; } r = e.results[0].result.response; if (r['error']) { isSendsMission = false; console.log(r['error']); setProgress(''); await popup.confirm(`<br>${I18N('REPETITIONS')}: ${param.count}` + ' 3 ' + r['error'], [ {msg: 'Ok', result: true}, ]) return; } param.count++; setProgress(`${I18N('MISSIONS_PASSED')}: ${param.count} (${I18N('STOP')})`, false, () => { isStopSendMission = true; }); setTimeout(sendsMission, 1, param); }); }) }); } /** * Opening of russian dolls * * Открытие матрешек */ async function openRussianDolls(libId, amount) { let sum = 0; const sumResult = {}; let count = 0; while (amount) { sum += amount; setProgress(`${I18N('TOTAL_OPEN')} ${sum}`); const calls = [ { name: 'consumableUseLootBox', args: { libId, amount }, ident: 'body', }, ]; const response = await Send(JSON.stringify({ calls })).then((e) => e.results[0].result.response); let [countLootBox, result] = Object.entries(response).pop(); count += +countLootBox; let newCount = 0; if (result?.consumable && result.consumable[libId]) { newCount = result.consumable[libId]; delete result.consumable[libId]; } mergeItemsObj(sumResult, result); amount = newCount; } setProgress(`${I18N('TOTAL_OPEN')} ${sum}`, 5000); return [count, sumResult]; } function mergeItemsObj(obj1, obj2) { for (const key in obj2) { if (obj1[key]) { if (typeof obj1[key] == 'object') { for (const innerKey in obj2[key]) { obj1[key][innerKey] = (obj1[key][innerKey] || 0) + obj2[key][innerKey]; } } else { obj1[key] += obj2[key] || 0; } } else { obj1[key] = obj2[key]; } } return obj1; } /** * Collect all mail, except letters with energy and charges of the portal * * Собрать всю почту, кроме писем с энергией и зарядами портала */ function mailGetAll() { const getMailInfo = '{"calls":[{"name":"mailGetAll","args":{},"ident":"body"}]}'; return Send(getMailInfo).then(dataMail => { const letters = dataMail.results[0].result.response.letters; const letterIds = lettersFilter(letters); if (!letterIds.length) { setProgress(I18N('NOTHING_TO_COLLECT'), true); return; } const calls = [ { name: "mailFarm", args: { letterIds }, ident: "body" } ]; return Send(JSON.stringify({ calls })).then(res => { const lettersIds = res.results[0].result.response; if (lettersIds) { const countLetters = Object.keys(lettersIds).length; setProgress(`${I18N('RECEIVED')} ${countLetters} ${I18N('LETTERS')}`, true); } }); }); } /** * Filters received emails * * Фильтрует получаемые письма */ function lettersFilter(letters) { const lettersIds = []; for (let l in letters) { letter = letters[l]; const reward = letter?.reward; if (!reward || !Object.keys(reward).length) { continue; } /** * Mail Collection Exceptions * * Исключения на сбор писем */ const isFarmLetter = !( /** Portals // сферы портала */ (reward?.refillable ? reward.refillable[45] : false) || /** Energy // энергия */ (reward?.stamina ? reward.stamina : false) || /** accelerating energy gain // ускорение набора энергии */ (reward?.buff ? true : false) || /** VIP Points // вип очки */ (reward?.vipPoints ? reward.vipPoints : false) || /** souls of heroes // душы героев */ (reward?.fragmentHero ? true : false) || /** heroes // герои */ (reward?.bundleHeroReward ? true : false) ); if (isFarmLetter) { lettersIds.push(~~letter.id); continue; } /** * Если до окончания годности письма менее 24 часов, * то оно собирается не смотря на исключения */ const availableUntil = +letter?.availableUntil; if (availableUntil) { const maxTimeLeft = 24 * 60 * 60 * 1000; const timeLeft = (new Date(availableUntil * 1000) - new Date()) console.log('Time left:', timeLeft) if (timeLeft < maxTimeLeft) { lettersIds.push(~~letter.id); continue; } } } return lettersIds; } /** * Displaying information about the areas of the portal and attempts on the VG * * Отображение информации о сферах портала и попытках на ВГ */ async function justInfo() { return new Promise(async (resolve, reject) => { const calls = [{ name: "userGetInfo", args: {}, ident: "userGetInfo" }, { name: "clanWarGetInfo", args: {}, ident: "clanWarGetInfo" }, { name: "titanArenaGetStatus", args: {}, ident: "titanArenaGetStatus" }]; const result = await Send(JSON.stringify({ calls })); const infos = result.results; const portalSphere = infos[0].result.response.refillable.find(n => n.id == 45); const clanWarMyTries = infos[1].result.response?.myTries ?? 0; const arePointsMax = infos[1].result.response?.arePointsMax; const titansLevel = +(infos[2].result.response?.tier ?? 0); const titansStatus = infos[2].result.response?.status; //peace_time || battle const sanctuaryButton = buttons['testAdventure'].button; const sanctuaryDot = sanctuaryButton.querySelector('.scriptMenu_dot'); const clanWarButton = buttons['goToClanWar'].button; const clanWarDot = clanWarButton.querySelector('.scriptMenu_dot'); const titansArenaButton = buttons['testTitanArena'].button; const titansArenaDot = titansArenaButton.querySelector('.scriptMenu_dot'); if (portalSphere.amount) { sanctuaryButton.classList.add('scriptMenu_attention'); sanctuaryDot.title = `${portalSphere.amount} ${I18N('PORTALS')}`; sanctuaryDot.innerText = portalSphere.amount; sanctuaryDot.style.backgroundColor = 'red'; } else { sanctuaryButton.classList.remove('scriptMenu_attention'); } if (clanWarMyTries && !arePointsMax) { clanWarButton.classList.add('scriptMenu_attention'); clanWarDot.title = `${clanWarMyTries} ${I18N('ATTEMPTS')}`; clanWarDot.innerText = clanWarMyTries; clanWarDot.style.backgroundColor = 'red'; } else { clanWarButton.classList.remove('scriptMenu_attention'); } if (titansLevel < 7 && titansStatus == 'battle') { ; titansArenaButton.classList.add('scriptMenu_attention'); titansArenaDot.title = `${titansLevel} ${I18N('LEVEL')}`; titansArenaDot.innerText = titansLevel; titansArenaDot.style.backgroundColor = 'red'; } else { titansArenaButton.classList.remove('scriptMenu_attention'); } const imgPortal = 'data:image/gif;base64,R0lGODlhLwAvAHAAACH5BAEAAP8ALAAAAAAvAC8AhwAAABkQWgjF3krO3ghSjAhSzinF3u+tGWvO3s5rGSmE5gha7+/OWghSrWvmnClShCmUlAiE5u+MGe/W3mvvWmspUmvvGSnOWinOnCnOGWsZjErvnAiUlErvWmsIUkrvGQjOWgjOnAjOGUoZjM6MGe/OIWvv5q1KGSnv5mulGe/vWs7v3ozv3kqEGYxKGWuEWmtSKUrv3mNaCEpKUs7OWiml5ggxWmMpEAgZpRlaCO/35q1rGRkxKWtarSkZrRljKSkZhAjv3msIGRk6CEparQhjWq3v3kql3ozOGe/vnM6tGYytWu9rGWuEGYzO3kqE3gil5s6MWq3vnGvFnM7vWoxrGc5KGYyMWs6tWq2MGYzOnO+tWmvFWkqlWoxrWgAZhEqEWq2tWoytnIyt3krFnGul3mulWmulnEIpUkqlGUqlnK3OnK2MWs7OnClSrSmUte+tnGvFGYytGYzvWs5rWowpGa3O3u/OnErFWoyMnGuE3muEnEqEnIyMGYzOWs7OGe9r3u9rWq3vWq1rWq1r3invWimlWu+t3q0pWq2t3u8pWu8p3q0p3invnCnvGe/vGa2tGa3vGa2tnK0pGe9rnK1rnCmlGe8pGe8pnK0pnGsZrSkp3msp3s7vGYzvnM7vnIzvGc6tnM5r3oxr3gilWs6t3owpWs4pWs4p3owp3s5rnIxrnAilGc4pGc4pnIwpnAgp3kop3s7O3u9KGe+MWoxKWoyM3kIIUgiUte+MnErFGc5KWowIGe9K3u9KWq3OWq1KWq1K3gjvWimEWu+M3q0IWq2M3u8IWu8I3q0I3gjvnAjvGa3OGa2MnK0IGe9KnK1KnCmEGe8IGe8InK0InEoZrSkI3msI3s6MnM5K3oxK3giEWs6M3owIWs4IWs4I3owI3s5KnIxKnAiEGc4IGc4InIwInAgI3koI3kJaCAgQKUIpEGtKUkJSKUIIECla7ylazmtahGta70pa70pahGtazkpazmtrWiExUkprUiljWikQKRkQCAAQCAAACAAAAAj/AP8JHEiwoMGDCBMqXMiwocODJlBIRBHDxMOLBmMEkSjAgICPE2Mw/OUH4z8TGz+agBIBCsuWUAQE0WLwzkAkKZZcnAilhk+fA1bUiEC0ZZABJOD8IyHhwJYDkpakafJQ4kooR5yw0LFihQ4WJhAMKCoARRYSTJgkUOInBZK2DiX2rGHEiI67eFcYATtAAVEoKEiQSFBFDs4UKbg0lGgAigIEeCNzrWvCxIChEcoy3dGiSoITTRQvnCLRrxOveI2McbKahevKJmooiKkFy4Gzg5tMMaMwitwIj/PqGPCugL0CT47ANhEjQg3Atg9IT5CiS4uEUcRIBH4EtREETuB9/xn/BUcBBbBXGGgpoPaBEid23EuXgvdBJhtQGFCwwA7eMgs0gEMDBJD3hR7KbRVbSwP8UcIWJNwjIRLXGZRAAhLVsIACR9y1whMNfNGAHgiUcUSBX8ADWwwKzCYADTSUcMA9ebwQmkFYMMFGhgu80x1XTxSAwxNdGWGCAiG6YQBzly3QkhYxlsDGP1cg4YBBaC0h1zsLPGHXCkfA00AZeu11hALl1VBZXwW0RAaMDGDxTxNdTGEQExJoiUINXCpwmhFOKJCcVmCdOR56MezXJhRvwFlCC2lcWVAUEjBxRobw9HhEXUYekWBlsoVoQEWyFbAAFPRIQQMDJcDQhRhYSv+QZ1kGcAnPYya4BhZYlb1TQ4iI+tVmBPpIQQWrMORxkKwSsEFrDaa+8xgCy1mmgLSHxtDXAhtGMIOxDKjgAkLM7iAAYD4VJ+0RAyAgVl++ikfAESxy62QB365awrjLyprAcxEY4FOmXEp7LbctjlfAAE1yGwEBYBirAgP8GtTUARIMM1QBPrVYQAHF9dgiml/Mexl/3DbAwxnHMqBExQVdLAEMjRXQgHOyydaibPCgqEDH3JrawDosUDExCTATZJuMJ0AAxRNXtLFFPD+P/DB58AC9wH4N4BMxDRPvkPRAbLx3AAlVMLBFCXeQgIaIKJKHQ9X8+forAetMsaoKB7j/MAhCL5j9VFNPJYBGiCGW18CtsvWIs5j7gLEGqyV81gxC6ZBQQgkSMEUCLQckMMLHNhcAD3B+8TdyA0PPACWrB8SH0BItyHAAAwdE4YILTSUww8cELwAyt7D4JSberkd5wA4neIFQE020sMPmJZBwAi0SJMBOA6WTXgAsDYDPOj7r3KNFy5WfkEBCKbTQBQzTM+By5wm4YAPr+LM+IIE27LPOFWswmgqqZ4UEXCEhLUjBGWbgAs3JD2OfWcc68GEDArCOAASwAfnWUYUwtIEKSVCBCiSgPuclpAlImMI9YNDAzeFuMEwQ2w3W4Q530PAGLthBFNqwghCKMAoF3MEB/xNihvr8Ix4sdCCrJja47CVAMFjAwid6eJcQWi8BO4jHQl6AGFjdwwUnOMF75CfCMpoxCTpAoxoZMBgs3qMh7ZODQFYYxgSMsQThCpcK0BiZJNxBCZ7zwhsbYqO3wCoe7AjjCaxAggNUcY94mcDa3qMECWSBHYN0CBfj0IQliEFCMFjkIulAAisUkBZYyB4USxAFCZnkH1xsgltSYCMYyACMpizghS7kOTZIKJMmeYEZzCCH6iCmBS1IRzpkcEsXVMGZMMgHJvfwyoLsYQ9nmMIUuDAFPIAhH8pUZjLbcY89rKKaC9nDFeLxy3vkYwbJTMcL0InOeOSjBVShJz2pqQvPfvrznwANKEMCAgA7'; setProgress('<img src="' + imgPortal + '" style="height: 25px;position: relative;top: 5px;"> ' + `${portalSphere.amount} </br> ${I18N('GUILD_WAR')}: ${clanWarMyTries}`, true); resolve(); }); } async function getDailyBonus() { const dailyBonusInfo = await Send(JSON.stringify({ calls: [{ name: "dailyBonusGetInfo", args: {}, ident: "body" }] })).then(e => e.results[0].result.response); const { availableToday, availableVip, currentDay } = dailyBonusInfo; if (!availableToday) { console.log('Уже собрано'); return; } const currentVipPoints = +userInfo.vipPoints; const dailyBonusStat = lib.getData('dailyBonusStatic'); const vipInfo = lib.getData('level').vip; let currentVipLevel = 0; for (let i in vipInfo) { vipLvl = vipInfo[i]; if (currentVipPoints >= vipLvl.vipPoints) { currentVipLevel = vipLvl.level; } } const vipLevelDouble = dailyBonusStat[`${currentDay}_0_0`].vipLevelDouble; const calls = [{ name: "dailyBonusFarm", args: { vip: availableVip && currentVipLevel >= vipLevelDouble ? 1 : 0 }, ident: "body" }]; const result = await Send(JSON.stringify({ calls })); if (result.error) { console.error(result.error); return; } const reward = result.results[0].result.response; const type = Object.keys(reward).pop(); const itemId = Object.keys(reward[type]).pop(); const count = reward[type][itemId]; const itemName = cheats.translate(`LIB_${type.toUpperCase()}_NAME_${itemId}`); console.log(`Ежедневная награда: Получено ${count} ${itemName}`, reward); } async function farmStamina(lootBoxId = 148) { const lootBox = await Send('{"calls":[{"name":"inventoryGet","args":{},"ident":"inventoryGet"}]}') .then(e => e.results[0].result.response.consumable[148]); /** Добавить другие ящики */ /** * 144 - медная шкатулка * 145 - бронзовая шкатулка * 148 - платиновая шкатулка */ if (!lootBox) { setProgress(I18N('NO_BOXES'), true); return; } let maxFarmEnergy = getSaveVal('maxFarmEnergy', 100); const result = await popup.confirm(I18N('OPEN_LOOTBOX', { lootBox }), [ { result: false, isClose: true }, { msg: I18N('BTN_YES'), result: true }, { msg: I18N('STAMINA'), isInput: true, default: maxFarmEnergy }, ]); if (!+result) { return; } if ((typeof result) !== 'boolean' && Number.parseInt(result)) { maxFarmEnergy = +result; setSaveVal('maxFarmEnergy', maxFarmEnergy); } else { maxFarmEnergy = 0; } let collectEnergy = 0; for (let count = lootBox; count > 0; count--) { const response = await Send('{"calls":[{"name":"consumableUseLootBox","args":{"libId":148,"amount":1},"ident":"body"}]}').then( (e) => e.results[0].result.response ); const result = Object.values(response).pop(); if ('stamina' in result) { setProgress(`${I18N('OPEN')}: ${lootBox - count}/${lootBox} ${I18N('STAMINA')} +${result.stamina}<br>${I18N('STAMINA')}: ${collectEnergy}`, false); console.log(`${ I18N('STAMINA') } + ${ result.stamina }`); if (!maxFarmEnergy) { return; } collectEnergy += +result.stamina; if (collectEnergy >= maxFarmEnergy) { console.log(`${I18N('STAMINA')} + ${ collectEnergy }`); setProgress(`${I18N('STAMINA')} + ${ collectEnergy }`, false); return; } } else { setProgress(`${I18N('OPEN')}: ${lootBox - count}/${lootBox}<br>${I18N('STAMINA')}: ${collectEnergy}`, false); console.log(result); } } setProgress(I18N('BOXES_OVER'), true); } async function fillActive() { const data = await Send(JSON.stringify({ calls: [{ name: "questGetAll", args: {}, ident: "questGetAll" }, { name: "inventoryGet", args: {}, ident: "inventoryGet" }, { name: "clanGetInfo", args: {}, ident: "clanGetInfo" } ] })).then(e => e.results.map(n => n.result.response)); const quests = data[0]; const inv = data[1]; const stat = data[2].stat; const maxActive = 2000 - stat.todayItemsActivity; if (maxActive <= 0) { setProgress(I18N('NO_MORE_ACTIVITY'), true); return; } let countGetActive = 0; const quest = quests.find(e => e.id > 10046 && e.id < 10051); if (quest) { countGetActive = 1750 - quest.progress; } if (countGetActive <= 0) { countGetActive = maxActive; } console.log(countGetActive); countGetActive = +(await popup.confirm(I18N('EXCHANGE_ITEMS', { maxActive }), [ { result: false, isClose: true }, { msg: I18N('GET_ACTIVITY'), isInput: true, default: countGetActive.toString() }, ])); if (!countGetActive) { return; } if (countGetActive > maxActive) { countGetActive = maxActive; } const items = lib.getData('inventoryItem'); let itemsInfo = []; for (let type of ['gear', 'scroll']) { for (let i in inv[type]) { const v = items[type][i]?.enchantValue || 0; itemsInfo.push({ id: i, count: inv[type][i], v, type }) } const invType = 'fragment' + type.toLowerCase().charAt(0).toUpperCase() + type.slice(1); for (let i in inv[invType]) { const v = items[type][i]?.fragmentEnchantValue || 0; itemsInfo.push({ id: i, count: inv[invType][i], v, type: invType }) } } itemsInfo = itemsInfo.filter(e => e.v < 4 && e.count > 200); itemsInfo = itemsInfo.sort((a, b) => b.count - a.count); console.log(itemsInfo); const activeItem = itemsInfo.shift(); console.log(activeItem); const countItem = Math.ceil(countGetActive / activeItem.v); if (countItem > activeItem.count) { setProgress(I18N('NOT_ENOUGH_ITEMS'), true); console.log(activeItem); return; } await Send(JSON.stringify({ calls: [{ name: "clanItemsForActivity", args: { items: { [activeItem.type]: { [activeItem.id]: countItem } } }, ident: "body" }] })).then(e => { /** TODO: Вывести потраченые предметы */ console.log(e); setProgress(`${I18N('ACTIVITY_RECEIVED')}: ` + e.results[0].result.response, true); }); } async function buyHeroFragments() { const result = await Send('{"calls":[{"name":"inventoryGet","args":{},"ident":"inventoryGet"},{"name":"shopGetAll","args":{},"ident":"shopGetAll"}]}') .then(e => e.results.map(n => n.result.response)); const inv = result[0]; const shops = Object.values(result[1]).filter(shop => [4, 5, 6, 8, 9, 10, 17].includes(shop.id)); const calls = []; for (let shop of shops) { const slots = Object.values(shop.slots); for (const slot of slots) { /* Уже куплено */ if (slot.bought) { continue; } /* Не душа героя */ if (!('fragmentHero' in slot.reward)) { continue; } const coin = Object.keys(slot.cost).pop(); const coinId = Object.keys(slot.cost[coin]).pop(); const stock = inv[coin][coinId] || 0; /* Не хватает на покупку */ if (slot.cost[coin][coinId] > stock) { continue; } inv[coin][coinId] -= slot.cost[coin][coinId]; calls.push({ name: "shopBuy", args: { shopId: shop.id, slot: slot.id, cost: slot.cost, reward: slot.reward, }, ident: `shopBuy_${shop.id}_${slot.id}`, }) } } if (!calls.length) { setProgress(I18N('NO_PURCHASABLE_HERO_SOULS'), true); return; } const bought = await Send(JSON.stringify({ calls })).then(e => e.results.map(n => n.result.response)); if (!bought) { console.log('что-то пошло не так') return; } let countHeroSouls = 0; for (const buy of bought) { countHeroSouls += +Object.values(Object.values(buy).pop()).pop(); } console.log(countHeroSouls, bought, calls); setProgress(I18N('PURCHASED_HERO_SOULS', { countHeroSouls }), true); } /** Открыть платные сундуки в Запределье за 90 */ async function bossOpenChestPay() { const callsNames = ['userGetInfo', 'bossGetAll', 'specialOffer_getAll', 'getTime']; const info = await Send({ calls: callsNames.map((name) => ({ name, args: {}, ident: name })) }).then((e) => e.results.map((n) => n.result.response) ); const user = info[0]; const boses = info[1]; const offers = info[2]; const time = info[3]; const discountOffer = offers.find((e) => e.offerType == 'costReplaceOutlandChest'); let discount = 1; if (discountOffer && discountOffer.endTime > time) { discount = 1 - discountOffer.offerData.outlandChest.discountPercent / 100; } cost9chests = 540 * discount; cost18chests = 1740 * discount; costFirstChest = 90 * discount; costSecondChest = 200 * discount; const currentStarMoney = user.starMoney; if (currentStarMoney < cost9chests) { setProgress('Недостаточно изюма, нужно ' + cost9chests + ' у Вас ' + currentStarMoney, true); return; } const imgEmerald = "<img style='position: relative;top: 3px;' src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAXCAYAAAD+4+QTAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAY8SURBVEhLpVV5bFVlFv/d7a19W3tfN1pKabGFAm3Rlg4toAWRiH+AioiaqAkaE42NycRR0ZnomJnJYHAJERGNyx/GJYoboo2igKVSMUUKreW1pRvUvr7XvvXe9+7qeW1nGJaJycwvObnny/fl/L7zO+c7l8EV0LAKzA+H83lAFAC/BeDJN2gnc5yd/WaQ8Q0NCCnAANkU+ZfjIpKqJWBOd4EDbHagueBPb1tWuesi9Rqn86zJZDbAMTp4xoSFzMaa4FVe6fra3bbzQbYN6A8Cmrz0qoBx8gzMmaj/QfKHWyxs+4e1DiC78M9v5TTn1RtbVH+kMWlJCCad100VOmQiUWFnNLg4HW42QeYEl3KnIiP5Bzu/dr27o0UistD48k2d8rF9Sib9GZKaejAnOmrs2/6e3VR3q7idF41GWVA41uQQ1RMY00ZJrChcrAYvx8HHaSjil8LLilCY98BORylBKlWQHhjzfvfFnuTfPn1O+xFolzM7s5nMI80rSl7qib8ykRNcWyaUosBWgnN6BL3pHuRwucjmnBTUCjfHwElkNiaNPHYr0mYCKnMeE/r3OC2NQiZZheHsfQ9Vu1uAM+eBIX2W5Nqsh/ewtxlrhl75NtUviDpwq+s+NOXWwWFhKKCd6iCQVByV2qSb0wEo5PvhY9YikGrH3uAdiBtBDIdVVAvlyfjBOffuesTcDxySqD3mUxaOPLZ6aktAOS/kqHaYigN7gnsxMGnDAuEuiPw6ymIt3MwaZFFQB7MeTmYjPLSWjTTCioQ5XCOMJIPeoInD/SNOviy6heLmALkckRTyf3xLbtQ8k6sdOodcxoocMoXU9JoFdF8VESMMiWRJmykyedqXTInaQJnOTtYDcJtZ+DXkRSrOou1cCoHx4LptL0nLgYU8kWhwlFgrNV2wFnEmVAr+w9gUzkwQic2DoNmLYe0QgkYXIuYg4uYYosYQJs1fMGkEpqWzUVucDh9E37gCIWFgvY9FcbniEipii6hbwZVilP0kXB/jysrrPLqU3yDG0JzXhA3OjWgsXo8UG6XbR6AxScqJjJHo/gmY0+9FIOn80I0UkukQFohJNFZmwV/uhosX2j59KPuF8JgS5CI3wHB90RUdKL12pMs7Z3VvfH6WyOajPt+Deb7FRDCBmNmNpNmPhHEWCW0IMXUQaTVEtVPhseYTZRCBeB86h8+hY0yDodsHfny+4NETB7JOLN74TXqmu1Yu4ixHuj3ii0/eaatx7RgY/NYKtR2tm+6B7lbwTGg3bDQ06MLTcsoJettR4DqaC8+u/gfe6HwZOzuGQU8JDR5f1B2+6uHWp8RPSjfsj5/dDyMzfIAj3bqSK8bGW579ECPWXRViHTijDK2BPojcPCxkbXCZflh1H5ISkCCSWJxI8jcjmErhnaHh6fdzdbZTd0aKd7Q+5T/gqj6VyBBkwmfG0QySkkHDJq19dDrgvP3GQq/Pt6h/8mesLqqFz+6DRq0qWkR4uGzEYhrGJBktNdvQGfoJH490YwmNuwKt+LWvWubtAk6GlPHhfw/LCyQz0BXEZOaoLcDf1lAt2z1z5nIhlIsL0Csfo90sWDkHXDYXaq2VWFZShffOfoQc0qOIzT9wbGvpXxOYGgG6SdwLuJSE6mPT1ZNdUdM9fyi8YlnTEiHLc423GBPaFBSVQcrQqcMYrJrbjElVRUf8FIq57K4z/8x7rL9f7ymsb0vHz83GmsXlJJSlsXKhxn3w+YSyrC48vKB0zVbLYqHCUYEe5SekaRYznBuLvU1olwbBmvr4r/v4RzteN4761x+Wxg9dGPH/wkzhL8WRHkMvKo7j/sc/Swfir7ZT/WTYSapc6LwFhc4qSKwLEYHXoz/bnzv8dOw7+4ojyYkvLyfI4MokhNToSKZwYf+6u3e39P3y8XH6AeY5yxHiBcx11OA8rZO9qTdaNx9/n9KPyUdnOulKuFyui6GHAAkHpEDBptqauaKtcMySRBW3HH2Do1+9WbP9GXocVGj5okJfit8jATY06Dh+MBIyiwZrrylb4XXneO1BV9df7n/tMb0/0J17O9LJU7Nn/x+UrKvOyOq58dXtNz0Q2Luz+cUnrqe1q+qmyv8q9/+EypuXZrK2kdEwgW3R5pW/r8I0gN8AVk6uP7Y929oAAAAASUVORK5CYII='>"; if (currentStarMoney < cost9chests) { setProgress(I18N('NOT_ENOUGH_EMERALDS_540', { currentStarMoney, imgEmerald }), true); return; } const buttons = [{ result: false, isClose: true }]; if (currentStarMoney >= cost9chests) { buttons.push({ msg: I18N('BUY_OUTLAND_BTN', { count: 9, countEmerald: cost9chests, imgEmerald }), result: [costFirstChest, costFirstChest, 0], }); } if (currentStarMoney >= cost18chests) { buttons.push({ msg: I18N('BUY_OUTLAND_BTN', { count: 18, countEmerald: cost18chests, imgEmerald }), result: [costFirstChest, costFirstChest, 0, costSecondChest, costSecondChest, 0], }); } const answer = await popup.confirm(`<div style="margin-bottom: 15px;">${I18N('BUY_OUTLAND')}</div>`, buttons); if (!answer) { return; } const callBoss = []; let n = 0; for (let boss of boses) { const bossId = boss.id; if (boss.chestNum != 2) { continue; } const calls = []; for (const starmoney of answer) { calls.push({ name: 'bossOpenChest', args: { amount: 1, bossId, starmoney, }, ident: 'bossOpenChest_' + ++n, }); } callBoss.push(calls); } if (!callBoss.length) { setProgress(I18N('CHESTS_NOT_AVAILABLE'), true); return; } let count = 0; let errors = 0; for (const calls of callBoss) { const result = await Send({ calls }); console.log(result); if (result?.results) { count += result.results.length; } else { errors++; } } setProgress(`${I18N('OUTLAND_CHESTS_RECEIVED')}: ${count}`, true); } async function autoRaidAdventure() { const calls = [ { name: "userGetInfo", args: {}, ident: "userGetInfo" }, { name: "adventure_raidGetInfo", args: {}, ident: "adventure_raidGetInfo" } ]; const result = await Send(JSON.stringify({ calls })) .then(e => e.results.map(n => n.result.response)); const portalSphere = result[0].refillable.find(n => n.id == 45); const adventureRaid = Object.entries(result[1].raid).filter(e => e[1]).pop() const adventureId = adventureRaid ? adventureRaid[0] : 0; if (!portalSphere.amount || !adventureId) { setProgress(I18N('RAID_NOT_AVAILABLE'), true); return; } const countRaid = +(await popup.confirm(I18N('RAID_ADVENTURE', { adventureId }), [ { result: false, isClose: true }, { msg: I18N('RAID'), isInput: true, default: portalSphere.amount }, ])); if (!countRaid) { return; } if (countRaid > portalSphere.amount) { countRaid = portalSphere.amount; } const resultRaid = await Send(JSON.stringify({ calls: [...Array(countRaid)].map((e, i) => ({ name: "adventure_raid", args: { adventureId }, ident: `body_${i}` })) })).then(e => e.results.map(n => n.result.response)); if (!resultRaid.length) { console.log(resultRaid); setProgress(I18N('SOMETHING_WENT_WRONG'), true); return; } console.log(resultRaid, adventureId, portalSphere.amount); setProgress(I18N('ADVENTURE_COMPLETED', { adventureId, times: resultRaid.length }), true); } /** Вывести всю клановую статистику в консоль браузера */ async function clanStatistic() { const copy = function (text) { const copyTextarea = document.createElement("textarea"); copyTextarea.style.opacity = "0"; copyTextarea.textContent = text; document.body.appendChild(copyTextarea); copyTextarea.select(); document.execCommand("copy"); document.body.removeChild(copyTextarea); delete copyTextarea; } const calls = [ { name: "clanGetInfo", args: {}, ident: "clanGetInfo" }, { name: "clanGetWeeklyStat", args: {}, ident: "clanGetWeeklyStat" }, { name: "clanGetLog", args: {}, ident: "clanGetLog" }, ]; const result = await Send(JSON.stringify({ calls })); const dataClanInfo = result.results[0].result.response; const dataClanStat = result.results[1].result.response; const dataClanLog = result.results[2].result.response; const membersStat = {}; for (let i = 0; i < dataClanStat.stat.length; i++) { membersStat[dataClanStat.stat[i].id] = dataClanStat.stat[i]; } const joinStat = {}; historyLog = dataClanLog.history; for (let j in historyLog) { his = historyLog[j]; if (his.event == 'join') { joinStat[his.userId] = his.ctime; } } const infoArr = []; const members = dataClanInfo.clan.members; for (let n in members) { var member = [ n, members[n].name, members[n].level, dataClanInfo.clan.warriors.includes(+n) ? 1 : 0, (new Date(members[n].lastLoginTime * 1000)).toLocaleString().replace(',', ''), joinStat[n] ? (new Date(joinStat[n] * 1000)).toLocaleString().replace(',', '') : '', membersStat[n].activity.reverse().join('\t'), membersStat[n].adventureStat.reverse().join('\t'), membersStat[n].clanGifts.reverse().join('\t'), membersStat[n].clanWarStat.reverse().join('\t'), membersStat[n].dungeonActivity.reverse().join('\t'), ]; infoArr.push(member); } const info = infoArr.sort((a, b) => (b[2] - a[2])).map((e) => e.join('\t')).join('\n'); console.log(info); copy(info); setProgress(I18N('CLAN_STAT_COPY'), true); } async function buyInStoreForGold() { const result = await Send('{"calls":[{"name":"shopGetAll","args":{},"ident":"body"},{"name":"userGetInfo","args":{},"ident":"userGetInfo"}]}').then(e => e.results.map(n => n.result.response)); const shops = result[0]; const user = result[1]; let gold = user.gold; const calls = []; if (shops[17]) { const slots = shops[17].slots; for (let i = 1; i <= 2; i++) { if (!slots[i].bought) { const costGold = slots[i].cost.gold; if ((gold - costGold) < 0) { continue; } gold -= costGold; calls.push({ name: "shopBuy", args: { shopId: 17, slot: i, cost: slots[i].cost, reward: slots[i].reward, }, ident: 'body_' + i, }) } } } const slots = shops[1].slots; for (let i = 4; i <= 6; i++) { if (!slots[i].bought && slots[i]?.cost?.gold) { const costGold = slots[i].cost.gold; if ((gold - costGold) < 0) { continue; } gold -= costGold; calls.push({ name: "shopBuy", args: { shopId: 1, slot: i, cost: slots[i].cost, reward: slots[i].reward, }, ident: 'body_' + i, }) } } if (!calls.length) { setProgress(I18N('NOTHING_BUY'), true); return; } const resultBuy = await Send(JSON.stringify({ calls })).then(e => e.results.map(n => n.result.response)); console.log(resultBuy); const countBuy = resultBuy.length; setProgress(I18N('LOTS_BOUGHT', { countBuy }), true); } function rewardsAndMailFarm() { return new Promise(function (resolve, reject) { let questGetAllCall = { calls: [{ name: "questGetAll", args: {}, ident: "questGetAll" }, { name: "mailGetAll", args: {}, ident: "mailGetAll" }] } send(JSON.stringify(questGetAllCall), function (data) { if (!data) return; const questGetAll = data.results[0].result.response.filter((e) => e.state == 2); const questBattlePass = lib.getData('quest').battlePass; const questChainBPass = lib.getData('battlePass').questChain; const listBattlePass = lib.getData('battlePass').list; const questAllFarmCall = { calls: [], }; const questIds = []; for (let quest of questGetAll) { if (quest.id >= 2001e4) { continue; } if (quest.id > 1e6 && quest.id < 2e7) { const questInfo = questBattlePass[quest.id]; const chain = questChainBPass[questInfo.chain]; if (chain.requirement?.battlePassTicket) { continue; } const battlePass = listBattlePass[chain.battlePass]; const startTime = battlePass.startCondition.time.value * 1e3 const endTime = new Date(startTime + battlePass.duration * 1e3); if (startTime > Date.now() || endTime < Date.now()) { continue; } } if (quest.id >= 2e7) { questIds.push(quest.id); continue; } questAllFarmCall.calls.push({ name: 'questFarm', args: { questId: quest.id, }, ident: `questFarm_${quest.id}`, }); } if (questIds.length) { questAllFarmCall.calls.push({ name: 'quest_questsFarm', args: { questIds }, ident: 'quest_questsFarm', }); } let letters = data?.results[1]?.result?.response?.letters; letterIds = lettersFilter(letters); if (letterIds.length) { questAllFarmCall.calls.push({ name: 'mailFarm', args: { letterIds }, ident: 'mailFarm', }); } if (!questAllFarmCall.calls.length) { setProgress(I18N('NOTHING_TO_COLLECT'), true); resolve(); return; } send(JSON.stringify(questAllFarmCall), async function (res) { let countQuests = 0; let countMail = 0; let questsIds = []; for (let call of res.results) { if (call.ident.includes('questFarm')) { countQuests++; } else if (call.ident.includes('questsFarm')) { countQuests += Object.keys(call.result.response).length; } else if (call.ident.includes('mailFarm')) { countMail = Object.keys(call.result.response).length; } const newQuests = call.result.newQuests; if (newQuests) { for (let quest of newQuests) { if ((quest.id < 1e6 || (quest.id >= 2e7 && quest.id < 2001e4)) && quest.state == 2) { questsIds.push(quest.id); } } } } while (questsIds.length) { const questIds = []; const calls = []; for (let questId of questsIds) { if (questId < 1e6) { calls.push({ name: 'questFarm', args: { questId, }, ident: `questFarm_${questId}`, }); countQuests++; } else if (questId >= 2e7 && questId < 2001e4) { questIds.push(questId); countQuests++; } } calls.push({ name: 'quest_questsFarm', args: { questIds }, ident: 'body', }); const results = await Send({ calls }).then((e) => e.results.map((e) => e.result)); questsIds = []; for (const result of results) { const newQuests = result.newQuests; if (newQuests) { for (let quest of newQuests) { if (quest.state == 2) { questsIds.push(quest.id); } } } } } setProgress(I18N('COLLECT_REWARDS_AND_MAIL', { countQuests, countMail }), true); resolve(); }); }); }) } class epicBrawl { timeout = null; time = null; constructor() { if (epicBrawl.inst) { return epicBrawl.inst; } epicBrawl.inst = this; return this; } runTimeout(func, timeDiff) { const worker = new Worker(URL.createObjectURL(new Blob([` self.onmessage = function(e) { const timeDiff = e.data; if (timeDiff > 0) { setTimeout(() => { self.postMessage(1); self.close(); }, timeDiff); } }; `]))); worker.postMessage(timeDiff); worker.onmessage = () => { func(); }; return true; } timeDiff(date1, date2) { const date1Obj = new Date(date1); const date2Obj = new Date(date2); const timeDiff = Math.abs(date2Obj - date1Obj); const totalSeconds = timeDiff / 1000; const minutes = Math.floor(totalSeconds / 60); const seconds = Math.floor(totalSeconds % 60); const formattedMinutes = String(minutes).padStart(2, '0'); const formattedSeconds = String(seconds).padStart(2, '0'); return `${formattedMinutes}:${formattedSeconds}`; } check() { console.log(new Date(this.time)) if (Date.now() > this.time) { this.timeout = null; this.start() return; } this.timeout = this.runTimeout(() => this.check(), 6e4); return this.timeDiff(this.time, Date.now()) } async start() { if (this.timeout) { const time = this.timeDiff(this.time, Date.now()); console.log(new Date(this.time)) setProgress(I18N('TIMER_ALREADY', { time }), false, hideProgress); return; } setProgress(I18N('EPIC_BRAWL'), false, hideProgress); const teamInfo = await Send('{"calls":[{"name":"teamGetAll","args":{},"ident":"teamGetAll"},{"name":"teamGetFavor","args":{},"ident":"teamGetFavor"},{"name":"userGetInfo","args":{},"ident":"userGetInfo"}]}').then(e => e.results.map(n => n.result.response)); const refill = teamInfo[2].refillable.find(n => n.id == 52) this.time = (refill.lastRefill + 3600) * 1000 const attempts = refill.amount; if (!attempts) { console.log(new Date(this.time)); const time = this.check(); setProgress(I18N('NO_ATTEMPTS_TIMER_START', { time }), false, hideProgress); return; } if (!teamInfo[0].epic_brawl) { setProgress(I18N('NO_HEROES_PACK'), false, hideProgress); return; } const args = { heroes: teamInfo[0].epic_brawl.filter(e => e < 1000), pet: teamInfo[0].epic_brawl.filter(e => e > 6000).pop(), favor: teamInfo[1].epic_brawl, } let wins = 0; let coins = 0; let streak = { progress: 0, nextStage: 0 }; for (let i = attempts; i > 0; i--) { const info = await Send(JSON.stringify({ calls: [ { name: "epicBrawl_getEnemy", args: {}, ident: "epicBrawl_getEnemy" }, { name: "epicBrawl_startBattle", args, ident: "epicBrawl_startBattle" } ] })).then(e => e.results.map(n => n.result.response)); const { progress, result } = await Calc(info[1].battle); const endResult = await Send(JSON.stringify({ calls: [{ name: "epicBrawl_endBattle", args: { progress, result }, ident: "epicBrawl_endBattle" }, { name: "epicBrawl_getWinStreak", args: {}, ident: "epicBrawl_getWinStreak" }] })).then(e => e.results.map(n => n.result.response)); const resultInfo = endResult[0].result; streak = endResult[1]; wins += resultInfo.win; coins += resultInfo.reward ? resultInfo.reward.coin[39] : 0; console.log(endResult[0].result) if (endResult[1].progress == endResult[1].nextStage) { const farm = await Send('{"calls":[{"name":"epicBrawl_farmWinStreak","args":{},"ident":"body"}]}').then(e => e.results[0].result.response); coins += farm.coin[39]; } setProgress(I18N('EPIC_BRAWL_RESULT', { i, wins, attempts, coins, progress: streak.progress, nextStage: streak.nextStage, end: '', }), false, hideProgress); } console.log(new Date(this.time)); const time = this.check(); setProgress(I18N('EPIC_BRAWL_RESULT', { wins, attempts, coins, i: '', progress: streak.progress, nextStage: streak.nextStage, end: I18N('ATTEMPT_ENDED', { time }), }), false, hideProgress); } } function countdownTimer(seconds, message) { message = message || I18N('TIMER'); const stopTimer = Date.now() + seconds * 1e3 return new Promise(resolve => { const interval = setInterval(async () => { const now = Date.now(); setProgress(`${message} ${((stopTimer - now) / 1000).toFixed(2)}`, false); if (now > stopTimer) { clearInterval(interval); setProgress('', 1); resolve(); } }, 100); }); } this.HWHFuncs.countdownTimer = countdownTimer; /** Набить килов в горниле душк */ async function bossRatingEventSouls() { const data = await Send({ calls: [ { name: "heroGetAll", args: {}, ident: "teamGetAll" }, { name: "offerGetAll", args: {}, ident: "offerGetAll" }, { name: "pet_getAll", args: {}, ident: "pet_getAll" }, ] }); const bossEventInfo = data.results[1].result.response.find(e => e.offerType == "bossEvent"); if (!bossEventInfo) { setProgress('Эвент завершен', true); return; } if (bossEventInfo.progress.score > 250) { setProgress('Уже убито больше 250 врагов'); rewardBossRatingEventSouls(); return; } const availablePets = Object.values(data.results[2].result.response).map(e => e.id); const heroGetAllList = data.results[0].result.response; const usedHeroes = bossEventInfo.progress.usedHeroes; const heroList = []; for (let heroId in heroGetAllList) { let hero = heroGetAllList[heroId]; if (usedHeroes.includes(hero.id)) { continue; } heroList.push(hero.id); } if (!heroList.length) { setProgress('Нет героев', true); return; } const pet = availablePets.includes(6005) ? 6005 : availablePets[Math.floor(Math.random() * availablePets.length)]; const petLib = lib.getData('pet'); let count = 1; for (const heroId of heroList) { const args = { heroes: [heroId], pet } /** Поиск питомца для героя */ for (const petId of availablePets) { if (petLib[petId].favorHeroes.includes(heroId)) { args.favor = { [heroId]: petId } break; } } const calls = [{ name: "bossRatingEvent_startBattle", args, ident: "body" }, { name: "offerGetAll", args: {}, ident: "offerGetAll" }]; const res = await Send({ calls }); count++; if ('error' in res) { console.error(res.error); setProgress('Перезагрузите игру и попробуйте позже', true); return; } const eventInfo = res.results[1].result.response.find(e => e.offerType == "bossEvent"); if (eventInfo.progress.score > 250) { break; } setProgress('Количество убитых врагов: ' + eventInfo.progress.score + '<br>Использовано ' + count + ' героев'); } rewardBossRatingEventSouls(); } /** Сбор награды из Горнила Душ */ async function rewardBossRatingEventSouls() { const data = await Send({ calls: [ { name: "offerGetAll", args: {}, ident: "offerGetAll" } ] }); const bossEventInfo = data.results[0].result.response.find(e => e.offerType == "bossEvent"); if (!bossEventInfo) { setProgress('Эвент завершен', true); return; } const farmedChests = bossEventInfo.progress.farmedChests; const score = bossEventInfo.progress.score; // setProgress('Количество убитых врагов: ' + score); const revard = bossEventInfo.reward; const calls = []; let count = 0; for (let i = 1; i < 10; i++) { if (farmedChests.includes(i)) { continue; } if (score < revard[i].score) { break; } calls.push({ name: "bossRatingEvent_getReward", args: { rewardId: i }, ident: "body_" + i }); count++; } if (!count) { setProgress('Нечего собирать', true); return; } Send({ calls }).then(e => { console.log(e); setProgress('Собрано ' + e?.results?.length + ' наград', true); }) } /** * Spin the Seer * * Покрутить провидца */ async function rollAscension() { const refillable = await Send({calls:[ { name:"userGetInfo", args:{}, ident:"userGetInfo" } ]}).then(e => e.results[0].result.response.refillable); const i47 = refillable.find(i => i.id == 47); if (i47?.amount) { await Send({ calls: [{ name: "ascensionChest_open", args: { paid: false, amount: 1 }, ident: "body" }] }); setProgress(I18N('DONE'), true); } else { setProgress(I18N('NOT_ENOUGH_AP'), true); } } /** * Collect gifts for the New Year * * Собрать подарки на новый год */ function getGiftNewYear() { Send({ calls: [{ name: "newYearGiftGet", args: { type: 0 }, ident: "body" }] }).then(e => { const gifts = e.results[0].result.response.gifts; const calls = gifts.filter(e => e.opened == 0).map(e => ({ name: "newYearGiftOpen", args: { giftId: e.id }, ident: `body_${e.id}` })); if (!calls.length) { setProgress(I18N('NY_NO_GIFTS'), 5000); return; } Send({ calls }).then(e => { console.log(e.results) const msg = I18N('NY_GIFTS_COLLECTED', { count: e.results.length }); console.log(msg); setProgress(msg, 5000); }); }) } async function updateArtifacts() { const count = +await popup.confirm(I18N('SET_NUMBER_LEVELS'), [ { msg: I18N('BTN_GO'), isInput: true, default: 10 }, { result: false, isClose: true } ]); if (!count) { return; } const quest = new questRun; await quest.autoInit(); const heroes = Object.values(quest.questInfo['heroGetAll']); const inventory = quest.questInfo['inventoryGet']; const calls = []; for (let i = count; i > 0; i--) { const upArtifact = quest.getUpgradeArtifact(); if (!upArtifact.heroId) { if (await popup.confirm(I18N('POSSIBLE_IMPROVE_LEVELS', { count: calls.length }), [ { msg: I18N('YES'), result: true }, { result: false, isClose: true } ])) { break; } else { return; } } const hero = heroes.find(e => e.id == upArtifact.heroId); hero.artifacts[upArtifact.slotId].level++; inventory[upArtifact.costCurrency][upArtifact.costId] -= upArtifact.costValue; calls.push({ name: "heroArtifactLevelUp", args: { heroId: upArtifact.heroId, slotId: upArtifact.slotId }, ident: `heroArtifactLevelUp_${i}` }); } if (!calls.length) { console.log(I18N('NOT_ENOUGH_RESOURECES')); setProgress(I18N('NOT_ENOUGH_RESOURECES'), false); return; } await Send(JSON.stringify({ calls })).then(e => { if ('error' in e) { console.log(I18N('NOT_ENOUGH_RESOURECES')); setProgress(I18N('NOT_ENOUGH_RESOURECES'), false); } else { console.log(I18N('IMPROVED_LEVELS', { count: e.results.length })); setProgress(I18N('IMPROVED_LEVELS', { count: e.results.length }), false); } }); } window.sign = a => { const i = this['\x78\x79\x7a']; return md5([i['\x6e\x61\x6d\x65'], i['\x76\x65\x72\x73\x69\x6f\x6e'], i['\x61\x75\x74\x68\x6f\x72'], ~(a % 1e3)]['\x6a\x6f\x69\x6e']('\x5f')) } async function updateSkins() { const count = +await popup.confirm(I18N('SET_NUMBER_LEVELS'), [ { msg: I18N('BTN_GO'), isInput: true, default: 10 }, { result: false, isClose: true } ]); if (!count) { return; } const quest = new questRun; await quest.autoInit(); const heroes = Object.values(quest.questInfo['heroGetAll']); const inventory = quest.questInfo['inventoryGet']; const calls = []; for (let i = count; i > 0; i--) { const upSkin = quest.getUpgradeSkin(); if (!upSkin.heroId) { if (await popup.confirm(I18N('POSSIBLE_IMPROVE_LEVELS', { count: calls.length }), [ { msg: I18N('YES'), result: true }, { result: false, isClose: true } ])) { break; } else { return; } } const hero = heroes.find(e => e.id == upSkin.heroId); hero.skins[upSkin.skinId]++; inventory[upSkin.costCurrency][upSkin.costCurrencyId] -= upSkin.cost; calls.push({ name: "heroSkinUpgrade", args: { heroId: upSkin.heroId, skinId: upSkin.skinId }, ident: `heroSkinUpgrade_${i}` }) } if (!calls.length) { console.log(I18N('NOT_ENOUGH_RESOURECES')); setProgress(I18N('NOT_ENOUGH_RESOURECES'), false); return; } await Send(JSON.stringify({ calls })).then(e => { if ('error' in e) { console.log(I18N('NOT_ENOUGH_RESOURECES')); setProgress(I18N('NOT_ENOUGH_RESOURECES'), false); } else { console.log(I18N('IMPROVED_LEVELS', { count: e.results.length })); setProgress(I18N('IMPROVED_LEVELS', { count: e.results.length }), false); } }); } function getQuestionInfo(img, nameOnly = false) { const libHeroes = Object.values(lib.data.hero); const parts = img.split(':'); const id = parts[1]; switch (parts[0]) { case 'titanArtifact_id': return cheats.translate("LIB_TITAN_ARTIFACT_NAME_" + id); case 'titan': return cheats.translate("LIB_HERO_NAME_" + id); case 'skill': return cheats.translate("LIB_SKILL_" + id); case 'inventoryItem_gear': return cheats.translate("LIB_GEAR_NAME_" + id); case 'inventoryItem_coin': return cheats.translate("LIB_COIN_NAME_" + id); case 'artifact': if (nameOnly) { return cheats.translate("LIB_ARTIFACT_NAME_" + id); } heroes = libHeroes.filter(h => h.id < 100 && h.artifacts.includes(+id)); return { /** Как называется этот артефакт? */ name: cheats.translate("LIB_ARTIFACT_NAME_" + id), /** Какому герою принадлежит этот артефакт? */ heroes: heroes.map(h => cheats.translate("LIB_HERO_NAME_" + h.id)) }; case 'hero': if (nameOnly) { return cheats.translate("LIB_HERO_NAME_" + id); } artifacts = lib.data.hero[id].artifacts; return { /** Как зовут этого героя? */ name: cheats.translate("LIB_HERO_NAME_" + id), /** Какой артефакт принадлежит этому герою? */ artifact: artifacts.map(a => cheats.translate("LIB_ARTIFACT_NAME_" + a)) }; } } function hintQuest(quest) { const result = {}; if (quest?.questionIcon) { const info = getQuestionInfo(quest.questionIcon); if (info?.heroes) { /** Какому герою принадлежит этот артефакт? */ result.answer = quest.answers.filter(e => info.heroes.includes(e.answerText.slice(1))); } if (info?.artifact) { /** Какой артефакт принадлежит этому герою? */ result.answer = quest.answers.filter(e => info.artifact.includes(e.answerText.slice(1))); } if (typeof info == 'string') { result.info = { name: info }; } else { result.info = info; } } if (quest.answers[0]?.answerIcon) { result.answer = quest.answers.filter(e => quest.question.includes(getQuestionInfo(e.answerIcon, true))) } if ((!result?.answer || !result.answer.length) && !result.info?.name) { return false; } let resultText = ''; if (result?.info) { resultText += I18N('PICTURE') + result.info.name; } console.log(result); if (result?.answer && result.answer.length) { resultText += I18N('ANSWER') + result.answer[0].id + (!result.answer[0].answerIcon ? ' - ' + result.answer[0].answerText : ''); } return resultText; } async function farmBattlePass() { const isFarmReward = (reward) => { return !(reward?.buff || reward?.fragmentHero || reward?.bundleHeroReward); }; const battlePassProcess = (pass) => { if (!pass.id) {return []} const levels = Object.values(lib.data.battlePass.level).filter(x => x.battlePass == pass.id) const last_level = levels[levels.length - 1]; let actual = Math.max(...levels.filter(p => pass.exp >= p.experience).map(p => p.level)) if (pass.exp > last_level.experience) { actual = last_level.level + (pass.exp - last_level.experience) / last_level.experienceByLevel; } const calls = []; for(let i = 1; i <= actual; i++) { const level = i >= last_level.level ? last_level : levels.find(l => l.level === i); const reward = {free: level?.freeReward, paid:level?.paidReward}; if (!pass.rewards[i]?.free && isFarmReward(reward.free)) { const args = {level: i, free:true}; if (!pass.gold) { args.id = pass.id } calls.push({ name: 'battlePass_farmReward', args, ident: `${pass.gold ? 'body' : 'spesial'}_free_${args.id}_${i}` }); } if (pass.ticket && !pass.rewards[i]?.paid && isFarmReward(reward.paid)) { const args = {level: i, free:false}; if (!pass.gold) { args.id = pass.id} calls.push({ name: 'battlePass_farmReward', args, ident: `${pass.gold ? 'body' : 'spesial'}_paid_${args.id}_${i}` }); } } return calls; } const passes = await Send({ calls: [ { name: 'battlePass_getInfo', args: {}, ident: 'getInfo' }, { name: 'battlePass_getSpecial', args: {}, ident: 'getSpecial' }, ], }).then((e) => [{...e.results[0].result.response?.battlePass, gold: true}, ...Object.values(e.results[1].result.response)]); const calls = passes.map(p => battlePassProcess(p)).flat() if (!calls.length) { setProgress(I18N('NOTHING_TO_COLLECT')); return; } let results = await Send({calls}); if (results.error) { console.log(results.error); setProgress(I18N('SOMETHING_WENT_WRONG')); } else { setProgress(I18N('SEASON_REWARD_COLLECTED', {count: results.results.length}), true); } } async function sellHeroSoulsForGold() { let { fragmentHero, heroes } = await Send({ calls: [ { name: 'inventoryGet', args: {}, ident: 'inventoryGet' }, { name: 'heroGetAll', args: {}, ident: 'heroGetAll' }, ], }) .then((e) => e.results.map((r) => r.result.response)) .then((e) => ({ fragmentHero: e[0].fragmentHero, heroes: e[1] })); const calls = []; for (let i in fragmentHero) { if (heroes[i] && heroes[i].star == 6) { calls.push({ name: 'inventorySell', args: { type: 'hero', libId: i, amount: fragmentHero[i], fragment: true, }, ident: 'inventorySell_' + i, }); } } if (!calls.length) { console.log(0); return 0; } const rewards = await Send({ calls }).then((e) => e.results.map((r) => r.result?.response?.gold || 0)); const gold = rewards.reduce((e, a) => e + a, 0); setProgress(I18N('GOLD_RECEIVED', { gold }), true); } /** * Attack of the minions of Asgard * * Атака прислужников Асгарда */ function testRaidNodes() { const { executeRaidNodes } = HWHClasses; return new Promise((resolve, reject) => { const tower = new executeRaidNodes(resolve, reject); tower.start(); }); } /** * Attack of the minions of Asgard * * Атака прислужников Асгарда */ function executeRaidNodes(resolve, reject) { let raidData = { teams: [], favor: {}, nodes: [], attempts: 0, countExecuteBattles: 0, cancelBattle: 0, } callsExecuteRaidNodes = { calls: [{ name: "clanRaid_getInfo", args: {}, ident: "clanRaid_getInfo" }, { name: "teamGetAll", args: {}, ident: "teamGetAll" }, { name: "teamGetFavor", args: {}, ident: "teamGetFavor" }] } this.start = function () { send(JSON.stringify(callsExecuteRaidNodes), startRaidNodes); } async function startRaidNodes(data) { res = data.results; clanRaidInfo = res[0].result.response; teamGetAll = res[1].result.response; teamGetFavor = res[2].result.response; let index = 0; let isNotFullPack = false; for (let team of teamGetAll.clanRaid_nodes) { if (team.length < 6) { isNotFullPack = true; } raidData.teams.push({ data: {}, heroes: team.filter(id => id < 6000), pet: team.filter(id => id >= 6000).pop(), battleIndex: index++ }); } raidData.favor = teamGetFavor.clanRaid_nodes; if (isNotFullPack) { if (await popup.confirm(I18N('MINIONS_WARNING'), [ { msg: I18N('BTN_NO'), result: true }, { msg: I18N('BTN_YES'), result: false }, ])) { endRaidNodes('isNotFullPack'); return; } } raidData.nodes = clanRaidInfo.nodes; raidData.attempts = clanRaidInfo.attempts; setIsCancalBattle(false); checkNodes(); } function getAttackNode() { for (let nodeId in raidData.nodes) { let node = raidData.nodes[nodeId]; let points = 0 for (team of node.teams) { points += team.points; } let now = Date.now() / 1000; if (!points && now > node.timestamps.start && now < node.timestamps.end) { let countTeam = node.teams.length; delete raidData.nodes[nodeId]; return { nodeId, countTeam }; } } return null; } function checkNodes() { setProgress(`${I18N('REMAINING_ATTEMPTS')}: ${raidData.attempts}`); let nodeInfo = getAttackNode(); if (nodeInfo && raidData.attempts) { startNodeBattles(nodeInfo); return; } endRaidNodes('EndRaidNodes'); } function startNodeBattles(nodeInfo) { let {nodeId, countTeam} = nodeInfo; let teams = raidData.teams.slice(0, countTeam); let heroes = raidData.teams.map(e => e.heroes).flat(); let favor = {...raidData.favor}; for (let heroId in favor) { if (!heroes.includes(+heroId)) { delete favor[heroId]; } } let calls = [{ name: "clanRaid_startNodeBattles", args: { nodeId, teams, favor }, ident: "body" }]; send(JSON.stringify({calls}), resultNodeBattles); } function resultNodeBattles(e) { if (e['error']) { endRaidNodes('nodeBattlesError', e['error']); return; } console.log(e); let battles = e.results[0].result.response.battles; let promises = []; let battleIndex = 0; for (let battle of battles) { battle.battleIndex = battleIndex++; promises.push(calcBattleResult(battle)); } Promise.all(promises) .then(results => { const endResults = {}; let isAllWin = true; for (let r of results) { isAllWin &&= r.result.win; } if (!isAllWin) { cancelEndNodeBattle(results[0]); return; } raidData.countExecuteBattles = results.length; let timeout = 500; for (let r of results) { setTimeout(endNodeBattle, timeout, r); timeout += 500; } }); } /** * Returns the battle calculation promise * * Возвращает промис расчета боя */ function calcBattleResult(battleData) { return new Promise(function (resolve, reject) { BattleCalc(battleData, "get_clanPvp", resolve); }); } /** * Cancels the fight * * Отменяет бой */ function cancelEndNodeBattle(r) { const fixBattle = function (heroes) { for (const ids in heroes) { hero = heroes[ids]; hero.energy = random(1, 999); if (hero.hp > 0) { hero.hp = random(1, hero.hp); } } } fixBattle(r.progress[0].attackers.heroes); fixBattle(r.progress[0].defenders.heroes); endNodeBattle(r); } /** * Ends the fight * * Завершает бой */ function endNodeBattle(r) { let nodeId = r.battleData.result.nodeId; let battleIndex = r.battleData.battleIndex; let calls = [{ name: "clanRaid_endNodeBattle", args: { nodeId, battleIndex, result: r.result, progress: r.progress }, ident: "body" }] SendRequest(JSON.stringify({calls}), battleResult); } /** * Processing the results of the battle * * Обработка результатов боя */ function battleResult(e) { if (e['error']) { endRaidNodes('missionEndError', e['error']); return; } r = e.results[0].result.response; if (r['error']) { if (r.reason == "invalidBattle") { raidData.cancelBattle++; checkNodes(); } else { endRaidNodes('missionEndError', e['error']); } return; } if (!(--raidData.countExecuteBattles)) { raidData.attempts--; checkNodes(); } } /** * Completing a task * * Завершение задачи */ function endRaidNodes(reason, info) { setIsCancalBattle(true); let textCancel = raidData.cancelBattle ? ` ${I18N('BATTLES_CANCELED')}: ${raidData.cancelBattle}` : ''; setProgress(`${I18N('MINION_RAID')} ${I18N('COMPLETED')}! ${textCancel}`, true); console.log(reason, info); resolve(); } } this.HWHClasses.executeRaidNodes = executeRaidNodes; /** * Asgard Boss Attack Replay * * Повтор атаки босса Асгарда */ function testBossBattle() { const { executeBossBattle } = HWHClasses; return new Promise((resolve, reject) => { const bossBattle = new executeBossBattle(resolve, reject); bossBattle.start(lastBossBattle); }); } /** * Asgard Boss Attack Replay * * Повтор атаки босса Асгарда */ function executeBossBattle(resolve, reject) { this.start = function (battleInfo) { preCalcBattle(battleInfo); } function getBattleInfo(battle) { return new Promise(function (resolve) { battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3); BattleCalc(battle, getBattleType(battle.type), e => { let extra = e.progress[0].defenders.heroes[1].extra; resolve(extra.damageTaken + extra.damageTakenNextLevel); }); }); } function preCalcBattle(battle) { let actions = []; const countTestBattle = getInput('countTestBattle'); for (let i = 0; i < countTestBattle; i++) { actions.push(getBattleInfo(battle, true)); } Promise.all(actions) .then(resultPreCalcBattle); } async function resultPreCalcBattle(damages) { let maxDamage = 0; let minDamage = 1e10; let avgDamage = 0; for (let damage of damages) { avgDamage += damage if (damage > maxDamage) { maxDamage = damage; } if (damage < minDamage) { minDamage = damage; } } avgDamage /= damages.length; console.log(damages.map(e => e.toLocaleString()).join('\n'), avgDamage, maxDamage); await popup.confirm( `${I18N('ROUND_STAT')} ${damages.length} ${I18N('BATTLE')}:` + `<br>${I18N('MINIMUM')}: ` + minDamage.toLocaleString() + `<br>${I18N('MAXIMUM')}: ` + maxDamage.toLocaleString() + `<br>${I18N('AVERAGE')}: ` + avgDamage.toLocaleString() , [ { msg: I18N('BTN_OK'), result: 0}, ]) endBossBattle(I18N('BTN_CANCEL')); } /** * Completing a task * * Завершение задачи */ function endBossBattle(reason, info) { console.log(reason, info); resolve(); } } this.HWHClasses.executeBossBattle = executeBossBattle; class FixBattle { minTimer = 1.3; maxTimer = 15.3; constructor(battle, isTimeout = true) { this.battle = structuredClone(battle); this.isTimeout = isTimeout; } timeout(callback, timeout) { if (this.isTimeout) { this.worker.postMessage(timeout); this.worker.onmessage = callback; } else { callback(); } } randTimer() { return Math.random() * (this.maxTimer - this.minTimer + 1) + this.minTimer; } setAvgTime(startTime) { this.fixTime += Date.now() - startTime; this.avgTime = this.fixTime / this.count; } init() { this.fixTime = 0; this.lastTimer = 0; this.index = 0; this.lastBossDamage = 0; this.bestResult = { count: 0, timer: 0, value: 0, result: null, progress: null, }; this.lastBattleResult = { win: false, }; this.worker = new Worker( URL.createObjectURL( new Blob([ `self.onmessage = function(e) { const timeout = e.data; setTimeout(() => { self.postMessage(1); }, timeout); };`, ]) ) ); } async start(endTime = Date.now() + 6e4, maxCount = 100) { this.endTime = endTime; this.maxCount = maxCount; this.init(); return await new Promise((resolve) => { this.resolve = resolve; this.count = 0; this.loop(); }); } endFix() { this.bestResult.maxCount = this.count; this.worker.terminate(); this.resolve(this.bestResult); } async loop() { const start = Date.now(); if (this.isEndLoop()) { this.endFix(); return; } this.count++; try { this.lastResult = await Calc(this.battle); } catch (e) { this.updateProgressTimer(this.index++); this.timeout(this.loop.bind(this), 0); return; } const { progress, result } = this.lastResult; this.lastBattleResult = result; this.lastBattleProgress = progress; this.setAvgTime(start); this.checkResult(); this.showResult(); this.updateProgressTimer(); this.timeout(this.loop.bind(this), 0); } isEndLoop() { return this.count >= this.maxCount || this.endTime < Date.now(); } updateProgressTimer(index = 0) { this.lastTimer = this.randTimer(); this.battle.progress = [{ attackers: { input: ['auto', 0, 0, 'auto', index, this.lastTimer] } }]; } showResult() { console.log( this.count, this.avgTime.toFixed(2), (this.endTime - Date.now()) / 1000, this.lastTimer.toFixed(2), this.lastBossDamage.toLocaleString(), this.bestResult.value.toLocaleString() ); } checkResult() { const { damageTaken, damageTakenNextLevel } = this.lastBattleProgress[0].defenders.heroes[1].extra; this.lastBossDamage = damageTaken + damageTakenNextLevel; if (this.lastBossDamage > this.bestResult.value) { this.bestResult = { count: this.count, timer: this.lastTimer, value: this.lastBossDamage, result: structuredClone(this.lastBattleResult), progress: structuredClone(this.lastBattleProgress), }; } } stopFix() { this.endTime = 0; } } this.HWHClasses.FixBattle = FixBattle; class WinFixBattle extends FixBattle { checkResult() { if (this.lastBattleResult.win) { this.bestResult = { count: this.count, timer: this.lastTimer, value: this.lastBattleResult.stars, result: structuredClone(this.lastBattleResult), progress: structuredClone(this.lastBattleProgress), battleTimer: this.lastResult.battleTimer, }; } } setWinTimer(value) { this.winTimer = value; } setMaxTimer(value) { this.maxTimer = value; } randTimer() { if (this.winTimer) { return this.winTimer; } return super.randTimer(); } isEndLoop() { return super.isEndLoop() || this.bestResult.result?.win; } showResult() { console.log( this.count, this.avgTime.toFixed(2), (this.endTime - Date.now()) / 1000, this.lastResult.battleTime, this.lastTimer, this.bestResult.value ); const endTime = ((this.endTime - Date.now()) / 1000).toFixed(2); const avgTime = this.avgTime.toFixed(2); const msg = `${I18N('LETS_FIX')} ${this.count}/${this.maxCount}<br/>${endTime}s<br/>${avgTime}ms`; setProgress(msg, false, this.stopFix.bind(this)); } } this.HWHClasses.WinFixBattle = WinFixBattle; class BestOrWinFixBattle extends WinFixBattle { isNoMakeWin = false; getState(result) { let beforeSumFactor = 0; const beforeHeroes = result.battleData.defenders[0]; for (let heroId in beforeHeroes) { const hero = beforeHeroes[heroId]; const state = hero.state; let factor = 1; if (state) { const hp = state.hp / (hero?.hp || 1); const energy = state.energy / 1e3; factor = hp + energy / 20; } beforeSumFactor += factor; } let afterSumFactor = 0; const afterHeroes = result.progress[0].defenders.heroes; for (let heroId in afterHeroes) { const hero = afterHeroes[heroId]; const hp = hero.hp / (beforeHeroes[heroId]?.hp || 1); const energy = hero.energy / 1e3; const factor = hp + energy / 20; afterSumFactor += factor; } return 100 - Math.floor((afterSumFactor / beforeSumFactor) * 1e4) / 100; } setNoMakeWin(value) { this.isNoMakeWin = value; } checkResult() { const state = this.getState(this.lastResult); console.log(state); if (state > this.bestResult.value) { if (!(this.isNoMakeWin && this.lastBattleResult.win)) { this.bestResult = { count: this.count, timer: this.lastTimer, value: state, result: structuredClone(this.lastBattleResult), progress: structuredClone(this.lastBattleProgress), battleTimer: this.lastResult.battleTimer, }; } } } } this.HWHClasses.BestOrWinFixBattle = BestOrWinFixBattle; class BossFixBattle extends FixBattle { showResult() { super.showResult(); //setTimeout(() => { const best = this.bestResult; const maxDmg = best.value.toLocaleString(); const avgTime = this.avgTime.toLocaleString(); const msg = `${I18N('LETS_FIX')} ${this.count}/${this.maxCount}<br/>${maxDmg}<br/>${avgTime}ms`; setProgress(msg, false, this.stopFix.bind(this)); //}, 0); } } this.HWHClasses.BossFixBattle = BossFixBattle; class DungeonFixBattle extends FixBattle { init() { super.init(); this.isTimeout = false; } setState() { const result = this.lastResult; let beforeSumFactor = 0; const beforeHeroes = result.battleData.attackers; for (let heroId in beforeHeroes) { const hero = beforeHeroes[heroId]; const state = hero.state; let factor = 1; if (state) { const hp = state.hp / (hero?.hp || 1); const energy = state.energy / 1e3; factor = hp + energy / 20; } beforeSumFactor += factor; } let afterSumFactor = 0; const afterHeroes = result.progress[0].attackers.heroes; for (let heroId in afterHeroes) { const hero = afterHeroes[heroId]; const hp = hero.hp / (beforeHeroes[heroId]?.hp || 1); const energy = hero.energy / 1e3; const factor = hp + energy / 20; afterSumFactor += factor; } this.lastState = Math.floor((afterSumFactor / beforeSumFactor) * 1e4) / 100; } checkResult() { this.setState(); if (this.lastResult.result.win && this.lastState > this.bestResult.value) { this.bestResult = { count: this.count, timer: this.lastTimer, value: this.lastState, result: this.lastResult.result, progress: this.lastResult.progress, }; } } showResult() { console.log( this.count, this.avgTime.toFixed(2), (this.endTime - Date.now()) / 1000, this.lastTimer.toFixed(2), this.lastState.toLocaleString(), this.bestResult.value.toLocaleString() ); } } this.HWHClasses.DungeonFixBattle = DungeonFixBattle; const masterWsMixin = { wsStart() { const socket = new WebSocket(this.url); socket.onopen = () => { console.log('Connected to server'); // Пример создания новой задачи const newTask = { type: 'newTask', battle: this.battle, endTime: this.endTime - 1e4, maxCount: this.maxCount, }; socket.send(JSON.stringify(newTask)); }; socket.onmessage = this.onmessage.bind(this); socket.onclose = () => { console.log('Disconnected from server'); }; this.ws = socket; }, onmessage(event) { const data = JSON.parse(event.data); switch (data.type) { case 'newTask': { console.log('newTask:', data); this.id = data.id; this.countExecutor = data.count; break; } case 'getSolTask': { console.log('getSolTask:', data); this.endFix(data.solutions); break; } case 'resolveTask': { console.log('resolveTask:', data); if (data.id === this.id && data.solutions.length === this.countExecutor) { this.worker.terminate(); this.endFix(data.solutions); } break; } default: console.log('Unknown message type:', data.type); } }, getTask() { this.ws.send( JSON.stringify({ type: 'getSolTask', id: this.id, }) ); }, }; /* mFix = new action.masterFixBattle(battle) await mFix.start(Date.now() + 6e4, 1); */ class masterFixBattle extends FixBattle { constructor(battle, url = 'wss://localho.st:3000') { super(battle, true); this.url = url; } async start(endTime, maxCount) { this.endTime = endTime; this.maxCount = maxCount; this.init(); this.wsStart(); return await new Promise((resolve) => { this.resolve = resolve; const timeout = this.endTime - Date.now(); this.timeout(this.getTask.bind(this), timeout); }); } async endFix(solutions) { this.ws.close(); let maxCount = 0; for (const solution of solutions) { maxCount += solution.maxCount; if (solution.value > this.bestResult.value) { this.bestResult = solution; } } this.count = maxCount; super.endFix(); } } Object.assign(masterFixBattle.prototype, masterWsMixin); this.HWHClasses.masterFixBattle = masterFixBattle; class masterWinFixBattle extends WinFixBattle { constructor(battle, url = 'wss://localho.st:3000') { super(battle, true); this.url = url; } async start(endTime, maxCount) { this.endTime = endTime; this.maxCount = maxCount; this.init(); this.wsStart(); return await new Promise((resolve) => { this.resolve = resolve; const timeout = this.endTime - Date.now(); this.timeout(this.getTask.bind(this), timeout); }); } async endFix(solutions) { this.ws.close(); let maxCount = 0; for (const solution of solutions) { maxCount += solution.maxCount; if (solution.value > this.bestResult.value) { this.bestResult = solution; } } this.count = maxCount; super.endFix(); } } Object.assign(masterWinFixBattle.prototype, masterWsMixin); this.HWHClasses.masterWinFixBattle = masterWinFixBattle; const slaveWsMixin = { wsStop() { this.ws.close(); }, wsStart() { const socket = new WebSocket(this.url); socket.onopen = () => { console.log('Connected to server'); }; socket.onmessage = this.onmessage.bind(this); socket.onclose = () => { console.log('Disconnected from server'); }; this.ws = socket; }, async onmessage(event) { const data = JSON.parse(event.data); switch (data.type) { case 'newTask': { console.log('newTask:', data.task); const { battle, endTime, maxCount } = data.task; this.battle = battle; const id = data.task.id; const solution = await this.start(endTime, maxCount); this.ws.send( JSON.stringify({ type: 'resolveTask', id, solution, }) ); break; } default: console.log('Unknown message type:', data.type); } }, }; /* sFix = new action.slaveFixBattle(); sFix.wsStart() */ class slaveFixBattle extends FixBattle { constructor(url = 'wss://localho.st:3000') { super(null, false); this.isTimeout = false; this.url = url; } } Object.assign(slaveFixBattle.prototype, slaveWsMixin); this.HWHClasses.slaveFixBattle = slaveFixBattle; class slaveWinFixBattle extends WinFixBattle { constructor(url = 'wss://localho.st:3000') { super(null, false); this.isTimeout = false; this.url = url; } } Object.assign(slaveWinFixBattle.prototype, slaveWsMixin); this.HWHClasses.slaveWinFixBattle = slaveWinFixBattle; /** * Auto-repeat attack * * Автоповтор атаки */ function testAutoBattle() { const { executeAutoBattle } = HWHClasses; return new Promise((resolve, reject) => { const bossBattle = new executeAutoBattle(resolve, reject); bossBattle.start(lastBattleArg, lastBattleInfo); }); } /** * Auto-repeat attack * * Автоповтор атаки */ function executeAutoBattle(resolve, reject) { let battleArg = {}; let countBattle = 0; let countError = 0; let findCoeff = 0; let dataNotEeceived = 0; let stopAutoBattle = false; let isSetWinTimer = false; const svgJustice = '<svg width="20" height="20" viewBox="0 0 124 125" xmlns="http://www.w3.org/2000/svg" style="fill: #fff;"><g><path d="m54 0h-1c-7.25 6.05-17.17 6.97-25.78 10.22-8.6 3.25-23.68 1.07-23.22 12.78s-0.47 24.08 1 35 2.36 18.36 7 28c4.43-8.31-3.26-18.88-3-30 0.26-11.11-2.26-25.29-1-37 11.88-4.16 26.27-0.42 36.77-9.23s20.53 6.05 29.23-0.77c-6.65-2.98-14.08-4.96-20-9z"/></g><g><path d="m108 5c-11.05 2.96-27.82 2.2-35.08 11.92s-14.91 14.71-22.67 23.33c-7.77 8.62-14.61 15.22-22.25 23.75 7.05 11.93 14.33 2.58 20.75-4.25 6.42-6.82 12.98-13.03 19.5-19.5s12.34-13.58 19.75-18.25c2.92 7.29-8.32 12.65-13.25 18.75-4.93 6.11-12.19 11.48-17.5 17.5s-12.31 11.38-17.25 17.75c10.34 14.49 17.06-3.04 26.77-10.23s15.98-16.89 26.48-24.52c10.5-7.64 12.09-24.46 14.75-36.25z"/></g><g><path d="m60 25c-11.52-6.74-24.53 8.28-38 6 0.84 9.61-1.96 20.2 2 29 5.53-4.04-4.15-23.2 4.33-26.67 8.48-3.48 18.14-1.1 24.67-8.33 2.73 0.3 4.81 2.98 7 0z"/></g><g><path d="m100 75c3.84-11.28 5.62-25.85 3-38-4.2 5.12-3.5 13.58-4 20s-3.52 13.18 1 18z"/></g><g><path d="m55 94c15.66-5.61 33.71-20.85 29-39-3.07 8.05-4.3 16.83-10.75 23.25s-14.76 8.35-18.25 15.75z"/></g><g><path d="m0 94v7c6.05 3.66 9.48 13.3 18 11-3.54-11.78 8.07-17.05 14-25 6.66 1.52 13.43 16.26 19 5-11.12-9.62-20.84-21.33-32-31-9.35 6.63 4.76 11.99 6 19-7.88 5.84-13.24 17.59-25 14z"/></g><g><path d="m82 125h26v-19h16v-1c-11.21-8.32-18.38-21.74-30-29-8.59 10.26-19.05 19.27-27 30h15v19z"/></g><g><path d="m68 110c-7.68-1.45-15.22 4.83-21.92-1.08s-11.94-5.72-18.08-11.92c-3.03 8.84 10.66 9.88 16.92 16.08s17.09 3.47 23.08-3.08z"/></g></svg>'; const svgBoss = '<svg width="20" height="20" viewBox="0 0 40 41" xmlns="http://www.w3.org/2000/svg" style="fill: #fff;"><g><path d="m21 12c-2.19-3.23 5.54-10.95-0.97-10.97-6.52-0.02 1.07 7.75-1.03 10.97-2.81 0.28-5.49-0.2-8-1-0.68 3.53 0.55 6.06 4 4 0.65 7.03 1.11 10.95 1.67 18.33 0.57 7.38 6.13 7.2 6.55-0.11 0.42-7.3 1.35-11.22 1.78-18.22 3.53 1.9 4.73-0.42 4-4-2.61 0.73-5.14 1.35-8 1m-1 17c-1.59-3.6-1.71-10.47 0-14 1.59 3.6 1.71 10.47 0 14z"/></g><g><path d="m6 19c-1.24-4.15 2.69-8.87 1-12-3.67 4.93-6.52 10.57-6 17 5.64-0.15 8.82 4.98 13 8 1.3-6.54-0.67-12.84-8-13z"/></g><g><path d="m33 7c0.38 5.57 2.86 14.79-7 15v10c4.13-2.88 7.55-7.97 13-8 0.48-6.46-2.29-12.06-6-17z"/></g></svg>'; const svgAttempt = '<svg width="20" height="20" viewBox="0 0 645 645" xmlns="http://www.w3.org/2000/svg" style="fill: #fff;"><g><path d="m442 26c-8.8 5.43-6.6 21.6-12.01 30.99-2.5 11.49-5.75 22.74-8.99 34.01-40.61-17.87-92.26-15.55-133.32-0.32-72.48 27.31-121.88 100.19-142.68 171.32 10.95-4.49 19.28-14.97 29.3-21.7 50.76-37.03 121.21-79.04 183.47-44.07 16.68 5.8 2.57 21.22-0.84 31.7-4.14 12.19-11.44 23.41-13.93 36.07 56.01-17.98 110.53-41.23 166-61-20.49-59.54-46.13-117.58-67-177z"/></g><g><path d="m563 547c23.89-16.34 36.1-45.65 47.68-71.32 23.57-62.18 7.55-133.48-28.38-186.98-15.1-22.67-31.75-47.63-54.3-63.7 1.15 14.03 6.71 26.8 8.22 40.78 12.08 61.99 15.82 148.76-48.15 183.29-10.46-0.54-15.99-16.1-24.32-22.82-8.2-7.58-14.24-19.47-23.75-24.25-4.88 59.04-11.18 117.71-15 177 62.9 5.42 126.11 9.6 189 15-4.84-9.83-17.31-15.4-24.77-24.23-9.02-7.06-17.8-15.13-26.23-22.77z"/></g><g><path d="m276 412c-10.69-15.84-30.13-25.9-43.77-40.23-15.39-12.46-30.17-25.94-45.48-38.52-15.82-11.86-29.44-28.88-46.75-37.25-19.07 24.63-39.96 48.68-60.25 72.75-18.71 24.89-42.41 47.33-58.75 73.25 22.4-2.87 44.99-13.6 66.67-13.67 0.06 22.8 10.69 42.82 20.41 62.59 49.09 93.66 166.6 114.55 261.92 96.08-6.07-9.2-22.11-9.75-31.92-16.08-59.45-26.79-138.88-75.54-127.08-151.92 21.66-2.39 43.42-4.37 65-7z"/></g></svg>'; this.start = function (battleArgs, battleInfo) { battleArg = battleArgs; if (nameFuncStartBattle == 'invasion_bossStart') { startBattle(); return; } preCalcBattle(battleInfo); } /** * Returns a promise for combat recalculation * * Возвращает промис для прерасчета боя */ function getBattleInfo(battle) { return new Promise(function (resolve) { battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3); Calc(battle).then(e => { e.coeff = calcCoeff(e, 'defenders'); resolve(e); }); }); } /** * Battle recalculation * * Прерасчет боя */ function preCalcBattle(battle) { let actions = []; const countTestBattle = getInput('countTestBattle'); for (let i = 0; i < countTestBattle; i++) { actions.push(getBattleInfo(battle)); } Promise.all(actions) .then(resultPreCalcBattle); } /** * Processing the results of the battle recalculation * * Обработка результатов прерасчета боя */ async function resultPreCalcBattle(results) { let countWin = results.reduce((s, w) => w.result.win + s, 0); setProgress(`${I18N('CHANCE_TO_WIN')} ${Math.floor(countWin / results.length * 100)}% (${results.length})`, false, hideProgress); if (countWin > 0) { setIsCancalBattle(false); startBattle(); return; } let minCoeff = 100; let maxCoeff = -100; let avgCoeff = 0; results.forEach(e => { if (e.coeff < minCoeff) minCoeff = e.coeff; if (e.coeff > maxCoeff) maxCoeff = e.coeff; avgCoeff += e.coeff; }); avgCoeff /= results.length; if (nameFuncStartBattle == 'invasion_bossStart' || nameFuncStartBattle == 'bossAttack') { const result = await popup.confirm( I18N('BOSS_VICTORY_IMPOSSIBLE', { battles: results.length }), [ { msg: I18N('BTN_CANCEL'), result: false, isCancel: true }, { msg: I18N('BTN_DO_IT'), result: true }, ]) if (result) { setIsCancalBattle(false); startBattle(); return; } setProgress(I18N('NOT_THIS_TIME'), true); endAutoBattle('invasion_bossStart'); return; } const result = await popup.confirm( I18N('VICTORY_IMPOSSIBLE') + `<br>${I18N('ROUND_STAT')} ${results.length} ${I18N('BATTLE')}:` + `<br>${I18N('MINIMUM')}: ` + minCoeff.toLocaleString() + `<br>${I18N('MAXIMUM')}: ` + maxCoeff.toLocaleString() + `<br>${I18N('AVERAGE')}: ` + avgCoeff.toLocaleString() + `<br>${I18N('FIND_COEFF')} ` + avgCoeff.toLocaleString(), [ { msg: I18N('BTN_CANCEL'), result: 0, isCancel: true }, { msg: I18N('BTN_GO'), isInput: true, default: Math.round(avgCoeff * 1000) / 1000 }, ]) if (result) { findCoeff = result; setIsCancalBattle(false); startBattle(); return; } setProgress(I18N('NOT_THIS_TIME'), true); endAutoBattle(I18N('NOT_THIS_TIME')); } /** * Calculation of the combat result coefficient * * Расчет коэфициента результата боя */ function calcCoeff(result, packType) { let beforeSumFactor = 0; const beforePack = result.battleData[packType][0]; for (let heroId in beforePack) { const hero = beforePack[heroId]; const state = hero.state; let factor = 1; if (state) { const hp = state.hp / state.maxHp; const energy = state.energy / 1e3; factor = hp + energy / 20; } beforeSumFactor += factor; } let afterSumFactor = 0; const afterPack = result.progress[0][packType].heroes; for (let heroId in afterPack) { const hero = afterPack[heroId]; const stateHp = beforePack[heroId]?.state?.hp || beforePack[heroId]?.stats?.hp; const hp = hero.hp / stateHp; const energy = hero.energy / 1e3; const factor = hp + energy / 20; afterSumFactor += factor; } const resultCoeff = -(afterSumFactor - beforeSumFactor); return Math.round(resultCoeff * 1000) / 1000; } /** * Start battle * * Начало боя */ function startBattle() { countBattle++; const countMaxBattle = getInput('countAutoBattle'); // setProgress(countBattle + '/' + countMaxBattle); if (countBattle > countMaxBattle) { setProgress(`${I18N('RETRY_LIMIT_EXCEEDED')}: ${countMaxBattle}`, true); endAutoBattle(`${I18N('RETRY_LIMIT_EXCEEDED')}: ${countMaxBattle}`) return; } if (stopAutoBattle) { setProgress(I18N('STOPPED'), true); endAutoBattle('STOPPED'); return; } send({calls: [{ name: nameFuncStartBattle, args: battleArg, ident: "body" }]}, calcResultBattle); } /** * Battle calculation * * Расчет боя */ async function calcResultBattle(e) { if (!e) { console.log('данные не были получены'); if (dataNotEeceived < 10) { dataNotEeceived++; startBattle(); return; } endAutoBattle('Error', 'данные не были получены ' + dataNotEeceived + ' раз'); return; } if ('error' in e) { if (e.error.description === 'too many tries') { invasionTimer += 100; countBattle--; countError++; console.log(`Errors: ${countError}`, e.error); startBattle(); return; } const result = await popup.confirm(I18N('ERROR_DURING_THE_BATTLE') + '<br>' + e.error.description, [ { msg: I18N('BTN_OK'), result: false }, { msg: I18N('RELOAD_GAME'), result: true }, ]); endAutoBattle('Error', e.error); if (result) { location.reload(); } return; } let battle = e.results[0].result.response.battle if (nameFuncStartBattle == 'towerStartBattle' || nameFuncStartBattle == 'bossAttack' || nameFuncStartBattle == 'invasion_bossStart') { battle = e.results[0].result.response; } lastBattleInfo = battle; BattleCalc(battle, getBattleType(battle.type), resultBattle); } /** * Processing the results of the battle * * Обработка результатов боя */ async function resultBattle(e) { const isWin = e.result.win; if (isWin) { endBattle(e, false); return; } else if (isChecked('tryFixIt_v2')) { const { WinFixBattle } = HWHClasses; const cloneBattle = structuredClone(e.battleData); const bFix = new WinFixBattle(cloneBattle); let attempts = Infinity; if (nameFuncStartBattle == 'invasion_bossStart' && !isSetWinTimer) { let winTimer = await popup.confirm(`Secret number:`, [ { result: false, isClose: true }, { msg: 'Go', isInput: true, default: '0' }, ]); winTimer = Number.parseFloat(winTimer); if (winTimer) { attempts = 5; bFix.setWinTimer(winTimer); } isSetWinTimer = true; } let endTime = Date.now() + 6e4; if (nameFuncStartBattle == 'invasion_bossStart') { endTime = Date.now() + 6e4 * 4; bFix.setMaxTimer(120.3); } const result = await bFix.start(endTime, attempts); console.log(result); if (result.value) { endBattle(result, false); return; } } const countMaxBattle = getInput('countAutoBattle'); if (findCoeff) { const coeff = calcCoeff(e, 'defenders'); setProgress(`${countBattle}/${countMaxBattle}, ${coeff}`); if (coeff > findCoeff) { endBattle(e, false); return; } } else { if (nameFuncStartBattle == 'invasion_bossStart') { const bossLvl = lastBattleInfo.typeId >= 130 ? lastBattleInfo.typeId : ''; const justice = lastBattleInfo?.effects?.attackers?.percentInOutDamageModAndEnergyIncrease_any_99_100_300_99_1000_300 || 0; setProgress(`${svgBoss} ${bossLvl} ${svgJustice} ${justice} <br>${svgAttempt} ${countBattle}/${countMaxBattle}`, false, () => { stopAutoBattle = true; }); await new Promise((resolve) => setTimeout(resolve, 5000)); } else { setProgress(`${countBattle}/${countMaxBattle}`); } } if (nameFuncStartBattle == 'towerStartBattle' || nameFuncStartBattle == 'bossAttack' || nameFuncStartBattle == 'invasion_bossStart') { startBattle(); return; } cancelEndBattle(e); } /** * Cancel fight * * Отмена боя */ function cancelEndBattle(r) { const fixBattle = function (heroes) { for (const ids in heroes) { hero = heroes[ids]; hero.energy = random(1, 999); if (hero.hp > 0) { hero.hp = random(1, hero.hp); } } } fixBattle(r.progress[0].attackers.heroes); fixBattle(r.progress[0].defenders.heroes); endBattle(r, true); } /** * End of the fight * * Завершение боя */ function endBattle(battleResult, isCancal) { let calls = [{ name: nameFuncEndBattle, args: { result: battleResult.result, progress: battleResult.progress }, ident: "body" }]; if (nameFuncStartBattle == 'invasion_bossStart') { calls[0].args.id = lastBattleArg.id; } send(JSON.stringify({ calls }), async e => { console.log(e); if (isCancal) { startBattle(); return; } setProgress(`${I18N('SUCCESS')}!`, 5000) if (nameFuncStartBattle == 'invasion_bossStart' || nameFuncStartBattle == 'bossAttack') { const countMaxBattle = getInput('countAutoBattle'); const bossLvl = lastBattleInfo.typeId >= 130 ? lastBattleInfo.typeId : ''; const justice = lastBattleInfo?.effects?.attackers?.percentInOutDamageModAndEnergyIncrease_any_99_100_300_99_1000_300 || 0; let winTimer = ''; if (nameFuncStartBattle == 'invasion_bossStart') { winTimer = '<br>Secret number: ' + battleResult.progress[0].attackers.input[5]; } const result = await popup.confirm( I18N('BOSS_HAS_BEEN_DEF_TEXT', { bossLvl: `${svgBoss} ${bossLvl} ${svgJustice} ${justice}`, countBattle: svgAttempt + ' ' + countBattle, countMaxBattle, winTimer, }), [ { msg: I18N('BTN_OK'), result: 0 }, { msg: I18N('MAKE_A_SYNC'), result: 1 }, { msg: I18N('RELOAD_GAME'), result: 2 }, ] ); if (result) { if (result == 1) { cheats.refreshGame(); } if (result == 2) { location.reload(); } } } endAutoBattle(`${I18N('SUCCESS')}!`) }); } /** * Completing a task * * Завершение задачи */ function endAutoBattle(reason, info) { setIsCancalBattle(true); console.log(reason, info); resolve(); } } this.HWHClasses.executeAutoBattle = executeAutoBattle; function testDailyQuests() { const { dailyQuests } = HWHClasses; return new Promise((resolve, reject) => { const quests = new dailyQuests(resolve, reject); quests.init(questsInfo); quests.start(); }); } /** * Automatic completion of daily quests * * Автоматическое выполнение ежедневных квестов */ class dailyQuests { /** * Send(' {"calls":[{"name":"userGetInfo","args":{},"ident":"body"}]}').then(e => console.log(e)) * Send(' {"calls":[{"name":"heroGetAll","args":{},"ident":"body"}]}').then(e => console.log(e)) * Send(' {"calls":[{"name":"titanGetAll","args":{},"ident":"body"}]}').then(e => console.log(e)) * Send(' {"calls":[{"name":"inventoryGet","args":{},"ident":"body"}]}').then(e => console.log(e)) * Send(' {"calls":[{"name":"questGetAll","args":{},"ident":"body"}]}').then(e => console.log(e)) * Send(' {"calls":[{"name":"bossGetAll","args":{},"ident":"body"}]}').then(e => console.log(e)) */ callsList = ['userGetInfo', 'heroGetAll', 'titanGetAll', 'inventoryGet', 'questGetAll', 'bossGetAll', 'missionGetAll']; dataQuests = { 10001: { description: 'Улучши умения героев 3 раза', // ++++++++++++++++ doItCall: () => { const upgradeSkills = this.getUpgradeSkills(); return upgradeSkills.map(({ heroId, skill }, index) => ({ name: 'heroUpgradeSkill', args: { heroId, skill }, ident: `heroUpgradeSkill_${index}`, })); }, isWeCanDo: () => { const upgradeSkills = this.getUpgradeSkills(); let sumGold = 0; for (const skill of upgradeSkills) { sumGold += this.skillCost(skill.value); if (!skill.heroId) { return false; } } return this.questInfo['userGetInfo'].gold > sumGold; }, }, 10002: { description: 'Пройди 10 миссий', // -------------- isWeCanDo: () => false, }, 10003: { description: 'Пройди 3 героические миссии', // ++++++++++++++++ isWeCanDo: () => { const vipPoints = +this.questInfo.userGetInfo.vipPoints; const goldTicket = !!this.questInfo.inventoryGet.consumable[151]; return (vipPoints > 100 || goldTicket) && this.getHeroicMissionId(); }, doItCall: () => { const selectedMissionId = this.getHeroicMissionId(); const goldTicket = !!this.questInfo.inventoryGet.consumable[151]; const vipLevel = Math.max(...lib.data.level.vip.filter(l => l.vipPoints <= +this.questInfo.userGetInfo.vipPoints).map(l => l.level)); // Возвращаем массив команд для рейда if (vipLevel >= 5 || goldTicket) { return [{ name: 'missionRaid', args: { id: selectedMissionId, times: 3 }, ident: 'missionRaid_1' }]; } else { return [ { name: 'missionRaid', args: { id: selectedMissionId, times: 1 }, ident: 'missionRaid_1' }, { name: 'missionRaid', args: { id: selectedMissionId, times: 1 }, ident: 'missionRaid_2' }, { name: 'missionRaid', args: { id: selectedMissionId, times: 1 }, ident: 'missionRaid_3' }, ]; } }, }, 10004: { description: 'Сразись 3 раза на Арене или Гранд Арене', // -------------- isWeCanDo: () => false, }, 10006: { description: 'Используй обмен изумрудов 1 раз', // ++++++++++++++++ doItCall: () => [ { name: 'refillableAlchemyUse', args: { multi: false }, ident: 'refillableAlchemyUse', }, ], isWeCanDo: () => { const starMoney = this.questInfo['userGetInfo'].starMoney; return starMoney >= 20; }, }, 10007: { description: 'Соверши 1 призыв в Атриуме Душ', // ++++++++++++++++ doItCall: () => [{ name: 'gacha_open', args: { ident: 'heroGacha', free: true, pack: false }, ident: 'gacha_open' }], isWeCanDo: () => { const soulCrystal = this.questInfo['inventoryGet'].coin[38]; return soulCrystal > 0; }, }, 10016: { description: 'Отправь подарки согильдийцам', // ++++++++++++++++ doItCall: () => [{ name: 'clanSendDailyGifts', args: {}, ident: 'clanSendDailyGifts' }], isWeCanDo: () => true, }, 10018: { description: 'Используй зелье опыта', // ++++++++++++++++ doItCall: () => { const expHero = this.getExpHero(); return [ { name: 'consumableUseHeroXp', args: { heroId: expHero.heroId, libId: expHero.libId, amount: 1, }, ident: 'consumableUseHeroXp', }, ]; }, isWeCanDo: () => { const expHero = this.getExpHero(); return expHero.heroId && expHero.libId; }, }, 10019: { description: 'Открой 1 сундук в Башне', doItFunc: testTower, isWeCanDo: () => false, }, 10020: { description: 'Открой 3 сундука в Запределье', // Готово doItCall: () => { return this.getOutlandChest(); }, isWeCanDo: () => { const outlandChest = this.getOutlandChest(); return outlandChest.length > 0; }, }, 10021: { description: 'Собери 75 Титанита в Подземелье Гильдии', isWeCanDo: () => false, }, 10022: { description: 'Собери 150 Титанита в Подземелье Гильдии', doItFunc: testDungeon, isWeCanDo: () => false, }, 10023: { description: 'Прокачай Дар Стихий на 1 уровень', // Готово doItCall: () => { const heroId = this.getHeroIdTitanGift(); return [ { name: 'heroTitanGiftLevelUp', args: { heroId }, ident: 'heroTitanGiftLevelUp' }, { name: 'heroTitanGiftDrop', args: { heroId }, ident: 'heroTitanGiftDrop' }, ]; }, isWeCanDo: () => { const heroId = this.getHeroIdTitanGift(); return heroId; }, }, 10024: { description: 'Повысь уровень любого артефакта один раз', // Готово doItCall: () => { const upArtifact = this.getUpgradeArtifact(); return [ { name: 'heroArtifactLevelUp', args: { heroId: upArtifact.heroId, slotId: upArtifact.slotId, }, ident: `heroArtifactLevelUp`, }, ]; }, isWeCanDo: () => { const upgradeArtifact = this.getUpgradeArtifact(); return upgradeArtifact.heroId; }, }, 10025: { description: 'Начни 1 Экспедицию', doItFunc: checkExpedition, isWeCanDo: () => false, }, 10026: { description: 'Начни 4 Экспедиции', // -------------- doItFunc: checkExpedition, isWeCanDo: () => false, }, 10027: { description: 'Победи в 1 бою Турнира Стихий', doItFunc: testTitanArena, isWeCanDo: () => false, }, 10028: { description: 'Повысь уровень любого артефакта титанов', // Готово doItCall: () => { const upTitanArtifact = this.getUpgradeTitanArtifact(); return [ { name: 'titanArtifactLevelUp', args: { titanId: upTitanArtifact.titanId, slotId: upTitanArtifact.slotId, }, ident: `titanArtifactLevelUp`, }, ]; }, isWeCanDo: () => { const upgradeTitanArtifact = this.getUpgradeTitanArtifact(); return upgradeTitanArtifact.titanId; }, }, 10029: { description: 'Открой сферу артефактов титанов', // ++++++++++++++++ doItCall: () => [{ name: 'titanArtifactChestOpen', args: { amount: 1, free: true }, ident: 'titanArtifactChestOpen' }], isWeCanDo: () => { return this.questInfo['inventoryGet']?.consumable[55] > 0; }, }, 10030: { description: 'Улучши облик любого героя 1 раз', // Готово doItCall: () => { const upSkin = this.getUpgradeSkin(); return [ { name: 'heroSkinUpgrade', args: { heroId: upSkin.heroId, skinId: upSkin.skinId, }, ident: `heroSkinUpgrade`, }, ]; }, isWeCanDo: () => { const upgradeSkin = this.getUpgradeSkin(); return upgradeSkin.heroId; }, }, 10031: { description: 'Победи в 6 боях Турнира Стихий', // -------------- doItFunc: testTitanArena, isWeCanDo: () => false, }, 10043: { description: 'Начни или присоеденись к Приключению', // -------------- isWeCanDo: () => false, }, 10044: { description: 'Воспользуйся призывом питомцев 1 раз', // ++++++++++++++++ doItCall: () => [{ name: 'pet_chestOpen', args: { amount: 1, paid: false }, ident: 'pet_chestOpen' }], isWeCanDo: () => { return this.questInfo['inventoryGet']?.consumable[90] > 0; }, }, 10046: { /** * TODO: Watch Adventure * TODO: Смотреть приключение */ description: 'Открой 3 сундука в Приключениях', isWeCanDo: () => false, }, 10047: { description: 'Набери 150 очков активности в Гильдии', // Готово doItCall: () => { const enchantRune = this.getEnchantRune(); return [ { name: 'heroEnchantRune', args: { heroId: enchantRune.heroId, tier: enchantRune.tier, items: { consumable: { [enchantRune.itemId]: 1 }, }, }, ident: `heroEnchantRune`, }, ]; }, isWeCanDo: () => { const userInfo = this.questInfo['userGetInfo']; const enchantRune = this.getEnchantRune(); return enchantRune.heroId && userInfo.gold > 1e3; }, }, }; constructor(resolve, reject, questInfo) { this.resolve = resolve; this.reject = reject; } init(questInfo) { this.questInfo = questInfo; this.isAuto = false; } async autoInit(isAuto) { this.isAuto = isAuto || false; const quests = {}; const calls = this.callsList.map((name) => ({ name, args: {}, ident: name, })); const result = await Send(JSON.stringify({ calls })).then((e) => e.results); for (const call of result) { quests[call.ident] = call.result.response; } this.questInfo = quests; } async start() { const weCanDo = []; const selectedActions = getSaveVal('selectedActions', {}); for (let quest of this.questInfo['questGetAll']) { if (quest.id in this.dataQuests && quest.state == 1) { if (!selectedActions[quest.id]) { selectedActions[quest.id] = { checked: false, }; } const isWeCanDo = this.dataQuests[quest.id].isWeCanDo; if (!isWeCanDo.call(this)) { continue; } weCanDo.push({ name: quest.id, label: I18N(`QUEST_${quest.id}`), checked: selectedActions[quest.id].checked, }); } } if (!weCanDo.length) { this.end(I18N('NOTHING_TO_DO')); return; } console.log(weCanDo); let taskList = []; if (this.isAuto) { taskList = weCanDo; } else { const answer = await popup.confirm( `${I18N('YOU_CAN_COMPLETE')}:`, [ { msg: I18N('BTN_DO_IT'), result: true }, { msg: I18N('BTN_CANCEL'), result: false, isCancel: true }, ], weCanDo ); if (!answer) { this.end(''); return; } taskList = popup.getCheckBoxes(); taskList.forEach((e) => { selectedActions[e.name].checked = e.checked; }); setSaveVal('selectedActions', selectedActions); } const calls = []; let countChecked = 0; for (const task of taskList) { if (task.checked) { countChecked++; const quest = this.dataQuests[task.name]; console.log(quest.description); if (quest.doItCall) { const doItCall = quest.doItCall.call(this); calls.push(...doItCall); } } } if (!countChecked) { this.end(I18N('NOT_QUEST_COMPLETED')); return; } const result = await Send(JSON.stringify({ calls })); if (result.error) { console.error(result.error, result.error.call); } this.end(`${I18N('COMPLETED_QUESTS')}: ${countChecked}`); } errorHandling(error) { //console.error(error); let errorInfo = error.toString() + '\n'; try { const errorStack = error.stack.split('\n'); const endStack = errorStack.map((e) => e.split('@')[0]).indexOf('testDoYourBest'); errorInfo += errorStack.slice(0, endStack).join('\n'); } catch (e) { errorInfo += error.stack; } copyText(errorInfo); } skillCost(lvl) { return 573 * lvl ** 0.9 + lvl ** 2.379; } getUpgradeSkills() { const heroes = Object.values(this.questInfo['heroGetAll']); const upgradeSkills = [ { heroId: 0, slotId: 0, value: 130 }, { heroId: 0, slotId: 0, value: 130 }, { heroId: 0, slotId: 0, value: 130 }, ]; const skillLib = lib.getData('skill'); /** * color - 1 (белый) открывает 1 навык * color - 2 (зеленый) открывает 2 навык * color - 4 (синий) открывает 3 навык * color - 7 (фиолетовый) открывает 4 навык */ const colors = [1, 2, 4, 7]; for (const hero of heroes) { const level = hero.level; const color = hero.color; for (let skillId in hero.skills) { const tier = skillLib[skillId].tier; const sVal = hero.skills[skillId]; if (color < colors[tier] || tier < 1 || tier > 4) { continue; } for (let upSkill of upgradeSkills) { if (sVal < upSkill.value && sVal < level) { upSkill.value = sVal; upSkill.heroId = hero.id; upSkill.skill = tier; break; } } } } return upgradeSkills; } getUpgradeArtifact() { const heroes = Object.values(this.questInfo['heroGetAll']); const inventory = this.questInfo['inventoryGet']; const upArt = { heroId: 0, slotId: 0, level: 100 }; const heroLib = lib.getData('hero'); const artifactLib = lib.getData('artifact'); for (const hero of heroes) { const heroInfo = heroLib[hero.id]; const level = hero.level; if (level < 20) { continue; } for (let slotId in hero.artifacts) { const art = hero.artifacts[slotId]; /* Текущая звезданость арта */ const star = art.star; if (!star) { continue; } /* Текущий уровень арта */ const level = art.level; if (level >= 100) { continue; } /* Идентификатор арта в библиотеке */ const artifactId = heroInfo.artifacts[slotId]; const artInfo = artifactLib.id[artifactId]; const costNextLevel = artifactLib.type[artInfo.type].levels[level + 1].cost; const costCurrency = Object.keys(costNextLevel).pop(); const costValues = Object.entries(costNextLevel[costCurrency]).pop(); const costId = costValues[0]; const costValue = +costValues[1]; /** TODO: Возможно стоит искать самый высокий уровень который можно качнуть? */ if (level < upArt.level && inventory[costCurrency][costId] >= costValue) { upArt.level = level; upArt.heroId = hero.id; upArt.slotId = slotId; upArt.costCurrency = costCurrency; upArt.costId = costId; upArt.costValue = costValue; } } } return upArt; } getUpgradeSkin() { const heroes = Object.values(this.questInfo['heroGetAll']); const inventory = this.questInfo['inventoryGet']; const upSkin = { heroId: 0, skinId: 0, level: 60, cost: 1500 }; const skinLib = lib.getData('skin'); for (const hero of heroes) { const level = hero.level; if (level < 20) { continue; } for (let skinId in hero.skins) { /* Текущий уровень скина */ const level = hero.skins[skinId]; if (level >= 60) { continue; } /* Идентификатор скина в библиотеке */ const skinInfo = skinLib[skinId]; if (!skinInfo.statData.levels?.[level + 1]) { continue; } const costNextLevel = skinInfo.statData.levels[level + 1].cost; const costCurrency = Object.keys(costNextLevel).pop(); const costCurrencyId = Object.keys(costNextLevel[costCurrency]).pop(); const costValue = +costNextLevel[costCurrency][costCurrencyId]; /** TODO: Возможно стоит искать самый высокий уровень который можно качнуть? */ if (level < upSkin.level && costValue < upSkin.cost && inventory[costCurrency][costCurrencyId] >= costValue) { upSkin.cost = costValue; upSkin.level = level; upSkin.heroId = hero.id; upSkin.skinId = skinId; upSkin.costCurrency = costCurrency; upSkin.costCurrencyId = costCurrencyId; } } } return upSkin; } getUpgradeTitanArtifact() { const titans = Object.values(this.questInfo['titanGetAll']); const inventory = this.questInfo['inventoryGet']; const userInfo = this.questInfo['userGetInfo']; const upArt = { titanId: 0, slotId: 0, level: 120 }; const titanLib = lib.getData('titan'); const artTitanLib = lib.getData('titanArtifact'); for (const titan of titans) { const titanInfo = titanLib[titan.id]; // const level = titan.level // if (level < 20) { // continue; // } for (let slotId in titan.artifacts) { const art = titan.artifacts[slotId]; /* Текущая звезданость арта */ const star = art.star; if (!star) { continue; } /* Текущий уровень арта */ const level = art.level; if (level >= 120) { continue; } /* Идентификатор арта в библиотеке */ const artifactId = titanInfo.artifacts[slotId]; const artInfo = artTitanLib.id[artifactId]; const costNextLevel = artTitanLib.type[artInfo.type].levels[level + 1].cost; const costCurrency = Object.keys(costNextLevel).pop(); let costValue = 0; let currentValue = 0; if (costCurrency == 'gold') { costValue = costNextLevel[costCurrency]; currentValue = userInfo.gold; } else { const costValues = Object.entries(costNextLevel[costCurrency]).pop(); const costId = costValues[0]; costValue = +costValues[1]; currentValue = inventory[costCurrency][costId]; } /** TODO: Возможно стоит искать самый высокий уровень который можно качнуть? */ if (level < upArt.level && currentValue >= costValue) { upArt.level = level; upArt.titanId = titan.id; upArt.slotId = slotId; break; } } } return upArt; } getEnchantRune() { const heroes = Object.values(this.questInfo['heroGetAll']); const inventory = this.questInfo['inventoryGet']; const enchRune = { heroId: 0, tier: 0, exp: 43750, itemId: 0 }; for (let i = 1; i <= 4; i++) { if (inventory.consumable[i] > 0) { enchRune.itemId = i; break; } return enchRune; } const runeLib = lib.getData('rune'); const runeLvls = Object.values(runeLib.level); /** * color - 4 (синий) открывает 1 и 2 символ * color - 7 (фиолетовый) открывает 3 символ * color - 8 (фиолетовый +1) открывает 4 символ * color - 9 (фиолетовый +2) открывает 5 символ */ // TODO: кажется надо учесть уровень команды const colors = [4, 4, 7, 8, 9]; for (const hero of heroes) { const color = hero.color; for (let runeTier in hero.runes) { /* Проверка на доступность руны */ if (color < colors[runeTier]) { continue; } /* Текущий опыт руны */ const exp = hero.runes[runeTier]; if (exp >= 43750) { continue; } let level = 0; if (exp) { for (let lvl of runeLvls) { if (exp >= lvl.enchantValue) { level = lvl.level; } else { break; } } } /** Уровень героя необходимый для уровня руны */ const heroLevel = runeLib.level[level].heroLevel; if (hero.level < heroLevel) { continue; } /** TODO: Возможно стоит искать самый высокий уровень который можно качнуть? */ if (exp < enchRune.exp) { enchRune.exp = exp; enchRune.heroId = hero.id; enchRune.tier = runeTier; break; } } } return enchRune; } getOutlandChest() { const bosses = this.questInfo['bossGetAll']; const calls = []; for (let boss of bosses) { if (boss.mayRaid) { calls.push({ name: 'bossRaid', args: { bossId: boss.id, }, ident: 'bossRaid_' + boss.id, }); calls.push({ name: 'bossOpenChest', args: { bossId: boss.id, amount: 1, starmoney: 0, }, ident: 'bossOpenChest_' + boss.id, }); } else if (boss.chestId == 1) { calls.push({ name: 'bossOpenChest', args: { bossId: boss.id, amount: 1, starmoney: 0, }, ident: 'bossOpenChest_' + boss.id, }); } } return calls; } getExpHero() { const heroes = Object.values(this.questInfo['heroGetAll']); const inventory = this.questInfo['inventoryGet']; const expHero = { heroId: 0, exp: 3625195, libId: 0 }; /** зелья опыта (consumable 9, 10, 11, 12) */ for (let i = 9; i <= 12; i++) { if (inventory.consumable[i]) { expHero.libId = i; break; } } for (const hero of heroes) { const exp = hero.xp; if (exp < expHero.exp) { expHero.heroId = hero.id; } } return expHero; } getHeroIdTitanGift() { const heroes = Object.values(this.questInfo['heroGetAll']); const inventory = this.questInfo['inventoryGet']; const user = this.questInfo['userGetInfo']; const titanGiftLib = lib.getData('titanGift'); /** Искры */ const titanGift = inventory.consumable[24]; let heroId = 0; let minLevel = 30; if (titanGift < 250 || user.gold < 7000) { return 0; } for (const hero of heroes) { if (hero.titanGiftLevel >= 30) { continue; } if (!hero.titanGiftLevel) { return hero.id; } const cost = titanGiftLib[hero.titanGiftLevel].cost; if (minLevel > hero.titanGiftLevel && titanGift >= cost.consumable[24] && user.gold >= cost.gold) { minLevel = hero.titanGiftLevel; heroId = hero.id; } } return heroId; } getHeroicMissionId() { // Получаем доступные миссии с 3 звездами const availableMissionsToRaid = Object.values(this.questInfo.missionGetAll) .filter((mission) => mission.stars === 3) .map((mission) => mission.id); // Получаем героев для улучшения, у которых меньше 6 звезд const heroesToUpgrade = Object.values(this.questInfo.heroGetAll) .filter((hero) => hero.star < 6) .sort((a, b) => b.power - a.power) .map((hero) => hero.id); // Получаем героические миссии, которые доступны для рейдов const heroicMissions = Object.values(lib.data.mission).filter((mission) => mission.isHeroic && availableMissionsToRaid.includes(mission.id)); // Собираем дропы из героических миссий const drops = heroicMissions.map((mission) => { const lastWave = mission.normalMode.waves[mission.normalMode.waves.length - 1]; const allRewards = lastWave.enemies[lastWave.enemies.length - 1] .drop.map((drop) => drop.reward); const heroId = +Object.keys(allRewards.find((reward) => reward.fragmentHero).fragmentHero).pop(); return { id: mission.id, heroId }; }); // Определяем, какие дропы подходят для героев, которых нужно улучшить const heroDrops = heroesToUpgrade.map((heroId) => drops.find((drop) => drop.heroId == heroId)).filter((drop) => drop); const firstMission = heroDrops[0]; // Выбираем миссию для рейда const selectedMissionId = firstMission ? firstMission.id : 1; const stamina = this.questInfo.userGetInfo.refillable.find((x) => x.id == 1).amount; const costMissions = 3 * lib.data.mission[selectedMissionId].normalMode.teamExp; if (stamina < costMissions) { console.log('Энергии не достаточно'); return 0; } return selectedMissionId; } end(status) { setProgress(status, true); this.resolve(); } } this.questRun = dailyQuests; this.HWHClasses.dailyQuests = dailyQuests; function testDoYourBest() { const { doYourBest } = HWHClasses; return new Promise((resolve, reject) => { const doIt = new doYourBest(resolve, reject); doIt.start(); }); } /** * Do everything button * * Кнопка сделать все */ class doYourBest { funcList = [ { name: 'getOutland', label: I18N('ASSEMBLE_OUTLAND'), checked: false }, { name: 'testTower', label: I18N('PASS_THE_TOWER'), checked: false }, { name: 'checkExpedition', label: I18N('CHECK_EXPEDITIONS'), checked: false }, { name: 'testTitanArena', label: I18N('COMPLETE_TOE'), checked: false }, { name: 'mailGetAll', label: I18N('COLLECT_MAIL'), checked: false }, { name: 'collectAllStuff', label: I18N('COLLECT_MISC'), title: I18N('COLLECT_MISC_TITLE'), checked: false }, { name: 'getDailyBonus', label: I18N('DAILY_BONUS'), checked: false }, { name: 'dailyQuests', label: I18N('DO_DAILY_QUESTS'), checked: false }, { name: 'rollAscension', label: I18N('SEER_TITLE'), checked: false }, { name: 'questAllFarm', label: I18N('COLLECT_QUEST_REWARDS'), checked: false }, { name: 'testDungeon', label: I18N('COMPLETE_DUNGEON'), checked: false }, { name: 'synchronization', label: I18N('MAKE_A_SYNC'), checked: false }, { name: 'reloadGame', label: I18N('RELOAD_GAME'), checked: false }, ]; functions = { getOutland, testTower, checkExpedition, testTitanArena, mailGetAll, collectAllStuff: async () => { await offerFarmAllReward(); await Send('{"calls":[{"name":"subscriptionFarm","args":{},"ident":"body"},{"name":"zeppelinGiftFarm","args":{},"ident":"zeppelinGiftFarm"},{"name":"grandFarmCoins","args":{},"ident":"grandFarmCoins"},{"name":"gacha_refill","args":{"ident":"heroGacha"},"ident":"gacha_refill"}]}'); }, dailyQuests: async function () { const quests = new dailyQuests(() => { }, () => { }); await quests.autoInit(true); await quests.start(); }, rollAscension, getDailyBonus, questAllFarm, testDungeon, synchronization: async () => { cheats.refreshGame(); }, reloadGame: async () => { location.reload(); }, } constructor(resolve, reject, questInfo) { this.resolve = resolve; this.reject = reject; this.questInfo = questInfo } async start() { const selectedDoIt = getSaveVal('selectedDoIt', {}); this.funcList.forEach(task => { if (!selectedDoIt[task.name]) { selectedDoIt[task.name] = { checked: task.checked } } else { task.checked = selectedDoIt[task.name].checked } }); const answer = await popup.confirm(I18N('RUN_FUNCTION'), [ { msg: I18N('BTN_CANCEL'), result: false, isCancel: true }, { msg: I18N('BTN_GO'), result: true }, ], this.funcList); if (!answer) { this.end(''); return; } const taskList = popup.getCheckBoxes(); taskList.forEach(task => { selectedDoIt[task.name].checked = task.checked; }); setSaveVal('selectedDoIt', selectedDoIt); for (const task of popup.getCheckBoxes()) { if (task.checked) { try { setProgress(`${task.label} <br>${I18N('PERFORMED')}!`); await this.functions[task.name](); setProgress(`${task.label} <br>${I18N('DONE')}!`); } catch (error) { if (await popup.confirm(`${I18N('ERRORS_OCCURRES')}:<br> ${task.label} <br>${I18N('COPY_ERROR')}?`, [ { msg: I18N('BTN_NO'), result: false }, { msg: I18N('BTN_YES'), result: true }, ])) { this.errorHandling(error); } } } } setTimeout((msg) => { this.end(msg); }, 2000, I18N('ALL_TASK_COMPLETED')); return; } errorHandling(error) { //console.error(error); let errorInfo = error.toString() + '\n'; try { const errorStack = error.stack.split('\n'); const endStack = errorStack.map(e => e.split('@')[0]).indexOf("testDoYourBest"); errorInfo += errorStack.slice(0, endStack).join('\n'); } catch (e) { errorInfo += error.stack; } copyText(errorInfo); } end(status) { setProgress(status, true); this.resolve(); } } this.HWHClasses.doYourBest = doYourBest; /** * Passing the adventure along the specified route * * Прохождение приключения по указанному маршруту */ function testAdventure(type) { const { executeAdventure } = HWHClasses; return new Promise((resolve, reject) => { const bossBattle = new executeAdventure(resolve, reject); bossBattle.start(type); }); } /** * Passing the adventure along the specified route * * Прохождение приключения по указанному маршруту */ class executeAdventure { type = 'default'; actions = { default: { getInfo: "adventure_getInfo", startBattle: 'adventure_turnStartBattle', endBattle: 'adventure_endBattle', collectBuff: 'adventure_turnCollectBuff' }, solo: { getInfo: "adventureSolo_getInfo", startBattle: 'adventureSolo_turnStartBattle', endBattle: 'adventureSolo_endBattle', collectBuff: 'adventureSolo_turnCollectBuff' } } terminatеReason = I18N('UNKNOWN'); callAdventureInfo = { name: "adventure_getInfo", args: {}, ident: "adventure_getInfo" } callTeamGetAll = { name: "teamGetAll", args: {}, ident: "teamGetAll" } callTeamGetFavor = { name: "teamGetFavor", args: {}, ident: "teamGetFavor" } callStartBattle = { name: "adventure_turnStartBattle", args: {}, ident: "body" } callEndBattle = { name: "adventure_endBattle", args: { result: {}, progress: {}, }, ident: "body" } callCollectBuff = { name: "adventure_turnCollectBuff", args: {}, ident: "body" } constructor(resolve, reject) { this.resolve = resolve; this.reject = reject; } async start(type) { this.type = type || this.type; this.callAdventureInfo.name = this.actions[this.type].getInfo; const data = await Send(JSON.stringify({ calls: [ this.callAdventureInfo, this.callTeamGetAll, this.callTeamGetFavor ] })); return this.checkAdventureInfo(data.results); } async getPath() { const oldVal = getSaveVal('adventurePath', ''); const keyPath = `adventurePath:${this.mapIdent}`; const answer = await popup.confirm(I18N('ENTER_THE_PATH'), [ { msg: I18N('START_ADVENTURE'), placeholder: '1,2,3,4,5,6', isInput: true, default: getSaveVal(keyPath, oldVal) }, { msg: I18N('BTN_CANCEL'), result: false, isCancel: true }, ]); if (!answer) { this.terminatеReason = I18N('BTN_CANCELED'); return false; } let path = answer.split(','); if (path.length < 2) { path = answer.split('-'); } if (path.length < 2) { this.terminatеReason = I18N('MUST_TWO_POINTS'); return false; } for (let p in path) { path[p] = +path[p].trim() if (Number.isNaN(path[p])) { this.terminatеReason = I18N('MUST_ONLY_NUMBERS'); return false; } } if (!this.checkPath(path)) { return false; } setSaveVal(keyPath, answer); return path; } checkPath(path) { for (let i = 0; i < path.length - 1; i++) { const currentPoint = path[i]; const nextPoint = path[i + 1]; const isValidPath = this.paths.some(p => (p.from_id === currentPoint && p.to_id === nextPoint) || (p.from_id === nextPoint && p.to_id === currentPoint) ); if (!isValidPath) { this.terminatеReason = I18N('INCORRECT_WAY', { from: currentPoint, to: nextPoint, }); return false; } } return true; } async checkAdventureInfo(data) { this.advInfo = data[0].result.response; if (!this.advInfo) { this.terminatеReason = I18N('NOT_ON_AN_ADVENTURE') ; return this.end(); } const heroesTeam = data[1].result.response.adventure_hero; const favor = data[2]?.result.response.adventure_hero; const heroes = heroesTeam.slice(0, 5); const pet = heroesTeam[5]; this.args = { pet, heroes, favor, path: [], broadcast: false } const advUserInfo = this.advInfo.users[userInfo.id]; this.turnsLeft = advUserInfo.turnsLeft; this.currentNode = advUserInfo.currentNode; this.nodes = this.advInfo.nodes; this.paths = this.advInfo.paths; this.mapIdent = this.advInfo.mapIdent; this.path = await this.getPath(); if (!this.path) { return this.end(); } if (this.currentNode == 1 && this.path[0] != 1) { this.path.unshift(1); } return this.loop(); } async loop() { const position = this.path.indexOf(+this.currentNode); if (!(~position)) { this.terminatеReason = I18N('YOU_IN_NOT_ON_THE_WAY'); return this.end(); } this.path = this.path.slice(position); if ((this.path.length - 1) > this.turnsLeft && await popup.confirm(I18N('ATTEMPTS_NOT_ENOUGH'), [ { msg: I18N('YES_CONTINUE'), result: false }, { msg: I18N('BTN_NO'), result: true }, ])) { this.terminatеReason = I18N('NOT_ENOUGH_AP'); return this.end(); } const toPath = []; for (const nodeId of this.path) { if (!this.turnsLeft) { this.terminatеReason = I18N('ATTEMPTS_ARE_OVER'); return this.end(); } toPath.push(nodeId); console.log(toPath); if (toPath.length > 1) { setProgress(toPath.join(' > ') + ` ${I18N('MOVES')}: ` + this.turnsLeft); } if (nodeId == this.currentNode) { continue; } const nodeInfo = this.getNodeInfo(nodeId); if (nodeInfo.type == 'TYPE_COMBAT') { if (nodeInfo.state == 'empty') { this.turnsLeft--; continue; } /** * Disable regular battle cancellation * * Отключаем штатную отменую боя */ setIsCancalBattle(false); if (await this.battle(toPath)) { this.turnsLeft--; toPath.splice(0, toPath.indexOf(nodeId)); nodeInfo.state = 'empty'; setIsCancalBattle(true); continue; } setIsCancalBattle(true); return this.end() } if (nodeInfo.type == 'TYPE_PLAYERBUFF') { const buff = this.checkBuff(nodeInfo); if (buff == null) { continue; } if (await this.collectBuff(buff, toPath)) { this.turnsLeft--; toPath.splice(0, toPath.indexOf(nodeId)); continue; } this.terminatеReason = I18N('BUFF_GET_ERROR'); return this.end(); } } this.terminatеReason = I18N('SUCCESS'); return this.end(); } /** * Carrying out a fight * * Проведение боя */ async battle(path, preCalc = true) { const data = await this.startBattle(path); try { const battle = data.results[0].result.response.battle; const result = await Calc(battle); if (result.result.win) { const info = await this.endBattle(result); if (info.results[0].result.response?.error) { this.terminatеReason = I18N('BATTLE_END_ERROR'); return false; } } else { await this.cancelBattle(result); if (preCalc && await this.preCalcBattle(battle)) { path = path.slice(-2); for (let i = 1; i <= getInput('countAutoBattle'); i++) { setProgress(`${I18N('AUTOBOT')}: ${i}/${getInput('countAutoBattle')}`); const result = await this.battle(path, false); if (result) { setProgress(I18N('VICTORY')); return true; } } this.terminatеReason = I18N('FAILED_TO_WIN_AUTO'); return false; } return false; } } catch (error) { console.error(error); if (await popup.confirm(I18N('ERROR_OF_THE_BATTLE_COPY'), [ { msg: I18N('BTN_NO'), result: false }, { msg: I18N('BTN_YES'), result: true }, ])) { this.errorHandling(error, data); } this.terminatеReason = I18N('ERROR_DURING_THE_BATTLE'); return false; } return true; } /** * Recalculate battles * * Прерасчтет битвы */ async preCalcBattle(battle) { const countTestBattle = getInput('countTestBattle'); for (let i = 0; i < countTestBattle; i++) { battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3); const result = await Calc(battle); if (result.result.win) { console.log(i, countTestBattle); return true; } } this.terminatеReason = I18N('NO_CHANCE_WIN') + countTestBattle; return false; } /** * Starts a fight * * Начинает бой */ startBattle(path) { this.args.path = path; this.callStartBattle.name = this.actions[this.type].startBattle; this.callStartBattle.args = this.args const calls = [this.callStartBattle]; return Send(JSON.stringify({ calls })); } cancelBattle(battle) { const fixBattle = function (heroes) { for (const ids in heroes) { const hero = heroes[ids]; hero.energy = random(1, 999); if (hero.hp > 0) { hero.hp = random(1, hero.hp); } } } fixBattle(battle.progress[0].attackers.heroes); fixBattle(battle.progress[0].defenders.heroes); return this.endBattle(battle); } /** * Ends the fight * * Заканчивает бой */ endBattle(battle) { this.callEndBattle.name = this.actions[this.type].endBattle; this.callEndBattle.args.result = battle.result this.callEndBattle.args.progress = battle.progress const calls = [this.callEndBattle]; return Send(JSON.stringify({ calls })); } /** * Checks if you can get a buff * * Проверяет можно ли получить баф */ checkBuff(nodeInfo) { let id = null; let value = 0; for (const buffId in nodeInfo.buffs) { const buff = nodeInfo.buffs[buffId]; if (buff.owner == null && buff.value > value) { id = buffId; value = buff.value; } } nodeInfo.buffs[id].owner = 'Я'; return id; } /** * Collects a buff * * Собирает баф */ async collectBuff(buff, path) { this.callCollectBuff.name = this.actions[this.type].collectBuff; this.callCollectBuff.args = { buff, path }; const calls = [this.callCollectBuff]; return Send(JSON.stringify({ calls })); } getNodeInfo(nodeId) { return this.nodes.find(node => node.id == nodeId); } errorHandling(error, data) { //console.error(error); let errorInfo = error.toString() + '\n'; try { const errorStack = error.stack.split('\n'); const endStack = errorStack.map(e => e.split('@')[0]).indexOf("testAdventure"); errorInfo += errorStack.slice(0, endStack).join('\n'); } catch (e) { errorInfo += error.stack; } if (data) { errorInfo += '\nData: ' + JSON.stringify(data); } copyText(errorInfo); } end() { setIsCancalBattle(true); setProgress(this.terminatеReason, true); console.log(this.terminatеReason); this.resolve(); } } this.HWHClasses.executeAdventure = executeAdventure; /** * Passage of brawls * * Прохождение потасовок */ function testBrawls(isAuto) { const { executeBrawls } = HWHClasses; return new Promise((resolve, reject) => { const brawls = new executeBrawls(resolve, reject); brawls.start(brawlsPack, isAuto); }); } /** * Passage of brawls * * Прохождение потасовок */ class executeBrawls { callBrawlQuestGetInfo = { name: "brawl_questGetInfo", args: {}, ident: "brawl_questGetInfo" } callBrawlFindEnemies = { name: "brawl_findEnemies", args: {}, ident: "brawl_findEnemies" } callBrawlQuestFarm = { name: "brawl_questFarm", args: {}, ident: "brawl_questFarm" } callUserGetInfo = { name: "userGetInfo", args: {}, ident: "userGetInfo" } callTeamGetMaxUpgrade = { name: "teamGetMaxUpgrade", args: {}, ident: "teamGetMaxUpgrade" } callBrawlGetInfo = { name: "brawl_getInfo", args: {}, ident: "brawl_getInfo" } stats = { win: 0, loss: 0, count: 0, } stage = { '3': 1, '7': 2, '12': 3, } attempts = 0; constructor(resolve, reject) { this.resolve = resolve; this.reject = reject; const allHeroIds = Object.keys(lib.getData('hero')); this.callTeamGetMaxUpgrade.args.units = { hero: allHeroIds.filter((id) => +id < 1000), titan: allHeroIds.filter((id) => +id >= 4000 && +id < 4100), pet: allHeroIds.filter((id) => +id >= 6000 && +id < 6100), }; } async start(args, isAuto) { this.isAuto = isAuto; this.args = args; setIsCancalBattle(false); this.brawlInfo = await this.getBrawlInfo(); this.attempts = this.brawlInfo.attempts; if (!this.attempts && !this.info.boughtEndlessLivesToday) { this.end(I18N('DONT_HAVE_LIVES')); return; } while (1) { if (!isBrawlsAutoStart) { this.end(I18N('BTN_CANCELED')); return; } const maxStage = this.brawlInfo.questInfo.stage; const stage = this.stage[maxStage]; const progress = this.brawlInfo.questInfo.progress; setProgress( `${I18N('STAGE')} ${stage}: ${progress}/${maxStage}<br>${I18N('FIGHTS')}: ${this.stats.count}<br>${I18N('WINS')}: ${ this.stats.win }<br>${I18N('LOSSES')}: ${this.stats.loss}<br>${I18N('LIVES')}: ${this.attempts}<br>${I18N('STOP')}`, false, function () { isBrawlsAutoStart = false; } ); if (this.brawlInfo.questInfo.canFarm) { const result = await this.questFarm(); console.log(result); } if (!this.continueAttack && this.brawlInfo.questInfo.stage == 12 && this.brawlInfo.questInfo.progress == 12) { if ( await popup.confirm(I18N('BRAWL_DAILY_TASK_COMPLETED'), [ { msg: I18N('BTN_NO'), result: true }, { msg: I18N('BTN_YES'), result: false }, ]) ) { this.end(I18N('SUCCESS')); return; } else { this.continueAttack = true; } } if (!this.attempts && !this.info.boughtEndlessLivesToday) { this.end(I18N('DONT_HAVE_LIVES')); return; } const enemie = Object.values(this.brawlInfo.findEnemies).shift(); // Автоматический подбор пачки if (this.isAuto) { if (this.mandatoryId <= 4000 && this.mandatoryId != 13) { this.end(I18N('BRAWL_AUTO_PACK_NOT_CUR_HERO')); return; } if (this.mandatoryId >= 4000 && this.mandatoryId < 4100) { this.args = await this.updateTitanPack(enemie.heroes); } else if (this.mandatoryId < 4000 && this.mandatoryId == 13) { this.args = await this.updateHeroesPack(enemie.heroes); } } const result = await this.battle(enemie.userId); this.brawlInfo = { questInfo: result[1].result.response, findEnemies: result[2].result.response, }; } } async updateTitanPack(enemieHeroes) { const packs = [ [4033, 4040, 4041, 4042, 4043], [4032, 4040, 4041, 4042, 4043], [4031, 4040, 4041, 4042, 4043], [4030, 4040, 4041, 4042, 4043], [4032, 4033, 4040, 4042, 4043], [4030, 4033, 4041, 4042, 4043], [4031, 4033, 4040, 4042, 4043], [4032, 4033, 4040, 4041, 4043], [4023, 4040, 4041, 4042, 4043], [4030, 4033, 4040, 4042, 4043], [4031, 4033, 4040, 4041, 4043], [4022, 4040, 4041, 4042, 4043], [4030, 4033, 4040, 4041, 4043], [4021, 4040, 4041, 4042, 4043], [4020, 4040, 4041, 4042, 4043], [4023, 4033, 4040, 4042, 4043], [4030, 4032, 4033, 4042, 4043], [4023, 4033, 4040, 4041, 4043], [4031, 4032, 4033, 4040, 4043], [4030, 4032, 4033, 4041, 4043], [4030, 4031, 4033, 4042, 4043], [4013, 4040, 4041, 4042, 4043], [4030, 4032, 4033, 4040, 4043], [4030, 4031, 4033, 4041, 4043], [4012, 4040, 4041, 4042, 4043], [4030, 4031, 4033, 4040, 4043], [4011, 4040, 4041, 4042, 4043], [4010, 4040, 4041, 4042, 4043], [4023, 4032, 4033, 4042, 4043], [4022, 4032, 4033, 4042, 4043], [4023, 4032, 4033, 4041, 4043], [4021, 4032, 4033, 4042, 4043], [4022, 4032, 4033, 4041, 4043], [4023, 4030, 4033, 4042, 4043], [4023, 4032, 4033, 4040, 4043], [4013, 4033, 4040, 4042, 4043], [4020, 4032, 4033, 4042, 4043], [4021, 4032, 4033, 4041, 4043], [4022, 4030, 4033, 4042, 4043], [4022, 4032, 4033, 4040, 4043], [4023, 4030, 4033, 4041, 4043], [4023, 4031, 4033, 4040, 4043], [4013, 4033, 4040, 4041, 4043], [4020, 4031, 4033, 4042, 4043], [4020, 4032, 4033, 4041, 4043], [4021, 4030, 4033, 4042, 4043], [4021, 4032, 4033, 4040, 4043], [4022, 4030, 4033, 4041, 4043], [4022, 4031, 4033, 4040, 4043], [4023, 4030, 4033, 4040, 4043], [4030, 4031, 4032, 4033, 4043], [4003, 4040, 4041, 4042, 4043], [4020, 4030, 4033, 4042, 4043], [4020, 4031, 4033, 4041, 4043], [4020, 4032, 4033, 4040, 4043], [4021, 4030, 4033, 4041, 4043], [4021, 4031, 4033, 4040, 4043], [4022, 4030, 4033, 4040, 4043], [4030, 4031, 4032, 4033, 4042], [4002, 4040, 4041, 4042, 4043], [4020, 4030, 4033, 4041, 4043], [4020, 4031, 4033, 4040, 4043], [4021, 4030, 4033, 4040, 4043], [4030, 4031, 4032, 4033, 4041], [4001, 4040, 4041, 4042, 4043], [4030, 4031, 4032, 4033, 4040], [4000, 4040, 4041, 4042, 4043], [4013, 4032, 4033, 4042, 4043], [4012, 4032, 4033, 4042, 4043], [4013, 4032, 4033, 4041, 4043], [4023, 4031, 4032, 4033, 4043], [4011, 4032, 4033, 4042, 4043], [4012, 4032, 4033, 4041, 4043], [4013, 4030, 4033, 4042, 4043], [4013, 4032, 4033, 4040, 4043], [4023, 4030, 4032, 4033, 4043], [4003, 4033, 4040, 4042, 4043], [4013, 4023, 4040, 4042, 4043], [4010, 4032, 4033, 4042, 4043], [4011, 4032, 4033, 4041, 4043], [4012, 4030, 4033, 4042, 4043], [4012, 4032, 4033, 4040, 4043], [4013, 4030, 4033, 4041, 4043], [4013, 4031, 4033, 4040, 4043], [4023, 4030, 4031, 4033, 4043], [4003, 4033, 4040, 4041, 4043], [4013, 4023, 4040, 4041, 4043], [4010, 4031, 4033, 4042, 4043], [4010, 4032, 4033, 4041, 4043], [4011, 4030, 4033, 4042, 4043], [4011, 4032, 4033, 4040, 4043], [4012, 4030, 4033, 4041, 4043], [4012, 4031, 4033, 4040, 4043], [4013, 4030, 4033, 4040, 4043], [4010, 4030, 4033, 4042, 4043], [4010, 4031, 4033, 4041, 4043], [4010, 4032, 4033, 4040, 4043], [4011, 4030, 4033, 4041, 4043], [4011, 4031, 4033, 4040, 4043], [4012, 4030, 4033, 4040, 4043], [4010, 4030, 4033, 4041, 4043], [4010, 4031, 4033, 4040, 4043], [4011, 4030, 4033, 4040, 4043], [4003, 4032, 4033, 4042, 4043], [4002, 4032, 4033, 4042, 4043], [4003, 4032, 4033, 4041, 4043], [4013, 4031, 4032, 4033, 4043], [4001, 4032, 4033, 4042, 4043], [4002, 4032, 4033, 4041, 4043], [4003, 4030, 4033, 4042, 4043], [4003, 4032, 4033, 4040, 4043], [4013, 4030, 4032, 4033, 4043], [4003, 4023, 4040, 4042, 4043], [4000, 4032, 4033, 4042, 4043], [4001, 4032, 4033, 4041, 4043], [4002, 4030, 4033, 4042, 4043], [4002, 4032, 4033, 4040, 4043], [4003, 4030, 4033, 4041, 4043], [4003, 4031, 4033, 4040, 4043], [4020, 4022, 4023, 4042, 4043], [4013, 4030, 4031, 4033, 4043], [4003, 4023, 4040, 4041, 4043], [4000, 4031, 4033, 4042, 4043], [4000, 4032, 4033, 4041, 4043], [4001, 4030, 4033, 4042, 4043], [4001, 4032, 4033, 4040, 4043], [4002, 4030, 4033, 4041, 4043], [4002, 4031, 4033, 4040, 4043], [4003, 4030, 4033, 4040, 4043], [4021, 4022, 4023, 4040, 4043], [4020, 4022, 4023, 4041, 4043], [4020, 4021, 4023, 4042, 4043], [4023, 4030, 4031, 4032, 4033], [4000, 4030, 4033, 4042, 4043], [4000, 4031, 4033, 4041, 4043], [4000, 4032, 4033, 4040, 4043], [4001, 4030, 4033, 4041, 4043], [4001, 4031, 4033, 4040, 4043], [4002, 4030, 4033, 4040, 4043], [4020, 4022, 4023, 4040, 4043], [4020, 4021, 4023, 4041, 4043], [4022, 4030, 4031, 4032, 4033], [4000, 4030, 4033, 4041, 4043], [4000, 4031, 4033, 4040, 4043], [4001, 4030, 4033, 4040, 4043], [4020, 4021, 4023, 4040, 4043], [4021, 4030, 4031, 4032, 4033], [4020, 4030, 4031, 4032, 4033], [4003, 4031, 4032, 4033, 4043], [4020, 4022, 4023, 4033, 4043], [4003, 4030, 4032, 4033, 4043], [4003, 4013, 4040, 4042, 4043], [4020, 4021, 4023, 4033, 4043], [4003, 4030, 4031, 4033, 4043], [4003, 4013, 4040, 4041, 4043], [4013, 4030, 4031, 4032, 4033], [4012, 4030, 4031, 4032, 4033], [4011, 4030, 4031, 4032, 4033], [4010, 4030, 4031, 4032, 4033], [4013, 4023, 4031, 4032, 4033], [4013, 4023, 4030, 4032, 4033], [4020, 4022, 4023, 4032, 4033], [4013, 4023, 4030, 4031, 4033], [4021, 4022, 4023, 4030, 4033], [4020, 4022, 4023, 4031, 4033], [4020, 4021, 4023, 4032, 4033], [4020, 4021, 4022, 4023, 4043], [4003, 4030, 4031, 4032, 4033], [4020, 4022, 4023, 4030, 4033], [4020, 4021, 4023, 4031, 4033], [4020, 4021, 4022, 4023, 4042], [4002, 4030, 4031, 4032, 4033], [4020, 4021, 4023, 4030, 4033], [4020, 4021, 4022, 4023, 4041], [4001, 4030, 4031, 4032, 4033], [4020, 4021, 4022, 4023, 4040], [4000, 4030, 4031, 4032, 4033], [4003, 4023, 4031, 4032, 4033], [4013, 4020, 4022, 4023, 4043], [4003, 4023, 4030, 4032, 4033], [4010, 4012, 4013, 4042, 4043], [4013, 4020, 4021, 4023, 4043], [4003, 4023, 4030, 4031, 4033], [4011, 4012, 4013, 4040, 4043], [4010, 4012, 4013, 4041, 4043], [4010, 4011, 4013, 4042, 4043], [4020, 4021, 4022, 4023, 4033], [4010, 4012, 4013, 4040, 4043], [4010, 4011, 4013, 4041, 4043], [4020, 4021, 4022, 4023, 4032], [4010, 4011, 4013, 4040, 4043], [4020, 4021, 4022, 4023, 4031], [4020, 4021, 4022, 4023, 4030], [4003, 4013, 4031, 4032, 4033], [4010, 4012, 4013, 4033, 4043], [4003, 4020, 4022, 4023, 4043], [4013, 4020, 4022, 4023, 4033], [4003, 4013, 4030, 4032, 4033], [4010, 4011, 4013, 4033, 4043], [4003, 4020, 4021, 4023, 4043], [4013, 4020, 4021, 4023, 4033], [4003, 4013, 4030, 4031, 4033], [4010, 4012, 4013, 4023, 4043], [4003, 4020, 4022, 4023, 4033], [4010, 4012, 4013, 4032, 4033], [4010, 4011, 4013, 4023, 4043], [4003, 4020, 4021, 4023, 4033], [4011, 4012, 4013, 4030, 4033], [4010, 4012, 4013, 4031, 4033], [4010, 4011, 4013, 4032, 4033], [4013, 4020, 4021, 4022, 4023], [4010, 4012, 4013, 4030, 4033], [4010, 4011, 4013, 4031, 4033], [4012, 4020, 4021, 4022, 4023], [4010, 4011, 4013, 4030, 4033], [4011, 4020, 4021, 4022, 4023], [4010, 4020, 4021, 4022, 4023], [4010, 4012, 4013, 4023, 4033], [4000, 4002, 4003, 4042, 4043], [4010, 4011, 4013, 4023, 4033], [4001, 4002, 4003, 4040, 4043], [4000, 4002, 4003, 4041, 4043], [4000, 4001, 4003, 4042, 4043], [4010, 4011, 4012, 4013, 4043], [4003, 4020, 4021, 4022, 4023], [4000, 4002, 4003, 4040, 4043], [4000, 4001, 4003, 4041, 4043], [4010, 4011, 4012, 4013, 4042], [4002, 4020, 4021, 4022, 4023], [4000, 4001, 4003, 4040, 4043], [4010, 4011, 4012, 4013, 4041], [4001, 4020, 4021, 4022, 4023], [4010, 4011, 4012, 4013, 4040], [4000, 4020, 4021, 4022, 4023], [4001, 4002, 4003, 4033, 4043], [4000, 4002, 4003, 4033, 4043], [4003, 4010, 4012, 4013, 4043], [4003, 4013, 4020, 4022, 4023], [4000, 4001, 4003, 4033, 4043], [4003, 4010, 4011, 4013, 4043], [4003, 4013, 4020, 4021, 4023], [4010, 4011, 4012, 4013, 4033], [4010, 4011, 4012, 4013, 4032], [4010, 4011, 4012, 4013, 4031], [4010, 4011, 4012, 4013, 4030], [4001, 4002, 4003, 4023, 4043], [4000, 4002, 4003, 4023, 4043], [4003, 4010, 4012, 4013, 4033], [4000, 4002, 4003, 4032, 4033], [4000, 4001, 4003, 4023, 4043], [4003, 4010, 4011, 4013, 4033], [4001, 4002, 4003, 4030, 4033], [4000, 4002, 4003, 4031, 4033], [4000, 4001, 4003, 4032, 4033], [4010, 4011, 4012, 4013, 4023], [4000, 4002, 4003, 4030, 4033], [4000, 4001, 4003, 4031, 4033], [4010, 4011, 4012, 4013, 4022], [4000, 4001, 4003, 4030, 4033], [4010, 4011, 4012, 4013, 4021], [4010, 4011, 4012, 4013, 4020], [4001, 4002, 4003, 4013, 4043], [4001, 4002, 4003, 4023, 4033], [4000, 4002, 4003, 4013, 4043], [4000, 4002, 4003, 4023, 4033], [4003, 4010, 4012, 4013, 4023], [4000, 4001, 4003, 4013, 4043], [4000, 4001, 4003, 4023, 4033], [4003, 4010, 4011, 4013, 4023], [4001, 4002, 4003, 4013, 4033], [4000, 4002, 4003, 4013, 4033], [4000, 4001, 4003, 4013, 4033], [4000, 4001, 4002, 4003, 4043], [4003, 4010, 4011, 4012, 4013], [4000, 4001, 4002, 4003, 4042], [4002, 4010, 4011, 4012, 4013], [4000, 4001, 4002, 4003, 4041], [4001, 4010, 4011, 4012, 4013], [4000, 4001, 4002, 4003, 4040], [4000, 4010, 4011, 4012, 4013], [4001, 4002, 4003, 4013, 4023], [4000, 4002, 4003, 4013, 4023], [4000, 4001, 4003, 4013, 4023], [4000, 4001, 4002, 4003, 4033], [4000, 4001, 4002, 4003, 4032], [4000, 4001, 4002, 4003, 4031], [4000, 4001, 4002, 4003, 4030], [4000, 4001, 4002, 4003, 4023], [4000, 4001, 4002, 4003, 4022], [4000, 4001, 4002, 4003, 4021], [4000, 4001, 4002, 4003, 4020], [4000, 4001, 4002, 4003, 4013], [4000, 4001, 4002, 4003, 4012], [4000, 4001, 4002, 4003, 4011], [4000, 4001, 4002, 4003, 4010], ].filter((p) => p.includes(this.mandatoryId)); const bestPack = { pack: packs[0], winRate: 0, countBattle: 0, id: 0, }; for (const id in packs) { const pack = packs[id]; const attackers = this.maxUpgrade.filter((e) => pack.includes(e.id)).reduce((obj, e) => ({ ...obj, [e.id]: e }), {}); const battle = { attackers, defenders: [enemieHeroes], type: 'brawl_titan', }; const isRandom = this.isRandomBattle(battle); const stat = { count: 0, win: 0, winRate: 0, }; for (let i = 1; i <= 20; i++) { battle.seed = Math.floor(Date.now() / 1000) + Math.random() * 1000; const result = await Calc(battle); stat.win += result.result.win; stat.count += 1; stat.winRate = stat.win / stat.count; if (!isRandom || (i >= 2 && stat.winRate < 0.65) || (i >= 10 && stat.winRate == 1)) { break; } } if (!isRandom && stat.win) { return { favor: {}, heroes: pack, }; } if (stat.winRate > 0.85) { return { favor: {}, heroes: pack, }; } if (stat.winRate > bestPack.winRate) { bestPack.countBattle = stat.count; bestPack.winRate = stat.winRate; bestPack.pack = pack; bestPack.id = id; } } //console.log(bestPack.id, bestPack.pack, bestPack.winRate, bestPack.countBattle); return { favor: {}, heroes: bestPack.pack, }; } isRandomPack(pack) { const ids = Object.keys(pack); return ids.includes('4023') || ids.includes('4021'); } isRandomBattle(battle) { return this.isRandomPack(battle.attackers) || this.isRandomPack(battle.defenders[0]); } async updateHeroesPack(enemieHeroes) { const packs = [{id:1,args:{userId:-830021,heroes:[63,13,9,48,1],pet:6006,favor:{1:6004,9:6005,13:6002,48:6e3,63:6009}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6022:130,8268:1,8269:1},power:198058,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:419649,intelligence:3644,physicalAttack:11481.6,strength:17049,armor:12720,dodge:17232.28,magicPenetration:22780,magicPower:55816,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6004,favorPower:11064},9:{id:9,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{335:130,336:130,337:130,338:130,6027:130,8270:1,8271:1},power:195886,star:6,runes:[43750,43750,43750,43750,43750],skins:{9:60,41:60,163:60,189:60,311:60,338:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6005,type:"hero",perks:[7,2,20],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3068,hp:227134,intelligence:19003,physicalAttack:7020.32,strength:3068,armor:19995,dodge:14644,magicPower:64780.6,magicResist:31597,modifiedSkillTier:5,skin:0,favorPetId:6005,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},48:{id:48,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{240:130,241:130,242:130,243:130,6002:130},power:190584,star:6,runes:[43750,43750,43750,43750,43750],skins:{103:60,165:60,217:60,296:60,326:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6e3,type:"hero",perks:[5,2],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:17308,hp:397737,intelligence:2888,physicalAttack:40298.32,physicalCritChance:12280,strength:3169,armor:12185,armorPenetration:20137.6,magicResist:24816,skin:0,favorPetId:6e3,favorPower:11064},63:{id:63,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{442:130,443:130,444:130,445:130,6041:130,8272:1,8273:1},power:193520,star:6,runes:[43750,43750,43750,43750,43750],skins:{341:60,350:60,351:60,352:1},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6009,type:"hero",perks:[6,1,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:17931,hp:488832,intelligence:2737,physicalAttack:54213.6,strength:2877,armor:800,armorPenetration:32477.6,magicResist:8526,physicalCritChance:9545,modifiedSkillTier:3,skin:0,favorPetId:6009,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:2,args:{userId:-830049,heroes:[46,13,52,49,4],pet:6006,favor:{4:6001,13:6002,46:6006,49:6004,52:6003}},attackers:{4:{id:4,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{255:130,256:130,257:130,258:130,6007:130},power:189782,star:6,runes:[43750,43750,43750,43750,43750],skins:{4:60,35:60,92:60,161:60,236:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6001,type:"hero",perks:[4,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:3065,hp:482631,intelligence:3402,physicalAttack:2800,strength:17488,armor:56262.6,magicPower:51021,magicResist:36971,skin:0,favorPetId:6001,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},46:{id:46,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{230:130,231:130,232:130,233:130,6032:130},power:189653,star:6,runes:[43750,43750,43750,43750,43750],skins:{101:60,159:60,178:60,262:60,315:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[9,5,1,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2122,hp:637517,intelligence:16208,physicalAttack:50,strength:5151,armor:38507.6,magicPower:74495.6,magicResist:22237,skin:0,favorPetId:6006,favorPower:11064},49:{id:49,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{245:130,246:130,247:130,248:130,6022:130},power:193163,star:6,runes:[43750,43750,43750,43750,43750],skins:{104:60,191:60,252:60,305:60,329:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[10,1,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:17935,hp:250405,intelligence:2790,physicalAttack:40413.6,strength:2987,armor:11655,dodge:14844.28,magicResist:3175,physicalCritChance:14135,skin:0,favorPetId:6004,favorPower:11064},52:{id:52,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{310:130,311:130,312:130,313:130,6017:130},power:185075,star:6,runes:[43750,43750,43750,43750,43750],skins:{188:60,213:60,248:60,297:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6003,type:"hero",perks:[5,8,2,13,15,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:18270,hp:226207,intelligence:2620,physicalAttack:44206,strength:3260,armor:13150,armorPenetration:40301,magicPower:9957.6,magicResist:33892.6,skin:0,favorPetId:6003,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:3,args:{userId:8263225,heroes:[29,63,13,48,1],pet:6006,favor:{1:6004,13:6002,29:6006,48:6e3,63:6003}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6022:130,8268:1,8269:1},power:198058,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:419649,intelligence:3644,physicalAttack:11481.6,strength:17049,armor:12720,dodge:17232.28,magicPenetration:22780,magicPower:55816,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6004,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},29:{id:29,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{145:130,146:130,147:130,148:130,6032:130},power:189790,star:6,runes:[43750,43750,43750,43750,43750],skins:{29:60,72:60,88:60,147:60,242:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[9,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2885,hp:491431,intelligence:18331,physicalAttack:106,strength:3020,armor:37716.6,magicPower:76792.6,magicResist:31377,skin:0,favorPetId:6006,favorPower:11064},48:{id:48,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{240:130,241:130,242:130,243:130,6002:130},power:190584,star:6,runes:[43750,43750,43750,43750,43750],skins:{103:60,165:60,217:60,296:60,326:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6e3,type:"hero",perks:[5,2],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:17308,hp:397737,intelligence:2888,physicalAttack:40298.32,physicalCritChance:12280,strength:3169,armor:12185,armorPenetration:20137.6,magicResist:24816,skin:0,favorPetId:6e3,favorPower:11064},63:{id:63,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{442:130,443:130,444:130,445:130,6017:130,8272:1,8273:1},power:191031,star:6,runes:[43750,43750,43750,43750,43750],skins:{341:60,350:60,351:60,352:1},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6003,type:"hero",perks:[6,1,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:17931,hp:488832,intelligence:2737,physicalAttack:44256,strength:2877,armor:800,armorPenetration:22520,magicPower:9957.6,magicResist:18483.6,physicalCritChance:9545,modifiedSkillTier:3,skin:0,favorPetId:6003,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:4,args:{userId:8263247,heroes:[55,13,40,51,1],pet:6006,favor:{1:6007,13:6002,40:6004,51:6006,55:6001}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6035:130,8268:1,8269:1},power:195170,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6007,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:419649,intelligence:3644,physicalAttack:1524,strength:17049,armor:22677.6,dodge:14245,magicPenetration:22780,magicPower:65773.6,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6007,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},40:{id:40,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{200:130,201:130,202:130,203:130,6022:130,8244:1,8245:1},power:192541,star:6,runes:[43750,43750,43750,43750,43750],skins:{53:60,89:60,129:60,168:60,314:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[5,9,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:17540,hp:343191,intelligence:2805,physicalAttack:48430.6,strength:2976,armor:24410,dodge:15732.28,magicResist:17633,modifiedSkillTier:3,skin:0,favorPetId:6004,favorPower:11064},51:{id:51,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{305:130,306:130,307:130,308:130,6032:130},power:190005,star:6,runes:[43750,43750,43750,43750,43750],skins:{181:60,219:60,260:60,290:60,334:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[5,9,1,12],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2526,hp:438205,intelligence:18851,physicalAttack:50,strength:2921,armor:39442.6,magicPower:88978.6,magicResist:22960,skin:0,favorPetId:6006,favorPower:11064},55:{id:55,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{325:130,326:130,327:130,328:130,6007:130},power:190529,star:6,runes:[43750,43750,43750,43750,43750],skins:{239:60,278:60,309:60,327:60,346:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6001,type:"hero",perks:[7,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2631,hp:499591,intelligence:19438,physicalAttack:50,strength:3286,armor:32892.6,armorPenetration:36870,magicPower:60704,magicResist:10010,skin:0,favorPetId:6001,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:5,args:{userId:8263303,heroes:[31,29,13,40,1],pet:6004,favor:{1:6001,13:6007,29:6002,31:6006,40:6004}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6007:130,8268:1,8269:1},power:195170,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6001,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:519225,intelligence:3644,physicalAttack:1524,strength:17049,armor:22677.6,dodge:14245,magicPenetration:22780,magicPower:55816,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6001,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6035:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6007,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:29017.6,magicPenetration:48181,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6007,favorPower:11064},29:{id:29,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{145:130,146:130,147:130,148:130,6012:130},power:189790,star:6,runes:[43750,43750,43750,43750,43750],skins:{29:60,72:60,88:60,147:60,242:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[9,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2885,hp:491431,intelligence:18331,physicalAttack:106,strength:3020,armor:27759,magicPenetration:9957.6,magicPower:76792.6,magicResist:31377,skin:0,favorPetId:6002,favorPower:11064},31:{id:31,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{155:130,156:130,157:130,158:130,6032:130},power:190305,star:6,runes:[43750,43750,43750,43750,43750],skins:{44:60,94:60,133:60,200:60,295:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[9,5,2,20],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2781,dodge:12620,hp:374484,intelligence:18945,physicalAttack:78,strength:2916,armor:28049.6,magicPower:67686.6,magicResist:15252,skin:0,favorPetId:6006,favorPower:11064},40:{id:40,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{200:130,201:130,202:130,203:130,6022:130,8244:1,8245:1},power:192541,star:6,runes:[43750,43750,43750,43750,43750],skins:{53:60,89:60,129:60,168:60,314:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[5,9,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:17540,hp:343191,intelligence:2805,physicalAttack:48430.6,strength:2976,armor:24410,dodge:15732.28,magicResist:17633,modifiedSkillTier:3,skin:0,favorPetId:6004,favorPower:11064},6004:{id:6004,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6020:130,6021:130},power:181943,type:"pet",perks:[5],name:null,armorPenetration:47911,intelligence:11064,strength:12360}}},{id:6,args:{userId:8263317,heroes:[62,13,9,56,61],pet:6003,favor:{9:6004,13:6002,56:6006,61:6001,62:6003}},attackers:{9:{id:9,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{335:130,336:130,337:130,338:130,6022:130,8270:1,8271:1},power:198525,star:6,runes:[43750,43750,43750,43750,43750],skins:{9:60,41:60,163:60,189:60,311:60,338:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[7,2,20],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3068,hp:227134,intelligence:19003,physicalAttack:10007.6,strength:3068,armor:19995,dodge:17631.28,magicPower:54823,magicResist:31597,modifiedSkillTier:5,skin:0,favorPetId:6004,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},56:{id:56,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{376:130,377:130,378:130,379:130,6032:130},power:184420,star:6,runes:[43750,43750,43750,43750,43750],skins:{264:60,279:60,294:60,321:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[5,7,1,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2791,hp:235111,intelligence:18813,physicalAttack:50,strength:2656,armor:22982.6,magicPenetration:48159,magicPower:75598.6,magicResist:13990,skin:0,favorPetId:6006,favorPower:11064},61:{id:61,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{411:130,412:130,413:130,414:130,6007:130},power:184868,star:6,runes:[43750,43750,43750,43750,43750],skins:{302:60,306:60,323:60,340:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6001,type:"hero",perks:[4,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2545,hp:466176,intelligence:3320,physicalAttack:34305,strength:18309,armor:31077.6,magicResist:24101,physicalCritChance:9009,skin:0,favorPetId:6001,favorPower:11064},62:{id:62,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{437:130,438:130,439:130,440:130,6017:130},power:173991,star:6,runes:[43750,43750,43750,43750,43750],skins:{320:60,343:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6003,type:"hero",perks:[8,7,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2530,hp:276010,intelligence:19245,physicalAttack:50,strength:3543,armor:12890,magicPenetration:23658,magicPower:80966.6,magicResist:12447.6,skin:0,favorPetId:6003,favorPower:11064},6003:{id:6003,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6015:130,6016:130},power:181943,type:"pet",perks:[8],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:7,args:{userId:8263335,heroes:[32,29,13,43,1],pet:6006,favor:{1:6004,13:6008,29:6006,32:6002,43:6007}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6022:130,8268:1,8269:1},power:198058,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:419649,intelligence:3644,physicalAttack:11481.6,strength:17049,armor:12720,dodge:17232.28,magicPenetration:22780,magicPower:55816,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6004,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6038:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6008,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:29017.6,magicPenetration:48181,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6008,favorPower:11064},29:{id:29,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{145:130,146:130,147:130,148:130,6032:130},power:189790,star:6,runes:[43750,43750,43750,43750,43750],skins:{29:60,72:60,88:60,147:60,242:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[9,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2885,hp:491431,intelligence:18331,physicalAttack:106,strength:3020,armor:37716.6,magicPower:76792.6,magicResist:31377,skin:0,favorPetId:6006,favorPower:11064},32:{id:32,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{160:130,161:130,162:130,163:130,6012:130},power:189956,star:6,runes:[43750,43750,43750,43750,43750],skins:{45:60,73:60,81:60,135:60,212:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2815,hp:551066,intelligence:18800,physicalAttack:50,strength:2810,armor:19040,magicPenetration:9957.6,magicPower:89495.6,magicResist:20805,skin:0,favorPetId:6002,favorPower:11064},43:{id:43,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{215:130,216:130,217:130,218:130,6035:130},power:189593,star:6,runes:[43750,43750,43750,43750,43750],skins:{98:60,130:60,169:60,201:60,304:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6007,type:"hero",perks:[7,9,1,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2447,hp:265217,intelligence:18758,physicalAttack:50,strength:2842,armor:18637.6,magicPenetration:52439,magicPower:75465.6,magicResist:22695,skin:0,favorPetId:6007,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}}]; const bestPack = { pack: packs[0], countWin: 0, } for (const pack of packs) { const attackers = pack.attackers; const battle = { attackers, defenders: [enemieHeroes], type: 'brawl', }; let countWinBattles = 0; let countTestBattle = 10; for (let i = 0; i < countTestBattle; i++) { battle.seed = Math.floor(Date.now() / 1000) + Math.random() * 1000; const result = await Calc(battle); if (result.result.win) { countWinBattles++; } if (countWinBattles > 7) { console.log(pack) return pack.args; } } if (countWinBattles > bestPack.countWin) { bestPack.countWin = countWinBattles; bestPack.pack = pack.args; } } console.log(bestPack); return bestPack.pack; } async questFarm() { const calls = [this.callBrawlQuestFarm]; const result = await Send(JSON.stringify({ calls })); return result.results[0].result.response; } async getBrawlInfo() { const data = await Send(JSON.stringify({ calls: [ this.callUserGetInfo, this.callBrawlQuestGetInfo, this.callBrawlFindEnemies, this.callTeamGetMaxUpgrade, this.callBrawlGetInfo, ] })); let attempts = data.results[0].result.response.refillable.find(n => n.id == 48); const maxUpgrade = data.results[3].result.response; const maxHero = Object.values(maxUpgrade.hero); const maxTitan = Object.values(maxUpgrade.titan); const maxPet = Object.values(maxUpgrade.pet); this.maxUpgrade = [...maxHero, ...maxPet, ...maxTitan]; this.info = data.results[4].result.response; this.mandatoryId = lib.data.brawl.promoHero[this.info.id].promoHero; return { attempts: attempts.amount, questInfo: data.results[1].result.response, findEnemies: data.results[2].result.response, } } /** * Carrying out a fight * * Проведение боя */ async battle(userId) { this.stats.count++; const battle = await this.startBattle(userId, this.args); const result = await Calc(battle); console.log(result.result); if (result.result.win) { this.stats.win++; } else { this.stats.loss++; if (!this.info.boughtEndlessLivesToday) { this.attempts--; } } return await this.endBattle(result); // return await this.cancelBattle(result); } /** * Starts a fight * * Начинает бой */ async startBattle(userId, args) { const call = { name: "brawl_startBattle", args, ident: "brawl_startBattle" } call.args.userId = userId; const calls = [call]; const result = await Send(JSON.stringify({ calls })); return result.results[0].result.response; } cancelBattle(battle) { const fixBattle = function (heroes) { for (const ids in heroes) { const hero = heroes[ids]; hero.energy = random(1, 999); if (hero.hp > 0) { hero.hp = random(1, hero.hp); } } } fixBattle(battle.progress[0].attackers.heroes); fixBattle(battle.progress[0].defenders.heroes); return this.endBattle(battle); } /** * Ends the fight * * Заканчивает бой */ async endBattle(battle) { battle.progress[0].attackers.input = ['auto', 0, 0, 'auto', 0, 0]; const calls = [{ name: "brawl_endBattle", args: { result: battle.result, progress: battle.progress }, ident: "brawl_endBattle" }, this.callBrawlQuestGetInfo, this.callBrawlFindEnemies, ]; const result = await Send(JSON.stringify({ calls })); return result.results; } end(endReason) { setIsCancalBattle(true); isBrawlsAutoStart = false; setProgress(endReason, true); console.log(endReason); this.resolve(); } } this.HWHClasses.executeBrawls = executeBrawls; })(); /** * TODO: * Получение всех уровней при сборе всех наград (квест на титанит и на энку) +- * Добивание на арене титанов * Закрытие окошек по Esc +- * Починить работу скрипта на уровне команды ниже 10 +- * Написать номальную синхронизацию * Запрет сбора квестов и отправки экспеиций в промежуток между локальным обновлением и глобальным обновлением дня * Улучшение боев */