// ==UserScript==
// @name HeroWarsHelper
// @name:en HeroWarsHelper
// @name:ru HeroWarsHelper
// @namespace HeroWarsHelper
// @version 2.423
// @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
// @downloadURL https://update.greasyfork.icu/scripts/450693/HeroWarsHelper.user.js
// @updateURL https://update.greasyfork.icu/scripts/450693/HeroWarsHelper.meta.js
// ==/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 battleType = data?.effects?.battleConfig ?? data?.type;
if (data?.effects?.battleConfig) {
console.log('config:', battleType, 'type:', data.type);
}
const type = getBattleType(battleType);
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',
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:
'',
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: 'VICTORY',
DEFEAT: 'DEFEAT',
CHANCE_TO_WIN: 'Chance to win based on pre-calculation',
OPEN_DOLLS: 'nesting dolls recursively',
SENT_QUESTION: 'Question sent',
SETTINGS: 'Settings',
MSG_BAN_ATTENTION: '
Using this feature may result in a ban.
Continue?',
BTN_YES_I_AGREE: 'Yes, I understand the risks!',
BTN_NO_I_AM_AGAINST: 'No, I refuse it!',
VALUES: 'Values',
EXPEDITIONS_SENT: 'Expeditions: Collected: {countGet} 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 Soul 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 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. 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: ' 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} {description}',
EVENT_AUTO_BOSS:
'Maximum number of battles for calculation:{length} ∗ {countTestBattle} = {maxCalcBattle}If you have a weak computer, it may take a long time for this, click on the cross to cancel.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... 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 {countBattle}/{countMaxBattle} attempts{winTimer} (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 If you buy brawl store coins for emeralds, you must use them immediately, otherwise they will disappear after restarting the game!',
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. 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: ' Hint: ',
PICTURE: ' Picture: ',
ANSWER: ' 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}',
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: {power} ',
BEST_RESULT: 'Best result: {value}%',
GUILD_ISLAND_TITLE: 'Fast travel to Guild Island',
TITAN_VALLEY_TITLE: 'Fast travel to Titan Valley',
EXTENSIONS: 'Extensions',
EXTENSIONS_TITLE: 'Extensions for the script',
EXTENSIONS_LIST_TITLE: 'Extensions for the script',
EVENT_IS_OVER: 'Event is over',
SET_COUNT_KILLS: 'Set the number of enemies that need to be killed today:',
MORE_ENEMIES_KILLED: 'Already killed more than {countKills} enemies',
RESTART_TRY_AGAIN_LATER: 'Restart the game and try again later',
ENEMIES_KILLED_AND_HEROES_USED: 'Number of enemies killed: {score} Used {count} heroes',
FURNACE_OF_SOULS: 'Furnace',
PUMPKINS: 'Pumps',
PUMPKINS_TITLE: 'Exchange all Ghost Energy for Spirit Festival Coins',
PUMPKINS_RUN: 'Exchange all Ghost Energy for Spirit Festival Coins?',
TIDY_INVENTORY: 'Tidy Inventory',
TIDY_INVENTORY_TITLE: 'Tidy Inventory',
EQUIPMENT_FRAGMENT_CRATES: 'Equipment Fragment Crates',
EQUIPMENT_FRAGMENT_CRATES_TITLE: 'Open all equipment fragment crates',
RAND_NUGGETS_AND_REGAL: 'Random Crystals and Insignia',
RAND_NUGGETS_AND_REGAL_TITLE: 'Open random Crystals and Insignia',
ARTIFACT_RESOURCES: 'Artifact Resources',
ARTIFACT_RESOURCES_TITLE: 'Open chests with artifact essences, scrolls, metals',
},
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: 'Архидемон',
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:
'',
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: 'ПОБЕДА',
DEFEAT: 'ПОРАЖЕНИЕ',
CHANCE_TO_WIN: 'Шансы на победу на основе прерасчета',
OPEN_DOLLS: 'матрешек рекурсивно',
SENT_QUESTION: 'Вопрос отправлен',
SETTINGS: 'Настройки',
MSG_BAN_ATTENTION: '
Использование этой функции может привести к бану.
Продолжить?',
BTN_YES_I_AGREE: 'Да, я беру на себя все риски!',
BTN_NO_I_AM_AGAINST: 'Нет, я отказываюсь от этого!',
VALUES: 'Значения',
EXPEDITIONS_SENT: 'Экспедиции: Собрано: {countGet} Отправлено: {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: 'Призошли ошибка в процессе прохождения боя Скопировать ошибку в буфер обмена?',
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} Зелье Питомца. Вы хотите купить {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: ' Попытки закончились, запущен таймер {time}',
EPIC_BRAWL: 'Вселенская битва',
EPIC_BRAWL_TITLE: 'Тратит попытки во Вселенской битве',
RELOAD_GAME: 'Перезагрузить игру',
TIMER: 'Таймер:',
SHOW_ERRORS: 'Отображать ошибки',
SHOW_ERRORS_TITLE: 'Отображать ошибки запросов к серверу',
ERROR_MSG: 'Ошибка: {name} {description}',
EVENT_AUTO_BOSS:
'Максимальное количество боев для расчета:{length} * {countTestBattle} = {maxCalcBattle}Если у Вас слабый компьютер на это может потребоваться много времени, нажмите крестик для отмены.Искать лучший пак из всех или первый подходящий?',
BEST_SLOW: 'Лучший (медленее)',
FIRST_FAST: 'Первый (быстрее)',
FREEZE_INTERFACE: 'Идет расчет... Интерфейс может зависнуть.',
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} побежден за {countBattle}/{countMaxBattle} попыток{winTimer} (Сделайте синхронизацию или перезагрузите игру для обновления данных)',
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:
'Магазины Если Вы купите монеты магазинов потасовок за изумруды, то их надо использовать сразу, иначе после перезагрузки игры они пропадут!',
MINIONS_WARNING: 'Пачки героев для атаки приспешников неполные, продолжить?',
FAST_SEASON: 'Быстрый сезон',
FAST_SEASON_TITLE: 'Пропуск экрана с выбором карты в сезоне',
SET_NUMBER_LEVELS: 'Указать колличество уровней:',
POSSIBLE_IMPROVE_LEVELS: 'Возможно улучшить только {count} уровней. Улучшаем?',
NOT_ENOUGH_RESOURECES: 'Не хватает ресурсов',
IMPROVED_LEVELS: 'Улучшено уровней: {count}',
ARTIFACTS_UPGRADE: 'Улучшение артефактов',
ARTIFACTS_UPGRADE_TITLE: 'Улучшает указанное количество самых дешевых артефактов героев',
SKINS_UPGRADE: 'Улучшение обликов',
SKINS_UPGRADE_TITLE: 'Улучшает указанное количество самых дешевых обликов героев',
HINT: ' Подсказка: ',
PICTURE: ' На картинке: ',
ANSWER: ' Ответ: ',
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}',
SERVER_NOT_ACCEPT: 'Сервер не принял результат',
INVASION_BOSS_BUFF: 'Для {bossLvl} босса нужен баф {needBuff} у вас {haveBuff}',
HERO_POWER: 'Сила героев',
HERO_POWER_TITLE: 'Отображает текущую и максимальную силу героев',
MAX_POWER_REACHED: 'Максимальная достигнутая мощь: {power}',
CURRENT_POWER: 'Текущая мощь: {power}',
POWER_TO_MAX: 'До максимума мощи осталось: {power} ',
BEST_RESULT: 'Лучший результат: {value}%',
GUILD_ISLAND_TITLE: 'Перейти к Острову гильдии',
TITAN_VALLEY_TITLE: 'Перейти к Долине титанов',
EXTENSIONS: 'Расширения',
EXTENSIONS_TITLE: 'Расширения для скрипта',
EXTENSIONS_LIST_TITLE: 'Расширения для скрипта',
EVENT_IS_OVER: 'Эвент завершен',
SET_COUNT_KILLS: 'Задайте колличество врагов которых необходимо убить сегодня:',
MORE_ENEMIES_KILLED: 'Уже убито больше {countKills} врагов',
RESTART_TRY_AGAIN_LATER: 'Перезагрузите игру и попробуйте позже',
ENEMIES_KILLED_AND_HEROES_USED: 'Количество убитых врагов: {score} Использовано {count} героев',
FURNACE_OF_SOULS: 'Горнило',
PUMPKINS: 'Тыквы!',
PUMPKINS_TITLE: 'Обмен всей Призрачной энергии на Монеты Фестиваля Духов',
PUMPKINS_RUN: 'Обменять всю Призрачную энергию на Монеты Фестиваля Духов?',
TIDY_INVENTORY: 'Прибрать инвентарь',
TIDY_INVENTORY_TITLE: 'Прибрать инвентарь',
EQUIPMENT_FRAGMENT_CRATES: 'Ящики фрагментов экипировки',
EQUIPMENT_FRAGMENT_CRATES_TITLE: 'Открыть все ящики фрагментов экипировки',
RAND_NUGGETS_AND_REGAL: 'Случайные самородки и регалии',
RAND_NUGGETS_AND_REGAL_TITLE: 'Открыть случайные самородки и регалии',
ARTIFACT_RESOURCES: 'Артефактные ресы',
ARTIFACT_RESOURCES_TITLE: 'Открыть сундуки с артефактными эссенсиями, свитакми, металлами',
},
};
function getLang() {
let lang = '';
if (typeof NXFlashVars !== 'undefined') {
lang = NXFlashVars.interface_lang
}
if (!lang) {
lang = (navigator.language || navigator.userLanguage).substr(0, 2);
}
const { i18nLangData } = HWHData;
if (i18nLangData[lang]) {
return lang;
}
return 'en';
}
this.I18N = function (constant, replace) {
const { i18nLangData } = HWHData;
const selectLang = getLang();
if (constant && constant in i18nLangData[selectLang]) {
const result = i18nLangData[selectLang][constant];
if (replace) {
return result.sprintf(replace);
}
return result;
}
console.warn('Language constant not found', {constant, replace});
if (i18nLangData['en'][constant]) {
const result = i18nLangData['en'][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: {
get label() { return I18N('SKIP_FIGHTS'); },
cbox: null,
get title() { return I18N('SKIP_FIGHTS_TITLE'); },
default: false,
},
sendExpedition: {
get label() { return I18N('AUTO_EXPEDITION'); },
cbox: null,
get title() { return I18N('AUTO_EXPEDITION_TITLE'); },
default: false,
},
cancelBattle: {
get label() { return I18N('CANCEL_FIGHT'); },
cbox: null,
get title() { return I18N('CANCEL_FIGHT_TITLE'); },
default: false,
},
preCalcBattle: {
get label() { return I18N('BATTLE_RECALCULATION'); },
cbox: null,
get title() { return I18N('BATTLE_RECALCULATION_TITLE'); },
default: false,
},
countControl: {
get label() { return I18N('QUANTITY_CONTROL'); },
cbox: null,
get title() { return I18N('QUANTITY_CONTROL_TITLE'); },
default: true,
},
repeatMission: {
get label() { return I18N('REPEAT_CAMPAIGN'); },
cbox: null,
get title() { return I18N('REPEAT_CAMPAIGN_TITLE'); },
default: false,
},
noOfferDonat: {
get label() { return I18N('DISABLE_DONAT'); },
cbox: null,
get title() { return 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: {
get label() { return I18N('DAILY_QUESTS'); },
cbox: null,
get title() { return I18N('DAILY_QUESTS_TITLE'); },
default: false,
},
// Потасовки
autoBrawls: {
get label() { return I18N('BRAWLS'); },
cbox: null,
get title() { return 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: {
get label() { return I18N('AUTO_QUIZ'); },
cbox: null,
get title() { return I18N('AUTO_QUIZ_TITLE'); },
default: false,
hide: false,
},
tryFixIt_v2: {
get label() { return I18N('BTN_TRY_FIX_IT'); },
cbox: null,
get title() { return I18N('BTN_TRY_FIX_IT_TITLE'); },
default: false,
hide: false,
},
showErrors: {
get label() { return I18N('SHOW_ERRORS'); },
cbox: null,
get title() { return I18N('SHOW_ERRORS_TITLE'); },
default: true,
},
buyForGold: {
get label() { return I18N('BUY_FOR_GOLD'); },
cbox: null,
get title() { return I18N('BUY_FOR_GOLD_TITLE'); },
default: false,
},
hideServers: {
get label() { return I18N('HIDE_SERVERS'); },
cbox: null,
get title() { return I18N('HIDE_SERVERS_TITLE'); },
default: false,
},
fastSeason: {
get label() { return I18N('FAST_SEASON'); },
cbox: null,
get title() { return I18N('FAST_SEASON_TITLE'); },
default: false,
},
};
/**
* Get checkbox state
*
* Получить состояние чекбокса
*/
function isChecked(checkBox) {
const { checkboxes } = HWHData;
if (!(checkBox in checkboxes)) {
return false;
}
return checkboxes[checkBox].cbox?.checked;
}
/**
* Input fields
*
* Поля ввода
*/
const inputs = {
countTitanit: {
input: null,
get title() { return I18N('HOW_MUCH_TITANITE'); },
default: 150,
},
speedBattle: {
input: null,
get title() { return I18N('COMBAT_SPEED'); },
default: 5,
},
countTestBattle: {
input: null,
get title() { return I18N('NUMBER_OF_TEST'); },
default: 10,
},
countAutoBattle: {
input: null,
get title() { return I18N('NUMBER_OF_AUTO_BATTLE'); },
default: 10,
},
FPS: {
input: null,
title: 'FPS',
default: 60,
}
}
/**
* Checks the checkbox
*
* Поплучить данные поля ввода
*/
function getInput(inputName) {
const { inputs } = HWHData;
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);
};
/**
* List of main menu buttons
*
* Список кнопочек основного меню
*/
const buttons = {
getOutland: {
get name() {
return I18N('TO_DO_EVERYTHING');
},
get title() {
return I18N('TO_DO_EVERYTHING_TITLE');
},
onClick: testDoYourBest,
},
doActions: {
get name() {
return I18N('ACTIONS');
},
get title() {
return I18N('ACTIONS_TITLE');
},
onClick: async function () {
const { actionsPopupButtons } = HWHData;
actionsPopupButtons.push({ result: false, isClose: true });
const answer = await popup.confirm(`${I18N('CHOOSE_ACTION')}:`, actionsPopupButtons);
if (typeof answer === 'function') {
answer();
}
},
},
doOthers: {
get name() {
return I18N('OTHERS');
},
get title() {
return I18N('OTHERS_TITLE');
},
onClick: async function () {
const { othersPopupButtons } = HWHData;
othersPopupButtons.push({ result: false, isClose: true });
const answer = await popup.confirm(`${I18N('CHOOSE_ACTION')}:`, othersPopupButtons);
if (typeof answer === 'function') {
answer();
}
},
},
testTitanArena: {
isCombine: true,
combineList: [
{
get name() {
return I18N('TITAN_ARENA');
},
get title() {
return I18N('TITAN_ARENA_TITLE');
},
onClick: function () {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('TITAN_ARENA')}?`, testTitanArena);
},
},
{
name: '>>',
onClick: cheats.goTitanValley,
get title() {
return I18N('TITAN_VALLEY_TITLE');
},
color: 'green',
},
],
},
testDungeon: {
isCombine: true,
combineList: [
{
get name() {
return I18N('DUNGEON');
},
onClick: function () {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('DUNGEON')}?`, testDungeon);
},
get title() {
return I18N('DUNGEON_TITLE');
},
},
{
name: '>>',
onClick: cheats.goClanIsland,
get title() {
return I18N('GUILD_ISLAND_TITLE');
},
color: 'green',
},
],
},
testAdventure: {
isCombine: true,
combineList: [
{
get name() {
return I18N('ADVENTURE');
},
onClick: () => {
testAdventure();
},
get title() {
return I18N('ADVENTURE_TITLE');
},
},
{
get name() {
return I18N('AUTO_RAID_ADVENTURE');
},
onClick: autoRaidAdventure,
get title() {
return I18N('AUTO_RAID_ADVENTURE_TITLE');
},
color: 'red',
},
{
name: '>>',
onClick: cheats.goSanctuary,
get title() {
return I18N('SANCTUARY_TITLE');
},
color: 'green',
},
],
},
rewardsAndMailFarm: {
get name() {
return I18N('REWARDS_AND_MAIL');
},
get title() {
return I18N('REWARDS_AND_MAIL_TITLE');
},
onClick: function () {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('REWARDS_AND_MAIL')}?`, rewardsAndMailFarm);
},
},
goToClanWar: {
get name() {
return I18N('GUILD_WAR');
},
get title() {
return I18N('GUILD_WAR_TITLE');
},
onClick: cheats.goClanWar,
dot: true,
},
dailyQuests: {
get name() {
return I18N('DAILY_QUESTS');
},
get title() {
return I18N('DAILY_QUESTS_TITLE');
},
onClick: async function () {
const quests = new dailyQuests(
() => {},
() => {}
);
await quests.autoInit();
quests.start();
},
},
newDay: {
get name() {
return I18N('SYNC');
},
get title() {
return I18N('SYNC_TITLE');
},
onClick: function () {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('SYNC')}?`, cheats.refreshGame);
},
},
// Архидемон
bossRatingEventDemon: {
get name() {
return I18N('ARCHDEMON');
},
get title() {
return I18N('ARCHDEMON_TITLE');
},
onClick: function () {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('ARCHDEMON')}?`, bossRatingEvent);
},
hide: true,
color: 'red',
},
// Горнило душ
bossRatingEventSouls: {
isCombine: true,
hide: true,
combineList: [
{
get name() {
return I18N('FURNACE_OF_SOULS');
},
get title() {
return I18N('ARCHDEMON_TITLE');
},
onClick: function () {
bossRatingEventSouls();
},
color: 'orange',
},
{
get name() {
return I18N('PUMPKINS');
},
get title() {
return I18N('PUMPKINS_TITLE');
},
onClick: function () {
confShow(I18N('PUMPKINS_RUN'), async () => {
const coins = (
await Caller.send(
[...Array(Math.floor((await Caller.send('inventoryGet').then((e) => e.coin[22])) / 250))].map(() => ({
name: 'lootBoxBuy',
args: { box: 'boxHalloween2025', offerId: 2035, price: 'openCoin' },
}))
).then((e) => e.map((n) => n[0]).filter((r) => r?.coin && r.coin[23]))
).length;
confShow(`${I18N('RECEIVED')} ${coins} ${cheats.translate('LIB_COIN_NAME_23')}`);
cheats.refreshInventory();
});
},
color: 'green',
},
],
},
extensions: {
get name() {
return I18N('EXTENSIONS');
},
get title() {
return I18N('EXTENSIONS_TITLE');
},
onClick: function () {
popup.customPopup(async (complete) => {
const selectLang = getLang();
const response = await fetch(`https://zingery.ru/heroes/ext.php?lang=${selectLang}`);
const html = await response.text();
const blob = new Blob([html], { type: 'text/html' });
const url = URL.createObjectURL(blob);
popup.custom.insertAdjacentHTML(
'beforeend',
``
);
popup.setMsgText(I18N('EXTENSIONS_LIST_TITLE'));
popup.addButton({ isClose: true }, () => {
complete(false);
popup.hide();
});
popup.show();
});
},
color: 'red',
},
};
/**
* List of buttons by the "Actions" button
*
* Список кнопочек по кнопке "Действия"
*/
const actionsPopupButtons = [
{
get msg() {
return I18N('TIDY_INVENTORY');
},
async result() {
await new InventoryTidier().run();
},
get title() {
return I18N('TIDY_INVENTORY_TITLE');
},
color: 'orange',
},
{
get msg() {
return I18N('OUTLAND');
},
result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('OUTLAND')}?`, getOutland);
},
get title() {
return I18N('OUTLAND_TITLE');
},
color: 'green',
isOneSocket: true,
},
{
get msg() {
return I18N('TOWER');
},
result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('TOWER')}?`, testTower);
},
get title() {
return I18N('TOWER_TITLE');
},
color: 'graphite',
},
{
get msg() {
return I18N('EXPEDITIONS');
},
result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('EXPEDITIONS')}?`, checkExpedition);
},
get title() {
return I18N('EXPEDITIONS_TITLE');
},
color: 'blue',
},
{
get msg() {
return I18N('MINIONS');
},
result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('MINIONS')}?`, testRaidNodes);
},
get title() {
return I18N('MINIONS_TITLE');
},
color: 'red',
},
{
get msg() {
return I18N('ESTER_EGGS');
},
result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('ESTER_EGGS')}?`, offerFarmAllReward);
},
get title() {
return I18N('ESTER_EGGS_TITLE');
},
color: 'yellow',
},
{
get msg() {
return I18N('STORM');
},
result() {
testAdventure('solo');
},
get title() {
return I18N('STORM_TITLE');
},
color: 'indigo',
},
{
get msg() {
return I18N('REWARDS');
},
result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('REWARDS')}?`, questAllFarm);
},
get title() {
return I18N('REWARDS_TITLE');
},
color: 'orange',
},
{
get msg() {
return I18N('MAIL');
},
result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('MAIL')}?`, mailGetAll);
},
get title() {
return I18N('MAIL_TITLE');
},
color: 'beige',
},
{
get msg() {
return I18N('SEER');
},
result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('SEER')}?`, rollAscension);
},
get title() {
return I18N('SEER_TITLE');
},
color: 'violet',
},
// {
// get msg() {
// return I18N('NY_GIFTS');
// },
// result: getGiftNewYear,
// get title() {
// return I18N('NY_GIFTS_TITLE');
// },
// color: 'pink',
// },
];
/**
* List of buttons by the "Others" button
*
* Список кнопочек по кнопке "Разное"
*/
const othersPopupButtons = [
{
get msg() {
return I18N('GET_ENERGY');
},
result: farmStamina,
get title() {
return I18N('GET_ENERGY_TITLE');
},
color: 'green',
isOneSocket: true,
},
{
get msg() {
return I18N('ITEM_EXCHANGE');
},
result: fillActive,
get title() {
return I18N('ITEM_EXCHANGE_TITLE');
},
color: 'beige',
},
{
get msg() {
return I18N('BUY_SOULS');
},
result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('BUY_SOULS')}?`, buyHeroFragments);
},
get title() {
return I18N('BUY_SOULS_TITLE');
},
color: 'violet',
},
{
get msg() {
return I18N('BUY_FOR_GOLD');
},
result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('BUY_FOR_GOLD')}?`, buyInStoreForGold);
},
get title() {
return I18N('BUY_FOR_GOLD_TITLE');
},
color: 'yellow',
},
{
get msg() {
return I18N('BUY_OUTLAND');
},
result: bossOpenChestPay,
get title() {
return I18N('BUY_OUTLAND_TITLE');
},
color: 'orange',
},
{
get msg() {
return I18N('CLAN_STAT');
},
result: clanStatistic,
get title() {
return I18N('CLAN_STAT_TITLE');
},
color: 'blue',
},
{
get msg() {
return I18N('EPIC_BRAWL');
},
result: async function () {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('EPIC_BRAWL')}?`, () => {
const brawl = new epicBrawl();
brawl.start();
});
},
get title() {
return I18N('EPIC_BRAWL_TITLE');
},
color: 'red',
},
{
get msg() {
return I18N('ARTIFACTS_UPGRADE');
},
result: updateArtifacts,
get title() {
return I18N('ARTIFACTS_UPGRADE_TITLE');
},
color: 'indigo',
},
{
get msg() {
return I18N('SKINS_UPGRADE');
},
result: updateSkins,
get title() {
return I18N('SKINS_UPGRADE_TITLE');
},
color: 'pink',
},
{
get msg() {
return I18N('SEASON_REWARD');
},
result: farmBattlePass,
get title() {
return I18N('SEASON_REWARD_TITLE');
},
color: 'graphite',
},
{
get msg() {
return I18N('SELL_HERO_SOULS');
},
result: sellHeroSoulsForGold,
get title() {
return I18N('SELL_HERO_SOULS_TITLE');
},
color: 'brown',
},
{
get msg() {
return I18N('CHANGE_MAP');
},
result: async function () {
const maps = Object.values(lib.data.seasonAdventure.list)
.filter((e) => e.map.cells.length > 3)
.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);
}
},
get title() {
return I18N('CHANGE_MAP_TITLE');
},
color: 'blue',
},
{
get msg() {
return I18N('HERO_POWER');
},
result: async () => {
const [userGetInfo, heroGetAll] = await Caller.send(['userGetInfo', 'heroGetAll']);
const maxHeroSumPower = userGetInfo.maxSumPower.heroes;
const heroSumPower = Object.values(heroGetAll).reduce((a, e) => a + e.power, 0);
const power = maxHeroSumPower - heroSumPower;
let msg =
I18N('MAX_POWER_REACHED', { power: maxHeroSumPower.toLocaleString() }) +
' ' +
I18N('CURRENT_POWER', { power: heroSumPower.toLocaleString() }) +
' ' +
I18N('POWER_TO_MAX', { power: power.toLocaleString(), color: power >= 4000 ? 'green' : 'red' });
await popup.confirm(msg, [{ msg: I18N('BTN_OK'), result: 0, color: 'green' }]);
},
get title() {
return I18N('HERO_POWER_TITLE');
},
color: 'green',
},
];
/**
* Display buttons
*
* Вывести кнопочки
*/
function addControlButtons() {
const { ScriptMenu } = HWHClasses;
const scriptMenu = ScriptMenu.getInst();
const { buttons } = HWHData;
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() {
const { ScriptMenu } = HWHClasses;
const scriptMenu = ScriptMenu.getInst();
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: 157,
buff: 0,
bossLvl: 130,
};
const invasionDataPacks = {
130: { buff: 0, pet: 6005, heroes: [55, 58, 63, 52, 47], favor: { 47: 6001, 52: 6003, 55: 6005, 58: 6002, 63: 6000 }, timer: 0 },
140: { buff: 0, pet: 6005, heroes: [55, 58, 63, 52, 47], favor: { 47: 6001, 52: 6003, 55: 6005, 58: 6002, 63: 6000 }, timer: 0 },
150: { buff: 0, pet: 6005, heroes: [55, 58, 63, 52, 47], favor: { 47: 6001, 52: 6003, 55: 6005, 58: 6002, 63: 6000 }, timer: 0 },
160: { buff: 0, pet: 6006, heroes: [55, 63, 48, 52, 42], favor: { 42: 6001, 48: 6005, 52: 6003, 55: 6007, 63: 6000 }, timer: 0 },
170: { buff: 0, pet: 6005, heroes: [55, 58, 63, 48, 51], favor: { 48: 6005, 51: 6006, 55: 6007, 58: 6008, 63: 6003 }, timer: 0 },
180: { buff: 0, pet: 6002, heroes: [58, 44, 63, 45, 42], favor: { 42: 6006, 44: 6003, 45: 6002, 58: 6008, 63: 6000 }, timer: 0 },
190: { buff: 0, pet: 6005, heroes: [55, 44, 63, 48, 52], favor: { 44: 6009, 48: 6005, 52: 6001, 55: 6007, 63: 6000 }, timer: 0 },
200: { buff: 0, pet: 6005, heroes: [63, 48, 40, 52, 45], favor: { 45: 6002, 48: 6005, 52: 6001, 63: 6009 }, timer: 0 },
210: { buff: 0, pet: 6005, heroes: [63, 48, 40, 52, 4], favor: { 4: 6007, 48: 6005, 52: 6001, 63: 6009 }, timer: 0 },
220: { buff: 0, pet: 6006, heroes: [55, 63, 48, 52, 2], favor: { 2: 6000, 48: 6005, 52: 6001, 55: 6007, 63: 6009 }, timer: 28.91644659118315 },
230: { buff: 0, pet: 6005, heroes: [55, 63, 48, 52, 47], favor: { 47: 6003, 48: 6005, 52: 6003, 55: 6005, 63: 6009 }, timer: 38.7890624894657 },
240: { buff: 0, pet: 6005, heroes: [55, 63, 48, 52, 2], favor: { 2: 6001, 48: 6005, 52: 6003, 55: 6007, 63: 6009 }, timer: 2.6315625 },
250: { buff: 0, pet: 6005, heroes: [55, 63, 48, 40, 52], favor: { 48: 6005, 52: 6003, 55: 6007, 63: 6009 }, timer: 2.396601562499999 },
260: { buff: 0, pet: 6005, heroes: [46, 55, 63, 45, 2], favor: { 2: 6000, 45: 6002, 46: 6006, 55: 6007, 63: 6003 }, timer: 108.98437516287758 },
270: { buff: 15, pet: 6005, heroes: [32, 55, 63, 48, 51], favor: { 32: 6007, 48: 6001, 51: 6001, 55: 6001, 63: 6000 }, timer: 67.77832032495091 },
280: { buff: 25, pet: 6005, heroes: [55, 63, 48, 52, 47], favor: { 47: 6003, 48: 6005, 52: 6006, 55: 6007, 63: 6009 }, timer: 39.84937499999998 },
290: { buff: 5, pet: 6005, heroes: [46, 55, 63, 48, 52], favor: { 46: 6006, 48: 6005, 52: 6003, 55: 6005, 63: 6003 }, timer: 20.053711007235812 },
300: { buff: 35, pet: 6005, heroes: [55, 58, 63, 43, 51], favor: { 43: 6006, 51: 6006, 55: 6005, 58: 6005, 63: 6000 }, timer: 40.13671886177282 },
//300: { buff: 70, pet: 6005, heroes: [55, 58, 63, 48, 51], favor: {48: 6005, 51: 6006, 55: 6007, 58: 6008, 63: 6009}, timer: 54.755859550678494 }
};
this.getInvasionBosses = (() => {
let cache = null;
return function () {
if (cache) {
return cache;
}
const libInvasion = lib.data.invasion;
const now = Date.now() / 1000;
const phase = Object.values(libInvasion.phase).find((e) => e.startDate < now && e.endDate > now);
const invasionId = phase.invasionId;
const chapterIds = new Set(
Object.values(libInvasion.chapter)
.filter((c) => c.invasionId === invasionId)
.map((c) => c.id)
);
const result = Object.values(libInvasion.phase)
.filter((p) => chapterIds.has(p.chapterId))
.reduce((acc, p) => Object.assign(acc, p.phaseData.boss), {});
cache = result;
return result;
};
})();
/**
* 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;
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
*
* Количество карт предсказаний
*/
const countPredictionCard = 0;
/**
* Brawl pack
*
* Пачка для потасовок
*/
let brawlsPack = null;
let clanDominationGetInfo = null;
/**
* Copies the text to the clipboard
*
* Копирует тест в буфер обмена
* @param {*} text copied text // копируемый текст
*/
function copyText(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);
}
/**
* 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, color: 'green'},
{ msg: I18N('BTN_CANCEL'), result: false, isCancel: true, color: 'red'},
]
} else {
yesCallback = () => {};
buts = [{ msg: I18N('BTN_OK'), result: true, color: 'green' }];
}
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) {
let parsedData = null;
let messageType = null;
try {
parsedData = JSON.parse(event.data);
messageType = parsedData?.result?.type;
} catch (e) {}
if (parsedData) {
if (!this.isWebSocketLogin && messageType === 'iframeEvent.login') {
this.isWebSocketLogin = true;
} else if (messageType === 'iframeEvent.login') {
return;
}
}
// Вызов обработчиков
Events.emit('WSMessage', messageType, parsedData?.result, event);
if (typeof oldOnmessage === 'function') {
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;
if (name == 'X-Auth-Signature') {
requestHistory[this.uniqid].signature.push(value);
if (!check) {
return;
}
}
} else {
check = true;
}
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();
}
Events.emit('startGame', this);
}
/**
* 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, color: 'green' },
{ msg: ansS, result: true, color: 'red' },
]);
} 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, color: 'green' },
{ msg: ansS, result: 1, color: 'red' },
{ msg: ansT, result: 2 },
]);
}
this._isChangeRequest = false;
const testData = JSON.parse(tempData);
for (const call of testData.calls) {
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, 500);
if (result.result?.win) {
call.args.result = result.result;
call.args.progress = result.progress;
this._isChangeRequest = true;
} else if (result.value > 0) {
if (
await popup.confirm(I18N('DEFEAT') + ' ' + I18N('BEST_RESULT', { value: result.value }), [
{ msg: I18N('BTN_CANCEL'), result: 0, color: 'red' },
{ msg: I18N('BTN_ACCEPT'), result: 1, color: 'geeen' },
])
) {
call.args.result = result.result;
call.args.progress = result.progress;
this._isChangeRequest = true;
}
}
} catch (error) {
console.error(error);
}
}
if (call.name == 'invasion_bossEnd' && lastBattleInfo) {
const cloneBattle = structuredClone(lastBattleInfo);
let result = null;
if (!call.args.result.win && isChecked('tryFixIt_v2')) {
setProgress(I18N('LETS_FIX'), false);
const bFix = new WinFixBattle(cloneBattle);
result = await bFix.start(cloneBattle.endTime, 500);
console.log(result);
let msgResult = I18N('DEFEAT');
if (result.result?.win) {
call.args.result = result.result;
call.args.progress = result.progress;
msgResult = I18N('VICTORY');
this._isChangeRequest = true;
}
setProgress(msgResult, false, hideProgress);
}
const bosses = getInvasionBosses();
if (bosses[call.args.id]?.isMainBoss) {
if (!result) {
result = await Calc(cloneBattle);
}
let timer = result.battleTimer;
const period = Math.ceil((Date.now() - lastBossBattleStart) / 1000);
console.log(timer, period);
if (period < timer) {
timer = timer - period;
await countdownTimer(timer);
lastBattleInfo.timer = true;
}
}
}
if (!call.args.result.win) {
let resultPopup = false;
if (
call.name == 'adventure_endBattle' ||
//call.name == 'invasion_bossEnd' ||
call.name == 'bossEndBattle' ||
// call.name == 'titanArenaEndBattle' ||
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 !== 'invasion_bossEnd' && 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);
this._isChangeRequest = 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);
this._isChangeRequest = true;
if (resultPopup > 1) {
this.onReadySuccess = testAutoBattle;
}
}
}
// Потасовки
if (isChecked('autoBrawls') && !HWHClasses.executeBrawls.isBrawlsAutoStart && call.name == 'brawl_endBattle') {
}
}
/**
* Save pack for Brawls
*
* Сохраняем пачку для потасовок
*/
if (isChecked('autoBrawls') && !HWHClasses.executeBrawls.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, color: 'red' },
{ msg: I18N('BTN_YES'), result: true, color: 'green' },
],
[
{
name: 'isAuto',
get label() {
return I18N('BRAWL_AUTO_PACK');
},
checked: false,
},
]
)
) {
HWHClasses.executeBrawls.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, color: 'green' },
{ msg: I18N('BTN_AUTO_F5'), result: 1 },
//{ msg: I18N('BTN_TRY_FIX_IT'), result: 2 },
...testFunc,
],
[
{
name: 'isStat',
get label() {
return I18N('CALC_STAT');
},
checked: false,
},
]
);
if (resultPopup) {
if (resultPopup == 2) {
// Отключено/Disabled
setProgress(I18N('LETS_FIX'), false);
await new Promise((e) => setTimeout(e, 0));
const cloneBattle = structuredClone(lastBossBattle);
const endTime = cloneBattle.endTime - 15e3;
console.log('fixBossBattleStart');
const { BossFixBattle } = HWHClasses;
const bFix = new BossFixBattle(cloneBattle);
const result = await bFix.start(endTime, 500);
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 +
' ' +
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);
}
this._isChangeRequest = 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 { invasionInfo } = HWHData;
console.log(
invasionInfo.bossLvl,
JSON.stringify({
buff: invasionInfo.buff,
pet: lastBattleArg.pet,
heroes: lastBattleArg.heroes,
favor: lastBattleArg.favor,
timer: 0,
})
);
const timePassed = Date.now() - lastBossBattleStart;
if (timePassed < invasionTimer) {
await new Promise((e) => setTimeout(e, invasionTimer - timePassed));
}
invasionTimer -= 1;
}
lastBossBattleStart = Date.now();
}
/**
* Disable spending divination cards
* Отключить трату карт предсказаний
*/
if (call.name == 'dungeonEndBattle') {
if (call.args.isRaid) {
if (HWHData.countPredictionCard <= 0) {
delete call.args.isRaid;
this._isChangeRequest = true;
} else if (HWHData.countPredictionCard > 0) {
HWHData.countPredictionCard--;
}
}
console.log(`Cards: ${HWHData.countPredictionCard}`);
/**
* Fix endless cards
* Исправление бесконечных карт
*/
const lastBattle = lastDungeonBattleData;
if (lastBattle && !call.args.isRaid) {
if (this._isChangeRequest) {
lastBattle.progress = [{ attackers: { input: ['auto', 0, 0, 'auto', 0, 0] } }];
} else {
lastBattle.progress = call.args.progress;
}
const result = await Calc(lastBattle);
if (this._isChangeRequest) {
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 == 'quiz_answer') {
/**
* Automatically changes the answer to the correct one if there is one.
* Автоматически меняет ответ на правильный если он есть
*/
if (lastAnswer && isChecked('getAnswer')) {
call.args.answerId = lastAnswer;
lastAnswer = null;
this._isChangeRequest = 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, color: 'red' },
{ msg: I18N('BTN_YES'), result: true, color: 'green' },
]);
}
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, color: 'green' },
{ msg: I18N('BTN_NO'), result: false, color: 'red' },
]))
) {
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.name == 'artifactChestOpen' ||
call.name == 'titanArtifactChestOpen') &&
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, color: 'green' }]);
if (result) {
let item = { id: 0, type: 'consumable' };
switch (call.name) {
case 'titanUseSummonCircle':
item.id = 13;
item.type = 'coin';
break;
case 'pet_chestOpen':
item.id = 90;
break;
case 'artifactChestOpen':
item.id = 45;
break;
case 'titanArtifactChestOpen':
item.id = 55;
break;
}
cheats.updateInventory({
[item.type]: {
[item.id]: -(result - startAmount),
},
});
call.args.amount = result;
this._isChangeRequest = true;
correctShowOpenArtifact = 0;
if ((call.name == 'artifactChestOpen' || call.name == 'titanArtifactChestOpen') && call.args.amount > 20) {
correctShowOpenArtifact = 3;
}
}
}
if (call.name == 'consumableUseLootBox') {
lastRussianDollId = call.args.libId;
/**
* Specify quantity for Platinum Box and Heroes Box
* Указать количество для платиновых шкатулок и ящиков с героями
*/
const lootBoxInfo = lib.data.inventoryItem.consumable[call.args.libId];
const playerChoiceType = lootBoxInfo?.effectDescription?.playerChoiceType;
if (isChecked('countControl') && ((call.args.libId == 148 && call.args.amount > 1) || playerChoiceType === 'hero')) {
const result = await popup.confirm(I18N('MSG_SPECIFY_QUANT'), [
{ msg: I18N('BTN_OPEN'), isInput: true, default: call.args.amount, color: 'green' },
]);
call.args.amount = result;
this._isChangeRequest = true;
}
}
if (call.name == 'invasion_bossStart' && isChecked('tryFixIt_v2')) {
const { invasionInfo, invasionDataPacks } = HWHData;
if (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;
this._isChangeRequest = true;
}
}
}
}
if (call.name == 'workshopBuff_create') {
const { invasionInfo, invasionDataPacks } = HWHData;
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
);
}
}
if (call.name == 'saleShowcase_rewardInfo') {
this[call.name] = {
offerId: call.args.offerId,
};
}
/**
* 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;
// this._isChangeRequest = true;
// }
// }
Events.emit('checkChangeSend', this, call);
}
let headers = requestHistory[this.uniqid].headers;
if (this._isChangeRequest) {
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 {
this._isChangeResponse = false;
callsIdent = requestHistory[this.uniqid].calls;
respond = JSON.parse(response);
/**
* If the request returned an error removes the error (removes synchronization errors)
* Если запрос вернул ошибку удаляет ошибку (убирает ошибки синхронизации)
*/
if (respond.error) {
this._isChangeResponse = 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 = [];
}
}
const allReward = {};
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 = [];
this._isChangeResponse = 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)
);
this._isChangeResponse = true;
}
}
/**
* Hiding donation offers 3
* Скрываем предложения доната 3
*/
if (getSaveVal('noOfferDonat') && call.result?.bundleUpdate) {
delete call.result.bundleUpdate;
this._isChangeResponse = 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)
);
this._isChangeResponse = true;
}
/**
* Copies a quiz question to the clipboard
* Копирует вопрос викторины в буфер обмена и получает на него ответ если есть
*/
if (call.ident == callsIdent['quiz_getNewQuestion']) {
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['quiz_answer']) {
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;
}
}
/**
* Prolongation of the brawl
* Продление потасовки
*/
if (call.ident == callsIdent['brawl_getInfo']) {
if (call.result.response?.endDate) {
call.result.response.endDate += 86400;
this._isChangeResponse = true;
}
}
/**
* Access to Prestige rewards and quests on a non-prestige day
* Доступ к наградам и квестам престижа в день без престижа
*/
if (call.ident == callsIdent['clan_prestigeGetInfo']) {
if (!call.result.response.prestigeId) {
call.result.response.prestigeId = 2;
call.result.response.endTime = call.result.response.nextTime;
this._isChangeResponse = true;
}
}
/**
* Start of the battle for recalculation
* Начало боя для прерасчета
*/
if (call.ident == callsIdent['clanWarAttack'] ||
call.ident == callsIdent['crossClanWar_startBattle'] ||
call.ident == callsIdent['bossAttack'] ||
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'] ||
call.ident == callsIdent['battleGetReplay'] && call.result?.response?.replay?.type !== "clan_raid") {
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 (isChecked('preCalcBattle')) {
const preCalcBattle = structuredClone(battle);
setProgress(I18N('BEING_RECALC'));
let battleDuration = 120;
try {
const battleType = preCalcBattle?.effects?.battleConfig ?? preCalcBattle.type;
if (preCalcBattle?.effects?.battleConfig) {
console.log('config:', battleType, 'type:', preCalcBattle.type);
}
const typeBattle = getBattleType(battleType);
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);
}
const battleType = battle?.effects?.battleConfig ?? battle.type;
BattleCalc(battle, getBattleType(battleType), (e) => resolve(e));
});
}
let actions = [getBattleInfo(preCalcBattle, false)];
let countTestBattle = getInput('countTestBattle');
if (call.ident == callsIdent['invasion_bossStart'] && preCalcBattle.seed === 8008) {
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);
});
}
}
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);
}
}
/**
* 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;
}
this._isChangeResponse = 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;
}
}
this._isChangeResponse = 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, color: 'green' },
{ msg: I18N('BTN_NO'), result: false, isClose: true, color: 'red' },
]))
) {
const [count, recursionResult] = await openRussianDolls(lastRussianDollId, newCount);
countLootBox += +count;
mergeItemsObj(lootBox, recursionResult);
this._isChangeResponse = true;
}
if (this._isChangeResponse) {
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']) {
HWHData.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]) {
HWHData.countPredictionCard += consumable[81];
console.log(`Cards: ${HWHData.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));
this._isChangeResponse = 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 += ' ' + I18N('PLAYER_POS');
for (const user of users) {
msg += ` ${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;
this._isChangeResponse = 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' },
});
}
if (Object.keys(trophy.clanReward).length && !trophy.clanRewardFarmed) {
calls.push({
name: 'hallOfFameFarmTrophyReward',
args: { trophyId: week, rewardType: 'clan' },
});
}
}
if (calls.length) {
Caller.send(calls).then((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') + ' ';
if (coin18) {
msg += cheats.translate('LIB_COIN_NAME_18') + `: ${coin18} `;
}
if (coin19) {
msg += cheats.translate('LIB_COIN_NAME_19') + `: ${coin19} `;
}
if (gold) {
msg += cheats.translate('LIB_PSEUDO_COIN') + `: ${gold} `;
}
if (starmoney) {
msg += cheats.translate('LIB_PSEUDO_STARMONEY') + `: ${starmoney} `;
}
return popup.confirm(msg, [{ msg: I18N('BTN_OK'), result: 0, color: 'green' }]);
});
}
}
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(' ' + I18N('SERVER_NOT_ACCEPT'));
}
addProgress(' Server > ' + I18N('BOSS_DAMAGE') + damage.toLocaleString());
}
if (call.ident == callsIdent['invasion_getInfo']) {
const r = call.result.response;
if (r?.actions?.length) {
const { invasionInfo, invasionDataPacks } = HWHData;
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) {
const { invasionInfo, invasionDataPacks } = HWHData;
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['mailFarm']) {
const letters = Object.values(call.result.response);
for (const letter of letters) {
if (letter.consumable?.[81]) {
console.log('Карты предсказаний', letter.consumable[81]);
HWHData.countPredictionCard += letter.consumable[81];
}
if (letter.refillable?.[45]) {
console.log('Сферы портала', letter.refillable[45]);
setPortals(+letter.refillable[45], true);
}
}
}
if (call.ident == callsIdent['quest_questsFarm']) {
const rewards = call.result.response;
for (const reward of rewards) {
if (reward.consumable?.[81]) {
console.log('Карты предсказаний', reward.consumable[81]);
HWHData.countPredictionCard += reward.consumable[81];
}
if (reward.refillable?.[45]) {
console.log('Сферы портала', reward.refillable[45]);
setPortals(+reward.refillable[45], true);
}
}
}
if (call.ident == callsIdent['adventure_start']) {
setPortals(-1, true);
}
if (call.ident == callsIdent['clanWarEndBattle']) {
setWarTries(-1, true);
}
if (call.ident == callsIdent['saleShowcase_rewardInfo']) {
if (new Date(call.result.response.nextRefill * 1000) < Date.now()) {
const offerId = this?.['saleShowcase_rewardInfo']?.offerId;
if (offerId) {
try {
void Caller.send({ name: 'saleShowcase_farmReward', args: { offerId } });
} catch (e) {
console.error(e);
}
}
}
}
/*
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;
}
}
this._isChangeResponse = true;
}
*/
Events.emit('checkChangeResponse', this, call, callsIdent);
}
} catch(err) {
console.log("Request(response, " + this.uniqid + "):\n", "Error:\n", response, err);
}
if (this._isChangeResponse) {
Object.defineProperty(this, 'responseText', {
writable: true,
});
this.responseText = JSON.stringify(respond);
}
}
/**
* Request an answer to a question
*
* Запрос ответа на вопрос
*/
async function getAnswer(question) {
// eW91dHUuYmUvZFF3NHc5V2dYY1E=
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) {
// MTIzNDU2Nzg5MA==
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')) {
if (strBattleType.includes('titan')) {
return 'get_invasionTitan';
}
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"]);
if (headers['X-Env-Unique-Session-Uuid']) {
sign.add('UNIQUE-SESSION-UUID=' + headers['X-Env-Unique-Session-Uuid']);
}
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) {
if (!event.key) {
return;
}
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;
}
}
const extentionsList = [];
/**
* Creates an interface
*
* Создает интерфейс
*/
function createInterface() {
popup.init();
const { ScriptMenu } = HWHClasses;
const scriptMenu = ScriptMenu.getInst();
scriptMenu.init();
scriptMenu.addHeader(GM_info.script.name, justInfo);
const versionHeader = scriptMenu.addHeader('v' + GM_info.script.version);
const { extentionsList } = HWHData;
if (extentionsList.length) {
versionHeader.title = '';
versionHeader.style.color = 'red';
for (const extention of extentionsList) {
const { name, ver, author } = extention;
versionHeader.title += name + ', v' + ver + ' by ' + author + '\n';
}
versionHeader.innerText += ` [${extentionsList.length}]`;
}
// 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) {
const { extentionsList } = HWHData;
extentionsList.push({
name,
ver,
author,
});
}
function addControls() {
createInterface();
const { ScriptMenu } = HWHClasses;
const scriptMenu = ScriptMenu.getInst();
const checkboxDetails = scriptMenu.addDetails(I18N('SETTINGS'), 'settings');
const { checkboxes } = HWHData;
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'), 'values');
const { inputs } = HWHData;
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) {
const { ScriptMenu } = HWHClasses;
const scriptMenu = ScriptMenu.getInst();
clearTimeout(hideTimeoutProgress);
hideTimeoutProgress = setTimeout(function () {
scriptMenu.setStatus('');
}, timeout);
}
/**
* Progress display
*
* Отображение прогресса
*/
function setProgress(text, hide, onclick) {
const { ScriptMenu } = HWHClasses;
const scriptMenu = ScriptMenu.getInst();
scriptMenu.setStatus(text, onclick);
hide = hide || false;
if (hide) {
if (typeof hide != 'number') {
hide = 3000;
}
hideProgress(hide);
}
}
/**
* Progress added
*
* Дополнение прогресса
*/
function addProgress(text) {
const { ScriptMenu } = HWHClasses;
const scriptMenu = ScriptMenu.getInst();
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,
};
this.HWHData = {
i18nLangData,
checkboxes,
inputs,
buttons,
invasionInfo,
invasionDataPacks,
countPredictionCard,
actionsPopupButtons,
othersPopupButtons,
/**
* @deprecated Use extentionsList
* @see extentionsList
*/
extintionsList: extentionsList,
extentionsList,
};
/**
* 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: {},
name: GM_info.script.name,
init: function () {
const { checkboxes, inputs } = HWHData;
this.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 }), {});
},
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.init();
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);
}
/**
* Миксин EventEmitter
* @param {Class} BaseClass Базовый класс (по умолчанию Object)
* @returns {Class} Класс с методами EventEmitter
*/
const EventEmitterMixin = (BaseClass = Object) =>
class EventEmitter extends BaseClass {
constructor(...args) {
super(...args);
this._events = new Map();
}
/**
* Подписаться на событие
* @param {string} event Имя события
* @param {function} listener Функция-обработчик
* @returns {this} Возвращает экземпляр для чейнинга
*/
on(event, listener) {
if (typeof listener !== 'function') {
throw new TypeError('Listener must be a function');
}
if (!this._events.has(event)) {
this._events.set(event, new Set());
}
this._events.get(event).add(listener);
return this;
}
/**
* Отписаться от события
* @param {string} event Имя события
* @param {function} listener Функция-обработчик
* @returns {this} Возвращает экземпляр для чейнинга
*/
off(event, listener) {
if (this._events.has(event)) {
const listeners = this._events.get(event);
listeners.delete(listener);
if (listeners.size === 0) {
this._events.delete(event);
}
}
return this;
}
/**
* Вызвать событие
* @param {string} event Имя события
* @param {...any} args Аргументы для обработчиков
* @returns {boolean} Было ли событие обработано
*/
emit(event, ...args) {
if (!this._events.has(event)) return false;
const listeners = new Set(this._events.get(event));
listeners.forEach((listener) => {
try {
listener.apply(this, args);
} catch (e) {
console.error(`Error in event handler for "${event}":`, e);
}
});
return true;
}
/**
* Подписаться на событие один раз
* @param {string} event Имя события
* @param {function} listener Функция-обработчик
* @returns {this} Возвращает экземпляр для чейнинга
*/
once(event, listener) {
const onceWrapper = (...args) => {
this.off(event, onceWrapper);
listener.apply(this, args);
};
return this.on(event, onceWrapper);
}
/**
* Удалить все обработчики для события
* @param {string} [event] Имя события (если не указано - очистить все)
* @returns {this} Возвращает экземпляр для чейнинга
*/
removeAllListeners(event) {
if (event) {
this._events.delete(event);
} else {
this._events.clear();
}
return this;
}
/**
* Получить количество обработчиков для события
* @param {string} event Имя события
* @returns {number} Количество обработчиков
*/
listenerCount(event) {
return this._events.has(event) ? this._events.get(event).size : 0;
}
};
this.HWHFuncs.EventEmitterMixin = EventEmitterMixin;
class GlobalEventHub extends EventEmitterMixin() {}
const Events = new GlobalEventHub();
this.HWHFuncs.Events = Events;
class TaskManager {
isConsoleLog = false;
functionRegistry = {};
constructor() {
if (!TaskManager.inst) {
console.log('intiTaskManager timeout: 1');
this.tasks = new Map();
this.setTimeout(1);
TaskManager.inst = this;
}
return TaskManager.inst;
}
async loop() {
if (this.isConsoleLog) {
console.log(new Date().toISOString(), this.tasks.size);
}
const currentTime = new Date();
for (const [task, executionTime] of this.tasks) {
if (executionTime <= currentTime) {
if (this.isConsoleLog) {
console.log('executeTask', task);
}
try {
if (typeof task.execute === 'function') {
task.execute();
} else if (task.fnName && this.functionRegistry[task.fnName]) {
this.functionRegistry[task.fnName](...(task.args || []));
delete this.functionRegistry[task.fnName];
} else {
console.warn('Task has no executable function:', task);
}
if (typeof task.onComplete === 'function') {
task.onComplete(task);
}
} catch (error) {
this.executeError(error, task);
}
this.tasks.delete(task);
if (task.repeat) {
this.addTask(task);
}
}
}
await this.sleep();
this.loop();
}
addTask(task) {
task.executeTime = task.executeAt instanceof Date ? task.executeAt : new Date(Date.now() + task.executeAt * 1e3);
if (this.isConsoleLog) {
console.log('addTask', task);
}
this.tasks.set(task, task.executeTime);
}
removeTaskById(id) {
for (const [task, _] of this.tasks) {
if (task.id === id) {
this.tasks.delete(task);
if (this.isConsoleLog) {
console.log('Removed task by ID:', id);
}
return true;
}
}
return false;
}
executeError(error, task) {
console.error('Task error:', error);
console.log('Faulty task:', task);
}
setTimeout(timeout) {
this.timeout = timeout * 1000;
if (this.worker) {
this.worker.terminate();
}
this.worker = new Worker(
URL.createObjectURL(
new Blob([
`self.onmessage = function(e) {
const timeout = e.data;
setTimeout(() => {
self.postMessage(1);
}, timeout);
};`,
])
)
);
this.loop();
}
registerFunction(name, fn) {
if (name in this.functionRegistry) {
console.log('Функция с таким именем уже есть');
return false;
}
this.functionRegistry[name] = fn;
return true;
}
async sleep() {
return new Promise((r) => {
this.worker.postMessage(this.timeout);
this.worker.onmessage = r;
});
}
static add(execute, executeAt, repeat) {
new TaskManager().addTask({ execute, executeAt, repeat });
return task;
}
}
this.HWHClasses.TaskManager = TaskManager;
/**
* 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 [emn178@gmail.com]
* @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>2]|=t[f]<>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>2]|=s<>2]|=(192|s>>6)<>2]|=(128|63&s)<=57344?(n[h>>2]|=(224|s>>12)<>2]|=(128|s>>6&63)<>2]|=(128|63&s)<>2]|=(240|s>>18)<>2]|=(128|s>>12&63)<>2]|=(128|s>>6&63)<>2]|=(128|63&s)<=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 _}))}();
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];
}
function findInstanceOf(obj, targetClass) {
const prototypeKeys = Object.keys(Object.getPrototypeOf(obj));
const matchingKey = prototypeKeys.find((key) => obj[key] instanceof targetClass);
return matchingKey ? obj[matchingKey] : null;
}
/**
* 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_21 = getProtoFn(Game.CommandManager, 21);
let MCL_2 = getProtoFn(Game.MissionCommandList, 2);
let MBR_15 = getF(Game.MultiBattleResult, 'get_result');
let RPCCB_17 = getProtoFn(Game.RPCCommandBase, 17);
let PMD_34 = getProtoFn(Game.PlayerMissionData, 34);
Game.GameModel[GM_2]()[GM_P2][CM_21][MCL_2](a[MBR_15]())[RPCCB_17](Game.bindFunc(this, this[PMD_34]));
};
},
/*
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 (HWHData.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;
};
},
doublePets: function () {
const TeamGatherPopupMediator = selfGame['game.mediator.gui.popup.team.TeamGatherPopupMediator'];
const InvasionBossTeamGatherPopupMediator = selfGame['game.mechanics.invasion.mediator.boss.InvasionBossTeamGatherPopupMediator'];
const TeamGatherPopupHeroValueObject = selfGame['game.mediator.gui.popup.team.TeamGatherPopupHeroValueObject'];
const ObjectPropertyWriteable = selfGame['engine.core.utils.property.ObjectPropertyWriteable'];
const TGPM_8 = getProtoFn(TeamGatherPopupMediator, 8);
const TGPM_45 = getProtoFn(TeamGatherPopupMediator, 45);
const TGPM_114 = getProtoFn(TeamGatherPopupMediator, 114);
const TGPM_117 = getProtoFn(TeamGatherPopupMediator, 117);
const TGPM_123 = getProtoFn(TeamGatherPopupMediator, 123);
const TGPM_135 = getProtoFn(TeamGatherPopupMediator, 135);
const TGPHVO_40 = getProtoFn(TeamGatherPopupHeroValueObject, 40);
const OPW_0 = getProtoFn(ObjectPropertyWriteable, 0);
const oldFunc = InvasionBossTeamGatherPopupMediator.prototype[TGPM_135];
InvasionBossTeamGatherPopupMediator.prototype[TGPM_135] = function (a, b) {
try {
if (b == 0) {
this[TGPM_8].remove(a);
} else {
this[TGPM_8].F[a] = b;
}
this[TGPM_114](this[TGPM_45], a)[TGPHVO_40][OPW_0](this[TGPM_117](b));
this[TGPM_123]();
return;
} catch (e) {}
oldFunc.call(this, a, b);
};
},
};
/**
* 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;
};
/** Возвращает объект игры */
this.getGame = function () {
return Game;
};
/**
* Updates game data
*
* Обновляет данные игры
*/
this.refreshGame = function () {
new Game.NextDayUpdatedManager()[getProtoFn(Game.NextDayUpdatedManager, 6)]();
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 Caller.send('inventoryGet'));
};
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 () {
const GM_0 = getProtoFn(Game.GameModel, 0);
let instance = getFnP(Game.GameModel, 'get_instance');
let player = Game.GameModel[instance]()[GM_0];
let clanWarSelect = selfGame['game.mechanics.cross_clan_war.popup.selectMode.CrossClanWarSelectModeMediator'];
new clanWarSelect(player).open();
};
/** Перейти к Острову гильдии */
this.goClanIsland = function () {
const GM_0 = getProtoFn(Game.GameModel, 0);
let instance = getFnP(Game.GameModel, 'get_instance');
let player = Game.GameModel[instance]()[GM_0];
let clanIslandSelect = selfGame['game.view.gui.ClanIslandPopupMediator'];
new clanIslandSelect(player).open();
};
/**
* Go to BrawlShop
*
* Переместиться в BrawlShop
*/
this.goBrawlShop = () => {
const GM_0 = getProtoFn(Game.GameModel, 0);
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]()[GM_0];
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 GM_0 = getProtoFn(Game.GameModel, 0);
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]()[GM_0];
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 PSAD_29 = getProtoFn(selfGame['game.mechanics.season_adventure.model.PlayerSeasonAdventureData'], 29);
const Player = Game.GameModel[GameInst]()[GM_0];
const PlayerSeasonAdventureData = findInstanceOf(Player, selfGame['game.mechanics.season_adventure.model.PlayerSeasonAdventureData']);
PlayerSeasonAdventureData[PSAD_29]({ 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';
const mapData = b.raw.tiledMap.list[3];
const mapExtraData = mapData.map.mapExtraData;
const chestLevels = mapExtraData.chestLevels;
const tiledMapLevels = b.raw.tiledMap.level;
for (const id in tiledMapLevels) {
const level = tiledMapLevels[id];
if (chestLevels.includes(level.level)) {
level.clientData.graphics.visible = ['hex_heal'];
level.clientData.graphics.fogged = ['fog', 'question'];
}
}
} 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() {
// bmF0cmlidS5vcmc=
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(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 Caller.send({ name: 'topGet', args: { type: 'bossRatingTop', extraId: 0 } });
if (!topGet) {
setProgress(`${I18N('EVENT')} ${I18N('NOT_AVAILABLE')}`, true);
return;
}
const replayId = topGet.userData.replayId;
const [battleGetReplay, heroGetAll, pet_getAll, offerGetAll] = await Caller.send([
{ name: 'battleGetReplay', args: { id: replayId } },
'heroGetAll',
'pet_getAll',
'offerGetAll',
]);
const bossEventInfo = offerGetAll.find((e) => e.offerType == 'bossEvent');
if (!bossEventInfo) {
setProgress(`${I18N('EVENT')} ${I18N('NOT_AVAILABLE')}`, true);
return;
}
const usedHeroes = bossEventInfo.progress.usedHeroes;
const party = Object.values(battleGetReplay.replay.attackers);
const availableHeroes = Object.values(heroGetAll).map((e) => e.id);
const availablePets = Object.values(pet_getAll).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: 'bossRating_startBattle',
args,
});
}
/**
* 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: 'bossRating_startBattle',
args: {
heroes: [...heroes],
pet: availablePets[Math.floor(Math.random() * availablePets.length)],
},
});
heroes = [];
count++;
}
}
if (!calls.length) {
setProgress(`${I18N('NO_HEROES')}`, true);
return;
}
console.log(await Caller.send(calls));
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: 'bossRating_getReward',
args: {
rewardId: i,
},
ident: 'body_' + i,
});
count++;
}
if (!count) {
setProgress(`${I18N('NOTHING_TO_COLLECT')}`, true);
return;
}
send(getRewardCall, e => {
console.log(e);
setProgress(`${I18N('COLLECTED')} ${e?.results?.length} ${I18N('REWARD')}`, true);
});
});
}
/**
* Collect Easter eggs and event rewards
*
* Собрать пасхалки и награды событий
*/
async function offerFarmAllReward() {
const offerGetAll = await Caller.send('offerGetAll');
const rewards = offerGetAll.filter((e) => e.type == 'reward' && !e?.freeRewardObtained && e.reward);
if (!rewards.length) {
setProgress(`${I18N('NOTHING_TO_COLLECT')}`, true);
return;
}
const results = await Caller.send(rewards.map((reward) => ({
name: 'offerFarmReward',
args: { offerId: reward.id },
})));
console.log(results);
setProgress(`${I18N('COLLECTED')} ${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(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(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(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) {
async function stopMission() {
isSendsMission = false;
console.log(I18N('STOPPED'));
setProgress('');
await popup.confirm(`${I18N('STOPPED')} ${I18N('REPETITIONS')}: ${param.count}`, [
{
msg: 'Ok',
result: true,
color: 'green',
},
]);
}
if (isStopSendMission) {
stopMission();
return;
}
lastMissionBattleStart = Date.now();
/**
* Mission Request
*
* Запрос на выполнение мисии
*/
let battle;
try {
battle = await Caller.send({
name: 'missionStart',
args: lastMissionStart,
});
} catch (e) {
isSendsMission = false;
console.error(e);
setProgress('');
return;
}
const result = await Calc(battle);
let timer = getTimer(result.battleTime) + 5;
const period = Math.ceil((Date.now() - lastMissionBattleStart) / 1000);
if (period < timer) {
timer = timer - period;
const isSuccess = await countdownTimer(timer, `${I18N('MISSIONS_PASSED')}: ${param.count}`, () => {
isStopSendMission = true;
});
if (!isSuccess) {
stopMission();
return;
}
}
let r;
try {
r = await Caller.send({
name: 'missionEnd',
args: {
id: param.id,
result: result.result,
progress: result.progress,
},
});
} catch (e) {
isSendsMission = false;
console.error(e);
setProgress('');
return;
}
if (r['error']) {
isSendsMission = false;
console.log(r['error']);
setProgress('');
await popup.confirm(` ${I18N('REPETITIONS')}: ${param.count}` + ' 3 ' + r['error'], [{ msg: 'Ok', result: true, color: 'green' }]);
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 response = await Caller.send({
name: 'consumableUseLootBox',
args: { libId, amount },
});
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
*
* Собрать всю почту, кроме писем с энергией и зарядами портала
*/
async function mailGetAll() {
const { Letters } = HWHClasses;
const mailGetAll = await Caller.send('mailGetAll');
const letterIds = Letters.filter(mailGetAll.letters);
if (!letterIds.length) {
setProgress(I18N('NOTHING_TO_COLLECT'), true);
return;
}
const lettersIds = await Caller.send({
name: 'mailFarm',
args: { letterIds },
});
if (lettersIds) {
const countLetters = Object.keys(lettersIds).length;
setProgress(`${I18N('RECEIVED')} ${countLetters} ${I18N('LETTERS')}`, true);
}
}
class Letters {
/**
* Максимальное оставшееся время для автоматического сбора письма (24 часа)
*/
static MAX_TIME_LEFT = 24 * 60 * 60 * 1000;
/**
* Фильтрует получаемые письма
* @param {Array} letters - Массив писем для фильтрации
* @returns {Array} - Массив ID писем, которые нужно собрать
*/
static filter(letters) {
const { Letters } = HWHClasses;
const lettersIds = [];
for (let l in letters) {
const letter = letters[l];
const reward = letter?.reward;
if (!reward || !Object.keys(reward).length) {
continue;
}
if (Letters.shouldCollectLetter(reward)) {
lettersIds.push(~~letter.id);
continue;
}
// Проверка времени до окончания годности письма
const availableUntil = +letter?.availableUntil;
if (availableUntil) {
const timeLeft = new Date(availableUntil * 1000) - new Date();
console.log('Time left:', timeLeft);
if (timeLeft < Letters.MAX_TIME_LEFT) {
lettersIds.push(~~letter.id);
}
}
}
return lettersIds;
}
/**
* Определяет, нужно ли собирать письмо (может быть переопределен в дочерних классах)
* @param {Object} reward - Награда письма
* @returns {boolean} - Нужно ли собирать письмо
*/
static shouldCollectLetter(reward) {
return !(
/** 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)
)
);
}
}
this.HWHClasses.Letters = Letters;
function setPortals(value = 0, isChange = false) {
const { buttons } = HWHData;
const sanctuaryButton = buttons['testAdventure'].button;
const sanctuaryDot = sanctuaryButton.querySelector('.scriptMenu_dot');
if (isChange) {
value = Math.max(+sanctuaryDot.innerText + value, 0);
}
if (value) {
sanctuaryButton.classList.add('scriptMenu_attention');
sanctuaryDot.title = `${value} ${I18N('PORTALS')}`;
sanctuaryDot.innerText = value;
sanctuaryDot.style.backgroundColor = 'red';
} else {
sanctuaryButton.classList.remove('scriptMenu_attention');
sanctuaryDot.innerText = 0;
}
}
function setWarTries(value = 0, isChange = false, arePointsMax = false) {
const { buttons } = HWHData;
const clanWarButton = buttons['goToClanWar'].button;
const clanWarDot = clanWarButton.querySelector('.scriptMenu_dot');
if (isChange) {
value = Math.max(+clanWarDot.innerText + value, 0);
}
if (value && !arePointsMax) {
clanWarButton.classList.add('scriptMenu_attention');
clanWarDot.title = `${value} ${I18N('ATTEMPTS')}`;
clanWarDot.innerText = value;
clanWarDot.style.backgroundColor = 'red';
} else {
clanWarButton.classList.remove('scriptMenu_attention');
clanWarDot.innerText = 0;
}
}
/**
* Displaying information about the areas of the portal and attempts on the VG
*
* Отображение информации о сферах портала и попытках на ВГ
*/
async function justInfo() {
return new Promise(async (resolve, reject) => {
const [userGetInfo, clanWarGetInfo, titanArenaGetStatus] = await Caller.send([
'userGetInfo',
'clanWarGetInfo',
'titanArenaGetStatus',
'quest_completeEasterEggQuest',
]);
const portalSphere = userGetInfo.refillable.find((n) => n.id == 45);
const clanWarMyTries = clanWarGetInfo?.myTries ?? 0;
const arePointsMax = clanWarGetInfo?.arePointsMax;
const titansLevel = +(titanArenaGetStatus?.tier ?? 0);
const titansStatus = titanArenaGetStatus?.status; //peace_time || battle
setPortals(portalSphere.amount);
setWarTries(clanWarMyTries, false, arePointsMax);
const { buttons } = HWHData;
const titansArenaButton = buttons['testTitanArena'].button;
const titansArenaDot = titansArenaButton.querySelector('.scriptMenu_dot');
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(' ' + `${portalSphere.amount} ${I18N('GUILD_WAR')}: ${clanWarMyTries}`, true);
resolve();
});
}
async function getDailyBonus() {
const dailyBonusInfo = await Caller.send('dailyBonusGetInfo');
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 reward = await Caller.send({
name: 'dailyBonusFarm',
args: {
vip: availableVip && currentVipLevel >= vipLevelDouble ? 1 : 0,
},
});;
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 inventory = await Caller.send('inventoryGet');
const lootBox = inventory.consumable?.[lootBoxId];
/** Добавить другие ящики */
/**
* 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, color: 'green' },
{ 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 Caller.send({
name: 'consumableUseLootBox',
args: { libId: lootBoxId, amount: 1 },
});
const result = Object.values(response).pop();
if ('stamina' in result) {
setProgress(
`${I18N('OPEN')}: ${lootBox - count}/${lootBox} ${I18N('STAMINA')} +${result.stamina} ${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} ${I18N('STAMINA')}: ${collectEnergy}`, false);
console.log(result);
}
}
setProgress(I18N('BOXES_OVER'), true);
}
async function fillActive() {
const [quests, inv, clanInfo] = await Caller.send(['questGetAll', 'inventoryGet', 'clanGetInfo']);
const stat = clanInfo.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(), color: 'green' },
]));
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;
}
const response = await Caller.send({
name: 'clanItemsForActivity',
args: {
items: {
[activeItem.type]: {
[activeItem.id]: countItem,
},
},
},
});
/** TODO: Вывести потраченые предметы */
console.log(response);
setProgress(`${I18N('ACTIVITY_RECEIVED')}: ` + response, true);
}
async function buyHeroFragments() {
const [inv, shopAll] = await Caller.send(['inventoryGet', 'shopGetAll']);
const shops = Object.values(shopAll).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,
},
});
}
}
if (!calls.length) {
setProgress(I18N('NO_PURCHASABLE_HERO_SOULS'), true);
return;
}
const bought = await Caller.send(calls);
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 [user, boses, offers, time] = await Caller.send(['userGetInfo', 'bossGetAll', 'specialOffer_getAll', 'getTime']);
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 =
"";
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],
color: 'green',
});
}
if (currentStarMoney >= cost18chests) {
buttons.push({
msg: I18N('BUY_OUTLAND_BTN', { count: 18, countEmerald: cost18chests, imgEmerald }),
result: [costFirstChest, costFirstChest, 0, costSecondChest, costSecondChest, 0],
color: 'green',
});
}
const answer = await popup.confirm(`