diff --git a/.cursorrules b/.cursorrules
new file mode 100644
index 0000000..5c17f1f
--- /dev/null
+++ b/.cursorrules
@@ -0,0 +1,138 @@
+# Cursor Rules for AutoHero Project
+
+## Extension Development Guidelines
+
+### Creating HeroWarsHelper Extensions
+
+When creating a new extension for HeroWarsHelper, follow this pattern:
+
+1. **File Structure**: Create a `.user.js` file with the extension name
+ - Example: `Secret Wealth Shop HwH Ext.user.js`
+
+2. **UserScript Header**: Include proper metadata
+ ```javascript
+ // ==UserScript==
+ // @name Extension Name HwH Ext
+ // @namespace HeroWarsHelper.ExtensionName
+ // @version 1.0
+ // @description Brief description of the extension
+ // @author YourName
+ // @match https://www.hero-wars.com/*
+ // @match https://apps-1701433570146040.apps.fbsbx.com/*
+ // @grant none
+ // @run-at document-end
+ // @downloadURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/Extension%20Name%20HwH%20Ext.user.js
+ // @updateURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/Extension%20Name%20HwH%20Ext.user.js
+ // ==/UserScript==
+ ```
+
+3. **Initialization Pattern**: Always wait for HWH to be ready
+ ```javascript
+ (function() {
+ 'use strict';
+
+ const waitForHWH = setInterval(() => {
+ if (window.HWHClasses && window.HWHClasses.ScriptMenu && window.lib && window.cheats) {
+ const scriptMenu = window.HWHClasses.ScriptMenu.getInst();
+ if (scriptMenu && scriptMenu.mainMenu) {
+ clearInterval(waitForHWH);
+ initializeExtension();
+ }
+ }
+ }, 200);
+
+ function initializeExtension() {
+ console.log('Extension Name: HWH UI is ready, initializing extension...');
+
+ const { HWHClasses, HWHFuncs, Send, cheats, Caller, lib } = window;
+
+ // Your extension code here
+
+ // Add menu button
+ const { ScriptMenu } = HWHClasses;
+ const scriptMenu = ScriptMenu.getInst();
+ scriptMenu.addCombinedButton([
+ { name: 'Extension Name', title: 'Description', onClick: yourFunction, color: 'purple' }
+ ]);
+ }
+ })();
+ ```
+
+4. **Auto-Load on Script Run**: To execute code automatically when the script loads:
+ ```javascript
+ function initializeExtension() {
+ // ... setup code ...
+
+ // Auto-execute function on script load
+ autoExecuteFunction().catch(error => {
+ console.error('Extension: Failed to auto-execute:', error);
+ });
+
+ // Add menu button
+ scriptMenu.addCombinedButton([...]);
+ }
+ ```
+
+5. **Popup Handling**: When using popups, always await the promise properly:
+ ```javascript
+ async function openPopup() {
+ const popupContent = document.createElement('div');
+ // ... build popup content ...
+
+ // Use confirm with proper async handling
+ const popupPromise = HWHFuncs.popup.confirm('', [{ msg: 'Close', result: true, isClose: true }]);
+
+ // Wait a tick for popup to initialize
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ const popupBody = document.querySelector('.PopUp_Container');
+ if (popupBody) {
+ popupBody.innerHTML = '';
+ popupBody.appendChild(popupContent);
+ }
+
+ // Wait for popup to close before returning
+ await popupPromise;
+ }
+ ```
+
+6. **Available HWH APIs**:
+ - `HWHClasses.ScriptMenu` - Menu system
+ - `HWHFuncs.setProgress(text, hide)` - Progress messages
+ - `HWHFuncs.popup.confirm()` - Popup dialogs
+ - `Caller` - API call wrapper
+ - `cheats.translate(key)` - Translation system
+ - `Send()` - Direct API sending
+
+7. **Best Practices**:
+ - Always check if HWH is ready before initializing
+ - Use proper error handling with try/catch
+ - Log important events to console
+ - Use HWHFuncs.setProgress for user feedback
+ - Follow existing code patterns from other extensions
+ - Use async/await for API calls
+ - Properly handle popup promises to prevent menu interference
+
+8. **API Call Requirements**:
+ - **NEVER mock or fake API calls** - All API calls must be real and functional
+ - **Always verify API calls against documentation** - Check the relevant API documentation files:
+ - `ARENA_API_DOCUMENTATION.md` for Arena and Grand Arena APIs
+ - `GUILD_WAR_API_DOCUMENTATION.md` for Guild War APIs
+ - `CLAN_RAID_API_DOCUMENTATION.md` for Raid APIs
+ - `SECRET_WEALTH_SHOP_API_DOCUMENTATION.md` for Shop APIs
+ - **Follow exact API structure** - Match the request format exactly as documented:
+ - Include `name`, `args`, `context` (with `actionTs`), and `ident` fields
+ - Use correct parameter names and types as specified in documentation
+ - Ensure response handling matches documented response structure
+ - **Verify against working code** - If documentation is incomplete, verify against existing working implementations in `HeroWarsHelper.user.js`
+ - **Test API calls** - Ensure all API calls are tested and working before committing
+ - **No placeholder APIs** - Never use placeholder or example API calls that don't actually work
+
+## Code Style
+
+- Use meaningful variable names
+- Add comments for complex logic
+- Follow existing indentation (spaces, not tabs)
+- Use consistent naming conventions
+- Handle errors gracefully with user-friendly messages
+
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..914b20d
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,2 @@
+# PostgreSQL connection for arena training results
+DATABASE_URL=postgresql://postgres:postgres@localhost:5432/autohero
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..50edd2a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,37 @@
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+
+# Virtual environments
+venv/
+env/
+ENV/
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Logs and temporary files
+*.log
+*.tmp
+*.bak
+
+# Generated files (optional - uncomment if you don't want to track these)
+# schedule_extracted.csv
+# hero_wars_events_email.txt
+# hero_wars_schedule.ics
+
+# Node
+node_modules/
+
+# Environment variables
+.env
diff --git a/API Debugger HwH Ext.user.js b/API Debugger HwH Ext.user.js
new file mode 100644
index 0000000..61fa4ca
--- /dev/null
+++ b/API Debugger HwH Ext.user.js
@@ -0,0 +1,320 @@
+// ==UserScript==
+// @name API Debugger HwH Ext
+// @namespace HeroWarsHelper.APIDebugger
+// @version 1.0
+// @description Debugging tool to test all Hero Wars API calls and capture results
+// @author AutoHero
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @grant none
+// @run-at document-end
+// @downloadURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/API%20Debugger%20HwH%20Ext.user.js
+// @updateURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/API%20Debugger%20HwH%20Ext.user.js
+// ==/UserScript==
+
+(function() {
+ 'use strict';
+
+ const waitForHWH = setInterval(() => {
+ if (window.HWHClasses && window.HWHClasses.ScriptMenu && window.lib && window.cheats) {
+ const scriptMenu = window.HWHClasses.ScriptMenu.getInst();
+ if (scriptMenu && scriptMenu.mainMenu) {
+ clearInterval(waitForHWH);
+ initializeExtension();
+ }
+ }
+ }, 200);
+
+ function initializeExtension() {
+ console.log('API Debugger: HWH UI is ready, initializing extension...');
+
+ const { HWHClasses, HWHFuncs, Send, cheats, Caller, lib } = window;
+
+ // API definitions organized by category
+ const apiDefinitions = {
+ 'Core User & Inventory': [
+ { name: 'userGetInfo', args: {}, description: 'Get user information (stats, resources, arena status)' },
+ { name: 'inventoryGet', args: {}, description: 'Get all inventory items (consumables, gear, fragments)' },
+ { name: 'getTime', args: {}, description: 'Get server time' }
+ ],
+ 'Heroes, Titans & Teams': [
+ { name: 'heroGetAll', args: {}, description: 'Get all hero information' },
+ { name: 'titanGetAll', args: {}, description: 'Get all titan information' },
+ { name: 'teamGetAll', args: {}, description: 'Get all team configurations' },
+ { name: 'teamGetFavor', args: {}, description: 'Get favor information for teams' },
+ { name: 'teamGetMaxUpgrade', args: {}, description: 'Get maximum upgrade information for teams' }
+ ],
+ 'Shops & Purchases': [
+ { name: 'shopGetAll', args: {}, description: 'Get all shop information' },
+ { name: 'shopGet', args: { shopId: 13 }, description: 'Get specific shop information (Titan Artifact Shop)' }
+ ],
+ 'Quests & Missions': [
+ { name: 'questGetAll', args: {}, description: 'Get all quest information' },
+ { name: 'missionGetAll', args: {}, description: 'Get all mission information' }
+ ],
+ 'Arena & PvP': [
+ { name: 'arenaFindEnemies', args: {}, description: 'Find available opponents in regular arena' },
+ { name: 'arenaCheckTargetRange', args: { ids: [] }, description: 'Check if target opponents are still in valid attack range' },
+ { name: 'grandFindEnemies', args: {}, description: 'Find available opponents in Grand Arena' },
+ { name: 'grandCheckTargetRange', args: { ids: [] }, description: 'Check if Grand Arena opponents are still available' },
+ { name: 'titanArenaGetStatus', args: {}, description: 'Get titan arena status' },
+ { name: 'demoBattles_getAll', args: {}, description: 'Get all battle simulation history' }
+ ],
+ 'Guild War & Clan': [
+ { name: 'clanWarGetInfo', args: {}, description: 'Get Guild War information (slots, teams)' },
+ { name: 'clanWarGetDefence', args: {}, description: 'Get Guild War defense information' },
+ { name: 'clanGetInfo', args: {}, description: 'Get clan information' },
+ { name: 'clanRaid_getInfo', args: {}, description: 'Get complete clan raid information' },
+ { name: 'clanRaid_usersInBossBattle', args: {}, description: 'Get information about other clan members currently fighting the same boss' },
+ { name: 'crossClanWar_getInfo', args: {}, description: 'Get Cross Clan War information' },
+ { name: 'crossClanWar_getAttackMap', args: {}, description: 'Get Cross Clan War attack map information' }
+ ],
+ 'Dungeon & Tower': [
+ { name: 'dungeonGetInfo', args: {}, description: 'Get dungeon information' },
+ { name: 'towerGetInfo', args: {}, description: 'Get tower information' }
+ ],
+ 'Adventure & Brawls': [
+ { name: 'adventure_getInfo', args: {}, description: 'Get adventure information' },
+ { name: 'adventureSolo_getInfo', args: {}, description: 'Get solo adventure information' },
+ { name: 'brawl_questGetInfo', args: {}, description: 'Get brawl quest information' },
+ { name: 'brawl_findEnemies', args: {}, description: 'Find enemies in brawls' },
+ { name: 'brawl_getInfo', args: {}, description: 'Get brawl information' },
+ { name: 'epicBrawl_getWinStreak', args: {}, description: 'Get epic brawl win streak information' }
+ ],
+ 'Boss & Rankings': [
+ { name: 'bossGetAll', args: {}, description: 'Get all Outland boss information' },
+ { name: 'topGet', args: { type: 'bossRatingTop', extraId: 0 }, description: 'Get top rankings' }
+ ],
+ 'Mail & Rewards': [
+ { name: 'mailGetAll', args: {}, description: 'Get all mail/letters' }
+ ],
+ 'Special Events & Offers': [
+ { name: 'specialOffer_getAll', args: {}, description: 'Get all special offers' },
+ { name: 'battlePass_getInfo', args: {}, description: 'Get battle pass information' },
+ { name: 'battlePass_getSpecial', args: {}, description: 'Get special battle pass information' },
+ { name: 'newYearGiftGet', args: { type: 0 }, description: 'Get new year gift information' },
+ { name: 'expeditionGet', args: {}, description: 'Get expedition information' },
+ { name: 'heroTalent_getReward', args: { talentType: 'tmntDungeonTalent', reroll: false }, description: 'Get hero talent reward information' }
+ ]
+ };
+
+ // Storage for API results
+ let apiResults = [];
+ let resultCounter = 0;
+
+ // Format timestamp
+ function formatTimestamp() {
+ const now = new Date();
+ return now.toISOString().replace('T', ' ').substring(0, 19);
+ }
+
+ // Log API call and result
+ async function logAPICall(apiName, args, response, error = null) {
+ const timestamp = formatTimestamp();
+ const resultId = ++resultCounter;
+
+ const logEntry = {
+ id: resultId,
+ timestamp: timestamp,
+ apiName: apiName,
+ args: args,
+ success: !error,
+ error: error ? {
+ name: error.name,
+ message: error.message,
+ stack: error.stack
+ } : null,
+ response: response,
+ responseSize: response ? JSON.stringify(response).length : 0
+ };
+
+ apiResults.push(logEntry);
+
+ // Console logging with detailed formatting
+ console.group(`%c[API Debugger] ${apiName} (ID: ${resultId})`, 'color: #4CAF50; font-weight: bold;');
+ console.log('%cTimestamp:', 'color: #2196F3; font-weight: bold;', timestamp);
+ console.log('%cArguments:', 'color: #FF9800; font-weight: bold;', args);
+
+ if (error) {
+ console.error('%cError:', 'color: #F44336; font-weight: bold;', error);
+ } else {
+ console.log('%cResponse:', 'color: #9C27B0; font-weight: bold;', response);
+ console.log('%cResponse Size:', 'color: #607D8B;', `${logEntry.responseSize} bytes`);
+ }
+
+ console.groupEnd();
+
+ return logEntry;
+ }
+
+ // Download results as JSON file
+ function downloadResults() {
+ if (apiResults.length === 0) {
+ HWHFuncs.setProgress('API Debugger: No results to download', true);
+ return;
+ }
+
+ const dataStr = JSON.stringify(apiResults, null, 2);
+ const dataBlob = new Blob([dataStr], { type: 'application/json' });
+ const url = URL.createObjectURL(dataBlob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = `api_debug_results_${Date.now()}.json`;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+
+ HWHFuncs.setProgress(`API Debugger: Downloaded ${apiResults.length} API results`, true);
+ console.log(`API Debugger: Downloaded ${apiResults.length} API results`);
+ }
+
+ // Clear all results
+ function clearResults() {
+ apiResults = [];
+ resultCounter = 0;
+ HWHFuncs.setProgress('API Debugger: Results cleared', true);
+ console.log('API Debugger: Results cleared');
+ }
+
+ // Execute API call
+ async function executeAPICall(apiDef) {
+ try {
+ HWHFuncs.setProgress(`API Debugger: Calling ${apiDef.name}...`);
+
+ const calls = [{
+ name: apiDef.name,
+ args: apiDef.args,
+ context: { actionTs: Date.now() },
+ ident: 'body'
+ }];
+
+ const startTime = performance.now();
+ const response = await Send(JSON.stringify({ calls }));
+ const endTime = performance.now();
+ const duration = endTime - startTime;
+
+ if (response.error) {
+ const error = new Error(`API error: ${response.error.name} - ${response.error.description}`);
+ await logAPICall(apiDef.name, apiDef.args, null, error);
+ HWHFuncs.setProgress(`API Debugger: ${apiDef.name} failed - ${error.message}`, true);
+ return;
+ }
+
+ const result = response.results && response.results[0] ? response.results[0].result.response : null;
+ const logEntry = await logAPICall(apiDef.name, apiDef.args, result);
+ logEntry.duration = `${duration.toFixed(2)}ms`;
+
+ HWHFuncs.setProgress(`API Debugger: ${apiDef.name} completed (${duration.toFixed(0)}ms)`, true);
+ console.log(`API Debugger: ${apiDef.name} completed in ${duration.toFixed(2)}ms`);
+
+ } catch (error) {
+ await logAPICall(apiDef.name, apiDef.args, null, error);
+ HWHFuncs.setProgress(`API Debugger: ${apiDef.name} error - ${error.message}`, true);
+ console.error(`API Debugger: ${apiDef.name} error:`, error);
+ }
+ }
+
+ // Open API debugger popup
+ async function openAPIDebugger() {
+ const popupContent = document.createElement('div');
+ popupContent.style.cssText = 'display: flex; flex-direction: column; height: 80vh; color: #fce1ac;';
+
+ const header = document.createElement('div');
+ header.style.cssText = 'padding: 15px; border-bottom: 2px solid #8b6914; background: rgba(0,0,0,0.3);';
+ header.innerHTML = `
+
API Debugger
+
+ Results: ${apiResults.length}
+ Download Results
+ Clear Results
+
+ `;
+ popupContent.appendChild(header);
+
+ const contentContainer = document.createElement('div');
+ contentContainer.style.cssText = 'flex-grow: 1; overflow-y: auto; padding: 15px;';
+
+ // Create category sections
+ for (const [category, apis] of Object.entries(apiDefinitions)) {
+ const categoryDiv = document.createElement('div');
+ categoryDiv.style.cssText = 'margin-bottom: 25px;';
+
+ const categoryHeader = document.createElement('h3');
+ categoryHeader.style.cssText = 'color: #ffd700; margin: 0 0 10px 0; padding-bottom: 5px; border-bottom: 1px solid #8b6914;';
+ categoryHeader.textContent = category;
+ categoryDiv.appendChild(categoryHeader);
+
+ const apiGrid = document.createElement('div');
+ apiGrid.style.cssText = 'display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 10px;';
+
+ for (const api of apis) {
+ const apiButton = document.createElement('button');
+ apiButton.style.cssText = `
+ padding: 12px;
+ background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%);
+ border: 1px solid #8b6914;
+ border-radius: 6px;
+ color: #fce1ac;
+ cursor: pointer;
+ text-align: left;
+ transition: all 0.3s;
+ `;
+ apiButton.innerHTML = `
+ ${api.name}
+ ${api.description}
+ `;
+ apiButton.addEventListener('mouseenter', () => {
+ apiButton.style.background = 'linear-gradient(135deg, #3a3a3a 0%, #2a2a2a 100%)';
+ apiButton.style.borderColor = '#ffd700';
+ });
+ apiButton.addEventListener('mouseleave', () => {
+ apiButton.style.background = 'linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%)';
+ apiButton.style.borderColor = '#8b6914';
+ });
+ apiButton.addEventListener('click', () => {
+ executeAPICall(api);
+ });
+ apiGrid.appendChild(apiButton);
+ }
+
+ categoryDiv.appendChild(apiGrid);
+ contentContainer.appendChild(categoryDiv);
+ }
+
+ popupContent.appendChild(contentContainer);
+
+ // Use confirm with proper async handling
+ const popupPromise = HWHFuncs.popup.confirm('', [{ msg: 'Close', result: true, isClose: true }]);
+
+ // Wait a tick for popup to initialize
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ const popupBody = document.querySelector('.PopUp_Container');
+ if (popupBody) {
+ popupBody.innerHTML = '';
+ popupBody.appendChild(popupContent);
+
+ // Attach event listeners
+ document.getElementById('downloadResults').addEventListener('click', downloadResults);
+ document.getElementById('clearResults').addEventListener('click', () => {
+ clearResults();
+ header.querySelector('span').textContent = `Results: ${apiResults.length}`;
+ });
+ }
+
+ // Wait for popup to close before returning
+ await popupPromise;
+ }
+
+ // Add menu button
+ const { ScriptMenu } = HWHClasses;
+ const scriptMenu = ScriptMenu.getInst();
+ scriptMenu.addCombinedButton([
+ { name: '🔍 API Debugger', title: 'Open API Debugger - Test all API calls and capture results', onClick: openAPIDebugger, color: 'purple' }
+ ]);
+
+ console.log('API Debugger: Extension initialized successfully');
+ }
+})();
+
diff --git a/API_LOGGING_README.md b/API_LOGGING_README.md
new file mode 100644
index 0000000..e85be5e
--- /dev/null
+++ b/API_LOGGING_README.md
@@ -0,0 +1,109 @@
+# API Monitor Logging System
+
+## 📁 **File Locations**
+
+### **Downloads Folder (Default)**
+- **Location**: `C:\Users\USER\Downloads\`
+- **Files**: `AutoHero-API-Logs-*.json`, `AutoHero-Test-*.json`
+- **Why**: Tampermonkey security restrictions
+
+### **Project Directory (After Copy)**
+- **Location**: `C:\Users\USER\Workspace\AutoHero\api-logs\`
+- **Files**: Same files copied from Downloads
+- **How**: Use the copy scripts below
+
+## 🔄 **How to Copy Logs to Project Directory**
+
+### **Method 1: PowerShell Script (Recommended)**
+```powershell
+# Run from project directory
+.\copy-api-logs.ps1
+```
+
+### **Method 2: Batch File**
+```cmd
+# Double-click or run from command prompt
+copy-logs.bat
+```
+
+### **Method 3: Manual Copy**
+1. Open Downloads folder: `C:\Users\USER\Downloads\`
+2. Find files starting with `AutoHero-API-Logs-`
+3. Copy them to: `C:\Users\USER\Workspace\AutoHero\api-logs\`
+
+## 📊 **Log File Formats**
+
+### **JSON Format (Default)**
+```json
+{
+ "requests": [...],
+ "responses": [...],
+ "errors": [...],
+ "metadata": {
+ "timestamp": "2024-01-01T12:00:00.000Z",
+ "totalRequests": 5,
+ "totalResponses": 5,
+ "totalErrors": 0
+ }
+}
+```
+
+### **Text Format**
+```
+=== API MONITOR LOGS ===
+Timestamp: 2024-01-01T12:00:00.000Z
+Total Requests: 5
+Total Responses: 5
+Total Errors: 0
+
+--- REQUESTS ---
+[Request details...]
+
+--- RESPONSES ---
+[Response details...]
+```
+
+### **CSV Format**
+```
+Type,Timestamp,Method,URL,Status,Size
+request,2024-01-01T12:00:00.000Z,GET,https://api.example.com/data,200,1024
+response,2024-01-01T12:00:01.000Z,GET,https://api.example.com/data,200,1024
+```
+
+## ⚙️ **Configuration**
+
+Edit `api-monitor.user.js` to change settings:
+
+```javascript
+const CONFIG = {
+ enableFileLogging: true, // Enable/disable file logging
+ logToFileInterval: 3000, // Write logs every 3 seconds
+ maxLogFileSize: 10 * 1024 * 1024, // 10MB max file size
+ logFormat: 'json' // 'json', 'text', 'csv'
+};
+```
+
+## 🎯 **Usage Workflow**
+
+1. **Install script** in Tampermonkey
+2. **Browse websites** - API calls are automatically captured
+3. **Logs are written** to Downloads folder every 3 seconds
+4. **Run copy script** to move logs to project directory
+5. **Analyze logs** using any text editor or JSON viewer
+
+## 🔍 **Troubleshooting**
+
+### **No Files Created**
+- Check console for errors
+- Verify Tampermonkey permissions
+- Ensure `GM_download` grant is enabled
+
+### **Files Not Copying**
+- Check PowerShell execution policy
+- Verify file paths exist
+- Run as administrator if needed
+
+### **Empty Log Files**
+- Check if API requests are being intercepted
+- Verify website is making API calls
+- Test with the built-in test requests
diff --git a/API_MONITOR_README.md b/API_MONITOR_README.md
new file mode 100644
index 0000000..131812c
--- /dev/null
+++ b/API_MONITOR_README.md
@@ -0,0 +1,270 @@
+# Hero Wars API Monitor
+
+A comprehensive Tampermonkey script for monitoring API calls, responses, and errors in web applications, specifically designed for Hero Wars and other Nexters Global games.
+
+## Features
+
+- **Complete API Monitoring**: Captures all fetch() and XMLHttpRequest calls
+- **Real-time Statistics**: Live stats display showing request/response counts
+- **Data Export**: Export captured data as JSON or HAR format
+- **Error Tracking**: Monitors and logs API errors with stack traces
+- **UI Controls**: Built-in interface for viewing and managing captured data
+- **Auto-save**: Automatically saves data every 30 seconds
+- **Response Body Capture**: Captures response bodies with smart content type handling
+- **Memory Management**: Limits stored data to prevent memory issues
+- **🆕 File Logging**: Automatically logs all API calls and responses to files
+- **🆕 Multiple Log Formats**: Support for JSON, text, and CSV log formats
+- **🆕 Automatic File Downloads**: Logs are automatically downloaded as files
+
+## Installation
+
+1. Install [Tampermonkey](https://www.tampermonkey.net/) browser extension
+2. Open Tampermonkey dashboard
+3. Click "Create a new script"
+4. Copy the contents of `api-monitor.user.js` into the editor
+5. Save the script (Ctrl+S)
+6. Navigate to any website to start monitoring
+
+## Usage
+
+### Automatic Monitoring
+The script automatically starts monitoring when you visit any website. You'll see:
+- A stats panel in the top-right corner
+- Control buttons in the top-left corner
+- Console logs for all API activity
+
+### Manual Commands
+Use these commands in the browser console:
+
+```javascript
+// View all captured data in a popup window
+window.apiMonitor.showData()
+
+// Clear all captured data
+window.apiMonitor.clearData()
+
+// Export data as JSON file
+window.apiMonitor.exportData('json')
+
+// Export data as HAR file (for use with browser dev tools)
+window.apiMonitor.exportData('har')
+
+// Get raw data object
+window.apiMonitor.getAllData()
+
+// 🆕 File Logging Commands
+// Force write logs to file immediately
+window.apiMonitor.forceWriteLogs()
+
+// Get logging statistics
+window.apiMonitor.getLogStats()
+```
+
+### Configuration
+Modify the CONFIG object in the script to customize behavior:
+
+```javascript
+const CONFIG = {
+ maxRequests: 1000, // Maximum requests to store
+ maxResponseSize: 1024 * 1024, // Max response body size (1MB)
+ enableUI: true, // Show UI controls
+ enableExport: true, // Enable export functionality
+ enableFiltering: true, // Enable request filtering
+ logLevel: 'all', // 'all', 'errors', 'requests', 'responses'
+ enableFileLogging: true, // Enable automatic file logging
+ logToFileInterval: 5000, // Log to file every 5 seconds
+ maxLogFileSize: 10 * 1024 * 1024, // 10MB max log file size
+ logFormat: 'json' // 'json', 'text', 'csv'
+};
+```
+
+## Captured Data Structure
+
+### Request Object
+```javascript
+{
+ id: "unique_request_id",
+ type: "fetch" | "xhr",
+ url: "https://api.example.com/endpoint",
+ method: "GET" | "POST" | "PUT" | "DELETE",
+ headers: { "Content-Type": "application/json" },
+ body: request_payload,
+ timestamp: "2024-01-01T12:00:00.000Z"
+}
+```
+
+### Response Object
+```javascript
+{
+ requestId: "matching_request_id",
+ status: 200,
+ statusText: "OK",
+ headers: { "Content-Type": "application/json" },
+ body: response_data,
+ timestamp: "2024-01-01T12:00:01.000Z"
+}
+```
+
+### Error Object
+```javascript
+{
+ requestId: "matching_request_id",
+ error: "Error message",
+ stack: "Error stack trace",
+ timestamp: "2024-01-01T12:00:01.000Z"
+}
+```
+
+## Hero Wars Specific Features
+
+The script is optimized for Hero Wars API monitoring with:
+- Special handling for Nexters Global API endpoints
+- Authentication header tracking
+- Session management monitoring
+- Battle API call tracking
+
+## File Logging
+
+### Automatic File Logging
+The script automatically logs all API calls and responses to files:
+
+- **Automatic Downloads**: Files are automatically downloaded to your default download folder
+- **Multiple Formats**: Support for JSON, text, and CSV formats
+- **Configurable Interval**: Set how often logs are written (default: every 5 seconds)
+- **File Naming**: Files are named with timestamps (e.g., `api-logs-2024-01-01T12-00-00-000Z.json`)
+
+### Log Formats
+
+#### JSON Format (Default)
+```json
+{
+ "session": {
+ "url": "https://heroes-wb.nextersglobal.com/",
+ "timestamp": "2024-01-01T12:00:00.000Z",
+ "logsCount": 5
+ },
+ "logs": [
+ {
+ "type": "request",
+ "data": { /* request data */ },
+ "timestamp": "2024-01-01T12:00:00.000Z"
+ },
+ {
+ "type": "response",
+ "data": { /* response data */ },
+ "timestamp": "2024-01-01T12:00:01.000Z"
+ }
+ ]
+}
+```
+
+#### Text Format
+```
+[2024-01-01T12:00:00.000Z] REQUEST: {
+ "id": "1234567890",
+ "type": "fetch",
+ "url": "https://api.example.com/endpoint",
+ "method": "POST",
+ "headers": { "Content-Type": "application/json" },
+ "body": { "key": "value" },
+ "timestamp": "2024-01-01T12:00:00.000Z"
+}
+
+[2024-01-01T12:00:01.000Z] RESPONSE: {
+ "requestId": "1234567890",
+ "status": 200,
+ "statusText": "OK",
+ "headers": { "Content-Type": "application/json" },
+ "body": { "success": true },
+ "timestamp": "2024-01-01T12:00:01.000Z"
+}
+```
+
+#### CSV Format
+```csv
+timestamp,type,url,method,status,error
+2024-01-01T12:00:00.000Z,request,https://api.example.com/endpoint,POST,,
+2024-01-01T12:00:01.000Z,response,https://api.example.com/endpoint,,200,
+2024-01-01T12:00:02.000Z,error,https://api.example.com/endpoint,,,Network Error
+```
+
+## Export Formats
+
+### JSON Export
+Contains all captured data in a structured format suitable for analysis.
+
+### HAR Export
+Creates a HAR (HTTP Archive) file compatible with:
+- Chrome DevTools
+- Postman
+- Other HTTP analysis tools
+
+## Troubleshooting
+
+### Script Not Working
+1. Check if Tampermonkey is enabled
+2. Verify the script is active for the current domain
+3. Check browser console for errors
+4. Ensure the website allows script execution
+
+### Missing API Calls
+1. Some APIs might use WebSockets (not captured by this script)
+2. Check if the website uses Service Workers
+3. Verify the script is running on the correct domain
+
+### Performance Issues
+1. Reduce `maxRequests` in CONFIG
+2. Reduce `maxResponseSize` in CONFIG
+3. Disable UI with `enableUI: false`
+
+## Development
+
+### Adding New Features
+The script is modular and easy to extend. Key areas for modification:
+- `CONFIG` object for configuration
+- `window.apiMonitor` object for core functionality
+- Interceptor functions for fetch/XHR monitoring
+- UI functions for interface management
+
+### Testing
+Test the script on various websites to ensure compatibility:
+- Hero Wars (primary target)
+- Other Nexters Global games
+- General web applications
+
+## License
+
+This script is part of the AutoHero project and is intended for educational and development purposes.
+
+## Contributing
+
+To contribute to this script:
+1. Fork the repository
+2. Create a feature branch
+3. Make your changes
+4. Test thoroughly
+5. Submit a pull request
+
+## Changelog
+
+### Version 2.1
+- **🆕 File Logging**: Added automatic file logging functionality
+- **🆕 Multiple Log Formats**: Support for JSON, text, and CSV formats
+- **🆕 Automatic Downloads**: Logs are automatically downloaded as files
+- **🆕 Configurable Logging**: Customizable logging intervals and formats
+- **🆕 Log Statistics**: Track logging performance and statistics
+- **🆕 Enhanced UI**: Added file logging controls and status display
+- **🆕 Force Logging**: Manual trigger for immediate log file creation
+
+### Version 2.0
+- Complete rewrite with enhanced features
+- Added HAR export functionality
+- Improved UI with real-time stats
+- Better error handling and logging
+- Auto-save functionality
+- Memory management improvements
+
+### Version 1.0
+- Initial release with basic API monitoring
+- JSON export functionality
+- Console logging
diff --git a/ARENA_TRAINING_RUNBOOK.md b/ARENA_TRAINING_RUNBOOK.md
new file mode 100644
index 0000000..18e11a2
--- /dev/null
+++ b/ARENA_TRAINING_RUNBOOK.md
@@ -0,0 +1,388 @@
+# Arena Training Kit — Setup Runbook
+
+End-to-end guide to run the local bridge server, PostgreSQL database, and Arena Training userscripts for Hero Wars.
+
+## What you are setting up
+
+```
+Hero Wars (browser + Tampermonkey)
+ │ demo battles, no arena attempts used
+ ▼
+Arena Training HwH Ext ──POST──► llm-bridge-server.mjs (:9876)
+LLM Controller HwH Ext ◄─poll── │
+ │ ▼
+ └── requires HeroWarsHelper.user.js PostgreSQL (autohero DB)
+```
+
+| Component | Role |
+|-----------|------|
+| **HeroWarsHelper.user.js** | Base script — APIs, battle calc, menu |
+| **LLM Controller HwH Ext** | Polls bridge; exposes `window.LLMHWH` |
+| **Arena Training HwH Ext** | Simulates combos vs top arena defenses; saves to DB |
+| **llm-bridge-server.mjs** | Local HTTP server on `127.0.0.1:9876` |
+| **PostgreSQL** | Stores opponent combos, your combos, win rates |
+
+---
+
+## Prerequisites
+
+- **Windows 10/11** (steps below use PowerShell; adapt for macOS/Linux)
+- **Node.js 18+** — [https://nodejs.org](https://nodejs.org)
+- **Tampermonkey** browser extension
+- **Hero Wars** account in browser ([hero-wars.com](https://www.hero-wars.com))
+- Git (optional, for cloning)
+
+---
+
+## 1. Get the project
+
+```powershell
+git clone https://github.com/mailming/AutoHero.git
+cd AutoHero
+git checkout develop
+```
+
+Or download the repo as a ZIP and extract it.
+
+Install Node dependencies:
+
+```powershell
+npm install
+```
+
+---
+
+## 2. Install PostgreSQL
+
+### Option A — winget (recommended on Windows)
+
+```powershell
+winget install PostgreSQL.PostgreSQL.17
+```
+
+During setup, note the **postgres user password** you choose.
+
+### Option B — installer
+
+Download PostgreSQL 17 from [https://www.postgresql.org/download/windows/](https://www.postgresql.org/download/windows/) and install with default options.
+
+### Create the database
+
+Open **SQL Shell (psql)** or PowerShell:
+
+```powershell
+# If psql is on PATH (adjust version folder if needed):
+& "C:\Program Files\PostgreSQL\17\bin\psql.exe" -U postgres -c "CREATE DATABASE autohero;"
+```
+
+---
+
+## 3. Configure the database connection
+
+Copy the example env file and edit the password if needed:
+
+```powershell
+copy .env.example .env
+notepad .env
+```
+
+Example `.env`:
+
+```env
+DATABASE_URL=postgresql://postgres:YOUR_PASSWORD@localhost:5432/autohero
+```
+
+Default (if no `.env`): `postgresql://postgres:postgres@localhost:5432/autohero`
+
+Initialize tables:
+
+```powershell
+npm run db:init
+```
+
+Expected: no errors; creates `opponent_combos`, `matchup_tests`, `training_rounds`.
+
+---
+
+## 4. Run PostgreSQL
+
+PostgreSQL usually runs as a Windows service after install.
+
+Check status:
+
+```powershell
+Get-Service postgresql*
+```
+
+Start if stopped:
+
+```powershell
+Start-Service postgresql-x64-17 # name may vary; check Get-Service postgresql*
+```
+
+Verify connection:
+
+```powershell
+& "C:\Program Files\PostgreSQL\17\bin\psql.exe" -U postgres -d autohero -c "SELECT 1;"
+```
+
+---
+
+## 5. Run the bridge server
+
+From the project folder:
+
+```powershell
+npm run bridge
+```
+
+Or:
+
+```powershell
+node llm-bridge-server.mjs
+```
+
+Expected output:
+
+```
+PostgreSQL connected: postgresql://postgres:****@localhost:5432/autohero
+LLM bridge listening on http://127.0.0.1:9876
+Arena training: GET /training/view (HTML), /training/results (JSON), /training/matchups
+Waiting for Hero Wars tab (LLM Controller) to poll /poll ...
+```
+
+Leave this terminal open while training.
+
+### Health check
+
+```powershell
+Invoke-RestMethod http://127.0.0.1:9876/health
+```
+
+Look for `"database": { "ready": true }`.
+
+### Port already in use
+
+```powershell
+netstat -ano | findstr ":9876"
+Stop-Process -Id -Force
+```
+
+Then restart the bridge.
+
+---
+
+## 6. Install the Arena Training kit (browser)
+
+Install scripts in **Tampermonkey** in this order. All must be **enabled** on Hero Wars URLs.
+
+### 6.1 Base script (required)
+
+Install **HeroWarsHelper.user.js** from the repo (Tampermonkey → Create new script → paste file contents, or use a raw GitHub URL).
+
+Arena Training depends on `HWHClasses`, `Send`, `cheats.BattleCalc`, and `HWHFuncs` from this script.
+
+### 6.2 LLM Controller (required for bridge + PowerShell control)
+
+Install **LLM Controller HwH Ext.user.js**
+
+- Raw URL: `https://github.com/mailming/AutoHero/raw/refs/heads/develop/LLM%20Controller%20HwH%20Ext.user.js`
+- Polls `http://127.0.0.1:9876/poll` so the bridge can run commands in-game
+
+### 6.3 Arena Training (required)
+
+Install **Arena Training HwH Ext.user.js**
+
+- Raw URL: `https://github.com/mailming/AutoHero/raw/refs/heads/develop/Arena%20Training%20HwH%20Ext.user.js`
+- Runs demo battles and saves results to PostgreSQL via the bridge
+
+### 6.4 Load the game
+
+1. Open [https://www.hero-wars.com](https://www.hero-wars.com) and log in
+2. Wait for the HWH menu to appear
+3. Confirm bridge connection:
+
+```powershell
+Invoke-RestMethod http://127.0.0.1:9876/health
+# browserConnected should become true after the tab loads
+```
+
+---
+
+## 7. Run Arena Training
+
+### Option A — In-game menu
+
+Click **Arena Train** in the HWH menu (starts loop training with defaults).
+
+### Option B — Browser console
+
+```javascript
+// Continuous loop vs arena top-list defenses
+arenaTrainingStartLoop({
+ topLimit: 12, // hero pool size for generated combos
+ opponentSource: 'topGet'
+});
+
+// Stop
+arenaTrainingStopLoop();
+```
+
+### Option C — PowerShell (bridge must be running, game tab open)
+
+```powershell
+.\loop-arena-training.ps1 -TopLimit 12 -HeroPoolSize 12
+```
+
+Single round only:
+
+```powershell
+.\run-arena-training.ps1 -OpponentIndex 0 -HeroPoolSize 12
+```
+
+### Default training behavior (v1.9+)
+
+| Setting | Default |
+|---------|---------|
+| Simulations per combo | 10 demo battles |
+| Target win rate | 80% (stop when found) |
+| Test order | Arena team → 3 Grand Arena teams → meta teams (DB) → generated combos |
+| Skip cached opponents | Yes — skip if DB has ≥80% counter within 30 days |
+| Opponent source | Arena top 50 via `topGet` |
+| Arena attempts used | **None** (demo battles only) |
+
+---
+
+## 8. View results
+
+| URL | Description |
+|-----|-------------|
+| [http://127.0.0.1:9876/](http://127.0.0.1:9876/) | HTML table — opponent combo, your combo, win %, last tested |
+| [http://127.0.0.1:9876/training/results?limit=100](http://127.0.0.1:9876/training/results?limit=100) | JSON API |
+| [http://127.0.0.1:9876/training/summary](http://127.0.0.1:9876/training/summary) | Counts and latest matchup |
+
+PowerShell:
+
+```powershell
+Invoke-RestMethod http://127.0.0.1:9876/training/summary
+Invoke-RestMethod "http://127.0.0.1:9876/training/skip-check?comboKey=7,64,17,12,50|6006|1"
+```
+
+---
+
+## 9. Scrape meta arena teams (hw-recruit)
+
+Collect popular arena defense teams from [hw-recruit.com](https://hw-recruit.com/arena) and store each run as a **timestamped snapshot** in PostgreSQL.
+
+Each run creates:
+- `meta_team_snapshots` — capture time, pages scraped, team counts
+- `meta_teams` — hero combo, banner, popularity count, row rank
+
+```powershell
+pip install -r requirements.txt
+npm run db:init
+npm run db:scrape-meta-teams
+```
+
+Options:
+
+```powershell
+# Top-10 arena meta only, first 5 pages (quick test)
+python scrape_meta_teams_to_db.py --position 10 --max-page 5
+
+# Full scrape until empty pages (can take a while)
+python scrape_meta_teams_to_db.py --position 10 --max-page 0
+
+# Scrape without writing to DB
+python scrape_meta_teams_to_db.py --max-page 1 --dry-run
+```
+
+View via bridge API:
+
+```powershell
+Invoke-RestMethod http://127.0.0.1:9876/training/meta-snapshots
+Invoke-RestMethod "http://127.0.0.1:9876/training/meta-teams?snapshotId=1"
+Invoke-RestMethod "http://127.0.0.1:9876/training/meta-candidates?limit=10"
+```
+
+Arena Training (v1.11+) fetches meta candidates from `/training/meta-candidates` (latest snapshot by default). Combos are filtered to heroes you own, ordered by popularity, and tested in **Phase 3** after grand arena teams.
+
+**HTML table:** [http://127.0.0.1:9876/training/meta-view](http://127.0.0.1:9876/training/meta-view)
+
+---
+
+## 10. Optional maintenance
+
+### Import old JSON results
+
+If you have files in `arena-training-results/`:
+
+```powershell
+npm run db:import-json
+```
+
+### Backfill matchup tables from legacy rounds
+
+```powershell
+npm run db:backfill-matchups
+```
+
+### Remove low win-rate rows (example: below 60%)
+
+```powershell
+node -e "
+import pg from 'pg';
+const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/autohero' });
+const r = await pool.query('DELETE FROM matchup_tests WHERE win_rate < 60');
+console.log('Deleted', r.rowCount, 'rows');
+await pool.end();
+"
+```
+
+---
+
+## 11. Troubleshooting
+
+| Problem | Fix |
+|---------|-----|
+| `Database not ready` | Start PostgreSQL; check `DATABASE_URL` in `.env`; run `npm run db:init` |
+| `browserConnected: false` | Open Hero Wars tab; ensure LLM Controller + HeroWarsHelper are enabled in Tampermonkey |
+| Bridge save warnings | Confirm bridge is running on port 9876 |
+| Port 9876 in use | Kill old `node llm-bridge-server.mjs` process (see §5) |
+| Training never starts | Check console for errors; ensure `cheats.BattleCalc` is available (base HWH loaded) |
+| Skip-check not working | Restart bridge after updates; reload Arena Training userscript |
+| CORS / fetch errors to localhost | Use same machine for browser and bridge; URL must be `127.0.0.1:9876` |
+
+### Useful console checks (in-game)
+
+```javascript
+window.ArenaTraining?.getStatus()
+window.LLMHWH?.arenaTrainingGetStatus()
+window.cheats?.translate('LIB_HERO_NAME_55') // should return 'Iris'
+```
+
+---
+
+## 12. Daily startup checklist
+
+1. Start PostgreSQL (usually automatic)
+2. `cd AutoHero` → `npm run bridge`
+3. Open Hero Wars in browser (logged in)
+4. Confirm `Invoke-RestMethod http://127.0.0.1:9876/health` → `browserConnected: true`
+5. Start training (menu, console, or `loop-arena-training.ps1`)
+6. Monitor [http://127.0.0.1:9876/](http://127.0.0.1:9876/)
+
+---
+
+## File reference
+
+| File | Purpose |
+|------|---------|
+| `llm-bridge-server.mjs` | HTTP bridge + training routes |
+| `training-db.mjs` | PostgreSQL schema and queries |
+| `training-view.mjs` | HTML results page |
+| `hero-names.mjs` | Hero/pet ID → name mapping |
+| `loop-arena-training.ps1` | Start loop via PowerShell |
+| `run-arena-training.ps1` | Single training round via PowerShell |
+| `scrape_meta_teams_to_db.py` | Scrape hw-recruit meta teams into PostgreSQL snapshots |
+| `.env` | `DATABASE_URL` (not committed; copy from `.env.example`) |
diff --git a/Advanced Auto-Buyer HwH Ext.user.js b/Advanced Auto-Buyer HwH Ext.user.js
new file mode 100644
index 0000000..6e7196d
--- /dev/null
+++ b/Advanced Auto-Buyer HwH Ext.user.js
@@ -0,0 +1,647 @@
+// ==UserScript==
+// @name Advanced Auto-Buyer HwH Ext
+// @namespace HeroWarsHelper.AdvancedAutoBuyer
+// @version 1.6
+// @description Multi-column UI with Import/Export. Buys items based on names.
+// @author YourName & Coding Partner
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @grant none
+// @run-at document-end
+// ==/UserScript==
+
+(function() {
+ 'use strict';
+
+ const waitForHWH = setInterval(() => {
+ if (window.HWHClasses && window.HWHClasses.ScriptMenu && window.lib && window.cheats) {
+ const scriptMenu = window.HWHClasses.ScriptMenu.getInst();
+ if (scriptMenu && scriptMenu.mainMenu) {
+ clearInterval(waitForHWH);
+ initializeExtension();
+ }
+ }
+ }, 200);
+
+ function initializeExtension() {
+ console.log('Advanced Auto-Buyer: HWH UI is ready, initializing extension...');
+
+ const { HWHClasses, HWHFuncs, cheats, Caller, lib } = window;
+ const STORAGE_PREFIX = 'advAutoBuyer_';
+
+ // --- DATA STRUCTURES & HELPERS ---
+ const SHOPS = [ { id: 1, name: 'Town Shop' }, { id: 4, name: 'Arena Shop' }, { id: 5, name: 'Grand Arena Shop' }, { id: 6, name: 'Tower Shop' }, { id: 8, name: 'Soul Shop' }, { id: 9, name: 'Friendship Shop' }, { id: 10, name: 'Outland Shop' }, { id: 13, name: 'Titan Artifact Shop' }, { id: 'SECRET_WEALTH', name: 'Secret Wealth Shop' } ];
+ const ITEMS_DATABASE = window.AUTO_BUYER_ITEM_DATABASE || {};
+
+ // Helper function to find Secret Wealth Shop by pattern (ends with 0026)
+ function findSecretWealthShop(shopsData) {
+ for (const shopId in shopsData) {
+ const shopIdNum = typeof shopId === 'string' ? parseInt(shopId) : shopId;
+ if (!isNaN(shopIdNum) && shopIdNum.toString().endsWith('0026')) {
+ const shop = shopsData[shopId];
+ // Verify it's actually a Secret Wealth Shop by checking for consumable/starmoney costs
+ if (shop && shop.slots) {
+ for (const slotId in shop.slots) {
+ const slot = shop.slots[slotId];
+ if (slot.cost && (slot.cost.consumable || slot.cost.starmoney)) {
+ return { id: shopIdNum, shop: shop };
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ // Helper function to get storage key for Secret Wealth Shop (consistent regardless of actual ID)
+ function getSecretWealthStorageKey() {
+ return STORAGE_PREFIX + 'SECRET_WEALTH';
+ }
+
+ // --- NEW: Import/Export Functions ---
+ function exportSettings() {
+ const settingsToExport = {};
+ SHOPS.forEach(shop => {
+ const key = shop.id === 'SECRET_WEALTH' ? getSecretWealthStorageKey() : STORAGE_PREFIX + shop.id;
+ const data = localStorage.getItem(key);
+ if (data) {
+ settingsToExport[key] = JSON.parse(data);
+ }
+ });
+
+ if (Object.keys(settingsToExport).length === 0) {
+ alert("No settings to export!");
+ return;
+ }
+
+ const jsonString = JSON.stringify(settingsToExport, null, 2);
+ const blob = new Blob([jsonString], { type: "application/json" });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `hwh-autobuyer-settings-${new Date().toISOString().slice(0, 10)}.json`;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ }
+
+ function importSettings() {
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = '.json';
+ input.onchange = e => {
+ const file = e.target.files[0];
+ if (!file) return;
+
+ const reader = new FileReader();
+ reader.onload = readerEvent => {
+ try {
+ const content = readerEvent.target.result;
+ const importedSettings = JSON.parse(content);
+ let settingsApplied = 0;
+
+ Object.keys(importedSettings).forEach(key => {
+ if (key.startsWith(STORAGE_PREFIX)) {
+ localStorage.setItem(key, JSON.stringify(importedSettings[key]));
+ settingsApplied++;
+ }
+ });
+
+ if (settingsApplied > 0) {
+ alert(`Import successful! ${settingsApplied} shop lists were loaded.\nPlease reopen the settings to see the changes.`);
+ } else {
+ alert("Import failed: The file does not contain valid settings.");
+ }
+ } catch (err) {
+ alert("Error reading or parsing the file. Make sure it's a valid JSON settings file.");
+ console.error("Import error:", err);
+ }
+ };
+ reader.readAsText(file);
+ };
+ input.click();
+ }
+
+ // --- UI LOGIC (Heavily modified for multi-column) ---
+ async function openSettingsPopup() {
+ try {
+ const popupContent = document.createElement('div');
+ popupContent.style.cssText = 'display: flex; flex-direction: column; height: 60vh; color: #fce1ac; text-shadow: 0 0 2px black;';
+
+ const headerContainer = document.createElement('div');
+ headerContainer.style.cssText = 'display: flex; justify-content: space-between; align-items: center; border-bottom: 2px solid #ce9767; margin-bottom: 10px;';
+
+ const tabContainer = document.createElement('div');
+ tabContainer.style.cssText = 'display: flex; flex-wrap: wrap;';
+
+ // --- NEW: Import/Export Button Container ---
+ const buttonContainer = document.createElement('div');
+ const exportBtn = document.createElement('button');
+ exportBtn.textContent = 'Export';
+ exportBtn.style.cssText = 'padding: 4px 8px; border: 1px solid #ce9767; background: #3a2e24; color: #fce1ac; cursor: pointer; margin-left: 5px;';
+ exportBtn.onclick = exportSettings;
+
+ const importBtn = document.createElement('button');
+ importBtn.textContent = 'Import';
+ importBtn.style.cssText = 'padding: 4px 8px; border: 1px solid #ce9767; background: #3a2e24; color: #fce1ac; cursor: pointer; margin-left: 5px;';
+ importBtn.onclick = importSettings;
+
+ buttonContainer.appendChild(importBtn);
+ buttonContainer.appendChild(exportBtn);
+ headerContainer.appendChild(tabContainer);
+ headerContainer.appendChild(buttonContainer);
+
+ // --- MODIFIED: Main content area is now a flex container for columns ---
+ const contentContainer = document.createElement('div');
+ contentContainer.style.cssText = 'flex-grow: 1; overflow-y: auto; padding: 5px; display: flex; flex-direction: row; align-items: flex-start;';
+
+ popupContent.appendChild(headerContainer);
+ popupContent.appendChild(contentContainer);
+
+ const loadShopContent = async (shopId) => {
+ contentContainer.innerHTML = ''; // Clear previous content
+
+ // Handle Secret Wealth Shop - need to fetch actual shop ID
+ let actualShopId = shopId;
+ if (shopId === 'SECRET_WEALTH') {
+ try {
+ const caller = new Caller(['shopGetAll']);
+ await caller.send();
+ const shopsData = caller.result('shopGetAll');
+ const secretShop = findSecretWealthShop(shopsData);
+ if (secretShop) {
+ actualShopId = secretShop.id;
+ } else {
+ contentContainer.innerHTML = 'Secret Wealth Shop not found. It may not be available at this time.
';
+ return;
+ }
+ } catch (error) {
+ contentContainer.innerHTML = `Error loading Secret Wealth Shop: ${error.message}
`;
+ return;
+ }
+ }
+
+ const items = ITEMS_DATABASE[actualShopId] || ITEMS_DATABASE[shopId] || [];
+ const storageKey = shopId === 'SECRET_WEALTH' ? getSecretWealthStorageKey() : STORAGE_PREFIX + shopId;
+ const savedItems = JSON.parse(localStorage.getItem(storageKey) || '{}');
+ const savedSlotIds = JSON.parse(localStorage.getItem(storageKey + '_slots') || '[]');
+ const savedAmount = parseInt(localStorage.getItem(storageKey + '_amount') || '9999');
+
+ // --- NEW: Fixed Slot ID Section ---
+ const slotSection = document.createElement('div');
+ slotSection.style.cssText = 'margin-bottom: 20px; padding: 10px; border: 1px solid #ce9767; border-radius: 5px;';
+
+ const slotTitle = document.createElement('h3');
+ slotTitle.textContent = 'Fixed Slot IDs';
+ slotTitle.style.cssText = 'color: #ffcc66; margin: 0 0 10px 0; border-bottom: 1px solid #ce9767; padding-bottom: 5px;';
+ slotSection.appendChild(slotTitle);
+
+ const slotDescription = document.createElement('p');
+ slotDescription.textContent = 'Enter slot IDs (comma-separated) to purchase specific slots, e.g., "6, 3"';
+ slotDescription.style.cssText = 'color: #fce1ac; font-size: 12px; margin: 5px 0;';
+ slotSection.appendChild(slotDescription);
+
+ const slotInputContainer = document.createElement('div');
+ slotInputContainer.style.cssText = 'display: flex; align-items: center; gap: 10px;';
+
+ const slotInput = document.createElement('input');
+ slotInput.type = 'text';
+ slotInput.placeholder = 'e.g., 6, 3, 24';
+ slotInput.value = savedSlotIds.join(', ');
+ slotInput.style.cssText = 'flex: 1; padding: 5px; border: 1px solid #ce9767; background: #3a2e24; color: #fce1ac;';
+
+ const saveSlotBtn = document.createElement('button');
+ saveSlotBtn.textContent = 'Save Slots';
+ saveSlotBtn.style.cssText = 'padding: 5px 10px; border: 1px solid #ce9767; background: #5c4b3a; color: #fce1ac; cursor: pointer;';
+ saveSlotBtn.onclick = () => {
+ const slotIds = slotInput.value.split(',').map(s => s.trim()).filter(s => s && !isNaN(parseInt(s))).map(s => parseInt(s));
+ const storageKey = shopId === 'SECRET_WEALTH' ? getSecretWealthStorageKey() : STORAGE_PREFIX + shopId;
+ localStorage.setItem(storageKey + '_slots', JSON.stringify(slotIds));
+ alert(`Saved ${slotIds.length} slot ID(s): ${slotIds.join(', ')}`);
+ };
+
+ slotInputContainer.appendChild(slotInput);
+ slotInputContainer.appendChild(saveSlotBtn);
+ slotSection.appendChild(slotInputContainer);
+ contentContainer.appendChild(slotSection);
+
+ // --- NEW: Bulk Purchase Amount Section (only for Titan Artifact Shop) ---
+ if (actualShopId === 13) {
+ const amountSection = document.createElement('div');
+ amountSection.style.cssText = 'margin-bottom: 20px; padding: 10px; border: 1px solid #ce9767; border-radius: 5px;';
+
+ const amountTitle = document.createElement('h3');
+ amountTitle.textContent = 'Bulk Purchase Amount';
+ amountTitle.style.cssText = 'color: #ffcc66; margin: 0 0 10px 0; border-bottom: 1px solid #ce9767; padding-bottom: 5px;';
+ amountSection.appendChild(amountTitle);
+
+ const amountDescription = document.createElement('p');
+ amountDescription.textContent = 'Enter the number of items to purchase in bulk (e.g., 300). The API will enforce the maximum available.';
+ amountDescription.style.cssText = 'color: #fce1ac; font-size: 12px; margin: 5px 0;';
+ amountSection.appendChild(amountDescription);
+
+ const amountInputContainer = document.createElement('div');
+ amountInputContainer.style.cssText = 'display: flex; align-items: center; gap: 10px;';
+
+ const amountInput = document.createElement('input');
+ amountInput.type = 'number';
+ amountInput.min = '1';
+ amountInput.placeholder = 'e.g., 300';
+ amountInput.value = savedAmount;
+ amountInput.style.cssText = 'flex: 1; padding: 5px; border: 1px solid #ce9767; background: #3a2e24; color: #fce1ac;';
+
+ const saveAmountBtn = document.createElement('button');
+ saveAmountBtn.textContent = 'Save Amount';
+ saveAmountBtn.style.cssText = 'padding: 5px 10px; border: 1px solid #ce9767; background: #5c4b3a; color: #fce1ac; cursor: pointer;';
+ saveAmountBtn.onclick = () => {
+ const amount = parseInt(amountInput.value) || 9999;
+ if (amount < 1) {
+ alert('Amount must be at least 1');
+ return;
+ }
+ const storageKey = shopId === 'SECRET_WEALTH' ? getSecretWealthStorageKey() : STORAGE_PREFIX + shopId;
+ localStorage.setItem(storageKey + '_amount', amount.toString());
+ alert(`Saved bulk purchase amount: ${amount}`);
+ };
+
+ amountInputContainer.appendChild(amountInput);
+ amountInputContainer.appendChild(saveAmountBtn);
+ amountSection.appendChild(amountInputContainer);
+ contentContainer.appendChild(amountSection);
+ }
+
+ if (items.length === 0) {
+ const noItemsMsg = document.createElement('p');
+ noItemsMsg.textContent = 'No items configured for this shop yet.';
+ noItemsMsg.style.cssText = 'color: #fce1ac; margin-top: 10px;';
+ contentContainer.appendChild(noItemsMsg);
+ return;
+ }
+
+ // --- NEW: Column and Title generation logic ---
+ let currentColumn = document.createElement('div');
+ currentColumn.style.cssText = 'display: flex; flex-direction: column; margin-right: 20px;';
+ contentContainer.appendChild(currentColumn);
+
+ items.forEach(item => {
+ // Handle special types: title and newColumn
+ if (item.type === 'title' || item.type === 'newColumn') {
+ if (item.type === 'newColumn') {
+ currentColumn = document.createElement('div');
+ currentColumn.style.cssText = 'display: flex; flex-direction: column; margin-right: 20px;';
+ contentContainer.appendChild(currentColumn);
+ }
+ const title = document.createElement('h3');
+ title.textContent = item.name;
+ title.style.cssText = 'color: #ffcc66; margin: 10px 0 5px 0; border-bottom: 1px solid #ce9767; padding-bottom: 3px;';
+ currentColumn.appendChild(title);
+ return; // Continue to next item
+ }
+
+ // Handle regular items
+ const itemDiv = document.createElement('div');
+ itemDiv.style.cssText = 'display: flex; align-items: center; margin-bottom: 8px;';
+ const checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.id = `item-${actualShopId}-${item.name.replace(/\s/g, '')}`;
+ checkbox.checked = savedItems[item.name] || false;
+ checkbox.onchange = () => {
+ savedItems[item.name] = checkbox.checked;
+ const storageKey = shopId === 'SECRET_WEALTH' ? getSecretWealthStorageKey() : STORAGE_PREFIX + shopId;
+ localStorage.setItem(storageKey, JSON.stringify(savedItems));
+ };
+ const label = document.createElement('label');
+ label.setAttribute('for', checkbox.id);
+ label.textContent = item.name;
+ label.style.marginLeft = '10px';
+ itemDiv.appendChild(checkbox);
+ itemDiv.appendChild(label);
+ currentColumn.appendChild(itemDiv);
+ });
+ };
+
+ SHOPS.forEach((shop, index) => {
+ const tab = document.createElement('button');
+ tab.textContent = shop.name;
+ tab.style.cssText = 'padding: 8px 12px; border: 1px solid #ce9767; background: #3a2e24; color: #fce1ac; cursor: pointer; margin: 2px;';
+ tab.onclick = () => {
+ Array.from(tabContainer.children).forEach(t => t.style.background = '#3a2e24');
+ tab.style.background = '#5c4b3a';
+ loadShopContent(shop.id);
+ };
+ tabContainer.appendChild(tab);
+ if (index === 0) {
+ setTimeout(() => tab.click(), 0);
+ }
+ });
+
+ // Use confirm with proper async handling (simplified like HWHhuntFragmentExt)
+ const popupPromise = HWHFuncs.popup.confirm('', [{ msg: 'Close', result: true, isClose: true }]);
+
+ // Wait a tick for popup to initialize, then replace content
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ const popupBody = document.querySelector('.PopUp_Container');
+ if (popupBody) {
+ // Clear and replace content (preserve the original close button in PopUp_buttons)
+ popupBody.innerHTML = '';
+ popupBody.appendChild(popupContent);
+ }
+
+ // Wait for popup to close before returning
+ await popupPromise;
+ } catch (error) {
+ console.error('Advanced Auto-Buyer: Popup error:', error);
+ HWHFuncs.setProgress(`Settings popup error: ${error.message}`, true);
+ }
+ }
+
+ // --- ACTION LOGIC (Unchanged from v1.5) ---
+ async function runAutoBuy() {
+ console.log("--- Advanced Auto-Buyer RUNNING (v1.6 Name Logic) ---");
+ HWHFuncs.setProgress("Auto-Buyer: Fetching data...");
+ try {
+ const caller = new Caller(['shopGetAll']);
+ await caller.send();
+ const shopsData = caller.result('shopGetAll');
+
+ // Find Secret Wealth Shop dynamically by pattern
+ const secretWealthShop = findSecretWealthShop(shopsData);
+ if (secretWealthShop) {
+ console.log(`Secret Wealth Shop detected with ID: ${secretWealthShop.id}`);
+ } else {
+ console.log("Secret Wealth Shop not found (no shop ID ending in 0026)");
+ }
+
+ const callsToMake = [];
+ for (const shop of SHOPS) {
+ let shopId = shop.id;
+ let currentShopData = null;
+
+ // Handle Secret Wealth Shop dynamically
+ if (shopId === 'SECRET_WEALTH') {
+ if (!secretWealthShop) {
+ console.log(`Shop ${shop.name}: Not available (no shop ID ending in 0026 found)`);
+ continue;
+ }
+ shopId = secretWealthShop.id;
+ currentShopData = secretWealthShop.shop;
+ } else {
+ // Try both string and number format for shopId (API may return string IDs)
+ currentShopData = shopsData[shopId] || shopsData[String(shopId)] || shopsData[Number(shopId)];
+ }
+
+ if (!currentShopData || !currentShopData.slots) {
+ console.log(`Shop ${shop.name} (ID: ${shopId}): No shop data or slots found`);
+ continue;
+ }
+
+ // Get storage key - use consistent key for Secret Wealth Shop
+ const storageKey = shop.id === 'SECRET_WEALTH' ? getSecretWealthStorageKey() : STORAGE_PREFIX + shop.id;
+ const shoppingList = JSON.parse(localStorage.getItem(storageKey) || '{}');
+ const fixedSlotIds = JSON.parse(localStorage.getItem(storageKey + '_slots') || '[]');
+
+ const wantedNames = new Set();
+ for(const name in shoppingList) { if(shoppingList[name] === true) { wantedNames.add(name); } }
+
+ console.log(`Shop ${shop.name} (ID: ${shopId}): Found ${Object.keys(currentShopData.slots).length} slots, ${wantedNames.size} wanted items, ${fixedSlotIds.length} fixed slots`);
+
+ // Check if we have anything to buy (names or fixed slots)
+ if (wantedNames.size === 0 && fixedSlotIds.length === 0) {
+ console.log(`Shop ${shop.name} (ID: ${shopId}): No items to buy (no checkboxes selected and no fixed slot IDs)`);
+ continue;
+ }
+
+ for (const slot of Object.values(currentShopData.slots)) {
+ // Don't check if item is available - submit API call even if not available
+ // Skip only if slot data is completely missing
+ if (!slot) continue;
+
+ let shouldBuy = false;
+ let itemDisplayName = `Slot ${slot.id}`;
+
+ // Check if this slot is in the fixed slot IDs list
+ if (fixedSlotIds.includes(slot.id)) {
+ shouldBuy = true;
+ // Try to get item name for logging
+ try {
+ const rewardType = Object.keys(slot.reward)[0];
+ const rewardId = Object.keys(slot.reward[rewardType])[0];
+ let libTypeForTranslate = rewardType.replace('fragment', '').toUpperCase();
+ const translationKey = `LIB_${libTypeForTranslate}_NAME_${rewardId}`;
+ itemDisplayName = cheats.translate(translationKey) || `Slot ${slot.id}`;
+ } catch (e) {
+ // Keep default slot ID if translation fails
+ }
+ }
+
+ // Also check by name if not already matched
+ if (!shouldBuy && wantedNames.size > 0) {
+ const rewardType = Object.keys(slot.reward)[0];
+ const rewardId = Object.keys(slot.reward[rewardType])[0];
+ let libTypeForTranslate = rewardType.replace('fragment', '').toUpperCase();
+ const translationKey = `LIB_${libTypeForTranslate}_NAME_${rewardId}`;
+ const itemName = cheats.translate(translationKey);
+ if (wantedNames.has(itemName)) {
+ shouldBuy = true;
+ itemDisplayName = itemName;
+ }
+ }
+
+ if (shouldBuy) {
+ // Use slot.cost if available, otherwise use empty object (API will handle validation)
+ const slotCost = slot.cost || {};
+ const currencyType = slotCost ? Object.keys(slotCost)[0] : null;
+
+ // Support multiple payment types: gold, coin (standard shops), consumable, starmoney (Secret Wealth Shop)
+ // If no cost, still submit the call (API will return error if needed)
+ if (!slotCost || currencyType === 'gold' || currencyType === 'coin' || currencyType === 'consumable' || currencyType === 'starmoney') {
+ // Convert cost values from strings to numbers if needed
+ // The API sometimes returns string values but expects numbers in shopBuy
+ const normalizedCost = {};
+ if (slotCost) {
+ for (const costType in slotCost) {
+ if (typeof slotCost[costType] === 'object' && slotCost[costType] !== null) {
+ // For nested objects like coin: { "18": "12" }
+ normalizedCost[costType] = {};
+ for (const costKey in slotCost[costType]) {
+ const costValue = slotCost[costType][costKey];
+ // Convert string numbers to actual numbers
+ normalizedCost[costType][costKey] = typeof costValue === 'string' && !isNaN(Number(costValue)) ? Number(costValue) : costValue;
+ }
+ } else {
+ // For direct values like gold: "1000"
+ const costValue = slotCost[costType];
+ normalizedCost[costType] = typeof costValue === 'string' && !isNaN(Number(costValue)) ? Number(costValue) : costValue;
+ }
+ }
+ }
+
+ // Build shopBuy arguments - use slot data or defaults
+ const shopBuyArgs = {
+ shopId: shopId,
+ slot: slot.id,
+ cost: normalizedCost,
+ reward: slot.reward || {}
+ };
+
+ // Secret Wealth Shop (dynamic ID ending in 0026) - fixed purchases only (no amount parameter)
+ // Titan Artifact Shop (shopId 13) - supports bulk purchases via amount parameter
+ if (shopId === 13 && slot.staticShopMultiplePurchase === 1) {
+ // For Titan Artifact Shop, we can specify amount for bulk purchase
+ // Get the saved amount from localStorage, or use slot's maxAmount, or default to 9999
+ const savedAmount = parseInt(localStorage.getItem(storageKey + '_amount') || '0');
+ const maxAmount = savedAmount > 0 ? savedAmount : (slot.maxAmount || slot.maxPurchaseAmount || 9999);
+ shopBuyArgs.amount = maxAmount;
+ }
+ // For Secret Wealth Shop and other shops, don't include amount (fixed purchase)
+ // Note: Secret Wealth Shop is identified by pattern matching (ends with 0026)
+
+ // Store purchase info with item details for individual API calls
+ callsToMake.push({
+ name: 'shopBuy',
+ args: shopBuyArgs,
+ itemInfo: `- ${itemDisplayName} (Slot ${slot.id}) from ${shop.name}`
+ });
+ }
+ }
+ }
+ }
+ // Process purchases one by one (1 API call per item)
+ // This way, if one purchase fails, others can still succeed
+ if (callsToMake.length > 0) {
+ HWHFuncs.setProgress(`Auto-Buyer: Attempting to buy ${callsToMake.length} item(s) (one at a time)...`);
+ const errors = [];
+ const successes = [];
+
+ for (let i = 0; i < callsToMake.length; i++) {
+ const purchaseCall = callsToMake[i];
+ const itemInfo = purchaseCall.itemInfo || `Item ${i + 1}`;
+
+ try {
+ HWHFuncs.setProgress(`Auto-Buyer: Purchasing item ${i + 1}/${callsToMake.length}...`);
+
+ // Send individual API call for this purchase
+ const caller = new Caller([{ name: purchaseCall.name, args: purchaseCall.args }]);
+ await caller.send();
+
+ // Check for errors first
+ const sideResults = caller.sideResults[purchaseCall.name] || [];
+
+ // Check if there's an error in side results
+ if (sideResults && sideResults.length > 0 && sideResults[0] && sideResults[0].error) {
+ const error = sideResults[0].error;
+ const errorMsg = typeof error === 'string' ? error : (error.name || error.description || JSON.stringify(error));
+ errors.push(`${itemInfo}: ${errorMsg}`);
+ console.error(`%cPurchase Failed: ${itemInfo}`, 'color: red; font-weight: bold;', error);
+ continue;
+ }
+
+ // Get result - caller.result() returns an array of response objects
+ // For shopBuy, the response is: [{"fragmentTitanArtifact":{"2005":5}}] or similar
+ const callResult = caller.result(purchaseCall.name);
+
+ // Debug logging
+ console.log(`%cPurchase result for ${itemInfo}:`, 'color: blue;', {
+ callResult,
+ callResultType: typeof callResult,
+ callResultIsArray: Array.isArray(callResult),
+ callResultLength: callResult ? callResult.length : 0,
+ firstElement: callResult && callResult.length > 0 ? callResult[0] : null,
+ sideResults
+ });
+
+ // Check if we have a valid result
+ // If callResult is an array with at least one element, and no error in sideResults, it's a success
+ // The response object can be empty {} or contain data - both mean success if no error
+ if (callResult && Array.isArray(callResult) && callResult.length > 0) {
+ // We got a response - check the first element
+ const responseObj = callResult[0];
+ // If responseObj is an object (even empty), it's a success
+ // If responseObj is null/undefined, might still be success if no error
+ if (responseObj !== null && responseObj !== undefined) {
+ // Success - we got a response
+ successes.push(itemInfo);
+ console.log(`%cPurchase Success: ${itemInfo}`, 'color: lightgreen; font-weight: bold;', responseObj);
+ } else if (sideResults.length === 0 || !sideResults[0] || !sideResults[0].error) {
+ // Response is null/undefined but no error - might still be success (API returned successfully)
+ successes.push(itemInfo);
+ console.log(`%cPurchase Success: ${itemInfo} (no response data but no error)`, 'color: lightgreen; font-weight: bold;');
+ } else {
+ // Response is null/undefined and there's an error
+ errors.push(`${itemInfo}: No response data`);
+ console.error(`%cPurchase Failed: ${itemInfo}`, 'color: red; font-weight: bold;', 'No response data');
+ }
+ } else {
+ // No result array or empty array - check if there's an error
+ // If no error in sideResults, might still be success (unlikely but possible)
+ if (sideResults.length === 0 || !sideResults[0] || !sideResults[0].error) {
+ // No error but no result - might be success
+ successes.push(itemInfo);
+ console.log(`%cPurchase Success: ${itemInfo} (no result array but no error)`, 'color: lightgreen; font-weight: bold;');
+ } else {
+ // No result and there's an error
+ console.warn(`%cPurchase result check failed for ${itemInfo}`, 'color: orange;', {
+ callResult,
+ callResultType: typeof callResult,
+ callResultIsArray: Array.isArray(callResult),
+ callResultLength: callResult ? callResult.length : 0,
+ sideResults
+ });
+ errors.push(`${itemInfo}: No response received`);
+ console.error(`%cPurchase Failed: ${itemInfo}`, 'color: red; font-weight: bold;', 'No response received');
+ }
+ }
+ } catch (callError) {
+ errors.push(`${itemInfo}: ${callError.message || 'Unknown error'}`);
+ console.error(`%cPurchase Failed: ${itemInfo}`, 'color: red; font-weight: bold;', callError);
+ }
+
+ // Small delay between purchases to avoid rate limiting
+ if (i < callsToMake.length - 1) {
+ await new Promise(resolve => setTimeout(resolve, 100));
+ }
+ }
+
+ if (errors.length > 0) {
+ console.error('%c--- Purchase Errors ---', 'color: red; font-weight: bold;');
+ errors.forEach(error => console.error(`%c${error}`, 'color: red;'));
+ }
+
+ if (successes.length > 0) {
+ console.log('%c--- Items Bought Successfully ---', 'color: lightgreen; font-weight: bold;');
+ successes.forEach(success => console.log(`%c${success}`, 'color: lightgreen;'));
+ }
+
+ const summary = `Bought ${successes.length}/${callsToMake.length} items. ${errors.length > 0 ? `${errors.length} failed - check console.` : ''}`;
+ HWHFuncs.setProgress(summary, true);
+ } else {
+ HWHFuncs.setProgress("Auto-Buyer: No items to buy.", true);
+ }
+ } catch (error) {
+ console.error("Auto-Buyer Error:", error);
+ HWHFuncs.setProgress("Auto-Buyer Error: Check console.", true);
+ }
+ console.log("--- Advanced Auto-Buyer FINISHED ---");
+ }
+
+ // --- MENU INTEGRATION ---
+ const { ScriptMenu } = HWHClasses;
+ const scriptMenu = ScriptMenu.getInst();
+ scriptMenu.addCombinedButton([
+ { name: 'Auto-Buy', title: 'Run the automatic buyer based on your settings', onClick: runAutoBuy, color: 'green' },
+ { name: '⚙️', title: 'Open Auto-Buyer Settings', onClick: openSettingsPopup, color: 'green' }
+ ]);
+ console.log('Advanced Auto-Buyer: UI initialized and attached to HWH menu.');
+
+ // --- AUTO-EXECUTE ON SCRIPT LOAD ---
+ // Automatically run auto-buy when script loads (after menu is set up)
+ // Use setTimeout to ensure menu initialization completes first
+ setTimeout(() => {
+ runAutoBuy().catch(error => {
+ console.error('Advanced Auto-Buyer: Failed to auto-execute on load:', error);
+ });
+ }, 100);
+ }
+})();
\ No newline at end of file
diff --git a/Arena Training HwH Ext.user.js b/Arena Training HwH Ext.user.js
new file mode 100644
index 0000000..beeb32e
--- /dev/null
+++ b/Arena Training HwH Ext.user.js
@@ -0,0 +1,2343 @@
+// ==UserScript==
+// @name Arena Training HwH Ext
+// @namespace HeroWarsHelper.ArenaTraining
+// @version 1.21
+// @description Simulate arena hero combos with demo battles and record win rates (no attempts used)
+// @author AutoHero
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @grant none
+// @run-at document-end
+// @downloadURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/Arena%20Training%20HwH%20Ext.user.js
+// @updateURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/Arena%20Training%20HwH%20Ext.user.js
+// ==/UserScript==
+
+(function() {
+ 'use strict';
+
+ const EXTENSION_NAME = 'Arena Training Extension';
+ const EXTENSION_VERSION = '1.21';
+ const BRIDGE_URL = 'http://127.0.0.1:9876';
+ const EXTENSION_AUTHOR = 'AutoHero';
+ const AUTO_START_CHECKBOX = 'autoArenaTraining';
+ const AUTO_START_DELAY_MS = 60_000;
+ const LEGACY_SETTINGS_STORAGE_KEY = 'arenaTrainingSettings';
+
+ let autoStartTimer = null;
+
+ function registerI18n() {
+ if (!window.HWHData?.i18nLangData) {
+ return false;
+ }
+ Object.assign(window.HWHData.i18nLangData.en, {
+ AUTO_ARENA_TRAINING: 'Auto Arena Training',
+ AUTO_ARENA_TRAINING_TITLE: 'Auto-start arena training loop 1 minute after game load (demo battles, no attempts)',
+ });
+ Object.assign(window.HWHData.i18nLangData.ru, {
+ AUTO_ARENA_TRAINING: 'Авто-тренировка арены',
+ AUTO_ARENA_TRAINING_TITLE: 'Автозапуск цикла тренировки арены через 1 минуту после загрузки (демо-бои, без попыток)',
+ });
+ return true;
+ }
+
+ function createAutoStartCheckboxDefinition() {
+ return {
+ get label() {
+ return window.HWHFuncs?.I18N?.('AUTO_ARENA_TRAINING') || 'Auto Arena Training';
+ },
+ cbox: null,
+ get title() {
+ return window.HWHFuncs?.I18N?.('AUTO_ARENA_TRAINING_TITLE')
+ || 'Auto-start arena training loop 1 minute after game load (demo battles, no attempts)';
+ },
+ default: false,
+ };
+ }
+
+ function registerSettingsCheckbox() {
+ if (!window.HWHData?.checkboxes) {
+ return false;
+ }
+ registerI18n();
+ const { checkboxes } = window.HWHData;
+ if (checkboxes[AUTO_START_CHECKBOX]) {
+ return true;
+ }
+
+ const entry = createAutoStartCheckboxDefinition();
+ const reordered = {};
+ for (const name in checkboxes) {
+ reordered[name] = checkboxes[name];
+ if (name === 'sendExpedition') {
+ reordered[AUTO_START_CHECKBOX] = entry;
+ }
+ }
+ if (!reordered[AUTO_START_CHECKBOX]) {
+ reordered[AUTO_START_CHECKBOX] = entry;
+ }
+ for (const name of Object.keys(checkboxes)) {
+ delete checkboxes[name];
+ }
+ Object.assign(checkboxes, reordered);
+ return true;
+ }
+
+ const registerSettingsInterval = setInterval(() => {
+ if (registerSettingsCheckbox()) {
+ clearInterval(registerSettingsInterval);
+ }
+ }, 50);
+
+ function ensureSettingsCheckboxUI(HWHFuncs) {
+ registerSettingsCheckbox();
+ const checkboxDef = window.HWHData?.checkboxes?.[AUTO_START_CHECKBOX];
+ if (!checkboxDef || checkboxDef.cbox) {
+ return checkboxDef?.cbox || null;
+ }
+
+ const scriptMenu = window.HWHClasses?.ScriptMenu?.getInst?.();
+ const settingsDetails = document.querySelector('details.scriptMenu_Details[data-name="settings"]');
+ if (!scriptMenu || !settingsDetails) {
+ return null;
+ }
+
+ checkboxDef.cbox = scriptMenu.addCheckbox(checkboxDef.label, checkboxDef.title, settingsDetails);
+
+ const expeditionCheckbox = window.HWHData.checkboxes.sendExpedition?.cbox;
+ if (expeditionCheckbox && checkboxDef.cbox) {
+ const expeditionRow = expeditionCheckbox.closest('.scriptMenu_divInput');
+ const autoRow = checkboxDef.cbox.closest('.scriptMenu_divInput');
+ if (expeditionRow && autoRow && expeditionRow.nextSibling !== autoRow) {
+ expeditionRow.parentNode.insertBefore(autoRow, expeditionRow.nextSibling);
+ }
+ }
+
+ const savedValue = HWHFuncs.getSaveVal?.(AUTO_START_CHECKBOX, checkboxDef.default);
+ checkboxDef.cbox.checked = !!savedValue;
+ checkboxDef.cbox.dataset.name = AUTO_START_CHECKBOX;
+ checkboxDef.cbox.addEventListener('change', function onAutoStartToggle() {
+ HWHFuncs.setSaveVal?.(AUTO_START_CHECKBOX, this.checked);
+ });
+
+ return checkboxDef.cbox;
+ }
+
+ function clearAutoStartTimer() {
+ if (autoStartTimer != null) {
+ clearTimeout(autoStartTimer);
+ autoStartTimer = null;
+ }
+ }
+
+ function isAutoStartEnabled(HWHFuncs) {
+ const checkboxDef = window.HWHData?.checkboxes?.[AUTO_START_CHECKBOX];
+ if (checkboxDef?.cbox) {
+ return checkboxDef.cbox.checked;
+ }
+ return !!HWHFuncs?.getSaveVal?.(AUTO_START_CHECKBOX, false);
+ }
+
+ function scheduleAutoStart(training, HWHFuncs) {
+ clearAutoStartTimer();
+ if (!isAutoStartEnabled(HWHFuncs)) {
+ return;
+ }
+
+ console.log(`[Arena Training] Auto-start scheduled in ${Math.round(AUTO_START_DELAY_MS / 1000)}s`);
+ autoStartTimer = setTimeout(() => {
+ autoStartTimer = null;
+ if (!isAutoStartEnabled(HWHFuncs)) {
+ return;
+ }
+ const status = training.getStatus?.() || {};
+ const loopStatus = training.getLoopStatus?.() || {};
+ if (status.running || loopStatus.loopRunning) {
+ console.log('[Arena Training] Auto-start skipped — training already running');
+ return;
+ }
+ console.log('[Arena Training] Auto-starting training loop');
+ HWHFuncs.setProgress('Arena Training: auto-starting loop...', true);
+ training.startLoop({ label: 'auto-loop', opponentSource: 'topGet' });
+ }, AUTO_START_DELAY_MS);
+ }
+
+ function migrateLegacyAutoStartSetting(HWHFuncs) {
+ try {
+ const legacy = JSON.parse(localStorage.getItem(LEGACY_SETTINGS_STORAGE_KEY) || '{}');
+ if (!legacy.autoStartOnLoad || isAutoStartEnabled(HWHFuncs)) {
+ return;
+ }
+ HWHFuncs.setSaveVal?.(AUTO_START_CHECKBOX, true);
+ const checkbox = ensureSettingsCheckboxUI(HWHFuncs);
+ if (checkbox) {
+ checkbox.checked = true;
+ }
+ localStorage.removeItem(LEGACY_SETTINGS_STORAGE_KEY);
+ console.log('[Arena Training] Migrated legacy auto-start setting to HWH settings');
+ } catch (error) {
+ console.warn('[Arena Training] Legacy settings migration failed:', error);
+ }
+ }
+
+ function bindAutoStartCheckbox(training, HWHFuncs) {
+ const checkbox = ensureSettingsCheckboxUI(HWHFuncs);
+ if (!checkbox || checkbox.dataset.arenaTrainingBound === '1') {
+ return;
+ }
+ checkbox.dataset.arenaTrainingBound = '1';
+ checkbox.addEventListener('change', () => {
+ scheduleAutoStart(training, HWHFuncs);
+ });
+ }
+
+ const CONSTANTS = {
+ BATTLE_VERSION: 273,
+ DEFAULT_PET_ID: 6005,
+ DEFAULT_SIMULATIONS: 10,
+ DEFAULT_MAX_COMBOS: 40,
+ DEFAULT_POOL_SIZE: 12,
+ DEFAULT_TARGET_WIN_RATE: 90,
+ DEFAULT_USER_TEAM_TARGET_WIN_RATE: 70,
+ DEFAULT_SKIP_CACHE_MIN_WIN_RATE: 90,
+ DEFAULT_SKIP_CACHE_MAX_AGE_DAYS: 30,
+ DEFAULT_META_TEAMS_LIMIT: 0,
+ };
+
+ const waitForHWH = setInterval(() => {
+ if (window.HWHClasses?.ScriptMenu && window.Send && window.cheats?.BattleCalc && window.HWHFuncs) {
+ const scriptMenu = window.HWHClasses.ScriptMenu.getInst();
+ if (scriptMenu?.mainMenu) {
+ clearInterval(waitForHWH);
+ initializeExtension();
+ }
+ }
+ }, 200);
+
+ function initializeExtension() {
+ const { HWHClasses, HWHFuncs, Send, cheats, lib } = window;
+ HWHFuncs.addExtentionName(EXTENSION_NAME, EXTENSION_VERSION, EXTENSION_AUTHOR);
+
+ const training = createArenaTraining({ Send, cheats, lib, HWHFuncs });
+ window.ArenaTraining = training;
+
+ if (window.LLMHWH) {
+ window.LLMHWH.arenaTrainingRun = (options) => training.run(options);
+ window.LLMHWH.arenaTrainingStartLoop = (options) => training.startLoop(options);
+ window.LLMHWH.arenaTrainingStopLoop = () => training.stopLoop();
+ window.LLMHWH.arenaTrainingGetLoopHistory = () => training.getLoopHistory();
+ window.LLMHWH.arenaTrainingGetOpponents = (forceRefresh, options) => training.getOpponents(forceRefresh, options);
+ window.LLMHWH.arenaTrainingGetResults = () => training.getResults();
+ window.LLMHWH.arenaTrainingExportResults = () => training.exportResults();
+ window.LLMHWH.arenaTrainingGetStatus = () => training.getStatus();
+ window.LLMHWH.arenaTrainingStop = () => training.stop();
+ }
+
+ HWHClasses.ScriptMenu.getInst().addButton({
+ name: 'Arena Train',
+ title: 'Loop arena top-list training — auto-saves results (demo battles, no attempts)',
+ onClick: () => training.startLoop({ label: 'menu-loop', opponentSource: 'topGet' }),
+ color: 'purple',
+ });
+
+ migrateLegacyAutoStartSetting(HWHFuncs);
+ bindAutoStartCheckbox(training, HWHFuncs);
+ scheduleAutoStart(training, HWHFuncs);
+
+ console.log(`${EXTENSION_NAME} v${EXTENSION_VERSION} ready`);
+ }
+
+ function createArenaTraining({ Send, cheats, lib, HWHFuncs }) {
+ const BattleCalc = cheats.BattleCalc;
+ let running = false;
+ let loopRunning = false;
+ let stopRequested = false;
+ let status = { running: false, loopRunning: false };
+ let lastResults = null;
+ let loopSession = null;
+ let opponentsCache = null;
+ let opponentsMeta = { myPlace: null, serverId: null };
+
+ function sleep(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+
+ const FATAL_ERROR_PATTERNS = [
+ /InvalidSession/i,
+ /Invalid session/i,
+ ];
+
+ function isFatalTrainingError(error) {
+ const message = String(error?.message || error || '');
+ return FATAL_ERROR_PATTERNS.some((pattern) => pattern.test(message));
+ }
+
+ function throwIfSendError(response, label = 'API call') {
+ if (response?.error) {
+ throw new Error(`${response.error.name}: ${response.error.description}`);
+ }
+ for (const item of response?.results || []) {
+ if (item?.error) {
+ throw new Error(`${item.error.name}: ${item.error.description}`);
+ }
+ }
+ }
+
+ function stopTrainingOnFatalError(error, context = '') {
+ if (!isFatalTrainingError(error)) {
+ return false;
+ }
+ stopRequested = true;
+ loopRunning = false;
+ const prefix = context ? `${context}: ` : '';
+ status.message = `${prefix}${error.message}`;
+ if (loopSession) {
+ loopSession.fatalError = error.message;
+ loopSession.stoppedAt = new Date().toISOString();
+ }
+ HWHFuncs.setProgress(`Arena Training stopped — ${error.message}`, true);
+ console.error(`[Arena Training] Fatal error, stopping loop${context ? ` (${context})` : ''}:`, error);
+ return true;
+ }
+
+ async function fetchMetaTeamCandidates(options = {}) {
+ const params = new URLSearchParams();
+ if (options.metaTeamsSnapshotId) {
+ params.set('snapshotId', String(options.metaTeamsSnapshotId));
+ }
+ const limit = Number(options.metaTeamsLimit ?? CONSTANTS.DEFAULT_META_TEAMS_LIMIT);
+ if (limit > 0) {
+ params.set('limit', String(limit));
+ }
+
+ try {
+ const res = await fetch(`${BRIDGE_URL}/training/meta-candidates?${params}`);
+ if (!res.ok) {
+ console.warn('[Arena Training] Bridge meta-candidates failed:', res.status);
+ return { snapshotId: null, candidates: [] };
+ }
+ const data = await res.json();
+ return data.ok
+ ? { snapshotId: data.snapshotId, candidates: data.candidates || [] }
+ : { snapshotId: null, candidates: [] };
+ } catch (e) {
+ console.warn('[Arena Training] Bridge meta-candidates error:', e.message);
+ return { snapshotId: null, candidates: [] };
+ }
+ }
+
+ function buildMetaTeamCandidates(metaTeams, data, options, defaultBanner) {
+ if (!metaTeams?.length) return [];
+
+ const arenaFavor = data.favor?.arena || {};
+ const ownedHeroIds = new Set(
+ (data.heroes || [])
+ .filter((h) => h?.id && h.id < 6000)
+ .map((h) => Number(h.id))
+ );
+ const ownedPetIds = new Set(
+ (data.heroes || [])
+ .filter((h) => h?.id >= 6000 && h.id < 7000)
+ .map((h) => Number(h.id))
+ );
+ const arenaPet = data.teams?.arena?.[5];
+ const fallbackPet = arenaPet && ownedPetIds.has(Number(arenaPet))
+ ? Number(arenaPet)
+ : CONSTANTS.DEFAULT_PET_ID;
+
+ const candidates = [];
+ for (const team of metaTeams) {
+ const heroes = (team.heroIds || team.hero_ids || []).map(Number).filter((id) => id > 0 && id < 6000);
+ if (heroes.length !== 5) continue;
+ if (!heroes.every((id) => ownedHeroIds.has(id))) continue;
+
+ let pet = team.pet != null ? Number(team.pet) : null;
+ if (pet && !ownedPetIds.has(pet)) {
+ pet = fallbackPet;
+ }
+ if (!pet) {
+ pet = fallbackPet;
+ }
+
+ const banner = team.banner != null ? Number(team.banner) : (defaultBanner || 1);
+ candidates.push({
+ heroes,
+ pet,
+ banner,
+ favor: pickFavor(heroes, arenaFavor),
+ source: 'meta-team',
+ metaPopularity: team.popularityCount ?? team.popularity_count ?? null,
+ metaRank: team.rowRank ?? team.row_rank ?? null,
+ metaComboKey: team.comboKey ?? team.combo_key ?? buildComboKey(heroes, pet, banner),
+ });
+ }
+
+ return candidates;
+ }
+
+ function buildMetaTeamOpponents(metaTeams) {
+ if (!metaTeams?.length) return [];
+
+ const opponents = [];
+ for (let index = 0; index < metaTeams.length; index++) {
+ const team = metaTeams[index];
+ const heroes = (team.heroIds || team.hero_ids || [])
+ .map(Number)
+ .filter((id) => id > 0 && id < 6000);
+ if (heroes.length !== 5) continue;
+
+ const pet = team.pet != null ? Number(team.pet) : CONSTANTS.DEFAULT_PET_ID;
+ const banner = team.banner != null ? Number(team.banner) : 1;
+ const comboKey = team.comboKey ?? team.combo_key ?? buildComboKey(heroes, pet, banner);
+ const heroNames = (team.heroNames || team.hero_names || heroes.map(heroName));
+ const popularity = team.popularityCount ?? team.popularity_count ?? null;
+ const rank = team.rowRank ?? team.row_rank ?? (index + 1);
+ const label = heroNames.length
+ ? `Meta: ${heroNames.join(', ')}`
+ : `Meta team #${rank}`;
+
+ const raw = {
+ userId: `meta_${comboKey.replace(/\|/g, '_')}`,
+ place: String(rank),
+ power: popularity,
+ heroes: [...heroes.map((id) => ({ id })), { id: pet, type: 'pet' }],
+ banners: [{ id: banner }],
+ user: { name: label },
+ source: 'meta-opponent',
+ metaComboKey: comboKey,
+ metaPopularity: popularity,
+ metaRank: rank,
+ };
+
+ opponents.push({
+ index: opponents.length,
+ userId: raw.userId,
+ name: label,
+ place: raw.place,
+ power: raw.power,
+ heroes,
+ heroNames,
+ pet,
+ banner,
+ source: raw.source,
+ metaComboKey: comboKey,
+ metaPopularity: popularity,
+ metaRank: rank,
+ raw,
+ });
+ }
+
+ return opponents;
+ }
+
+ async function fetchMetaTeamOpponents(options = {}) {
+ const meta = await fetchMetaTeamCandidates(options);
+ const opponents = buildMetaTeamOpponents(meta.candidates);
+ return {
+ snapshotId: meta.snapshotId,
+ opponents,
+ };
+ }
+
+ async function saveRoundToBridge(result) {
+ try {
+ const res = await fetch(`${BRIDGE_URL}/training/save`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(result),
+ });
+ if (!res.ok) {
+ console.warn('[Arena Training] Bridge save failed:', res.status);
+ return false;
+ }
+ return true;
+ } catch (e) {
+ console.warn('[Arena Training] Bridge save error:', e.message);
+ return false;
+ }
+ }
+
+ function buildComboKey(heroIds, pet, banner = 0) {
+ const heroes = (Array.isArray(heroIds) ? heroIds : [])
+ .map(Number)
+ .filter((id) => id > 0 && id < 6000)
+ .slice(0, 5);
+ return `${heroes.join(',')}|${Number(pet) || 0}|${Number(banner) || 0}`;
+ }
+
+ function resolveOpponentComboContext(opponentRaw, opponentMeta = {}) {
+ const team = extractOpponentConfig(opponentRaw || opponentMeta.raw || opponentMeta);
+ const heroes = team.hasValidTeam
+ ? team.heroes
+ : (Array.isArray(opponentMeta.heroes) ? opponentMeta.heroes : []).slice(0, 5);
+ const pet = team.pet ?? opponentMeta.pet;
+ const banner = team.banner ?? opponentMeta.banner;
+ const comboKey = heroes.length === 5
+ ? buildComboKey(heroes, pet, banner)
+ : null;
+ return {
+ heroes,
+ pet,
+ banner,
+ comboKey,
+ hasValidTeam: team.hasValidTeam || heroes.length === 5,
+ };
+ }
+
+ async function fetchUserCounterSkipCheck(comboKey, counterLineup, testerUserId, options = {}) {
+ const maxAgeDays = Number(options.skipCacheMaxAgeDays ?? CONSTANTS.DEFAULT_SKIP_CACHE_MAX_AGE_DAYS);
+ const params = new URLSearchParams({
+ comboKey,
+ mode: 'user',
+ testerUserId: String(testerUserId),
+ myPet: String(counterLineup?.pet ?? ''),
+ maxAgeDays: String(maxAgeDays),
+ });
+ for (const heroId of counterLineup?.heroes || []) {
+ params.append('myHero', String(heroId));
+ }
+
+ try {
+ const res = await fetch(`${BRIDGE_URL}/training/skip-check?${params}`);
+ if (!res.ok) {
+ console.warn('[Arena Training] Bridge user skip-check failed:', res.status);
+ return { shouldSkip: false, reason: 'bridge_error' };
+ }
+ const data = await res.json();
+ return data.ok ? data : { shouldSkip: false, reason: 'invalid_response' };
+ } catch (e) {
+ console.warn('[Arena Training] Bridge user skip-check error:', e.message);
+ return { shouldSkip: false, reason: 'bridge_unreachable' };
+ }
+ }
+
+ async function resolveCurrentTesterUserId() {
+ try {
+ const response = await Send({
+ calls: [{ name: 'userGetInfo', args: {}, ident: 'userGetInfo' }],
+ });
+ const userInfo = response.results?.find((r) => r.ident === 'userGetInfo')?.result?.response || {};
+ const userId = userInfo?.userId ?? userInfo?.id ?? null;
+ return userId != null ? String(userId) : null;
+ } catch (e) {
+ console.warn('[Arena Training] Could not resolve tester user id:', e.message);
+ return null;
+ }
+ }
+
+ async function fetchOpponentSkipCheck(comboKey, options = {}) {
+ const minWinRate = Number(options.skipCacheMinWinRate ?? CONSTANTS.DEFAULT_SKIP_CACHE_MIN_WIN_RATE);
+ const maxAgeDays = Number(options.skipCacheMaxAgeDays ?? CONSTANTS.DEFAULT_SKIP_CACHE_MAX_AGE_DAYS);
+ const params = new URLSearchParams({
+ comboKey,
+ minWinRate: String(minWinRate),
+ maxAgeDays: String(maxAgeDays),
+ });
+ const opponentHeroIds = options.opponentHeroIds || options.opponentHeroes;
+ if (Array.isArray(opponentHeroIds)) {
+ for (const heroId of opponentHeroIds) {
+ params.append('opponentHero', String(heroId));
+ }
+ }
+
+ try {
+ const res = await fetch(`${BRIDGE_URL}/training/skip-check?${params}`);
+ if (!res.ok) {
+ console.warn('[Arena Training] Bridge skip-check failed:', res.status);
+ return { shouldSkip: false, reason: 'bridge_error' };
+ }
+ const data = await res.json();
+ return data.ok ? data : { shouldSkip: false, reason: 'invalid_response' };
+ } catch (e) {
+ console.warn('[Arena Training] Bridge skip-check error:', e.message);
+ return { shouldSkip: false, reason: 'bridge_unreachable' };
+ }
+ }
+
+ function getActionTs() {
+ return Date.now();
+ }
+
+ function getBattleType(strBattleType) {
+ if (!strBattleType) return 'get_pvp';
+ if (strBattleType === 'arena' || strBattleType === 'pvp' || strBattleType === 'grand') {
+ return 'get_pvp';
+ }
+ return 'get_pvp';
+ }
+
+ function isValidBattleResult(result) {
+ return result && result.result && typeof result.result.win === 'boolean';
+ }
+
+ function parseHeroes(raw) {
+ if (!raw) return [];
+ return Array.isArray(raw) ? raw : Object.values(raw);
+ }
+
+ function heroPower(hero) {
+ return Number(hero?.power || hero?.sumPower || 0);
+ }
+
+ function heroName(heroId) {
+ const id = Number(heroId);
+ if (!Number.isFinite(id)) return String(heroId);
+
+ try {
+ const key = id >= 6000 && id < 7000
+ ? `LIB_PET_NAME_${id}`
+ : `LIB_HERO_NAME_${id}`;
+ const translated = cheats?.translate?.(key);
+ if (translated && translated !== key) return translated;
+
+ const data = lib?.getData?.('hero');
+ const hero = data?.[id] || data?.[String(id)];
+ if (hero?.name || hero?.caption) return hero.name || hero.caption;
+ } catch {
+ // fall through to numeric fallback
+ }
+
+ return id >= 6000 && id < 7000 ? `Pet ${id}` : `Hero ${id}`;
+ }
+
+ function combinations(items, size, maxCount = Infinity) {
+ if (!Array.isArray(items) || items.length < size) return [];
+ const limit = maxCount == null || !Number.isFinite(maxCount) ? Infinity : maxCount;
+ const result = [];
+ const combo = [];
+ function backtrack(start) {
+ if (result.length >= limit) return;
+ if (combo.length === size) {
+ result.push([...combo]);
+ return;
+ }
+ for (let i = start; i < items.length; i++) {
+ combo.push(items[i]);
+ backtrack(i + 1);
+ combo.pop();
+ }
+ }
+ backtrack(0);
+ return result;
+ }
+
+ function pickFavor(heroIds, arenaFavor = {}) {
+ const favor = {};
+ for (const heroId of heroIds) {
+ const key = String(heroId);
+ if (arenaFavor[key] != null) {
+ favor[key] = arenaFavor[key];
+ } else if (arenaFavor[heroId] != null) {
+ favor[key] = arenaFavor[heroId];
+ }
+ }
+ return favor;
+ }
+
+ function resolveTesterInfo(userInfo, maxUpgrade = true) {
+ if (maxUpgrade !== false) {
+ return { userId: '0', name: 'maxHeros', maxUpgrade: true };
+ }
+ const userId = userInfo?.userId ?? userInfo?.id ?? null;
+ const name = userInfo?.name ?? userInfo?.nickname ?? null;
+ return {
+ userId: userId != null ? String(userId) : null,
+ name: name != null ? String(name) : null,
+ maxUpgrade: false,
+ };
+ }
+
+ function counterLineupFromBest(best, arenaFavor = {}) {
+ const heroes = (best?.heroes || []).map(Number);
+ return {
+ heroes,
+ pet: Number(best?.pet) || CONSTANTS.DEFAULT_PET_ID,
+ banner: best?.banner,
+ favor: best?.favor || pickFavor(heroes, arenaFavor),
+ source: best?.source || 'max-counter',
+ heroNames: best?.heroNames,
+ };
+ }
+
+ function counterLineupFromCache(cachedMatch, arenaFavor = {}, defaultBanner = 1) {
+ const heroes = (cachedMatch?.myHeroIds || []).map(Number).filter((id) => id > 0 && id < 6000);
+ const pet = Number(cachedMatch?.myPet) || CONSTANTS.DEFAULT_PET_ID;
+ return {
+ heroes,
+ pet,
+ banner: defaultBanner,
+ favor: pickFavor(heroes, arenaFavor),
+ source: 'cached-counter',
+ heroNames: cachedMatch?.myHeroNames,
+ cachedWinRate: cachedMatch?.winRate != null ? Number(cachedMatch.winRate) : null,
+ };
+ }
+
+ function normalizeExcludeKeys(keys = []) {
+ return new Set(
+ keys.map((entry) => (typeof entry === 'string' ? entry : candidateKey(entry)))
+ );
+ }
+
+ function buildRatingStashEvents(meta = {}) {
+ const actionTs = meta.actionTs || getActionTs();
+ const timestamp = meta.timestamp || Math.floor(Date.now() / 1000);
+ const sessionNumber = meta.sessionNumber || 1;
+ const windowCounter = meta.windowCounter || 12;
+ const baseParams = {
+ sessionNumber,
+ assetsReloadNum: 0,
+ assetsType: 'web',
+ assetsLoadingPercent: 0,
+ assetsLoadingTime: 0,
+ };
+ return [
+ {
+ type: '.client.window.close',
+ params: {
+ ...baseParams,
+ actionTs,
+ windowName: 'rating',
+ prevWindowName: 'global',
+ timestamp,
+ windowCounter,
+ },
+ },
+ {
+ type: '.client.button.click',
+ params: {
+ ...baseParams,
+ actionTs: actionTs + 100,
+ windowName: 'rating',
+ buttonName: 'rating_tab:0',
+ timestamp,
+ windowCounter: 0,
+ assetsType: 'cache',
+ assetsLoadingTime: 0,
+ },
+ },
+ {
+ type: '.client.window.open',
+ params: {
+ ...baseParams,
+ actionTs: actionTs + 103,
+ windowName: 'rating',
+ prevWindowName: 'global',
+ timestamp,
+ windowCounter: windowCounter + 1,
+ assetsLoadingTime: 17,
+ },
+ },
+ ];
+ }
+
+ function parseTopGetArenaEntry(entry, users = {}, index = 0) {
+ if (!entry || typeof entry !== 'object') return null;
+
+ const userId = entry.userId ?? entry.id ?? entry.uid ?? entry.user_id;
+ const user = entry.user || users[String(userId)] || users[userId] || {};
+ const heroesRaw = entry.heroes
+ || entry.heroIds
+ || entry.team?.units
+ || entry.team?.heroes
+ || entry.defenceTeam?.units
+ || entry.defence?.units
+ || [];
+
+ const heroes = [];
+ const heroItems = Array.isArray(heroesRaw) ? heroesRaw : Object.values(heroesRaw || {});
+ for (const item of heroItems) {
+ if (typeof item === 'number') {
+ heroes.push({ id: item });
+ } else if (item?.id != null) {
+ heroes.push(item);
+ }
+ }
+
+ const petId = entry.pet ?? entry.team?.pet ?? entry.defenceTeam?.pet;
+ if (petId != null && !heroes.some((hero) => (hero?.id || hero) >= 6000)) {
+ heroes.push({ id: Number(petId), type: 'pet' });
+ }
+
+ if (!heroes.length && !userId) return null;
+
+ const banners = entry.banners
+ || (entry.banner != null ? [{ id: entry.banner }] : [])
+ || (entry.defenceBanner != null ? [{ id: entry.defenceBanner }] : []);
+
+ return {
+ userId: userId != null ? String(userId) : `top_${index}`,
+ place: entry.place ?? entry.rank ?? entry.position ?? String(index + 1),
+ power: entry.power ?? entry.teamPower ?? entry.score ?? entry.value,
+ heroes,
+ banners,
+ user: { name: user.name || entry.name || entry.nickname || `Top ${index + 1}` },
+ source: 'topGet',
+ };
+ }
+
+ function normalizeArenaTopResponse(response, users = {}) {
+ if (!response) return [];
+
+ const userMap = users && typeof users === 'object' ? users : {};
+ let entries = [];
+
+ if (Array.isArray(response.top)) {
+ entries = response.top;
+ } else if (Array.isArray(response)) {
+ entries = response;
+ } else if (Array.isArray(response.list)) {
+ entries = response.list;
+ } else if (Array.isArray(response.rating)) {
+ entries = response.rating;
+ } else if (Array.isArray(response.data)) {
+ entries = response.data;
+ } else if (Array.isArray(response.entries)) {
+ entries = response.entries;
+ } else if (typeof response === 'object') {
+ entries = Object.entries(response)
+ .filter(([key]) => key !== 'users' && key !== 'place')
+ .map(([, value]) => value)
+ .filter((value) => (
+ value
+ && typeof value === 'object'
+ && !Array.isArray(value)
+ && (value.heroes || value.heroIds || value.team || value.userId || value.id)
+ ));
+ }
+
+ return entries
+ .map((entry, index) => parseTopGetArenaEntry(entry, userMap, index))
+ .filter((entry) => entry && extractOpponentConfig(entry).hasValidTeam);
+ }
+
+ function extractTopGetResult(response) {
+ return response?.results?.find((r) => (
+ r.ident === 'group_1_body' || r.ident === 'topGet'
+ ))?.result?.response;
+ }
+
+ async function resolveServerId(options = {}) {
+ if (options.serverId != null) return Number(options.serverId);
+ const response = await Send({
+ calls: [{ name: 'userGetInfo', args: {}, ident: 'userGetInfo' }],
+ });
+ const userInfo = response.results?.find((r) => r.ident === 'userGetInfo')?.result?.response || {};
+ return Number(userInfo.serverId || userInfo.server || 0) || null;
+ }
+
+ async function fetchArenaTopOpponents(options = {}) {
+ const actionTs = getActionTs();
+ const serverId = await resolveServerId(options);
+ if (!serverId) {
+ throw new Error('Could not resolve serverId for topGet arena');
+ }
+
+ const calls = [];
+ if (options.skipStashClient !== true) {
+ calls.push({
+ name: 'stashClient',
+ args: { data: buildRatingStashEvents({ ...options.stashMeta, actionTs }) },
+ context: { actionTs },
+ ident: 'group_0_body',
+ });
+ }
+
+ calls.push({
+ name: 'topGet',
+ args: {
+ type: 'arena',
+ extraId: options.extraId ?? 0,
+ serverId,
+ },
+ context: { actionTs: actionTs + 1500 },
+ ident: 'group_1_body',
+ });
+
+ const response = await Send({ calls });
+ if (response?.error) {
+ throw new Error(`${response.error.name}: ${response.error.description}`);
+ }
+
+ const topGetResult = extractTopGetResult(response);
+ const users = topGetResult?.users || {};
+ let opponents = normalizeArenaTopResponse(topGetResult, users);
+ if (options.opponentLimit > 0) {
+ opponents = opponents.slice(0, options.opponentLimit);
+ }
+ if (!opponents.length) {
+ throw new Error('topGet arena returned no opponent teams');
+ }
+
+ opponentsMeta = {
+ myPlace: topGetResult?.place || null,
+ serverId,
+ count: opponents.length,
+ };
+
+ return opponents;
+ }
+
+ async function fetchArenaFindEnemies() {
+ const response = await Send({
+ calls: [{ name: 'arenaFindEnemies', args: {}, ident: 'arenaFindEnemies' }],
+ });
+ return response.results?.find((r) => r.ident === 'arenaFindEnemies')?.result?.response || [];
+ }
+
+ async function fetchOpponents(options = {}) {
+ const source = options.opponentSource || 'topGet';
+ if (source === 'arenaFindEnemies') {
+ return fetchArenaFindEnemies();
+ }
+ return fetchArenaTopOpponents(options);
+ }
+
+ async function loadTrainingBaseData() {
+ const response = await Send({
+ calls: [
+ { name: 'teamGetAll', args: {}, ident: 'teamGetAll' },
+ { name: 'teamGetFavor', args: {}, ident: 'teamGetFavor' },
+ { name: 'heroGetAll', args: {}, ident: 'heroGetAll' },
+ { name: 'userGetInfo', args: {}, ident: 'userGetInfo' },
+ ],
+ });
+
+ const get = (ident) => response.results?.find((r) => r.ident === ident)?.result?.response;
+ return {
+ teams: get('teamGetAll') || {},
+ favor: get('teamGetFavor') || {},
+ heroes: parseHeroes(get('heroGetAll')),
+ userInfo: get('userGetInfo') || {},
+ };
+ }
+
+ async function loadGameData(options = {}) {
+ if (options.opponentOverride) {
+ const base = await loadTrainingBaseData();
+ return { ...base, opponents: [options.opponentOverride] };
+ }
+
+ const [base, opponents] = await Promise.all([
+ loadTrainingBaseData(),
+ fetchOpponents(options),
+ ]);
+ return { ...base, opponents };
+ }
+
+ function extractOpponentConfig(opponent) {
+ const heroes = [];
+ let pet = CONSTANTS.DEFAULT_PET_ID;
+ let banner = 1;
+
+ for (const item of opponent?.heroes || []) {
+ const id = typeof item === 'number' ? item : item?.id;
+ if (!id) continue;
+ if (id >= 6000 && id < 7000) {
+ pet = id;
+ } else if (id < 6000 && heroes.length < 5) {
+ heroes.push(id);
+ }
+ }
+
+ if (opponent?.pet != null && opponent.pet >= 6000) {
+ pet = Number(opponent.pet);
+ }
+
+ if (opponent?.banner != null) {
+ banner = Number(opponent.banner);
+ } else if (opponent?.banners?.[0]) {
+ const b = opponent.banners[0];
+ banner = typeof b === 'number' ? b : (b?.id || 1);
+ }
+
+ return {
+ hasValidTeam: heroes.length === 5,
+ heroes,
+ pet,
+ banner,
+ favor: {},
+ };
+ }
+
+ function describeOpponentTeam(opponent) {
+ const team = extractOpponentConfig(opponent || {});
+ return {
+ userId: opponent?.userId ?? null,
+ name: opponent?.user?.name || opponent?.name || null,
+ place: opponent?.place ?? null,
+ source: opponent?.source ?? null,
+ heroCount: team.heroes.length,
+ heroes: team.heroes,
+ pet: team.pet,
+ banner: team.banner,
+ };
+ }
+
+ function recordInvalidOpponentRound(loopSession, roundNum, opponentMeta, opponentRaw) {
+ const teamInfo = describeOpponentTeam(opponentRaw || opponentMeta);
+ console.warn(
+ `[Arena Training] Round ${roundNum} skipped — incomplete opponent team (${teamInfo.heroCount}/5 heroes)`,
+ teamInfo
+ );
+ loopSession.rounds.push({
+ round: roundNum,
+ skipped: true,
+ skipReason: 'invalid_opponent_team',
+ opponent: {
+ index: opponentMeta?.index,
+ userId: teamInfo.userId,
+ name: teamInfo.name,
+ place: teamInfo.place,
+ source: teamInfo.source,
+ metaComboKey: opponentMeta?.metaComboKey,
+ },
+ heroCount: teamInfo.heroCount,
+ heroes: teamInfo.heroes,
+ completedAt: new Date().toISOString(),
+ });
+ }
+
+ function buildMyTeamConfig(heroIds, pet, banner, arenaFavor) {
+ return {
+ heroes: heroIds,
+ pet,
+ banners: [banner],
+ favor: pickFavor(heroIds, arenaFavor),
+ };
+ }
+
+ async function endDemoBattle(calcResult, battleData) {
+ const progress = calcResult.progress?.length
+ ? calcResult.progress
+ : [{
+ v: CONSTANTS.BATTLE_VERSION,
+ b: 0,
+ seed: battleData?.seed || Math.floor(Math.random() * 1e9),
+ attackers: { input: [], heroes: {} },
+ defenders: { input: [], heroes: {} },
+ }];
+
+ const response = await Send({
+ calls: [{
+ name: 'demoBattles_endBattle',
+ args: {
+ result: {
+ win: !!calcResult.result?.win,
+ stars: calcResult.result?.stars || 0,
+ },
+ progress,
+ },
+ context: { actionTs: getActionTs() },
+ ident: 'body',
+ }],
+ });
+
+ throwIfSendError(response, 'demoBattles_endBattle');
+
+ const battle = response?.results?.[0]?.result?.response?.battle;
+ return {
+ parentId: battle?.parentId,
+ battleId: battle?.id,
+ };
+ }
+
+ async function runSingleDemoBattle(myTeam, opponentTeam, parentId = 0, battleOptions = {}) {
+ const maxUpgrade = battleOptions.maxUpgrade !== false;
+ const args = {
+ mechanic: 'arena',
+ defenceMaxUpgrade: true,
+ maxUpgrade,
+ defenceBuffs: {},
+ buffs: {},
+ parentId,
+ entryId: 0,
+ defenceTeam: {
+ units: opponentTeam.heroes,
+ pet: opponentTeam.pet || CONSTANTS.DEFAULT_PET_ID,
+ },
+ defenceBanner: opponentTeam.banner || 1,
+ defenceBannerStones: {},
+ defenceFavor: opponentTeam.favor || {},
+ team: {
+ units: myTeam.heroes,
+ pet: myTeam.pet || CONSTANTS.DEFAULT_PET_ID,
+ },
+ banner: myTeam.banners?.[0] || 1,
+ bannerStones: {},
+ favor: myTeam.favor || {},
+ };
+
+ const startResponse = await Send({
+ calls: [{
+ name: 'demoBattles_startBattle',
+ args,
+ context: { actionTs: getActionTs() },
+ ident: 'body',
+ }],
+ });
+
+ if (startResponse?.error) {
+ throw new Error(`${startResponse.error.name}: ${startResponse.error.description}`);
+ }
+ throwIfSendError(startResponse, 'demoBattles_startBattle');
+
+ const responseData = startResponse.results?.[0]?.result?.response;
+ const battleData = responseData?.battle || responseData;
+ if (!battleData) {
+ throw new Error('No battle data in demoBattles_startBattle response');
+ }
+
+ const calcResult = await new Promise((resolve) => {
+ const battleType = battleData?.effects?.battleConfig ?? battleData?.type ?? 'arena';
+ BattleCalc(battleData, getBattleType(battleType), (result) => resolve(result));
+ });
+
+ if (!isValidBattleResult(calcResult)) {
+ return { win: false, battleTime: 0, parentId };
+ }
+
+ const endInfo = await endDemoBattle(calcResult, battleData);
+ const nextParentId = parentId === 0
+ ? (endInfo.battleId || endInfo.parentId || 0)
+ : parentId;
+
+ return {
+ win: !!calcResult.result.win,
+ battleTime: calcResult.battleTime || 0,
+ parentId: nextParentId,
+ };
+ }
+
+ async function simulateTeam(myTeam, opponentTeam, simulationCount, battleOptions = {}) {
+ const simulations = [];
+ let parentId = 0;
+ let firstBattleId = null;
+
+ for (let i = 0; i < simulationCount; i++) {
+ if (stopRequested) break;
+ const result = await runSingleDemoBattle(
+ myTeam,
+ opponentTeam,
+ i === 0 ? 0 : (firstBattleId || parentId),
+ battleOptions
+ );
+ simulations.push(result);
+ if (i === 0 && result.parentId) {
+ firstBattleId = result.parentId;
+ parentId = result.parentId;
+ }
+ }
+
+ const wins = simulations.filter((s) => s.win).length;
+ const losses = simulations.length - wins;
+ const battleTimes = simulations.map((s) => s.battleTime).filter((t) => t > 0);
+ const averageBattleTime = battleTimes.length
+ ? battleTimes.reduce((a, b) => a + b, 0) / battleTimes.length
+ : 0;
+
+ return {
+ total: simulations.length,
+ wins,
+ losses,
+ winRate: simulations.length ? (wins / simulations.length) * 100 : 0,
+ averageBattleTime,
+ simulations,
+ };
+ }
+
+ function resolveBanner(userInfo, teams) {
+ if (userInfo?.banner) {
+ return Array.isArray(userInfo.banner) ? userInfo.banner[0] : userInfo.banner;
+ }
+ return 1;
+ }
+
+ function resolveGrandBanners(userInfo, fallbackBanner = 1) {
+ const banners = [];
+ if (Array.isArray(userInfo?.banners) && userInfo.banners.length) {
+ for (let i = 0; i < 3; i++) {
+ banners.push(Number(userInfo.banners[i] ?? userInfo.banners[0]));
+ }
+ return banners;
+ }
+ if (userInfo?.banner != null) {
+ const banner = Array.isArray(userInfo.banner) ? userInfo.banner[0] : userInfo.banner;
+ return [banner, banner, banner];
+ }
+ return [fallbackBanner, fallbackBanner + 1, fallbackBanner + 2].map((b) => b || 1);
+ }
+
+ function candidateKey(candidate) {
+ return `${candidate.heroes.join(',')}:${candidate.pet}`;
+ }
+
+ function dedupeCandidates(candidates, seen = new Set()) {
+ const unique = [];
+ for (const candidate of candidates) {
+ const key = candidateKey(candidate);
+ if (seen.has(key)) continue;
+ seen.add(key);
+ unique.push(candidate);
+ }
+ return unique;
+ }
+
+ function resolveHeroPoolSize(options = {}) {
+ if (options.topLimit > 0) return Number(options.topLimit);
+ if (options.heroPoolSize > 0) return Number(options.heroPoolSize);
+ return CONSTANTS.DEFAULT_POOL_SIZE;
+ }
+
+ function applyTrainingOptions(options = {}) {
+ const heroPoolSize = resolveHeroPoolSize(options);
+ return { ...options, heroPoolSize, topLimit: heroPoolSize };
+ }
+
+ function resolvePetPool(ownedPets, arenaTeam, options = {}) {
+ if (options.petPool?.length) {
+ return options.petPool.map(Number);
+ }
+
+ const exhaustive = options.searchUntilTarget !== false;
+ if (exhaustive) {
+ const pets = [...new Set([
+ arenaTeam[5],
+ ...ownedPets,
+ CONSTANTS.DEFAULT_PET_ID,
+ ].filter(Boolean))];
+ return pets.length ? pets : [CONSTANTS.DEFAULT_PET_ID];
+ }
+
+ return [...new Set([
+ arenaTeam[5],
+ ...ownedPets.slice(0, 3),
+ CONSTANTS.DEFAULT_PET_ID,
+ ].filter(Boolean))];
+ }
+
+ function buildTrainingPools(data, options) {
+ const arenaTeam = data.teams?.arena || [];
+ const arenaFavor = data.favor?.arena || {};
+ const grandFavor = data.favor?.grand || arenaFavor;
+ const ownedHeroes = data.heroes
+ .filter((h) => h?.id && h.id < 6000)
+ .sort((a, b) => heroPower(b) - heroPower(a));
+
+ const ownedPets = data.heroes
+ .filter((h) => h?.id >= 6000 && h.id < 7000)
+ .sort((a, b) => heroPower(b) - heroPower(a))
+ .map((h) => h.id);
+
+ const poolSize = resolveHeroPoolSize(options);
+ const heroPool = (options.heroPool?.length
+ ? options.heroPool.map(Number)
+ : ownedHeroes.slice(0, poolSize).map((h) => h.id));
+
+ const petPool = resolvePetPool(ownedPets, arenaTeam, options);
+ const banner = options.banner ?? resolveBanner(data.userInfo, data.teams);
+ const grandBanners = resolveGrandBanners(data.userInfo, banner);
+
+ const priorityCandidates = [
+ ...buildArenaCandidates(arenaTeam, arenaFavor, banner, options),
+ ...buildGrandArenaCandidates(data.teams?.grand || [], grandFavor, grandBanners, banner, options),
+ ];
+
+ return {
+ arenaTeam,
+ arenaFavor,
+ heroPool,
+ petPool,
+ banner,
+ priorityCandidates,
+ };
+ }
+
+ function buildArenaCandidates(arenaTeam, arenaFavor, banner, options = {}) {
+ if (options.includeCurrentTeam === false || arenaTeam.length < 6) {
+ return [];
+ }
+
+ const heroes = arenaTeam.slice(0, 5).map(Number);
+ return [{
+ heroes,
+ pet: Number(arenaTeam[5]),
+ banner,
+ favor: pickFavor(heroes, arenaFavor),
+ source: 'arena-team',
+ }];
+ }
+
+ function buildGrandArenaCandidates(grandTeams, grandFavor, grandBanners, banner, options = {}) {
+ if (options.includeGrandArenaTeams === false || !Array.isArray(grandTeams)) {
+ return [];
+ }
+
+ const candidates = [];
+ grandTeams.forEach((team, index) => {
+ if (!Array.isArray(team) || team.length < 6) return;
+ const heroes = team.slice(0, 5).map(Number);
+ if (heroes.some((id) => !id)) return;
+ candidates.push({
+ heroes,
+ pet: Number(team[5]),
+ banner: grandBanners[index] ?? banner,
+ favor: pickFavor(heroes, grandFavor),
+ source: `grand-arena-team-${index + 1}`,
+ });
+ });
+ return candidates;
+ }
+
+ function buildGeneratedCandidates(pools, { maxHeroCombos = Infinity, excludeKeys = new Set() } = {}) {
+ const { heroPool, petPool, banner, arenaFavor } = pools;
+ const heroCombos = combinations(heroPool, 5, maxHeroCombos);
+ const generated = [];
+
+ for (const heroIds of heroCombos) {
+ for (const pet of petPool) {
+ const candidate = {
+ heroes: heroIds,
+ pet,
+ banner,
+ favor: pickFavor(heroIds, arenaFavor),
+ source: 'generated',
+ };
+ if (excludeKeys.has(candidateKey(candidate))) continue;
+ generated.push(candidate);
+ }
+ }
+
+ return generated;
+ }
+
+ function buildPhasedCandidatePlan(data, options) {
+ const pools = buildTrainingPools(data, options);
+ const arenaCandidates = buildArenaCandidates(
+ pools.arenaTeam,
+ pools.arenaFavor,
+ pools.banner,
+ options
+ );
+ const grandArenaCandidates = buildGrandArenaCandidates(
+ data.teams?.grand || [],
+ data.favor?.grand || pools.arenaFavor,
+ resolveGrandBanners(data.userInfo, pools.banner),
+ pools.banner,
+ options
+ );
+ const priorityCandidates = dedupeCandidates([
+ ...arenaCandidates,
+ ...grandArenaCandidates,
+ ]);
+
+ return {
+ ...pools,
+ arenaCandidates,
+ grandArenaCandidates,
+ priorityCandidates,
+ };
+ }
+
+ function buildCandidateTeams(data, options) {
+ const plan = buildPhasedCandidatePlan(data, options);
+ const maxCombinations = options.maxCombinations || CONSTANTS.DEFAULT_MAX_COMBOS;
+ const testedKeys = new Set(plan.priorityCandidates.map(candidateKey));
+ const generatedCandidates = buildGeneratedCandidates(plan, {
+ maxHeroCombos: maxCombinations,
+ excludeKeys: testedKeys,
+ });
+
+ const unique = [...plan.priorityCandidates];
+ for (const candidate of generatedCandidates) {
+ if (unique.length >= plan.priorityCandidates.length + maxCombinations) break;
+ const key = candidateKey(candidate);
+ if (testedKeys.has(key)) continue;
+ testedKeys.add(key);
+ unique.push(candidate);
+ }
+
+ return { candidates: unique, ...plan };
+ }
+
+ function pickOpponent(opponents, options) {
+ const validOpponents = (opponents || []).filter((opponent) => extractOpponentConfig(opponent).hasValidTeam);
+ if (!validOpponents.length) {
+ throw new Error('No arena opponents with complete 5-hero teams available');
+ }
+ if (options.opponentUserId != null) {
+ const found = validOpponents.find((o) => String(o.userId) === String(options.opponentUserId));
+ if (!found) {
+ throw new Error(`Opponent ${options.opponentUserId} not found or has incomplete team data`);
+ }
+ return found;
+ }
+ const index = Math.max(0, Math.min(validOpponents.length - 1, Number(options.opponentIndex) || 0));
+ return validOpponents[index];
+ }
+
+ function resolveOpponentRaw(opponents, options) {
+ if (options.opponentOverride) {
+ return options.opponentOverride;
+ }
+ if (options.opponentUserId != null) {
+ const found = (opponents || []).find((o) => String(o.userId) === String(options.opponentUserId));
+ if (found) {
+ return found;
+ }
+ }
+ return pickOpponent(opponents, options);
+ }
+
+ return {
+ async getOpponents(forceRefresh = false, options = {}) {
+ const source = options.opponentSource || 'topGet';
+ if (!forceRefresh && opponentsCache?.source === source && opponentsCache?.list) {
+ return opponentsCache.list;
+ }
+
+ const opponents = await fetchOpponents({ ...options, opponentSource: source });
+ opponentsCache = {
+ source,
+ myPlace: opponentsMeta.myPlace,
+ serverId: opponentsMeta.serverId,
+ list: opponents.map((opp, index) => {
+ const team = extractOpponentConfig(opp);
+ return {
+ index,
+ userId: opp.userId,
+ name: opp.user?.name || `Opponent ${opp.userId}`,
+ place: opp.place,
+ power: opp.power,
+ heroes: team.heroes,
+ heroNames: team.heroes.map(heroName),
+ pet: team.pet,
+ banner: team.banner,
+ source,
+ raw: opp,
+ };
+ }),
+ };
+ return opponentsCache.list;
+ },
+
+ getStatus() {
+ return {
+ ...status,
+ ...this.getLoopStatus(),
+ lastResultsId: lastResults?.sessionId || null,
+ };
+ },
+
+ getResults() {
+ return lastResults;
+ },
+
+ exportResults() {
+ return lastResults ? { exportedAt: new Date().toISOString(), ...lastResults } : null;
+ },
+
+ stop() {
+ stopRequested = true;
+ loopRunning = false;
+ return this.getStatus();
+ },
+
+ stopLoop() {
+ return this.stop();
+ },
+
+ getLoopHistory() {
+ return loopSession;
+ },
+
+ getLoopStatus() {
+ return {
+ loopRunning,
+ loopId: loopSession?.loopId || null,
+ roundCount: loopSession?.rounds?.length || 0,
+ startedAt: loopSession?.startedAt || null,
+ lastSavedAt: loopSession?.lastSavedAt || null,
+ };
+ },
+
+ async runCounterPairWorkflow(opponentMeta, runOptions = {}, trainOptions = {}, roundNum = 1) {
+ const opponentRaw = runOptions.opponentOverride ?? opponentMeta.raw ?? null;
+ const maxTargetWinRate = Number(trainOptions.targetWinRate ?? CONSTANTS.DEFAULT_TARGET_WIN_RATE);
+ const userTargetWinRate = Number(
+ trainOptions.userTeamTargetWinRate ?? CONSTANTS.DEFAULT_USER_TEAM_TARGET_WIN_RATE
+ );
+ const excludeKeys = normalizeExcludeKeys(trainOptions.excludeCandidateKeys);
+ const pairAttempts = [];
+ let attempt = 0;
+ let finalMaxResult = null;
+ let finalUserResult = null;
+ let userTargetMet = false;
+
+ const opponentCombo = resolveOpponentComboContext(opponentRaw, opponentMeta);
+ const comboKey = opponentCombo.comboKey;
+ let workflowTesterUserId;
+
+ const tryApplyCachedMaxSkip = (skipInfo, attemptNum) => {
+ const bestMatch = skipInfo?.bestMatch || skipInfo?.cachedBestMatch;
+ const shouldSkip = skipInfo?.shouldSkip || skipInfo?.skipped;
+ if (!shouldSkip || bestMatch?.myHeroIds?.length !== 5) {
+ if (shouldSkip) {
+ console.warn(
+ `[Arena Training] Round ${roundNum} skip-check returned shouldSkip without a valid 5-hero counter`,
+ skipInfo
+ );
+ } else if (comboKey) {
+ console.log(
+ `[Arena Training] Round ${roundNum} skip-check — no cached ${trainOptions.skipCacheMinWinRate ?? CONSTANTS.DEFAULT_SKIP_CACHE_MIN_WIN_RATE}%+ max counter for ${comboKey}`
+ );
+ }
+ return null;
+ }
+
+ const cachedLineup = counterLineupFromCache(bestMatch);
+ const cachedKey = candidateKey(cachedLineup);
+ if (excludeKeys.has(cachedKey)) {
+ console.log(
+ `[Arena Training] Round ${roundNum} cached counter excluded (${cachedKey}), searching another max counter`
+ );
+ return null;
+ }
+
+ const skippedResult = {
+ skipped: true,
+ skipReason: 'cached_counter',
+ attempt: attemptNum,
+ opponentComboKey: comboKey,
+ cachedBestWinRate: skipInfo.bestWinRate ?? skipInfo.cachedBestWinRate,
+ cachedBestMatch: bestMatch,
+ cachedLastTestedAt: skipInfo.lastTestedAt ?? skipInfo.cachedLastTestedAt,
+ counterLineup: cachedLineup,
+ opponent: {
+ index: opponentMeta.index,
+ userId: opponentMeta.userId,
+ name: opponentMeta.name,
+ place: opponentMeta.place,
+ power: opponentMeta.power,
+ source: opponentMeta.source,
+ metaComboKey: opponentMeta.metaComboKey,
+ metaPopularity: opponentMeta.metaPopularity,
+ metaRank: opponentMeta.metaRank,
+ },
+ completedAt: new Date().toISOString(),
+ };
+ pairAttempts.push({ attempt: attemptNum, type: 'max-cache', result: skippedResult });
+ if (loopSession) loopSession.rounds.push(skippedResult);
+ console.log(
+ `[Arena Training] Round ${roundNum} attempt ${attemptNum} — using cached ${(skipInfo.bestWinRate ?? skipInfo.cachedBestWinRate)?.toFixed?.(1) ?? skipInfo.bestWinRate ?? skipInfo.cachedBestWinRate}% counter`
+ );
+ return cachedLineup;
+ };
+
+ while (!stopRequested && (!loopSession || loopRunning)) {
+ attempt++;
+ let counterLineup = null;
+ let maxResult = null;
+ let usedCache = false;
+
+ if (
+ attempt === 1
+ && trainOptions.skipCachedOpponents !== false
+ && comboKey
+ ) {
+ const skipInfo = await fetchOpponentSkipCheck(comboKey, {
+ ...trainOptions,
+ opponentHeroIds: opponentCombo.heroes,
+ });
+ counterLineup = tryApplyCachedMaxSkip(skipInfo, attempt);
+ usedCache = !!counterLineup;
+ }
+
+ if (!counterLineup) {
+ status.message = `Round ${roundNum} attempt ${attempt} — max search (${excludeKeys.size} excluded)`;
+ maxResult = await this.runSingle({
+ ...trainOptions,
+ maxUpgrade: true,
+ skipCachedOpponents: attempt === 1
+ ? trainOptions.skipCachedOpponents !== false
+ : false,
+ opponentHeroIds: opponentCombo.heroes,
+ excludeCandidateKeys: [...excludeKeys],
+ searchUntilTarget: true,
+ opponentUserId: opponentMeta.userId,
+ opponentIndex: runOptions.opponentIndex ?? opponentMeta.index ?? 0,
+ opponentOverride: opponentRaw,
+ label: `${trainOptions.label}-r${roundNum}-max-a${attempt}`,
+ });
+ finalMaxResult = maxResult;
+ pairAttempts.push({ attempt, type: 'max-search', result: maxResult });
+ if (loopSession) loopSession.rounds.push(maxResult);
+
+ if (maxResult?.skipped && maxResult.cachedBestMatch?.myHeroIds?.length === 5) {
+ counterLineup = tryApplyCachedMaxSkip(maxResult, attempt);
+ usedCache = !!counterLineup;
+ }
+
+ if (!counterLineup) {
+ if (trainOptions.saveToBridge !== false) {
+ const saved = await saveRoundToBridge(maxResult);
+ if (saved && loopSession) {
+ loopSession.lastSavedAt = new Date().toISOString();
+ }
+ }
+
+ if (!maxResult.best || maxResult.best.winRate < maxTargetWinRate) {
+ console.log(
+ `[Arena Training] Round ${roundNum} — no further ${maxTargetWinRate}%+ max counter found after ${attempt} attempt(s)`
+ );
+ break;
+ }
+
+ counterLineup = counterLineupFromBest(maxResult.best);
+ console.log(
+ `[Arena Training] Round ${roundNum} attempt ${attempt} max — ${maxResult.best.winRate.toFixed(1)}%:`,
+ maxResult.best.heroNames.join(', ')
+ );
+ }
+ } else if (usedCache) {
+ console.log(
+ `[Arena Training] Round ${roundNum} attempt ${attempt} max — cached ${counterLineup.cachedWinRate?.toFixed?.(1) ?? '?'}%:`,
+ (counterLineup.heroNames || counterLineup.heroes.map(heroName)).join(', ')
+ );
+ }
+
+ let userWinRate = null;
+ let userResult = null;
+
+ if (
+ trainOptions.skipCachedOpponents !== false
+ && comboKey
+ && counterLineup
+ ) {
+ if (workflowTesterUserId === undefined) {
+ workflowTesterUserId = await resolveCurrentTesterUserId();
+ }
+ if (workflowTesterUserId) {
+ const userSkip = await fetchUserCounterSkipCheck(
+ comboKey,
+ counterLineup,
+ workflowTesterUserId,
+ trainOptions
+ );
+ if (userSkip.shouldSkip) {
+ userWinRate = userSkip.cachedWinRate ?? 0;
+ userResult = {
+ skipped: true,
+ skipReason: 'cached_user_counter',
+ attempt,
+ opponentComboKey: comboKey,
+ cachedWinRate: userSkip.cachedWinRate,
+ cachedMatch: userSkip.cachedMatch,
+ cachedLastTestedAt: userSkip.lastTestedAt,
+ tester: {
+ userId: workflowTesterUserId,
+ name: userSkip.testerName,
+ maxUpgrade: false,
+ },
+ counterLineup,
+ best: {
+ heroes: counterLineup.heroes,
+ heroNames: counterLineup.heroNames
+ || counterLineup.heroes.map(heroName),
+ pet: counterLineup.pet,
+ winRate: userWinRate,
+ wins: userSkip.cachedWins,
+ losses: userSkip.cachedLosses,
+ },
+ completedAt: new Date().toISOString(),
+ };
+ pairAttempts.push({
+ attempt,
+ type: 'user-cache',
+ result: userResult,
+ counterLineup,
+ });
+ if (loopSession) loopSession.rounds.push(userResult);
+ console.log(
+ `[Arena Training] Round ${roundNum} attempt ${attempt} user skipped — cached ${userWinRate.toFixed(1)}% within ${trainOptions.skipCacheMaxAgeDays ?? CONSTANTS.DEFAULT_SKIP_CACHE_MAX_AGE_DAYS} days`
+ );
+ }
+ }
+ }
+
+ if (!userResult) {
+ status.message = `Round ${roundNum} attempt ${attempt} — user test same lineup`;
+ userResult = await this.runSingle({
+ ...trainOptions,
+ maxUpgrade: false,
+ counterLineup,
+ counterLineupOnly: true,
+ skipCachedOpponents: false,
+ opponentUserId: opponentMeta.userId,
+ opponentIndex: runOptions.opponentIndex ?? opponentMeta.index ?? 0,
+ opponentOverride: opponentRaw,
+ label: `${trainOptions.label}-r${roundNum}-user-a${attempt}`,
+ });
+ pairAttempts.push({ attempt, type: 'user-counter', result: userResult, counterLineup });
+ if (loopSession) loopSession.rounds.push(userResult);
+
+ if (trainOptions.saveToBridge !== false) {
+ const userSaved = await saveRoundToBridge(userResult);
+ if (userSaved && loopSession) {
+ loopSession.lastSavedAt = new Date().toISOString();
+ }
+ }
+
+ userWinRate = userResult.best?.winRate ?? 0;
+ }
+
+ finalUserResult = userResult;
+ lastResults = userResult;
+
+ const userHeroNames = userResult.best?.heroNames?.join(', ')
+ || counterLineup.heroNames?.join(', ')
+ || counterLineup.heroes.map(heroName).join(', ');
+ if (!userResult.skipped) {
+ console.log(
+ `[Arena Training] Round ${roundNum} attempt ${attempt} user — ${userWinRate.toFixed(1)}%:`,
+ userHeroNames
+ );
+ }
+
+ if (userWinRate >= userTargetWinRate) {
+ userTargetMet = true;
+ console.log(
+ `[Arena Training] Round ${roundNum} — user team reached ${userTargetWinRate}%+ with counter lineup`
+ );
+ break;
+ }
+
+ excludeKeys.add(candidateKey(counterLineup));
+ console.log(
+ `[Arena Training] Round ${roundNum} — user ${userWinRate.toFixed(1)}% < ${userTargetWinRate}%, searching another ${maxTargetWinRate}%+ max counter`
+ );
+
+ const maxAttempts = Number(trainOptions.maxCounterAttempts);
+ if (Number.isFinite(maxAttempts) && maxAttempts > 0 && attempt >= maxAttempts) {
+ console.warn(`[Arena Training] Round ${roundNum} — stopped after ${maxAttempts} counter attempts`);
+ break;
+ }
+ }
+
+ const summary = {
+ round: roundNum,
+ attempts: attempt,
+ userTargetWinRate,
+ maxTargetWinRate,
+ userTargetMet,
+ excludeCount: excludeKeys.size,
+ pairAttempts,
+ maxResult: finalMaxResult,
+ userResult: finalUserResult,
+ completedAt: new Date().toISOString(),
+ };
+ lastResults = summary;
+ return summary;
+ },
+
+ startLoop(options = {}) {
+ if (loopRunning) {
+ return { started: false, ...this.getLoopStatus(), message: 'Loop already running' };
+ }
+
+ const trainDefaults = {
+ label: 'loop',
+ opponentSource: 'topGet',
+ topLimit: 12,
+ heroPoolSize: 12,
+ maxCombinations: 20,
+ simulationsPerCombo: 10,
+ targetWinRate: CONSTANTS.DEFAULT_TARGET_WIN_RATE,
+ userTeamTargetWinRate: CONSTANTS.DEFAULT_USER_TEAM_TARGET_WIN_RATE,
+ searchUntilTarget: true,
+ skipCachedOpponents: true,
+ skipCacheMinWinRate: CONSTANTS.DEFAULT_SKIP_CACHE_MIN_WIN_RATE,
+ skipCacheMaxAgeDays: CONSTANTS.DEFAULT_SKIP_CACHE_MAX_AGE_DAYS,
+ includeCurrentTeam: true,
+ includeGrandArenaTeams: true,
+ useMetaTeams: true,
+ useMetaTeamsAsOpponents: true,
+ metaTeamsLimit: CONSTANTS.DEFAULT_META_TEAMS_LIMIT,
+ includeUserTeamTest: true,
+ saveToBridge: true,
+ delayBetweenRoundsMs: 2000,
+ repeatCycle: true,
+ maxRounds: 0,
+ };
+ const trainOptions = applyTrainingOptions({ ...trainDefaults, ...options });
+
+ loopRunning = true;
+ stopRequested = false;
+ loopSession = {
+ loopId: `loop_${Date.now()}`,
+ label: trainOptions.label,
+ startedAt: new Date().toISOString(),
+ config: trainOptions,
+ rounds: [],
+ lastSavedAt: null,
+ };
+
+ status.loopRunning = true;
+ status.message = 'Loop training started';
+
+ (async () => {
+ let roundNum = 0;
+
+ const runOpponentRound = async (opponentMeta, runOptions = {}) => {
+ if (!loopRunning || stopRequested) return false;
+ if (trainOptions.maxRounds > 0 && roundNum >= trainOptions.maxRounds) {
+ loopRunning = false;
+ return false;
+ }
+
+ roundNum++;
+ status.loopRound = roundNum;
+ const phaseLabel = runOptions.phaseLabel || 'Loop';
+ status.message = `${phaseLabel} round ${roundNum} — ${opponentMeta?.name || opponentMeta?.userId || 'opponent'}`;
+
+ try {
+ const opponentRaw = runOptions.opponentOverride ?? opponentMeta.raw ?? null;
+ if (opponentRaw && !extractOpponentConfig(opponentRaw).hasValidTeam) {
+ recordInvalidOpponentRound(loopSession, roundNum, opponentMeta, opponentRaw);
+ return true;
+ }
+
+ if (trainOptions.includeUserTeamTest === false) {
+ const result = await this.runSingle({
+ ...trainOptions,
+ maxUpgrade: true,
+ skipCachedOpponents: false,
+ opponentUserId: opponentMeta.userId,
+ opponentIndex: runOptions.opponentIndex ?? opponentMeta.index ?? 0,
+ opponentOverride: opponentRaw,
+ label: `${trainOptions.label}-r${roundNum}-max`,
+ });
+ loopSession.rounds.push(result);
+ lastResults = result;
+ if (trainOptions.saveToBridge !== false) {
+ const saved = await saveRoundToBridge(result);
+ if (saved) loopSession.lastSavedAt = new Date().toISOString();
+ }
+ } else {
+ await this.runCounterPairWorkflow(
+ opponentMeta,
+ runOptions,
+ trainOptions,
+ roundNum
+ );
+ }
+ } catch (err) {
+ console.error(`[Arena Training] Round ${roundNum} failed:`, err);
+ loopSession.rounds.push({
+ round: roundNum,
+ opponentIndex: opponentMeta?.index,
+ opponentSource: opponentMeta?.source,
+ error: err.message,
+ fatal: isFatalTrainingError(err),
+ completedAt: new Date().toISOString(),
+ });
+ if (stopTrainingOnFatalError(err, `round ${roundNum}`)) {
+ return false;
+ }
+ }
+
+ if (trainOptions.delayBetweenRoundsMs > 0) {
+ await sleep(trainOptions.delayBetweenRoundsMs);
+ }
+ return true;
+ };
+
+ try {
+ while (loopRunning && !stopRequested) {
+ const opponents = await this.getOpponents(true, trainOptions);
+ const indexes = Array.isArray(trainOptions.opponentIndexes) && trainOptions.opponentIndexes.length
+ ? trainOptions.opponentIndexes
+ : opponents.map((o) => o.index);
+
+ for (const idx of indexes) {
+ if (!loopRunning || stopRequested) break;
+ const opponentList = await this.getOpponents(false, trainOptions);
+ const opponentMeta = opponentList.find((o) => o.index === idx);
+ if (!opponentMeta) continue;
+ const shouldContinue = await runOpponentRound.call(this, opponentMeta, {
+ phaseLabel: 'Arena',
+ opponentIndex: idx,
+ opponentOverride: opponentMeta.raw,
+ });
+ if (!shouldContinue) break;
+ }
+
+ if (
+ loopRunning
+ && !stopRequested
+ && trainOptions.useMetaTeamsAsOpponents !== false
+ ) {
+ const meta = await fetchMetaTeamOpponents(trainOptions);
+ if (meta.opponents.length) {
+ loopSession.metaOpponentsSnapshotId = meta.snapshotId;
+ console.log(
+ `[Arena Training] Meta opponent phase: ${meta.opponents.length} teams from snapshot ${meta.snapshotId}`
+ );
+ for (const opponentMeta of meta.opponents) {
+ if (!loopRunning || stopRequested) break;
+ const shouldContinue = await runOpponentRound.call(this, opponentMeta, {
+ phaseLabel: 'Meta opponent',
+ opponentOverride: opponentMeta.raw,
+ opponentIndex: 0,
+ });
+ if (!shouldContinue) break;
+ }
+ } else {
+ console.warn('[Arena Training] No meta team opponents available from bridge');
+ }
+ }
+
+ if (trainOptions.maxRounds > 0 && roundNum >= trainOptions.maxRounds) break;
+ if (!trainOptions.repeatCycle) break;
+ }
+ } finally {
+ loopRunning = false;
+ status.loopRunning = false;
+ status.message = loopSession?.fatalError
+ ? `Loop stopped — ${loopSession.fatalError}`
+ : stopRequested
+ ? `Loop stopped after ${roundNum} rounds`
+ : `Loop finished after ${roundNum} rounds`;
+ loopSession.completedAt = new Date().toISOString();
+ loopSession.totalRounds = roundNum;
+ HWHFuncs.setProgress(status.message, true);
+ }
+ })();
+
+ HWHFuncs.setProgress('Arena loop training started — results auto-save to bridge', true);
+ return { started: true, ...this.getLoopStatus(), message: 'Loop training started' };
+ },
+
+ async run(options = {}) {
+ options = applyTrainingOptions(options);
+
+ if (options.includeUserTeamTest === false) {
+ return this.runSingle({
+ ...options,
+ maxUpgrade: true,
+ label: `${options.label || 'arena-training'}-max`,
+ });
+ }
+
+ const opponents = await this.getOpponents(false, options);
+ const opponentRaw = resolveOpponentRaw(opponents, options);
+ const opponentMeta = opponents.find((o) => o.raw === opponentRaw)
+ || opponents[options.opponentIndex ?? 0]
+ || (() => {
+ const team = extractOpponentConfig(opponentRaw);
+ return {
+ index: options.opponentIndex ?? 0,
+ userId: opponentRaw?.userId,
+ name: opponentRaw?.user?.name,
+ place: opponentRaw?.place,
+ power: opponentRaw?.power,
+ heroes: team.heroes,
+ pet: team.pet,
+ banner: team.banner,
+ source: opponentRaw?.source || options.opponentSource,
+ raw: opponentRaw,
+ };
+ })();
+
+ return this.runCounterPairWorkflow(
+ opponentMeta,
+ {
+ opponentOverride: opponentRaw,
+ opponentIndex: options.opponentIndex ?? opponentMeta.index ?? 0,
+ },
+ options,
+ 1
+ );
+ },
+
+ async runSingle(options = {}) {
+ if (running) {
+ throw new Error('Arena training already running');
+ }
+
+ options = applyTrainingOptions(options);
+ running = true;
+ stopRequested = false;
+ const maxUpgrade = options.maxUpgrade !== false;
+ const userTeamOnly = options.userTeamOnly === true;
+ const counterLineupOnly = options.counterLineupOnly === true && !!options.counterLineup;
+ const externalExcludeKeys = normalizeExcludeKeys(options.excludeCandidateKeys);
+ const sessionSuffix = maxUpgrade ? 'max' : 'user';
+ const sessionId = `arena_train_${Date.now()}_${sessionSuffix}`;
+ const startedAt = new Date().toISOString();
+ const simulationsPerCombo = options.simulationsPerCombo || CONSTANTS.DEFAULT_SIMULATIONS;
+ const targetWinRate = Number(options.targetWinRate ?? CONSTANTS.DEFAULT_TARGET_WIN_RATE);
+ const searchUntilTarget = options.searchUntilTarget !== false;
+
+ status = {
+ running: true,
+ sessionId,
+ currentCombo: 0,
+ totalCombos: 0,
+ targetWinRate,
+ searchUntilTarget,
+ label: options.label || 'arena-training',
+ message: 'Loading arena data...',
+ };
+
+ try {
+ HWHFuncs.setProgress('Arena Training: loading opponents and heroes...', true);
+ const data = await loadGameData(options);
+ const tester = resolveTesterInfo(data.userInfo, maxUpgrade);
+ const opponentRaw = resolveOpponentRaw(data.opponents, options);
+ const opponentTeam = extractOpponentConfig(opponentRaw);
+ if (!opponentTeam.hasValidTeam) {
+ const teamInfo = describeOpponentTeam(opponentRaw);
+ throw new Error(
+ `Selected opponent has invalid team data (${teamInfo.heroCount}/5 heroes, user ${teamInfo.userId || teamInfo.name || 'unknown'})`
+ );
+ }
+
+ const opponentComboKey = buildComboKey(
+ opponentTeam.heroes,
+ opponentTeam.pet,
+ opponentTeam.banner
+ );
+
+ if (options.skipCachedOpponents !== false && maxUpgrade) {
+ const skipInfo = await fetchOpponentSkipCheck(opponentComboKey, {
+ ...options,
+ opponentHeroIds: opponentTeam.heroes,
+ });
+ if (skipInfo.shouldSkip) {
+ const skippedResult = {
+ sessionId,
+ label: options.label || 'arena-training',
+ startedAt,
+ completedAt: new Date().toISOString(),
+ skipped: true,
+ skipReason: 'cached_counter',
+ opponentComboKey,
+ cachedBestWinRate: skipInfo.bestWinRate,
+ cachedBestMatch: skipInfo.bestMatch,
+ cachedLastTestedAt: skipInfo.lastTestedAt,
+ opponent: {
+ index: data.opponents.indexOf(opponentRaw),
+ userId: opponentRaw.userId,
+ name: opponentRaw.user?.name,
+ place: opponentRaw.place,
+ power: opponentRaw.power,
+ banner: opponentTeam.banner,
+ source: opponentRaw.source || options.opponentSource || 'topGet',
+ team: opponentTeam,
+ },
+ config: {
+ skipCachedOpponents: true,
+ skipCacheMinWinRate: Number(options.skipCacheMinWinRate ?? CONSTANTS.DEFAULT_SKIP_CACHE_MIN_WIN_RATE),
+ skipCacheMaxAgeDays: Number(options.skipCacheMaxAgeDays ?? CONSTANTS.DEFAULT_SKIP_CACHE_MAX_AGE_DAYS),
+ },
+ };
+ lastResults = skippedResult;
+ HWHFuncs.setProgress(
+ `Arena Training skipped — cached ${skipInfo.bestWinRate?.toFixed?.(1) ?? skipInfo.bestWinRate}% counter (${opponentComboKey})`,
+ true
+ );
+ return skippedResult;
+ }
+ }
+
+ const plan = buildPhasedCandidatePlan(data, options);
+ const {
+ arenaCandidates,
+ grandArenaCandidates,
+ heroPool,
+ petPool,
+ banner,
+ } = plan;
+
+ let metaTeamCandidates = [];
+ let metaTeamsSnapshotId = null;
+ if (!userTeamOnly && options.useMetaTeams !== false) {
+ const meta = await fetchMetaTeamCandidates(options);
+ metaTeamsSnapshotId = meta.snapshotId;
+ metaTeamCandidates = buildMetaTeamCandidates(meta.candidates, data, options, banner);
+ if (metaTeamCandidates.length) {
+ console.log(
+ `[Arena Training] Loaded ${metaTeamCandidates.length} meta team candidates from snapshot ${metaTeamsSnapshotId}`
+ );
+ }
+ }
+
+ const testedKeys = new Set();
+ const rankings = [];
+ let stoppedBecause = 'exhausted';
+ let targetMet = false;
+ let plannedGenerated = 0;
+ let plannedMeta = metaTeamCandidates.length;
+
+ const battleOptions = { maxUpgrade };
+ const testCandidateBatch = async (candidates, phaseLabel) => {
+ for (let i = 0; i < candidates.length; i++) {
+ if (stopRequested) {
+ stoppedBecause = 'user_stop';
+ return true;
+ }
+
+ const candidate = candidates[i];
+ const key = candidateKey(candidate);
+ if (externalExcludeKeys.has(key) || testedKeys.has(key)) continue;
+ testedKeys.add(key);
+
+ status.currentCombo = rankings.length + 1;
+ status.message = `${phaseLabel} ${i + 1}/${candidates.length}`;
+
+ const heroLabels = candidate.heroes.map(heroName);
+ HWHFuncs.setProgress(
+ `Arena Training [${phaseLabel}] ${heroLabels.join(', ')}`,
+ true
+ );
+
+ const myTeam = buildMyTeamConfig(
+ candidate.heroes,
+ candidate.pet,
+ candidate.banner ?? banner,
+ candidate.favor
+ );
+
+ const simulation = await simulateTeam(
+ myTeam,
+ opponentTeam,
+ simulationsPerCombo,
+ battleOptions
+ );
+ rankings.push({
+ rank: 0,
+ heroes: candidate.heroes,
+ heroNames: heroLabels,
+ pet: candidate.pet,
+ banner: myTeam.banners[0],
+ favor: myTeam.favor,
+ source: candidate.source,
+ wins: simulation.wins,
+ losses: simulation.losses,
+ winRate: simulation.winRate,
+ averageBattleTime: simulation.averageBattleTime,
+ simulations: simulation.simulations,
+ });
+
+ if (searchUntilTarget && simulation.winRate >= targetWinRate) {
+ stoppedBecause = 'target_met';
+ targetMet = true;
+ status.message = `Found ${simulation.winRate.toFixed(1)}% in ${phaseLabel}`;
+ return true;
+ }
+ }
+ return false;
+ };
+
+ status.totalCombos = arenaCandidates.length + grandArenaCandidates.length + plannedMeta;
+
+ if (counterLineupOnly) {
+ const arenaFavor = data.favor?.arena || {};
+ const candidate = {
+ ...options.counterLineup,
+ banner: options.counterLineup.banner ?? banner,
+ favor: options.counterLineup.favor || pickFavor(options.counterLineup.heroes, arenaFavor),
+ };
+ if (!candidate.heroNames?.length) {
+ candidate.heroNames = candidate.heroes.map(heroName);
+ }
+ status.totalCombos = 1;
+ status.message = `${maxUpgrade ? 'Max' : 'User'} counter lineup vs ${opponentRaw.user?.name || opponentRaw.userId}`;
+ await testCandidateBatch([candidate], maxUpgrade ? 'counter max' : 'counter user');
+ } else if (userTeamOnly) {
+ status.totalCombos = arenaCandidates.length + grandArenaCandidates.length;
+ status.message = `User team test vs ${opponentRaw.user?.name || opponentRaw.userId}`;
+ if (!(await testCandidateBatch(arenaCandidates, 'user arena'))) {
+ await testCandidateBatch(grandArenaCandidates, 'user grand arena');
+ }
+ } else {
+ status.message = `Phase 1: arena team vs ${opponentRaw.user?.name || opponentRaw.userId}`;
+
+ if (await testCandidateBatch(arenaCandidates, 'arena')) {
+ // target met or user stopped
+ } else {
+ status.message = `Phase 2: grand arena teams vs ${opponentRaw.user?.name || opponentRaw.userId}`;
+ status.totalCombos += grandArenaCandidates.length;
+ if (!(await testCandidateBatch(grandArenaCandidates, 'grand arena'))) {
+ const runGeneratedPhase = async (phaseNumber) => {
+ const maxCombinations = options.maxCombinations || CONSTANTS.DEFAULT_MAX_COMBOS;
+ const generatedCandidates = searchUntilTarget
+ ? buildGeneratedCandidates(plan, { excludeKeys: testedKeys })
+ : buildGeneratedCandidates(plan, {
+ maxHeroCombos: maxCombinations,
+ excludeKeys: testedKeys,
+ }).slice(0, maxCombinations);
+
+ if (generatedCandidates.length > 0) {
+ plannedGenerated = generatedCandidates.length;
+ status.totalCombos += plannedGenerated;
+ status.message = `Phase ${phaseNumber}: testing ${plannedGenerated} generated combos`;
+ await testCandidateBatch(generatedCandidates, 'generated');
+ } else if (!searchUntilTarget) {
+ stoppedBecause = 'max_combos';
+ }
+ };
+
+ if (metaTeamCandidates.length > 0) {
+ status.message = `Phase 3: ${metaTeamCandidates.length} meta teams vs ${opponentRaw.user?.name || opponentRaw.userId}`;
+ if (!(await testCandidateBatch(metaTeamCandidates, 'meta'))) {
+ await runGeneratedPhase(4);
+ }
+ } else {
+ await runGeneratedPhase(3);
+ }
+ }
+ }
+ }
+
+ if (stopRequested && stoppedBecause !== 'target_met') {
+ stoppedBecause = 'user_stop';
+ }
+
+ rankings.sort((a, b) => {
+ if (b.winRate !== a.winRate) return b.winRate - a.winRate;
+ if (b.wins !== a.wins) return b.wins - a.wins;
+ return a.averageBattleTime - b.averageBattleTime;
+ });
+ rankings.forEach((entry, index) => {
+ entry.rank = index + 1;
+ });
+
+ lastResults = {
+ sessionId,
+ label: options.label || 'arena-training',
+ startedAt,
+ completedAt: new Date().toISOString(),
+ stoppedEarly: stopRequested,
+ stoppedBecause,
+ targetWinRate,
+ targetMet,
+ tester,
+ searchPhases: {
+ arena: arenaCandidates.length,
+ grandArena: grandArenaCandidates.length,
+ meta: plannedMeta,
+ metaSnapshotId: metaTeamsSnapshotId,
+ generated: plannedGenerated,
+ },
+ opponent: {
+ index: options.opponentOverride ? (options.opponentIndex ?? 0) : data.opponents.indexOf(opponentRaw),
+ userId: opponentRaw.userId,
+ name: opponentRaw.user?.name,
+ place: opponentRaw.place,
+ power: opponentRaw.power,
+ banner: opponentTeam.banner,
+ source: opponentRaw.source || options.opponentSource || 'topGet',
+ metaComboKey: opponentRaw.metaComboKey || null,
+ metaPopularity: opponentRaw.metaPopularity ?? null,
+ metaRank: opponentRaw.metaRank ?? null,
+ team: opponentTeam,
+ },
+ config: {
+ heroPool,
+ petPool,
+ simulationsPerCombo,
+ targetWinRate,
+ searchUntilTarget,
+ maxUpgrade: tester.maxUpgrade,
+ userTeamOnly,
+ counterLineupOnly,
+ userTeamTargetWinRate: Number(
+ options.userTeamTargetWinRate ?? CONSTANTS.DEFAULT_USER_TEAM_TARGET_WIN_RATE
+ ),
+ excludeCandidateCount: externalExcludeKeys.size,
+ maxCombinations: options.maxCombinations || CONSTANTS.DEFAULT_MAX_COMBOS,
+ includeCurrentTeam: options.includeCurrentTeam !== false,
+ includeGrandArenaTeams: options.includeGrandArenaTeams !== false,
+ useMetaTeams: options.useMetaTeams !== false,
+ useMetaTeamsAsOpponents: options.useMetaTeamsAsOpponents !== false,
+ metaTeamsLimit: Number(options.metaTeamsLimit ?? CONSTANTS.DEFAULT_META_TEAMS_LIMIT),
+ metaTeamsSnapshotId: options.metaTeamsSnapshotId || metaTeamsSnapshotId || null,
+ opponentSource: options.opponentSource || 'topGet',
+ topLimit: options.topLimit,
+ heroPoolSize: options.heroPoolSize,
+ opponentLimit: options.opponentLimit || 0,
+ myArenaPlace: opponentsMeta.myPlace,
+ },
+ plannedCombos: arenaCandidates.length + grandArenaCandidates.length + plannedMeta + plannedGenerated,
+ testedCombos: rankings.length,
+ rankings,
+ best: rankings[0] || null,
+ };
+
+ const best = rankings[0];
+ const summary = best
+ ? `Best: ${best.heroNames.join(', ')} + pet ${best.pet} (${best.winRate.toFixed(1)}% WR)`
+ : 'No combinations tested';
+ const stopNote = stoppedBecause === 'target_met'
+ ? ` Target ${targetWinRate}%+ reached.`
+ : (searchUntilTarget ? ` Exhausted ${rankings.length} combos (no ${targetWinRate}%+).` : '');
+ HWHFuncs.setProgress(`Arena Training done. ${summary}${stopNote}`, true);
+ console.log('[Arena Training] Results:', lastResults);
+ return lastResults;
+ } finally {
+ running = false;
+ status = {
+ running: false,
+ sessionId,
+ currentCombo: status.currentCombo,
+ totalCombos: status.totalCombos,
+ message: stopRequested ? 'Stopped by user' : 'Completed',
+ };
+ }
+ },
+ };
+ }
+
+ async function openTrainingPopup(training, HWHFuncs) {
+ try {
+ const content = document.createElement('div');
+ content.style.cssText = 'padding: 16px; color: #fce1ac; max-width: 640px; line-height: 1.5;';
+ content.innerHTML = `
+ Arena Training
+ Loop mode loads the arena top 50 via topGet, then tests vs meta team opponents from the bridge DB.
+ Demo battles only — no arena attempts used .
+ Skips opponents already solved in PostgreSQL: ${CONSTANTS.DEFAULT_SKIP_CACHE_MIN_WIN_RATE}%+ max counter within ${CONSTANTS.DEFAULT_SKIP_CACHE_MAX_AGE_DAYS} days . User-team tests for the same lineup are also skipped within ${CONSTANTS.DEFAULT_SKIP_CACHE_MAX_AGE_DAYS} days .
+ Per opponent: find a ${CONSTANTS.DEFAULT_TARGET_WIN_RATE}%+ max counter (or use cache), test the same lineup with your real heroes, and re-search if user win rate is below ${CONSTANTS.DEFAULT_USER_TEAM_TARGET_WIN_RATE}% .
+ Max search phases: arena → grand arena → meta teams → generated.
+ Run node llm-bridge-server.mjs with PostgreSQL (DATABASE_URL) so results save to the bridge database.
+ `;
+
+ const popupPromise = HWHFuncs.popup.confirm('', [
+ { msg: 'Start loop', result: 'loop', color: 'green' },
+ { msg: 'One round only', result: 'run', color: 'blue' },
+ { msg: 'Stop loop', result: 'stop', color: 'red' },
+ { msg: 'Close', result: false, isClose: true },
+ ]);
+ await new Promise((resolve) => setTimeout(resolve, 0));
+ const popupBody = document.querySelector('.PopUp_Container');
+ if (popupBody) {
+ popupBody.innerHTML = '';
+ popupBody.appendChild(content);
+ }
+ const choice = await popupPromise;
+ if (choice === 'loop') {
+ training.startLoop({ label: 'popup-loop' });
+ } else if (choice === 'run') {
+ training.run({ label: 'popup-single', opponentIndex: 0, opponentSource: 'topGet' }).catch((err) => {
+ HWHFuncs.setProgress(`Arena Training failed: ${err.message}`, true);
+ });
+ } else if (choice === 'stop') {
+ training.stopLoop();
+ HWHFuncs.setProgress('Arena loop stopped', true);
+ }
+ } catch (error) {
+ HWHFuncs.setProgress(`Arena Training error: ${error.message}`, true);
+ }
+ }
+})();
diff --git a/AutoAdventureExt.user.js b/AutoAdventureExt.user.js
new file mode 100644
index 0000000..d864202
--- /dev/null
+++ b/AutoAdventureExt.user.js
@@ -0,0 +1,1118 @@
+// ==UserScript==
+// @name AutoAdventureExt
+// @namespace AutoAdventureExt
+// @version 0.1.0
+// @license Copyright ZingerY & orb
+// @description Extension for Hero Wars Helper. Modifies the adventure button to use predefined paths directly within the script, allowing modification before starting. HeroWarsHelper
+// @author ZingerY & CR3 Cappu Red + Pizza Clan (Modified by AI)
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @run-at document-end
+// @grant none
+// @downloadURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/AutoAdventureExt.user.js
+// @updateURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/AutoAdventureExt.user.js
+// ==/UserScript==
+
+(function () {
+ 'use strict';
+
+ const waitForHWH = setInterval(() => {
+ if (window.HWHClasses && window.HWHClasses.ScriptMenu && window.HWHFuncs && window.lib && window.cheats) {
+ const scriptMenu = window.HWHClasses.ScriptMenu.getInst();
+ if (scriptMenu && scriptMenu.mainMenu) {
+ clearInterval(waitForHWH);
+ initializeExtension();
+ }
+ }
+ }, 200);
+
+ function initializeExtension() {
+ console.log('%cAuto Adventure Extension (v0.1.0) loaded', 'color: green');
+
+ // Inject custom styles for popup buttons
+ function injectCustomStyles() {
+ const style = document.createElement('style');
+ style.type = 'text/css';
+ style.innerHTML = `
+ .PopUp_buttonText {
+ white-space: normal !important;
+ word-break: break-all !important;
+ text-align: left !important;
+ line-height: 1.2 !important;
+ }
+ .PopUp_button {
+ max-width: 450px;
+ width: 100%;
+ height: auto;
+ box-sizing: border-box;
+ }
+ `;
+ document.head.appendChild(style);
+ console.log('%cCustom popup styles injected for text wrapping.', 'color: cyan');
+ }
+
+ injectCustomStyles();
+
+ const { addExtentionName, getSaveVal, I18N, popup, setSaveVal, setProgress } = window.HWHFuncs;
+ const { Send } = window;
+ addExtentionName(GM_info.script.name, GM_info.script.version, GM_info.script.author);
+
+ // Constants
+ const COLOR_EMOJIS = {
+ blue: '🔵', orange: '🟠', green: '🟢', yellow: '🟡',
+ purple: '🟣', red: '🔴', white: '⚪', black: '⚫', brown: '🟤'
+ };
+ const ORDERED_COLORS = ['blue', 'orange', 'green', 'yellow', 'purple', 'red', 'white', 'black', 'brown'];
+ const PORTAL_SPHERE_ID = 45;
+ const REWARD_COLLECTION_DELAY = 500;
+
+ // Adventure paths configuration
+ const defaultWays = {
+ adventure: {
+ //Галахад, 1-я
+ "adv_strongford_2pl_easy": {
+ default: { path: '1,2,4,7,6', label: 'Default (Orange)' },
+ blue: { path: '1,2,3,5,6', label: 'Solfors Blue' },
+ orange: { path: '1,2,4,7,6', label: 'Solfors Orange' },
+ green: { path: '1,2,3,5,6', label: 'Solfors Green' },
+ yellow: { path: '', label: 'Goodwin A' },
+ purple: { path: '', label: 'Goodwin B' },
+ red: { path: '', label: 'Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Джинджер, 2-я
+ "adv_valley_3pl_easy": {
+ default: { path: '1,3,6,9,11', label: 'Default (Orange)' },
+ blue: { path: '1,2,5,8,9,11', label: 'Solfors Blue' },
+ orange: { path: '1,3,6,9,11', label: 'Solfors Orange' },
+ green: { path: '1,4,7,10,9,11', label: 'Solfors Green' },
+ yellow: { path: '', label: 'Goodwin A' },
+ purple: { path: '', label: 'Goodwin B' },
+ red: { path: '', label: 'Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Орион, 3-я
+ "adv_ghirwil_3pl_easy": {
+ default: { path: '1,4,12,13,11', label: 'Default (Orange)' },
+ blue: { path: '1,5,6,9,11', label: 'Solfors Blue' },
+ orange: { path: '1,4,12,13,11', label: 'Solfors Orange' },
+ green: { path: '1,2,3,7,10,11', label: 'Solfors Green' },
+ yellow: { path: '', label: 'Goodwin A' },
+ purple: { path: '', label: 'Goodwin B' },
+ red: { path: '', label: 'Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Тесак, 4-я
+ "adv_angels_3pl_easy_fire": {
+ default: { path: '1,3,6,11,17,10,16,21,22,23', label: 'Default (Orange)' },
+ blue: { path: '1,2,4,7,18,8,12,19,22,23', label: 'Solfors Blue' },
+ orange: { path: '1,3,6,11,17,10,16,21,22,23', label: 'Solfors Orange' },
+ green: { path: '1,5,24,25,9,14,15,20,22,23', label: 'Solfors Green' },
+ yellow: { path: '', label: 'Goodwin A' },
+ purple: { path: '', label: 'Goodwin B' },
+ red: { path: '', label: 'Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Галахад, 5-я
+ "adv_strongford_3pl_normal_2": {
+ default: { path: '1,4,6,10,11,15,22,15,19,18,24', label: 'Default (Orange)' },
+ blue: { path: '1,2,7,8,12,16,23,26,25,21,24', label: 'Solfors Blue' },
+ orange: { path: '1,4,6,10,11,15,22,15,19,18,24', label: 'Solfors Orange' },
+ green: { path: '1,5,9,10,14,17,20,27,25,21,24', label: 'Solfors Green' },
+ yellow: { path: '', label: 'Goodwin A' },
+ purple: { path: '', label: 'Goodwin B' },
+ red: { path: '', label: 'Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Джинджер, 6-я
+ "adv_valley_3pl_normal": {
+ default: { path: '05,07,08,11,14,17,20,23,25', label: 'Default (Orange)' },
+ blue: { path: '02,04,07,10,13,16,19,24,22,25', label: 'Solfors Blue' },
+ orange: { path: '05,07,08,11,14,17,20,23,25', label: 'Solfors Orange' },
+ green: { path: '03,06,09,12,15,18,21,26,25', label: 'Solfors Green' },
+ yellow: { path: '1,2,4,7,10,13,16,19,24,22,25', label: 'Goodwin A' },
+ purple: { path: '1,3,6,9,12,15,18,21,26,23,25', label: 'Goodwin B' },
+ red: { path: '1,5,7,8,11,14,17,20,22,25', label: 'Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Орион, 7-я
+ "adv_ghirwil_3pl_normal_2": {
+ default: { path: '11,10,14,17,13,19,20,24,27', label: 'Default (Orange)' },
+ blue: { path: '08,01,11,12,15,12,11,21,25,27', label: 'Solfors Blue' },
+ orange: { path: '11,10,14,17,13,19,20,24,27', label: 'Solfors Orange' },
+ green: { path: '07,03,04,05,09,16,23,22,26,27', label: 'Solfors Green' },
+ yellow: { path: '1,11,10,11,12,15,12,11,21,25,27', label: 'Goodwin A' },
+ purple: { path: '1,7,3,4,3,6,13,19,20,24,27', label: 'Goodwin B' },
+ red: { path: '1,7,3,4,3,6,13,19,20,24,27', label: 'Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Тесак, 8-я
+ "adv_angels_3pl_normal": {
+ default: { path: '03,05,07,09,11,14,18,20,22,24,27,30,26,29,25', label: 'Default (Orange)' },
+ blue: { path: '03,02,06,07,09,10,13,17,16,20,22,21,28,32', label: 'Solfors Blue' },
+ orange: { path: '03,05,07,09,11,14,18,20,22,24,27,30,26,29,25', label: 'Solfors Orange' },
+ green: { path: '03,04,08,07,09,11,15,19,20,22,23,31,32', label: 'Solfors Green' },
+ yellow: { path: '1,3,4,8,7,9,10,13,17,16,20,22,23,31,32', label: 'Goodwin A' },
+ purple: { path: '1,3,5,7,8,11,14,18,20,22,24,27,30,26,32', label: 'Goodwin B' },
+ red: { path: '1,3,2,6,7,9,11,15,19,20,22,21,28,29,25', label: 'Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Галахад, 9-я
+ "adv_strongford_3pl_hard_2": {
+ default: { path: '03,08,12,11,07,16,21,26,30,31,32,35,37,40,45', label: 'Default (Orange)' },
+ blue: { path: '02,06,10,15,20,14,24,29,25,36,39,42,44,45', label: 'Solfors Blue' },
+ orange: { path: '03,08,12,11,07,16,21,26,30,31,32,35,37,40,45', label: 'Solfors Orange' },
+ green: { path: '03,04,13,19,18,23,17,22,38,41,43,46,45', label: 'Solfors Green' },
+ yellow: { path: '1,2,6,10,15,7,16,17,23,22,27,32,35,37,40,45', label: 'Goodwin A' },
+ purple: { path: '1,3,8,12,11,18,19,28,34,33,38,41,43,46,45', label: 'Goodwin B' },
+ red: { path: '1,2,5,9,14,20,26,21,30,36,39,42,44,45', label: 'Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Джинджер, 10-я
+ "adv_valley_3pl_hard": {
+ default: { path: '1,4,8,13,18,22,26,31,36,40,45,44,43,38,33,28', label: 'Default (Orange)' },
+ blue: { path: '1,3,2,6,11,17,25,30,35,34,29,24,21,17,12,7', label: 'Solfors Blue' },
+ orange: { path: '1,4,8,13,18,22,26,31,36,40,45,44,43,38,33,28', label: 'Solfors Orange' },
+ green: { path: '1,5,9,14,19,23,27,32,37,42,48,51,50,49,46,52', label: 'Solfors Green' },
+ yellow: { path: '', label: 'Goodwin A' },
+ purple: { path: '', label: 'Goodwin B' },
+ red: { path: '', label: 'Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Орион, 11-я
+ "adv_ghirwil_3pl_hard": {
+ default: { path: '1,2,4,6,9,13,18,17,16,22,28,29,30,31,25,19', label: 'Default (Orange)' },
+ blue: { path: '1,2,3,6,8,12,11,15,21,27,36,34,33,35,37', label: 'Solfors Blue' },
+ orange: { path: '1,2,4,6,9,13,18,17,16,22,28,29,30,31,25,19', label: 'Solfors Orange' },
+ green: { path: '1,2,5,6,10,13,14,20,26,32,38,41,40,39,37', label: 'Solfors Green' },
+ yellow: { path: '', label: 'Goodwin A' },
+ purple: { path: '', label: 'Goodwin B' },
+ red: { path: '', label: 'Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Тесак, 12-я
+ "adv_angels_3pl_hard": {
+ default: { path: '08,02,04,07,16,23,32,23,24,17,11,08,01,09,13', label: 'No Wait' },
+ blue: { path: '9,3,6,10,22,31,36,35,29,34,29,30,21,13', label: 'Solfors Blue' },
+ orange: { path: '1,5,12,15,28,20,12,14,26,18,19,20,27', label: 'Solfors Orange' },
+ green: { path: '8,2,4,7,16,23,32,33,25,24,17,11', label: 'Solfors Green' },
+ yellow: { path: '1,2,8,11,7,4,7,16,23,32,33,25,34,29,35,36', label: 'Goodwin A' },
+ purple: { path: '1,3,9,13,10,6,10,22,31,30,21,30,15,28,20,27', label: 'Goodwin B' },
+ red: { path: '1,5,12,14,24,17,24,25,26,18,19,20,27', label: 'Goodwin C' },
+ white: { path: '8,2,4,7,16,23,32,23,24,14,26,25,24,17,11', label: '1 NoWait 1' },
+ black: { path: '9,1,5,12,15,28,29,34,25,26,18,19,20,27', label: '2 NoWait 2' },
+ brown: { path: '3,6,10,22,31,36,31,30,21,13', label: '3 NoWait 3 -easy' }
+ },
+ //Тесак, 13-я map12 (probabilmente hard o superiore)
+ "adv_angels_3pl_hell": {
+ default: { path: '07,02,04,06,16,23,33,23,24,17,11,07,01,09,13', label: 'Default (Orange)' },
+ blue: { path: ' 09,03,05,10,22,31,36,35,29,32,29,30,21,13 ', label: 'Solfors Blue' },
+ orange: { path: ' 08,12,15,28,20,12,14,26,18,19,20,27 ', label: 'Solfors Orange' },
+ green: { path: ' 07,02,04,06,16,23,33,34,25,24,17,11 ', label: 'Solfors Green' },
+ yellow: { path: '1,2,4,6,16,23,33,34,25,32,29,28,20,27', label: '2 - Goodwin A' },
+ purple: { path: '1,7,11,17,24,14,26,18,19,20,27,20,12,8', label: '1 - Goodwin B' },
+ red: { path: '1,9,3,5,10,22,31,36,31,30,15,28,29,30,21,13', label: '3 - Goodwin C' },
+ white: { path: ' 07,02,04,06,16,23,33,23,24,14,26,25,24,17,11 ', label: '1 NoWait 1' },
+ black: { path: ' 09,01,08,12,15,28,29,32,25,26,18,19,20,27 ', label: '2 NoWait 2' },
+ brown: { path: ' 09,03,05,10,22,31,36,35,29,32,29,30,21,13 ', label: '3 NoWait 3' }
+ },
+ //Galhad, 13-a map9 (probabilmente hard o superiore)
+ "adv_strongford_3pl_hell": {
+ default: { path: '1,2,6,12,15,7,16,17,23,22,27,42,34,36,39,44', label: '1 NoWait | Goodwin B' },
+ blue: { path: ' 2,06,12,15,20,14,24,29,25,35,38,41,43 ', label: 'Solfors Blue' },
+ orange: { path: ' 03,08,09,13,07,16,21,26,30,31,42,34,36,39 ', label: 'Solfors Orange' },
+ green: { path: ' 03,04,10,19,18,23,17,22,37,40,32,45 ', label: 'Solfors Green' },
+ yellow: { path: '1,2,5,11,14,20,26,21,30,35,38,41,43,44', label: '2/3 NoWait | Goodwin A' },
+ purple: { path: '1,2,6,12,15,7,16,17,23,22,27,42,34,36,39,44', label: '1 NoWait | Goodwin B' },
+ red: { path: '1,3,8,9,13,18,19,28,0,33,37,40,32,45,44', label: '3/2 NoWait | Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Orion, 13-a mp11 (probabilmente hard o superiore)
+ "adv_ghirwil_3pl_hell": {
+ default: { path: ' 2,4,6,8,12,17,18,19,25,31,30,29,28,22,16 ', label: 'Default (Orange)' },
+ blue: { path: ' 2,3,6,7,12,11,15,21,27,36,39,40,41 ', label: '2/3 Solfors Blue' },
+ orange: { path: ' 2,4,6,8,12,17,18,19,25,31,30,29,28,22,16 ', label: '1 Solfors Orange' },
+ green: { path: ' 2,5,6,9,13,14,20,26,32,38,35,33,34 ', label: '3/2 Solfors Green' },
+ yellow: { path: '1,2,3,6,8,12,11,15,21,27,36,34,33,35,37', label: '2/3 Goodwin A' },
+ purple: { path: '1,2,4,6,9,13,18,17,16,22,28,29,30,31,25,19', label: '1 Goodwin B' },
+ red: { path: '1,2,5,6,10,13,14,20,26,32,38,41,40,39,37', label: '3/2 Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ },
+ //Ginger, 13-a map10 (probabilmente hard o superiore)
+ "adv_valley_3pl_hell": {
+ default: { path: '1,4,8,13,18,22,26,31,36,40,45,44,43,38,33,28', label: '3 Solfors Blue' },
+ blue: { path: '1,3,2,6,11,17,25,30,35,34,29,24,21,17,12,7', label: '3 Solfors Blue' },
+ orange: { path: '1,4,8,13,18,22,26,31,36,40,45,44,43,38,33,28', label: '1 Solfors Orange' },
+ green: { path: '1,5,9,14,19,23,27,32,37,42,48,51,50,49,46,52', label: '2 Solfors Green' },
+ yellow: { path: '', label: 'Goodwin A' },
+ purple: { path: '', label: 'Goodwin B' },
+ red: { path: '', label: 'Goodwin C' },
+ white: { path: '', label: 'NoWait 1' },
+ black: { path: '', label: 'NoWait 2' },
+ brown: { path: '', label: 'NoWait 3' }
+ }
+ },
+ storm: {
+ "tempest_3_3": {
+ blue: { path: '1,2,3,4,5,56,55,53,50,49,48,45,46,43,41,39,38,40,36,35,33,31,29,28,27,25,26,22,21,20,17,18,15,13,10,9,11,7,8', label: 'Path 1' },
+ orange: { path: '1,2,5,4,3,7,9,10,13,11,15,17,20,21,18,22,25,27,28,26,29,31,33,35,36,38,39,41,40,43,45,48,49,46,50,53,55,56,54,52,6,8', label: 'Path 2' },
+ green: { path: '1,2,5,4,3,7,9,10,13,11,15,17,20,21,18,22,25,27,28,26,29,31,33,35,36,38,39,41,40,43,45,48,49,46,50,53,55,56,54,51,47,44,42,37,32,30,24,23,19,16,14,12,8,6,52,57', label: 'Path 3' },
+ black: { path: '8,12,14,16,19,23,24,30,32,37,42,44,47,51,52,6', label: 'Inner 1' },
+ white: { path: '8,6,52,51,47,44,42,37,32,30,24,23,19,16,14,12', label: 'Inner 2' },
+ }
+ }
+ };
+
+ const originalExecuteAdventure = window.HWHClasses.executeAdventure;
+
+ // Cache for user ID to avoid repeated API calls
+ let cachedUserId = null;
+
+ // Helper: Get current user ID (with caching)
+ async function getCurrentUserId() {
+ if (cachedUserId) return cachedUserId;
+ try {
+ const response = await Send(JSON.stringify({
+ calls: [{
+ name: "userGetInfo",
+ args: {},
+ ident: "userGetInfo"
+ }]
+ }));
+ cachedUserId = response.results[0].result.response.id.toString();
+ return cachedUserId;
+ } catch (error) {
+ console.error('Error getting user ID:', error);
+ return null;
+ }
+ }
+
+ // Helper: Get adventure info
+ async function getAdventureInfo() {
+ try {
+ const response = await Send(JSON.stringify({
+ calls: [{
+ name: "adventure_getInfo",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "group_1_body"
+ }]
+ }));
+
+ if (!response?.results?.[0]?.result?.response) {
+ return null;
+ }
+
+ const adventureInfo = response.results[0].result.response;
+ return (adventureInfo.id && adventureInfo.users) ? adventureInfo : null;
+ } catch (error) {
+ return null;
+ }
+ }
+
+ // Helper: Check if reward is already collected
+ function isRewardCollected(rewardsCollected, rewardId) {
+ if (Array.isArray(rewardsCollected)) {
+ return rewardsCollected.includes(rewardId);
+ }
+ return typeof rewardsCollected === 'object' && rewardId in rewardsCollected;
+ }
+
+ // Helper: Calculate total team points from all users
+ function calculateTotalTeamPoints(adventureInfo) {
+ if (!adventureInfo || !adventureInfo.users) {
+ return 0;
+ }
+
+ let totalPoints = 0;
+ for (const [userId, userData] of Object.entries(adventureInfo.users)) {
+ const points = parseInt(userData.points) || 0;
+ totalPoints += points;
+ }
+
+ return totalPoints;
+ }
+
+ // Helper: Check if all available rewards are collected
+ function areAllRewardsCollected(adventureInfo, userId) {
+ if (!adventureInfo || !adventureInfo.users || !adventureInfo.users[userId]) {
+ return false;
+ }
+
+ const userData = adventureInfo.users[userId];
+ const rewards = adventureInfo.rewards;
+ if (!rewards) {
+ return false;
+ }
+
+ const rewardsCollected = userData.rewardsCollected || [];
+ const totalTeamPoints = calculateTotalTeamPoints(adventureInfo);
+
+ // Check all point-based rewards
+ const pointThresholds = Object.keys(rewards.points || {});
+ for (const thresholdKey of pointThresholds) {
+ const threshold = parseInt(thresholdKey);
+ if (totalTeamPoints >= threshold) {
+ if (!isRewardCollected(rewardsCollected, thresholdKey)) {
+ console.log(`Reward at ${thresholdKey} points not yet collected`);
+ return false;
+ }
+ }
+ }
+
+ // Check boss reward
+ const bossRewardsAvailable = rewards.boss && Object.keys(rewards.boss.lootBox || {}).length > 0;
+ if (bossRewardsAvailable && !isRewardCollected(rewardsCollected, 'boss')) {
+ console.log('Boss reward not yet collected');
+ return false;
+ }
+
+ return true;
+ }
+
+ // Collect all available rewards
+ async function collectAllRewards(adventureInfo, userId) {
+ try {
+ // Refresh adventure info at start to get fresh data
+ let currentAdventureInfo = await getAdventureInfo() || adventureInfo;
+ let userData = currentAdventureInfo.users[userId];
+
+ if (!userData) {
+ console.log('User data not found');
+ return;
+ }
+
+ const rewards = currentAdventureInfo.rewards;
+ if (!rewards) {
+ console.log('No rewards available');
+ return;
+ }
+
+ // Collect point-based rewards - use string keys directly from rewards.points
+ const pointThresholds = Object.keys(rewards.points || {})
+ .map(key => ({ key, threshold: parseInt(key) }))
+ .sort((a, b) => a.threshold - b.threshold);
+
+ console.log(`Available point thresholds:`, pointThresholds.map(p => p.key));
+
+ // Calculate total team points from all users
+ let totalTeamPoints = calculateTotalTeamPoints(currentAdventureInfo);
+ console.log(`Total team points: ${totalTeamPoints}`);
+ console.log(`All users points:`, Object.entries(currentAdventureInfo.users).map(([uid, ud]) => ({ userId: uid, points: ud.points })));
+
+ for (const { key, threshold } of pointThresholds) {
+ // Get fresh adventure info for each check
+ currentAdventureInfo = await getAdventureInfo() || currentAdventureInfo;
+ if (!currentAdventureInfo || !currentAdventureInfo.users[userId]) {
+ console.error('Failed to get adventure info');
+ break;
+ }
+
+ // Recalculate total team points with fresh data
+ totalTeamPoints = calculateTotalTeamPoints(currentAdventureInfo);
+ userData = currentAdventureInfo.users[userId];
+ const rewardsCollected = userData.rewardsCollected || [];
+
+ console.log(`Checking reward ${key}: Total team points = ${totalTeamPoints}, Threshold = ${threshold}, Already collected = ${isRewardCollected(rewardsCollected, key)}`);
+
+ if (totalTeamPoints >= threshold) {
+ if (!isRewardCollected(rewardsCollected, key)) {
+ console.log(`✓ Collecting point reward at threshold ${key} (${threshold} points)...`);
+ setProgress(`Collecting reward at ${key} points...`, false);
+
+ try {
+ const response = await Send(JSON.stringify({
+ calls: [{
+ name: "adventure_collectReward",
+ args: { rewardId: key },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+ }));
+
+ if (response.error) {
+ console.error(`Error collecting reward at ${key}:`, response.error);
+ } else {
+ console.log(`✓ Reward at ${key} points collected successfully`);
+ }
+
+ // Refresh adventure info after collecting to get updated state
+ await new Promise(resolve => setTimeout(resolve, REWARD_COLLECTION_DELAY));
+ } catch (error) {
+ console.error(`Error collecting reward at ${key}:`, error);
+ }
+ } else {
+ console.log(`Reward at ${key} points already collected`);
+ }
+ } else {
+ console.log(`⚠ Not enough points for reward at ${key} (have ${totalTeamPoints} total team points, need ${threshold})`);
+ }
+ }
+
+ // Refresh one more time before checking boss reward
+ currentAdventureInfo = await getAdventureInfo() || currentAdventureInfo;
+ if (currentAdventureInfo && currentAdventureInfo.users[userId]) {
+ userData = currentAdventureInfo.users[userId];
+ }
+ const rewardsCollected = userData.rewardsCollected || [];
+
+ // Collect boss reward if available
+ const bossRewardsAvailable = rewards.boss && Object.keys(rewards.boss.lootBox || {}).length > 0;
+ if (bossRewardsAvailable && !isRewardCollected(rewardsCollected, 'boss')) {
+ console.log('Collecting boss reward...');
+ setProgress('Collecting boss reward...', false);
+
+ try {
+ const response = await Send(JSON.stringify({
+ calls: [{
+ name: "adventure_collectReward",
+ args: { rewardId: "boss" },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+ }));
+
+ if (response.error) {
+ console.error('Error collecting boss reward:', response.error);
+ } else {
+ console.log('✓ Boss reward collected successfully');
+ }
+ } catch (error) {
+ console.error('Error collecting boss reward:', error);
+ }
+ } else if (bossRewardsAvailable) {
+ console.log('Boss reward already collected');
+ }
+
+ setProgress('All rewards collected', false);
+ } catch (error) {
+ console.error('Error collecting rewards:', error);
+ throw error;
+ }
+ }
+
+ // Check adventure status and other players
+ async function checkAdventureStatus() {
+ const adventureInfo = await getAdventureInfo();
+ if (!adventureInfo) {
+ return { hasActive: false, otherPlayersLeft: false, adventureInfo: null };
+ }
+
+ const currentUserId = await getCurrentUserId();
+ if (!currentUserId) {
+ return { hasActive: true, otherPlayersLeft: false, adventureInfo };
+ }
+
+ const users = adventureInfo.users;
+ let otherPlayersLeftCount = 0;
+ let totalOtherPlayers = 0;
+
+ for (const [userId, userData] of Object.entries(users)) {
+ if (userId !== currentUserId) {
+ totalOtherPlayers++;
+ if (userData.left === true) {
+ otherPlayersLeftCount++;
+ }
+ console.log(`User ${userId}: left=${userData.left}, points=${userData.points}`);
+ }
+ }
+
+ // If there are exactly 2 other players and both have left, we should end the adventure
+ const otherPlayersLeft = totalOtherPlayers === 2 && otherPlayersLeftCount === 2;
+ console.log(`Adventure status check: totalOtherPlayers=${totalOtherPlayers}, otherPlayersLeftCount=${otherPlayersLeftCount}, otherPlayersLeft=${otherPlayersLeft}`);
+
+ return {
+ hasActive: true,
+ otherPlayersLeft,
+ adventureInfo,
+ currentUserId
+ };
+ }
+
+ // End adventure
+ async function endAdventure(forceEnd = false) {
+ try {
+ const currentUserId = await getCurrentUserId();
+ if (!currentUserId) {
+ throw new Error('Could not get current user ID');
+ }
+
+ // If forceEnd is true (other players left), try to collect rewards once but don't retry
+ if (forceEnd) {
+ console.log('Other players have left - collecting available rewards before ending...');
+ setProgress('Collecting available rewards before ending adventure...', false);
+
+ let adventureInfo = await getAdventureInfo();
+ if (adventureInfo && adventureInfo.users[currentUserId]) {
+ await collectAllRewards(adventureInfo, currentUserId);
+ await new Promise(resolve => setTimeout(resolve, 500));
+ }
+
+ console.log('Proceeding to end adventure (other players left)');
+ } else {
+ // Collect all rewards with retry mechanism (normal case)
+ let allRewardsCollected = false;
+ let attempts = 0;
+ const maxAttempts = 3;
+
+ while (!allRewardsCollected && attempts < maxAttempts) {
+ attempts++;
+ setProgress(`Collecting rewards before ending adventure (attempt ${attempts}/${maxAttempts})...`, false);
+
+ let adventureInfo = await getAdventureInfo();
+ if (!adventureInfo || !adventureInfo.users[currentUserId]) {
+ throw new Error('Could not get adventure info');
+ }
+
+ // Collect all available rewards
+ await collectAllRewards(adventureInfo, currentUserId);
+
+ // Wait a bit for state to update
+ await new Promise(resolve => setTimeout(resolve, 500));
+
+ // Verify all rewards are collected
+ adventureInfo = await getAdventureInfo();
+ if (adventureInfo && adventureInfo.users[currentUserId]) {
+ allRewardsCollected = areAllRewardsCollected(adventureInfo, currentUserId);
+
+ if (allRewardsCollected) {
+ console.log('✓ All rewards collected successfully');
+ } else {
+ console.log(`⚠ Not all rewards collected yet, retrying... (attempt ${attempts}/${maxAttempts})`);
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ }
+ }
+ }
+
+ if (!allRewardsCollected) {
+ console.warn('⚠ Not all rewards were collected, but proceeding to end adventure');
+ }
+ }
+
+ setProgress('Ending adventure...', false);
+ const response = await Send(JSON.stringify({
+ calls: [{
+ name: "adventure_end",
+ args: { isFinished: true },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+ }));
+
+ // Check for errors in the response structure
+ if (response.results?.[0]?.result?.error) {
+ const error = response.results[0].result.error;
+ throw new Error(`Failed to end adventure: ${error.description || error.name || 'Unknown error'}`);
+ }
+
+ // Verify adventure was actually ended by checking status
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ const verifyStatus = await checkAdventureStatus();
+ if (verifyStatus.hasActive) {
+ console.warn('⚠ Adventure still appears active after end call, retrying...');
+ // Retry ending the adventure once
+ const retryResponse = await Send(JSON.stringify({
+ calls: [{
+ name: "adventure_end",
+ args: { isFinished: true },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+ }));
+
+ if (retryResponse.results?.[0]?.result?.error) {
+ const error = retryResponse.results[0].result.error;
+ throw new Error(`Failed to end adventure on retry: ${error.description || error.name || 'Unknown error'}`);
+ }
+
+ // Wait and verify again
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ const finalStatus = await checkAdventureStatus();
+ if (finalStatus.hasActive) {
+ throw new Error('Adventure still active after end attempts');
+ }
+ }
+
+ console.log('✓ Adventure ended successfully');
+ return response;
+ } catch (error) {
+ console.error('Error ending adventure:', error);
+ throw error;
+ }
+ }
+
+ // Get portal charge amount
+ async function getPortalCharge() {
+ try {
+ const response = await Send(JSON.stringify({
+ calls: [{
+ name: "userGetInfo",
+ args: {},
+ ident: "userGetInfo"
+ }]
+ }));
+ const userInfo = response.results[0].result.response;
+ const portalSphere = userInfo.refillable.find(n => n.id == PORTAL_SPHERE_ID);
+ return portalSphere ? portalSphere.amount : 0;
+ } catch (error) {
+ console.error('Error getting portal charge:', error);
+ return 0;
+ }
+ }
+
+ // Check if adventure can be raided
+ async function canRaidAdventure() {
+ try {
+ const calls = [
+ { name: "userGetInfo", args: {}, ident: "userGetInfo" },
+ { name: "adventure_raidGetInfo", args: {}, ident: "adventure_raidGetInfo" }
+ ];
+ const result = await Send(JSON.stringify({ calls }))
+ .then(e => e.results.map(n => n.result.response));
+
+ const portalSphere = result[0].refillable.find(n => n.id == PORTAL_SPHERE_ID);
+ const adventureRaid = Object.entries(result[1].raid).filter(e => e[1]).pop();
+ const adventureId = adventureRaid ? adventureRaid[0] : 0;
+
+ if (!portalSphere?.amount || !adventureId) {
+ return { canRaid: false, adventureId: 0, maxCount: 0 };
+ }
+
+ return {
+ canRaid: true,
+ adventureId: parseInt(adventureId),
+ maxCount: portalSphere.amount
+ };
+ } catch (error) {
+ console.error('Error checking raid availability:', error);
+ return { canRaid: false, adventureId: 0, maxCount: 0 };
+ }
+ }
+
+ // Perform adventure raid
+ async function raidAdventure(adventureId, maxCount) {
+ try {
+ const resultRaid = await Send(JSON.stringify({
+ calls: [...Array(maxCount)].map((e, i) => ({
+ name: "adventure_raid",
+ args: { adventureId },
+ ident: `body_${i}`
+ }))
+ })).then(e => e.results.map(n => n.result.response));
+
+ if (!resultRaid.length) {
+ throw new Error('Raid failed - no results');
+ }
+
+ console.log(`Raid completed: ${resultRaid.length} times for adventure ${adventureId}`);
+ return resultRaid;
+ } catch (error) {
+ console.error('Error performing raid:', error);
+ throw error;
+ }
+ }
+
+ // Start new adventure
+ async function startNewAdventure(adventureId) {
+ try {
+ const response = await Send(JSON.stringify({
+ calls: [{
+ name: "adventure_start",
+ args: {
+ adventureId: parseInt(adventureId),
+ private: false,
+ isClan: true
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+ }));
+
+ if (response.error) {
+ throw new Error(`Failed to start adventure: ${response.error.description || response.error.name}`);
+ }
+
+ console.log(`Adventure ${adventureId} started successfully`);
+ return response;
+ } catch (error) {
+ console.error('Error starting adventure:', error);
+ throw error;
+ }
+ }
+
+ // Run adventure with default path
+ async function runAdventureWithDefaultPath() {
+ try {
+ const adventureInfo = await getAdventureInfo();
+ if (!adventureInfo) {
+ throw new Error('Could not get adventure info');
+ }
+
+ const mapIdent = adventureInfo.mapIdent;
+ if (!mapIdent) {
+ throw new Error('Could not get map identifier');
+ }
+
+ const currentAdventureWays = defaultWays.adventure[mapIdent];
+ if (!currentAdventureWays?.default?.path) {
+ throw new Error(`No default path found for map: ${mapIdent}`);
+ }
+
+ const defaultPathStr = currentAdventureWays.default.path.trim();
+ if (!defaultPathStr) {
+ throw new Error(`Default path is empty for map: ${mapIdent}`);
+ }
+
+ const path = defaultPathStr.split(',')
+ .map(p => p.trim())
+ .filter(p => p.length > 0)
+ .map(p => parseInt(p))
+ .filter(p => !isNaN(p));
+
+ if (path.length < 2) {
+ throw new Error(`Invalid default path: ${defaultPathStr}`);
+ }
+
+ console.log(`Using default path for ${mapIdent}:`, path);
+
+ class AutoDefaultAdventure extends ExtCombinedAdventureStorm {
+ async getPath() {
+ return path;
+ }
+ }
+
+ return new Promise((resolve, reject) => {
+ const adventure = new AutoDefaultAdventure(resolve, reject);
+ adventure.start('default').catch(reject);
+ });
+ } catch (error) {
+ console.error('Error running adventure with default path:', error);
+ throw error;
+ }
+ }
+
+ // Function to start adventure with level input
+ async function startAdventureWithLevel() {
+ try {
+ const adventureStatus = await checkAdventureStatus();
+
+ if (adventureStatus.hasActive) {
+ // Collect rewards if adventure is active
+ if (adventureStatus.adventureInfo && adventureStatus.currentUserId) {
+ await collectAllRewards(adventureStatus.adventureInfo, adventureStatus.currentUserId);
+ }
+
+ if (adventureStatus.otherPlayersLeft) {
+ console.log('%cAdventure active with other 2 players left. Ending adventure...', 'color: orange');
+ setProgress('Other players left. Ending adventure...', false);
+ await endAdventure(true); // Force end since other players left
+ console.log('%cAdventure ended', 'color: green');
+ setProgress('Adventure ended', true);
+ return;
+ } else {
+ await popup.confirm('You are already on an adventure. Please complete it first.', [
+ { msg: 'OK', result: true, color: 'green' }
+ ]);
+ return;
+ }
+ }
+
+ // Check portal charges
+ const portalCharge = await getPortalCharge();
+ if (portalCharge === 0) {
+ await popup.confirm('No portal charges available.', [
+ { msg: 'OK', result: true, color: 'green' }
+ ]);
+ return;
+ }
+
+ // Create popup message with info
+ const savedLevel = getSaveVal('adventureId', 13);
+ const popupMessage = `
+
+
Portal Charges: ${portalCharge}
+
Note: Level will be saved and adventure will start with default path automatically.
+
Enter Adventure Level (1-13):
+
+ `;
+
+ const answer = await popup.confirm(popupMessage, [
+ {
+ msg: 'Start Adventure',
+ isInput: true,
+ placeholder: 'Enter level (1-13)',
+ default: savedLevel.toString(),
+ color: 'green'
+ },
+ { msg: I18N('BTN_CANCEL'), result: false, isCancel: true, color: 'red' }
+ ]);
+
+ if (!answer) {
+ return;
+ }
+
+ // Validate and save adventure level
+ const newAdventureId = parseInt(answer) || 13;
+ if (newAdventureId < 1 || newAdventureId > 13) {
+ await popup.confirm('Invalid adventure level. Must be between 1 and 13.', [
+ { msg: 'OK', result: true, color: 'green' }
+ ]);
+ return;
+ }
+
+ setSaveVal('adventureId', newAdventureId);
+ console.log(`Adventure level saved: ${newAdventureId}`);
+
+ // Check again if user started an adventure while popup was open
+ const hasActiveNow = (await checkAdventureStatus()).hasActive;
+ if (hasActiveNow) {
+ await popup.confirm('An adventure was already started. Please complete it first.', [
+ { msg: 'OK', result: true, color: 'green' }
+ ]);
+ return;
+ }
+
+ // Start adventure and run default path
+ setProgress(`Starting adventure ${newAdventureId}...`, false);
+ await startNewAdventure(newAdventureId);
+ await new Promise(resolve => setTimeout(resolve, 1000));
+
+ setProgress('Running adventure with default path...', false);
+ await runAdventureWithDefaultPath();
+ setProgress('Adventure started and running', true);
+ } catch (error) {
+ console.error('Error starting adventure:', error);
+ setProgress(`Error: ${error.message}`, true);
+ }
+ }
+
+ // Auto-execute adventure raid or start logic
+ async function autoAdventureRaidOrStart() {
+ try {
+ setProgress('Checking adventure raid availability...', false);
+
+ // Check if can raid adventure
+ const canRaid = await canRaidAdventure();
+ if (canRaid.canRaid) {
+ console.log(`%cCan raid adventure ${canRaid.adventureId}`, 'color: green');
+ setProgress(`Raid available for adventure ${canRaid.adventureId}. Raiding...`, false);
+ await raidAdventure(canRaid.adventureId, canRaid.maxCount);
+ setProgress(`Raid completed ${canRaid.maxCount} times`, true);
+ return;
+ }
+
+ // Check adventure status and portal charges
+ setProgress('Checking portal charges and adventure status...', false);
+ const [portalCharge, adventureStatus] = await Promise.all([
+ getPortalCharge(),
+ checkAdventureStatus()
+ ]);
+
+ // Collect rewards if adventure is active
+ if (adventureStatus.hasActive && adventureStatus.adventureInfo && adventureStatus.currentUserId) {
+ await collectAllRewards(adventureStatus.adventureInfo, adventureStatus.currentUserId);
+ }
+
+ // If other players left, end adventure
+ if (adventureStatus.hasActive && adventureStatus.otherPlayersLeft) {
+ console.log('%cAdventure active with other 2 players left. Ending adventure...', 'color: orange');
+ setProgress('Other players left. Ending adventure...', false);
+ try {
+ await endAdventure(true); // Force end since other players left
+ console.log('%cAdventure ended successfully', 'color: green');
+ setProgress('Adventure ended successfully', true);
+ } catch (endError) {
+ console.error('%cFailed to end adventure:', 'color: red', endError);
+ setProgress(`Failed to end adventure: ${endError.message}`, true);
+ throw endError; // Re-throw to be caught by outer try-catch
+ }
+ return;
+ }
+
+ // Start new adventure if portal charges available
+ if (portalCharge > 0 && !adventureStatus.hasActive) {
+ console.log(`%cPortal charges available (${portalCharge}) and no active adventure. Starting new adventure...`, 'color: green');
+ const adventureId = getSaveVal('adventureId', 13);
+ setProgress(`Starting adventure ${adventureId}...`, false);
+ await startNewAdventure(adventureId);
+ await new Promise(resolve => setTimeout(resolve, 1000));
+
+ setProgress('Running adventure with default path...', false);
+ await runAdventureWithDefaultPath();
+ setProgress('Adventure started and running', true);
+ } else {
+ if (portalCharge === 0) {
+ console.log('%cNo portal charges available', 'color: yellow');
+ setProgress('No portal charges available', true);
+ } else if (adventureStatus.hasActive) {
+ console.log('%cAdventure already active', 'color: yellow');
+ setProgress('Adventure already active', true);
+ }
+ }
+ } catch (error) {
+ console.error('Auto adventure raid/start error:', error);
+ setProgress(`Error: ${error.message}`, true);
+ }
+ }
+
+ // Extended executeAdventure class with path selection
+ class ExtCombinedAdventureStorm extends originalExecuteAdventure {
+ async getPath() {
+ console.log(`Current adventure type: ${this.type}, Map Identifier: ${this.mapIdent}`);
+
+ const adventureTypeKey = this.type === 'solo' ? 'storm' : 'adventure';
+ const currentAdventureWays = defaultWays[adventureTypeKey]?.[this.mapIdent];
+
+ const oldVal = getSaveVal('adventurePath', '');
+ const keyPath = `adventurePath:${this.mapIdent}`;
+ const popupButtons = [];
+
+ if (currentAdventureWays) {
+ // Add default path first if available
+ if (currentAdventureWays.default?.path?.trim()) {
+ const defaultPath = currentAdventureWays.default.path.trim();
+ popupButtons.push({
+ msg: `⭐ ${currentAdventureWays.default.label} | ${defaultPath}`,
+ result: defaultPath
+ });
+ }
+
+ // Add other color paths
+ ORDERED_COLORS.forEach((color) => {
+ const pathData = currentAdventureWays[color];
+ if (pathData?.path?.trim()) {
+ popupButtons.push({
+ msg: `${COLOR_EMOJIS[color] || '⚪'} ${pathData.label} | ${pathData.path}`,
+ result: pathData.path
+ });
+ }
+ });
+ } else {
+ console.log(`%cNo predefined paths for ${adventureTypeKey} map: ${this.mapIdent}`, 'color: yellow');
+ }
+
+ // Add input button at the end
+ popupButtons.push({
+ msg: I18N('START_ADVENTURE'),
+ placeholder: 'Click a path above or enter your own',
+ isInput: true,
+ default: getSaveVal(keyPath, oldVal),
+ color: 'green'
+ });
+
+ // Add cancel button
+ popupButtons.push({
+ msg: I18N('BTN_CANCEL'),
+ result: false,
+ isCancel: true,
+ color: 'red'
+ });
+
+ let answer = await popup.confirm('SELECT A PREDEFINED PATH OR ENTER A CUSTOM ONE', popupButtons);
+
+ if (!answer) {
+ this.terminatеReason = I18N('BTN_CANCELED');
+ return false;
+ }
+
+ // If answer is a predefined path, show confirmation popup
+ if (typeof answer === 'string' && answer.length > 0) {
+ const isPredefinedPath = answer.includes(',') && /^[\d,\s]+$/.test(answer.replace(/\s/g, ''));
+
+ if (isPredefinedPath) {
+ const confirmButtons = [
+ {
+ msg: I18N('START_ADVENTURE'),
+ placeholder: 'Review path or modify',
+ isInput: true,
+ default: answer,
+ color: 'green'
+ },
+ {
+ msg: I18N('BTN_CANCEL'),
+ result: false,
+ isCancel: true,
+ color: 'red'
+ }
+ ];
+ const confirmedAnswer = await popup.confirm('REVIEW AND CONFIRM PATH', confirmButtons);
+ if (!confirmedAnswer) {
+ this.terminatеReason = I18N('BTN_CANCELED');
+ return false;
+ }
+ answer = confirmedAnswer;
+ }
+ }
+
+ // Parse path
+ let path = answer.split(',');
+ if (path.length < 2) path = answer.split('-');
+ if (path.length < 2) {
+ this.terminatеReason = I18N('MUST_TWO_POINTS');
+ return false;
+ }
+
+ for (let p in path) {
+ path[p] = +path[p].trim();
+ if (Number.isNaN(path[p])) {
+ this.terminatеReason = I18N('MUST_ONLY_NUMBERS');
+ return false;
+ }
+ }
+
+ if (!this.checkPath(path)) {
+ return false;
+ }
+
+ setSaveVal(keyPath, answer);
+ return path;
+ }
+ }
+
+ window.HWHClasses.executeAdventure = ExtCombinedAdventureStorm;
+
+ // Add menu button for starting adventure
+ const { ScriptMenu } = window.HWHClasses;
+ const scriptMenu = ScriptMenu.getInst();
+ scriptMenu.addCombinedButton([
+ {
+ name: '🚀 Start Adventure',
+ title: 'Start adventure with level input (when not on adventure)',
+ onClick: startAdventureWithLevel,
+ color: 'green'
+ }
+ ]);
+
+ // Auto-execute on initialization
+ autoAdventureRaidOrStart().catch(error => {
+ console.error('Auto adventure raid/start failed:', error);
+ });
+ }
+})();
+
diff --git a/AutoBattle HwH Ext.user.js b/AutoBattle HwH Ext.user.js
new file mode 100644
index 0000000..e549dda
--- /dev/null
+++ b/AutoBattle HwH Ext.user.js
@@ -0,0 +1,4071 @@
+// ==UserScript==
+// @name AutoBattle HwH Ext
+// @namespace HeroWarsHelper.AutoBattle
+// @version 1.1
+// @description Auto-execute Arena, Grand Arena, Guild War attacks, and Raid Nodes on script load
+// @author YourName
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @grant none
+// @run-at document-end
+// @downloadURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/AutoBattle%20HwH%20Ext.user.js
+// @updateURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/AutoBattle%20HwH%20Ext.user.js
+// ==/UserScript==
+
+(function() {
+ 'use strict';
+
+ const waitForHWH = setInterval(() => {
+ if (window.HWHClasses && window.HWHClasses.ScriptMenu && window.lib && window.cheats) {
+ const scriptMenu = window.HWHClasses.ScriptMenu.getInst();
+ if (scriptMenu && scriptMenu.mainMenu) {
+ clearInterval(waitForHWH);
+ initializeExtension();
+ }
+ }
+ }, 200);
+
+ function initializeExtension() {
+ console.log('AutoBattle: HWH UI is ready, initializing extension...');
+
+ const { HWHClasses, HWHFuncs, Send, cheats } = window;
+
+ // Helper function to get battle 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':
+ case 'adventure':
+ case 'clan_global_pvp':
+ case 'epic_brawl':
+ case 'clan_pvp':
+ return 'get_clanPvp';
+ case 'dungeon_titan':
+ case 'titan_tower':
+ return 'get_titan';
+ case 'tower':
+ case 'clan_dungeon':
+ return 'get_tower';
+ case 'pve':
+ case 'mission':
+ return 'get_pve';
+ case 'mission_boss':
+ return 'get_missionBoss';
+ case 'challenge':
+ case 'pvp_manual':
+ return 'get_pvpManual';
+ case 'grand':
+ case 'arena':
+ case 'pvp':
+ case 'clan_domination':
+ return 'get_pvp';
+ case 'core':
+ return 'get_core';
+ default: {
+ if (strBattleType.includes('invasion')) {
+ return 'get_invasion';
+ }
+ if (strBattleType.includes('boss')) {
+ return 'get_boss';
+ }
+ if (strBattleType.includes('titan_arena')) {
+ return 'get_titanPvpManual';
+ }
+ return 'get_clanPvp';
+ }
+ }
+ }
+
+ // Helper function to access I18N (translation)
+ function I18N(constant, replace) {
+ // Map of constants that might not exist in I18N - use fallbacks directly
+ const fallbacks = {
+ 'ARENA': 'Arena',
+ 'GRAND_ARENA': 'Grand Arena',
+ 'GUILD_WAR': 'Guild War',
+ 'MINION_RAID': 'Minion Raid',
+ 'INITIALIZING': 'Initializing',
+ 'BATTLE': 'Battle',
+ 'COMPLETED': 'Completed',
+ 'BATTLES_CANCELED': 'Battles Canceled',
+ 'REMAINING_ATTEMPTS': 'Remaining Attempts',
+ 'TITAN_ARENA': 'Titan Arena'
+ };
+
+ // If we have a fallback for this constant, use it directly to avoid I18N warnings
+ if (fallbacks.hasOwnProperty(constant)) {
+ let result = fallbacks[constant];
+ if (replace) {
+ for (const key in replace) {
+ result = result.replace(`{${key}}`, replace[key]);
+ }
+ }
+ return result;
+ }
+
+ // For other constants, try to use window.I18N if available
+ if (window.I18N && typeof window.I18N === 'function') {
+ try {
+ const result = window.I18N(constant, replace);
+ // If I18N returns the constant name unchanged (meaning it wasn't found), use fallback
+ if (result === constant && fallbacks[constant]) {
+ return fallbacks[constant];
+ }
+ return result;
+ } catch (error) {
+ // If translation constant not found, fall back to constant name or English defaults
+ return fallbacks[constant] || constant;
+ }
+ }
+
+ // Final fallback
+ let result = fallbacks[constant] || constant;
+ if (replace) {
+ for (const key in replace) {
+ result = result.replace(`{${key}}`, replace[key]);
+ }
+ }
+ return result;
+ }
+
+ // Helper function to access getUserInfo
+ function getUserInfo() {
+ if (window.getUserInfo && typeof window.getUserInfo === 'function') {
+ return window.getUserInfo();
+ }
+ return {};
+ }
+
+ // Helper function for setIsCancalBattle
+ function setIsCancalBattle(value) {
+ if (window.setIsCancalBattle && typeof window.setIsCancalBattle === 'function') {
+ window.setIsCancalBattle(value);
+ }
+ }
+
+ // Helper function for setProgress
+ function setProgress(text, hide) {
+ HWHFuncs.setProgress(text, hide);
+ }
+
+ // Helper function for random
+ function random(min, max) {
+ return Math.floor(Math.random() * (max - min + 1) + min);
+ }
+
+ // Helper function for Send (used in raid nodes)
+ function SendRequest(json, callback) {
+ if (typeof Send === 'function') {
+ Send(json).then(result => {
+ if (callback) callback(result);
+ }).catch(error => {
+ if (callback) callback({ error: error });
+ });
+ } else {
+ console.error('Send function not available');
+ if (callback) callback({ error: 'Send function not available' });
+ }
+ }
+
+ // BattleCalc from cheats
+ const BattleCalc = cheats.BattleCalc;
+
+ // ========== CONSTANTS ==========
+ const CONSTANTS = {
+ WIN_RATE_THRESHOLD: 70,
+ SIMULATION_COUNT: 10,
+ BATTLE_VERSION: 273,
+ DELAY_BETWEEN_BATTLES: 1000,
+ DELAY_BATTLE_COMPLETE: 100,
+ ARENA_ATTEMPTS_REFILLABLE_ID: 6,
+ GRAND_ARENA_ATTEMPTS_REFILLABLE_ID: 21,
+ DEFAULT_PET_ID: 6005,
+ PET_ID_RANGE_MIN: 6000,
+ PET_ID_RANGE_MAX: 7000,
+ DAYS: {
+ SUNDAY: 0,
+ MONDAY: 1,
+ SATURDAY: 6
+ }
+ };
+
+ // ========== UTILITY FUNCTIONS ==========
+ const Utils = {
+ // Cached date for day checks (updated once per execution)
+ currentDate: new Date(),
+
+ getDayOfWeek: function() {
+ return this.currentDate.getDay();
+ },
+
+ isTitanArenaDay: function() {
+ const day = this.getDayOfWeek();
+ return day >= CONSTANTS.DAYS.MONDAY && day <= CONSTANTS.DAYS.SATURDAY;
+ },
+
+ isRaidBossDay: function() {
+ const day = this.getDayOfWeek();
+ return day === CONSTANTS.DAYS.SUNDAY || day === CONSTANTS.DAYS.SATURDAY;
+ },
+
+ getDayName: function(dayOfWeek) {
+ return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][dayOfWeek];
+ },
+
+ // Optimized logging - can be disabled in production
+ log: function(level, ...args) {
+ if (window.DEBUG !== false) {
+ console[level](...args);
+ }
+ },
+
+ // Create action timestamp (called per API request for uniqueness)
+ getActionTs: function() {
+ return Date.now();
+ },
+
+ // Validate battle result
+ isValidBattleResult: function(result) {
+ return result && result.result && typeof result.result.win === 'boolean';
+ }
+ };
+
+ // ========== EXECUTE ARENA CLASS ==========
+ function executeArena(resolve, reject) {
+ this.resolve = resolve;
+ this.reject = reject;
+ this.arenaType = 'arena';
+ this.attemptsRemaining = 0;
+ this.victories = 0;
+ this.arenaInfo = null;
+ this.teamInfo = null;
+ this.opponents = [];
+ this.myUserId = null;
+ this.myClanId = null;
+ this.allyUserIds = new Set();
+
+ this.start = async function(arenaType = 'arena') {
+ this.arenaType = arenaType;
+ const arenaName = this.arenaType === 'grand' ? 'Grand Arena' : 'Arena';
+ setProgress(`${arenaName}: Initializing...`);
+
+ try {
+ // Get arena status and team data
+ await this.getArenaStatus();
+
+ if (this.attemptsRemaining <= 0) {
+ if (this.arenaInfo && this.arenaInfo.status === 'peace_time') {
+ this.end('Arena is in peace time - no battles available');
+ } else if (this.arenaInfo && this.arenaInfo.status === 'disabled') {
+ this.end('Arena is disabled - no battles available');
+ } else if (this.arenaInfo && this.arenaInfo.status === 'error') {
+ const errorMsg = this.arenaInfo.errorMessage || 'Arena API error - no battles available';
+ this.end(errorMsg);
+ } else {
+ this.end('No attempts remaining');
+ }
+ return;
+ }
+
+ await this.getAvailableTeams();
+ await this.loadAllyUserIds();
+
+ // Get detailed opponent information
+ const detailedOpponents = await this.getArenaOpponents();
+ if (detailedOpponents && (detailedOpponents.array || detailedOpponents.map)) {
+ // Store both map and array to preserve API order
+ this.opponentsData = detailedOpponents;
+ } else if (detailedOpponents && typeof detailedOpponents === 'object' && Object.keys(detailedOpponents).length > 0) {
+ // Fallback: old format (just map)
+ this.opponents = detailedOpponents;
+ }
+
+ // Process opponents in API order (one by one, no sorting)
+ this.findEasiestOpponents();
+
+ // Execute battles
+ await this.executeBattles();
+
+ } catch (error) {
+ console.error('Arena execution error:', error);
+ this.end('Error: ' + error.message);
+ }
+ }
+
+ this.getArenaStatus = async function() {
+ try {
+ const calls = [{
+ name: "userGetInfo",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "body"
+ }];
+
+ const response = await Send(JSON.stringify({calls}));
+ console.log('User info response:', response);
+
+ if (response && response.results && response.results[0] && response.results[0].result) {
+ const userInfo = response.results[0].result.response;
+ console.log('User info:', userInfo);
+
+ this.myUserId = userInfo.userId != null ? String(userInfo.userId) : null;
+ this.myClanId = userInfo.clanId != null ? String(userInfo.clanId) : null;
+
+ if (this.arenaType === 'grand') {
+ // Grand Arena attempts are stored in refillable array with id: 21
+ const grandAttemptsItem = userInfo.refillable ? userInfo.refillable.find(r => r.id === CONSTANTS.GRAND_ARENA_ATTEMPTS_REFILLABLE_ID) : null;
+ const grandAttempts = grandAttemptsItem ? grandAttemptsItem.amount : 0;
+
+ this.arenaInfo = {
+ attempts: grandAttempts,
+ rank: userInfo.grandPlace || 1000,
+ status: grandAttempts > 0 ? 'active' : 'no_attempts',
+ rivals: [],
+ canUpdateDefenders: false,
+ battleStartTs: 0
+ };
+ this.attemptsRemaining = grandAttempts;
+
+ if (grandAttempts <= 0) {
+ setProgress(`Grand Arena: No attempts remaining (${grandAttempts})`);
+ return;
+ }
+
+ setProgress(`Grand Arena: ${grandAttempts} attempts available`);
+ return;
+ } else {
+ // Arena attempts are stored in refillable array with id: 6
+ const arenaAttemptsItem = userInfo.refillable ? userInfo.refillable.find(r => r.id === CONSTANTS.ARENA_ATTEMPTS_REFILLABLE_ID) : null;
+ const arenaAttempts = arenaAttemptsItem ? arenaAttemptsItem.amount : 0;
+
+ this.arenaInfo = {
+ attempts: arenaAttempts,
+ rank: userInfo.arenaPlace || 1000,
+ status: arenaAttempts > 0 ? 'active' : 'no_attempts',
+ rivals: [],
+ canUpdateDefenders: false,
+ battleStartTs: 0
+ };
+ this.attemptsRemaining = arenaAttempts;
+
+ if (arenaAttempts <= 0) {
+ setProgress(`Arena: No attempts remaining (${arenaAttempts})`);
+ return;
+ }
+
+ setProgress(`Arena: ${arenaAttempts} attempts available`);
+ return;
+ }
+ }
+ } catch (error) {
+ console.log('Could not get user info, using fallback:', error);
+ }
+
+ // Fallback to placeholder data
+ console.log(`${this.arenaType === 'grand' ? 'Grand Arena' : 'Arena'} GetInfo API not available, using alternative approach`);
+ this.arenaInfo = {
+ attempts: 1,
+ rank: 1000,
+ status: 'active',
+ rivals: [],
+ canUpdateDefenders: false,
+ battleStartTs: 0
+ };
+ this.attemptsRemaining = 1;
+ this.opponents = [];
+ const arenaName = this.arenaType === 'grand' ? 'Grand Arena' : 'Arena';
+ setProgress(`${arenaName}: Initializing...`);
+ return;
+ }
+
+ this.refreshOpponents = async function() {
+ const detailedOpponents = await this.getArenaOpponents();
+ if (detailedOpponents && (detailedOpponents.array || detailedOpponents.map)) {
+ this.opponentsData = detailedOpponents;
+ } else if (detailedOpponents && typeof detailedOpponents === 'object' && Object.keys(detailedOpponents).length > 0) {
+ this.opponents = detailedOpponents;
+ }
+ this.findEasiestOpponents();
+ }
+
+ this.getAvailableTeams = async function() {
+ const calls = [{
+ name: "teamGetAll",
+ args: {},
+ ident: "teamGetAll"
+ }, {
+ name: "teamGetFavor",
+ args: {},
+ ident: "teamGetFavor"
+ }, {
+ name: "heroGetAll",
+ args: {},
+ ident: "heroGetAll"
+ }];
+
+ const response = await Send(JSON.stringify({calls}));
+ console.log('Team API response:', response);
+
+ if (!response || !response.results || response.results.length < 3) {
+ throw new Error('Invalid team API response structure');
+ }
+
+ if (!response.results[0] || !response.results[0].result || !response.results[0].result.response) {
+ throw new Error('Invalid teamGetAll response');
+ }
+ if (!response.results[1] || !response.results[1].result || !response.results[1].result.response) {
+ throw new Error('Invalid teamGetFavor response');
+ }
+ if (!response.results[2] || !response.results[2].result || !response.results[2].result.response) {
+ throw new Error('Invalid heroGetAll response');
+ }
+
+ this.teamInfo = {
+ teams: response.results[0].result.response,
+ favor: response.results[1].result.response,
+ heroes: Object.values(response.results[2].result.response)
+ };
+
+ console.log('Team info:', this.teamInfo);
+ }
+
+ this.getArenaOpponents = async function() {
+ console.log('Getting arena opponents...');
+
+ const apiName = this.arenaType === 'grand' ? 'grandFindEnemies' : 'arenaFindEnemies';
+ const calls = [{
+ name: apiName,
+ args: {},
+ context: {
+ actionTs: Utils.getActionTs()
+ },
+ ident: "body"
+ }];
+
+ try {
+ const response = await Send(JSON.stringify({calls}));
+ console.log('Arena opponents API response:', response);
+
+ if (!response || !response.results || !response.results[0] || !response.results[0].result) {
+ throw new Error(`Invalid API response structure for ${apiName}`);
+ }
+
+ const opponents = response.results[0].result.response;
+ console.log('Detailed opponents info:', opponents);
+
+ // Return both map (for lookup) and array (for order preservation)
+ const opponentsMap = {};
+ const opponentsArray = [];
+
+ if (Array.isArray(opponents)) {
+ // Preserve the order from API response
+ opponents.forEach(opponent => {
+ opponentsMap[opponent.userId] = opponent;
+ opponentsArray.push(opponent);
+ });
+ }
+
+ return {
+ map: opponentsMap,
+ array: opponentsArray // Preserve API order
+ };
+ } catch (error) {
+ console.error('Error getting arena opponents:', error);
+ return { map: {}, array: [] };
+ }
+ }
+
+ this.findEasiestOpponents = function() {
+ // Process opponents in the exact order they come from API
+ // Arena will try them one by one as returned by the server (no sorting)
+ if (this.opponentsData && this.opponentsData.array && Array.isArray(this.opponentsData.array)) {
+ const availableOpponents = [];
+
+ // Process in API order (preserve original array order)
+ this.opponentsData.array.forEach(opponentData => {
+ availableOpponents.push({
+ opponent: {
+ id: opponentData.userId,
+ power: parseInt(opponentData.power) || 0,
+ place: parseInt(opponentData.place) || 1000,
+ heroes: opponentData.heroes || [],
+ banners: opponentData.banners || [],
+ user: opponentData.user || {}
+ },
+ rank: parseInt(opponentData.place) || 1000,
+ difficulty: parseInt(opponentData.power) || 0
+ });
+ });
+
+ // Keep original order from API - try opponents one by one as returned
+ // No sorting - will attempt in the order the server provides
+ this.opponents = availableOpponents;
+ console.log(`[OPPONENTS] Processing ${this.opponents.length} opponents in API order (one by one, no sorting)`);
+ console.log('[OPPONENTS] Opponent order:', this.opponents.map(o => ({
+ id: o.opponent.id,
+ place: o.rank,
+ power: o.difficulty
+ })));
+ } else if (this.opponents && typeof this.opponents === 'object') {
+ // Fallback: if we have the old format (map), convert to array
+ // Note: Object.entries() may not preserve order, but we'll try
+ const availableOpponents = [];
+ for (const [opponentId, opponentData] of Object.entries(this.opponents)) {
+ availableOpponents.push({
+ opponent: {
+ id: opponentId,
+ power: parseInt(opponentData.power) || 0,
+ place: parseInt(opponentData.place) || 1000,
+ heroes: opponentData.heroes || [],
+ banners: opponentData.banners || [],
+ user: opponentData.user || {}
+ },
+ rank: parseInt(opponentData.place) || 1000,
+ difficulty: parseInt(opponentData.power) || 0
+ });
+ }
+ this.opponents = availableOpponents;
+ console.log(`[OPPONENTS] Processing ${this.opponents.length} opponents (fallback mode)`);
+ } else {
+ console.log('[OPPONENTS] No opponents data to process');
+ this.opponents = [];
+ }
+ }
+
+ this.loadAllyUserIds = async function() {
+ this.allyUserIds = new Set();
+
+ if (!this.myUserId) {
+ try {
+ const userInfo = getUserInfo();
+ if (userInfo?.userId != null) {
+ this.myUserId = String(userInfo.userId);
+ }
+ if (userInfo?.clanId != null) {
+ this.myClanId = String(userInfo.clanId);
+ }
+ } catch (e) {
+ console.warn('[ALLIES] Could not read user info from getUserInfo():', e);
+ }
+ }
+
+ if (this.myUserId) {
+ this.allyUserIds.add(String(this.myUserId));
+ }
+
+ if (this.myClanId && this.myClanId !== '0') {
+ try {
+ const response = await Send(JSON.stringify({
+ calls: [{
+ name: 'clanGetInfo',
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: 'clanGetInfo'
+ }]
+ }));
+ const members = response?.results?.[0]?.result?.response?.clan?.members;
+ if (members && typeof members === 'object') {
+ for (const memberId of Object.keys(members)) {
+ this.allyUserIds.add(String(memberId));
+ }
+ console.log(`[ALLIES] Loaded ${Object.keys(members).length} guild members`);
+ }
+ } catch (error) {
+ console.warn('[ALLIES] Could not load guild members from clanGetInfo:', error);
+ }
+ }
+
+ try {
+ const response = await Send(JSON.stringify({
+ calls: [{
+ name: 'crossClanWar_getAttackMap',
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: 'body'
+ }]
+ }));
+ const clanTries = response?.results?.[0]?.result?.response?.clanTries;
+ if (clanTries && typeof clanTries === 'object') {
+ const teamCountBefore = this.allyUserIds.size;
+ for (const userId of Object.keys(clanTries)) {
+ this.allyUserIds.add(String(userId));
+ }
+ console.log(`[ALLIES] Loaded ${this.allyUserIds.size - teamCountBefore} cross-clan team members`);
+ }
+ } catch (error) {
+ console.warn('[ALLIES] Could not load cross-clan team members:', error);
+ }
+
+ console.log(`[ALLIES] Total ally user IDs to skip: ${this.allyUserIds.size}`);
+ }
+
+ this.getAllySkipReason = function(opponent) {
+ const opponentId = String(opponent?.opponent?.id || '');
+ if (!opponentId) {
+ return null;
+ }
+
+ if (this.myUserId && opponentId === String(this.myUserId)) {
+ return 'self';
+ }
+
+ if (this.allyUserIds.has(opponentId)) {
+ return 'guild or team member';
+ }
+
+ const opponentClanId = opponent?.opponent?.user?.clanId;
+ if (this.myClanId && opponentClanId &&
+ String(this.myClanId) !== '0' && String(opponentClanId) !== '0' &&
+ String(this.myClanId) === String(opponentClanId)) {
+ return 'same guild';
+ }
+
+ return null;
+ }
+
+ this.executeBattles = async function() {
+ let battlesAttempted = 0;
+ let battlesSkipped = 0;
+ let initialOpponentCount = this.opponents.length;
+ let allySkippedInRow = 0;
+
+ // Continue trying opponents until we run out of attempts or viable opponents
+ while (battlesAttempted < this.attemptsRemaining && this.opponents.length > 0) {
+ const opponent = this.opponents.shift();
+ const opponentId = opponent.opponent.id;
+
+ console.log(`[EXECUTE] ===== Processing opponent ${opponentId} (attempt ${battlesAttempted + 1}/${this.attemptsRemaining}, ${this.opponents.length} remaining) =====`);
+ const arenaName = this.arenaType === 'grand' ? 'Grand Arena' : 'Arena';
+ setProgress(`${arenaName}: Battle ${battlesAttempted + 1}/${this.attemptsRemaining} - Opponent ${opponentId}`);
+
+ try {
+ const allySkipReason = this.getAllySkipReason(opponent);
+ if (allySkipReason) {
+ console.log(`[EXECUTE] Skipping opponent ${opponentId} (${allySkipReason})`);
+ battlesSkipped++;
+ allySkippedInRow++;
+ if (allySkippedInRow >= initialOpponentCount && battlesAttempted === 0) {
+ console.log('[EXECUTE] All opponents are guild/team allies, ending execution');
+ break;
+ }
+ continue;
+ }
+ allySkippedInRow = 0;
+
+ if (this.arenaType === 'grand') {
+ const canAttack = await this.checkTargetRange(opponentId);
+ if (!canAttack) {
+ console.log(`[EXECUTE] Target ${opponentId} is not in range, skipping`);
+ battlesSkipped++;
+ continue;
+ }
+ }
+
+ const result = await this.executeBattle(opponent);
+
+ if (result.skipped) {
+ console.log(`[EXECUTE] Battle skipped due to low win rate (${result.winRate?.toFixed(2)}%)`);
+ battlesSkipped++;
+ if (this.opponents.length === 0) {
+ console.log(`[EXECUTE] No more opponents available, ending execution`);
+ break;
+ }
+ continue;
+ }
+
+ // Battle was attempted (not skipped)
+ battlesAttempted++;
+ if (result.win) {
+ this.victories++;
+ console.log(`[EXECUTE] ✓ Victory against opponent ${opponentId}`);
+ } else {
+ console.log(`[EXECUTE] ✗ Defeat against opponent ${opponentId}`);
+ }
+
+ if (battlesAttempted < this.attemptsRemaining) {
+ await this.refreshOpponents();
+ initialOpponentCount = this.opponents.length;
+ allySkippedInRow = 0;
+ if (this.opponents.length === 0) {
+ console.log('[EXECUTE] No opponents returned after refresh, ending execution');
+ break;
+ }
+ }
+ } catch (error) {
+ console.error(`[EXECUTE] Battle error for opponent ${opponentId}:`, error);
+ battlesAttempted++;
+ if (battlesAttempted < this.attemptsRemaining) {
+ await this.refreshOpponents();
+ initialOpponentCount = this.opponents.length;
+ allySkippedInRow = 0;
+ }
+ }
+ }
+
+ const summary = `Completed ${this.victories}/${battlesAttempted} victories${battlesSkipped > 0 ? `, ${battlesSkipped} skipped` : ''}`;
+ console.log(`[EXECUTE] ===== Execution Summary =====`);
+ console.log(`[EXECUTE] Victories: ${this.victories}/${battlesAttempted}`);
+ console.log(`[EXECUTE] Skipped: ${battlesSkipped}`);
+ console.log(`[EXECUTE] =============================`);
+ this.end(summary);
+ }
+
+ this.executeBattle = async function(opponent) {
+ try {
+ if (!opponent || !opponent.opponent || !opponent.opponent.id) {
+ console.error('[DEMO] Invalid opponent data:', opponent);
+ return { win: false };
+ }
+
+ const opponentId = opponent.opponent.id;
+ console.log(`[DEMO] ===== Starting demo battle simulation for opponent ${opponentId} =====`);
+
+ // Step 1: Get team configurations
+ console.log('[DEMO] Step 1: Getting team configurations...');
+ const myTeamConfig = this.getTeamConfiguration();
+ const opponentTeamConfig = this.getOpponentTeamConfig(opponent);
+
+ console.log('[DEMO] My team config:', JSON.stringify(myTeamConfig, null, 2));
+ console.log('[DEMO] Opponent team config:', JSON.stringify(opponentTeamConfig, null, 2));
+
+ if (!opponentTeamConfig || !opponentTeamConfig.hasValidTeam) {
+ console.warn('[DEMO] ⚠️ Cannot get opponent team data, proceeding with attack anyway');
+ const battleResult = await this.startArenaBattle(opponentId, myTeamConfig);
+ await this.endArenaBattle(battleResult);
+ return battleResult;
+ }
+
+ // Skip simulation for Grand Arena (demo battles only support single team, not 3-team Grand Arena)
+ if (this.arenaType === 'grand') {
+ console.log('[DEMO] Grand Arena: Skipping simulation (demo battles do not support 3-team battles), proceeding directly to attack');
+ const battleResult = await this.startArenaBattle(opponentId, myTeamConfig);
+ await this.endArenaBattle(battleResult);
+ return battleResult;
+ }
+
+ // Step 2: Simulate battles using demoBattles_startBattle (Regular Arena only)
+ Utils.log('log', '[DEMO] Step 2: Running demo battle simulations (no attempts consumed)...');
+ const simulationResult = await this.simulateWithDemoBattles(myTeamConfig, opponentTeamConfig, CONSTANTS.SIMULATION_COUNT);
+
+ console.log('[DEMO] Simulation results:', {
+ totalSimulations: simulationResult.total,
+ wins: simulationResult.wins,
+ losses: simulationResult.losses,
+ winRate: simulationResult.winRate.toFixed(2) + '%',
+ averageBattleTime: simulationResult.averageBattleTime.toFixed(2) + 's'
+ });
+
+ // Step 3: Check win rate threshold
+ const shouldProceed = simulationResult.winRate > CONSTANTS.WIN_RATE_THRESHOLD;
+
+ Utils.log('log', `[DEMO] Step 3: Win rate check (threshold: ${CONSTANTS.WIN_RATE_THRESHOLD}%)`);
+ Utils.log('log', `[DEMO] Win rate: ${simulationResult.winRate.toFixed(2)}%`);
+ Utils.log('log', `[DEMO] Decision: ${shouldProceed ? 'PROCEED' : 'SKIP'} (${shouldProceed ? 'Win rate above threshold' : 'Win rate below threshold'})`);
+
+ if (!shouldProceed) {
+ Utils.log('warn', `[DEMO] ⚠️ Win rate ${simulationResult.winRate.toFixed(2)}% is below ${CONSTANTS.WIN_RATE_THRESHOLD}%, skipping this opponent`);
+ console.log(`[DEMO] ✓ No battle attempt consumed - using demo battles API`);
+ console.log(`[DEMO] Looking for next opponent...`);
+ return { win: false, skipped: true, winRate: simulationResult.winRate };
+ }
+
+ // Step 4: Proceed with actual battle
+ console.log(`[DEMO] ✓ Win rate ${simulationResult.winRate.toFixed(2)}% is above threshold, proceeding with actual attack`);
+ console.log('[DEMO] Step 4: Executing actual battle...');
+
+ const battleResult = await this.startArenaBattle(opponentId, myTeamConfig);
+
+ console.log('[DEMO] Actual battle result:', {
+ win: battleResult.win,
+ note: 'Actual battle may differ from simulation due to seed variance'
+ });
+
+ await this.endArenaBattle(battleResult);
+
+ console.log(`[DEMO] ===== Battle completed for opponent ${opponentId} =====`);
+ return battleResult;
+ } catch (error) {
+ console.error('[DEMO] Error in executeBattle:', error);
+ console.error('[DEMO] Error stack:', error.stack);
+ return { win: false };
+ }
+ }
+
+ this.checkTargetRange = async function(targetId) {
+ if (this.arenaType !== 'grand') {
+ return true;
+ }
+
+ const targetIdStr = String(targetId);
+
+ try {
+ const calls = [{
+ name: "grandCheckTargetRange",
+ args: {
+ ids: [targetIdStr]
+ },
+ context: {
+ actionTs: Utils.getActionTs()
+ },
+ ident: "body"
+ }];
+
+ const response = await Send(JSON.stringify({calls}));
+ console.log('Target range check response:', response);
+
+ if (response && response.results && response.results[0] && response.results[0].result) {
+ const result = response.results[0].result.response;
+ return result[targetIdStr] === true || result[targetId] === true;
+ }
+
+ return false;
+ } catch (error) {
+ console.error('Error checking target range:', error);
+ return false;
+ }
+ }
+
+ this.getTeamConfiguration = function() {
+ if (!this.teamInfo || !this.teamInfo.teams) {
+ console.error('Team info not available, using fallback configuration');
+ return this.getFallbackTeamConfiguration();
+ }
+
+ const teamData = this.teamInfo.teams;
+ const favorData = this.teamInfo.favor;
+
+ if (this.arenaType === 'grand') {
+ const grandTeams = teamData.grand || [];
+ const grandFavor = favorData.grand || {};
+
+ console.log('Grand Arena teams from system:', grandTeams);
+ console.log('Grand Arena favor from system:', grandFavor);
+
+ const heroes = [];
+ const pets = [];
+
+ for (let i = 0; i < grandTeams.length; i++) {
+ const team = grandTeams[i];
+ if (team && team.length >= 6) {
+ heroes.push(team.slice(0, 5));
+ pets.push(team[5]);
+ }
+ }
+
+ let banners = [1, 2, 3];
+ try {
+ const userInfo = getUserInfo();
+ if (userInfo && userInfo.banners) {
+ if (Array.isArray(userInfo.banners)) {
+ banners = userInfo.banners.length >= 3 ? userInfo.banners.slice(0, 3) :
+ userInfo.banners.length === 1 ? [userInfo.banners[0], userInfo.banners[0], userInfo.banners[0]] : [1, 2, 3];
+ } else if (typeof userInfo.banners === 'number') {
+ banners = [userInfo.banners, userInfo.banners, userInfo.banners];
+ }
+ }
+ } catch (e) {
+ console.log('Could not get banners from userInfo, using defaults');
+ }
+
+ return {
+ heroes: heroes,
+ pets: pets,
+ favor: grandFavor,
+ banners: banners
+ };
+ } else {
+ const arenaTeam = teamData.arena || [];
+ const arenaFavor = favorData.arena || {};
+
+ console.log('Regular Arena team from system:', arenaTeam);
+ console.log('Regular Arena favor from system:', arenaFavor);
+
+ let heroes = [];
+ let pet = null;
+
+ if (arenaTeam && arenaTeam.length >= 6) {
+ heroes = arenaTeam.slice(0, 5);
+ pet = arenaTeam[5];
+ }
+
+ let banners = [1];
+ try {
+ const userInfo = getUserInfo();
+ if (userInfo && userInfo.banner) {
+ banners = typeof userInfo.banner === 'number' ? [userInfo.banner] :
+ Array.isArray(userInfo.banner) ? userInfo.banner : [1];
+ }
+ } catch (e) {
+ console.log('Could not get banner from userInfo, using default');
+ }
+
+ return {
+ heroes: heroes,
+ pet: pet,
+ favor: arenaFavor,
+ banners: banners
+ };
+ }
+ }
+
+ this.getFallbackTeamConfiguration = function() {
+ if (this.arenaType === 'grand') {
+ return {
+ heroes: [
+ [58, 1, 64, 13, 55],
+ [42, 56, 9, 62, 43],
+ [16, 31, 57, 40, 48]
+ ],
+ pets: [6006, 6005, 6004],
+ favor: {},
+ banners: [1, 2, 3]
+ };
+ } else {
+ return {
+ heroes: [57, 31, 55, 40, 16],
+ pet: 6008,
+ favor: {},
+ banners: [1]
+ };
+ }
+ }
+
+ this.getOpponentTeamConfig = function(opponent) {
+ console.log('[DEMO] Extracting opponent team configuration...');
+
+ if (!opponent || !opponent.opponent) {
+ console.warn('[DEMO] No opponent data available');
+ return { hasValidTeam: false };
+ }
+
+ const opp = opponent.opponent;
+ let hasValidTeam = false;
+ let config = {};
+
+ // Helper function to extract hero/pet ID from object
+ const extractId = (item) => {
+ if (typeof item === 'number') {
+ return item; // Already an ID
+ } else if (item && typeof item === 'object' && item.id) {
+ return item.id; // Extract ID from object
+ }
+ return null;
+ };
+
+ // Helper function to check if item is a pet
+ const isPet = (item) => {
+ if (typeof item === 'number') {
+ return item >= 6000 && item < 7000; // Pet ID range
+ } else if (item && typeof item === 'object') {
+ return item.type === 'pet' || (item.id >= 6000 && item.id < 7000);
+ }
+ return false;
+ };
+
+ // Helper function to extract banner ID
+ const extractBannerId = (banner) => {
+ if (typeof banner === 'number') {
+ return banner;
+ } else if (banner && typeof banner === 'object' && banner.id) {
+ return banner.id;
+ }
+ return 1; // Default banner
+ };
+
+ if (this.arenaType === 'grand') {
+ // Grand Arena: 3 teams
+ // heroes is array of 3 teams, each team is array of 6 objects (5 heroes + 1 pet)
+ if (opp.heroes && Array.isArray(opp.heroes) && opp.heroes.length >= 3) {
+ const teams = [];
+ const pets = [];
+ const favor = {};
+
+ // Extract teams from heroes array
+ for (let i = 0; i < Math.min(3, opp.heroes.length); i++) {
+ const team = opp.heroes[i];
+ if (team && Array.isArray(team) && team.length >= 6) {
+ // Extract hero IDs (first 5 items)
+ const heroIds = [];
+ let petId = 6005; // Default pet
+
+ for (let j = 0; j < team.length; j++) {
+ const item = team[j];
+ const id = extractId(item);
+
+ if (id && !isPet(item)) {
+ // It's a hero
+ if (heroIds.length < 5) {
+ heroIds.push(id);
+ }
+ } else if (id && isPet(item)) {
+ // It's a pet
+ petId = id;
+ }
+ }
+
+ if (heroIds.length === 5) {
+ teams.push(heroIds);
+ pets.push(petId);
+ hasValidTeam = true;
+ }
+ }
+ }
+
+ // Extract banners (array of 3 banner objects)
+ const banners = [];
+ if (opp.banners && Array.isArray(opp.banners)) {
+ for (let i = 0; i < Math.min(3, opp.banners.length); i++) {
+ banners.push(extractBannerId(opp.banners[i]));
+ }
+ }
+ // Fill with defaults if needed
+ while (banners.length < 3) {
+ banners.push(1);
+ }
+
+ if (hasValidTeam) {
+ config = {
+ hasValidTeam: true,
+ heroes: teams,
+ pets: pets,
+ banners: banners.slice(0, 3),
+ favor: favor
+ };
+ console.log('[DEMO] Grand Arena config extracted:', {
+ teams: teams.length,
+ pets: pets.length,
+ banners: banners.length
+ });
+ }
+ }
+ } else {
+ // Regular Arena: 1 team
+ // heroes is array of 6 objects (5 heroes + 1 pet)
+ if (opp.heroes && Array.isArray(opp.heroes) && opp.heroes.length >= 6) {
+ const heroIds = [];
+ let petId = 6005; // Default pet
+
+ // Extract hero IDs and pet ID from objects
+ for (let i = 0; i < opp.heroes.length; i++) {
+ const item = opp.heroes[i];
+ const id = extractId(item);
+
+ if (id && !isPet(item)) {
+ // It's a hero
+ if (heroIds.length < 5) {
+ heroIds.push(id);
+ }
+ } else if (id && isPet(item)) {
+ // It's a pet (usually the 6th item)
+ petId = id;
+ }
+ }
+
+ // Extract banner ID from banner object
+ let bannerId = 1; // Default
+ if (opp.banners && Array.isArray(opp.banners) && opp.banners.length > 0) {
+ bannerId = extractBannerId(opp.banners[0]);
+ } else if (typeof opp.banner === 'number') {
+ bannerId = opp.banner;
+ }
+
+ if (heroIds.length === 5) {
+ hasValidTeam = true;
+ config = {
+ hasValidTeam: true,
+ heroes: heroIds,
+ pet: petId,
+ banner: bannerId,
+ favor: {} // Favor data not available in arenaFindEnemies response
+ };
+ console.log('[DEMO] Regular Arena config extracted:', {
+ heroes: heroIds,
+ pet: petId,
+ banner: bannerId
+ });
+ }
+ }
+ }
+
+ if (!hasValidTeam) {
+ console.warn('[DEMO] Could not extract valid opponent team configuration');
+ console.log('[DEMO] Opponent data structure:', {
+ hasHeroes: !!opp.heroes,
+ heroesType: opp.heroes ? (Array.isArray(opp.heroes) ? 'array' : typeof opp.heroes) : 'none',
+ heroesLength: opp.heroes ? (Array.isArray(opp.heroes) ? opp.heroes.length : 'N/A') : 0,
+ firstHeroType: opp.heroes && Array.isArray(opp.heroes) && opp.heroes.length > 0
+ ? (typeof opp.heroes[0]) : 'N/A',
+ hasBanners: !!opp.banners
+ });
+ console.log('[DEMO] Full opponent data:', JSON.stringify(opp, null, 2));
+ } else {
+ console.log('[DEMO] Successfully extracted opponent team configuration');
+ }
+
+ return config;
+ }
+
+ this.simulateWithDemoBattles = async function(myTeam, opponentTeam, simulationCount = 10) {
+ Utils.log('log', `[DEMO] Starting ${simulationCount} demo battle simulations...`);
+
+ // Note: demoBattles API only supports "arena" mechanic, even for Grand Arena
+ const mechanic = 'arena';
+
+ const simulations = [];
+ let parentId = 0; // Start with 0 for first battle
+ let firstBattleId = null; // Store first battle's ID to use as parentId for subsequent battles
+
+ for (let i = 0; i < simulationCount; i++) {
+ try {
+ // First battle uses parentId=0, subsequent battles use first battle's ID as parentId
+ const result = await this.runSingleDemoBattle(myTeam, opponentTeam, mechanic, i, parentId);
+ simulations.push(result);
+
+ // For first battle: store the battle ID to use as parentId for subsequent battles
+ if (i === 0 && result.battleId) {
+ firstBattleId = result.battleId;
+ parentId = firstBattleId;
+ }
+ // For subsequent battles: use the first battle's ID as parentId
+ else if (i > 0 && firstBattleId) {
+ parentId = firstBattleId;
+ }
+ // Fallback: try to extract parentId from endBattle response
+ else if (result.parentId !== undefined && result.parentId !== null && result.parentId !== 0) {
+ parentId = result.parentId;
+ }
+ } catch (error) {
+ console.error(`[DEMO] Simulation ${i + 1} failed:`, error);
+ simulations.push({ win: false, battleTime: 0, error: error.message, parentId: parentId });
+ }
+ }
+
+ // Calculate statistics
+ const wins = simulations.filter(s => s.win).length;
+ const losses = simulations.length - wins;
+ const winRate = (wins / simulations.length) * 100;
+ const battleTimes = simulations.map(s => s.battleTime).filter(t => t > 0);
+ const averageBattleTime = battleTimes.length > 0
+ ? battleTimes.reduce((a, b) => a + b, 0) / battleTimes.length
+ : 0;
+
+ Utils.log('log', `[DEMO] Simulation complete: ${wins}W/${losses}L (${winRate.toFixed(1)}% win rate)`);
+
+ return {
+ total: simulations.length,
+ wins: wins,
+ losses: losses,
+ winRate: winRate,
+ averageBattleTime: averageBattleTime,
+ simulations: simulations
+ };
+ }
+
+ this.runSingleDemoBattle = async function(myTeam, opponentTeam, mechanic, seedOffset = 0, parentId = 0) {
+ return new Promise((resolve, reject) => {
+ try {
+ let args = {
+ mechanic: mechanic,
+ defenceMaxUpgrade: true, // Use max upgrade for opponent to get accurate simulation
+ maxUpgrade: true, // Use max upgrade for our team to get accurate simulation
+ defenceBuffs: {},
+ buffs: {},
+ parentId: parentId,
+ entryId: 0
+ };
+
+ // Handle team configuration based on arena type
+ // Note: demoBattles API only supports "arena" mechanic
+ // For Grand Arena, we simulate the first team as a proxy
+ if (this.arenaType === 'grand') {
+ // Grand Arena: 3 teams - simulate first team as proxy
+ // Note: demoBattles_startBattle only simulates one team at a time
+ // We use the first team as a proxy for overall win probability
+ const teamIndex = seedOffset % 3; // Rotate through teams for variety
+
+ args.defenceTeam = {
+ units: opponentTeam.heroes[teamIndex] || opponentTeam.heroes[0] || [],
+ pet: opponentTeam.pets[teamIndex] || opponentTeam.pets[0] || CONSTANTS.DEFAULT_PET_ID
+ };
+ args.defenceBanner = opponentTeam.banners[teamIndex] || opponentTeam.banners[0] || 1;
+ args.defenceBannerStones = {}; // Required field from HAR file
+ args.defenceFavor = opponentTeam.favor || {};
+
+ args.team = {
+ units: myTeam.heroes[teamIndex] || myTeam.heroes[0] || [],
+ pet: myTeam.pets[teamIndex] || myTeam.pets[0] || CONSTANTS.DEFAULT_PET_ID
+ };
+ args.banner = myTeam.banners[teamIndex] || myTeam.banners[0] || 1;
+ args.bannerStones = {}; // Required field from HAR file
+ args.favor = myTeam.favor || {};
+ } else {
+ // Regular Arena: 1 team
+ args.defenceTeam = {
+ units: opponentTeam.heroes || [],
+ pet: opponentTeam.pet || CONSTANTS.DEFAULT_PET_ID
+ };
+ args.defenceBanner = opponentTeam.banner || 1;
+ args.defenceBannerStones = {}; // Required field from HAR file
+ args.defenceFavor = opponentTeam.favor || {};
+
+ args.team = {
+ units: myTeam.heroes || [],
+ pet: myTeam.pet || CONSTANTS.DEFAULT_PET_ID
+ };
+ args.banner = myTeam.banners[0] || 1;
+ args.bannerStones = {}; // Required field from HAR file
+ args.favor = myTeam.favor || {};
+ }
+
+ // Validate required fields before making API call
+ if (!args.team || !args.team.units || args.team.units.length === 0) {
+ reject(new Error('Invalid team configuration: missing or empty hero units'));
+ return;
+ }
+ if (!args.defenceTeam || !args.defenceTeam.units || args.defenceTeam.units.length === 0) {
+ reject(new Error('Invalid defence team configuration: missing or empty hero units'));
+ return;
+ }
+ if (!args.team.pet || typeof args.team.pet !== 'number') {
+ reject(new Error('Invalid team pet: must be a number'));
+ return;
+ }
+ if (!args.defenceTeam.pet || typeof args.defenceTeam.pet !== 'number') {
+ reject(new Error('Invalid defence team pet: must be a number'));
+ return;
+ }
+
+ const calls = [{
+ name: "demoBattles_startBattle",
+ args: args,
+ context: {
+ actionTs: Utils.getActionTs()
+ },
+ ident: "body"
+ }];
+
+ const startTime = Date.now();
+
+ Send(JSON.stringify({calls}))
+ .then(response => {
+ if (response.error) {
+ console.error('[DEMO] API error:', response.error);
+ reject(new Error(`Demo battle API error: ${response.error.name} - ${response.error.description}`));
+ return;
+ }
+
+ if (!response.results || !response.results[0] || !response.results[0].result) {
+ console.error('[DEMO] Invalid API response structure');
+ reject(new Error('Invalid demo battle API response'));
+ return;
+ }
+
+ const responseData = response.results[0].result.response;
+ // Battle data is nested under 'battle' property
+ const battleData = responseData?.battle || responseData;
+
+ if (!battleData) {
+ console.error('[DEMO] No battle data found in response');
+ reject(new Error('No battle data in API response'));
+ return;
+ }
+
+ // Calculate battle result using BattleCalc
+ const battleType = battleData?.effects?.battleConfig ?? battleData?.type ?? mechanic;
+ const battleConfigType = getBattleType(battleType);
+
+ BattleCalc(battleData, battleConfigType, (calcResult) => {
+ if (!Utils.isValidBattleResult(calcResult)) {
+ Utils.log('error', '[DEMO] BattleCalc returned invalid result');
+ resolve({
+ win: false,
+ battleTime: 0,
+ error: 'Invalid calculation result',
+ parentId: parentId
+ });
+ return;
+ }
+
+ const battleTime = calcResult.battleTime || 0;
+ const win = calcResult.result.win || false;
+
+ // Call demoBattles_endBattle to get battleId for parentId chaining
+ // Strategy: Use first battle's ID as parentId for all subsequent battles
+ const self = this;
+ self.endDemoBattle(calcResult, battleData)
+ .then(endBattleResult => {
+ const extractedParentId = endBattleResult?.parentId;
+ const battleId = endBattleResult?.battleId;
+
+ // Strategy: For first battle, use its ID as parentId for subsequent battles
+ let nextParentId = parentId;
+
+ if (parentId === 0 && battleId) {
+ // First battle: use its ID as parentId for next battle
+ nextParentId = battleId;
+ } else if (parentId !== 0) {
+ // Subsequent battle: keep using the first battle's ID
+ nextParentId = parentId;
+ } else if (extractedParentId && extractedParentId !== 0) {
+ // Fallback: use parentId from endBattle response
+ nextParentId = extractedParentId;
+ }
+
+ resolve({
+ win: win,
+ battleTime: battleTime,
+ result: calcResult,
+ parentId: nextParentId,
+ battleId: battleId
+ });
+ })
+ .catch(endError => {
+ Utils.log('warn', '[DEMO] Failed to call endBattle:', endError);
+ resolve({
+ win: win,
+ battleTime: battleTime,
+ result: calcResult,
+ parentId: parentId,
+ battleId: null
+ });
+ });
+ });
+ })
+ .catch(error => {
+ console.error('[DEMO] Error in demo battle:', error);
+ reject(error);
+ });
+ } catch (error) {
+ console.error('[DEMO] Error preparing demo battle:', error);
+ reject(error);
+ }
+ });
+ }
+
+ this.endDemoBattle = async function(calcResult, battleData) {
+ return new Promise((resolve, reject) => {
+ try {
+ // Prepare progress data from battle calculation result
+ const progress = calcResult.progress || [];
+
+ // Ensure progress array has at least one entry
+ if (progress.length === 0 && calcResult.result) {
+ // Create minimal progress entry from result
+ progress.push({
+ v: CONSTANTS.BATTLE_VERSION,
+ b: 0,
+ seed: battleData?.seed || Math.floor(Math.random() * 1000000000),
+ attackers: {
+ input: [],
+ heroes: {}
+ },
+ defenders: {
+ input: [],
+ heroes: {}
+ }
+ });
+ }
+
+ const endBattleArgs = {
+ result: {
+ win: calcResult.result.win || false,
+ stars: calcResult.result.stars || 0
+ },
+ progress: progress
+ };
+
+ const calls = [{
+ name: "demoBattles_endBattle",
+ args: endBattleArgs,
+ context: {
+ actionTs: Utils.getActionTs()
+ },
+ ident: "body"
+ }];
+
+ Send(JSON.stringify({calls}))
+ .then(response => {
+ if (response.error) {
+ Utils.log('warn', '[DEMO] EndBattle API error:', response.error);
+ resolve(null); // Return null on error, will use original parentId
+ return;
+ }
+
+ if (!response.results || !response.results[0] || !response.results[0].result) {
+ Utils.log('warn', '[DEMO] Invalid endBattle response structure');
+ resolve(null);
+ return;
+ }
+
+ const endBattleResponse = response.results[0].result.response;
+
+ // Extract both parentId and battleId from battle object in response
+ // Strategy: Use first battle's ID as parentId for subsequent battles
+ const battle = endBattleResponse?.battle;
+ const extractedParentId = battle?.parentId;
+ const battleId = battle?.id;
+
+ resolve({
+ parentId: extractedParentId !== undefined && extractedParentId !== null ? extractedParentId : null,
+ battleId: battleId !== undefined && battleId !== null ? battleId : null
+ });
+ })
+ .catch(error => {
+ Utils.log('warn', '[DEMO] Error calling endBattle:', error);
+ resolve(null); // Return null on error, will use original parentId
+ });
+ } catch (error) {
+ Utils.log('warn', '[DEMO] Error preparing endBattle:', error);
+ resolve(null);
+ }
+ });
+ }
+
+ this.startArenaBattle = async function(rivalId, team) {
+ const apiName = this.arenaType === 'grand' ? 'grandAttack' : 'arenaAttack';
+
+ // Ensure rivalId is a number
+ const userId = typeof rivalId === 'string' ? parseInt(rivalId, 10) : rivalId;
+
+ let args;
+ if (this.arenaType === 'grand') {
+ // Grand Arena: heroes is array of 3 arrays, pets is array of 3 numbers
+ if (!team.heroes || !Array.isArray(team.heroes) || team.heroes.length !== 3) {
+ throw new Error('Grand Arena requires 3 hero teams');
+ }
+ if (!team.pets || !Array.isArray(team.pets) || team.pets.length !== 3) {
+ throw new Error('Grand Arena requires 3 pets');
+ }
+ if (!team.banners || !Array.isArray(team.banners) || team.banners.length !== 3) {
+ throw new Error('Grand Arena requires 3 banners');
+ }
+
+ args = {
+ userId: userId,
+ heroes: team.heroes, // Array of 3 arrays: [[team1], [team2], [team3]]
+ pets: team.pets, // Array of 3 pet IDs
+ favor: team.favor || {}, // Object mapping hero IDs (strings) to pet IDs
+ banners: team.banners // Array of 3 banner IDs
+ };
+ } else {
+ // Regular Arena: heroes is flat array of 5 numbers, pet is single number
+ if (!team.heroes || !Array.isArray(team.heroes) || team.heroes.length !== 5) {
+ throw new Error('Arena requires exactly 5 heroes');
+ }
+ if (!team.pet || typeof team.pet !== 'number') {
+ throw new Error('Arena requires a valid pet ID');
+ }
+
+ // Ensure banners is an array (even if single banner)
+ const banners = Array.isArray(team.banners) ? team.banners :
+ team.banners ? [team.banners] : [1];
+
+ args = {
+ userId: userId,
+ heroes: team.heroes, // Flat array of 5 hero IDs
+ pet: team.pet, // Single pet ID (number)
+ favor: team.favor || {}, // Object mapping hero IDs (strings) to pet IDs
+ banners: banners // Array of banner IDs (usually single element)
+ };
+ }
+
+ const calls = [{
+ name: apiName,
+ args: args,
+ context: {
+ actionTs: Utils.getActionTs()
+ },
+ ident: "body"
+ }];
+
+ console.log(`[ARENA] Calling ${apiName} with args:`, JSON.stringify(args, null, 2));
+ console.log(`[ARENA] Full API call:`, JSON.stringify({calls}, null, 2));
+
+ const response = await Send(JSON.stringify({calls}));
+ console.log('[ARENA] Battle API response:', response);
+
+ if (response.error) {
+ const errorName = response.error.name || 'Unknown';
+ const errorDesc = response.error.description || '';
+
+ let errorMessage = `API error: ${errorName}`;
+ if (errorDesc) {
+ errorMessage += ` - ${errorDesc}`;
+ }
+
+ if (errorName === 'NotAvailable') {
+ errorMessage = 'Arena not available - may be in peace time, no attempts left, or arena locked';
+ } else if (errorName === 'InvalidRequest') {
+ errorMessage = 'Invalid request - check opponent IDs and team configuration';
+ } else if (errorName === 'ArgumentError') {
+ errorMessage = 'Missing required arguments - check team data';
+ }
+
+ throw new Error(errorMessage);
+ }
+
+ if (this.arenaType === 'grand') {
+ if (response.results && response.results[0] && response.results[0].result) {
+ const result = response.results[0].result.response;
+ if (result.battles && result.battles.length > 0) {
+ const battleData = result.battles[0];
+ return new Promise((resolve) => {
+ BattleCalc(battleData, getBattleType(this.arenaType), (calcResult) => {
+ if (!calcResult || !calcResult.result) {
+ console.error('BattleCalc returned invalid result:', calcResult);
+ resolve({
+ win: false,
+ progress: [],
+ result: { win: false }
+ });
+ return;
+ }
+ resolve({
+ win: calcResult.result.win,
+ progress: calcResult.progress,
+ result: calcResult.result
+ });
+ });
+ });
+ }
+ }
+ } else {
+ let battleData = null;
+ if (response.results && response.results[0]) {
+ const result = response.results[0].result || response.results[0];
+ if (result.response && result.response.battle) {
+ battleData = result.response.battle;
+ } else if (result.battle) {
+ battleData = result.battle;
+ } else if (result.response) {
+ battleData = result.response;
+ }
+ }
+
+ if (battleData) {
+ return new Promise((resolve) => {
+ BattleCalc(battleData, getBattleType(this.arenaType), (result) => {
+ if (!result || !result.result) {
+ console.error('BattleCalc returned invalid result:', result);
+ resolve({
+ win: false,
+ progress: [],
+ result: { win: false }
+ });
+ return;
+ }
+ resolve({
+ win: result.result.win,
+ progress: result.progress,
+ result: result.result
+ });
+ });
+ });
+ }
+ }
+
+ console.log('No battle data found, assuming success');
+ return {
+ win: true,
+ progress: [],
+ result: { win: true }
+ };
+ }
+
+ this.endArenaBattle = async function(battleResult) {
+ // Skip stashClient call - battles auto-close and this can cause NotFound errors
+ // The battle popup will close automatically after battle completion
+ // Calling stashClient can trigger errors if the battle type doesn't match
+ console.log('Battle completed, popup will auto-close');
+
+ // Optional: Add a small delay to ensure battle processing completes
+ await new Promise(resolve => setTimeout(resolve, CONSTANTS.DELAY_BATTLE_COMPLETE));
+ }
+
+ this.end = function(message) {
+ console.log('Arena execution ended:', message);
+ setProgress(`Arena: ${message}`, true);
+ this.resolve();
+ }
+ }
+
+ // ========== EXECUTE GUILD WAR CLASS ==========
+ function executeGuildWar(resolve, reject) {
+ this.resolve = resolve;
+ this.reject = reject;
+ this.victories = 0;
+ this.guildWarInfo = null;
+ this.teamInfo = null;
+ this.myTries = null;
+
+ this.start = async function() {
+ setProgress(`${I18N('GUILD_WAR')}: ${I18N('INITIALIZING')}...`);
+
+ try {
+ const hasAttempts = await this.getGuildWarInfo();
+ if (!hasAttempts) {
+ // getGuildWarInfo already handled the end message, just return
+ return;
+ }
+ await this.getTeamData();
+ await this.attackDirectSlots();
+ } catch (error) {
+ console.error('Guild War error:', error);
+ this.end(`Error: ${error.message}`);
+ }
+ }
+
+ this.getGuildWarInfo = async function() {
+ console.log('Getting Guild War info...');
+
+ const calls = [{
+ name: "clanWarGetInfo",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "clanWarGetInfo"
+ }];
+
+ const response = await Send(JSON.stringify({calls}));
+
+ if (response.error) {
+ console.log(`Guild War info API error: ${response.error.name} - ${response.error.description}`);
+ this.end(`Guild War not available: ${response.error.description || response.error.name}`);
+ return false;
+ }
+
+ if (!response.results || !response.results[0] || !response.results[0].result || !response.results[0].result.response) {
+ console.log('Invalid clanWarGetInfo response');
+ this.end('Guild War: Invalid response from server');
+ return false;
+ }
+
+ this.guildWarInfo = response.results[0].result.response;
+
+ // Check if myTries exists (only exists when war is active)
+ if ('myTries' in this.guildWarInfo) {
+ this.myTries = this.guildWarInfo.myTries;
+ console.log(`Guild War attempts remaining: ${this.myTries}`);
+
+ if (this.myTries <= 0) {
+ console.log('No Guild War attempts remaining');
+ this.end('No Guild War attempts remaining');
+ return false;
+ }
+ } else {
+ console.log('Guild War is not currently active - myTries field not available');
+ this.end('Guild War is not currently active');
+ return false;
+ }
+
+ console.log('Guild War info loaded');
+ return true;
+ }
+
+ this.refreshGuildWarAttempts = async function() {
+ try {
+ const calls = [{
+ name: "clanWarGetInfo",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "clanWarGetInfo"
+ }];
+
+ const response = await Send(JSON.stringify({calls}));
+
+ if (response.error) {
+ console.warn('Failed to refresh Guild War attempts:', response.error);
+ return false;
+ }
+
+ if (response.results && response.results[0] && response.results[0].result && response.results[0].result.response) {
+ const guildWarInfo = response.results[0].result.response;
+ if ('myTries' in guildWarInfo) {
+ this.myTries = guildWarInfo.myTries;
+ this.guildWarInfo = guildWarInfo;
+ console.log(`Refreshed Guild War attempts: ${this.myTries}`);
+ return true;
+ }
+ }
+ return false;
+ } catch (error) {
+ console.warn('Error refreshing Guild War attempts:', error);
+ return false;
+ }
+ }
+
+ this.getTeamData = async function() {
+ console.log('Getting team data...');
+
+ const calls = [
+ {
+ name: "teamGetAll",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "teamGetAll"
+ },
+ {
+ name: "teamGetFavor",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "teamGetFavor"
+ },
+ {
+ name: "heroGetAll",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "heroGetAll"
+ },
+ {
+ // Needed for Guild War titan team power comparisons
+ name: "titanGetAll",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "titanGetAll"
+ }
+ ];
+
+ const response = await Send(JSON.stringify({calls}));
+
+ if (response.error) {
+ throw new Error(`Team data API error: ${response.error.name} - ${response.error.description}`);
+ }
+
+ if (!response.results[0] || !response.results[0].result || !response.results[0].result.response) {
+ throw new Error('Invalid teamGetAll response - team data not available');
+ }
+ if (!response.results[1] || !response.results[1].result || !response.results[1].result.response) {
+ throw new Error('Invalid teamGetFavor response - favor data not available');
+ }
+ if (!response.results[2] || !response.results[2].result || !response.results[2].result.response) {
+ throw new Error('Invalid heroGetAll response - hero data not available');
+ }
+ if (!response.results[3] || !response.results[3].result || !response.results[3].result.response) {
+ throw new Error('Invalid titanGetAll response - titan data not available');
+ }
+
+ const heroesById = response.results[2].result.response || {};
+ const titansById = response.results[3].result.response || {};
+
+ this.teamInfo = {
+ teams: response.results[0].result.response,
+ favor: response.results[1].result.response,
+ heroesById: heroesById,
+ titansById: titansById
+ };
+
+ console.log('Team data loaded');
+ }
+
+ this.getUnitPower = function(unitId, isTitan) {
+ if (!this.teamInfo) return 0;
+ const source = isTitan ? this.teamInfo.titansById : this.teamInfo.heroesById;
+ if (!source) return 0;
+ const unit = source[unitId];
+ const p = unit && unit.power !== undefined ? Number(unit.power) : 0;
+ return Number.isFinite(p) ? p : 0;
+ }
+
+ this.getMyTeamPower = function(teamConfig, isTitanBattle) {
+ const ids = isTitanBattle ? (teamConfig?.titans || []) : (teamConfig?.heroes || []);
+ return ids.slice(0, 5).reduce((sum, id) => sum + this.getUnitPower(id, isTitanBattle), 0);
+ }
+
+ this.getOpponentSlotPower = function(slotId, isTitanBattle) {
+ if (!this.guildWarInfo || !this.guildWarInfo.enemySlots) return 0;
+ const slotData = this.guildWarInfo.enemySlots[String(slotId)];
+ if (!slotData || !Array.isArray(slotData.team)) return 0;
+
+ const expectedType = isTitanBattle ? 'titan' : 'hero';
+ let sum = 0;
+ for (const memberObj of slotData.team) {
+ if (!memberObj || typeof memberObj !== 'object') continue;
+ const position = Object.keys(memberObj)[0];
+ const unit = memberObj[position];
+ if (!unit || unit.type !== expectedType) continue;
+ const p = unit.power !== undefined ? Number(unit.power) : 0;
+ if (Number.isFinite(p)) sum += p;
+ }
+ return sum;
+ }
+
+ this.attackDirectSlots = async function() {
+ console.log('Starting direct Guild War attacks on slots 7, 8, 9, 34, 1, and 2...');
+
+ const slots = [7, 8, 9, 34, 1, 2];
+ const slotNames = {
+ 7: 'slot 7 (Titans - Bridge)',
+ 8: 'slot 8 (Titans - Bridge)',
+ 9: 'slot 9 (Titans - Bridge)',
+ 34: 'slot 34 (Titans - Bridge)',
+ 1: 'slot 1',
+ 2: 'slot 2'
+ };
+
+ for (let i = 0; i < slots.length; i++) {
+ const slotId = slots[i];
+
+ // Refresh attempts from API before each attack to get accurate count
+ await this.refreshGuildWarAttempts();
+
+ // Check if we have attempts remaining before each attack
+ if (this.myTries === null || this.myTries === undefined || this.myTries <= 0) {
+ console.log(`No attempts remaining (myTries: ${this.myTries}), stopping attacks`);
+ break;
+ }
+
+ try {
+ console.log(`Attacking ${slotNames[slotId]}... (${this.myTries} attempts remaining)`);
+ setProgress(`${I18N('GUILD_WAR')}: Attacking ${slotNames[slotId]} (${this.myTries} attempts)`);
+ await this.attackSlot(slotId);
+ this.victories++;
+ console.log(`${slotNames[slotId]} attack completed successfully`);
+
+ // Refresh attempts after successful attack to get updated count
+ await this.refreshGuildWarAttempts();
+ } catch (error) {
+ console.error(`Error attacking ${slotNames[slotId]}:`, error);
+
+ // Check if this is a skip error (from simulation)
+ if (error.message && error.message.startsWith('Skipped:')) {
+ console.log(`[GUILD_WAR] ${slotNames[slotId]} skipped due to low win rate, continuing to next target`);
+ Utils.log('warn', `Skipped ${slotNames[slotId]}: ${error.message}, continuing to next target`);
+ // Don't increment victories, just continue
+ } else {
+ // Other errors: continue to next slot instead of stopping
+ Utils.log('warn', `Failed to attack ${slotNames[slotId]}: ${error.message}, continuing to next target`);
+ }
+ }
+
+ // Add delay between attacks (except after the last one)
+ if (i < slots.length - 1) {
+ await new Promise(resolve => setTimeout(resolve, CONSTANTS.DELAY_BETWEEN_BATTLES));
+ }
+ }
+
+ // Final refresh to get accurate remaining attempts
+ await this.refreshGuildWarAttempts();
+ const summary = `Completed ${this.victories} Guild War attacks${this.myTries > 0 ? ` (${this.myTries} attempts remaining)` : ''}`;
+ this.end(summary);
+ }
+
+ this.attackSlot = async function(slotId) {
+ console.log(`Attacking slot ${slotId}...`);
+
+ // Check if myTries exists and is greater than 0 before attacking
+ if (this.myTries === null || this.myTries === undefined) {
+ throw new Error('Guild War attempts not available - war may not be active');
+ }
+
+ if (this.myTries <= 0) {
+ throw new Error(`No Guild War attempts remaining (myTries: ${this.myTries})`);
+ }
+
+ const isTitanBattle = (slotId === 7 || slotId === 8 || slotId === 9 || slotId === 34);
+
+ let teamConfig;
+ if (isTitanBattle) {
+ teamConfig = this.getTitanTeamConfiguration();
+
+ if (!teamConfig.titans || teamConfig.titans.length < 5) {
+ throw new Error('Titan team not properly configured - need at least 5 titans');
+ }
+
+ // Power check BEFORE running expensive simulations / consuming attempts
+ try {
+ const myPower = this.getMyTeamPower(teamConfig, true);
+ const oppPower = this.getOpponentSlotPower(slotId, true);
+ if (myPower > 0 && oppPower > 0 && myPower < (oppPower * 0.5)) {
+ const msg = `Skipped: Power check failed (my ${myPower} vs enemy ${oppPower})`;
+ console.log(`[GUILD_WAR_TITAN] ⚠️ ${msg}`);
+ setProgress(`${I18N('GUILD_WAR')}: Skipping slot ${slotId} (power too low)`);
+ throw new Error(msg);
+ }
+ } catch (e) {
+ // Re-throw explicit skip errors to continue to next slot
+ if (e?.message && e.message.startsWith('Skipped:')) throw e;
+ // Otherwise ignore power-check issues and proceed
+ }
+
+ // Run demo battle simulation for titan battles before attacking
+ console.log(`[GUILD_WAR_TITAN] Running demo battle simulation for slot ${slotId}...`);
+ try {
+ const opponentTitanTeam = this.getOpponentTitanTeamFromSlot(slotId);
+ if (opponentTitanTeam && opponentTitanTeam.titans && opponentTitanTeam.titans.length >= 5) {
+ const simulationResult = await this.simulateGuildWarTitanBattle(teamConfig, opponentTitanTeam, CONSTANTS.SIMULATION_COUNT);
+
+ console.log(`[GUILD_WAR_TITAN] Simulation results: ${simulationResult.wins}W/${simulationResult.losses}L (${simulationResult.winRate.toFixed(2)}% win rate)`);
+
+ // Check win rate threshold
+ if (simulationResult.winRate <= CONSTANTS.WIN_RATE_THRESHOLD) {
+ console.log(`[GUILD_WAR_TITAN] ⚠️ Win rate ${simulationResult.winRate.toFixed(2)}% is below ${CONSTANTS.WIN_RATE_THRESHOLD}%, skipping slot ${slotId}`);
+ setProgress(`${I18N('GUILD_WAR')}: Skipping slot ${slotId} (win rate ${simulationResult.winRate.toFixed(2)}%)`);
+ throw new Error(`Skipped: Win rate ${simulationResult.winRate.toFixed(2)}% below threshold`);
+ }
+
+ console.log(`[GUILD_WAR_TITAN] ✓ Win rate ${simulationResult.winRate.toFixed(2)}% is above threshold, proceeding with attack`);
+ } else {
+ console.warn(`[GUILD_WAR_TITAN] ⚠️ Cannot get opponent titan team data for slot ${slotId}, proceeding with attack anyway`);
+ }
+ } catch (error) {
+ if (error.message && error.message.startsWith('Skipped:')) {
+ // Re-throw skip errors to continue to next slot
+ throw error;
+ }
+ console.warn(`[GUILD_WAR_TITAN] Simulation error for slot ${slotId}:`, error);
+ console.log(`[GUILD_WAR_TITAN] Proceeding with attack despite simulation error`);
+ }
+ } else {
+ teamConfig = this.getArenaTeamConfiguration();
+
+ if (!teamConfig.heroes || teamConfig.heroes.length < 5) {
+ throw new Error('Arena team not properly configured - need at least 5 heroes');
+ }
+
+ // Power check before attacking hero slot
+ try {
+ const myPower = this.getMyTeamPower(teamConfig, false);
+ const oppPower = this.getOpponentSlotPower(slotId, false);
+ if (myPower > 0 && oppPower > 0 && myPower < (oppPower * 0.5)) {
+ const msg = `Skipped: Power check failed (my ${myPower} vs enemy ${oppPower})`;
+ console.log(`[GUILD_WAR] ⚠️ ${msg}`);
+ setProgress(`${I18N('GUILD_WAR')}: Skipping slot ${slotId} (power too low)`);
+ throw new Error(msg);
+ }
+ } catch (e) {
+ if (e?.message && e.message.startsWith('Skipped:')) throw e;
+ }
+ }
+
+ let attackArgs = {
+ slotId: slotId,
+ heroes: isTitanBattle ? teamConfig.titans.slice(0, 5) : teamConfig.heroes.slice(0, 5)
+ };
+
+ if (isTitanBattle) {
+ attackArgs.favor = {};
+ } else {
+ attackArgs.pet = teamConfig.pet;
+ attackArgs.favor = teamConfig.favor;
+ attackArgs.banner = teamConfig.banners && teamConfig.banners.length > 0 ? teamConfig.banners[0] : 1;
+ }
+
+ const calls = [
+ {
+ name: "clanWarAttack",
+ args: attackArgs,
+ context: {
+ actionTs: Utils.getActionTs()
+ },
+ ident: "body"
+ }
+ ];
+
+ const response = await Send(JSON.stringify({calls}));
+
+ if (response.error) {
+ if (response.error.name === 'NotAvailable') {
+ throw new Error('Guild War is not currently available');
+ } else if (response.error.name === 'InvalidRequest') {
+ throw new Error('Invalid attack request - check team configuration');
+ } else if (response.error.name === 'ArgumentError') {
+ throw new Error('Missing required attack arguments');
+ } else if (response.error.name === 'NotFound') {
+ throw new Error(`Target slot ${slotId} not found`);
+ } else {
+ throw new Error(`Attack failed: ${response.error.name} - ${response.error.description}`);
+ }
+ }
+
+ if (!response.results || !response.results[0]) {
+ throw new Error('Invalid attack response - no results received');
+ }
+
+ const result = response.results[0].result;
+ if (!result) {
+ throw new Error('Invalid attack response - no result data');
+ }
+
+ console.log(`Slot ${slotId} attack completed successfully`);
+
+ // Note: myTries will be refreshed from API after attack, don't manually decrement
+ // to avoid desync with server-side value
+
+ return result;
+ }
+
+ this.getArenaTeamConfiguration = function() {
+ if (!this.teamInfo || !this.teamInfo.teams) {
+ console.error('Team info not available, using fallback configuration');
+ return this.getFallbackTeamConfiguration();
+ }
+
+ const teamData = this.teamInfo.teams;
+ const favorData = this.teamInfo.favor;
+
+ const arenaTeam = teamData.arena || [];
+ const arenaFavor = favorData.arena || {};
+
+ console.log('Arena team from system:', arenaTeam);
+ console.log('Arena favor from system:', arenaFavor);
+
+ let heroes = [];
+ let pet = null;
+
+ if (arenaTeam && arenaTeam.length >= 6) {
+ heroes = arenaTeam.slice(0, 5);
+ pet = arenaTeam[5];
+ }
+
+ let banners = [1];
+ try {
+ const userInfo = getUserInfo();
+ if (userInfo && userInfo.banner) {
+ banners = typeof userInfo.banner === 'number' ? [userInfo.banner] :
+ Array.isArray(userInfo.banner) ? userInfo.banner : [1];
+ }
+ } catch (e) {
+ console.log('Could not get banner from userInfo, using default');
+ }
+
+ return {
+ heroes: heroes,
+ pet: pet,
+ favor: arenaFavor,
+ banners: banners
+ };
+ }
+
+ this.getTitanTeamConfiguration = function() {
+ if (!this.teamInfo || !this.teamInfo.teams) {
+ console.error('Team info not available, using fallback titan configuration');
+ return this.getFallbackTitanTeamConfiguration();
+ }
+
+ const teamData = this.teamInfo.teams;
+
+ const titanTeam = teamData.clan_pvp_titan || teamData.titan_arena || [];
+
+ console.log('Titan team from system:', titanTeam);
+
+ if (!titanTeam || titanTeam.length < 5) {
+ console.warn('Titan team not properly configured, using fallback');
+ return this.getFallbackTitanTeamConfiguration();
+ }
+
+ return {
+ titans: titanTeam.slice(0, 5)
+ };
+ }
+
+ this.getFallbackTeamConfiguration = function() {
+ console.log('Using fallback team configuration');
+ return {
+ heroes: [46, 57, 40, 16, 65],
+ pet: 6004,
+ favor: {},
+ banners: [1]
+ };
+ }
+
+ this.getFallbackTitanTeamConfiguration = function() {
+ console.log('Using fallback titan team configuration');
+ return {
+ titans: [4033, 4003, 4001, 4032, 4000]
+ };
+ }
+
+ this.getOpponentTitanTeamFromSlot = function(slotId) {
+ if (!this.guildWarInfo || !this.guildWarInfo.enemySlots) {
+ console.warn('[GUILD_WAR_TITAN] No enemy slots data available');
+ return null;
+ }
+
+ const slotData = this.guildWarInfo.enemySlots[slotId.toString()];
+ if (!slotData || !slotData.team || !Array.isArray(slotData.team)) {
+ console.warn(`[GUILD_WAR_TITAN] No team data found for slot ${slotId}`);
+ return null;
+ }
+
+ // Extract titan IDs from team array
+ // Team structure: [{"1": {id: 4033, ...}}, {"2": {id: 4003, ...}}, ...]
+ const titanIds = [];
+ for (const memberObj of slotData.team) {
+ if (memberObj && typeof memberObj === 'object') {
+ // Get the first key (position) and extract the titan object
+ const position = Object.keys(memberObj)[0];
+ const titan = memberObj[position];
+ if (titan && titan.id && titan.type === 'titan') {
+ titanIds.push(titan.id);
+ }
+ }
+ }
+
+ if (titanIds.length < 5) {
+ console.warn(`[GUILD_WAR_TITAN] Only found ${titanIds.length} titans in slot ${slotId}, need 5`);
+ return null;
+ }
+
+ console.log(`[GUILD_WAR_TITAN] Extracted opponent titan team from slot ${slotId}:`, titanIds);
+
+ return {
+ titans: titanIds.slice(0, 5)
+ };
+ }
+
+ this.simulateGuildWarTitanBattle = async function(myTeam, opponentTeam, simulationCount = 10) {
+ Utils.log('log', `[GUILD_WAR_TITAN] Starting ${simulationCount} demo battle simulations...`);
+
+ const mechanic = 'clan_pvp_titan';
+
+ const simulations = [];
+ let parentId = 0; // Start with 0 for first battle
+ let firstBattleId = null; // Store first battle's ID to use as parentId for subsequent battles
+
+ for (let i = 0; i < simulationCount; i++) {
+ try {
+ // First battle uses parentId=0, subsequent battles use first battle's ID as parentId
+ const result = await this.runSingleGuildWarTitanDemoBattle(myTeam, opponentTeam, mechanic, i, parentId);
+ simulations.push(result);
+
+ // For first battle: store the battle ID to use as parentId for subsequent battles
+ if (i === 0 && result.battleId) {
+ firstBattleId = result.battleId;
+ parentId = firstBattleId;
+ }
+ // For subsequent battles: use the first battle's ID as parentId
+ else if (i > 0 && firstBattleId) {
+ parentId = firstBattleId;
+ }
+ // Fallback: try to extract parentId from endBattle response
+ else if (result.parentId !== undefined && result.parentId !== null && result.parentId !== 0) {
+ parentId = result.parentId;
+ }
+ } catch (error) {
+ console.error(`[GUILD_WAR_TITAN] Simulation ${i + 1} failed:`, error);
+ simulations.push({ win: false, battleTime: 0, error: error.message, parentId: parentId });
+ }
+ }
+
+ // Calculate statistics
+ const wins = simulations.filter(s => s.win).length;
+ const losses = simulations.length - wins;
+ const winRate = (wins / simulations.length) * 100;
+ const battleTimes = simulations.map(s => s.battleTime).filter(t => t > 0);
+ const averageBattleTime = battleTimes.length > 0
+ ? battleTimes.reduce((a, b) => a + b, 0) / battleTimes.length
+ : 0;
+
+ Utils.log('log', `[GUILD_WAR_TITAN] Simulation complete: ${wins}W/${losses}L (${winRate.toFixed(1)}% win rate)`);
+
+ return {
+ total: simulations.length,
+ wins: wins,
+ losses: losses,
+ winRate: winRate,
+ averageBattleTime: averageBattleTime,
+ simulations: simulations
+ };
+ }
+
+ this.runSingleGuildWarTitanDemoBattle = async function(myTeam, opponentTeam, mechanic, seedOffset = 0, parentId = 0) {
+ return new Promise((resolve, reject) => {
+ try {
+ // Get element spirits from user info (default to dark/water if not available)
+ let firstSpiritElement = 'dark';
+ let secondSpiritElement = 'water';
+ let defenceFirstSpiritElement = 'earth';
+
+ try {
+ const userInfo = getUserInfo();
+ // Try to get element spirits from userInfo if available
+ // For now, use defaults
+ } catch (e) {
+ // Use defaults
+ }
+
+ let args = {
+ mechanic: mechanic,
+ defenceMaxUpgrade: true,
+ defenceTeam: {
+ units: opponentTeam.titans || []
+ },
+ defenceFavor: {},
+ maxUpgrade: true,
+ team: {
+ units: myTeam.titans || []
+ },
+ favor: {},
+ defenceBuffs: {},
+ buffs: {},
+ firstSpiritElement: firstSpiritElement,
+ firstSpiritSkills: {},
+ secondSpiritElement: secondSpiritElement,
+ secondSpiritSkills: {},
+ defenceFirstSpiritElement: defenceFirstSpiritElement,
+ defenceFirstSpiritSkills: {},
+ parentId: parentId,
+ entryId: 0
+ };
+
+ // Validate required fields
+ if (!args.team || !args.team.units || args.team.units.length === 0) {
+ reject(new Error('Invalid team configuration: missing or empty titan units'));
+ return;
+ }
+ if (!args.defenceTeam || !args.defenceTeam.units || args.defenceTeam.units.length === 0) {
+ reject(new Error('Invalid defence team configuration: missing or empty titan units'));
+ return;
+ }
+
+ const calls = [{
+ name: "demoBattles_startBattle",
+ args: args,
+ context: {
+ actionTs: Utils.getActionTs()
+ },
+ ident: "body"
+ }];
+
+ const startTime = Date.now();
+
+ Send(JSON.stringify({calls}))
+ .then(response => {
+ if (response.error) {
+ console.error('[GUILD_WAR_TITAN] API error:', response.error);
+ reject(new Error(`Demo battle API error: ${response.error.name} - ${response.error.description}`));
+ return;
+ }
+
+ if (!response.results || !response.results[0] || !response.results[0].result) {
+ console.error('[GUILD_WAR_TITAN] Invalid API response structure');
+ reject(new Error('Invalid demo battle API response'));
+ return;
+ }
+
+ const responseData = response.results[0].result.response;
+ const battleData = responseData?.battle || responseData;
+
+ if (!battleData) {
+ console.error('[GUILD_WAR_TITAN] No battle data found in response');
+ reject(new Error('No battle data in API response'));
+ return;
+ }
+
+ // Calculate battle result using BattleCalc
+ const battleType = battleData?.type ?? mechanic;
+ const battleConfigType = getBattleType(battleType);
+
+ BattleCalc(battleData, battleConfigType, (calcResult) => {
+ if (!Utils.isValidBattleResult(calcResult)) {
+ Utils.log('error', '[GUILD_WAR_TITAN] BattleCalc returned invalid result');
+ resolve({
+ win: false,
+ battleTime: 0,
+ error: 'Invalid calculation result',
+ parentId: parentId
+ });
+ return;
+ }
+
+ const battleTime = calcResult.battleTime || 0;
+ const win = calcResult.result.win || false;
+
+ // Call demoBattles_endBattle to get battleId for parentId chaining
+ const self = this;
+ self.endGuildWarTitanDemoBattle(calcResult, battleData)
+ .then(endBattleResult => {
+ const extractedParentId = endBattleResult?.parentId;
+ const battleId = endBattleResult?.battleId;
+
+ // Strategy: For first battle, use its ID as parentId for subsequent battles
+ let nextParentId = parentId;
+
+ if (parentId === 0 && battleId) {
+ // First battle: use its ID as parentId for next battle
+ nextParentId = battleId;
+ } else if (parentId !== 0) {
+ // Subsequent battle: keep using the first battle's ID
+ nextParentId = parentId;
+ } else if (extractedParentId && extractedParentId !== 0) {
+ // Fallback: use parentId from endBattle response
+ nextParentId = extractedParentId;
+ }
+
+ resolve({
+ win: win,
+ battleTime: battleTime,
+ result: calcResult,
+ parentId: nextParentId,
+ battleId: battleId
+ });
+ })
+ .catch(endError => {
+ Utils.log('warn', '[GUILD_WAR_TITAN] Failed to call endBattle:', endError);
+ resolve({
+ win: win,
+ battleTime: battleTime,
+ result: calcResult,
+ parentId: parentId,
+ battleId: null
+ });
+ });
+ });
+ })
+ .catch(error => {
+ console.error('[GUILD_WAR_TITAN] Error in demo battle:', error);
+ reject(error);
+ });
+ } catch (error) {
+ console.error('[GUILD_WAR_TITAN] Error preparing demo battle:', error);
+ reject(error);
+ }
+ });
+ }
+
+ this.endGuildWarTitanDemoBattle = async function(calcResult, battleData) {
+ return new Promise((resolve, reject) => {
+ try {
+ // Prepare progress data from battle calculation result
+ const progress = calcResult.progress || [];
+
+ // Ensure progress array has at least one entry
+ if (progress.length === 0 && calcResult.result) {
+ // Create minimal progress entry from result
+ progress.push({
+ v: CONSTANTS.BATTLE_VERSION,
+ b: 0,
+ seed: battleData?.seed || Math.floor(Math.random() * 1000000000),
+ attackers: {
+ input: [],
+ heroes: {}
+ },
+ defenders: {
+ input: [],
+ heroes: {}
+ }
+ });
+ }
+
+ const endBattleArgs = {
+ result: {
+ win: calcResult.result.win || false,
+ stars: calcResult.result.stars || 0
+ },
+ progress: progress
+ };
+
+ const calls = [{
+ name: "demoBattles_endBattle",
+ args: endBattleArgs,
+ context: {
+ actionTs: Utils.getActionTs()
+ },
+ ident: "body"
+ }];
+
+ Send(JSON.stringify({calls}))
+ .then(response => {
+ if (response.error) {
+ Utils.log('warn', '[GUILD_WAR_TITAN] EndBattle API error:', response.error);
+ resolve(null);
+ return;
+ }
+
+ if (!response.results || !response.results[0] || !response.results[0].result) {
+ Utils.log('warn', '[GUILD_WAR_TITAN] Invalid endBattle response structure');
+ resolve(null);
+ return;
+ }
+
+ const endBattleResponse = response.results[0].result.response;
+
+ // Extract both parentId and battleId from battle object in response
+ // Strategy: Use first battle's ID as parentId for subsequent battles
+ const battle = endBattleResponse?.battle;
+ const extractedParentId = battle?.parentId;
+ const battleId = battle?.id;
+
+ resolve({
+ parentId: extractedParentId !== undefined && extractedParentId !== null ? extractedParentId : null,
+ battleId: battleId !== undefined && battleId !== null ? battleId : null
+ });
+ })
+ .catch(error => {
+ Utils.log('warn', '[GUILD_WAR_TITAN] Error calling endBattle:', error);
+ resolve(null);
+ });
+ } catch (error) {
+ Utils.log('warn', '[GUILD_WAR_TITAN] Error preparing endBattle:', error);
+ resolve(null);
+ }
+ });
+ }
+
+ this.end = function(reason) {
+ setProgress(`${I18N('GUILD_WAR')}: ${reason}`, true);
+ console.log('Guild War completed:', reason);
+ this.resolve();
+ }
+ }
+
+ // ========== EXECUTE RAID NODES CLASS ==========
+ function executeRaidNodes(resolve, reject) {
+ let raidData = {
+ teams: [],
+ favor: {},
+ nodes: [],
+ attempts: 0,
+ countExecuteBattles: 0,
+ cancelBattle: 0,
+ }
+
+ const callsExecuteRaidNodes = {
+ calls: [{
+ name: "clanRaid_getInfo",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "clanRaid_getInfo"
+ }, {
+ name: "teamGetAll",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "teamGetAll"
+ }, {
+ name: "teamGetFavor",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "teamGetFavor"
+ }]
+ }
+
+ this.start = function () {
+ SendRequest(JSON.stringify(callsExecuteRaidNodes), startRaidNodes);
+ }
+
+ async function startRaidNodes(data) {
+ // Validate response structure
+ if (data.error) {
+ console.error('Raid Nodes: API error:', data.error);
+ endRaidNodes('APIError', data.error);
+ return;
+ }
+
+ if (!data.results || !Array.isArray(data.results) || data.results.length < 3) {
+ console.error('Raid Nodes: Invalid response structure - missing results');
+ endRaidNodes('InvalidResponse', 'Missing or invalid results array');
+ return;
+ }
+
+ if (!data.results[0] || !data.results[0].result || !data.results[0].result.response) {
+ console.error('Raid Nodes: Invalid clanRaid_getInfo response');
+ endRaidNodes('InvalidResponse', 'Invalid clanRaid_getInfo response');
+ return;
+ }
+
+ if (!data.results[1] || !data.results[1].result || !data.results[1].result.response) {
+ console.error('Raid Nodes: Invalid teamGetAll response');
+ endRaidNodes('InvalidResponse', 'Invalid teamGetAll response');
+ return;
+ }
+
+ if (!data.results[2] || !data.results[2].result || !data.results[2].result.response) {
+ console.error('Raid Nodes: Invalid teamGetFavor response');
+ endRaidNodes('InvalidResponse', 'Invalid teamGetFavor response');
+ return;
+ }
+
+ const res = data.results;
+ const clanRaidInfo = res[0].result.response;
+ const teamGetAll = res[1].result.response;
+ const teamGetFavor = res[2].result.response;
+
+ // Check attempts from clanRaid_getInfo - if 0, skip minion attack
+ const attempts = clanRaidInfo.attempts || 0;
+ if (attempts === 0) {
+ console.log('AutoBattle: Minion attempts is 0, skipping minion attack');
+ setProgress(`${I18N('MINION_RAID')}: No attempts remaining (attempts: 0)`, true);
+ endRaidNodes('NoAttempts');
+ return;
+ }
+
+ let index = 0;
+ let isNotFullPack = false;
+
+ // Validate teamGetAll structure
+ if (!teamGetAll || !teamGetAll.clanRaid_nodes || !Array.isArray(teamGetAll.clanRaid_nodes)) {
+ console.error('Raid Nodes: Invalid teamGetAll structure - missing clanRaid_nodes');
+ endRaidNodes('InvalidTeamData', 'Invalid teamGetAll structure');
+ return;
+ }
+
+ for (let team of teamGetAll.clanRaid_nodes) {
+ if (!Array.isArray(team)) {
+ console.warn('Raid Nodes: Skipping invalid team (not an array):', team);
+ continue;
+ }
+
+ if (team.length < 6) {
+ isNotFullPack = true;
+ }
+
+ const heroes = team.filter(id => id < 6000);
+ const pets = team.filter(id => id >= 6000);
+ const pet = pets.length > 0 ? pets.pop() : null;
+
+ if (heroes.length < 5) {
+ console.warn(`Raid Nodes: Team ${index} has less than 5 heroes (${heroes.length}), skipping`);
+ index++;
+ continue;
+ }
+
+ if (!pet) {
+ console.warn(`Raid Nodes: Team ${index} has no pet, using default`);
+ }
+
+ raidData.teams.push({
+ data: {},
+ heroes: heroes,
+ pet: pet || CONSTANTS.DEFAULT_PET_ID,
+ battleIndex: index++
+ });
+ }
+
+ if (raidData.teams.length === 0) {
+ console.error('Raid Nodes: No valid teams found');
+ endRaidNodes('NoTeams', 'No valid teams found');
+ return;
+ }
+ raidData.favor = teamGetFavor.clanRaid_nodes;
+
+ if (isNotFullPack) {
+ // Skip the popup confirmation for auto-execution
+ // Just continue with the raid
+ }
+
+ raidData.nodes = clanRaidInfo.nodes;
+ raidData.attempts = attempts;
+ setIsCancalBattle(false);
+
+ checkNodes();
+ }
+
+ function getAttackNode() {
+ if (!raidData.nodes || typeof raidData.nodes !== 'object') {
+ return null;
+ }
+
+ for (let nodeId in raidData.nodes) {
+ let node = raidData.nodes[nodeId];
+ if (!node || typeof node !== 'object') {
+ continue;
+ }
+
+ // Validate node structure
+ if (!node.teams || !Array.isArray(node.teams)) {
+ continue;
+ }
+
+ if (!node.timestamps || typeof node.timestamps !== 'object') {
+ continue;
+ }
+
+ let points = 0;
+ for (let team of node.teams) {
+ if (team && typeof team === 'object' && typeof team.points === 'number') {
+ points += team.points;
+ }
+ }
+
+ let now = Date.now() / 1000;
+ if (!points &&
+ typeof node.timestamps.start === 'number' &&
+ typeof node.timestamps.end === 'number' &&
+ now > node.timestamps.start &&
+ now < node.timestamps.end) {
+ let countTeam = node.teams.length;
+ delete raidData.nodes[nodeId];
+ return {
+ nodeId,
+ countTeam
+ };
+ }
+ }
+ return null;
+ }
+
+ function checkNodes() {
+ setProgress(`${I18N('REMAINING_ATTEMPTS')}: ${raidData.attempts}`);
+ let nodeInfo = getAttackNode();
+ if (nodeInfo && raidData.attempts) {
+ startNodeBattles(nodeInfo);
+ return;
+ }
+
+ endRaidNodes('EndRaidNodes');
+ }
+
+ function startNodeBattles(nodeInfo) {
+ let {nodeId, countTeam} = nodeInfo;
+ let teams = raidData.teams.slice(0, countTeam);
+ let heroes = raidData.teams.map(e => e.heroes).flat();
+ let favor = {...raidData.favor};
+ for (let heroId in favor) {
+ if (!heroes.includes(+heroId)) {
+ delete favor[heroId];
+ }
+ }
+
+ let calls = [{
+ name: "clanRaid_startNodeBattles",
+ args: {
+ nodeId,
+ teams,
+ favor
+ },
+ context: { actionTs: Utils.getActionTs() },
+ ident: "body"
+ }];
+
+ SendRequest(JSON.stringify({calls}), resultNodeBattles);
+ }
+
+ function resultNodeBattles(e) {
+ if (e['error']) {
+ endRaidNodes('nodeBattlesError', e['error']);
+ return;
+ }
+
+ // Validate response structure
+ if (!e.results || !Array.isArray(e.results) || e.results.length === 0) {
+ console.error('Raid Nodes: Invalid resultNodeBattles response - missing results');
+ endRaidNodes('InvalidResponse', 'Missing results in node battles response');
+ return;
+ }
+
+ if (!e.results[0] || !e.results[0].result || !e.results[0].result.response) {
+ console.error('Raid Nodes: Invalid resultNodeBattles response structure');
+ endRaidNodes('InvalidResponse', 'Invalid node battles response structure');
+ return;
+ }
+
+ const response = e.results[0].result.response;
+ if (!response.battles || !Array.isArray(response.battles) || response.battles.length === 0) {
+ console.error('Raid Nodes: No battles in response');
+ endRaidNodes('NoBattles', 'No battles found in response');
+ return;
+ }
+
+ console.log('Raid Nodes: Processing', response.battles.length, 'battles');
+ let battles = response.battles;
+ let promises = [];
+ let battleIndex = 0;
+ for (let battle of battles) {
+ battle.battleIndex = battleIndex++;
+ promises.push(calcBattleResult(battle));
+ }
+
+ Promise.all(promises)
+ .then(results => {
+ if (!results || results.length === 0) {
+ console.error('Raid Nodes: No battle results calculated');
+ endRaidNodes('NoResults', 'No battle results calculated');
+ return;
+ }
+
+ const endResults = {};
+ let isAllWin = true;
+ for (let r of results) {
+ if (!r || !r.result) {
+ console.warn('Raid Nodes: Invalid battle result:', r);
+ isAllWin = false;
+ continue;
+ }
+ isAllWin &&= r.result.win;
+ }
+ if (!isAllWin) {
+ if (results[0]) {
+ cancelEndNodeBattle(results[0]);
+ } else {
+ console.error('Raid Nodes: Cannot cancel battle - no results');
+ endRaidNodes('CancelError', 'Cannot cancel battle - no results');
+ }
+ return;
+ }
+ raidData.countExecuteBattles = results.length;
+ let timeout = 500;
+ for (let r of results) {
+ setTimeout(endNodeBattle, timeout, r);
+ timeout += 500;
+ }
+ })
+ .catch(error => {
+ console.error('Raid Nodes: Error calculating battle results:', error);
+ endRaidNodes('CalculationError', error);
+ });
+ }
+
+ function calcBattleResult(battleData) {
+ return new Promise(function (resolve, reject) {
+ if (!battleData) {
+ reject(new Error('No battle data provided'));
+ return;
+ }
+
+ try {
+ BattleCalc(battleData, "get_clanPvp", (result) => {
+ if (!result || !result.result) {
+ console.error('Raid Nodes: BattleCalc returned invalid result:', result);
+ reject(new Error('Invalid battle calculation result'));
+ return;
+ }
+ resolve(result);
+ });
+ } catch (error) {
+ console.error('Raid Nodes: Error in BattleCalc:', error);
+ reject(error);
+ }
+ });
+ }
+
+ function cancelEndNodeBattle(r) {
+ const fixBattle = function (heroes) {
+ for (const ids in heroes) {
+ let hero = heroes[ids];
+ hero.energy = random(1, 999);
+ if (hero.hp > 0) {
+ hero.hp = random(1, hero.hp);
+ }
+ }
+ }
+ fixBattle(r.progress[0].attackers.heroes);
+ fixBattle(r.progress[0].defenders.heroes);
+ endNodeBattle(r);
+ }
+
+ function endNodeBattle(r) {
+ // Validate battle result structure
+ if (!r) {
+ console.error('Raid Nodes: No battle result provided to endNodeBattle');
+ return;
+ }
+
+ if (!r.battleData || !r.battleData.result) {
+ console.error('Raid Nodes: Invalid battle data structure:', r);
+ return;
+ }
+
+ if (!r.result) {
+ console.error('Raid Nodes: Missing result in battle data');
+ return;
+ }
+
+ if (!r.progress || !Array.isArray(r.progress)) {
+ console.error('Raid Nodes: Missing or invalid progress array');
+ return;
+ }
+
+ let nodeId = r.battleData.result.nodeId;
+ let battleIndex = r.battleData.battleIndex;
+
+ if (!nodeId) {
+ console.error('Raid Nodes: Missing nodeId in battle result');
+ return;
+ }
+
+ if (battleIndex === undefined || battleIndex === null) {
+ console.error('Raid Nodes: Missing battleIndex in battle result');
+ return;
+ }
+
+ let calls = [{
+ name: "clanRaid_endNodeBattle",
+ args: {
+ nodeId,
+ battleIndex,
+ result: r.result,
+ progress: r.progress
+ },
+ context: { actionTs: Utils.getActionTs() },
+ ident: "body"
+ }];
+
+ SendRequest(JSON.stringify({calls}), battleResult);
+ }
+
+ function battleResult(e) {
+ if (e['error']) {
+ endRaidNodes('missionEndError', e['error']);
+ return;
+ }
+
+ // Validate response structure
+ if (!e.results || !Array.isArray(e.results) || e.results.length === 0) {
+ console.error('Raid Nodes: Invalid battleResult response - missing results');
+ endRaidNodes('InvalidResponse', 'Missing results in battle result response');
+ return;
+ }
+
+ if (!e.results[0] || !e.results[0].result || !e.results[0].result.response) {
+ console.error('Raid Nodes: Invalid battleResult response structure');
+ endRaidNodes('InvalidResponse', 'Invalid battle result response structure');
+ return;
+ }
+
+ let r = e.results[0].result.response;
+ if (r['error']) {
+ if (r.reason == "invalidBattle") {
+ raidData.cancelBattle++;
+ checkNodes();
+ } else {
+ endRaidNodes('missionEndError', r['error'] || e['error']);
+ }
+ return;
+ }
+
+ if (!(--raidData.countExecuteBattles)) {
+ raidData.attempts--;
+ checkNodes();
+ }
+ }
+
+ function endRaidNodes(reason, info) {
+ setIsCancalBattle(true);
+ let textCancel = raidData.cancelBattle ? ` ${I18N('BATTLES_CANCELED')}: ${raidData.cancelBattle}` : '';
+ setProgress(`${I18N('MINION_RAID')} ${I18N('COMPLETED')}! ${textCancel}`, true);
+ console.log(reason, info);
+ resolve();
+ }
+ }
+
+ // ========== EXECUTE RAID BOSS CLASS ==========
+ function executeRaidBoss(resolve, reject) {
+ this.resolve = resolve;
+ this.reject = reject;
+ this.bossAttempts = 0;
+ this.attacksCompleted = 0;
+ this.heroTeams = [
+ [46, 52, 48, 40, 37],
+ [58, 50, 42, 9, 51],
+ [64, 13, 29, 1, 43],
+ [16, 65, 57, 31, 61],
+ [56, 62, 55, 63, 28]
+ ];
+ this.pets = [6005, 6005, 6005, 6005, 6006];
+ this.favorTeams = [
+ { "37": 6000, "40": 6004, "46": 6001, "48": 6005, "52": 6006 },
+ { "9": 6004, "42": 6006, "50": 6001, "58": 6005 },
+ { "1": 6004, "13": 6008, "29": 6006, "43": 6002, "64": 6005 },
+ { "16": 6004, "31": 6006, "57": 6003, "61": 6001, "65": 6000 },
+ { "28": 6004, "55": 6005, "56": 6006, "62": 6008, "63": 6003 }
+ ];
+
+ this.start = async function() {
+ setProgress('Raid Boss: Initializing...');
+ try {
+ await this.getRaidInfo();
+
+ if (this.bossAttempts <= 0) {
+ this.end('No boss attempts remaining');
+ return;
+ }
+
+ await this.attackBoss();
+ } catch (error) {
+ console.error('Raid Boss error:', error);
+ this.end(`Error: ${error.message}`);
+ }
+ }
+
+ this.getRaidInfo = async function() {
+ const calls = [{
+ name: "clanRaid_getInfo",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "clanRaid_getInfo"
+ }];
+
+ const response = await Send(JSON.stringify({calls}));
+
+ if (response.error) {
+ throw new Error(`Raid info API error: ${response.error.name} - ${response.error.description}`);
+ }
+
+ if (!response.results || !response.results[0] || !response.results[0].result || !response.results[0].result.response) {
+ throw new Error('Invalid clanRaid_getInfo response');
+ }
+
+ this.raidInfo = response.results[0].result.response;
+ this.bossAttempts = this.raidInfo.bossAttempts || 0;
+
+ const currentBoss = this.raidInfo.stats?.currentBoss || "1";
+ const bossName = currentBoss === "1" ? "OSH" : "Mastro";
+
+ console.log(`Raid Boss: ${bossName}, Attempts: ${this.bossAttempts}`);
+ setProgress(`Raid Boss: ${bossName} - ${this.bossAttempts} attempts available`);
+ }
+
+
+ this.attackBoss = async function() {
+ const maxAttacks = Math.min(5, this.bossAttempts);
+
+ for (let i = 0; i < maxAttacks; i++) {
+ if (this.bossAttempts <= 0) {
+ break;
+ }
+
+ setProgress(`Raid Boss: Attack ${i + 1}/${maxAttacks}`);
+
+ try {
+ const teamIndex = i % this.heroTeams.length;
+ const heroes = this.heroTeams[teamIndex];
+ const pet = this.pets[teamIndex];
+ const favor = this.favorTeams[teamIndex];
+
+ const battleData = await this.startBossBattle(heroes, pet, favor);
+ const battleResult = await this.calculateBattleResult(battleData);
+ await this.endBossBattle(battleResult);
+
+ this.attacksCompleted++;
+ this.bossAttempts--;
+
+ if (i < maxAttacks - 1) {
+ await new Promise(resolve => setTimeout(resolve, CONSTANTS.DELAY_BETWEEN_BATTLES));
+ }
+ } catch (error) {
+ console.error(`Error in attack ${i + 1}:`, error);
+ break; // Stop on error to avoid wasting attempts
+ }
+ }
+
+ this.end(`Completed ${this.attacksCompleted} boss attacks`);
+ }
+
+
+ this.startBossBattle = async function(heroes, pet, favor) {
+ const calls = [{
+ name: "clanRaid_startBossBattle",
+ args: {
+ heroes: heroes,
+ pet: pet,
+ favor: favor
+ },
+ context: { actionTs: Utils.getActionTs() },
+ ident: "body"
+ }];
+
+ const response = await Send(JSON.stringify({calls}));
+
+ if (response.error) {
+ throw new Error(`Start boss battle failed: ${response.error.name} - ${response.error.description}`);
+ }
+
+ if (!response.results || !response.results[0] || !response.results[0].result) {
+ throw new Error('Invalid start boss battle response');
+ }
+
+ const battleData = response.results[0].result.response;
+ if (!battleData || !battleData.battle) {
+ throw new Error('No battle data in response');
+ }
+
+ return battleData.battle;
+ }
+
+ this.calculateBattleResult = async function(battleData) {
+ return new Promise((resolve, reject) => {
+ BattleCalc(battleData, getBattleType('clan_raid'), (result) => {
+ if (!result || !result.result) {
+ console.error('BattleCalc returned invalid result:', result);
+ reject(new Error('Invalid battle calculation result'));
+ return;
+ }
+ resolve({
+ win: result.result.win,
+ progress: result.progress,
+ result: result.result,
+ battleData: battleData
+ });
+ });
+ });
+ }
+
+ this.endBossBattle = async function(battleResult) {
+ const calls = [{
+ name: "clanRaid_endBossBattle",
+ args: {
+ result: {
+ win: battleResult.win,
+ stars: battleResult.result.stars || 0
+ },
+ progress: battleResult.progress
+ },
+ context: { actionTs: Utils.getActionTs() },
+ ident: "group_1_body"
+ }];
+
+ const response = await Send(JSON.stringify({calls}));
+
+ if (response.error) {
+ throw new Error(`End boss battle failed: ${response.error.name} - ${response.error.description}`);
+ }
+ }
+
+ this.end = function(reason) {
+ setProgress(`Raid Boss: ${reason}`, true);
+ console.log('Raid Boss completed:', reason);
+ this.resolve();
+ }
+ }
+
+ // ========== EXECUTE CROSS CLAN WAR CLASS ==========
+ function executeCrossClanWar(resolve, reject) {
+ this.resolve = resolve;
+ this.reject = reject;
+ this.currentUserId = null;
+ this.attackMapData = null;
+ this.teamInfo = null;
+ this.victories = 0;
+ this.attacksCompleted = 0;
+
+ this.start = async function() {
+ setProgress('Cross Clan War: Initializing...');
+ try {
+ await this.getCurrentUserId();
+ if (!this.currentUserId) {
+ this.end('Could not get current user ID');
+ return;
+ }
+
+ await this.getAttackMap();
+ await this.getTeamData();
+ await this.attackAssignedTargets();
+ } catch (error) {
+ console.error('Cross Clan War error:', error);
+ this.end(`Error: ${error.message}`);
+ }
+ }
+
+ this.getCurrentUserId = async function() {
+ try {
+ const calls = [{
+ name: "userGetInfo",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "body"
+ }];
+
+ const response = await Send(JSON.stringify({calls}));
+
+ if (response.error) {
+ throw new Error(`User info API error: ${response.error.name} - ${response.error.description}`);
+ }
+
+ if (!response.results || !response.results[0] || !response.results[0].result || !response.results[0].result.response) {
+ throw new Error('Invalid userGetInfo response');
+ }
+
+ const userInfo = response.results[0].result.response;
+ this.currentUserId = userInfo.id ? parseInt(userInfo.id, 10) : null;
+
+ if (!this.currentUserId) {
+ throw new Error('User ID not found in response');
+ }
+
+ console.log(`Cross Clan War: Current user ID: ${this.currentUserId}`);
+ return this.currentUserId;
+ } catch (error) {
+ console.error('Error getting current user ID:', error);
+ throw error;
+ }
+ }
+
+ this.getAttackMap = async function() {
+ console.log('Getting Cross Clan War attack map...');
+
+ const calls = [{
+ name: "crossClanWar_getAttackMap",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "body"
+ }];
+
+ const response = await Send(JSON.stringify({calls}));
+
+ if (response.error) {
+ throw new Error(`Attack map API error: ${response.error.name} - ${response.error.description}`);
+ }
+
+ if (!response.results || !response.results[0] || !response.results[0].result || !response.results[0].result.response) {
+ throw new Error('Invalid crossClanWar_getAttackMap response');
+ }
+
+ this.attackMapData = response.results[0].result.response;
+ console.log('Cross Clan War attack map loaded');
+ }
+
+ this.getTeamData = async function() {
+ console.log('Getting team data...');
+
+ const calls = [
+ {
+ name: "teamGetAll",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "teamGetAll"
+ },
+ {
+ name: "teamGetFavor",
+ args: {},
+ context: { actionTs: Utils.getActionTs() },
+ ident: "teamGetFavor"
+ }
+ ];
+
+ const response = await Send(JSON.stringify({calls}));
+
+ if (response.error) {
+ throw new Error(`Team data API error: ${response.error.name} - ${response.error.description}`);
+ }
+
+ if (!response.results[0] || !response.results[0].result || !response.results[0].result.response) {
+ throw new Error('Invalid teamGetAll response');
+ }
+ if (!response.results[1] || !response.results[1].result || !response.results[1].result.response) {
+ throw new Error('Invalid teamGetFavor response');
+ }
+
+ this.teamInfo = {
+ teams: response.results[0].result.response,
+ favor: response.results[1].result.response
+ };
+
+ console.log('Team data loaded');
+ }
+
+ this.attackAssignedTargets = async function() {
+ if (!this.attackMapData || !this.attackMapData.targets) {
+ this.end('No targets available');
+ return;
+ }
+
+ const targets = this.attackMapData.targets;
+ const enemySlots = this.attackMapData.enemySlots || {};
+
+ // Filter targets assigned to current user with state === 0
+ const myTargets = [];
+ Object.entries(targets).forEach(([slotId, target]) => {
+ if (target.userId === this.currentUserId && target.state === 0) {
+ myTargets.push({ slotId, target });
+ }
+ });
+
+ if (myTargets.length === 0) {
+ this.end('No targets assigned to you or all targets already completed');
+ return;
+ }
+
+ console.log(`Cross Clan War: Found ${myTargets.length} targets assigned to you`);
+ setProgress(`Cross Clan War: Attacking ${myTargets.length} targets...`);
+
+ let skippedCount = 0; // Track skipped slots separately
+
+ // Process all targets - ensure loop always continues even on errors
+ for (let i = 0; i < myTargets.length; i++) {
+ const { slotId, target } = myTargets[i];
+ const enemySlot = enemySlots[slotId];
+ let attackSuccess = false;
+ let attackError = null;
+
+ try {
+ console.log(`Cross Clan War: ===== Starting attack ${i + 1}/${myTargets.length} - Slot ${slotId} =====`);
+ setProgress(`Cross Clan War: Slot ${slotId} (${i + 1}/${myTargets.length})`);
+
+ // Attempt the attack
+ await this.attackSlot(slotId, target, enemySlot);
+ attackSuccess = true;
+ this.attacksCompleted++;
+ this.victories++;
+ console.log(`Cross Clan War: ✓ Successfully completed attack ${i + 1}/${myTargets.length} - Slot ${slotId}`);
+
+ } catch (error) {
+ attackError = error;
+ attackSuccess = false;
+
+ // Determine if this is a skip (dead units, not available) vs actual failure
+ const errorMsg = error.message || String(error);
+ const isSkip = errorMsg.includes('dead units') ||
+ errorMsg.includes('not available') ||
+ errorMsg.includes('skipping');
+
+ if (isSkip) {
+ // Don't count skips as attempts - these are intentional skips
+ skippedCount++;
+ console.warn(`Cross Clan War: ⚠ Skipping slot ${slotId} (${i + 1}/${myTargets.length}): ${errorMsg}`);
+ } else {
+ // Count actual failures as attempts
+ this.attacksCompleted++;
+ console.error(`Cross Clan War: ✗ Error attacking slot ${slotId} (${i + 1}/${myTargets.length}):`, error);
+ console.error(`Cross Clan War: Error message: ${errorMsg}`);
+ if (error.stack) {
+ console.error(`Cross Clan War: Error stack:`, error.stack);
+ }
+ }
+ }
+
+ // Always continue to next target, regardless of success or failure
+ if (i < myTargets.length - 1) {
+ if (attackSuccess) {
+ console.log(`Cross Clan War: Attack ${i + 1} completed, proceeding to next target...`);
+ } else {
+ console.log(`Cross Clan War: Attack ${i + 1} failed, but continuing to next target...`);
+ }
+ // Add delay before next attack
+ await new Promise(resolve => setTimeout(resolve, CONSTANTS.DELAY_BETWEEN_BATTLES));
+ } else {
+ // Last target
+ if (attackSuccess) {
+ console.log(`Cross Clan War: Final attack completed`);
+ } else {
+ console.log(`Cross Clan War: Final attack failed`);
+ }
+ }
+ }
+
+ console.log(`Cross Clan War: ===== All attacks completed =====`);
+ console.log(`Cross Clan War: Total attempts: ${this.attacksCompleted}, Victories: ${this.victories}, Skipped: ${skippedCount}`);
+ let summary = `Completed ${this.victories}/${this.attacksCompleted} attacks`;
+ if (skippedCount > 0) {
+ summary += ` (${skippedCount} skipped)`;
+ }
+ this.end(summary);
+ }
+
+ this.attackSlot = async function(slotId, target, enemySlot) {
+ let battleData = null;
+ let battleResult = null;
+
+ try {
+ console.log(`Cross Clan War: [attackSlot] Starting attack on slot ${slotId}`);
+ console.log(`Cross Clan War: [attackSlot] Target data:`, target);
+
+ // Verify slot is available
+ if (enemySlot) {
+ if (enemySlot.status !== "ready" || enemySlot.attackerId !== null) {
+ throw new Error(`Slot ${slotId} is not available for attack (status: ${enemySlot.status}, attackerId: ${enemySlot.attackerId})`);
+ }
+
+ // Check if any units are clearly dead (more lenient check)
+ // Only skip if we can definitively see dead units
+ const team = enemySlot.team || {};
+ const teamValues = Object.values(team);
+
+ if (teamValues.length > 0) {
+ // Check if any unit is explicitly marked as dead
+ const hasDeadUnits = teamValues.some(unit => {
+ // Only consider dead if state exists and explicitly says isDead === true
+ return unit && unit.state && unit.state.isDead === true;
+ });
+
+ if (hasDeadUnits) {
+ console.warn(`Cross Clan War: [attackSlot] Slot ${slotId} has dead units, skipping...`);
+ throw new Error(`Slot ${slotId} has dead units - skipping`);
+ }
+ }
+ }
+
+ // Determine battle type
+ let battleType = null;
+ if (enemySlot && enemySlot.team) {
+ const team = enemySlot.team;
+ for (const unit of Object.values(team)) {
+ if (unit.type === "hero") {
+ battleType = "hero";
+ break;
+ } else if (unit.type === "titan") {
+ battleType = "titan";
+ break;
+ }
+ }
+ }
+
+ // Fallback: use slot ID to guess (lower IDs are usually hero battles)
+ if (!battleType) {
+ const slotNum = parseInt(slotId);
+ battleType = slotNum <= 16 ? "hero" : "titan";
+ console.warn(`Cross Clan War: [attackSlot] Could not determine battle type from enemySlot, using slot ID heuristic: ${battleType}`);
+ }
+
+ console.log(`Cross Clan War: [attackSlot] Battle type: ${battleType}, teamIndex: ${target.teamIndex}`);
+
+ // Get team configuration
+ let teamConfig;
+ try {
+ teamConfig = this.getTeamConfiguration(target.teamIndex, battleType);
+ console.log(`Cross Clan War: [attackSlot] Team config obtained:`, {
+ type: teamConfig.type,
+ heroes: teamConfig.heroes || teamConfig.titans,
+ pet: teamConfig.pet,
+ favorKeys: teamConfig.favor ? Object.keys(teamConfig.favor).length : 0
+ });
+ } catch (error) {
+ console.error(`Cross Clan War: [attackSlot] Error getting team configuration:`, error);
+ throw new Error(`Failed to get team configuration: ${error.message}`);
+ }
+
+ // Start battle
+ try {
+ console.log(`Cross Clan War: [attackSlot] Starting battle...`);
+ battleData = await this.startBattle(parseInt(slotId), teamConfig, battleType);
+ console.log(`Cross Clan War: [attackSlot] Battle started successfully`);
+ } catch (error) {
+ console.error(`Cross Clan War: [attackSlot] Error starting battle:`, error);
+ throw new Error(`Failed to start battle: ${error.message}`);
+ }
+
+ // Calculate battle result
+ try {
+ console.log(`Cross Clan War: [attackSlot] Calculating battle result...`);
+ battleResult = await this.calculateBattleResult(battleData, battleType);
+ console.log(`Cross Clan War: [attackSlot] Battle result: ${battleResult.win ? 'Victory' : 'Defeat'}`);
+ } catch (error) {
+ console.error(`Cross Clan War: [attackSlot] Error calculating battle result:`, error);
+ // If battle was started but calculation failed, we should still try to end it
+ // But for now, just throw the error and let the caller handle it
+ throw new Error(`Failed to calculate battle result: ${error.message}`);
+ }
+
+ // End battle
+ try {
+ console.log(`Cross Clan War: [attackSlot] Ending battle...`);
+ await this.endBattle(parseInt(slotId), battleResult);
+ console.log(`Cross Clan War: [attackSlot] Battle ended successfully`);
+ } catch (error) {
+ console.error(`Cross Clan War: [attackSlot] Error ending battle:`, error);
+ // Even if ending fails, the battle was attempted, so we consider it an error but continue
+ throw new Error(`Failed to end battle: ${error.message}`);
+ }
+
+ console.log(`Cross Clan War: [attackSlot] ✓ Slot ${slotId} attack completed: ${battleResult.win ? 'Victory' : 'Defeat'}`);
+ } catch (error) {
+ console.error(`Cross Clan War: [attackSlot] ✗ Error in attackSlot for slot ${slotId}:`, error);
+ if (error.stack) {
+ console.error(`Cross Clan War: [attackSlot] Error stack:`, error.stack);
+ }
+ // Always re-throw to ensure calling function knows about the failure
+ throw error;
+ }
+ }
+
+ this.getTeamConfiguration = function(teamIndex, battleType) {
+ if (!this.teamInfo || !this.teamInfo.teams) {
+ throw new Error('Team info not available');
+ }
+
+ const teamData = this.teamInfo.teams;
+ const favorData = this.teamInfo.favor;
+
+ if (battleType === "hero") {
+ const crossClanDefenceHeroes = teamData.crossClanDefence_heroes || [];
+
+ if (teamIndex < 0 || teamIndex >= crossClanDefenceHeroes.length) {
+ throw new Error(`Invalid teamIndex ${teamIndex} for crossClanDefence_heroes`);
+ }
+
+ const team = crossClanDefenceHeroes[teamIndex];
+ if (!team || team.length < 6) {
+ throw new Error(`Invalid team configuration at index ${teamIndex}`);
+ }
+
+ const heroes = team.slice(0, 5);
+ const pet = team[5];
+
+ // Get favor for this team
+ // Favor structure: favorData.crossClanDefence_heroes is a flat object
+ // where keys are hero IDs (as numbers or strings) and values are pet IDs
+ // Example: {1: 6006, 9: 6007, 13: 6002, 16: 6004, ...}
+ const crossClanDefenceFavor = favorData.crossClanDefence_heroes || {};
+ let favor = {};
+
+ console.log(`Cross Clan War: Getting favor for teamIndex ${teamIndex}`);
+ console.log(`Cross Clan War: crossClanDefenceFavor structure:`, crossClanDefenceFavor);
+ console.log(`Cross Clan War: Team heroes:`, heroes);
+
+ // Build favor object by looking up each hero in the flat favor structure
+ if (crossClanDefenceFavor && typeof crossClanDefenceFavor === 'object' && !Array.isArray(crossClanDefenceFavor)) {
+ // Check if it's a nested structure (team index -> favor object) or flat (hero ID -> pet ID)
+ // If the first hero ID exists as a key, it's a flat structure
+ const firstHeroId = heroes[0];
+ const isFlatStructure = firstHeroId !== undefined &&
+ (crossClanDefenceFavor[firstHeroId] !== undefined ||
+ crossClanDefenceFavor[String(firstHeroId)] !== undefined);
+
+ if (isFlatStructure) {
+ // Flat structure: hero ID -> pet ID
+ heroes.forEach(heroId => {
+ // Try both number and string key
+ const petId = crossClanDefenceFavor[heroId] || crossClanDefenceFavor[String(heroId)];
+ if (petId !== undefined && typeof petId === 'number') {
+ // Favor object uses hero IDs as string keys
+ favor[String(heroId)] = petId;
+ }
+ });
+ console.log(`Cross Clan War: Built favor from flat structure:`, favor);
+ } else {
+ // Nested structure: team index -> favor object
+ // Try numeric index first
+ if (crossClanDefenceFavor[teamIndex] !== undefined) {
+ const favorValue = crossClanDefenceFavor[teamIndex];
+ if (favorValue && typeof favorValue === 'object' && !Array.isArray(favorValue)) {
+ favor = favorValue;
+ console.log(`Cross Clan War: Got favor from nested structure at index ${teamIndex}:`, favor);
+ } else {
+ console.warn(`Cross Clan War: Favor at index ${teamIndex} is not an object (got ${typeof favorValue}: ${favorValue}), building from flat structure`);
+ // Fallback: try to build from flat structure
+ heroes.forEach(heroId => {
+ const petId = crossClanDefenceFavor[heroId] || crossClanDefenceFavor[String(heroId)];
+ if (petId !== undefined && typeof petId === 'number') {
+ favor[String(heroId)] = petId;
+ }
+ });
+ }
+ } else {
+ // Try string key
+ const stringKey = String(teamIndex);
+ if (crossClanDefenceFavor[stringKey] !== undefined) {
+ const favorValue = crossClanDefenceFavor[stringKey];
+ if (favorValue && typeof favorValue === 'object' && !Array.isArray(favorValue)) {
+ favor = favorValue;
+ console.log(`Cross Clan War: Got favor from nested structure at string key "${stringKey}":`, favor);
+ } else {
+ console.warn(`Cross Clan War: Favor at string key "${stringKey}" is not an object, building from flat structure`);
+ // Fallback: try to build from flat structure
+ heroes.forEach(heroId => {
+ const petId = crossClanDefenceFavor[heroId] || crossClanDefenceFavor[String(heroId)];
+ if (petId !== undefined && typeof petId === 'number') {
+ favor[String(heroId)] = petId;
+ }
+ });
+ }
+ } else {
+ console.log(`Cross Clan War: No favor found at index ${teamIndex}, building from flat structure`);
+ // Build from flat structure as fallback
+ heroes.forEach(heroId => {
+ const petId = crossClanDefenceFavor[heroId] || crossClanDefenceFavor[String(heroId)];
+ if (petId !== undefined && typeof petId === 'number') {
+ favor[String(heroId)] = petId;
+ }
+ });
+ }
+ }
+ }
+ } else {
+ console.warn(`Cross Clan War: crossClanDefenceFavor is not a valid object, using empty favor`);
+ }
+
+ // Validate favor is an object (hero IDs as string keys, pet IDs as values)
+ if (typeof favor !== 'object' || Array.isArray(favor)) {
+ console.warn(`Cross Clan War: Invalid favor structure after processing, using empty object. Got:`, favor, `(type: ${typeof favor})`);
+ favor = {};
+ }
+
+ console.log(`Cross Clan War: Final favor object:`, favor);
+
+ // Get banner
+ let banner = 1;
+ try {
+ const userInfo = getUserInfo();
+ if (userInfo && userInfo.banner) {
+ banner = typeof userInfo.banner === 'number' ? userInfo.banner :
+ Array.isArray(userInfo.banner) ? userInfo.banner[0] : 1;
+ }
+ } catch (e) {
+ console.log('Could not get banner from userInfo, using default');
+ }
+
+ return {
+ type: 'hero',
+ heroes: heroes,
+ pet: pet,
+ favor: favor,
+ banner: banner
+ };
+ } else {
+ const crossClanDefenceTitans = teamData.crossClanDefence_titans || [];
+
+ if (teamIndex < 0 || teamIndex >= crossClanDefenceTitans.length) {
+ throw new Error(`Invalid teamIndex ${teamIndex} for crossClanDefence_titans`);
+ }
+
+ const titans = crossClanDefenceTitans[teamIndex];
+ if (!titans || titans.length < 5) {
+ throw new Error(`Invalid titan team configuration at index ${teamIndex}`);
+ }
+
+ return {
+ type: 'titan',
+ titans: titans.slice(0, 5)
+ };
+ }
+ }
+
+ this.startBattle = async function(slotId, teamConfig, battleType) {
+ let args = {
+ slotId: slotId
+ };
+
+ if (battleType === "hero") {
+ args.team = {
+ units: teamConfig.heroes,
+ pet: teamConfig.pet
+ };
+
+ // Ensure favor is always an object (hero IDs as string keys, pet IDs as values)
+ let favor = teamConfig.favor || {};
+ if (typeof favor !== 'object' || Array.isArray(favor)) {
+ console.warn(`Cross Clan War: Invalid favor type (${typeof favor}), using empty object. Value:`, favor);
+ favor = {};
+ }
+ args.favor = favor;
+
+ args.banner = teamConfig.banner || 1;
+
+ // Log the request for debugging
+ console.log(`Cross Clan War: Battle args for slot ${slotId}:`, {
+ slotId: args.slotId,
+ team: args.team,
+ favor: args.favor,
+ banner: args.banner,
+ favorType: typeof args.favor,
+ favorIsArray: Array.isArray(args.favor)
+ });
+ } else {
+ // Titan battle
+ args.team = {
+ units: teamConfig.titans
+ };
+ // Include favor even for titan battles (should be empty object)
+ let favor = teamConfig.favor || {};
+ if (typeof favor !== 'object' || Array.isArray(favor)) {
+ console.warn(`Cross Clan War: Invalid favor type for titan battle (${typeof favor}), using empty object. Value:`, favor);
+ favor = {};
+ }
+ args.favor = favor;
+
+ // Log the request for debugging
+ console.log(`Cross Clan War: Battle args for slot ${slotId} (titan):`, {
+ slotId: args.slotId,
+ team: args.team,
+ favor: args.favor,
+ favorType: typeof args.favor,
+ favorIsArray: Array.isArray(args.favor)
+ });
+ }
+
+ const calls = [{
+ name: "crossClanWar_startBattle",
+ args: args,
+ context: { actionTs: Utils.getActionTs() },
+ ident: "body"
+ }];
+
+ console.log(`Cross Clan War: Starting battle for slot ${slotId} (${battleType})`);
+ const response = await Send(JSON.stringify({calls}));
+
+ if (response.error) {
+ throw new Error(`Start battle failed: ${response.error.name} - ${response.error.description}`);
+ }
+
+ if (!response.results || !response.results[0] || !response.results[0].result) {
+ throw new Error('Invalid start battle response');
+ }
+
+ const battleData = response.results[0].result.response;
+ if (!battleData || !battleData.battle) {
+ throw new Error('No battle data in response');
+ }
+
+ return battleData.battle;
+ }
+
+ this.calculateBattleResult = async function(battleData, battleType) {
+ return new Promise((resolve, reject) => {
+ const battleTypeStr = battleType === "hero" ? "clan_global_pvp" : "clan_global_pvp_titan";
+ BattleCalc(battleData, getBattleType(battleTypeStr), (result) => {
+ if (!result || !result.result) {
+ console.error('BattleCalc returned invalid result:', result);
+ reject(new Error('Invalid battle calculation result'));
+ return;
+ }
+ resolve({
+ win: result.result.win,
+ progress: result.progress,
+ result: result.result,
+ battleData: battleData
+ });
+ });
+ });
+ }
+
+ this.endBattle = async function(slotId, battleResult) {
+ const calls = [{
+ name: "crossClanWar_endBattle",
+ args: {
+ slotId: slotId,
+ result: {
+ win: battleResult.win,
+ stars: battleResult.result.stars || 0
+ },
+ progress: battleResult.progress
+ },
+ context: { actionTs: Utils.getActionTs() },
+ ident: "body"
+ }];
+
+ const response = await Send(JSON.stringify({calls}));
+
+ if (response.error) {
+ throw new Error(`End battle failed: ${response.error.name} - ${response.error.description}`);
+ }
+ }
+
+ this.end = function(reason) {
+ setProgress(`Cross Clan War: ${reason}`, true);
+ console.log('Cross Clan War completed:', reason);
+ this.resolve();
+ }
+ }
+
+ // Store classes in HWHClasses for consistency
+ HWHClasses.executeArena = executeArena;
+ HWHClasses.executeGuildWar = executeGuildWar;
+ HWHClasses.executeRaidNodes = executeRaidNodes;
+ HWHClasses.executeRaidBoss = executeRaidBoss;
+ HWHClasses.executeCrossClanWar = executeCrossClanWar;
+
+ let autoBattleRunning = false;
+
+ function isDungeonActive() {
+ return !!(window.HWH_DUNGEON_RUNNING || window.HWH_DUNGEON_BATTLE_OPEN);
+ }
+
+ function sleep(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+
+ async function waitForDungeonIdle(maxWaitMs = 60000) {
+ const start = Date.now();
+ while (isDungeonActive() && Date.now() - start < maxWaitMs) {
+ await sleep(500);
+ }
+ return !isDungeonActive();
+ }
+
+ function shouldStopForDungeon() {
+ return isDungeonActive();
+ }
+
+ async function runAutoBattleStep(label, progressMsg, fn) {
+ if (shouldStopForDungeon()) {
+ console.log(`AutoBattle: Skipping ${label} — dungeon active`);
+ return false;
+ }
+ try {
+ console.log(`AutoBattle: Starting ${label}...`);
+ HWHFuncs.setProgress(progressMsg);
+ await fn();
+ console.log(`%cAutoBattle: ${label} completed`, 'color: lightgreen; font-weight: bold;');
+ return true;
+ } catch (error) {
+ console.error(`AutoBattle: ${label} error:`, error);
+ return false;
+ }
+ }
+
+ async function autoBattle() {
+ if (autoBattleRunning) {
+ console.log('AutoBattle: Already running — skipped');
+ return;
+ }
+ if (isDungeonActive()) {
+ console.log('AutoBattle: Dungeon active — waiting before auto-battles...');
+ HWHFuncs.setProgress('AutoBattle: Waiting for dungeon...', true);
+ const ready = await waitForDungeonIdle();
+ if (!ready) {
+ console.warn('AutoBattle: Dungeon still active — skipping auto-battles');
+ return;
+ }
+ }
+
+ autoBattleRunning = true;
+ window.HWH_AUTOBATTLE_RUNNING = true;
+
+ try {
+ console.log('AutoBattle: Starting auto-battle sequence...');
+ HWHFuncs.setProgress('AutoBattle: Starting auto-battles...');
+
+ const results = {
+ arena: false,
+ grandArena: false,
+ guildWar: false,
+ raidNodes: false,
+ raidBoss: false,
+ titanArena: false,
+ crossClanWar: false
+ };
+
+ results.arena = await runAutoBattleStep('Arena', 'AutoBattle: Arena battles...', () =>
+ new Promise((resolve, reject) => {
+ const arena = new executeArena(resolve, reject);
+ arena.start('arena');
+ })
+ );
+
+ results.grandArena = await runAutoBattleStep('Grand Arena', 'AutoBattle: Grand Arena battles...', () =>
+ new Promise((resolve, reject) => {
+ const grandArena = new executeArena(resolve, reject);
+ grandArena.start('grand');
+ })
+ );
+
+ results.guildWar = await runAutoBattleStep('Guild War', 'AutoBattle: Guild War attacks...', () =>
+ new Promise((resolve, reject) => {
+ const guildWar = new executeGuildWar(resolve, reject);
+ guildWar.start();
+ })
+ );
+
+ results.raidNodes = await runAutoBattleStep('Raid Nodes', 'AutoBattle: Raid Nodes...', () =>
+ new Promise((resolve, reject) => {
+ const raidNodes = new executeRaidNodes(resolve, reject);
+ raidNodes.start();
+ })
+ );
+
+ if (shouldStopForDungeon()) {
+ console.log('AutoBattle: Stopping before Titan Arena — dungeon active');
+ } else {
+ try {
+ if (Utils.isTitanArenaDay()) {
+ results.titanArena = await runAutoBattleStep('Titan Arena (ToE)', 'AutoBattle: Titan Arena (ToE)...', async () => {
+ if (window.HWHClasses && window.HWHClasses.executeTitanArena) {
+ await new Promise((resolve, reject) => {
+ const titanArena = new window.HWHClasses.executeTitanArena(resolve, reject);
+ titanArena.start();
+ });
+ } else if (window.testTitanArena && typeof window.testTitanArena === 'function') {
+ await window.testTitanArena();
+ } else {
+ throw new Error('Titan Arena execution class not available');
+ }
+ });
+ } else {
+ Utils.log('log', `AutoBattle: Skipping Titan Arena (not Monday-Saturday, current day: ${Utils.getDayOfWeek()})`);
+ }
+ } catch (error) {
+ console.error('AutoBattle: Titan Arena error:', error);
+ }
+ }
+
+ if (shouldStopForDungeon()) {
+ console.log('AutoBattle: Stopping before Raid Boss — dungeon active');
+ } else {
+ try {
+ if (Utils.isRaidBossDay()) {
+ results.raidBoss = await runAutoBattleStep('Raid Boss', 'AutoBattle: Raid Boss attacks...', () =>
+ new Promise((resolve, reject) => {
+ const raidBoss = new executeRaidBoss(resolve, reject);
+ raidBoss.start();
+ })
+ );
+ } else {
+ Utils.log('log', `AutoBattle: Skipping Raid Boss (not Saturday/Sunday, current day: ${Utils.getDayOfWeek()})`);
+ }
+ } catch (error) {
+ console.error('AutoBattle: Raid Boss error:', error);
+ }
+ }
+
+ results.crossClanWar = await runAutoBattleStep('Cross Clan War', 'AutoBattle: Cross Clan War attacks...', () =>
+ new Promise((resolve, reject) => {
+ const crossClanWar = new executeCrossClanWar(resolve, reject);
+ crossClanWar.start();
+ })
+ );
+
+ const completed = Object.values(results).filter(v => v === true).length;
+ const total = Object.keys(results).length;
+ const summary = [
+ `Arena: ${results.arena ? '✓' : '✗'}`,
+ `Grand Arena: ${results.grandArena ? '✓' : '✗'}`,
+ `Guild War: ${results.guildWar ? '✓' : '✗'}`,
+ `Raid Nodes: ${results.raidNodes ? '✓' : '✗'}`,
+ `Titan Arena: ${results.titanArena ? '✓' : '✗'}`,
+ `Raid Boss: ${results.raidBoss ? '✓' : '✗'}`,
+ `Cross Clan War: ${results.crossClanWar ? '✓' : '✗'}`
+ ].join(' | ');
+
+ console.log(`%cAutoBattle: Completed ${completed}/${total} battle types`, 'color: cyan; font-weight: bold;');
+ console.log(summary);
+ HWHFuncs.setProgress(`AutoBattle: Complete! ${completed}/${total} battle types executed.`, true);
+ } catch (error) {
+ console.error('AutoBattle: Fatal error:', error);
+ HWHFuncs.setProgress(`AutoBattle: Error - ${error.message}`, true);
+ } finally {
+ autoBattleRunning = false;
+ window.HWH_AUTOBATTLE_RUNNING = false;
+ }
+ }
+
+ // Individual battle functions for manual triggers
+ async function runArena() {
+ try {
+ HWHFuncs.setProgress('AutoBattle: Running Arena...');
+ await new Promise((resolve, reject) => {
+ const arena = new executeArena(resolve, reject);
+ arena.start('arena');
+ });
+ HWHFuncs.setProgress('AutoBattle: Arena complete!', true);
+ } catch (error) {
+ console.error('Arena error:', error);
+ HWHFuncs.setProgress(`Arena error: ${error.message}`, true);
+ }
+ }
+
+ async function runGrandArena() {
+ try {
+ HWHFuncs.setProgress('AutoBattle: Running Grand Arena...');
+ await new Promise((resolve, reject) => {
+ const grandArena = new executeArena(resolve, reject);
+ grandArena.start('grand');
+ });
+ HWHFuncs.setProgress('AutoBattle: Grand Arena complete!', true);
+ } catch (error) {
+ console.error('Grand Arena error:', error);
+ HWHFuncs.setProgress(`Grand Arena error: ${error.message}`, true);
+ }
+ }
+
+ async function runGuildWar() {
+ try {
+ HWHFuncs.setProgress('AutoBattle: Running Guild War...');
+ await new Promise((resolve, reject) => {
+ const guildWar = new executeGuildWar(resolve, reject);
+ guildWar.start();
+ });
+ HWHFuncs.setProgress('AutoBattle: Guild War complete!', true);
+ } catch (error) {
+ console.error('Guild War error:', error);
+ HWHFuncs.setProgress(`Guild War error: ${error.message}`, true);
+ }
+ }
+
+ async function runRaidNodes() {
+ try {
+ HWHFuncs.setProgress('AutoBattle: Running Raid Nodes...');
+ await new Promise((resolve, reject) => {
+ const raidNodes = new executeRaidNodes(resolve, reject);
+ raidNodes.start();
+ });
+ HWHFuncs.setProgress('AutoBattle: Raid Nodes complete!', true);
+ } catch (error) {
+ console.error('Raid Nodes error:', error);
+ HWHFuncs.setProgress(`Raid Nodes error: ${error.message}`, true);
+ }
+ }
+
+ async function runTitanArena() {
+ try {
+ if (Utils.isTitanArenaDay()) {
+ HWHFuncs.setProgress('AutoBattle: Running Titan Arena (ToE)...');
+
+ // Use HWHClasses.executeTitanArena if available, otherwise use local implementation
+ if (window.HWHClasses && window.HWHClasses.executeTitanArena) {
+ await new Promise((resolve, reject) => {
+ const titanArena = new window.HWHClasses.executeTitanArena(resolve, reject);
+ titanArena.start();
+ });
+ } else {
+ // Fallback: use testTitanArena function if available
+ if (window.testTitanArena && typeof window.testTitanArena === 'function') {
+ await window.testTitanArena();
+ } else {
+ throw new Error('Titan Arena execution class not available');
+ }
+ }
+ HWHFuncs.setProgress('AutoBattle: Titan Arena (ToE) complete!', true);
+ } else {
+ const dayName = Utils.getDayName(Utils.getDayOfWeek());
+ HWHFuncs.setProgress(`Titan Arena: Only available Monday-Saturday (today is ${dayName})`, true);
+ Utils.log('log', `Titan Arena: Skipped - today is ${dayName}, only runs Monday-Saturday`);
+ }
+ } catch (error) {
+ console.error('Titan Arena error:', error);
+ HWHFuncs.setProgress(`Titan Arena error: ${error.message}`, true);
+ }
+ }
+
+ async function runRaidBoss() {
+ try {
+ if (Utils.isRaidBossDay()) {
+ HWHFuncs.setProgress('AutoBattle: Running Raid Boss...');
+ await new Promise((resolve, reject) => {
+ const raidBoss = new executeRaidBoss(resolve, reject);
+ raidBoss.start();
+ });
+ HWHFuncs.setProgress('AutoBattle: Raid Boss complete!', true);
+ } else {
+ const dayName = Utils.getDayName(Utils.getDayOfWeek());
+ HWHFuncs.setProgress(`Raid Boss: Only available on Saturday or Sunday (today is ${dayName})`, true);
+ Utils.log('log', `Raid Boss: Skipped - today is ${dayName}, only runs on Saturday/Sunday`);
+ }
+ } catch (error) {
+ console.error('Raid Boss error:', error);
+ HWHFuncs.setProgress(`Raid Boss error: ${error.message}`, true);
+ }
+ }
+
+ async function runCrossClanWar() {
+ try {
+ HWHFuncs.setProgress('AutoBattle: Running Cross Clan War...');
+ await new Promise((resolve, reject) => {
+ const crossClanWar = new executeCrossClanWar(resolve, reject);
+ crossClanWar.start();
+ });
+ HWHFuncs.setProgress('AutoBattle: Cross Clan War complete!', true);
+ } catch (error) {
+ console.error('Cross Clan War error:', error);
+ HWHFuncs.setProgress(`Cross Clan War error: ${error.message}`, true);
+ }
+ }
+
+ // Auto-execute on script load
+ autoBattle().catch(error => {
+ console.error('AutoBattle: Failed to auto-execute:', error);
+ });
+
+ // Helper function to get I18N translation
+ function getI18N(key) {
+ if (window.I18N && typeof window.I18N === 'function') {
+ return window.I18N(key);
+ }
+ // Fallback translations
+ const fallbacks = {
+ 'TITAN_ARENA': 'ToE',
+ 'TITAN_ARENA_TITLE': 'Tournament of Elements'
+ };
+ return fallbacks[key] || key;
+ }
+
+ // Popup menu for manual triggers
+ async function openManualTriggersPopup() {
+ const popupContent = document.createElement('div');
+ popupContent.style.cssText = 'display: flex; flex-direction: column; height: 70vh; color: #fce1ac;';
+
+ const contentContainer = document.createElement('div');
+ contentContainer.style.cssText = 'flex-grow: 1; overflow-y: auto; padding: 10px;';
+
+ const title = document.createElement('h2');
+ title.textContent = 'Manual Battle Triggers';
+ title.style.cssText = 'text-align: center; color: #fce1ac; margin-bottom: 20px; border-bottom: 2px solid #8b6914; padding-bottom: 10px;';
+ contentContainer.appendChild(title);
+
+ const buttonGrid = document.createElement('div');
+ buttonGrid.style.cssText = 'display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; padding: 10px;';
+
+ const battleButtons = [
+ { name: 'Arena', title: 'Run Arena battles only', onClick: runArena, color: '#4A90E2', icon: '⚔️' },
+ { name: 'Grand Arena', title: 'Run Grand Arena battles only', onClick: runGrandArena, color: '#4A90E2', icon: '⚔️' },
+ { name: 'Guild War', title: 'Run Guild War attacks only', onClick: runGuildWar, color: '#9B59B6', icon: '🛡️' },
+ { name: 'Raid Nodes', title: 'Run Raid Nodes only', onClick: runRaidNodes, color: '#E67E22', icon: '⚡' },
+ { name: getI18N('TITAN_ARENA'), title: `Run ${getI18N('TITAN_ARENA')} only (Monday-Saturday)`, onClick: runTitanArena, color: '#1ABC9C', icon: '🏛️' },
+ { name: 'Raid Boss', title: 'Run Raid Boss attacks only (5 attacks)', onClick: runRaidBoss, color: '#E74C3C', icon: '👹' },
+ { name: 'Cross Clan War', title: 'Run Cross Clan War attacks only', onClick: runCrossClanWar, color: '#F39C12', icon: '⚔️' }
+ ];
+
+ battleButtons.forEach(battle => {
+ const button = document.createElement('button');
+ button.style.cssText = `
+ padding: 15px;
+ background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%);
+ border: 2px solid ${battle.color};
+ border-radius: 8px;
+ color: #fce1ac;
+ cursor: pointer;
+ text-align: center;
+ transition: all 0.3s;
+ font-size: 14px;
+ font-weight: bold;
+ `;
+ button.innerHTML = `
+ ${battle.icon}
+ ${battle.name}
+ `;
+ button.title = battle.title;
+
+ button.addEventListener('mouseenter', () => {
+ button.style.background = `linear-gradient(135deg, ${battle.color}40 0%, ${battle.color}20 100%)`;
+ button.style.borderColor = battle.color;
+ button.style.transform = 'scale(1.05)';
+ });
+ button.addEventListener('mouseleave', () => {
+ button.style.background = 'linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%)';
+ button.style.borderColor = battle.color;
+ button.style.transform = 'scale(1)';
+ });
+ button.addEventListener('click', async () => {
+ // Close popup first
+ const popupBody = document.querySelector('.PopUp_Container');
+ if (popupBody && popupBody.parentElement) {
+ const closeBtn = document.querySelector('.PopUp_buttons button');
+ if (closeBtn) closeBtn.click();
+ }
+ // Then execute battle
+ await battle.onClick();
+ });
+
+ buttonGrid.appendChild(button);
+ });
+
+ contentContainer.appendChild(buttonGrid);
+ popupContent.appendChild(contentContainer);
+
+ // Use confirm with proper async handling
+ const popupPromise = HWHFuncs.popup.confirm('', [{ msg: 'Close', result: true, isClose: true }]);
+
+ // Wait a tick for popup to initialize
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ const popupBody = document.querySelector('.PopUp_Container');
+ if (popupBody) {
+ popupBody.innerHTML = '';
+ popupBody.appendChild(popupContent);
+ }
+
+ // Wait for popup to close before returning
+ await popupPromise;
+ }
+
+ // Menu integration
+ const { ScriptMenu } = HWHClasses;
+ const scriptMenu = ScriptMenu.getInst();
+
+ scriptMenu.addCombinedButton([
+ { name: '⚔️ Auto Battle', title: 'Run all auto-battles (Arena, Grand Arena, Guild War, Raids, ToE, Boss, Cross Clan War)', onClick: autoBattle, color: 'green' },
+ { name: '⚙️ Manual Triggers', title: 'Open manual battle triggers menu', onClick: openManualTriggersPopup, color: 'gray' }
+ ]);
+
+ console.log('AutoBattle: UI initialized and attached to HWH menu.');
+ }
+})();
diff --git a/AutoHeroWars.side b/AutoHeroWars.side
deleted file mode 100644
index ec90293..0000000
--- a/AutoHeroWars.side
+++ /dev/null
@@ -1,87 +0,0 @@
-{
- "id": "93c9bdfd-1317-4452-aa7b-81d4626fc353",
- "version": "2.0",
- "name": "AutoDoAll",
- "url": "https://www.hero-wars.com",
- "tests": [{
- "id": "17d0ca73-9f18-4583-8c79-ec8cca07147a",
- "name": "AutoDoAll",
- "commands": [{
- "id": "5ef7fe0f-873b-40c2-b43f-128018949b62",
- "comment": "",
- "command": "open",
- "target": "/",
- "targets": [],
- "value": ""
- }, {
- "id": "25b23329-5cb7-4a37-9c4f-4a53df29074f",
- "comment": "",
- "command": "setWindowSize",
- "target": "2576x1408",
- "targets": [],
- "value": ""
- }, {
- "id": "7fea18b2-92d8-489d-b6ba-638aea75e554",
- "comment": "",
- "command": "pause",
- "target": "15000",
- "targets": [],
- "value": ""
- }, {
- "id": "f60b4fd4-eafa-40f5-a0af-9c06c1421850",
- "comment": "",
- "command": "click",
- "target": "css=.scriptMenu_button:nth-child(6) > .scriptMenu_buttonText",
- "targets": [
- ["css=.scriptMenu_button:nth-child(6) > .scriptMenu_buttonText", "css:finder"],
- ["xpath=//div[8]/div[2]/div[3]/div", "xpath:position"]
- ],
- "value": ""
- }, {
- "id": "cfd56101-84b4-4194-9631-90aabb80103c",
- "comment": "",
- "command": "click",
- "target": "css=.PopUp_ContCheckbox:nth-child(8) > label",
- "targets": [
- ["css=.PopUp_ContCheckbox:nth-child(8) > label", "css:finder"],
- ["xpath=//div[8]/label", "xpath:position"],
- ["xpath=//label[contains(.,'Do daily quests')]", "xpath:innerText"]
- ],
- "value": ""
- }, {
- "id": "8e3f6f8a-e355-42d6-9369-dcf048c684e3",
- "comment": "",
- "command": "click",
- "target": "css=.PopUp_buttons:nth-child(2) .PopUp_text",
- "targets": [
- ["css=.PopUp_buttons:nth-child(2) .PopUp_text", "css:finder"],
- ["xpath=//div[3]/div[2]/div/div", "xpath:position"]
- ],
- "value": ""
- }, {
- "id": "d99888f3-8528-40d8-bbab-6e88d73cf039",
- "comment": "",
- "command": "pause",
- "target": "120000",
- "targets": [],
- "value": ""
- }, {
- "id": "8475873f-f1f1-490e-861a-39be293eaf5a",
- "comment": "",
- "command": "close",
- "target": "",
- "targets": [],
- "value": ""
- }]
- }],
- "suites": [{
- "id": "ef149131-45f8-491c-a0d0-34c978e7ec9e",
- "name": "Default Suite",
- "persistSession": false,
- "parallel": false,
- "timeout": 300,
- "tests": ["17d0ca73-9f18-4583-8c79-ec8cca07147a"]
- }],
- "urls": ["https://www.hero-wars.com/"],
- "plugins": []
-}
\ No newline at end of file
diff --git a/BATTLE_SIMULATION_API_DOCUMENTATION.md b/BATTLE_SIMULATION_API_DOCUMENTATION.md
new file mode 100644
index 0000000..8220410
--- /dev/null
+++ b/BATTLE_SIMULATION_API_DOCUMENTATION.md
@@ -0,0 +1,2048 @@
+# Battle Simulation API Documentation
+
+## Overview
+The Battle Simulation API allows players to simulate battles between teams without consuming resources or affecting game state. This is used for testing team compositions, strategies, and battle outcomes in various game modes (Arena, Grand Arena, etc.).
+
+## API Endpoints
+
+### 1. `demoBattles_startBattle`
+
+Starts a new battle simulation with specified attacker and defender teams.
+
+### Request
+
+**Endpoint:** `https://heroes-wb.nextersglobal.com/api/`
+
+**Method:** `POST`
+
+**Headers:**
+- `Content-Type: application/json; charset=UTF-8`
+- `x-auth-application-id: 3`
+- `x-auth-network-ident: web`
+- `x-auth-player-id: `
+- `x-auth-user-id: `
+- `x-auth-token: `
+- `x-auth-signature: `
+- `x-auth-session-id: `
+- `x-env-unique-session-id: `
+- `x-request-id: `
+- `x-server-time: 0`
+- `Origin: https://www.hero-wars.com`
+- `Referer: https://www.hero-wars.com/`
+
+**Request Body:**
+```json
+{
+ "calls": [
+ {
+ "name": "demoBattles_startBattle",
+ "args": {
+ "mechanic": "arena",
+ "defenceMaxUpgrade": true,
+ "defenceTeam": {
+ "units": [57, 58, 63, 48, 67],
+ "pet": 6008
+ },
+ "defenceBanner": 5,
+ "defenceBannerStones": {},
+ "defenceFavor": {
+ "48": 6000,
+ "58": 6002,
+ "63": 6003,
+ "67": 6001
+ },
+ "maxUpgrade": true,
+ "team": {
+ "units": [31, 58, 13, 40, 56],
+ "pet": 6008
+ },
+ "banner": 3,
+ "bannerStones": {},
+ "favor": {
+ "13": 6002,
+ "31": 6006,
+ "40": 6004,
+ "56": 6001,
+ "58": 6005
+ },
+ "defenceBuffs": {},
+ "buffs": {},
+ "parentId": 0,
+ "entryId": 0
+ },
+ "context": {
+ "actionTs": 405488
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+**Request Parameters:**
+
+#### Top Level
+- `name`: `"demoBattles_startBattle"` - The API method name
+- `args`: Object - Battle configuration parameters
+- `context.actionTs`: Number - Action timestamp
+- `ident`: `"body"` - Identifier for the request
+
+#### Args Object (`args`)
+- `mechanic`: String - Battle mechanic type (e.g., `"arena"`, `"grandArena"`, etc.)
+- `defenceMaxUpgrade`: Boolean - Whether to apply maximum upgrades to defense team
+- `defenceTeam`: Object - Defender team configuration
+ - `units`: Array - Array of hero IDs (e.g., `[57, 58, 63, 48, 67]`)
+ - `pet`: Number - Pet ID (e.g., `6008`)
+- `defenceBanner`: Number - Banner ID for defense team (e.g., `5`)
+- `defenceBannerStones`: Object - Banner stones configuration for defense (usually empty `{}`)
+- `defenceFavor`: Object - Favor pet assignments for defense heroes
+ - Keys are hero IDs as strings (e.g., `"48"`)
+ - Values are favor pet IDs (e.g., `6000`)
+- `maxUpgrade`: Boolean - Whether to apply maximum upgrades to attack team
+- `team`: Object - Attacker team configuration
+ - `units`: Array - Array of hero IDs (e.g., `[31, 58, 13, 40, 56]`)
+ - `pet`: Number - Pet ID (e.g., `6008`)
+- `banner`: Number - Banner ID for attack team (e.g., `3`)
+- `bannerStones`: Object - Banner stones configuration for attack (usually empty `{}`)
+- `favor`: Object - Favor pet assignments for attack heroes
+ - Keys are hero IDs as strings (e.g., `"13"`)
+ - Values are favor pet IDs (e.g., `6002`)
+- `defenceBuffs`: Object - Buffs applied to defense team (usually empty `{}`)
+- `buffs`: Object - Buffs applied to attack team (usually empty `{}`)
+- `parentId`: Number - Parent battle ID
+ - Use `0` for the first battle in a simulation session
+ - For retry battles, use the `parentId` from the previous battle's `endBattle` response (`result.response.battle.parentId`)
+ - The `parentId` links retry battles to the original battle session
+- `entryId`: Number - Entry ID (use `0` for new battles)
+
+### Response
+
+**Status Code:** `200 OK`
+
+**Response Body Structure:**
+```json
+{
+ "date": 1764567438.155992,
+ "results": [
+ {
+ "ident": "body",
+ "result": {
+ "response": {
+ "battle": {
+ "userId": "35979991",
+ "typeId": "35979991",
+ "attackers": {
+ "": {
+ "id": 31,
+ "xp": 3625195,
+ "level": 130,
+ "color": 18,
+ "slots": [0, 0, 0, 0, 0, 0],
+ "skills": {
+ "": 130
+ },
+ "power": 202858,
+ "star": 6,
+ "runes": [43750, 43750, 43750, 43750, 43750],
+ "skins": {
+ "": 60
+ },
+ "currentSkin": 44,
+ "titanGiftLevel": 30,
+ "titanCoinsSpent": null,
+ "artifacts": [
+ {
+ "level": 130,
+ "star": 6
+ }
+ ],
+ "scale": 1,
+ "petId": 6006,
+ "type": "hero",
+ "perks": [9, 5, 2, 20],
+ "ascensions": {
+ "1": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ "2": [0, 1, 2, 3, 4, 5, 6, 7, 8, 10]
+ },
+ "agility": 2781,
+ "dodge": 12620,
+ "hp": 453104,
+ "intelligence": 18945,
+ "physicalAttack": 78,
+ "strength": 2916,
+ "armor": 32339.6,
+ "magicPower": 76036.6,
+ "magicResist": 19856,
+ "skin": 44,
+ "favorPetId": 6006,
+ "favorPower": 11064,
+ "state": {
+ "hp": 569744,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 569744
+ }
+ }
+ },
+ "defenders": [
+ {
+ "": {
+ "id": 57,
+ "xp": 3625195,
+ "level": 130,
+ "color": 18,
+ "slots": [0, 0, 0, 0, 0, 0],
+ "skills": {
+ "": 130
+ },
+ "power": 195299,
+ "star": 6,
+ "runes": [43750, 43750, 43750, 43750, 43750],
+ "skins": {
+ "": 60
+ },
+ "currentSkin": 269,
+ "titanGiftLevel": 30,
+ "titanCoinsSpent": null,
+ "artifacts": [
+ {
+ "level": 130,
+ "star": 6
+ }
+ ],
+ "scale": 1,
+ "petId": 0,
+ "type": "hero",
+ "perks": [5, 8, 2, 22],
+ "ascensions": {
+ "1": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ },
+ "agility": 3249,
+ "hp": 346518,
+ "intelligence": 2854,
+ "physicalAttack": 52117,
+ "strength": 18671,
+ "armor": 47498,
+ "magicPower": 3024,
+ "magicResist": 45098,
+ "skin": 269,
+ "favorPetId": 0,
+ "favorPower": 0,
+ "state": {
+ "hp": 1093358,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 1093358
+ }
+ }
+ }
+ ],
+ "effects": {
+ "defenders": {
+ "percentBuffByPerk_castSpeed_10": 5
+ },
+ "defendersBanner": {
+ "id": 5,
+ "slots": []
+ },
+ "attackers": {
+ "percentBuffAllEnemy_healing": -10
+ },
+ "attackersBanner": {
+ "id": 3,
+ "slots": []
+ }
+ },
+ "reward": [],
+ "startTime": 1764567438,
+ "seed": 774012097,
+ "type": "arena"
+ }
+ }
+ }
+ }
+ ]
+}
+```
+
+**Response Fields:**
+
+#### Top Level
+- `date`: Number - Server timestamp (e.g., `1764567438.155992`)
+- `results`: Array - Array of result objects
+ - `ident`: String - Identifier matching the request (`"body"`)
+ - `result.response`: Object - The actual response data
+
+#### Battle Object (`result.response.battle`)
+- `userId`: String - User ID (e.g., `"35979991"`)
+- `typeId`: String - Type ID (usually same as userId)
+- `attackers`: Object - Attacker heroes and pets
+ - Keys are hero/pet IDs as strings (e.g., `"31"`, `"6008"`)
+ - Values are hero/pet objects with detailed stats
+- `defenders`: Array - Array of defender team objects
+ - Each element is an object with hero/pet IDs as keys
+ - Values are hero/pet objects with detailed stats
+- `effects`: Object - Battle effects and buffs
+ - `defenders`: Object - Effects applied to defenders
+ - `defendersBanner`: Object - Banner configuration for defenders
+ - `attackers`: Object - Effects applied to attackers
+ - `attackersBanner`: Object - Banner configuration for attackers
+- `reward`: Array - Rewards (empty for simulations)
+- `startTime`: Number - Battle start timestamp
+- `seed`: Number - Random seed for battle simulation
+- `type`: String - Battle type (e.g., `"arena"`)
+
+#### Hero/Pet Object Structure
+- `id`: Number - Hero/Pet ID
+- `xp`: Number - Experience points
+- `level`: Number - Level (e.g., `130`)
+- `color`: Number - Color/rarity (e.g., `18`)
+- `slots`: Array - Equipment slots (usually `[0, 0, 0, 0, 0, 0]`)
+- `skills`: Object - Skill levels
+ - Keys are skill IDs as strings
+ - Values are skill levels (e.g., `130`)
+- `power`: Number - Total power
+- `star`: Number - Star level (e.g., `6`)
+- `runes`: Array - Rune values
+- `skins`: Object - Available skins
+ - Keys are skin IDs as strings
+ - Values are skin levels (e.g., `60`)
+- `currentSkin`: Number - Currently equipped skin ID
+- `titanGiftLevel`: Number - Titan gift level
+- `titanCoinsSpent`: Number | null - Titan coins spent
+- `artifacts`: Array - Artifact configurations
+ - `level`: Number - Artifact level
+ - `star`: Number - Artifact star level
+- `scale`: Number - Scale factor (usually `1`)
+- `petId`: Number - Pet ID (for heroes) or `0` if no pet
+- `type`: String - Type (`"hero"` or `"pet"`)
+- `perks`: Array - Perk IDs
+- `ascensions`: Object - Ascension data
+ - Keys are ascension paths as strings (e.g., `"1"`, `"2"`)
+ - Values are arrays of ascension node IDs
+- `agility`: Number - Agility stat
+- `dodge`: Number - Dodge stat
+- `hp`: Number - Base HP
+- `intelligence`: Number - Intelligence stat
+- `physicalAttack`: Number - Physical attack stat
+- `strength`: Number - Strength stat
+- `armor`: Number - Armor stat
+- `magicPower`: Number - Magic power stat
+- `magicResist`: Number - Magic resistance stat
+- `magicPenetration`: Number - Magic penetration (optional)
+- `armorPenetration`: Number - Armor penetration (optional)
+- `physicalCritChance`: Number - Physical crit chance (optional)
+- `skin`: Number - Currently equipped skin ID
+- `favorPetId`: Number - Favor pet ID
+- `favorPower`: Number - Favor pet power
+- `modifiedSkillTier`: Number - Modified skill tier (optional)
+- `state`: Object - Current battle state
+ - `hp`: Number - Current HP (can be `-1` for pets)
+ - `energy`: Number - Current energy
+ - `isDead`: Boolean - Whether unit is dead
+ - `maxHp`: Number - Maximum HP (can be `-1` for pets)
+
+---
+
+### 2. `demoBattles_endBattle`
+
+Ends a battle simulation and returns the battle result and replay data.
+
+### Request
+
+**Endpoint:** `https://heroes-wb.nextersglobal.com/api/`
+
+**Method:** `POST`
+
+**Headers:**
+- `Content-Type: application/json; charset=UTF-8`
+- `x-auth-application-id: 3`
+- `x-auth-network-ident: web`
+- `x-auth-player-id: `
+- `x-auth-user-id: `
+- `x-auth-token: `
+- `x-auth-signature: `
+- `x-auth-session-id: `
+- `x-env-unique-session-id: `
+- `x-request-id: `
+- `x-server-time: 0`
+- `Origin: https://www.hero-wars.com`
+- `Referer: https://www.hero-wars.com/`
+
+**Request Body:**
+```json
+{
+ "calls": [
+ {
+ "name": "demoBattles_endBattle",
+ "args": {
+ "result": {
+ "win": false,
+ "stars": 0
+ },
+ "progress": [
+ {
+ "v": 273,
+ "b": 0,
+ "seed": -1566019136,
+ "attackers": {
+ "input": [],
+ "heroes": {
+ "6008": {
+ "hp": -1,
+ "energy": 491,
+ "isDead": false
+ }
+ }
+ },
+ "defenders": {
+ "input": [],
+ "heroes": {
+ "48": {
+ "hp": 434998,
+ "energy": 400,
+ "isDead": false,
+ "extra": {
+ "hero48StartEnergy": 1
+ }
+ },
+ "57": {
+ "hp": 610200,
+ "energy": 100,
+ "isDead": false
+ },
+ "58": {
+ "hp": 185406,
+ "energy": 669,
+ "isDead": false
+ },
+ "63": {
+ "hp": 621150,
+ "energy": 100,
+ "isDead": false
+ },
+ "67": {
+ "hp": 326219,
+ "energy": 669,
+ "isDead": false
+ },
+ "6008": {
+ "hp": -1,
+ "energy": 425,
+ "isDead": false
+ }
+ }
+ }
+ }
+ ]
+ },
+ "context": {
+ "actionTs": 416478
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+**Request Parameters:**
+
+#### Top Level
+- `name`: `"demoBattles_endBattle"` - The API method name
+- `args`: Object - Battle end parameters
+- `context.actionTs`: Number - Action timestamp
+- `ident`: `"body"` - Identifier for the request
+
+#### Args Object (`args`)
+- `result`: Object - Battle result
+ - `win`: Boolean - Whether the attacker won (`true`) or lost (`false`)
+ - `stars`: Number - Stars earned (0-3, usually `0` for simulations)
+- `progress`: Array - Battle progress snapshots
+ - `v`: Number - Version number (e.g., `273`)
+ - `b`: Number - Battle phase (e.g., `0`)
+ - `seed`: Number - Random seed for this progress snapshot
+ - `attackers`: Object - Attacker state at this snapshot
+ - `input`: Array - Input data (usually empty `[]`)
+ - `heroes`: Object - Hero/pet states
+ - Keys are hero/pet IDs as strings
+ - Values are state objects with `hp`, `energy`, `isDead`, and optional `extra`
+ - `defenders`: Object - Defender state at this snapshot
+ - `input`: Array - Input data (usually empty `[]`)
+ - `heroes`: Object - Hero/pet states
+ - Keys are hero/pet IDs as strings
+ - Values are state objects with `hp`, `energy`, `isDead`, and optional `extra`
+
+### Response
+
+**Status Code:** `200 OK`
+
+**Response Body Structure:**
+```json
+{
+ "date": 1764567449.1103261,
+ "results": [
+ {
+ "ident": "body",
+ "result": {
+ "response": {
+ "replay": {
+ "userId": "35979991",
+ "typeId": "35979991",
+ "attackers": {
+ "": {
+ // Same hero structure as startBattle response
+ }
+ },
+ "defenders": [
+ {
+ "": {
+ // Same hero structure as startBattle response
+ }
+ }
+ ],
+ "effects": {
+ // Same effects structure as startBattle response
+ },
+ "reward": [],
+ "startTime": "1764567448",
+ "seed": "2728948160",
+ "type": "arena",
+ "id": "1764567448572398347",
+ "progress": [
+ {
+ "v": 273,
+ "b": 0,
+ "seed": -1566019136,
+ "attackers": {
+ "input": [],
+ "heroes": {
+ "": {
+ "hp": -1,
+ "energy": 491,
+ "isDead": false
+ }
+ }
+ },
+ "defenders": {
+ "input": [],
+ "heroes": {
+ "": {
+ "hp": 434998,
+ "energy": 400,
+ "isDead": false,
+ "extra": {
+ "hero48StartEnergy": 1
+ }
+ }
+ }
+ }
+ }
+ ],
+ "endTime": "1764567448",
+ "result": {
+ "win": false,
+ "stars": 0,
+ "serverVersion": 273
+ }
+ },
+ "battle": {
+ "id": 71897117,
+ "parentId": 71897112,
+ "userId": 35979991,
+ "replayId": "1764567448572398347",
+ "mechanic": "arena",
+ "hash": "NDgyMTM1OGU3ZDQ1ZmIyNTcxMmY0ZmVlOWJmMGQyNTM3OGVmZTQyNg==",
+ "data": {
+ "entryId": 0,
+ "attackMax": true,
+ "defenceMax": false,
+ "attackBuffs": [],
+ "defenceBuffs": [],
+ "attackFavor": {
+ "13": 6002,
+ "31": 6006,
+ "40": 6004,
+ "56": 6001,
+ "58": 6005
+ },
+ "win": false,
+ "attack": {
+ "powerSum": 1197491,
+ "units": {
+ "": {
+ "id": 31,
+ "level": 130,
+ "star": 6,
+ "power": 202858,
+ "color": 18,
+ "favorPetId": 6006,
+ "favorPower": 11064
+ }
+ },
+ "banner": {
+ "id": 3,
+ "slots": []
+ }
+ },
+ "defence": {
+ "powerSum": 1190075,
+ "units": {
+ "": {
+ "id": 57,
+ "level": 130,
+ "star": 6,
+ "power": 195299,
+ "color": 18,
+ "favorPetId": 0,
+ "favorPower": 0
+ }
+ },
+ "banner": {
+ "id": 5,
+ "slots": []
+ }
+ }
+ },
+ "ctime": 1764567449
+ }
+ }
+ }
+ }
+ ]
+}
+```
+
+**Response Fields:**
+
+#### Top Level
+- `date`: Number - Server timestamp (e.g., `1764567449.1103261`)
+- `results`: Array - Array of result objects
+ - `ident`: String - Identifier matching the request (`"body"`)
+ - `result.response`: Object - The actual response data
+
+#### Response Object (`result.response`)
+- `replay`: Object - Battle replay data
+ - Contains the same structure as the battle object from `startBattle`, plus:
+ - `id`: String - Replay ID (e.g., `"1764567448572398347"`)
+ - `progress`: Array - Battle progress snapshots (same structure as request)
+ - `endTime`: String - Battle end timestamp
+ - `result`: Object - Final battle result
+ - `win`: Boolean - Whether attacker won
+ - `stars`: Number - Stars earned
+ - `serverVersion`: Number - Server version number
+- `battle`: Object - Battle record data
+ - **Location**: `response.results[0].result.response.battle`
+ - `id`: Number - Current battle ID (e.g., `71898586`)
+ - **Critical**: Extract `battle.id` from the first battle's `endBattle` response
+ - **Extraction Path**: `response.results[0].result.response.battle.id`
+ - **Usage**: Use this `id` value as the `parentId` parameter for all subsequent `demoBattles_startBattle` calls
+ - **First Battle**: For the first battle, use `parentId: 0` in the request, then extract `battle.id` from response
+ - **Retry Battles**: Use the first battle's `id` as `parentId` for all subsequent battles
+ - **Linking**: All retry battles use the same `parentId` (first battle's ID), linking them together
+ - **Example Flow**:
+ - Battle 1: Request `parentId: 0` → Response `battle.id: 71898586` → Use `71898586` for next battle
+ - Battle 2: Request `parentId: 71898586` → Response `battle.id: 71898587` → Continue using `71898586`
+ - Battle 3: Request `parentId: 71898586` → Response `battle.id: 71898588` → Continue using `71898586`
+ - `parentId`: Number - Parent battle ID (e.g., `71897112`)
+ - This field can be ignored for retry logic - use `battle.id` instead
+ - `userId`: Number - User ID (e.g., `35979991`)
+ - `replayId`: String - Replay ID (e.g., `"1764567448572398347"`)
+ - `mechanic`: String - Battle mechanic (e.g., `"arena"`)
+ - `hash`: String - Battle hash (base64 encoded)
+ - `data`: Object - Battle configuration data
+ - `entryId`: Number - Entry ID
+ - `attackMax`: Boolean - Whether attack team had max upgrades
+ - `defenceMax`: Boolean - Whether defense team had max upgrades
+ - `attackBuffs`: Array - Attack team buffs
+ - `defenceBuffs`: Array - Defense team buffs
+ - `attackFavor`: Object - Attack team favor pet assignments
+ - `win`: Boolean - Battle result
+ - `attack`: Object - Attack team summary
+ - `powerSum`: Number - Total power
+ - `units`: Object - Unit summaries
+ - `banner`: Object - Banner configuration
+ - `defence`: Object - Defense team summary
+ - `powerSum`: Number - Total power
+ - `units`: Object - Unit summaries
+ - `banner`: Object - Banner configuration
+ - `ctime`: Number - Creation timestamp
+
+---
+
+## ParentId Mechanism
+
+### Overview
+
+The `parentId` mechanism is used to link multiple battle simulations together, allowing you to retry battles with the same team configuration. This is essential for running multiple simulations of the same battle scenario to calculate win rates or test different strategies.
+
+### How ParentId Works
+
+**Important**: Use the **first battle's ID** (not `parentId`) as the `parentId` for all subsequent battles.
+
+1. **First Battle (Initial Simulation)**:
+ - Use `parentId: 0` in `demoBattles_startBattle` to start a new battle session
+ - After the battle completes, call `demoBattles_endBattle`
+ - The `endBattle` response contains a `battle` object with:
+ - `id`: The current battle ID (e.g., `71898586`) - **Use this for subsequent battles**
+ - `parentId`: The parent battle ID (can be ignored for retry logic)
+ - **Extract the `battle.id` from the first battle's `endBattle` response**
+ - **Use this `id` value as the `parentId` for all subsequent retry battles**
+
+2. **Retry Battles (Subsequent Simulations)**:
+ - Use the first battle's `id` (extracted from first battle's `endBattle` response) as `parentId`
+ - All retry battles should use the same `parentId` (the first battle's ID)
+ - Example: First battle returns `id: 71898586`, all subsequent battles use `parentId: 71898586`
+ - This links all retry battles to the original battle session
+
+### Response Structure
+
+The battle `id` (used as `parentId` for retries) is located in the `demoBattles_endBattle` response at:
+
+```
+response.results[0].result.response.battle.id
+```
+
+**Note**: Extract `battle.id` (not `battle.parentId`) from the first battle's `endBattle` response to use as `parentId` for subsequent battles.
+
+**Full Response Structure:**
+```json
+{
+ "date": 1764567449.1103261,
+ "results": [
+ {
+ "ident": "body",
+ "result": {
+ "response": {
+ "replay": {
+ // ... replay data ...
+ },
+ "battle": {
+ "id": 71897117, // Current battle ID
+ "parentId": 71897112, // ← Extract this for retries
+ "userId": 35979991,
+ "replayId": "1764567448572398347",
+ "mechanic": "arena",
+ "hash": "NDgyMTM1OGU3ZDQ1ZmIyNTcxMmY0ZmVlOWJmMGQyNTM3OGVmZTQyNg==",
+ "data": {
+ // ... battle configuration data ...
+ },
+ "ctime": 1764567449
+ }
+ }
+ }
+ }
+ ]
+}
+```
+
+### Extracting ParentId
+
+**JavaScript Example:**
+```javascript
+// After calling demoBattles_endBattle
+const endBattleResponse = await Send(JSON.stringify({
+ calls: [{
+ name: "demoBattles_endBattle",
+ args: {
+ result: { win: false, stars: 0 },
+ progress: [/* ... progress data ... */]
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+}));
+
+// Extract battle ID from first battle's endBattle response
+const firstBattleId = endBattleResponse.results[0].result.response.battle.id;
+
+// Use this battle ID as parentId for all subsequent battles
+console.log(`All subsequent battles will use parentId: ${firstBattleId}`);
+```
+
+**Error Handling:**
+```javascript
+function extractBattleId(endBattleResponse) {
+ // Check if response structure is valid
+ if (!endBattleResponse?.results?.[0]?.result?.response) {
+ console.error('Invalid endBattle response structure');
+ return null;
+ }
+
+ const battle = endBattleResponse.results[0].result.response.battle;
+
+ if (!battle) {
+ console.error('Battle object not found in response');
+ return null;
+ }
+
+ // Extract battle.id (not parentId) for use as parentId in subsequent battles
+ if (battle.id === undefined || battle.id === null) {
+ console.warn('Battle ID not found in battle object');
+ return null;
+ }
+
+ return battle.id;
+}
+```
+
+### Complete Retry Flow Example
+
+```javascript
+// Step 1: First battle with parentId = 0
+let parentId = 0;
+let firstBattleId = null;
+
+for (let i = 0; i < 10; i++) {
+ console.log(`Simulation ${i + 1}: Using parentId=${parentId}`);
+
+ // Start battle with current parentId
+ const startResponse = await Send(JSON.stringify({
+ calls: [{
+ name: "demoBattles_startBattle",
+ args: {
+ mechanic: "arena",
+ parentId: parentId, // Use 0 for first battle, then first battle's ID for retries
+ // ... other battle configuration ...
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+ }));
+
+ const battleData = startResponse.results[0].result.response.battle;
+
+ // Simulate battle (client-side calculation)
+ const battleResult = await simulateBattle(battleData);
+
+ // End battle
+ const endResponse = await Send(JSON.stringify({
+ calls: [{
+ name: "demoBattles_endBattle",
+ args: {
+ result: {
+ win: battleResult.win,
+ stars: battleResult.stars
+ },
+ progress: battleResult.progress
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+ }));
+
+ // Extract battle ID from endBattle response
+ const battleId = endResponse.results[0].result.response.battle.id;
+
+ // For first battle: store its ID to use as parentId for subsequent battles
+ if (i === 0 && battleId) {
+ firstBattleId = battleId;
+ parentId = firstBattleId;
+ console.log(`First battle completed - using battleId ${battleId} as parentId for subsequent battles`);
+ }
+ // For subsequent battles: continue using first battle's ID
+ else if (i > 0 && firstBattleId) {
+ parentId = firstBattleId;
+ }
+
+ // Process battle result
+ console.log(`Simulation ${i + 1} result: ${battleResult.win ? 'WIN' : 'LOSS'}`);
+}
+```
+
+### Important Notes
+
+1. **First Battle**: Always use `parentId: 0` for the first battle in a simulation sequence
+2. **Extract Battle ID**: After the first battle's `endBattle` call, extract `battle.id` (not `battle.parentId`)
+3. **Retry Battles**: Use the first battle's `id` as `parentId` for all subsequent battles
+4. **Same ParentId**: All retry battles use the same `parentId` (the first battle's ID)
+5. **Battle Linking**: Using the same `parentId` links all retry battles to the original battle session
+6. **Error Handling**: If battle ID extraction fails, you can continue using the stored first battle ID or restart with `parentId: 0`
+7. **Response Structure**: The `battle` object is nested under `response.results[0].result.response.battle`
+8. **Type**: Both `id` and `parentId` are numbers (e.g., `71898586`)
+9. **Key Difference**: Use `battle.id` from first battle, not `battle.parentId`
+
+### Common Issues
+
+**Issue: parentId is null or undefined**
+- **Cause**: Response structure may differ or battle object is missing
+- **Solution**: Check response structure with logging, verify `endBattle` call succeeded
+
+**Issue: All battles use parentId = 0**
+- **Cause**: Not extracting parentId from `endBattle` response
+- **Solution**: Ensure you're reading `response.results[0].result.response.battle.parentId`
+
+**Issue: parentId changes between retries**
+- **Cause**: This is normal - each battle gets a new `id`, but `parentId` should remain constant for retries
+- **Solution**: Use `parentId` (not `id`) for linking battles
+
+---
+
+## Usage Notes
+
+1. **Battle Flow**:
+ - Call `demoBattles_startBattle` with `parentId: 0` to initialize a new battle simulation
+ - Execute the battle simulation (client-side)
+ - Call `demoBattles_endBattle` with the battle result and progress data
+ - The `endBattle` response contains `result.response.battle.parentId` which can be used for retries
+
+2. **Retry Battle Flow**:
+ - After ending a battle, extract `parentId` from `result.response.battle.parentId` in the `endBattle` response
+ - Call `demoBattles_startBattle` again with the same team configuration but use the extracted `parentId` instead of `0`
+ - Execute the battle simulation (client-side)
+ - Call `demoBattles_endBattle` with the new battle result
+ - Repeat as needed - all retries use the same `parentId` from the original battle
+
+3. **Mechanic Types**: Common values include:
+ - `"arena"` - Arena battles
+ - `"grandArena"` - Grand Arena battles
+ - Other game mode identifiers
+
+4. **Team Configuration**:
+ - Teams consist of up to 5 heroes (specified in `units` array)
+ - Each team can have one pet (specified in `pet` field)
+ - Favor pets are assigned per hero in the `favor` object
+
+5. **Banners**: Banner IDs represent different banner types that provide team-wide bonuses
+
+6. **Progress Snapshots**: The `progress` array in `endBattle` contains snapshots of unit states at different points during the battle, used for replay functionality
+
+7. **Battle Seeds**: Both `startTime`/`seed` in startBattle and `seed` values in progress snapshots are used to ensure deterministic battle simulations
+
+8. **Pet HP**: Pet HP values are typically `-1` indicating they don't have traditional HP mechanics
+
+8. **Retry Battles**: There is no separate "retry battle" API. To retry a battle:
+ - After calling `demoBattles_endBattle`, extract the `parentId` from the response (`result.response.battle.parentId`)
+ - Call `demoBattles_startBattle` again with the same team configuration, but use the `parentId` from the previous battle instead of `0`
+ - This creates a retry battle linked to the original battle session
+ - Example: First battle uses `parentId: 0`, retry battles use `parentId: 71897112` (from previous battle's endBattle response)
+
+---
+
+## Example Usage
+
+### Starting a Battle Simulation
+
+```javascript
+const startBattleRequest = {
+ calls: [{
+ name: "demoBattles_startBattle",
+ args: {
+ mechanic: "arena",
+ defenceMaxUpgrade: true,
+ defenceTeam: {
+ units: [57, 58, 63, 48, 67],
+ pet: 6008
+ },
+ defenceBanner: 5,
+ defenceBannerStones: {},
+ defenceFavor: {
+ "48": 6000,
+ "58": 6002,
+ "63": 6003,
+ "67": 6001
+ },
+ maxUpgrade: true,
+ team: {
+ units: [31, 58, 13, 40, 56],
+ pet: 6008
+ },
+ banner: 3,
+ bannerStones: {},
+ favor: {
+ "13": 6002,
+ "31": 6006,
+ "40": 6004,
+ "56": 6001,
+ "58": 6005
+ },
+ defenceBuffs: {},
+ buffs: {},
+ parentId: 0,
+ entryId: 0
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+};
+```
+
+### Ending a Battle Simulation
+
+```javascript
+const endBattleRequest = {
+ calls: [{
+ name: "demoBattles_endBattle",
+ args: {
+ result: {
+ win: false,
+ stars: 0
+ },
+ progress: [
+ {
+ v: 273,
+ b: 0,
+ seed: -1566019136,
+ attackers: {
+ input: [],
+ heroes: {
+ "6008": {
+ hp: -1,
+ energy: 491,
+ isDead: false
+ }
+ }
+ },
+ defenders: {
+ input: [],
+ heroes: {
+ "48": {
+ hp: 434998,
+ energy: 400,
+ isDead: false,
+ extra: {
+ hero48StartEnergy: 1
+ }
+ },
+ "57": {
+ hp: 610200,
+ energy: 100,
+ isDead: false
+ },
+ "58": {
+ hp: 185406,
+ energy: 669,
+ isDead: false
+ },
+ "63": {
+ hp: 621150,
+ energy: 100,
+ isDead: false
+ },
+ "67": {
+ hp: 326219,
+ energy: 669,
+ isDead: false
+ },
+ "6008": {
+ hp: -1,
+ energy: 425,
+ isDead: false
+ }
+ }
+ }
+ }
+ ]
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+};
+
+// After calling endBattle, extract parentId for retries
+const endBattleResponse = await fetch(apiEndpoint, {
+ method: 'POST',
+ body: JSON.stringify(endBattleRequest),
+ headers: headers
+});
+const endBattleData = await endBattleResponse.json();
+const parentId = endBattleData.results[0].result.response.battle.parentId;
+// Use this parentId for retry battles
+```
+
+---
+
+## Guild War Titan Demo Battles
+
+Guild War titan demo battles allow you to simulate titan battles for Guild War slots without consuming attack attempts. This is useful for testing titan team compositions against enemy defenses before committing to an actual attack.
+
+### Key Differences from Hero Battles
+
+- **Mechanic:** Use `"clan_pvp_titan"` for Guild War titan battles or `"clan_global_pvp_titan"` for Clash of Worlds titan battles (instead of `"arena"` or `"grandArena"`)
+- **No Pets/Banners:** Titans do not use pets, banners, or favor pets
+- **Element Spirits:** Titans use element spirits instead of pets
+- **Unit Type:** All units are titans (type `"titan"`), not heroes
+
+### Request Example: Guild War Titan Demo Battle
+
+**Request Body:**
+```json
+{
+ "calls": [
+ {
+ "name": "demoBattles_startBattle",
+ "args": {
+ "mechanic": "clan_pvp_titan",
+ "defenceMaxUpgrade": true,
+ "defenceTeam": {
+ "units": [4021, 4023, 4024, 4022, 4020]
+ },
+ "defenceFavor": {},
+ "maxUpgrade": true,
+ "team": {
+ "units": [4033, 4003, 4001, 4032, 4000]
+ },
+ "favor": {},
+ "defenceBuffs": {},
+ "buffs": {},
+ "firstSpiritElement": "dark",
+ "firstSpiritSkills": {},
+ "secondSpiritElement": "water",
+ "secondSpiritSkills": {},
+ "defenceFirstSpiritElement": "earth",
+ "defenceFirstSpiritSkills": {},
+ "parentId": 0,
+ "entryId": 0
+ },
+ "context": {
+ "actionTs": 887192
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+**Request Parameters (Guild War Titan Specific):**
+
+#### Args Object (`args`)
+- `mechanic`: `"clan_pvp_titan"` or `"clan_global_pvp_titan"` - **Required** - Battle mechanic type for titan battles
+ - `"clan_pvp_titan"` - Guild War titan battles
+ - `"clan_global_pvp_titan"` - Clash of Worlds titan battles
+- `defenceMaxUpgrade`: Boolean - Whether to apply maximum upgrades to defense team
+- `defenceTeam`: Object - Defender titan team configuration
+ - `units`: Array - Array of 5 titan IDs (e.g., `[4021, 4023, 4024, 4022, 4020]`)
+ - **Note:** No `pet` field for titans
+- `defenceFavor`: Object - Empty object `{}` (titans don't use favor pets)
+- `maxUpgrade`: Boolean - Whether to apply maximum upgrades to attacker team
+- `team`: Object - Attacker titan team configuration
+ - `units`: Array - Array of 5 titan IDs (e.g., `[4033, 4003, 4001, 4032, 4000]`)
+ - **Note:** No `pet` field for titans
+- `favor`: Object - Empty object `{}` (titans don't use favor pets)
+- `defenceBuffs`: Object - Empty object `{}` (no buffs for defense)
+- `buffs`: Object - Empty object `{}` (no buffs for attackers)
+- `firstSpiritElement`: String - First element spirit element for attacker (e.g., `"dark"`, `"water"`, `"earth"`, `"fire"`, `"light"`)
+- `firstSpiritSkills`: Object - First element spirit skills (usually empty `{}`)
+- `secondSpiritElement`: String - Second element spirit element for attacker (e.g., `"water"`)
+- `secondSpiritSkills`: Object - Second element spirit skills (usually empty `{}`)
+- `defenceFirstSpiritElement`: String - First element spirit element for defender (e.g., `"earth"`)
+- `defenceFirstSpiritSkills`: Object - Defense first element spirit skills (usually empty `{}`)
+- `parentId`: Number - Parent battle ID for retries
+ - **First Battle**: Use `0` to start a new battle session
+ - **Subsequent Battles**: Use the `battle.id` from the **first battle's** `endBattle` response (not `battle.parentId`)
+ - **Important**: All retry battles should use the same `parentId` (the first battle's ID) to link them together
+ - **Extraction**: Get from `response.results[0].result.response.battle.id` after the first battle's `endBattle` call
+- `entryId`: Number - Entry ID (usually `0`)
+
+**Note:** The following fields are **NOT used** for titan battles:
+- `defenceBanner` - Titans don't use banners
+- `defenceBannerStones` - Titans don't use banners
+- `banner` - Titans don't use banners
+- `bannerStones` - Titans don't use banners
+
+### Response Example: Guild War Titan Demo Battle
+
+**Response Structure:**
+```json
+{
+ "date": 1764606641.3391621,
+ "results": [
+ {
+ "ident": "body",
+ "result": {
+ "response": {
+ "battle": {
+ "userId": "35979991",
+ "typeId": "35979991",
+ "attackers": {
+ "4033": {
+ "id": 4033,
+ "xp": 1009660,
+ "level": 130,
+ "star": 6,
+ "skills": {
+ "4034": 130,
+ "4035": 130
+ },
+ "power": 292009,
+ "skins": {
+ "10019": 60,
+ "10038": 60
+ },
+ "currentSkin": 0,
+ "artifacts": [
+ {
+ "level": 130,
+ "star": 6
+ },
+ {
+ "level": 130,
+ "star": 6
+ },
+ {
+ "level": 130,
+ "star": 6
+ }
+ ],
+ "scale": 0.8,
+ "type": "titan",
+ "perks": [6, 5],
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 11762805.93,
+ "physicalAttack": 1029700.37,
+ "elementArmor": 405627,
+ "elementAttack": 479475,
+ "elementSpiritPower": 2655135,
+ "element": "dark",
+ "elementSpiritLevel": 130,
+ "elementSpiritStar": 6,
+ "elementSpiritSkills": [],
+ "elementAffinityPower": 487.5,
+ "skin": 0,
+ "state": {
+ "hp": 11762805,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 11762805
+ }
+ }
+ // ... more titans
+ },
+ "defenders": [
+ {
+ "4021": {
+ "id": 4021,
+ "xp": 1009660,
+ "level": 130,
+ "star": 6,
+ "skills": {
+ "4021": 130
+ },
+ "power": 221937,
+ "skins": {
+ "10010": 60,
+ "10031": 60,
+ "10050": 60
+ },
+ "currentSkin": 0,
+ "artifacts": [
+ {
+ "level": 130,
+ "star": 6
+ },
+ {
+ "level": 130,
+ "star": 6
+ },
+ {
+ "level": 130,
+ "star": 6
+ }
+ ],
+ "scale": 0.8,
+ "type": "titan",
+ "perks": [6],
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 12942563.01,
+ "physicalAttack": 895975.85,
+ "elementArmor": 146547,
+ "elementAttack": 709635,
+ "elementSpiritPower": 7659015,
+ "element": "earth",
+ "elementSpiritLevel": 130,
+ "elementSpiritStar": 6,
+ "elementSpiritSkills": [],
+ "elementAffinityPower": 487.5,
+ "skin": 0,
+ "state": {
+ "hp": 12942563,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 12942563
+ }
+ }
+ // ... more titans
+ }
+ ],
+ "effects": [],
+ "reward": [],
+ "startTime": 1764606641,
+ "seed": 1187705384,
+ "type": "clan_pvp_titan" // or "clan_global_pvp_titan" for Clash of Worlds battles
+ }
+ }
+ }
+ }
+ ]
+}
+```
+
+### Titan Object Structure (Response)
+
+Titan objects in the response have the following structure:
+
+- `id`: Number - Titan ID (e.g., `4033` for Hyperion, `4003` for Nova, etc.)
+- `xp`: Number - Experience points
+- `level`: Number - Titan level (e.g., `130`)
+- `star`: Number - Star level (e.g., `6`)
+- `skills`: Object - Skill levels
+ - Keys are skill IDs as strings (e.g., `"4034"`, `"4035"`)
+ - Values are skill levels (e.g., `130`)
+- `power`: Number - Total power
+- `skins`: Object - Available skins
+ - Keys are skin IDs as strings (e.g., `"10019"`, `"10038"`)
+ - Values are skin levels (e.g., `60`)
+- `currentSkin`: Number - Currently equipped skin ID (or `0` if no skin)
+- `artifacts`: Array - Artifact configurations
+ - `level`: Number - Artifact level
+ - `star`: Number - Artifact star level
+- `scale`: Number - Scale factor (typically `0.8` for titans)
+- `type`: String - Always `"titan"` for titan battles
+- `perks`: Array - Perk IDs (e.g., `[6, 5]`)
+- `anticrit`: Number - Anti-crit value (typically `1`)
+- `antidodge`: Number - Anti-dodge value (typically `1`)
+- `hp`: Number - Base HP
+- `physicalAttack`: Number - Physical attack stat
+- `elementArmor`: Number - Element armor stat
+- `elementAttack`: Number - Element attack stat
+- `elementSpiritPower`: Number - Element spirit power
+- `element`: String - Element type (`"dark"`, `"water"`, `"earth"`, `"fire"`, `"light"`)
+- `elementSpiritLevel`: Number - Element spirit level
+- `elementSpiritStar`: Number - Element spirit star level
+- `elementSpiritSkills`: Array - Element spirit skills (usually empty `[]`)
+- `elementAffinityPower`: Number - Element affinity power
+- `skin`: Number - Currently equipped skin ID (or `0`)
+- `state`: Object - Current battle state
+ - `hp`: Number - Current HP
+ - `energy`: Number - Current energy
+ - `isDead`: Boolean - Whether titan is dead
+ - `maxHp`: Number - Maximum HP
+
+### Ending a Guild War Titan Demo Battle
+
+**Request Body:**
+```json
+{
+ "calls": [
+ {
+ "name": "demoBattles_endBattle",
+ "args": {
+ "result": {
+ "win": false,
+ "stars": 0
+ },
+ "progress": [
+ {
+ "v": 273,
+ "b": 0,
+ "seed": -1979921791,
+ "attackers": {
+ "input": [],
+ "heroes": {}
+ },
+ "defenders": {
+ "input": [],
+ "heroes": {
+ "4020": {
+ "hp": 1612514,
+ "energy": 200,
+ "isDead": false
+ },
+ "4021": {
+ "hp": 8751684,
+ "energy": 761,
+ "isDead": false
+ },
+ "4022": {
+ "hp": 5767391,
+ "energy": 200,
+ "isDead": false
+ },
+ "4023": {
+ "hp": 10277969,
+ "energy": 500,
+ "isDead": false
+ },
+ "4024": {
+ "hp": 12250433,
+ "energy": 720,
+ "isDead": false
+ }
+ }
+ }
+ }
+ ]
+ },
+ "context": {
+ "actionTs": 897677
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+**Note:** For titan battles, the `heroes` field in `progress` contains titan IDs (not hero IDs), but the field name remains `heroes` for compatibility.
+
+### Example Usage: Guild War Titan Demo Battle
+
+```javascript
+// Start a Guild War titan demo battle
+const startBattleRequest = {
+ calls: [{
+ name: "demoBattles_startBattle",
+ args: {
+ mechanic: "clan_pvp_titan", // Use "clan_global_pvp_titan" for Clash of Worlds battles
+ defenceMaxUpgrade: true,
+ defenceTeam: {
+ units: [4021, 4023, 4024, 4022, 4020] // Earth titans
+ },
+ defenceFavor: {},
+ maxUpgrade: true,
+ team: {
+ units: [4033, 4003, 4001, 4032, 4000] // Dark/Water titans
+ },
+ favor: {},
+ defenceBuffs: {},
+ buffs: {},
+ firstSpiritElement: "dark",
+ firstSpiritSkills: {},
+ secondSpiritElement: "water",
+ secondSpiritSkills: {},
+ defenceFirstSpiritElement: "earth",
+ defenceFirstSpiritSkills: {},
+ parentId: 0,
+ entryId: 0
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+};
+
+const startBattleResponse = await fetch(apiEndpoint, {
+ method: 'POST',
+ body: JSON.stringify(startBattleRequest),
+ headers: headers
+});
+const startBattleData = await startBattleResponse.json();
+
+// Extract battle seed and titan data
+const battle = startBattleData.results[0].result.response.battle;
+const seed = battle.seed;
+const attackerTitans = battle.attackers;
+const defenderTitans = battle.defenders[0];
+
+// Simulate battle and get final state
+// ... (battle simulation logic) ...
+
+// End the battle
+const endBattleRequest = {
+ calls: [{
+ name: "demoBattles_endBattle",
+ args: {
+ result: {
+ win: false,
+ stars: 0
+ },
+ progress: [{
+ v: 273,
+ b: 0,
+ seed: seed, // Must match startBattle seed
+ attackers: {
+ input: [],
+ heroes: {} // Empty if all attackers dead
+ },
+ defenders: {
+ input: [],
+ heroes: {
+ "4020": { hp: 1612514, energy: 200, isDead: false },
+ "4021": { hp: 8751684, energy: 761, isDead: false },
+ "4022": { hp: 5767391, energy: 200, isDead: false },
+ "4023": { hp: 10277969, energy: 500, isDead: false },
+ "4024": { hp: 12250433, energy: 720, isDead: false }
+ }
+ }
+ }]
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+};
+
+const endBattleResponse = await fetch(apiEndpoint, {
+ method: 'POST',
+ body: JSON.stringify(endBattleRequest),
+ headers: headers
+});
+const endBattleData = await endBattleResponse.json();
+
+// Extract battle ID from first battle's endBattle response
+// IMPORTANT: Use battle.id (not battle.parentId) from the first battle as parentId for subsequent battles
+const firstBattleId = endBattleData.results[0].result.response.battle?.id;
+
+// For subsequent battles, use this firstBattleId as parentId
+console.log(`First battle ID: ${firstBattleId}`);
+console.log(`All subsequent battles will use parentId: ${firstBattleId}`);
+```
+
+### Retrying Guild War Titan Demo Battles
+
+For retry battles, use the `battle.id` from the **first battle's** `endBattle` response as the `parentId` for all subsequent battles:
+
+```javascript
+// After completing the first battle and extracting firstBattleId (see above)
+
+// Retry battle 1 - use firstBattleId as parentId
+const retryBattleRequest1 = {
+ calls: [{
+ name: "demoBattles_startBattle",
+ args: {
+ mechanic: "clan_pvp_titan", // Use "clan_global_pvp_titan" for Clash of Worlds battles
+ defenceMaxUpgrade: true,
+ defenceTeam: {
+ units: [4021, 4023, 4024, 4022, 4020]
+ },
+ defenceFavor: {},
+ maxUpgrade: true,
+ team: {
+ units: [4033, 4003, 4001, 4032, 4000]
+ },
+ favor: {},
+ defenceBuffs: {},
+ buffs: {},
+ firstSpiritElement: "dark",
+ firstSpiritSkills: {},
+ secondSpiritElement: "water",
+ secondSpiritSkills: {},
+ defenceFirstSpiritElement: "earth",
+ defenceFirstSpiritSkills: {},
+ parentId: firstBattleId, // Use battle.id from first battle's endBattle response
+ entryId: 0
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+};
+
+// Retry battle 2 - also use the same firstBattleId
+const retryBattleRequest2 = {
+ calls: [{
+ name: "demoBattles_startBattle",
+ args: {
+ // ... same args as above ...
+ parentId: firstBattleId, // Same firstBattleId for all retries
+ entryId: 0
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+};
+```
+
+**Key Points:**
+- **First Battle**: Use `parentId: 0` to start a new battle session
+- **Extract Battle ID**: After the first battle's `endBattle` call, extract `battle.id` (not `battle.parentId`)
+- **Subsequent Battles**: Use the first battle's `id` as `parentId` for all retry battles
+- **Same ParentId**: All retry battles use the same `parentId` (the first battle's ID), linking them together
+- **Response Path**: The battle ID is located at `response.results[0].result.response.battle.id`
+
+### Common Titan IDs
+
+**Dark Titans:**
+- `4033` - Hyperion
+- `4032` - Araji
+- `4030` - Keros
+- `4031` - Ignis
+
+**Water Titans:**
+- `4003` - Nova
+- `4001` - Angus
+- `4000` - Sigurd
+- `4002` - Moloch
+
+**Earth Titans:**
+- `4021` - Eden
+- `4023` - Iyari
+- `4024` - Amon
+- `4022` - Sylva
+- `4020` - Mairi
+
+**Fire Titans:**
+- `4013` - Vulcan
+- `4011` - Keros
+- `4010` - Ignis
+
+**Light Titans:**
+- `4042` - Solaris
+- `4043` - Hyperion
+- `4040` - Nova
+
+### Notes
+
+- **No Resource Consumption:** Demo battles do not consume Guild War attack attempts
+- **Testing Only:** Results are for testing purposes only and do not affect actual Guild War standings
+- **Element Spirits:** Titans use element spirits instead of pets, specified via `firstSpiritElement`, `secondSpiritElement`, etc.
+- **No Banners:** Titans do not use banners or banner stones
+- **No Favor Pets:** Titans do not use favor pets (always use empty `{}` for `favor` and `defenceFavor`)
+- **Scale Factor:** Titans typically use a scale factor of `0.8` (vs `1.0` for heroes)
+- **Battle Type:** Response `type` field will be `"clan_pvp_titan"` for Guild War titan battles, or `"clan_global_pvp_titan"` for Clash of Worlds titan battles
+
+### Retrying a Battle Simulation
+
+```javascript
+// Use the parentId from the previous battle's endBattle response
+const retryBattleRequest = {
+ calls: [{
+ name: "demoBattles_startBattle",
+ args: {
+ mechanic: "arena",
+ defenceMaxUpgrade: true,
+ defenceTeam: {
+ units: [57, 58, 63, 48, 67],
+ pet: 6008
+ },
+ defenceBanner: 5,
+ defenceBannerStones: {},
+ defenceFavor: {
+ "48": 6000,
+ "58": 6002,
+ "63": 6003,
+ "67": 6001
+ },
+ maxUpgrade: true,
+ team: {
+ units: [31, 58, 13, 40, 56],
+ pet: 6008
+ },
+ banner: 3,
+ bannerStones: {},
+ favor: {
+ "13": 6002,
+ "31": 6006,
+ "40": 6004,
+ "56": 6001,
+ "58": 6005
+ },
+ defenceBuffs: {},
+ buffs: {},
+ parentId: 71897112, // Use parentId from previous battle's endBattle response
+ entryId: 0
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+};
+```
+
+---
+
+### 3. `demoBattles_getAll`
+
+Retrieves all battle simulation history for the current user. This API allows you to view past simulation battles and their results.
+
+### Request
+
+**Endpoint:** `https://heroes-wb.nextersglobal.com/api/`
+
+**Method:** `POST`
+
+**Headers:**
+- `Content-Type: application/json; charset=UTF-8`
+- `x-auth-application-id: 3`
+- `x-auth-network-ident: web`
+- `x-auth-player-id: `
+- `x-auth-user-id: `
+- `x-auth-token: `
+- `x-auth-signature: `
+- `x-auth-session-id: `
+- `x-env-unique-session-id: `
+- `x-request-id: `
+- `x-server-time: 0`
+- `Origin: https://www.hero-wars.com`
+- `Referer: https://www.hero-wars.com/`
+
+**Request Body:**
+```json
+{
+ "calls": [
+ {
+ "name": "demoBattles_getAll",
+ "args": {},
+ "context": {
+ "actionTs": 183153
+ },
+ "ident": "group_2_body"
+ }
+ ]
+}
+```
+
+**Request Parameters:**
+
+#### Top Level
+- `name`: `"demoBattles_getAll"` - The API method name
+- `args`: Object - Empty object `{}` (no parameters required)
+- `context.actionTs`: Number - Action timestamp
+- `ident`: String - Identifier for the request (e.g., `"group_2_body"`)
+
+### Response
+
+**Status Code:** `200 OK`
+
+**Response Body Structure:**
+```json
+{
+ "date": 1764619585.5742991,
+ "results": [
+ {
+ "ident": "group_2_body",
+ "result": {
+ "response": {
+ "items": [
+ {
+ "id": "71419928",
+ "parentId": 0,
+ "userId": "35979991",
+ "replayId": "1764052363181330914",
+ "mechanic": "arena",
+ "hash": "ZWY2ZTIzZDlmM2NiMWY0Yzk5ODQ1MjY1MGE4NmQwMTE4YWZiY2M0MA==",
+ "data": {
+ "entryId": 0,
+ "attackMax": false,
+ "defenceMax": false,
+ "attackBuffs": [],
+ "defenceBuffs": [],
+ "attackFavor": {
+ "40": 6004,
+ "55": 6001,
+ "56": 6006,
+ "58": 6005,
+ "64": 6008
+ },
+ "win": true,
+ "attack": {
+ "powerSum": 1089662,
+ "units": {
+ "40": {
+ "id": 40,
+ "level": 130,
+ "star": 6,
+ "power": 192138,
+ "color": 18,
+ "favorPetId": 6004,
+ "favorPower": 10154
+ },
+ "64": {
+ "id": 64,
+ "level": 130,
+ "star": 6,
+ "power": 168453,
+ "color": 18,
+ "favorPetId": 6008,
+ "favorPower": 11064
+ },
+ "6008": {
+ "id": 6008,
+ "level": 130,
+ "star": 6,
+ "power": 181943,
+ "color": 10,
+ "favorPetId": null,
+ "favorPower": null,
+ "type": "pet"
+ }
+ },
+ "banner": {
+ "id": 1,
+ "slots": {
+ "0": 15,
+ "1": 43,
+ "2": 19
+ }
+ }
+ },
+ "defence": {
+ "powerSum": 835567,
+ "units": {
+ "16": {
+ "id": 16,
+ "level": 130,
+ "star": 6,
+ "power": 179182,
+ "color": 18,
+ "favorPetId": 0,
+ "favorPower": 0
+ },
+ "6006": {
+ "id": 6006,
+ "level": 130,
+ "star": 5,
+ "power": 171933,
+ "color": 10,
+ "favorPetId": null,
+ "favorPower": null,
+ "type": "pet"
+ }
+ },
+ "banner": {
+ "id": 2,
+ "slots": {
+ "0": 70,
+ "1": 35,
+ "2": 13
+ }
+ }
+ }
+ },
+ "ctime": "1764052367"
+ }
+ ]
+ }
+ }
+ }
+ ]
+}
+```
+
+**Response Fields:**
+
+#### Top Level
+- `date`: Number - Server timestamp (e.g., `1764619585.5742991`)
+- `results`: Array - Array of result objects
+ - `ident`: String - Identifier matching the request (e.g., `"group_2_body"`)
+ - `result.response`: Object - The actual response data
+
+#### Response Object (`result.response`)
+- `items`: Array - Array of battle history items
+
+#### Battle History Item Structure
+Each item in the `items` array represents a completed battle simulation:
+
+- `id`: String - Battle ID (e.g., `"71401948"`)
+- `parentId`: Number - Parent battle ID
+ - `0` indicates this is the first battle in a simulation session
+ - Non-zero values indicate this is a retry battle linked to the parent battle
+- `userId`: String - User ID who ran the simulation (e.g., `"35621043"`)
+- `replayId`: String - Replay ID for viewing the battle replay (e.g., `"1764021483414708034"`)
+- `mechanic`: String - Battle mechanic type
+ - `"arena"` - Arena battles
+ - `"grand"` - Grand Arena battles
+ - `"clan_pvp_titan"` - Guild War titan battles
+ - `"clan_global_pvp_titan"` - Clash of Worlds titan battles
+ - Other game mode identifiers
+- `hash`: String - Battle hash (base64 encoded, used for verification)
+- `data`: Object - Battle configuration and result data
+ - `entryId`: Number - Entry ID (usually `0`, but can be non-zero for Guild War/Clash of Worlds battles)
+ - `attackMax`: Boolean - Whether attack team had maximum upgrades
+ - `defenceMax`: Boolean - Whether defense team had maximum upgrades
+ - `attackBuffs`: Array - Attack team buffs (usually empty `[]`)
+ - `defenceBuffs`: Array | Object - Defense team buffs
+ - Usually empty `[]` for hero battles
+ - Can be an object with buff IDs as keys for titan battles (e.g., `{"96": 72}`)
+ - `attackFavor`: Object | Array - Attack team favor pet assignments
+ - **Hero Battles**: Object with hero IDs as keys (strings) and favor pet IDs as values (e.g., `{"40": 6004, "55": 6001}`)
+ - **Titan Battles**: Empty array `[]` (titans don't use favor pets)
+ - `win`: Boolean - Whether the attacker won (`true`) or lost (`false`)
+ - `attack`: Object - Attack team summary
+ - `powerSum`: Number - Total team power
+ - `units`: Object - Unit summaries
+ - Keys are hero/titan/pet IDs as strings
+ - Values are unit objects with:
+ - `id`: Number - Hero/Titan/Pet ID
+ - `level`: Number - Level
+ - `star`: Number - Star level
+ - `power`: Number - Power
+ - `color`: Number - Color/rarity (for heroes and pets)
+ - **Hero Units:**
+ - `favorPetId`: Number | null - Favor pet ID (or `0`/`null` if none)
+ - `favorPower`: Number | null - Favor pet power
+ - **Pet Units:**
+ - `type`: String - Always `"pet"` for pet units
+ - `favorPetId`: null - Always `null` for pets
+ - `favorPower`: null - Always `null` for pets
+ - **Titan Units:**
+ - `element`: String - Element type (`"dark"`, `"water"`, `"earth"`, `"fire"`, `"light"`)
+ - `elementSpiritLevel`: Number - Element spirit level
+ - `elementSpiritStar`: Number - Element spirit star level
+ - `elementSpiritSkills`: Array - Element spirit skills
+ - Each skill object contains:
+ - `skillId`: Number - Skill ID
+ - `level`: Number - Skill level
+ - `tierScale`: Number - Tier scale value
+ - `banner`: Object | null - Banner configuration
+ - **Hero Battles**: Object with:
+ - `id`: Number - Banner ID
+ - `slots`: Object | Array - Banner stone slots
+ - Can be an object with string keys (e.g., `{"0": 15, "1": 43, "2": 19}`)
+ - Can be an empty array `[]` if no stones
+ - **Titan Battles**: `null` (titans don't use banners)
+ - `defence`: Object - Defense team summary
+ - Same structure as `attack` object
+- `ctime`: String - Creation timestamp (Unix timestamp as string, e.g., `"1764052367"`)
+
+### Titan Battle Example
+
+For titan battles (`mechanic: "clan_pvp_titan"` or `"clan_global_pvp_titan"`), the structure differs:
+
+```json
+{
+ "id": "71419928",
+ "parentId": 0,
+ "userId": "35979991",
+ "replayId": "1764052363181330914",
+ "mechanic": "clan_global_pvp_titan",
+ "hash": "MTU0NGNkYmQ1MThhOGJlMTE2YmFhMDEwNGRmYTRhYjRlOWI0NzMzYg==",
+ "data": {
+ "entryId": 40,
+ "attackMax": false,
+ "defenceMax": true,
+ "attackBuffs": [],
+ "defenceBuffs": {
+ "96": 72
+ },
+ "attackFavor": [],
+ "win": true,
+ "attack": {
+ "powerSum": 1113776,
+ "units": {
+ "4020": {
+ "id": 4020,
+ "level": 130,
+ "star": 6,
+ "power": 231824,
+ "element": "earth",
+ "elementSpiritLevel": 125,
+ "elementSpiritStar": 6,
+ "elementSpiritSkills": [
+ {
+ "skillId": 4511,
+ "level": 2,
+ "tierScale": 0.325
+ },
+ {
+ "skillId": 4514,
+ "level": 3,
+ "tierScale": 6
+ }
+ ]
+ }
+ },
+ "banner": null
+ },
+ "defence": {
+ "powerSum": 1176564,
+ "units": {
+ "4000": {
+ "id": 4000,
+ "level": 130,
+ "star": 6,
+ "power": 221975,
+ "element": "water",
+ "elementSpiritLevel": 130,
+ "elementSpiritStar": 6,
+ "elementSpiritSkills": []
+ }
+ },
+ "banner": null
+ }
+ },
+ "ctime": "1764052367"
+}
+```
+
+**Key Differences for Titan Battles:**
+- `attackFavor`: Empty array `[]` (titans don't use favor pets)
+- `defenceBuffs`: Can be an object with buff IDs as keys (e.g., `{"96": 72}`)
+- Units have `element`, `elementSpiritLevel`, `elementSpiritStar`, and `elementSpiritSkills` fields instead of `favorPetId`/`favorPower`
+- `banner`: Always `null` (titans don't use banners)
+- No `color` field for titan units
+- `entryId` can be non-zero for Guild War/Clash of Worlds battles
+
+### Usage Notes
+
+1. **Retrieving History**: Call `demoBattles_getAll` with empty args to retrieve all battle simulation history for the current user
+2. **Battle Linking**: Use `parentId` to identify which battles belong to the same simulation session
+ - Battles with `parentId: 0` are the first battle in a session
+ - Battles with the same non-zero `parentId` are retry battles from the same session
+3. **Replay Viewing**: Use `replayId` to view or replay a specific battle
+4. **Filtering**: You can filter results client-side by:
+ - `mechanic` - Battle type (arena, grand, clan_pvp_titan, clan_global_pvp_titan, etc.)
+ - `win` - Win/loss status
+ - `parentId` - Group battles by simulation session
+ - `ctime` - Sort by creation time
+5. **Team Analysis**: The `data.attack` and `data.defence` objects contain team composition and power information for analysis
+
+### Example Usage
+
+```javascript
+// Retrieve all battle simulation history
+const getAllHistoryRequest = {
+ calls: [{
+ name: "demoBattles_getAll",
+ args: {},
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+};
+
+const response = await fetch(apiEndpoint, {
+ method: 'POST',
+ body: JSON.stringify(getAllHistoryRequest),
+ headers: headers
+});
+
+const data = await response.json();
+
+// Extract battle history items
+const historyItems = data.results[0].result.response.items;
+
+// Filter by win status
+const wins = historyItems.filter(item => item.data.win === true);
+const losses = historyItems.filter(item => item.data.win === false);
+
+// Group by simulation session (parentId)
+const sessions = {};
+historyItems.forEach(item => {
+ const sessionKey = item.parentId === 0 ? item.id : item.parentId;
+ if (!sessions[sessionKey]) {
+ sessions[sessionKey] = [];
+ }
+ sessions[sessionKey].push(item);
+});
+
+// Get win rate for a specific team composition
+const teamPower = 1171245;
+const teamBattles = historyItems.filter(item =>
+ item.data.attack.powerSum === teamPower
+);
+const winRate = teamBattles.filter(item => item.data.win).length / teamBattles.length;
+
+console.log(`Win rate for team power ${teamPower}: ${(winRate * 100).toFixed(2)}%`);
+```
+
+### Response Path
+
+The battle history items are located at:
+```
+response.results[0].result.response.items
+```
+
+Each item in the array contains the complete battle information including team composition, result, and metadata.
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..ea21092
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,88 @@
+# Changelog
+
+All notable changes to the HeroWarsHelper project will be documented in this file.
+
+## [Unreleased] - Arena Auto-Attack Feature
+
+### Added
+- **Arena Auto-Attack System**: Intelligent automated battle functionality for Arena and Grand Arena
+- **Smart Team Selection**: Automatically selects best counter teams with win rate calculation
+- **Opponent Difficulty Ranking**: Sorts opponents by power ratio and rank for optimal targeting
+- **Battle Simulation**: Pre-calculates win probabilities before attacking to maximize victories
+- **Dual Arena Support**: Separate handling for Arena and Grand Arena with appropriate API calls
+- **Auto-Run Integration**: Added to "Do All" function for automatic execution on page load
+- **Multilingual Support**: English and Russian translations for all arena features
+
+### Technical Implementation
+- **Helper Functions**:
+ - `calcArenaBattleWinRate()` - Battle win probability calculation
+ - `selectBestTeamForOpponent()` - Smart team selection logic
+ - `evaluateOpponentDifficulty()` - Opponent difficulty ranking
+
+- **Main Classes**:
+ - `executeArena` - Handles both Arena and Grand Arena battles
+ - Supports both `arena` and `grand` arena types
+ - Full API integration with game's battle system
+
+- **API Integration**:
+ - `arenaGetInfo` / `grandGetInfo` - Get arena status and opponents
+ - `arenaStartBattle` / `grandStartBattle` - Start battles
+ - `arenaEndBattle` / `grandEndBattle` - Complete battles
+ - `teamGetAll`, `teamGetFavor`, `heroGetAll` - Team data
+
+### User Interface
+- **New Buttons**:
+ - Arena - Individual arena battles
+ - Grand Arena - Individual grand arena battles
+ - Auto Arena & Grand Arena - Combined function
+
+- **Auto-Run Integration**:
+ - Added to "Do All" function list
+ - Runs automatically on page load when enabled
+ - Excluded from auto mode to prevent infinite loops
+
+### Battle Strategy
+1. **Get arena status** → Extract opponents and attempts
+2. **Load team data** → Get available heroes and teams
+3. **Sort opponents** → Easiest targets first
+4. **For each attempt**:
+ - Select easiest unbeaten opponent
+ - Try current team in simulation
+ - If losing, try alternative teams
+ - Start battle with best team
+ - Calculate and complete battle
+5. **Report results** → Total victories achieved
+
+### Configuration
+- **Team Selection**: Try current team first, fallback to top 5 heroes by power
+- **Win Rate Threshold**: Minimum 30% win probability to attack
+- **Opponent Selection**: Sort by difficulty (power ratio + rank factor)
+- **Skip Strategy**: Avoid opponents with no winning team
+
+### Files Modified
+- `HeroWarsHelper.user.js` - Main implementation file (~372 lines added)
+
+### Dependencies
+- Existing `BattleCalc` / `Calc` functions for battle simulation
+- Existing `Send` function for API calls
+- Existing `teamGetAll`, `teamGetFavor` data structures
+- Existing `setProgress` for status updates
+
+## [2.376] - Previous Release
+
+### Fixed
+- **Daily Quests Auto Mode**: Fixed popup appearing despite auto mode being enabled
+- **Do All Function**: Restored auto mode functionality to skip popup and auto-check tasks
+- **Test Daily Quests**: Modified to use auto mode initialization
+- **Force Auto Mode**: Daily quests now always run in auto mode by default
+
+### Changed
+- **Auto Mode Logic**: Daily quests skip popup and auto-check all tasks when in auto mode
+- **Do All Integration**: Added automatic execution of Do All function on page load
+- **Reload Game Exclusion**: Excluded reload game from auto mode to prevent infinite loops
+
+### Technical Details
+- Modified `dailyQuests` class `start()` method to handle auto mode
+- Updated `testDailyQuests()` function to call `autoInit(true)`
+- Added auto mode flag to `doYourBest` class
+- Integrated arena auto-attack into Do All function list
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
new file mode 100644
index 0000000..17c1e8a
--- /dev/null
+++ b/DEVELOPMENT.md
@@ -0,0 +1,103 @@
+# Development Workflow
+
+## Branch Structure
+
+### Main Branches
+- **`main`** - Production-ready code, stable releases
+- **`develop`** - Integration branch for features, development base
+
+### Feature Branches
+- **`feature/feature-name`** - New features and enhancements
+- **`bugfix/bug-name`** - Bug fixes and patches
+- **`hotfix/issue-name`** - Critical fixes for production
+
+## Development Workflow
+
+### 1. Starting New Work
+```bash
+# Always start from develop branch
+git checkout develop
+git pull origin develop
+
+# Create feature branch
+git checkout -b feature/your-feature-name
+```
+
+### 2. Development Process
+```bash
+# Make your changes
+# Commit frequently with descriptive messages
+git add .
+git commit -m "Add feature: description of changes"
+
+# Push feature branch
+git push origin feature/your-feature-name
+```
+
+### 3. Integration
+```bash
+# Merge feature into develop
+git checkout develop
+git merge feature/your-feature-name
+git push origin develop
+
+# Delete feature branch
+git branch -d feature/your-feature-name
+git push origin --delete feature/your-feature-name
+```
+
+### 4. Release Process
+```bash
+# Create release branch from develop
+git checkout develop
+git checkout -b release/version-number
+
+# Make final adjustments, update version numbers
+# Merge to main when ready
+git checkout main
+git merge release/version-number
+git tag v1.0.0
+git push origin main --tags
+
+# Merge back to develop
+git checkout develop
+git merge main
+git push origin develop
+```
+
+## Current Status
+
+### Active Branches
+- **`main`** - Latest stable version
+- **`develop`** - Integration branch with Arena Auto-Attack feature
+- **`feature/arena-auto-attack`** - Arena automation feature (ready for merge)
+
+### Recent Features
+- ✅ **Arena Auto-Attack** - Automated Arena and Grand Arena battles
+- ✅ **Daily Quests Auto-Mode** - Automatic quest completion
+- ✅ **Do All Function** - Comprehensive automation suite
+- ✅ **API Integration** - Game API monitoring and interaction
+
+## Best Practices
+
+### Commit Messages
+- Use descriptive commit messages
+- Reference issues when applicable
+- Keep commits focused and atomic
+
+### Code Quality
+- Test features thoroughly before merging
+- Update documentation for new features
+- Follow existing code patterns and style
+
+### Documentation
+- Update README.md for user-facing changes
+- Add technical documentation for complex features
+- Keep CHANGELOG.md updated with releases
+
+## Next Steps
+
+1. **Merge Arena Auto-Attack** - Complete integration testing
+2. **Release v1.0** - First stable release with Arena automation
+3. **Future Features** - Based on develop branch
+4. **Continuous Integration** - Automated testing and deployment
diff --git a/DUNGEON_OPTIMIZATION_ALGORITHM.md b/DUNGEON_OPTIMIZATION_ALGORITHM.md
new file mode 100644
index 0000000..012b994
--- /dev/null
+++ b/DUNGEON_OPTIMIZATION_ALGORITHM.md
@@ -0,0 +1,677 @@
+# Dungeon Optimization Algorithm Documentation
+
+## Overview
+
+The Dungeon Optimization Algorithm is an advanced automated system for maximizing titanite collection in Hero Wars Dungeon mode while ensuring titan survival. The algorithm uses sophisticated recovery-based team selection, parallel battle simulation, and iterative refinement to achieve optimal results.
+
+**Primary Goals:**
+1. **Maximize Titanite Collection** - Collect as much titanite as possible within the target limit
+2. **Prevent Titan Deaths** - Ensure all battles result in 3-star victories (no titan deaths)
+3. **Optimize Recovery** - Maximize titan health and energy recovery after each battle
+4. **Efficient Resource Usage** - Minimize time and prediction card usage
+
+## Algorithm Architecture
+
+### Core Components
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ executeDungeon │
+│ (Main Controller - Initializes and coordinates) │
+└─────────────────┬───────────────────────────────────────┘
+ │
+ ┌─────────┴─────────┐
+ │ │
+┌───────▼────────┐ ┌───────▼────────┐
+│ checkFloor │ │ startDungeon │
+│ (Floor Logic) │ │ (Init Teams) │
+└───────┬────────┘ └───────────────┘
+ │
+ │
+┌───────▼──────────────────────────────────────┐
+│ chooseElement │
+│ (Routes to element-specific strategies) │
+└───────┬──────────────────────────────────────┘
+ │
+ ┌───┴───┬────────┬────────┬────────┐
+ │ │ │ │ │
+┌───▼───┐ ┌──▼──┐ ┌──▼──┐ ┌──▼──┐ ┌──▼──┐
+│ Hero │ │Water│ │Earth│ │Fire │ │Neutral│
+│Direct │ │Direct│ │Opt │ │Opt │ │Complex│
+└───────┘ └────┘ └─────┘ └─────┘ └──────┘
+```
+
+## Core Concepts
+
+### 1. Recovery System
+
+**Recovery** is the primary metric used to evaluate battle outcomes. It represents the net change in titan combat effectiveness after a battle.
+
+**Recovery Formula:**
+```
+Recovery = AfterSumFactor - BeforeSumFactor
+```
+
+Where:
+- **AfterSumFactor**: Sum of all titan factors after battle
+- **BeforeSumFactor**: Sum of all titan factors before battle
+
+**Titan Factor Calculation:**
+```javascript
+factor = percentHP + energyBonus
+```
+
+For Earth/Fire titans: `factor = percentHP + energyBonus`
+For other titans: `factor = (percentHP + energyBonus) / 10`
+
+**Energy Bonus:**
+- Hyperion (4020) at full energy (1000): `+0.1`
+- Other titans: `energy / 20000.0`
+
+### 2. Team Selection Priority
+
+The algorithm prioritizes element types in this order:
+1. **Hero** - Direct attack, no optimization needed
+2. **Water** - Direct attack, no optimization needed
+3. **Earth** - Optimized selection (tests multiple team combinations)
+4. **Fire** - Optimized selection (tests multiple team combinations)
+5. **Neutral** - Most complex optimization (tests hundreds of combinations)
+
+### 3. Battle Safety Checks
+
+Before accepting a battle result, the algorithm verifies:
+
+1. **3-Star Victory** - `result.stars >= 3` (no titan deaths)
+2. **Titan Health Thresholds** - Each titan must meet minimum health requirements:
+ - **Hyperion (4020)**: HP > 25% OR (energy == 1000 AND HP > 5%)
+ - **Moloch (4010)**: HP% + energy/2000 > 0.63
+ - **Angus (4000)**: HP > 62% OR specific HP/energy combinations
+ - **Others**: No special requirements
+
+## Attack Strategies by Element
+
+### Strategy 1: Hero/Water (Direct Attack)
+
+**Complexity:** Low
+**Optimization:** None
+
+```javascript
+case 'hero':
+case 'water':
+ result = await startBattle(teamNum, attackerType, teams[attackerType]);
+```
+
+- Uses predefined team composition
+- No battle simulation or optimization
+- Fastest execution path
+
+**Team Composition:**
+- **Hero**: Heroes (< 6000) + Pet (>= 6000)
+- **Water**: [4000, 4001, 4002, 4003] (filtered by alive status)
+
+### Strategy 2: Earth/Fire (Optimized Selection)
+
+**Complexity:** Medium
+**Optimization:** Team composition testing
+
+**Process:**
+1. **Initial Selection** (`chooseEarthOrFire`):
+ - Tests up to 4 different team compositions
+ - Each composition tests 25 battle simulations
+ - Selects team with best recovery
+
+2. **Team Composition Testing** (`attemptAttackEarthOrFire`):
+ ```javascript
+ startIndex = team.heroes.length + attempt - 4
+ team.heroes = team.heroes.slice(startIndex)
+ ```
+ - Attempt 0: Uses last 4 titans
+ - Attempt 1: Uses last 3 titans
+ - Attempt 2: Uses last 2 titans
+ - Attempt 3: Uses last 1 titan
+
+3. **Recovery Refinement** (`findAttack`):
+ - Iteratively runs battles until target recovery is met
+ - Adjusts target recovery by 0.01 per iteration
+ - Continues until actual recovery >= target recovery
+
+**Team Composition:**
+- **Earth**: [4020, 4022, 4021, 4023, 4024] (filtered by alive status)
+- **Fire**: [4010, 4011, 4012, 4013, 4014] (filtered by alive status)
+
+### Strategy 3: Neutral (Complex Optimization)
+
+**Complexity:** High
+**Optimization:** Extensive team combination testing
+
+**Two-Phase Approach:**
+
+#### Phase 1: Fast Mode (`mode = true`)
+- Tests top 4 readiness factors
+- Tests common combinations:
+ - Single factor titans
+ - Factor pairs with water titans (4001, 4002, 4003)
+ - Aragi (4013) combinations
+ - Eden (4023) + Aragi combinations
+
+#### Phase 2: Full Mode (`mode = false`)
+- Only executed if fast mode fails or recovery < 0.2
+- Tests all possible combinations:
+ - All factor titans
+ - Factor + Aragi combinations
+ - Factor + Dark titans (4032, 4033)
+ - Factor + Light titans (4042)
+ - Factor + Factor combinations
+ - Dark titan combinations
+ - Light titan combinations
+
+**Readiness Factor Calculation:**
+```javascript
+factor = (titan.hp / titan.maxHp) + (titan.energy / 10000.0)
+```
+
+Factors are sorted ascending (weakest titans first).
+
+**Neutral Team Building:**
+- Base: Water team (4 titans)
+- Add: Neutral titan(s) based on combinations
+- Swap: Replace water titans with other elements if needed
+
+**Team Composition:**
+- **Neutral**: [4023, 4022, 4012, 4021, 4011, 4010, 4020, 4024, 4014]
+
+## Recovery Calculation System
+
+### Function: `getRecovery(result)`
+
+**Purpose:** Calculate the net recovery value for a battle result.
+
+**Process:**
+1. **Safety Check**: Returns -100 if battle didn't achieve 3 stars
+2. **Calculate After Factor**: Sum of all titan factors after battle
+3. **Calculate Before Factor**: Sum of all titan factors before battle
+4. **Return Difference**: `afterSumFactor - beforeSumFactor`
+
+**Titan Factor Calculation** (`getFactor`):
+```javascript
+function getFactor(id, energy, percentHP) {
+ let elemantId = id.slice(2, 3);
+ let isEarthOrFire = elemantId == '1' || elemantId == '2';
+ let energyBonus = id == '4020' && energy == 1000 ? 0.1 : energy / 20000.0;
+ let factor = percentHP + energyBonus;
+ return isEarthOrFire ? factor : factor / 10;
+}
+```
+
+**Key Points:**
+- Earth/Fire titans (IDs ending in 1 or 2) have 10x weight
+- Hyperion (4020) gets special bonus at full energy
+- Other titans have reduced weight (1/10)
+
+### Function: `checkTitan(id, energy, percentHP)`
+
+**Purpose:** Verify if a titan meets minimum safety requirements.
+
+**Safety Thresholds:**
+
+| Titan ID | Name | Requirement |
+|----------|------|-------------|
+| 4020 | Hyperion | HP > 25% OR (energy == 1000 AND HP > 5%) |
+| 4010 | Moloch | HP% + energy/2000 > 0.63 |
+| 4000 | Angus | HP > 62% OR (energy < 1000 AND specific HP/energy thresholds) |
+| Others | - | No special requirements (always true) |
+
+## Battle Optimization Process
+
+### 1. Battle Simulation
+
+**Function:** `startBattle(teamNum, attackerType, args)`
+
+**Process:**
+1. Creates API call to `dungeonStartBattle`
+2. Sends battle request
+3. Receives battle data
+4. Simulates battle using `BattleCalc`
+5. Returns battle result promise
+
+**Battle Simulation Settings:**
+```javascript
+battleData.progress = [{ attackers: { input: ['auto', 0, 0, 'auto', 0, 0] } }];
+```
+- Auto skill usage enabled
+- Manual skill timing disabled
+
+### 2. Parallel Battle Testing
+
+**Function:** `getBestRecovery(teamNum, attackerType, team, countBattle)`
+
+**Purpose:** Test multiple battle outcomes in parallel to find best recovery.
+
+**Process:**
+1. Creates array of battle promises (typically 25 battles)
+2. Executes all battles in parallel using `Promise.all()`
+3. Calculates recovery for each result
+4. Returns best recovery value
+
+**Advantages:**
+- Fast execution (parallel processing)
+- Statistical sampling (25 battles gives good average)
+- Finds optimal RNG outcomes
+
+### 3. Iterative Refinement
+
+**Function:** `findAttack(teamNum, attackerType, team)`
+
+**Purpose:** Iteratively refine battle result until target recovery is met.
+
+**Process:**
+```javascript
+for (let needRecovery = bestBattle.recovery;
+ recovery < needRecovery;
+ needRecovery -= correction) {
+ result = await startBattle(teamNum, attackerType, team);
+ recovery = getRecovery(result);
+}
+```
+
+**Parameters:**
+- `correction = 0.01` - Adjustment per iteration
+- Continues until `recovery >= needRecovery`
+
+**Purpose:**
+- Accounts for RNG variance in battle outcomes
+- Ensures consistent recovery levels
+- Prevents accepting suboptimal results
+
+## Floor Processing Flow
+
+### Function: `checkFloor(dungeonInfo)`
+
+**Main Loop Logic:**
+
+```
+1. Check Completion
+ ├─ Floor state == 2 → Save progress → Return
+ └─ Continue
+
+2. Check Talent Rewards
+ └─ checkTalent(dungeonInfo)
+
+3. Check Activity Limit
+ ├─ dungeonActivity >= maxDungeonActivity → End dungeon
+ └─ Continue
+
+4. Check Stop Flag
+ ├─ stopDung == true → End dungeon
+ └─ Continue
+
+5. Select Element
+ ├─ Multiple floor choices → Priority selection
+ │ ├─ Hero → Direct attack
+ │ ├─ Water → Direct attack
+ │ ├─ Earth → Optimized selection
+ │ ├─ Fire → Optimized selection
+ │ └─ Neutral → Complex optimization
+ └─ Single floor choice → Direct attack
+
+6. Execute Battle
+ └─ chooseElement(attackerType, teamNum)
+```
+
+### Element Selection Priority
+
+When multiple floor choices are available:
+
+```javascript
+for (let element in teams) {
+ let teamNum = findElement(floorChoices, element);
+ if (!!teamNum) {
+ // Found matching element, use it
+ chooseElement(floorChoices[teamNum].attackerType, teamNum);
+ return;
+ }
+}
+```
+
+**Priority Order:**
+1. Hero
+2. Water
+3. Earth (with special optimization)
+4. Fire
+5. Neutral
+
+## Special Features
+
+### 1. Talent Reward Collection
+
+**Function:** `checkTalent(dungeonInfo)`
+
+**Purpose:** Automatically collect TMNT (Teenage Mutant Ninja Turtles) talent rewards.
+
+**Process:**
+1. Checks if current floor matches talent floor
+2. Verifies doors amount (must be < 3)
+3. Checks if reward already collected
+4. Collects reward via API calls:
+ - `heroTalent_getReward`
+ - `heroTalent_farmReward`
+5. Updates UI message with reward info
+
+**Display:**
+```
+TMNT Talent: 2/3
+ 50 Gold
+ 10 Energy
+```
+
+### 2. Prediction Card Usage
+
+**Purpose:** Skip battle timers using prediction cards.
+
+**Process:**
+```javascript
+if (countPredictionCard > 0) {
+ args.isRaid = true;
+ countPredictionCard--;
+} else {
+ await countdownTimer(timer, message);
+}
+```
+
+**Benefits:**
+- Faster dungeon completion
+- Automatic resource management
+- Only uses cards when available
+
+### 3. Statistics Tracking
+
+**Function:** `showStats()`
+
+**Tracks:**
+- Total titanite collected
+- Collection speed (titanite/hour)
+- Time spent in different phases:
+ - `all` - Total time
+ - `findAttack` - Time finding optimal attacks
+ - `attackNeutral` - Time optimizing neutral attacks
+ - `attackEarthOrFire` - Time optimizing earth/fire attacks
+- Team usage frequency
+
+## Key Functions Reference
+
+### Core Functions
+
+| Function | Purpose | Complexity |
+|----------|---------|-----------|
+| `executeDungeon` | Main controller | High |
+| `startDungeon` | Initialize teams and data | Medium |
+| `checkFloor` | Process current floor | High |
+| `chooseElement` | Route to element strategy | Medium |
+| `getRecovery` | Calculate recovery value | Medium |
+| `getFactor` | Calculate titan factor | Low |
+| `checkTitan` | Verify titan safety | Low |
+
+### Element-Specific Functions
+
+| Function | Element | Purpose |
+|----------|--------|---------|
+| `attackNeutral` | Neutral | Complex optimization |
+| `findBestBattleNeutral` | Neutral | Test team combinations |
+| `attackEarthOrFire` | Earth/Fire | Optimized selection |
+| `chooseEarthOrFire` | Earth | Select best option |
+| `attemptAttackEarthOrFire` | Earth/Fire | Test team composition |
+
+### Battle Functions
+
+| Function | Purpose |
+|----------|---------|
+| `startBattle` | Initiate battle simulation |
+| `resultBattle` | Process battle result |
+| `endBattle` | Complete battle and wait timer |
+| `getBestRecovery` | Test multiple battles in parallel |
+| `findAttack` | Iteratively refine battle result |
+
+### Utility Functions
+
+| Function | Purpose |
+|----------|---------|
+| `getTitanTeam` | Get team for element type |
+| `calcFactor` | Calculate titan readiness factors |
+| `getNeutralTeam` | Build neutral team composition |
+| `clone` | Deep copy object |
+| `findElement` | Find element in floor choices |
+
+## Algorithm Flow Examples
+
+### Example 1: Simple Water Attack
+
+```
+1. checkFloor() → Finds water element
+2. chooseElement('water', 0)
+3. startBattle(0, 'water', teams.water)
+4. resultBattle() → Simulates battle
+5. endBattle() → Checks 3 stars, waits timer
+6. resultEndBattle() → Updates activity, continues
+```
+
+**Time:** ~5-10 seconds per battle
+
+### Example 2: Optimized Earth Attack
+
+```
+1. checkFloor() → Finds earth element
+2. chooseElement('earth', 0)
+3. attackEarthOrFire(0, 'earth')
+4. attemptAttackEarthOrFire() × 4 attempts
+ ├─ Each: getBestRecovery() × 25 battles
+ └─ Select best recovery
+5. findAttack() → Iterate until recovery met
+6. endBattle() → Complete battle
+7. resultEndBattle() → Continue
+```
+
+**Time:** ~30-60 seconds per battle (optimization overhead)
+
+### Example 3: Complex Neutral Attack
+
+```
+1. checkFloor() → Finds neutral element
+2. chooseElement('neutral', 0)
+3. attackNeutral(0, 'neutral')
+4. calcFactor() → Calculate readiness
+5. findBestBattleNeutral(mode=true) → Fast mode
+ ├─ Test top 4 factors
+ ├─ Test common combinations
+ └─ ~20-30 battle simulations
+6. If recovery insufficient:
+ └─ findBestBattleNeutral(mode=false) → Full mode
+ ├─ Test all factors
+ ├─ Test all combinations
+ └─ ~100-200 battle simulations
+7. findAttack() → Iterate until recovery met
+8. endBattle() → Complete battle
+9. resultEndBattle() → Continue
+```
+
+**Time:** ~60-120 seconds per battle (extensive optimization)
+
+## Performance Characteristics
+
+### Time Complexity
+
+| Element | Optimization | Average Time | Battle Simulations |
+|---------|-------------|--------------|-------------------|
+| Hero | None | 5-10s | 1 |
+| Water | None | 5-10s | 1 |
+| Earth | Medium | 30-60s | 25-100 |
+| Fire | Medium | 30-60s | 25-100 |
+| Neutral | High | 60-120s | 100-200+ |
+
+### Space Complexity
+
+- **Team Storage**: O(1) - Fixed number of teams
+- **Battle Results**: O(n) - Where n = number of parallel battles
+- **Factor Array**: O(m) - Where m = number of neutral titans
+
+### Optimization Trade-offs
+
+**Fast Mode (Neutral):**
+- ✅ Faster execution (~20-30 battles)
+- ✅ Good for most situations
+- ❌ May miss optimal combinations
+
+**Full Mode (Neutral):**
+- ✅ Exhaustive search
+- ✅ Best possible recovery
+- ❌ Slower execution (~100-200 battles)
+- ❌ Only used when fast mode fails
+
+## Error Handling
+
+### Safety Mechanisms
+
+1. **Titan Death Prevention:**
+ - Always requires 3-star victory
+ - Checks titan health thresholds
+ - Stops dungeon if death risk detected
+
+2. **Recovery Validation:**
+ - Negative recovery = failed battle
+ - Recovery < -10 = unacceptable
+ - Iterative refinement ensures minimum recovery
+
+3. **Connection Loss:**
+ - Detects missing API responses
+ - Ends dungeon gracefully
+ - Shows error message to user
+
+4. **Impossible Battles:**
+ - Detects when no safe battle exists
+ - Ends dungeon with error message
+ - Prevents infinite loops
+
+## Configuration Parameters
+
+### Tunable Values
+
+| Parameter | Default | Purpose |
+|-----------|---------|---------|
+| `maxDungeonActivity` | 150 | Target titanite amount |
+| `limitDungeonActivity` | 30180 | Maximum possible titanite |
+| `countBattle` (Earth/Fire) | 25 | Number of parallel battles |
+| `correction` (findAttack) | 0.01 | Recovery refinement step |
+| `attempts` (Earth/Fire) | 4 | Maximum team composition attempts |
+
+### Team Compositions
+
+**Neutral Titans:**
+```javascript
+[4023, 4022, 4012, 4021, 4011, 4010, 4020, 4024, 4014]
+```
+
+**Water Titans:**
+```javascript
+[4000, 4001, 4002, 4003]
+```
+
+**Earth Titans:**
+```javascript
+[4020, 4022, 4021, 4023, 4024]
+```
+
+**Fire Titans:**
+```javascript
+[4010, 4011, 4012, 4013, 4014]
+```
+
+## Best Practices
+
+### When to Use
+
+✅ **Optimal Scenarios:**
+- Long dungeon runs (1000+ titanite)
+- Multiple neutral floors
+- Need maximum titanite collection
+- Have prediction cards available
+
+⚠️ **Consider Alternatives:**
+- Short runs (< 500 titanite)
+- Only hero/water floors
+- Time-sensitive situations
+- Limited prediction cards
+
+### Optimization Tips
+
+1. **Prediction Cards:**
+ - Save for neutral floors (longest optimization)
+ - Use automatically when available
+ - Speeds up completion significantly
+
+2. **Activity Limits:**
+ - Set realistic targets
+ - Consider time constraints
+ - Monitor collection speed
+
+3. **Team Preparation:**
+ - Ensure all titans are alive
+ - Level up key titans (Hyperion, Moloch, Angus)
+ - Upgrade titan artifacts
+
+## Limitations
+
+1. **RNG Dependency:**
+ - Battle outcomes have randomness
+ - May require multiple iterations
+ - Cannot guarantee perfect recovery
+
+2. **Time Consumption:**
+ - Neutral optimization is slow
+ - Full mode can take 2+ minutes per battle
+ - May not be suitable for time-limited runs
+
+3. **Resource Usage:**
+ - Uses prediction cards automatically
+ - May consume all available cards
+ - No manual control over card usage
+
+4. **Complexity:**
+ - Hard to debug issues
+ - Many interdependent functions
+ - Requires understanding of recovery system
+
+## Future Improvements
+
+### Potential Enhancements
+
+1. **Adaptive Optimization:**
+ - Adjust battle count based on recovery variance
+ - Skip optimization for easy floors
+ - Learn from previous battles
+
+2. **Configuration Options:**
+ - User-selectable optimization levels
+ - Manual prediction card control
+ - Custom recovery thresholds
+
+3. **Performance Optimization:**
+ - Cache battle results
+ - Parallel floor processing
+ - Reduce redundant calculations
+
+4. **Enhanced Statistics:**
+ - Real-time recovery tracking
+ - Success rate monitoring
+ - Optimal team recommendations
+
+## Conclusion
+
+The Dungeon Optimization Algorithm represents a sophisticated approach to automated dungeon farming. By prioritizing recovery optimization and titan safety, it achieves high titanite collection rates while preventing titan deaths. The multi-strategy approach ensures efficient execution for different floor types, with the most complex optimization reserved for challenging neutral floors.
+
+The algorithm's strength lies in its ability to:
+- **Adapt** to different floor types
+- **Optimize** team selection for maximum recovery
+- **Protect** titans from death
+- **Efficiently** use available resources
+
+While the algorithm is complex, it provides significant value for players seeking to maximize their dungeon farming efficiency.
+
diff --git a/EXTENSION_DEVELOPMENT.md b/EXTENSION_DEVELOPMENT.md
new file mode 100644
index 0000000..6fd9a51
--- /dev/null
+++ b/EXTENSION_DEVELOPMENT.md
@@ -0,0 +1,437 @@
+# HeroWarsHelper Extension Development Guide
+
+This guide explains how to create extensions for the HeroWarsHelper (HWH) system that integrate seamlessly with the main script.
+
+## Table of Contents
+
+1. [Extension Structure](#extension-structure)
+2. [Initialization Pattern](#initialization-pattern)
+3. [Auto-Loading on Script Run](#auto-loading-on-script-run)
+4. [Menu Integration](#menu-integration)
+5. [Popup Handling](#popup-handling)
+6. [API Integration](#api-integration)
+7. [Best Practices](#best-practices)
+8. [Examples](#examples)
+
+## Extension Structure
+
+### File Naming
+
+Extensions should follow the naming pattern: `[Extension Name] HwH Ext.user.js`
+
+Example: `Secret Wealth Shop HwH Ext.user.js`
+
+### UserScript Header
+
+Every extension must include a proper UserScript header with metadata:
+
+```javascript
+// ==UserScript==
+// @name Extension Name HwH Ext
+// @namespace HeroWarsHelper.ExtensionName
+// @version 1.0
+// @description Brief description of what the extension does
+// @author YourName
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @grant none
+// @run-at document-end
+// @downloadURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/Extension%20Name%20HwH%20Ext.user.js
+// @updateURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/Extension%20Name%20HwH%20Ext.user.js
+// ==/UserScript==
+```
+
+**Important Notes:**
+- `@namespace` should be unique and follow the pattern `HeroWarsHelper.ExtensionName`
+- `@downloadURL` and `@updateURL` should use URL-encoded filenames (spaces become `%20`)
+- Always include both `@match` entries for the Hero Wars domains
+
+## Initialization Pattern
+
+### Waiting for HWH to Load
+
+Extensions must wait for HeroWarsHelper to be fully loaded before initializing:
+
+```javascript
+(function() {
+ 'use strict';
+
+ const waitForHWH = setInterval(() => {
+ if (window.HWHClasses && window.HWHClasses.ScriptMenu && window.lib && window.cheats) {
+ const scriptMenu = window.HWHClasses.ScriptMenu.getInst();
+ if (scriptMenu && scriptMenu.mainMenu) {
+ clearInterval(waitForHWH);
+ initializeExtension();
+ }
+ }
+ }, 200);
+
+ function initializeExtension() {
+ console.log('Extension Name: HWH UI is ready, initializing extension...');
+
+ // Destructure HWH APIs
+ const { HWHClasses, HWHFuncs, Send, cheats, Caller, lib } = window;
+
+ // Your extension code here
+ }
+})();
+```
+
+**Why this pattern?**
+- HeroWarsHelper loads asynchronously
+- The interval checks every 200ms until HWH is ready
+- Only initializes when all required components are available
+
+## Auto-Loading on Script Run
+
+To execute code automatically when the extension loads (without user interaction):
+
+```javascript
+function initializeExtension() {
+ const { HWHClasses, HWHFuncs, Send, cheats, Caller, lib } = window;
+
+ // Define your auto-execute function
+ async function autoExecuteFunction() {
+ try {
+ console.log('Extension: Auto-executing...');
+ HWHFuncs.setProgress('Extension: Running auto-task...');
+
+ // Your auto-execution code here
+ // Example: API calls, data processing, etc.
+
+ HWHFuncs.setProgress('Extension: Auto-task complete!', true);
+ } catch (error) {
+ console.error('Extension: Auto-execute error:', error);
+ HWHFuncs.setProgress(`Extension: Error - ${error.message}`, true);
+ }
+ }
+
+ // Execute immediately when extension loads
+ autoExecuteFunction().catch(error => {
+ console.error('Extension: Failed to auto-execute:', error);
+ });
+
+ // Add menu button (optional, for manual triggers)
+ const { ScriptMenu } = HWHClasses;
+ const scriptMenu = ScriptMenu.getInst();
+ scriptMenu.addCombinedButton([
+ { name: 'Extension Name', title: 'Description', onClick: yourFunction, color: 'purple' }
+ ]);
+}
+```
+
+**Key Points:**
+- Auto-execution happens in `initializeExtension()` after HWH is ready
+- Use `.catch()` to handle errors gracefully
+- Auto-execution doesn't block menu integration
+- You can still provide manual triggers via menu buttons
+
+## Menu Integration
+
+### Adding Menu Buttons
+
+```javascript
+const { ScriptMenu } = HWHClasses;
+const scriptMenu = ScriptMenu.getInst();
+
+// Single button
+scriptMenu.addButton({
+ name: 'Button Name',
+ title: 'Tooltip text',
+ onClick: yourFunction,
+ color: 'green' // Optional: 'green', 'red', 'purple', etc.
+});
+
+// Combined buttons (multiple buttons in one menu item)
+scriptMenu.addCombinedButton([
+ { name: 'Action 1', title: 'Description 1', onClick: function1, color: 'green' },
+ { name: '⚙️', title: 'Settings', onClick: openSettings }
+]);
+```
+
+### Button Colors
+
+Available colors: `'green'`, `'red'`, `'purple'`, `'blue'`, etc.
+
+## Popup Handling
+
+### Creating Custom Popups
+
+When creating popups, always properly handle the popup promise to prevent menu interference:
+
+```javascript
+async function openPopup() {
+ const popupContent = document.createElement('div');
+ popupContent.style.cssText = 'display: flex; flex-direction: column; height: 70vh; color: #fce1ac;';
+
+ // Build your popup content
+ const contentContainer = document.createElement('div');
+ contentContainer.style.cssText = 'flex-grow: 1; overflow-y: auto; padding: 10px;';
+ // ... add content to contentContainer ...
+ popupContent.appendChild(contentContainer);
+
+ try {
+ // Your popup logic here
+ // Fetch data, build UI, etc.
+ } catch (error) {
+ console.error("Popup Error:", error);
+ contentContainer.innerHTML = `Error: ${error.message}
`;
+ }
+
+ // Use confirm with proper async handling
+ const popupPromise = HWHFuncs.popup.confirm('', [{ msg: 'Close', result: true, isClose: true }]);
+
+ // Wait a tick for popup to initialize
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ const popupBody = document.querySelector('.PopUp_Container');
+ if (popupBody) {
+ popupBody.innerHTML = '';
+ popupBody.appendChild(popupContent);
+ }
+
+ // CRITICAL: Wait for popup to close before returning
+ // This prevents menu interference bugs
+ await popupPromise;
+}
+```
+
+**Important:** Always `await` the popup promise. Not doing so can cause other menu items to incorrectly show your popup.
+
+## API Integration
+
+### Using the Caller Class
+
+The `Caller` class provides a clean way to make API calls:
+
+```javascript
+// Single API call
+const caller = new Caller(['shopGetAll']);
+await caller.send();
+const shopsData = caller.result('shopGetAll');
+
+// Multiple API calls
+const caller = new Caller(['shopGetAll', 'userGetInfo']);
+await caller.send();
+const shops = caller.result('shopGetAll');
+const userInfo = caller.result('userGetInfo');
+
+// API call with arguments
+const call = {
+ name: 'shopBuy',
+ args: {
+ shopId: 1576000026,
+ slot: 6,
+ cost: { consumable: { "85": 40000 } },
+ reward: { consumable: { "55": 80 } }
+ }
+};
+const caller = new Caller([call]);
+await caller.send();
+const result = caller.result('shopBuy');
+```
+
+### Using Send Function
+
+For direct API calls:
+
+```javascript
+const response = await Send('{"calls":[{"name":"shopGetAll","args":{},"ident":"body"}]}');
+```
+
+### Translation System
+
+Use the translation system for item names:
+
+```javascript
+// Consumable name
+const translationKey = `LIB_CONSUMABLE_NAME_${consumableId}`;
+const itemName = cheats.translate(translationKey);
+
+// Fragment/Hero name
+const libTypeForTranslate = 'HERO'; // or 'TITAN', etc.
+const translationKey = `LIB_${libTypeForTranslate}_NAME_${itemId}`;
+const itemName = cheats.translate(translationKey);
+```
+
+## Best Practices
+
+### Error Handling
+
+Always use try/catch blocks:
+
+```javascript
+try {
+ // Your code
+} catch (error) {
+ console.error("Error:", error);
+ HWHFuncs.setProgress(`Error: ${error.message}`, true);
+}
+```
+
+### Progress Messages
+
+Provide user feedback:
+
+```javascript
+HWHFuncs.setProgress('Starting operation...');
+// ... do work ...
+HWHFuncs.setProgress('Operation complete!', true); // true = auto-hide
+```
+
+### Console Logging
+
+Use descriptive console logs:
+
+```javascript
+console.log('Extension: Starting process...');
+console.log('%cSuccess!', 'color: green; font-weight: bold;');
+console.error('Error occurred:', error);
+```
+
+### Code Organization
+
+- Keep functions focused and single-purpose
+- Use descriptive variable names
+- Add comments for complex logic
+- Follow existing code patterns from other extensions
+
+## Examples
+
+### Complete Extension Template
+
+```javascript
+// ==UserScript==
+// @name Example Extension HwH Ext
+// @namespace HeroWarsHelper.ExampleExtension
+// @version 1.0
+// @description Example extension template
+// @author YourName
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @grant none
+// @run-at document-end
+// @downloadURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/Example%20Extension%20HwH%20Ext.user.js
+// @updateURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/Example%20Extension%20HwH%20Ext.user.js
+// ==/UserScript==
+
+(function() {
+ 'use strict';
+
+ const waitForHWH = setInterval(() => {
+ if (window.HWHClasses && window.HWHClasses.ScriptMenu && window.lib && window.cheats) {
+ const scriptMenu = window.HWHClasses.ScriptMenu.getInst();
+ if (scriptMenu && scriptMenu.mainMenu) {
+ clearInterval(waitForHWH);
+ initializeExtension();
+ }
+ }
+ }, 200);
+
+ function initializeExtension() {
+ console.log('Example Extension: HWH UI is ready, initializing extension...');
+
+ const { HWHClasses, HWHFuncs, Send, cheats, Caller, lib } = window;
+
+ // Auto-execute function (runs on script load)
+ async function autoExecute() {
+ try {
+ console.log('Example Extension: Auto-executing...');
+ HWHFuncs.setProgress('Example Extension: Running...');
+
+ // Your auto-execution code here
+
+ HWHFuncs.setProgress('Example Extension: Complete!', true);
+ } catch (error) {
+ console.error('Example Extension: Error:', error);
+ HWHFuncs.setProgress(`Example Extension: Error - ${error.message}`, true);
+ }
+ }
+
+ // Manual trigger function
+ async function manualFunction() {
+ try {
+ HWHFuncs.setProgress('Example Extension: Manual trigger...');
+
+ // Your manual trigger code here
+
+ HWHFuncs.setProgress('Example Extension: Done!', true);
+ } catch (error) {
+ console.error('Example Extension: Error:', error);
+ HWHFuncs.setProgress(`Error: ${error.message}`, true);
+ }
+ }
+
+ // Popup function
+ async function openPopup() {
+ const popupContent = document.createElement('div');
+ popupContent.style.cssText = 'display: flex; flex-direction: column; height: 70vh; color: #fce1ac;';
+
+ const contentContainer = document.createElement('div');
+ contentContainer.style.cssText = 'flex-grow: 1; overflow-y: auto; padding: 10px;';
+ contentContainer.innerHTML = 'Example popup content
';
+ popupContent.appendChild(contentContainer);
+
+ const popupPromise = HWHFuncs.popup.confirm('', [{ msg: 'Close', result: true, isClose: true }]);
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ const popupBody = document.querySelector('.PopUp_Container');
+ if (popupBody) {
+ popupBody.innerHTML = '';
+ popupBody.appendChild(popupContent);
+ }
+
+ await popupPromise;
+ }
+
+ // Auto-execute on load
+ autoExecute().catch(error => {
+ console.error('Example Extension: Failed to auto-execute:', error);
+ });
+
+ // Menu integration
+ const { ScriptMenu } = HWHClasses;
+ const scriptMenu = ScriptMenu.getInst();
+ scriptMenu.addCombinedButton([
+ { name: 'Example Action', title: 'Run example action', onClick: manualFunction, color: 'green' },
+ { name: '⚙️', title: 'Open Settings', onClick: openPopup }
+ ]);
+
+ console.log('Example Extension: UI initialized and attached to HWH menu.');
+ }
+})();
+```
+
+## Reference Extensions
+
+Study these existing extensions for patterns:
+
+- **Secret Wealth Shop HwH Ext.user.js** - Auto-purchase on load, popup handling
+- **Advanced Auto-Buyer HwH Ext-1.6.user.js** - Complex UI, settings management
+
+## Troubleshooting
+
+### Extension Not Loading
+
+- Check browser console for errors
+- Verify HWH is loaded (check `window.HWHClasses`)
+- Ensure all required APIs are available before use
+
+### Popup Showing Wrong Content
+
+- Always `await` the popup promise
+- Wait a tick before modifying popup content
+- Ensure popup is properly closed before opening another
+
+### API Calls Failing
+
+- Check network tab for request/response
+- Verify API call format matches documentation
+- Use try/catch for error handling
+
+## Additional Resources
+
+- See `SECRET_WEALTH_SHOP_API_DOCUMENTATION.md` for API examples
+- Check `GUILD_WAR_API_DOCUMENTATION.md` for complex API patterns
+- Review `HeroWarsHelper.user.js` for HWH API implementations
+
diff --git a/GUILD_RAID_BOSS_API_DOCUMENTATION.md b/GUILD_RAID_BOSS_API_DOCUMENTATION.md
new file mode 100644
index 0000000..8237afe
--- /dev/null
+++ b/GUILD_RAID_BOSS_API_DOCUMENTATION.md
@@ -0,0 +1,1261 @@
+# Hero Wars Guild Raid Boss API Documentation
+
+## Overview
+
+This document provides comprehensive documentation for the Hero Wars Guild Raid Boss API endpoints, including actual request and response examples captured from network traffic in `Boss2.har`.
+
+**Base URL:** `https://heroes-wb.nextersglobal.com/api/`
+
+**Protocol:** HTTPS
+
+**Method:** POST
+
+**Content-Type:** `application/json; charset=UTF-8`
+
+---
+
+## Authentication Headers
+
+All API requests require the same authentication headers as other Hero Wars APIs:
+
+| Header | Description | Example Value |
+|--------|-------------|---------------|
+| `X-Auth-User-Id` | User's unique identifier | `73660848` |
+| `X-Auth-Token` | Authentication token | `ps-nXSputHQNgzhVMJxIFscmwqTPliGdAfCrjaKUkDbOZy/ov-1761358347-104.28.233.73-6454e221c47a6073792c019a80f5338e` |
+| `X-Auth-Player-Id` | Player's unique identifier | `35979991` |
+| `X-Auth-Session-Id` | Session identifier | `0t4o0ss08xlrca` |
+| `X-Auth-Session-Key` | Session key (can be empty) | `` |
+| `X-Auth-Signature` | Request signature for validation | `d6f7ec2a94a371bc178860784c84bf09` |
+| `X-Auth-Application-Id` | Application identifier | `3` |
+| `X-Auth-Network-Ident` | Network identifier | `web` |
+| `X-Request-Id` | Unique request identifier | `20` |
+| `X-Server-Time` | Server time offset | `0` |
+| `X-Env-Unique-Session-Id` | Unique session identifier | `7387672360677220301` |
+| `X-Env-Library-Version` | Library version | `1` |
+| `X-Full-Referer` | Full referrer URL | `https://www.hero-wars.com/` |
+
+---
+
+## API Request Structure
+
+All API requests follow the same JSON structure:
+
+```json
+{
+ "calls": [
+ {
+ "name": "methodName",
+ "args": { /* method-specific arguments */ },
+ "context": {
+ "actionTs": 106827 // Action timestamp in milliseconds
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+---
+
+## Guild Raid Boss Endpoints
+
+### 1. clanRaid_getInfo
+
+**Description:** Retrieves comprehensive information about the current Guild Raid (Minions Attack) status, including Asgard boss information, minion node status, shop items, buffs, and user statistics. This endpoint provides the current state of both boss battles and minion nodes.
+
+**Note:** This API returns information for **Asgard bosses**:
+- **Boss 1** = OSH
+- **Boss 2** = Mastro
+
+**Request:**
+
+```json
+{
+ "calls": [
+ {
+ "name": "clanRaid_getInfo",
+ "args": {},
+ "context": {
+ "actionTs": 1762577778086
+ },
+ "ident": "clanRaid_getInfo"
+ }
+ ]
+}
+```
+
+**Request Parameters:**
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| (none) | - | This endpoint takes no arguments |
+
+**Response Structure:**
+
+The response contains multiple sections:
+
+```json
+{
+ "boss": {
+ "timestamps": {
+ "start": 1762480800,
+ "end": 1762736400
+ },
+ "teams": [
+ {
+ "statLevel": 485,
+ "team": 21,
+ "unitLevel": 130,
+ "states": [
+ {
+ "1": {
+ "id": 2025,
+ "level": 130,
+ "hp": 447630109.82,
+ "state": {
+ "hp": 104142623,
+ "maxHp": 447979143,
+ "isDead": false
+ }
+ // ... boss stats ...
+ },
+ "2": {
+ "id": 2025,
+ "level": 130,
+ "hp": 859409061.01,
+ "state": {
+ "hp": 860079174,
+ "maxHp": 860079174,
+ "isDead": false
+ }
+ // ... boss stats ...
+ }
+ }
+ ]
+ }
+ ],
+ "level": 150
+ },
+ "nodes": {
+ "1": {
+ "reward": {
+ "consumable": {
+ "159": 15540,
+ "169": 37,
+ "170": 27
+ }
+ },
+ "victoryPoints": [80],
+ "timestamps": {
+ "start": 1762135200,
+ "end": 1762480800
+ },
+ "teams": [
+ {
+ "statLevel": 310,
+ "team": 19,
+ "unitLevel": 130,
+ "victoryPoints": 80,
+ "states": [
+ {
+ "1": {
+ "id": 2030,
+ "state": {
+ "isDead": true,
+ "hp": 0
+ }
+ // ... minion stats ...
+ }
+ }
+ ],
+ "points": 80
+ }
+ ]
+ }
+ // ... nodes 2-9 ...
+ },
+ "shop": {
+ "1": {
+ "buffId": 113,
+ "buffValue": 5,
+ "buyLimit": 5,
+ "cost": {
+ "gold": 1000000
+ },
+ "boughtCount": 0
+ }
+ // ... more shop items ...
+ },
+ "buffs": {
+ "114": {
+ "id": 114,
+ "value": 5
+ }
+ // ... active buffs ...
+ },
+ "stats": {
+ "currentBoss": "2",
+ "points": "24119",
+ "bossKilled": [],
+ "clanBuff": [
+ {
+ "id": 30,
+ "value": 24.119
+ }
+ ],
+ "weekStart": "1762135200"
+ },
+ "userStats": {
+ "damage": "21079289",
+ "points": "1000",
+ "usedHeroes": [46, 52, 48, 40, 37],
+ "bossReward": [],
+ "damageReward": {
+ "15000": {
+ "ascensionGear": {
+ "1": "1",
+ "2": "1",
+ "3": "1"
+ }
+ }
+ // ... more damage rewards ...
+ }
+ },
+ "attempts": 0,
+ "bossAttempts": 0,
+ "lastBossId": "1",
+ "coins": 0
+}
+```
+
+**Response Fields:**
+
+#### Boss Information (`boss`)
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `boss.timestamps.start` | Number | Boss event start timestamp |
+| `boss.timestamps.end` | Number | Boss event end timestamp |
+| `boss.teams` | Array | Boss team configurations with current states |
+| `boss.teams[].states[].1` | Object | **Boss 1 (OSH)** - First phase stats and state |
+| `boss.teams[].states[].2` | Object | **Boss 2 (Mastro)** - Second phase stats and state (if applicable) |
+| `boss.level` | Number | Current boss level (e.g., 150) |
+
+**Boss Identification:**
+- **Boss 1** = **OSH** (id: 2025, first phase)
+- **Boss 2** = **Mastro** (id: 2025, second phase, or separate boss)
+
+#### Minion Nodes Information (`nodes`)
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `nodes[1-9]` | Object | Minion node status (nodes 1 through 9) |
+| `nodes[].reward` | Object | Rewards available for this node |
+| `nodes[].reward.consumable` | Object | Consumable items (fragments, etc.) |
+| `nodes[].victoryPoints` | Array | Victory points available (e.g., [80]) |
+| `nodes[].timestamps.start` | Number | Node start timestamp |
+| `nodes[].timestamps.end` | Number | Node end timestamp |
+| `nodes[].teams` | Array | Minion team configurations |
+| `nodes[].teams[].states[].1` | Object | Minion hero state (isDead, hp, etc.) |
+| `nodes[].teams[].points` | Number | Points earned from this team |
+
+#### Current Status (`stats`)
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `stats.currentBoss` | String | Currently active boss ID ("1" = OSH, "2" = Mastro) |
+| `stats.points` | String | Total clan points |
+| `stats.bossKilled` | Array | Array of killed boss IDs |
+| `stats.clanBuff` | Array | Active clan buffs |
+| `stats.weekStart` | String | Week start timestamp |
+
+#### User Statistics (`userStats`)
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `userStats.damage` | String | Total damage dealt by user |
+| `userStats.points` | String | User's contribution points |
+| `userStats.usedHeroes` | Array | Hero IDs that have been used in battles |
+| `userStats.bossReward` | Array | Boss rewards claimed |
+| `userStats.damageReward` | Object | Damage milestone rewards (keyed by damage threshold) |
+
+#### Attempts and Resources
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `attempts` | Number | Remaining minion node attempts |
+| `bossAttempts` | Number | Remaining boss battle attempts |
+| `lastBossId` | String | Last boss ID fought ("1" = OSH, "2" = Mastro) |
+| `coins` | Number | Raid coins available |
+
+#### Shop and Buffs
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `shop` | Object | Available shop items (keyed by item ID) |
+| `shop[].buffId` | Number | Buff ID this item provides |
+| `shop[].buffValue` | Number | Buff value/amount |
+| `shop[].buyLimit` | Number | Purchase limit for this item |
+| `shop[].cost` | Object | Cost (gold or coins) |
+| `shop[].boughtCount` | Number | Number of times already purchased |
+| `buffs` | Object | Currently active buffs (keyed by buff ID) |
+| `buffs[].id` | Number | Buff ID |
+| `buffs[].value` | Number | Buff value |
+
+**Usage Notes:**
+
+- This endpoint provides the complete current state of Guild Raid (Minions Attack)
+- **Boss Status**: The `boss` object contains current Asgard boss information (OSH and Mastro)
+- **Minion Status**: The `nodes` object contains status of all 9 minion nodes
+- Use `stats.currentBoss` to determine which boss is currently active
+- Use `bossAttempts` and `attempts` to check remaining battle attempts
+- `userStats.usedHeroes` tracks which heroes have been used (heroes can only be used once per day)
+- Shop items provide buffs that enhance battle performance
+- Active buffs are listed in the `buffs` object
+
+**Example Usage:**
+
+```javascript
+// Get current Guild Raid status
+const response = await Send(JSON.stringify({
+ calls: [{
+ name: "clanRaid_getInfo",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "clanRaid_getInfo"
+ }]
+}));
+
+const raidInfo = response.results[0].result.response;
+
+// Check current boss
+const currentBoss = raidInfo.stats.currentBoss; // "1" = OSH, "2" = Mastro
+console.log(`Current boss: ${currentBoss === "1" ? "OSH" : "Mastro"}`);
+
+// Check boss attempts
+console.log(`Boss attempts remaining: ${raidInfo.bossAttempts}`);
+
+// Check minion node status
+const node1 = raidInfo.nodes["1"];
+console.log(`Node 1 status: ${node1.teams[0].states[0]["1"].state.isDead ? "Defeated" : "Active"}`);
+
+// Check minion attempts
+console.log(`Minion attempts remaining: ${raidInfo.attempts}`);
+```
+
+---
+
+### 2. clanRaid_usersInBossBattle
+
+**Description:** Retrieves information about other clan members currently fighting the same boss. Returns an empty array if no one is currently in battle.
+
+**Request:**
+
+```json
+{
+ "calls": [
+ {
+ "name": "clanRaid_usersInBossBattle",
+ "args": {},
+ "context": {
+ "actionTs": 106827
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+**Request Parameters:**
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| (none) | - | This endpoint takes no arguments |
+
+**Response:**
+
+When no users are in battle:
+
+```json
+{
+ "date": 1762543730.265765,
+ "results": [
+ {
+ "ident": "body",
+ "result": {
+ "response": []
+ }
+ }
+ ]
+}
+```
+
+When users are in battle, the response contains an array of user objects with their battle status.
+
+**Response Fields:**
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `date` | Number | Server timestamp of the response |
+| `results[].ident` | String | Request identifier ("body") |
+| `results[].result.response` | Array | Array of users currently in boss battle (empty if none) |
+
+**Usage Notes:**
+
+- This endpoint is typically called periodically to check if other clan members are fighting the boss
+- An empty array indicates the boss is available for battle
+- Used to coordinate multiple clan members attacking the same boss
+
+---
+
+### 3. clanRaid_startBossBattle
+
+**Description:** Initiates a battle against a guild raid boss. Returns detailed battle configuration including hero stats, boss stats, and battle effects.
+
+**Request:**
+
+All 5 attacks from the HAR file are documented below. Each attack uses a different hero combination:
+
+#### Attack 1
+
+```json
+{
+ "calls": [
+ {
+ "name": "clanRaid_startBossBattle",
+ "args": {
+ "heroes": [46, 52, 48, 40, 37],
+ "pet": 6005,
+ "favor": {
+ "37": 6000,
+ "40": 6004,
+ "46": 6001,
+ "48": 6005,
+ "52": 6006
+ }
+ },
+ "context": {
+ "actionTs": 172882
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+#### Attack 2
+
+```json
+{
+ "calls": [
+ {
+ "name": "clanRaid_startBossBattle",
+ "args": {
+ "heroes": [58, 50, 42, 9, 51],
+ "pet": 6005,
+ "favor": {
+ "9": 6004,
+ "42": 6006,
+ "50": 6001,
+ "58": 6005
+ }
+ },
+ "context": {
+ "actionTs": 284456
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+#### Attack 3
+
+```json
+{
+ "calls": [
+ {
+ "name": "clanRaid_startBossBattle",
+ "args": {
+ "heroes": [64, 13, 29, 1, 43],
+ "pet": 6005,
+ "favor": {
+ "1": 6004,
+ "13": 6008,
+ "29": 6006,
+ "43": 6002,
+ "64": 6005
+ }
+ },
+ "context": {
+ "actionTs": 376716
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+#### Attack 4
+
+```json
+{
+ "calls": [
+ {
+ "name": "clanRaid_startBossBattle",
+ "args": {
+ "heroes": [16, 65, 57, 31, 61],
+ "pet": 6005,
+ "favor": {
+ "16": 6004,
+ "31": 6006,
+ "57": 6003,
+ "61": 6001,
+ "65": 6000
+ }
+ },
+ "context": {
+ "actionTs": 429316
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+#### Attack 5
+
+```json
+{
+ "calls": [
+ {
+ "name": "clanRaid_startBossBattle",
+ "args": {
+ "heroes": [56, 62, 55, 63, 28],
+ "pet": 6006,
+ "favor": {
+ "28": 6004,
+ "55": 6005,
+ "56": 6006,
+ "62": 6008,
+ "63": 6003
+ }
+ },
+ "context": {
+ "actionTs": 466406
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+**Request Parameters:**
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `heroes` | Array[Number] | Array of 5 hero IDs to use in battle |
+| `pet` | Number | Pet ID to use in battle (e.g., 6005 or 6006) |
+| `favor` | Object | Favor pet assignments - mapping of hero ID (string) to pet ID (number). Note: Not all heroes may have favor assignments |
+
+**Summary of All 5 Attacks:**
+
+| Attack | Heroes | Pet | Favor Assignments | actionTs |
+|--------|--------|-----|-------------------|----------|
+| 1 | [46, 52, 48, 40, 37] | 6005 | 37→6000, 40→6004, 46→6001, 48→6005, 52→6006 | 172882 |
+| 2 | [58, 50, 42, 9, 51] | 6005 | 9→6004, 42→6006, 50→6001, 58→6005 | 284456 |
+| 3 | [64, 13, 29, 1, 43] | 6005 | 1→6004, 13→6008, 29→6006, 43→6002, 64→6005 | 376716 |
+| 4 | [16, 65, 57, 31, 61] | 6005 | 16→6004, 31→6006, 57→6003, 61→6001, 65→6000 | 429316 |
+| 5 | [56, 62, 55, 63, 28] | 6006 | 28→6004, 55→6005, 56→6006, 62→6008, 63→6003 | 466406 |
+
+**Response:**
+
+The response includes comprehensive battle data:
+
+```json
+{
+ "date": 1762543796.3174701,
+ "results": [
+ {
+ "ident": "body",
+ "result": {
+ "response": {
+ "battle": {
+ "userId": "35979991",
+ "typeId": 11002,
+ "attackers": {
+ "46": {
+ "id": 46,
+ "xp": 3625195,
+ "level": 130,
+ "color": 18,
+ "slots": [0, 0, 0, 0, 0, 0],
+ "skills": {
+ "230": 130,
+ "231": 130,
+ "232": 130,
+ "233": 130,
+ "6007": 130
+ },
+ "power": 109893,
+ "star": 6,
+ "runes": [43750, 9850, 3740, 8260, 9830],
+ "skins": {
+ "101": 35,
+ "315": 16,
+ "159": 10,
+ "262": 53
+ },
+ "currentSkin": 262,
+ "titanGiftLevel": 30,
+ "artifacts": [
+ {"level": 100, "star": 6},
+ {"level": 78, "star": 5},
+ {"level": 50, "star": 5}
+ ],
+ "scale": 1,
+ "petId": 6001,
+ "type": "hero",
+ "perks": [9, 5, 1, 22],
+ "ascensions": {
+ "1": [0, 1, 3, 5, 2, 4, 6, 7, 8, 9],
+ "2": [0]
+ },
+ "agility": 2122,
+ "hp": 334910,
+ "intelligence": 7077,
+ "physicalAttack": 50,
+ "strength": 5151,
+ "armor": 10554.3,
+ "magicPower": 45696,
+ "magicResist": 9025,
+ "skin": 262,
+ "favorPetId": 6001,
+ "favorPower": 5417
+ }
+ // ... additional heroes (52, 48, 40, 37) ...
+ },
+ "defenders": [
+ {
+ "1": {
+ "id": 2025,
+ "xp": 0,
+ "level": 130,
+ "color": 18,
+ "slots": [],
+ "skills": {
+ "3052": 130,
+ "3053": 130,
+ "3054": 130,
+ "3055": 130,
+ "3056": 130
+ },
+ "power": 22955853,
+ "star": 6,
+ "runes": [0, 0, 0, 0, 0],
+ "skins": [],
+ "currentSkin": 0,
+ "scale": "1.5",
+ "petId": 0,
+ "type": "hero",
+ "perks": null,
+ "ascensions": [],
+ "agility": 8725.85,
+ "armor": 79212.11,
+ "armorPenetration": 42748.41,
+ "hp": 447630109.82,
+ "intelligence": 39434.28,
+ "magicPenetration": 41724.51,
+ "magicPower": 218797.01,
+ "magicResist": 8689.38,
+ "physicalAttack": 296434.05,
+ "strength": 8725.85,
+ "skin": 0,
+ "favorPetId": 0,
+ "favorPower": 0,
+ "mainStat": "intelligence",
+ "stats": {
+ "additionalPower": 0,
+ "agility": 8725.8505347018272,
+ "anticrit": 0,
+ "antidodge": 0,
+ "armor": 79212.106718626412,
+ "armorPenetration": 42748.409808695746,
+ "dodge": 0,
+ "hp": 447630109.8180936,
+ "intelligence": 39434.278718236761,
+ "lifesteal": 0,
+ "magicPenetration": 41724.510717210644,
+ "magicPower": 218797.01134175769,
+ "magicResist": 8689.3840861536009,
+ "physicalAttack": 296434.0513363143,
+ "physicalCritChance": 0,
+ "strength": 8725.8505347018272
+ },
+ "state": {
+ "hp": 168618240,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 447979143
+ }
+ },
+ "2": {
+ "id": 2025,
+ "xp": 0,
+ "level": 130,
+ "color": 18,
+ "slots": [],
+ "skills": {
+ "3052": 130,
+ "3053": 130,
+ "3054": 130,
+ "3055": 130,
+ "3056": 130,
+ "2030": 1
+ },
+ "power": 44073148,
+ "star": 6,
+ "runes": [0, 0, 0, 0, 0],
+ "skins": [],
+ "currentSkin": 0,
+ "scale": "1.5",
+ "petId": 0,
+ "type": "hero",
+ "perks": null,
+ "ascensions": [],
+ "agility": 16752.84,
+ "armor": 152080.03,
+ "armorPenetration": 82073.06,
+ "hp": 859409061.01,
+ "intelligence": 75710.23,
+ "magicPenetration": 80107.26,
+ "magicPower": 420070.34,
+ "magicResist": 16682.83,
+ "physicalAttack": 569126.39,
+ "strength": 16752.84,
+ "skin": 0,
+ "favorPetId": 0,
+ "favorPower": 0,
+ "mainStat": "intelligence",
+ "stats": {
+ "additionalPower": 0,
+ "agility": 16752.838672133334,
+ "anticrit": 0,
+ "antidodge": 0,
+ "armor": 152080.03385566853,
+ "armorPenetration": 82073.055247421085,
+ "dodge": 0,
+ "hp": 859409061.01345468,
+ "intelligence": 75710.225254406789,
+ "lifesteal": 0,
+ "magicPenetration": 80107.262202035185,
+ "magicPower": 420070.34367322532,
+ "magicResist": 16682.826410630052,
+ "physicalAttack": 569126.39280428167,
+ "physicalCritChance": 0,
+ "strength": 16752.838672133334
+ },
+ "state": {
+ "hp": 860079174,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 860079174
+ }
+ }
+ }
+ ],
+ "effects": {
+ "attackers": {
+ "percentDamageBuff_any": 24.119,
+ "bossAstralHealOnAttack": 200,
+ "bossAstralSwitcherCDReduce": 5,
+ "bossAstralFatigueStacksReduction": 3,
+ "bossAstralParalyseHealReduction": 20,
+ "bossAstralMaterialAuraDuration": 3,
+ "bossAstralMaterialAuraReduction": 5,
+ "bossAstralAntihealAuraReduction": 5,
+ "bossAstralBonusEnergyOnSwitch": 10,
+ "bossAstralDamageReductionOnSwitch_5": 20
+ },
+ "battleConfig": "clan_pvp"
+ },
+ "reward": [],
+ "startTime": 1762543796,
+ "seed": 2976712129,
+ "type": "clan_raid",
+ "result": {
+ "raidId": "2",
+ "level": "150"
+ }
+ },
+ "endTime": 1762543976
+ }
+ }
+ }
+ ]
+}
+```
+
+**Response Fields:**
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `battle.userId` | String | User ID of the player |
+| `battle.typeId` | Number | Battle type ID (11002 for clan raid) |
+| `battle.attackers` | Object | Player heroes and pet data (keyed by hero/pet ID) |
+| `battle.defenders` | Array | Boss phases (array with boss objects keyed by phase number "1", "2", etc.) |
+| `battle.defenders[].1` | Object | First boss phase stats |
+| `battle.defenders[].2` | Object | Second boss phase stats (if applicable) |
+| `battle.effects.attackers` | Object | Active buffs and effects for attackers |
+| `battle.effects.battleConfig` | String | Battle configuration type ("clan_pvp") |
+| `battle.startTime` | Number | Battle start timestamp |
+| `battle.seed` | Number | Random seed for battle simulation |
+| `battle.type` | String | Battle type ("clan_raid") |
+| `battle.result.raidId` | String | Raid ID |
+| `battle.result.level` | String | Boss level |
+
+**Boss Phase Structure:**
+
+Each boss phase contains:
+- **Stats**: Base stats (hp, armor, magicPower, etc.)
+- **State**: Current battle state (hp, energy, isDead, maxHp)
+- **Skills**: Boss skill levels
+- **Scale**: Boss size multiplier ("1.5")
+
+**Battle Effects:**
+
+The `effects.attackers` object contains various buffs:
+- `percentDamageBuff_any`: Overall damage increase percentage
+- `bossAstralHealOnAttack`: Heal amount on attack
+- `bossAstralSwitcherCDReduce`: Cooldown reduction percentage
+- `bossAstralFatigueStacksReduction`: Fatigue stack reduction
+- `bossAstralParalyseHealReduction`: Heal reduction when paralyzed
+- `bossAstralMaterialAuraDuration`: Material aura duration
+- `bossAstralMaterialAuraReduction`: Material aura reduction
+- `bossAstralAntihealAuraReduction`: Anti-heal aura reduction
+- `bossAstralBonusEnergyOnSwitch`: Bonus energy on switch
+- `bossAstralDamageReductionOnSwitch_5`: Damage reduction on switch
+
+---
+
+### 4. clanRaid_endBossBattle
+
+**Description:** Submits the battle result and progress to the server. Returns damage dealt, quest progress, and rewards.
+
+**Request:**
+
+```json
+{
+ "calls": [
+ {
+ "name": "clanRaid_endBossBattle",
+ "args": {
+ "result": {
+ "win": false,
+ "stars": 0
+ },
+ "progress": [
+ {
+ "v": 273,
+ "b": 0,
+ "seed": -1416505026,
+ "attackers": {
+ "input": ["auto", 0, 0, "auto", 0, 0],
+ "heroes": {
+ "6005": {
+ "hp": -1,
+ "energy": 233,
+ "isDead": false
+ }
+ }
+ },
+ "defenders": {
+ "input": [],
+ "heroes": {
+ "1": {
+ "hp": 155333473,
+ "energy": 1000,
+ "isDead": false,
+ "extra": {
+ "damageTaken": 6693500,
+ "damageTakenNextLevel": 0
+ }
+ },
+ "2": {
+ "hp": 860079174,
+ "energy": 0,
+ "isDead": false
+ }
+ }
+ }
+ }
+ ]
+ },
+ "context": {
+ "actionTs": 383180
+ },
+ "ident": "group_1_body"
+ }
+ ]
+}
+```
+
+**Request Parameters:**
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `result.win` | Boolean | Whether the battle was won |
+| `result.stars` | Number | Stars earned (0-3) |
+| `progress` | Array | Array of battle progress snapshots |
+| `progress[].v` | Number | Server version |
+| `progress[].b` | Number | Battle index |
+| `progress[].seed` | Number | Battle seed (must match start battle seed) |
+| `progress[].attackers.input` | Array | Player input actions during battle |
+| `progress[].attackers.heroes` | Object | Final state of player heroes/pet (keyed by ID) |
+| `progress[].defenders.heroes` | Object | Final state of boss phases (keyed by phase number) |
+| `progress[].defenders.heroes[].extra.damageTaken` | Number | Total damage dealt to this boss phase |
+| `progress[].defenders.heroes[].extra.damageTakenNextLevel` | Number | Damage carried to next phase (if applicable) |
+
+**Response:**
+
+```json
+{
+ "date": 1762544008.237371,
+ "results": [
+ {
+ "ident": "group_1_body",
+ "result": {
+ "response": {
+ "damage": {
+ "1": 6693500,
+ "2": 0
+ },
+ "states": {
+ "id": 2025,
+ "xp": 0,
+ "level": 130,
+ "color": 18,
+ "slots": [],
+ "skills": {
+ "3052": 130,
+ "3053": 130,
+ "3054": 130,
+ "3055": 130,
+ "3056": 130
+ },
+ "power": 22955853,
+ "star": 6,
+ "runes": [0, 0, 0, 0, 0],
+ "skins": [],
+ "currentSkin": 0,
+ "titanGiftLevel": 0,
+ "titanCoinsSpent": null,
+ "artifacts": null,
+ "scale": "1.5",
+ "petId": 0,
+ "type": "hero",
+ "perks": null,
+ "ascensions": [],
+ "agility": 8725.85,
+ "armor": 79212.11,
+ "armorPenetration": 42748.41,
+ "hp": 447630109.82,
+ "intelligence": 39434.28,
+ "magicPenetration": 41724.51,
+ "magicPower": 218797.01,
+ "magicResist": 8689.38,
+ "physicalAttack": 296434.05,
+ "strength": 8725.85,
+ "skin": 0,
+ "favorPetId": 0,
+ "favorPower": 0,
+ "mainStat": "intelligence",
+ "stats": {
+ "additionalPower": 0,
+ "agility": 8725.8505347018272,
+ "anticrit": 0,
+ "antidodge": 0,
+ "armor": 79212.106718626412,
+ "armorPenetration": 42748.409808695746,
+ "dodge": 0,
+ "hp": 447630109.8180936,
+ "intelligence": 39434.278718236761,
+ "lifesteal": 0,
+ "magicPenetration": 41724.510717210644,
+ "magicPower": 218797.01134175769,
+ "magicResist": 8689.3840861536009,
+ "physicalAttack": 296434.0513363143,
+ "physicalCritChance": 0,
+ "strength": 8725.8505347018272
+ },
+ "state": {
+ "hp": 155333473,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 447979143
+ }
+ },
+ "result": {
+ "win": false,
+ "stars": 0,
+ "serverVersion": 273,
+ "damage": {
+ "1": 6693500,
+ "2": 0
+ },
+ "raidId": "2",
+ "level": "150"
+ },
+ "replay": "1762544000125308606",
+ "quests": [
+ {
+ "id": 20000120,
+ "state": 2,
+ "progress": 56539621,
+ "reward": {
+ "clanQuestsPoints": 6,
+ "prestige": 30
+ },
+ "createTime": 1762489829
+ }
+ // ... additional quests ...
+ ]
+ }
+ }
+ }
+ ]
+}
+```
+
+**Response Fields:**
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `damage` | Object | Damage dealt per boss phase (keyed by phase number "1", "2", etc.) |
+| `states` | Object | Updated boss state after battle |
+| `result.win` | Boolean | Whether battle was won |
+| `result.stars` | Number | Stars earned |
+| `result.serverVersion` | Number | Server version that processed the battle |
+| `result.damage` | Object | Confirmed damage per phase |
+| `result.raidId` | String | Raid ID |
+| `result.level` | String | Boss level |
+| `replay` | String | Replay identifier for battle replay |
+| `quests` | Array | Updated quest progress with rewards |
+
+**Quest Progress Structure:**
+
+Each quest in the `quests` array contains:
+- `id`: Quest ID
+- `state`: Quest state (1 = in progress, 2 = completed)
+- `progress`: Current progress value
+- `reward`: Reward object with `clanQuestsPoints` and `prestige`
+- `createTime`: Quest creation timestamp
+
+---
+
+## Example Usage Flow
+
+### 1. Check Who's Fighting
+
+```javascript
+// Check if other clan members are fighting
+POST https://heroes-wb.nextersglobal.com/api/
+{
+ "calls": [{
+ "name": "clanRaid_usersInBossBattle",
+ "args": {},
+ "context": {"actionTs": Date.now()},
+ "ident": "body"
+ }]
+}
+```
+
+### 2. Start Boss Battle
+
+```javascript
+// Initiate raid boss battle
+POST https://heroes-wb.nextersglobal.com/api/
+{
+ "calls": [{
+ "name": "clanRaid_startBossBattle",
+ "args": {
+ "heroes": [46, 52, 48, 40, 37],
+ "pet": 6005,
+ "favor": {
+ "37": 6000,
+ "40": 6004,
+ "46": 6001,
+ "48": 6005,
+ "52": 6006
+ }
+ },
+ "context": {"actionTs": Date.now()},
+ "ident": "body"
+ }]
+}
+```
+
+### 3. Submit Battle Result
+
+```javascript
+// Submit battle outcome and progress
+POST https://heroes-wb.nextersglobal.com/api/
+{
+ "calls": [{
+ "name": "clanRaid_endBossBattle",
+ "args": {
+ "result": {"win": false, "stars": 0},
+ "progress": [{
+ "v": 273,
+ "b": 0,
+ "seed": -1416505026,
+ "attackers": {
+ "input": ["auto", 0, 0, "auto", 0, 0],
+ "heroes": {
+ "6005": {"hp": -1, "energy": 233, "isDead": false}
+ }
+ },
+ "defenders": {
+ "input": [],
+ "heroes": {
+ "1": {
+ "hp": 155333473,
+ "energy": 1000,
+ "isDead": false,
+ "extra": {
+ "damageTaken": 6693500,
+ "damageTakenNextLevel": 0
+ }
+ },
+ "2": {
+ "hp": 860079174,
+ "energy": 0,
+ "isDead": false
+ }
+ }
+ }
+ }]
+ },
+ "context": {"actionTs": Date.now()},
+ "ident": "group_1_body"
+ }]
+}
+```
+
+---
+
+## Important Notes
+
+### Boss Phases
+
+- Bosses can have multiple phases (indicated by keys "1", "2", etc. in `defenders`)
+- Each phase has separate HP pools and stats
+- Damage is tracked per phase in the `damage` object
+- The `extra.damageTaken` field in progress indicates damage dealt to that specific phase
+
+### Battle Seed
+
+- The `seed` value from `clanRaid_startBossBattle` must be used in `clanRaid_endBossBattle` progress
+- The seed ensures battle simulation consistency between client and server
+
+### Damage Calculation
+
+- Damage is calculated per boss phase
+- Total damage is the sum of all phase damages
+- The `damage` object in the response confirms the damage accepted by the server
+
+### Quest Progress
+
+- Quests are automatically updated based on damage dealt
+- Quest rewards include `clanQuestsPoints` and `prestige`
+- Multiple quests can be completed in a single battle
+
+### Battle Effects
+
+- Various buffs from clan raid shop purchases are applied automatically
+- Effects are listed in `battle.effects.attackers`
+- These effects modify hero performance during battle
+
+### Error Handling
+
+- If battle seed doesn't match, server will reject the result
+- Invalid hero/pet combinations will cause the request to fail
+- Boss must be available (not being fought by another player) to start battle
+
+---
+
+## Data Models
+
+### Boss Hero Object
+
+```typescript
+{
+ id: number; // Boss ID (e.g., 2025)
+ level: number; // Boss level (e.g., 130)
+ hp: number; // Current/max HP (very large numbers)
+ state: {
+ hp: number; // Current HP
+ energy: number; // Current energy
+ isDead: boolean; // Whether boss is dead
+ maxHp: number; // Maximum HP
+ };
+ stats: {
+ agility: number;
+ armor: number;
+ armorPenetration: number;
+ hp: number;
+ intelligence: number;
+ magicPenetration: number;
+ magicPower: number;
+ magicResist: number;
+ physicalAttack: number;
+ strength: number;
+ // ... other stats
+ };
+ skills: {
+ [skillId: string]: number; // Skill ID -> level
+ };
+ scale: string; // Boss size multiplier (e.g., "1.5")
+}
+```
+
+### Battle Progress Snapshot
+
+```typescript
+{
+ v: number; // Server version
+ b: number; // Battle index
+ seed: number; // Battle seed (must match start battle)
+ attackers: {
+ input: Array; // Player input actions
+ heroes: {
+ [heroId: string]: {
+ hp: number;
+ energy: number;
+ isDead: boolean;
+ };
+ };
+ };
+ defenders: {
+ input: Array; // Boss input actions (usually empty)
+ heroes: {
+ [phaseNumber: string]: {
+ hp: number;
+ energy: number;
+ isDead: boolean;
+ extra?: {
+ damageTaken: number;
+ damageTakenNextLevel: number;
+ };
+ };
+ };
+ };
+}
+```
+
+---
+
+## Source
+
+This documentation is based on actual network traffic captured in `Boss2.har` on November 7, 2025, during guild raid boss battles.
+
diff --git a/HERO_DATA_DOCUMENTATION.md b/HERO_DATA_DOCUMENTATION.md
new file mode 100644
index 0000000..7220b84
--- /dev/null
+++ b/HERO_DATA_DOCUMENTATION.md
@@ -0,0 +1,449 @@
+# Hero Data Documentation
+
+This document describes the structure and properties of Hero objects used in Hero Wars Helper extensions.
+
+## Overview
+
+Heroes are the main playable characters in Hero Wars. Each hero has unique stats, abilities, artifacts, and belongs to different roles and character types. Heroes can be upgraded through stars (1-6), colors (1-18), and have various battle statistics.
+
+## Data Structure
+
+### Root Object
+The hero data is stored as a JSON object where each key is the hero's ID (as a string), and the value is a Hero object.
+
+```json
+{
+ "1": { /* Hero object */ },
+ "2": { /* Hero object */ },
+ ...
+}
+```
+
+## Hero Object Properties
+
+### Core Properties
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `id` | `number` | Unique identifier for the hero (typically 1-100+) |
+| `type` | `string` | Hero type: `"hero"` (playable) or `"creep"` (enemy/monster) |
+| `mainStat` | `string` | Primary stat: `"strength"`, `"agility"`, or `"intelligence"` |
+| `battleOrder` | `number` | Battle position order (0-100+) |
+| `scale` | `number` or `null` | Scale factor for display (e.g., 1.5) |
+| `role` | `string` or `null` | Battle position: `"front"`, `"middle"`, or `"back"` |
+| `roleExtended` | `string[]` or `null` | Extended role classifications (e.g., `["melee_tank"]`, `["ranged_dps"]`) |
+| `characterType` | `string` or `null` | Character classification: `"warrior"`, `"demon"`, `"snob"`, `"healer"`, `"cutie"` |
+| `asset` | `string` | Asset identifier for the hero model (e.g., `"hero01_aurora"`, `"demon"`) |
+| `iconAssetAtlas` | `number` | Icon atlas ID for UI display |
+| `iconAssetTexture` | `string` | Icon texture identifier (e.g., `"0001"`, `"0004"`) |
+| `obtainType` | `string` or `null` | How to obtain the hero (JSON string or null) |
+| `silhouette` | `string` or `null` | Silhouette type (e.g., `"flying"`) |
+| `perk` | `number[]` or `null` | Array of perk IDs that define special abilities |
+| `artifacts` | `number[]` or `null` | Array of artifact IDs that can be equipped |
+| `runes` | `number[]` or `null` | Array of rune IDs |
+| `skill` | `object` or `null` | Object mapping skill slots to skill IDs (e.g., `{"0": 1, "1": 2, "2": 3, "3": 4, "4": 5}`) |
+| `fragmentBuyCost` | `object` or `null` | Cost to buy hero fragments (e.g., `{"starmoney": 40}`) |
+| `fragmentSellCost` | `object` or `null` | Gold received when selling fragments (e.g., `{"gold": 4000}`) |
+| `fragmentSpecialCost` | `number` | Special currency cost for fragments (typically 100) |
+| `lockedUntil` | `number` or `null` | Timestamp or level requirement to unlock |
+| `ultCinematic` | `object` or `null` | Ultimate ability cinematic data (e.g., `{"ident": "hero01_battle_animation"}`) |
+| `epicArtAsset` | `object` or `null` | Epic art asset configuration |
+| `spineEpicArtAsset` | `object` or `null` | Spine epic art asset (e.g., `{"name": "Aurora"}`) |
+| `sfxAsset` | `string` or `null` | Sound effects asset identifier (e.g., `"hero01_sfx"`) |
+| `musicAsset` | `object` or `null` | Music asset configuration |
+| `assetsIdent` | `string` or `null` | Additional asset identifier |
+
+### Base Stats Object
+
+Each hero has a `baseStats` object containing initial stat values:
+
+```json
+"baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 55,
+ "strength": 20
+}
+```
+
+**Base Stats Properties:**
+- `agility`: Agility stat value
+- `hp`: Health points
+- `intelligence`: Intelligence stat value
+- `physicalAttack`: Physical attack damage
+- `strength`: Strength stat value
+
+### Stars Object
+
+Each hero has a `stars` object containing battle statistics for each star level (1-6). Regular heroes have stars 1-6, while some special heroes may start at higher star levels.
+
+```json
+"stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "2": { /* ... */ },
+ ...
+}
+```
+
+**Star Level Properties:**
+- `battleStatData.agility`: Agility bonus
+- `battleStatData.armor`: Physical armor
+- `battleStatData.armorPenetration`: Physical armor penetration
+- `battleStatData.dodge`: Dodge chance
+- `battleStatData.hp`: Health points bonus
+- `battleStatData.intelligence`: Intelligence bonus
+- `battleStatData.lifesteal`: Lifesteal percentage
+- `battleStatData.magicPenetration`: Magic penetration
+- `battleStatData.magicPower`: Magic power
+- `battleStatData.magicResist`: Magic resistance
+- `battleStatData.physicalAttack`: Physical attack bonus
+- `battleStatData.physicalCritChance`: Physical critical hit chance
+- `battleStatData.strength`: Strength bonus
+
+### Color Object
+
+Each hero has a `color` object containing upgrade progression data for each color level (1-18). Color 1 typically only has `items`, while colors 2-18 include both `battleStatData` and `items`.
+
+```json
+"color": {
+ "1": {
+ "items": [1, 7, 6, 2, 13, 4]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 585,
+ "intelligence": 12,
+ "magicPower": 25,
+ "physicalAttack": 12,
+ "strength": 7
+ },
+ "items": [6, 7, 10, 18, 25, 27]
+ },
+ ...
+}
+```
+
+**Color Level Properties:**
+- `items`: Array of item IDs required for this color upgrade
+- `battleStatData`: Battle stat bonuses for this color level (colors 2-18)
+
+## Hero Types
+
+### Playable Heroes (`type: "hero"`)
+Main playable characters that can be collected, upgraded, and used in battles. These heroes have:
+- Full star progression (1-6 stars)
+- Color progression (1-18 colors)
+- Artifacts and skills
+- Role and character type classifications
+
+### Creeps (`type: "creep"`)
+Enemy/monster units used in battles. These typically have:
+- Limited or no star progression
+- Simplified stat structures
+- May have `scale` property for display
+- Often have `null` values for many properties
+
+## Main Stats
+
+Heroes have three primary stat types that determine their scaling:
+
+### Strength (`mainStat: "strength"`)
+- Typically used by tanks and melee fighters
+- Increases HP and physical attack
+- Examples: Aurora (ID 1), Astaroth (ID 4)
+
+### Agility (`mainStat: "agility"`)
+- Typically used by physical damage dealers
+- Increases physical attack and dodge
+- Examples: Galahad (ID 2), Keira (ID 5)
+
+### Intelligence (`mainStat: "intelligence"`)
+- Typically used by mages and healers
+- Increases magic power and magic resistance
+- Examples: Thea (ID 3), Celeste (ID 6)
+
+## Battle Roles
+
+### Front (`role: "front"`)
+Front-line heroes that tank damage and protect the team. Examples: Aurora (ID 1), Astaroth (ID 4)
+
+### Middle (`role: "middle"`)
+Mid-line heroes that deal damage or provide support. Examples: Galahad (ID 2), Keira (ID 5)
+
+### Back (`role: "back"`)
+Back-line heroes that provide healing, support, or ranged damage. Examples: Thea (ID 3), Celeste (ID 6)
+
+## Role Extended Classifications
+
+Extended roles provide more specific classifications:
+
+- `"melee_tank"` - Front-line tank heroes
+- `"ranged_dps"` - Ranged damage dealers
+- `"support"` - Support/healing heroes
+- `"mage"` - Magic-based heroes
+- `"healer"` - Healing-focused heroes
+- `"dps"` - Damage per second heroes
+
+## Character Types
+
+- `"warrior"` - Warrior-class heroes
+- `"demon"` - Demon-class heroes
+- `"snob"` - Snob-class heroes
+- `"healer"` - Healer-class heroes
+- `"cutie"` - Cutie-class heroes
+
+## Usage in Code
+
+### Accessing Hero Data
+
+```javascript
+// Assuming heroData is the loaded JSON object
+const aurora = heroData["1"];
+console.log(aurora.role); // "front"
+console.log(aurora.mainStat); // "strength"
+console.log(aurora.stars["1"].battleStatData.hp); // 0
+```
+
+### Filtering by Type
+
+```javascript
+// Get all playable heroes
+const playableHeroes = Object.values(heroData).filter(h => h.type === "hero");
+
+// Get all creeps
+const creeps = Object.values(heroData).filter(h => h.type === "creep");
+```
+
+### Filtering by Main Stat
+
+```javascript
+// Get all strength-based heroes
+const strengthHeroes = Object.values(heroData).filter(h => h.mainStat === "strength");
+
+// Get all agility-based heroes
+const agilityHeroes = Object.values(heroData).filter(h => h.mainStat === "agility");
+
+// Get all intelligence-based heroes
+const intelligenceHeroes = Object.values(heroData).filter(h => h.mainStat === "intelligence");
+```
+
+### Filtering by Role
+
+```javascript
+// Get all front-line heroes
+const frontHeroes = Object.values(heroData).filter(h => h.role === "front");
+
+// Get all back-line heroes
+const backHeroes = Object.values(heroData).filter(h => h.role === "back");
+```
+
+### Getting Star Level Stats
+
+```javascript
+function getHeroStats(heroId, starLevel) {
+ const hero = heroData[heroId.toString()];
+ if (!hero || !hero.stars[starLevel]) {
+ return null;
+ }
+ return hero.stars[starLevel].battleStatData;
+}
+
+// Example: Get Aurora's stats at 3 stars
+const auroraStats = getHeroStats(1, "3");
+// Returns battle stat data for star level 3
+```
+
+### Getting Color Level Stats
+
+```javascript
+function getHeroColorStats(heroId, colorLevel) {
+ const hero = heroData[heroId.toString()];
+ if (!hero || !hero.color[colorLevel]) {
+ return null;
+ }
+ return hero.color[colorLevel].battleStatData || {};
+}
+
+// Example: Get Aurora's color 5 stats
+const auroraColor5 = getHeroColorStats(1, "5");
+// Returns battle stat data for color level 5
+```
+
+### Getting Hero Artifacts
+
+```javascript
+function getHeroArtifacts(heroId) {
+ const hero = heroData[heroId.toString()];
+ return hero?.artifacts || [];
+}
+
+// Example: Get Aurora's artifacts
+const auroraArtifacts = getHeroArtifacts(1);
+// Returns: [1001, 2002, 3002]
+```
+
+### Getting Hero Skills
+
+```javascript
+function getHeroSkills(heroId) {
+ const hero = heroData[heroId.toString()];
+ if (!hero || !hero.skill) {
+ return null;
+ }
+ return Object.values(hero.skill);
+}
+
+// Example: Get Aurora's skill IDs
+const auroraSkills = getHeroSkills(1);
+// Returns array of skill IDs
+```
+
+## Notes
+
+1. **HP and Attack Values**: Battle stat values can be numbers or strings. Always parse when doing calculations.
+
+2. **Star Levels**: Regular heroes have stars 1-6. Some special heroes may start at higher star levels.
+
+3. **Color Levels**: Heroes have color progression from 1-18. Color 1 only has items, while colors 2-18 include stat bonuses.
+
+4. **Artifacts**: Heroes can have up to 3 artifacts. The `artifacts` array contains artifact IDs that correspond to artifact data.
+
+5. **Skills**: Heroes have multiple skills mapped by slot number (0-4 typically, with some having slot 7-8 for special skills).
+
+6. **Fragment Costs**: Heroes can be obtained through fragments. `fragmentBuyCost` shows the cost to buy fragments, while `fragmentSellCost` shows the gold received when selling fragments.
+
+7. **Role Positioning**:
+ - `"front"` - Front line (tanks)
+ - `"middle"` - Mid line (DPS/support)
+ - `"back"` - Back line (support/DPS/healers)
+
+8. **Hero IDs**: Playable heroes typically have IDs in the range 1-100+. Creeps and special units may have higher IDs.
+
+9. **Null Values**: Many properties can be `null` for certain hero types (especially creeps). Always check for null before accessing nested properties.
+
+10. **Type Filtering**: Always filter by `type === "hero"` when working with playable heroes, as the data file may contain creeps and other non-playable units.
+
+## Hero ID Reference
+
+The following table provides a complete reference for all playable Hero IDs (type: "hero") found in the hero data file. Hero names are extracted from the `spineEpicArtAsset.name` field in the hero data. These names can also be accessed via translation keys `LIB_HERO_NAME_{id}` in the game.
+
+| ID | Hero Name | Asset Name | Role | Main Stat | Character Type |
+|----|-----------|------------|------|----------|----------------|
+| 1 | Aurora | hero01_aurora | front | strength | warrior |
+| 2 | 02_galahad | hero02_galahad | front | strength | warrior |
+| 3 | 03_keira | hero3_keira | middle | agility | warrior |
+| 4 | Astaroth | demon | front | strength | demon |
+| 5 | 05_Kai | mage | middle | intelligence | snob |
+| 6 | thing | thing | back | intelligence | demon |
+| 7 | sunsupport | sunsupport | back | intelligence | healer |
+| 8 | 08_daredevil | daredevil | back | agility | cutie |
+| 9 | 09_heidi | hero09_heidi | middle | intelligence | warrior |
+| 10 | 10_Faceless | spell_stealer | back | intelligence | demon |
+| 11 | glutton | glutton | front | strength | snob |
+| 12 | 12_arachne | arachne | middle | agility | snob |
+| 13 | 13_Orion | elemental | back | intelligence | snob |
+| 14 | hero14_fox | hero14_fox | back | agility | cutie |
+| 15 | 15_Ginger | pirate | back | agility | snob |
+| 16 | 16_Dante | hero16_dante | middle | agility | snob |
+| 17 | 17_Mojo | shaman | middle | intelligence | snob |
+| 18 | hero18_judge | hero18_judge | middle | intelligence | - |
+| 19 | 19_DarkStar | archer | back | agility | warrior |
+| 20 | Artemis | arbalester | back | agility | warrior |
+| 21 | paladin | paladin | front | intelligence | healer |
+| 22 | jester | jester | back | intelligence | cutie |
+| 23 | 23_Lian | tailed | back | intelligence | cutie |
+| 24 | 24_cleaver | butcher | front | strength | warrior |
+| 25 | 25_Ishmael | hero25 | front | agility | demon |
+| 26 | hero26_lilith | hero26_lilith | back | intelligence | demon |
+| 27 | hero27_paladin_warrior | hero27_paladin_warrior | front | strength | snob |
+| 28 | QuingMao | hero28_asian_girl | front | agility | warrior |
+| 29 | hero29_vampire | hero29_vampire | back | intelligence | demon |
+| 30 | hero30_antimage | hero30_antimage | back | intelligence | snob |
+| 31 | 31_jet | hero31_alchemist | back | intelligence | cutie |
+| 32 | helios | hero32_sun | back | intelligence | snob |
+| 33 | 33_lars | hero33_deerboy | back | intelligence | snob |
+| 34 | 34_Krista | hero34_deergirl | middle | intelligence | cutie |
+| 35 | hero35_catooldan | hero35_catooldan | middle | strength | demon |
+| 36 | 36_Maya | hero36_flowey | middle | intelligence | healer |
+| 37 | 37_jhu | hero37_boomerang | middle | strength | warrior |
+| 38 | hero38_sandphantom | hero38_sandphantom | front | agility | warrior |
+| 39 | 39_Ziri | hero39_scorpio | front | strength | cutie |
+| 40 | 40_Nebula | hero40_space_balls | middle | agility | cutie |
+| 41 | hero41_tentacle | hero41_tentacle | front | agility | warrior |
+| 42 | hero42_fatty | hero42_fatty | front | strength | cutie |
+| 43 | hero43_daynight | hero43_daynight | middle | intelligence | - |
+| 44 | 44_Astrid_lukas | hero44_petmaster | back | agility | - |
+| 45 | 45_Satori | hero45_blackfox | front | intelligence | - |
+| 46 | hero46_grandma | hero46_grandma | back | intelligence | - |
+| 47 | 47_Andvari | hero47_andvari | front | strength | - |
+| 48 | 48_Sebastian | hero48_sebastian | middle | agility | - |
+| 49 | Yasmin | hero49_naga | front | agility | - |
+| 50 | 50_corvus | hero50_corvus | front | strength | - |
+| 51 | 51_Morrigan | hero51_morrigan | middle | intelligence | - |
+| 52 | hero52_isaac | hero52_isaac | middle | agility | - |
+| 53 | hero53_alvanor | hero53_alvanor | front | intelligence | snob |
+| 54 | hero54_tristan | hero54_tristan | front | strength | - |
+| 55 | 55_iris | hero55_iris | back | intelligence | - |
+| 56 | 56_amira | hero56_amira | middle | intelligence | - |
+| 57 | 57_fafnir | hero57_fafnir | back | strength | - |
+| 58 | 58_aidan | hero58_aidan | back | intelligence | - |
+| 59 | 59_kayla | hero59_keila | front | agility | - |
+| 60 | 60_mushroom | hero60_mushroom | front | strength | - |
+| 61 | 61_julius | hero61_julius | front | strength | - |
+| 62 | 62_polaris | hero62_polaris | back | intelligence | - |
+| 63 | 63_laracroft_epic | hero63_laracroft | back | agility | cutie |
+| 64 | 64_augustus | hero64_augustus | back | intelligence | snob |
+| 65 | 65_tmnt | hero65_tmnt | front | agility | warrior |
+| 66 | 66_folio | hero66_folio | back | intelligence | - |
+| 67 | 67_lyria | hero67_lyria | front | strength | warrior |
+| 68 | 68_gus | hero_68_gus | middle | strength | healer |
+| 69 | 69_cascade | hero_69_cascade | middle | intelligence | - |
+| 70 | 70_Necro | hero_70_electra | front | strength | - |
+| 71 | Fluffy | — | — | — | — |
+| 72 | Byrna | — | — | — | — |
+| 73 | Adam | — | — | — | — |
+| 74 | Somna | — | — | — | — |
+
+**Total Playable Heroes:** 70 in `heroData.txt`; IDs **71+** (e.g. Fluffy, Byrna) appear in live game data — use translation keys for current names.
+
+**Note:** Hero names in the game are accessed using translation keys. To get a hero's display name in code:
+```javascript
+const heroId = 72;
+const heroName = window.cheats?.translate(`LIB_HERO_NAME_${heroId}`);
+```
+
+For example:
+- `cheats.translate("LIB_HERO_NAME_1")` returns "Aurora"
+- `cheats.translate("LIB_HERO_NAME_4")` returns "Astaroth"
+- `cheats.translate("LIB_HERO_NAME_13")` returns "Orion"
+- `cheats.translate("LIB_HERO_NAME_55")` returns "Iris"
+- `cheats.translate("LIB_HERO_NAME_72")` returns "Byrna"
+- `cheats.translate("LIB_HERO_NAME_73")` returns "Adam"
+- `cheats.translate("LIB_HERO_NAME_74")` returns "Somna"
+
+## Related Files
+
+- `heroData.txt` - The source JSON data file containing all hero definitions
+- `HeroWarsHelper.user.js` - Uses hero data for quest automation and team management
+- `HeroWarsHelper - Auto Daily Extension.user.js` - Uses hero data for daily quest automation
+- `LIB_DATA_DOCUMENTATION.md` - Documentation for the lib.data structure used in-game
+- `HERO_WARS_API_DOCUMENTATION.md` - Contains additional hero name reference table
+
diff --git a/HERO_LIST.md b/HERO_LIST.md
new file mode 100644
index 0000000..63d016b
--- /dev/null
+++ b/HERO_LIST.md
@@ -0,0 +1,217 @@
+# Hero List
+
+This document contains a complete list of all heroes defined in the hero data object file.
+
+## Summary
+
+- **Total Heroes**: 74+ (IDs 1–70 in `heroData.txt`; 71+ such as Fluffy, Byrna verified via `LIB_HERO_NAME_{id}` in-game)
+- **Corrupted Heroes**: 4 (IDs 7002, 7013, 7015, 7024)
+
+## Hero List
+
+| ID | Asset Name | Main Stat | Role | Character Type |
+|----|------------|-----------|------|----------------|
+| 1 | hero01_aurora | strength | front | warrior |
+| 2 | hero02_galahad | strength | front | warrior |
+| 3 | hero3_keira | agility | middle | warrior |
+| 4 | demon | strength | front | demon |
+| 5 | mage | intelligence | middle | snob |
+| 6 | thing | intelligence | back | demon |
+| 7 | sunsupport | intelligence | back | healer |
+| 8 | daredevil | agility | back | cutie |
+| 9 | hero09_heidi | intelligence | middle | warrior |
+| 10 | spell_stealer | intelligence | back | demon |
+| 11 | glutton | strength | front | snob |
+| 12 | arachne | agility | middle | snob |
+| 13 | elemental | intelligence | back | snob |
+| 14 | hero14_fox | agility | back | cutie |
+| 15 | pirate | agility | back | snob |
+| 16 | hero16_dante | agility | middle | snob |
+| 17 | shaman | intelligence | middle | snob |
+| 18 | hero18_judge | intelligence | middle | - |
+| 19 | archer | agility | back | warrior |
+| 20 | arbalester | agility | back | warrior |
+| 21 | paladin | intelligence | front | healer |
+| 22 | jester | intelligence | back | cutie |
+| 23 | tailed | intelligence | back | cutie |
+| 24 | butcher | strength | front | warrior |
+| 25 | hero25 | agility | front | demon |
+| 26 | hero26_lilith | intelligence | back | demon |
+| 27 | hero27_paladin_warrior | strength | front | snob |
+| 28 | hero28_asian_girl | agility | front | warrior |
+| 29 | hero29_vampire | intelligence | back | demon |
+| 30 | hero30_antimage | intelligence | back | snob |
+| 31 | hero31_alchemist | intelligence | back | cutie |
+| 32 | hero32_sun | intelligence | back | snob |
+| 33 | hero33_deerboy | intelligence | back | snob |
+| 34 | hero34_deergirl | intelligence | middle | cutie |
+| 35 | hero35_catooldan | strength | middle | demon |
+| 36 | hero36_flowey | intelligence | middle | healer |
+| 37 | hero37_boomerang | strength | middle | warrior |
+| 38 | hero38_sandphantom | agility | front | warrior |
+| 39 | hero39_scorpio | strength | front | cutie |
+| 40 | hero40_space_balls | agility | middle | cutie |
+| 41 | hero41_tentacle | agility | front | warrior |
+| 42 | hero42_fatty | strength | front | cutie |
+| 43 | hero43_daynight | intelligence | middle | - |
+| 44 | hero44_petmaster | agility | back | - |
+| 45 | hero45_blackfox | intelligence | front | - |
+| 46 | hero46_grandma | intelligence | back | - |
+| 47 | hero47_andvari | strength | front | - |
+| 48 | hero48_sebastian | agility | middle | - |
+| 49 | hero49_naga | agility | front | - |
+| 50 | hero50_corvus | strength | front | - |
+| 51 | hero51_morrigan | intelligence | middle | - |
+| 52 | hero52_isaac | agility | middle | - |
+| 53 | hero53_alvanor | intelligence | front | snob |
+| 54 | hero54_tristan | strength | front | - |
+| 55 | hero55_iris | intelligence | back | - |
+| 56 | hero56_amira | intelligence | middle | - |
+| 57 | hero57_fafnir | strength | back | - |
+| 58 | hero58_aidan | intelligence | back | - |
+| 59 | hero59_keila | agility | front | - |
+| 60 | hero60_mushroom | strength | front | - |
+| 61 | hero61_julius | strength | front | - |
+| 62 | hero62_polaris | intelligence | back | - |
+| 63 | hero63_laracroft | agility | back | cutie |
+| 64 | hero64_augustus | intelligence | back | snob |
+| 65 | hero65_tmnt | agility | front | warrior |
+| 66 | hero66_folio | intelligence | back | - |
+| 67 | hero67_lyria | strength | front | warrior |
+| 68 | hero_68_gus | strength | middle | healer |
+| 69 | hero_69_cascade | intelligence | middle | - |
+| 70 | hero_70_electra | strength | front | - |
+| 71 | — | — | — | — |
+| 72 | — | — | — | — |
+| 73 | — | — | — | — |
+| 74 | — | — | — | — |
+| 7002 | hero7002_corrupted_galahad | strength | front | warrior |
+| 7013 | hero7013_corrupted_orion | intelligence | back | snob |
+| 7015 | hero7015_corrupted_ginger | agility | back | snob |
+| 7024 | hero7024_corrupted_cleaver | strength | front | warrior |
+
+**IDs 71–74+:** Not in `heroData.txt`. Display names from in-game translations: 71 = Fluffy, 72 = Byrna, 73 = Adam, 74 = Somna.
+
+## Hero Categories
+
+### By Main Stat
+
+#### Strength Heroes (24)
+- Front-line tanks and warriors
+- IDs: 1, 2, 4, 11, 24, 27, 35, 37, 39, 42, 47, 50, 54, 57, 60, 61, 67, 68, 70, 7002, 7024
+
+#### Agility Heroes (20)
+- Fast attackers and middle/back line damage dealers
+- IDs: 3, 8, 12, 14, 15, 16, 19, 20, 28, 38, 40, 41, 44, 48, 49, 52, 59, 63, 65
+
+#### Intelligence Heroes (30)
+- Mages, healers, and support heroes
+- IDs: 5, 6, 7, 9, 10, 13, 17, 18, 21, 22, 23, 26, 29, 30, 31, 32, 33, 34, 36, 43, 45, 46, 51, 53, 55, 56, 58, 62, 64, 66, 69, 7013, 7015
+
+### By Role
+
+#### Front Line (28)
+- Tanks and front-line fighters
+- IDs: 1, 2, 4, 11, 21, 24, 25, 27, 28, 35, 38, 39, 41, 42, 45, 47, 50, 53, 54, 59, 60, 61, 65, 67, 70, 7002, 7024
+
+#### Middle Line (15)
+- Mid-line damage dealers and support
+- IDs: 3, 5, 9, 16, 17, 18, 34, 35, 37, 40, 43, 48, 51, 52, 56, 68, 69
+
+#### Back Line (31)
+- Ranged attackers, healers, and support
+- IDs: 6, 7, 8, 10, 13, 14, 15, 19, 20, 22, 23, 26, 29, 30, 31, 32, 33, 44, 46, 55, 57, 58, 62, 63, 64, 66, 7013, 7015
+
+### By Character Type
+
+#### Warrior (15)
+- IDs: 1, 2, 3, 9, 19, 20, 24, 28, 37, 38, 41, 65, 67, 7002, 7024
+
+#### Demon (7)
+- IDs: 4, 6, 10, 25, 26, 29, 35
+
+#### Snob (12)
+- IDs: 5, 11, 12, 13, 15, 16, 17, 27, 30, 32, 33, 53, 64, 7013, 7015
+
+#### Cutie (10)
+- IDs: 8, 14, 22, 23, 31, 34, 39, 40, 42, 63
+
+#### Healer (4)
+- IDs: 7, 21, 36, 68
+
+#### Unspecified (26)
+- Heroes without a character type classification
+- IDs: 18, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 66, 69, 70
+
+### Special Categories
+
+#### Corrupted Heroes (4)
+- Special variant heroes
+- IDs: 7002 (Corrupted Galahad), 7013 (Corrupted Orion), 7015 (Corrupted Ginger), 7024 (Corrupted Cleaver)
+
+## Data Structure
+
+The hero data is stored as a JSON object where each key is a hero ID (as a string), and the value is a hero object.
+
+### Core Properties
+
+- **`id`** (number): Unique identifier for the hero
+- **`baseStats`** (object): Base statistics (`agility`, `hp`, `intelligence`, `physicalAttack`, `strength`)
+- **`stars`** (object): Star level data (1-18+) with `battleStatData` and optional `items` arrays
+- **`color`** (object, optional): Color/quality upgrade system with stat bonuses and item requirements
+- **`runes`** (array | null): Array of rune IDs
+- **`artifacts`** (array | null): Array of artifact IDs
+- **`mainStat`** (string): Primary stat - `"strength"`, `"agility"`, or `"intelligence"`
+- **`battleOrder`** (number): Battle priority order
+- **`type`** (string): `"hero"` for playable heroes, `"creep"` for enemies
+- **`asset`** (string): Asset identifier for the hero's model/sprite
+- **`iconAssetAtlas`** (number): Atlas ID for the hero's icon
+- **`iconAssetTexture`** (string): Texture identifier within the atlas
+- **`role`** (string | null): Battle position - `"front"`, `"middle"`, `"back"`, or `null`
+- **`characterType`** (string | null): Classification - `"warrior"`, `"demon"`, `"snob"`, `"cutie"`, `"healer"`, or `null`
+- **`roleExtended`** (array | null): Extended role classifications
+- **`skill`** (object | array | null): Skill configuration mapping slots to skill IDs, or array of skill IDs
+- **`perk`** (array | null): Array of perk IDs
+- **`fragmentBuyCost`** (object | null): Cost to buy hero fragments
+- **`fragmentSellCost`** (object | null): Value when selling hero fragments
+
+### Battle Stat Data
+
+Each star/color level's `battleStatData` contains:
+- `agility`, `armor`, `armorPenetration`, `dodge`, `hp`, `intelligence`
+- `lifesteal`, `magicPenetration`, `magicPower`, `magicResist`
+- `physicalAttack`, `physicalCritChance`, `strength`
+
+### Important Notes
+
+- Many properties can be `null` - always check before accessing nested properties
+- Not all heroes have all properties - use defensive coding
+- `skill` can be an object, array, or null - handle all cases
+- Final hero stats = `baseStats` + star bonuses + color bonuses + equipment bonuses
+- Hero IDs are stored as string keys in the root object but as numbers in the `id` property
+
+## Usage
+
+To access hero data in code:
+
+```javascript
+// Access hero by ID
+const hero = heroData[1]; // Aurora
+const hero = heroData[7002]; // Corrupted Galahad
+
+// Check if hero exists and is a playable hero
+if (heroData[id] && heroData[id].type === 'hero') {
+ const mainStat = heroData[id].mainStat;
+ const role = heroData[id].role;
+ const skills = heroData[id].skill;
+}
+
+// Access star level data
+const star1Stats = heroData[1].stars[1].battleStatData;
+
+// Access color level data (if exists)
+if (heroData[1].color) {
+ const color1Items = heroData[1].color[1].items;
+}
+```
+
diff --git a/HERO_WARS_API_DOCUMENTATION.md b/HERO_WARS_API_DOCUMENTATION.md
new file mode 100644
index 0000000..c10f7bf
--- /dev/null
+++ b/HERO_WARS_API_DOCUMENTATION.md
@@ -0,0 +1,6410 @@
+# Hero Wars API Documentation
+
+## Overview
+
+This document provides comprehensive documentation for all API calls used in the HeroWarsHelper script. The API uses a unified request/response structure where multiple API calls can be batched in a single request.
+
+**Base URL:** `https://heroes-wb.nextersglobal.com/api/`
+
+**Protocol:** HTTPS
+
+**Method:** POST
+
+**Content-Type:** `application/json; charset=UTF-8`
+
+---
+
+## Send Function
+
+The `Send` function is the primary method for making API calls. It accepts either a JSON string or a JavaScript object.
+
+### Function Signature
+
+```javascript
+async function Send(json, pr)
+```
+
+### Parameters
+
+- `json`: Either a JSON string or JavaScript object containing the API call structure
+- `pr`: Optional parameter (unused in current implementation)
+
+### Return Value
+
+Returns a Promise that resolves to the API response object.
+
+---
+
+## Request Structure
+
+All API requests follow this structure:
+
+```json
+{
+ "calls": [
+ {
+ "name": "apiMethodName",
+ "args": {
+ // Method-specific arguments
+ },
+ "context": {
+ "actionTs": 1234567890 // Timestamp in milliseconds (auto-added if missing)
+ },
+ "ident": "body" // Identifier for response mapping
+ }
+ ]
+}
+```
+
+### Request Fields
+
+| Field | Type | Required | Description |
+|-------|------|----------|-------------|
+| `calls` | Array | Yes | Array of API call objects |
+| `name` | String | Yes | API method name (e.g., "userGetInfo", "shopGetAll") |
+| `args` | Object | Yes | Method-specific arguments (can be empty `{}`) |
+| `context` | Object | No | Context information (auto-added if missing) |
+| `context.actionTs` | Number | No | Action timestamp in milliseconds |
+| `ident` | String | Yes | Identifier used to map responses. Use "body" for single calls, or unique identifiers for multiple calls |
+
+### Request Examples
+
+**Single API Call (String Format):**
+```javascript
+const response = await Send('{"calls":[{"name":"userGetInfo","args":{},"ident":"body"}]}');
+```
+
+**Single API Call (Object Format):**
+```javascript
+const response = await Send({ calls: [{ name: 'userGetInfo', args: {}, ident: 'body' }] });
+```
+
+**Multiple API Calls:**
+```javascript
+const response = await Send({
+ calls: [
+ { name: 'userGetInfo', args: {}, ident: 'userGetInfo' },
+ { name: 'inventoryGet', args: {}, ident: 'inventoryGet' },
+ { name: 'shopGetAll', args: {}, ident: 'shopGetAll' }
+ ]
+});
+```
+
+**API Call with Arguments:**
+```javascript
+const response = await Send({
+ calls: [{
+ name: 'consumableUseLootBox',
+ args: {
+ libId: 148,
+ amount: 1
+ },
+ ident: 'body'
+ }]
+});
+```
+
+**API Call with Context:**
+```javascript
+const response = await Send(JSON.stringify({
+ calls: [{
+ name: 'userGetInfo',
+ args: {},
+ context: {
+ actionTs: Date.now()
+ },
+ ident: 'body'
+ }]
+}));
+```
+
+---
+
+## Response Structure
+
+All API responses follow this structure:
+
+```json
+{
+ "results": [
+ {
+ "ident": "body",
+ "result": {
+ "response": {
+ // Method-specific response data
+ },
+ // Additional fields may be present (e.g., "error", "sideEffects", etc.)
+ }
+ }
+ ],
+ "error": null // Present if there was an error
+}
+```
+
+### Response Fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `results` | Array | Array of result objects, one per API call |
+| `results[].ident` | String | Matches the `ident` from the request |
+| `results[].result` | Object | Contains the actual result data |
+| `results[].result.response` | Any | The actual response data (structure varies by API method) |
+| `error` | Object/null | Error object if request failed |
+
+### Response Examples
+
+**Single Call Response:**
+```javascript
+const response = await Send('{"calls":[{"name":"userGetInfo","args":{},"ident":"body"}]}');
+const userInfo = response.results[0].result.response;
+```
+
+**Multiple Calls Response:**
+```javascript
+const response = await Send({
+ calls: [
+ { name: 'userGetInfo', args: {}, ident: 'userGetInfo' },
+ { name: 'inventoryGet', args: {}, ident: 'inventoryGet' }
+ ]
+});
+
+const userInfo = response.results[0].result.response; // First call result
+const inventory = response.results[1].result.response; // Second call result
+```
+
+**Using .map() for Multiple Results:**
+```javascript
+const result = await Send('{"calls":[{"name":"inventoryGet","args":{},"ident":"inventoryGet"},{"name":"shopGetAll","args":{},"ident":"shopGetAll"}]}')
+ .then(e => e.results.map(n => n.result.response));
+
+const inv = result[0];
+const shops = result[1];
+```
+
+**Error Handling:**
+```javascript
+const response = await Send({ calls: [...] });
+
+if (response.error) {
+ console.error('API Error:', response.error);
+ throw new Error(`API error: ${response.error.name} - ${response.error.description}`);
+}
+```
+
+---
+
+## API Methods
+
+### User Information
+
+#### userGetInfo
+
+Get comprehensive user information including stats, resources, arena status, etc.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"userGetInfo","args":{},"ident":"body"}]}')
+```
+
+**Response Structure:**
+```javascript
+{
+ userId: string,
+ name: string,
+ level: string,
+ gold: number,
+ starMoney: number,
+ refillable: [
+ {
+ id: number, // Resource type ID
+ amount: number, // Current amount
+ lastRefill: number,
+ boughtToday: number
+ }
+ ],
+ arenaPlace: number, // Current arena rank
+ grandPlace: number, // Current grand arena rank
+ // ... many more fields
+}
+```
+
+**Refillable Resource IDs:**
+- `id: 1` - Stamina/Energy
+- `id: 6` - **Arena attempts available** (number of remaining arena battle attempts)
+- `id: 21` - **Grand Arena attempts available** (number of remaining grand arena battle attempts)
+- Other IDs represent various game resources
+
+**Accessing Refillable Data:**
+
+To get descriptions and metadata for all refillable resources, access `lib.data.refillable`:
+```javascript
+// Get all refillable resource descriptions
+const refillableData = lib.data.refillable;
+// This object contains metadata for all refillable types including:
+// - id: Resource ID
+// - ident: Identifier string (e.g., 'stamina', 'arena_battle')
+// - refillSeconds: Time in seconds until refill
+// - maxValue: Maximum value array
+// - maxRefillCount: Maximum refill count array
+// - refillByReset: Whether refill resets on daily reset
+// - refillCountResetLocalTime: Local time reset array
+// - serverTimeRefill: Whether server time is used for refill
+```
+
+To get the actual current values of refillable resources, use:
+```javascript
+// Using Caller class (recommended)
+const refillableValues = (await Caller.send('userGetInfo')).refillable;
+// Returns array of objects with:
+// - id: Resource type ID
+// - amount: Current amount/value
+// - lastRefill: Timestamp of last refill
+// - boughtToday: Number purchased today
+
+// Using Send function
+const userInfo = await Send('{"calls":[{"name":"userGetInfo","args":{},"ident":"body"}]}')
+ .then(e => e.results[0].result.response);
+const refillableValues = userInfo.refillable;
+```
+
+**Note:** Arena attempts are stored in the `refillable` array with `id: 6`. Grand Arena attempts are stored with `id: 21`. The `amount` field indicates how many battle attempts are currently available for each respective arena type.
+
+**Important:** Guild War attempts are **NOT** stored in the `refillable` array. Instead, Guild War attempts are tracked separately in the `clanWarGetInfo` API response as `myTries`. See the [Guild War API](#guild-war-api) section for details.
+
+**Console Usage:**
+These commands can be executed directly in the browser console when using the HeroWarsHelper script:
+```javascript
+// Get refillable descriptions/metadata
+lib.data.refillable
+
+// Get current refillable values
+(await Caller.send('userGetInfo')).refillable
+```
+
+**Example Usage:**
+```javascript
+const userInfo = await Send('{"calls":[{"name":"userGetInfo","args":{},"ident":"body"}]}')
+ .then(e => e.results[0].result.response);
+
+console.log(`Gold: ${userInfo.gold}`);
+console.log(`Arena rank: ${userInfo.arenaPlace}`);
+console.log(`Grand Arena rank: ${userInfo.grandPlace}`);
+
+// Get arena attempts
+const arenaAttempts = userInfo.refillable.find(r => r.id === 6);
+if (arenaAttempts) {
+ console.log(`Arena attempts available: ${arenaAttempts.amount}`);
+}
+
+// Get Grand Arena attempts
+const grandArenaAttempts = userInfo.refillable.find(r => r.id === 21);
+if (grandArenaAttempts) {
+ console.log(`Grand Arena attempts available: ${grandArenaAttempts.amount}`);
+}
+```
+
+---
+
+### Inventory
+
+#### inventoryGet
+
+Get all inventory items including consumables, gear, fragments, etc.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"inventoryGet","args":{},"ident":"inventoryGet"}]}')
+```
+
+**Response Structure:**
+```javascript
+{
+ consumable: {
+ [libId]: amount, // e.g., { "148": 5 } = 5 platinum loot boxes
+ },
+ gear: {
+ [itemId]: amount,
+ },
+ scroll: {
+ [itemId]: amount,
+ },
+ fragmentGear: {
+ [itemId]: amount,
+ },
+ fragmentScroll: {
+ [itemId]: amount,
+ },
+ // ... other item types
+}
+```
+
+**Example Usage:**
+```javascript
+const inventory = await Send('{"calls":[{"name":"inventoryGet","args":{},"ident":"inventoryGet"}]}')
+ .then(e => e.results[0].result.response);
+
+const lootBoxes = inventory.consumable[148] || 0;
+console.log(`Platinum loot boxes: ${lootBoxes}`);
+```
+
+#### consumableUseLootBox
+
+Open a loot box consumable item.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"consumableUseLootBox","args":{"libId":148,"amount":1},"ident":"body"}]}')
+```
+
+**Arguments:**
+- `libId` (number): The library ID of the loot box (e.g., 144 = copper, 145 = bronze, 148 = platinum)
+- `amount` (number): Number of loot boxes to open
+
+**Response Structure:**
+```javascript
+{
+ [rewardType]: {
+ [itemId]: amount
+ }
+ // e.g., { "stamina": 100 } or { "coin": { "39": 500 } }
+}
+```
+
+**Example Usage:**
+```javascript
+const response = await Send('{"calls":[{"name":"consumableUseLootBox","args":{"libId":148,"amount":1},"ident":"body"}]}')
+ .then(e => e.results[0].result.response);
+
+const result = Object.values(response).pop();
+if ('stamina' in result) {
+ console.log(`Received ${result.stamina} stamina`);
+}
+```
+
+---
+
+### Heroes and Titans
+
+#### heroGetAll
+
+Get all hero information.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"heroGetAll","args":{},"ident":"body"}]}')
+```
+
+**Response Structure:**
+```javascript
+{
+ [heroId]: {
+ id: number,
+ level: number,
+ stars: number,
+ power: number,
+ // ... hero stats
+ }
+}
+```
+
+**Example Usage:**
+```javascript
+const heroes = await Send('{"calls":[{"name":"heroGetAll","args":{},"ident":"body"}]}')
+ .then(e => e.results[0].result.response);
+
+const heroList = Object.values(heroes);
+console.log(`Total heroes: ${heroList.length}`);
+```
+
+#### titanGetAll
+
+Get all titan information.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"titanGetAll","args":{},"ident":"body"}]}')
+```
+
+**Response Structure:**
+```javascript
+{
+ [titanId]: {
+ id: number,
+ level: number,
+ stars: number,
+ power: number,
+ // ... titan stats
+ }
+}
+```
+
+---
+
+### Teams
+
+#### teamGetAll
+
+Get all team configurations.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"teamGetAll","args":{},"ident":"teamGetAll"}]}')
+```
+
+**Response Structure:**
+```javascript
+{
+ [teamId]: {
+ id: number,
+ heroes: [heroId1, heroId2, ...],
+ pets: [petId1, ...], // For grand arena
+ pet: petId, // For regular arena
+ favor: favorId,
+ banners: [bannerId1, ...]
+ }
+}
+```
+
+**Example Usage:**
+```javascript
+const teams = await Send('{"calls":[{"name":"teamGetAll","args":{},"ident":"teamGetAll"}]}')
+ .then(e => e.results[0].result.response);
+
+const team1 = teams[1];
+console.log(`Team 1 heroes: ${team1.heroes.join(', ')}`);
+```
+
+#### teamGetFavor
+
+Get favor information for teams.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"teamGetFavor","args":{},"ident":"teamGetFavor"}]}')
+```
+
+#### teamGetMaxUpgrade
+
+Get maximum upgrade information for teams.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"teamGetMaxUpgrade","args":{},"ident":"teamGetMaxUpgrade"}]}')
+```
+
+---
+
+### Shops
+
+#### shopGetAll
+
+Get all shop information including available items and prices.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"shopGetAll","args":{},"ident":"shopGetAll"}]}')
+```
+
+**Response Structure:**
+```javascript
+{
+ [shopId]: {
+ id: number,
+ slots: {
+ [slotId]: {
+ id: number,
+ cost: {
+ [currencyType]: {
+ [currencyId]: amount
+ }
+ },
+ reward: {
+ [rewardType]: {
+ [itemId]: amount
+ }
+ },
+ bought: boolean
+ }
+ }
+ }
+}
+```
+
+**Example Usage:**
+```javascript
+const shops = await Send('{"calls":[{"name":"shopGetAll","args":{},"ident":"shopGetAll"}]}')
+ .then(e => e.results[0].result.response);
+
+const shop17 = shops[17];
+const slots = Object.values(shop17.slots);
+const availableSlots = slots.filter(slot => !slot.bought);
+```
+
+#### shopBuy
+
+Purchase an item from a shop.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "shopBuy",
+ args: {
+ shopId: 17,
+ slot: 1,
+ cost: {
+ gold: 10000
+ },
+ reward: {
+ fragmentHero: {
+ "123": 5
+ }
+ }
+ },
+ ident: "body"
+ }]
+}))
+```
+
+**Arguments:**
+- `shopId` (number): Shop identifier
+- `slot` (number): Slot identifier within the shop
+- `cost` (object): Cost structure matching the shop slot's cost
+- `reward` (object): Reward structure matching the shop slot's reward
+
+**Example Usage:**
+```javascript
+const calls = [];
+for (const slot of availableSlots) {
+ calls.push({
+ name: "shopBuy",
+ args: {
+ shopId: shop.id,
+ slot: slot.id,
+ cost: slot.cost,
+ reward: slot.reward
+ },
+ ident: `shopBuy_${shop.id}_${slot.id}`
+ });
+}
+
+const result = await Send(JSON.stringify({ calls }))
+ .then(e => e.results.map(n => n.result.response));
+```
+
+---
+
+### Quests
+
+#### questGetAll
+
+Get all quest information.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"questGetAll","args":{},"ident":"questGetAll"}]}')
+```
+
+**Response Structure:**
+```javascript
+[
+ {
+ id: number,
+ progress: number,
+ state: number, // 0 = not started, 1 = in progress, 2 = completed
+ // ... quest details
+ }
+]
+```
+
+**Example Usage:**
+```javascript
+const quests = await Send('{"calls":[{"name":"questGetAll","args":{},"ident":"questGetAll"}]}')
+ .then(e => e.results[0].result.response);
+
+const completedQuests = quests.filter(q => q.state === 2);
+```
+
+---
+
+### Arena
+
+#### arenaAttack
+
+Attack a rival in regular arena.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "arenaAttack",
+ args: {
+ userId: rivalId,
+ heroes: [heroId1, heroId2, heroId3, heroId4, heroId5],
+ pet: petId,
+ favor: favorId,
+ banners: [bannerId1, ...]
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+}))
+```
+
+**Arguments:**
+- `userId` (number): Target user ID
+- `heroes` (array): Array of 5 hero IDs
+- `pet` (number): Pet ID
+- `favor` (number): Favor ID
+- `banners` (array): Array of banner IDs
+
+#### grandAttack
+
+Attack a rival in grand arena.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "grandAttack",
+ args: {
+ userId: rivalId,
+ heroes: [heroId1, heroId2, heroId3, heroId4, heroId5],
+ pets: [petId1, petId2, petId3], // Note: plural "pets" for grand arena
+ favor: favorId,
+ banners: [bannerId1, ...]
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+}))
+```
+
+**Arguments:**
+- `userId` (number): Target user ID
+- `heroes` (array): Array of 5 hero IDs
+- `pets` (array): Array of 3 pet IDs (plural for grand arena)
+- `favor` (number): Favor ID
+- `banners` (array): Array of banner IDs
+
+---
+
+### Guild War
+
+#### guildWar_attackSlot
+
+Attack a slot in Guild War. **Note:** This is an alternative API name. The primary Guild War APIs use the `clanWar` prefix (e.g., `clanWarAttack`).
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "guildWar_attackSlot",
+ args: {
+ slotId: 1,
+ team: {
+ heroes: [heroId1, heroId2, heroId3, heroId4, heroId5],
+ pet: petId, // For hero battles
+ pets: [petId1, petId2, petId3], // For titan battles
+ favor: favorId,
+ banners: [bannerId1, ...]
+ }
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+}))
+```
+
+**Arguments:**
+- `slotId` (number): Slot number to attack (1-9)
+- `team` (object): Team configuration
+ - For hero battles (slots 1-7): Use `pet` (singular)
+ - For titan battles (slots 8-9): Use `pets` (plural array)
+
+---
+
+### Dungeon
+
+#### dungeonGetInfo
+
+Get dungeon information.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "dungeonGetInfo",
+ args: {},
+ ident: "dungeonGetInfo"
+ }]
+}))
+```
+
+#### dungeonStartBattle
+
+Start a dungeon battle.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "dungeonStartBattle",
+ args: {
+ // Battle-specific arguments
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### dungeonEndBattle
+
+End a dungeon battle.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "dungeonEndBattle",
+ args: {
+ result: {},
+ progress: {}
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### dungeonSaveProgress
+
+Save dungeon progress.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "dungeonSaveProgress",
+ args: {},
+ ident: "body"
+ }]
+}))
+```
+
+---
+
+### Tower
+
+#### towerGetInfo
+
+Get tower information.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "towerGetInfo",
+ args: {},
+ ident: "towerGetInfo"
+ }]
+}))
+```
+
+#### towerStartBattle
+
+Start a tower battle.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "towerStartBattle",
+ args: {},
+ ident: "body"
+ }]
+}))
+```
+
+#### towerEndBattle
+
+End a tower battle.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "towerEndBattle",
+ args: {
+ result: {},
+ progress: {}
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### towerNextFloor
+
+Move to the next floor in the tower.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "towerNextFloor",
+ args: {},
+ ident: "body"
+ }]
+}))
+```
+
+#### towerOpenChest
+
+Open a chest in the tower.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "towerOpenChest",
+ args: {
+ floorNumber: 10
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### towerSkipFloor
+
+Skip a floor in the tower.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "towerSkipFloor",
+ args: {},
+ ident: "body"
+ }]
+}))
+```
+
+#### towerBuyBuff
+
+Buy a buff in the tower.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "towerBuyBuff",
+ args: {
+ buffId: 1
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### tower_farmPointRewards
+
+Farm point rewards from tower.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "tower_farmPointRewards",
+ args: {},
+ ident: "body"
+ }]
+}))
+```
+
+#### tower_farmSkullReward
+
+Farm skull rewards from tower.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "tower_farmSkullReward",
+ args: {},
+ ident: "body"
+ }]
+}))
+```
+
+---
+
+### Titan Arena
+
+#### titanArenaGetStatus
+
+Get titan arena status.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "titanArenaGetStatus",
+ args: {},
+ ident: "titanArenaGetStatus"
+ }]
+}))
+```
+
+#### titanArenaCompleteTier
+
+Complete a tier in titan arena.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "titanArenaCompleteTier",
+ args: {},
+ ident: "body"
+ }]
+}))
+```
+
+#### titanArenaStartBattle
+
+Start a titan arena battle.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "titanArenaStartBattle",
+ args: {
+ rivalId: userId
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### titanArenaEndBattle
+
+End a titan arena battle.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "titanArenaEndBattle",
+ args: {
+ result: {},
+ progress: {}
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### titanArenaStartRaid
+
+Start a titan arena raid.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "titanArenaStartRaid",
+ args: {},
+ ident: "body"
+ }]
+}))
+```
+
+#### titanArenaEndRaid
+
+End a titan arena raid.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "titanArenaEndRaid",
+ args: {
+ results: []
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### titanArenaFarmDailyReward
+
+Farm daily rewards from titan arena.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "titanArenaFarmDailyReward",
+ args: {},
+ ident: "body"
+ }]
+}))
+```
+
+---
+
+### Adventure
+
+#### adventure_getInfo
+
+Get adventure information including map data, nodes, paths, buffs, and player progress.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "adventure_getInfo",
+ args: {},
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "group_1_body"
+ }]
+}))
+```
+
+**Alternative Request Format:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "adventure_getInfo",
+ args: {},
+ ident: "adventure_getInfo"
+ }]
+}))
+```
+
+**Note:** The `ident` field can be either `"group_1_body"` or `"adventure_getInfo"` depending on the context. The `context` field with `actionTs` is optional but recommended for proper timestamp tracking.
+
+**Response Fields:**
+
+**Adventure Metadata:**
+- `id` (string): Adventure instance ID
+- `adventureId` (string): Adventure type/level ID (e.g., "11", "13")
+- `mapIdent` (string): Map identifier (e.g., "adv_ghirwil_3pl_hell", "adv_ghirwil_3pl_hard")
+- `status` (string): Adventure status/difficulty code
+ - `"1"`: Active adventure - Hell difficulty (e.g., `adv_ghirwil_3pl_hell`)
+ - `"2"`: Active adventure - Hard difficulty (e.g., `adv_ghirwil_3pl_hard`)
+ - **Note**: Both values indicate the adventure is active/in progress, but the number corresponds to the difficulty level. The exact mapping may vary by adventure type.
+- `assetIdent` (string): Asset identifier for the map (e.g., "adventure_map_ghirvil_city")
+- `battleground` (number): Battleground ID
+
+**Users:**
+- `users` (object): Map of user IDs to user adventure data
+ - Each user object contains:
+ - `id` (string): User ID
+ - `buffs` (array): Array of active buffs for the user
+ - Each buff object:
+ - `id` (number): Buff ID
+ - `value` (number): Buff value/percentage
+ - `currentNode` (number): Current node ID where the user is located
+ - `turnsLeft` (number): Number of turns remaining
+ - `points` (number): Total points accumulated
+ - `rewardsCollected` (array|object): Rewards that have been collected
+ - **Array format**: Array of reward IDs (when no rewards collected yet, typically empty array `[]`)
+ - **Object format**: Map of reward point thresholds (as string keys) to reward data
+ - Each key is a point threshold (e.g., `"260"`, `"460"`, `"660"`) or `"boss"` for boss reward
+ - Each value contains reward details:
+ - `consumable` (object): Map of consumable IDs to quantities
+ - Example: `{"85": 1258}` means consumable ID 85 with quantity 1258
+ - `petGear` (object, optional): Map of pet gear IDs to quantities (boss reward only)
+ - Example: `{"6": 1}` means pet gear ID 6 with quantity 1
+ - `left` (boolean): Whether the user has left the adventure
+ - `bossQuestEmitted` (boolean, optional): Whether the boss quest/event has been triggered (boss defeated). Only present when `true`.
+ - `lastTeam` (array): Last team composition used
+ - Each team member object:
+ - `id` (number): Hero or pet ID
+ - `level` (number): Unit level
+ - `color` (number): Color/ascension level
+ - `star` (number): Star level
+ - `power` (number): Unit power
+ - `type` (string): "hero" or "pet"
+ - `user` (object): User profile information
+ - `id` (string): User ID
+ - `name` (string): Player name
+ - `lastLoginTime` (string): Unix timestamp of last login
+ - `serverId` (string): Server ID
+ - `level` (string): Player level
+ - `clanId` (string): Clan ID
+ - `clanRole` (string): Role in clan
+ - `commander` (boolean): Whether player is a commander
+ - `avatarId` (string): Avatar ID
+ - `isChatModerator` (boolean): Chat moderator status
+ - `frameId` (number): Frame ID
+ - `leagueId` (number): League ID
+ - `allowPm` (string): PM permission setting
+ - `clanTitle` (string): Clan name
+ - `clanIcon` (object): Clan icon configuration
+
+**Nodes:**
+- `nodes` (array): Array of adventure nodes (map locations)
+ - Each node object contains:
+ - `id` (number): Node ID
+ - `type` (string): Node type
+ - `"TYPE_START"`: Starting node
+ - `"TYPE_COMBAT"`: Combat/battle node
+ - `"TYPE_PLAYERBUFF"`: Player buff collection node
+ - `state` (string): Node state
+ - `"empty"`: Node is empty/available
+ - `"occupied"`: Node is occupied by enemy team
+ - `lastBoss` (boolean): Whether this is the final boss node
+ - `playerBuffPower` (number): Player buff power value (for buff nodes)
+ - `buffs` (array, optional): Array of buffs available on this node
+ - Each buff object:
+ - `id` (number): Buff ID
+ - `value` (number): Buff value/percentage
+ - `owner` (string|null): User ID who owns the buff (null if unclaimed)
+ - `team` (array, optional): Enemy team composition (for combat nodes)
+ - Array of team member objects with position keys ("1", "2", etc.)
+ - Each member object:
+ - `id` (number): Hero ID
+ - `star` (number): Star level
+ - `color` (number): Color/ascension level
+ - `level` (number): Unit level
+ - `power` (number): Unit power
+ - `type` (string): "hero"
+ - `state` (object): Current battle state
+ - `hp` (number): Current HP
+ - `energy` (number): Current energy
+ - `isDead` (boolean): Whether unit is dead
+ - `maxHp` (number): Maximum HP
+ - `featuredHero` (number, optional): Featured hero ID (for boss nodes)
+ - `pointsFarmed` (number): Points farmed from this node (0 if not cleared)
+
+**Paths:**
+- `paths` (array): Array of path connections between nodes
+ - Each path object:
+ - `from_id` (number): Source node ID
+ - `to_id` (number): Destination node ID
+
+**Buffs:**
+- `buffs` (array): Array of buff connections between nodes
+ - Each buff object:
+ - `from_id` (number): Source node ID
+ - `to_id` (number): Destination node ID
+ - `buffPower` (number): Buff power value (typically 1000)
+ - `buffs` (array): Array of buff effects
+ - Each buff effect:
+ - `id` (number): Buff ID
+ - `value` (number): Buff value/percentage
+
+**Global Buffs:**
+- `globalBuffs` (array): Array of global buffs active in the adventure
+ - Each global buff object:
+ - `id` (number): Buff ID
+ - `rowId` (number): Row ID
+ - `value` (number): Buff value/percentage
+- `globalBuffsReset` (number): Unix timestamp when global buffs reset
+
+**Rewards:**
+- `rewards` (object): Reward structure
+ - `boss` (object): Boss rewards
+ - `lootBox` (object): Loot box rewards (map of loot box IDs to quantities)
+ - `points` (object): Point-based rewards
+ - Each key is a point threshold (string)
+ - Each value contains:
+ - `lootBox` (object): Loot box rewards at this threshold
+
+**Log:**
+- `log` (array): Array of adventure activity log entries
+ - Each log entry:
+ - `ts` (number): Unix timestamp of the action
+ - `userId` (string): User ID who performed the action
+ - `type` (string): Action type
+ - `"join"`: User joined the adventure
+ - `"collectBuff"`: User collected a buff
+ - `"startBattle"`: User started a battle
+ - `"endBattle"`: User ended a battle
+ - `data` (object|array): Action-specific data
+ - For `collectBuff`:
+ - `path` (array): Path taken [fromNode, toNode]
+ - `node` (number): Node ID where buff was collected
+ - `buff` (number): Buff index collected
+ - For `startBattle`:
+ - `path` (array): Path taken [fromNode, toNode]
+ - `node` (number): Node ID being attacked
+ - For `endBattle`:
+ - `node` (string): Node ID where battle occurred
+ - `win` (boolean): Whether battle was won
+ - `replayId` (string): Replay ID
+ - `points` (number): Points earned from battle
+
+**Example Usage:**
+```javascript
+const response = await Send(JSON.stringify({
+ calls: [{
+ name: "adventure_getInfo",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "group_1_body"
+ }]
+}));
+
+const adventureInfo = response.results[0].result.response;
+
+// Get adventure metadata
+console.log(`Adventure ID: ${adventureInfo.id}`);
+console.log(`Map: ${adventureInfo.mapIdent}`);
+console.log(`Status: ${adventureInfo.status}`);
+
+// Get user progress
+const userId = Object.keys(adventureInfo.users)[0];
+const userData = adventureInfo.users[userId];
+console.log(`Current Node: ${userData.currentNode}`);
+console.log(`Turns Left: ${userData.turnsLeft}`);
+console.log(`Points: ${userData.points}`);
+console.log(`Active Buffs: ${userData.buffs.length}`);
+
+// Get nodes
+const nodes = adventureInfo.nodes;
+console.log(`Total Nodes: ${nodes.length}`);
+
+// Find combat nodes
+const combatNodes = nodes.filter(n => n.type === "TYPE_COMBAT");
+console.log(`Combat Nodes: ${combatNodes.length}`);
+
+// Find empty nodes (available to attack)
+const emptyNodes = nodes.filter(n => n.state === "empty" && n.type === "TYPE_COMBAT");
+console.log(`Empty Combat Nodes: ${emptyNodes.length}`);
+
+// Get paths
+const paths = adventureInfo.paths;
+console.log(`Total Paths: ${paths.length}`);
+
+// Find paths from current node
+const currentPaths = paths.filter(p => p.from_id === userData.currentNode);
+console.log(`Available paths from node ${userData.currentNode}:`, currentPaths.map(p => p.to_id));
+
+// Get buff nodes
+const buffNodes = nodes.filter(n => n.type === "TYPE_PLAYERBUFF");
+console.log(`Buff Nodes: ${buffNodes.length}`);
+
+// Get adventure log
+const log = adventureInfo.log;
+console.log(`Adventure Log Entries: ${log.length}`);
+const recentActions = log.slice(-5);
+recentActions.forEach(entry => {
+ console.log(`${new Date(entry.ts * 1000).toLocaleString()}: ${entry.type} by user ${entry.userId}`);
+});
+
+// Get rewards
+const rewards = adventureInfo.rewards;
+console.log(`Boss Rewards:`, rewards.boss);
+console.log(`Point Rewards:`, Object.keys(rewards.points));
+```
+
+**Determining if Adventure is Complete:**
+
+To check if an adventure is complete and ready to exit/collect rewards, check the following conditions:
+
+```javascript
+function isAdventureComplete(adventureInfo, userId) {
+ const userData = adventureInfo.users[userId];
+ if (!userData) return false;
+
+ // 1. Check if user has already left
+ if (userData.left) {
+ return true; // Already exited
+ }
+
+ // 2. Find the boss node (lastBoss: true)
+ const bossNode = adventureInfo.nodes.find(n => n.lastBoss === true);
+ if (!bossNode) {
+ return false; // No boss node found
+ }
+
+ // 3. Check if boss node has been cleared
+ // Boss is cleared if state is "empty" and pointsFarmed > 0
+ const bossCleared = bossNode.state === "empty" && bossNode.pointsFarmed > 0;
+
+ // 4. Check if user is at the boss node
+ const atBossNode = userData.currentNode === bossNode.id;
+
+ // 5. Check if there are no available paths from current node
+ const paths = adventureInfo.paths;
+ const availablePaths = paths.filter(p => p.from_id === userData.currentNode);
+ const hasNoPaths = availablePaths.length === 0;
+
+ // 6. Check if turns are exhausted (optional - some adventures may allow staying after turns run out)
+ const noTurnsLeft = userData.turnsLeft === 0;
+
+ // 7. Check if 4 rewards are available for collection
+ const rewards = adventureInfo.rewards;
+ const pointThresholds = Object.keys(rewards.points || {}).map(Number).sort((a, b) => a - b);
+ const userPoints = userData.points;
+
+ // Count uncollected point rewards
+ let uncollectedPointRewards = 0;
+ pointThresholds.forEach(threshold => {
+ if (userPoints >= threshold) {
+ // Check if already collected
+ const collected = Array.isArray(userData.rewardsCollected)
+ ? userData.rewardsCollected.includes(threshold.toString())
+ : (typeof userData.rewardsCollected === 'object' && threshold.toString() in userData.rewardsCollected);
+
+ if (!collected) {
+ uncollectedPointRewards++;
+ }
+ }
+ });
+
+ // Check boss reward availability
+ const bossRewardsAvailable = rewards.boss && Object.keys(rewards.boss.lootBox || {}).length > 0;
+ const bossRewardCollected = Array.isArray(userData.rewardsCollected)
+ ? userData.rewardsCollected.length > 0
+ : (typeof userData.rewardsCollected === 'object' && Object.keys(userData.rewardsCollected).length > 0);
+
+ const uncollectedBossRewards = bossRewardsAvailable && !bossRewardCollected ? 1 : 0;
+ const totalUncollectedRewards = uncollectedPointRewards + uncollectedBossRewards;
+
+ // Adventure is complete if:
+ // - Boss node is cleared AND user is at boss node, OR
+ // - Boss node is cleared AND no paths available, OR
+ // - Boss node is cleared AND no turns left
+ // AND there are 4 rewards available for collection
+ const adventureProgressComplete = bossCleared && (atBossNode || hasNoPaths || noTurnsLeft);
+ const rewardsReady = totalUncollectedRewards === 4;
+
+ return adventureProgressComplete && rewardsReady;
+}
+
+// Usage example:
+const userId = "55167289"; // Your user ID
+const isComplete = isAdventureComplete(adventureInfo, userId);
+
+if (isComplete) {
+ console.log("Adventure is complete! Ready to exit and collect rewards.");
+
+ // Check if rewards need to be collected
+ const userData = adventureInfo.users[userId];
+ const rewards = adventureInfo.rewards;
+
+ // Check point-based rewards
+ const pointThresholds = Object.keys(rewards.points).map(Number).sort((a, b) => a - b);
+ const userPoints = userData.points;
+
+ // Find uncollected point rewards
+ const uncollectedPointRewards = pointThresholds.filter(threshold => {
+ if (userPoints < threshold) return false; // Not reached yet
+
+ // Check if already collected
+ if (Array.isArray(userData.rewardsCollected)) {
+ return !userData.rewardsCollected.includes(threshold.toString());
+ } else if (typeof userData.rewardsCollected === 'object') {
+ return !(threshold.toString() in userData.rewardsCollected);
+ }
+ return true; // No rewards collected yet
+ });
+
+ // Check boss rewards
+ const bossRewardsAvailable = rewards.boss && Object.keys(rewards.boss.lootBox).length > 0;
+ const bossRewardCollected = Array.isArray(userData.rewardsCollected)
+ ? userData.rewardsCollected.length > 0
+ : typeof userData.rewardsCollected === 'object' && Object.keys(userData.rewardsCollected).length > 0;
+
+ console.log(`Uncollected point rewards at thresholds: ${uncollectedPointRewards.join(', ')}`);
+ console.log(`Boss rewards available: ${bossRewardsAvailable}`);
+ console.log(`Boss reward collected: ${bossRewardCollected}`);
+
+ if (uncollectedPointRewards.length > 0 || (bossRewardsAvailable && !bossRewardCollected)) {
+ console.log("Rewards are available to collect!");
+ }
+} else {
+ console.log("Adventure is still in progress.");
+
+ // Check why it's not complete
+ const userData = adventureInfo.users[userId];
+ const bossNode = adventureInfo.nodes.find(n => n.lastBoss === true);
+
+ if (bossNode) {
+ console.log(`Boss node (${bossNode.id}) state: ${bossNode.state}, pointsFarmed: ${bossNode.pointsFarmed}`);
+ console.log(`Current node: ${userData.currentNode}, Turns left: ${userData.turnsLeft}`);
+ }
+}
+```
+
+**Key Indicators for Adventure Completion:**
+
+1. **Boss Node Cleared**: The boss node (`lastBoss: true`) must have `state: "empty"` and `pointsFarmed > 0`
+2. **User Position**: User should be at or past the boss node
+3. **No Available Paths**: No paths available from current node (indicates end of map)
+4. **Turns Exhausted**: `turnsLeft === 0` (may indicate completion, but check boss status first)
+5. **4 Rewards Available for Collection**: There must be exactly 4 rewards available for collection:
+ - Point-based rewards from `rewards.points` thresholds (typically 3 thresholds: e.g., 220, 440, 660 points)
+ - Boss reward from `rewards.boss` (1 reward)
+ - Total: 4 rewards that haven't been collected yet (checked against `rewardsCollected`)
+
+**Note**: The `status` field (`"1"` or `"2"`) indicates the adventure is active/in progress, not complete. Completion is determined by the boss node state, user position, AND having 4 rewards available for collection.
+
+**Complete State - All Rewards Redeemed and Boss Defeated:**
+
+When an adventure is fully completed with all rewards collected and the boss defeated, the response shows:
+
+**Boss Node State:**
+- Boss node (`lastBoss: true`) has:
+ - `state: "empty"` (cleared)
+ - `pointsFarmed: 20` (points earned from defeating boss)
+ - Boss team members have `isDead: true` and `hp: 0`
+
+**User Rewards Collected:**
+- `rewardsCollected` is an **object** (not an array) containing all 4 collected rewards:
+ ```json
+ {
+ "260": {
+ "consumable": {
+ "85": 1258
+ }
+ },
+ "460": {
+ "consumable": {
+ "85": 1614
+ }
+ },
+ "660": {
+ "consumable": {
+ "85": 2905
+ }
+ },
+ "boss": {
+ "consumable": {
+ "85": 4357
+ },
+ "petGear": {
+ "6": 1
+ }
+ }
+ }
+ ```
+- Each key represents a reward threshold that was collected:
+ - Point thresholds: `"260"`, `"460"`, `"660"` (values may vary by difficulty)
+ - Boss reward: `"boss"`
+- Each value contains the actual reward data received:
+ - `consumable` (object): Map of consumable item IDs to quantities
+ - `petGear` (object, optional): Map of pet gear IDs to quantities (boss reward only)
+
+**User Status Fields:**
+- `bossQuestEmitted: true` - Indicates the boss quest/event has been triggered (boss defeated)
+- `left: false` - User is still in the adventure (can be `true` if user has exited)
+- `turnsLeft: 0` - No turns remaining (typical after completing adventure)
+- `points: 240` (or higher) - Total points accumulated (must meet all reward thresholds)
+
+**Example - Checking if All Rewards Collected:**
+
+```javascript
+function hasAllRewardsCollected(adventureInfo, userId) {
+ const userData = adventureInfo.users[userId];
+ if (!userData) return false;
+
+ // Check if rewardsCollected is an object with all 4 keys
+ if (typeof userData.rewardsCollected !== 'object' || Array.isArray(userData.rewardsCollected)) {
+ return false; // Not in object format yet
+ }
+
+ const rewards = adventureInfo.rewards;
+ const collected = userData.rewardsCollected;
+
+ // Count expected rewards
+ const pointThresholds = Object.keys(rewards.points || {}).sort((a, b) => parseInt(a) - parseInt(b));
+ const hasBossReward = rewards.boss && Object.keys(rewards.boss.lootBox || {}).length > 0;
+
+ // Check all point rewards are collected
+ const allPointRewardsCollected = pointThresholds.every(threshold => threshold in collected);
+
+ // Check boss reward is collected
+ const bossRewardCollected = !hasBossReward || ("boss" in collected);
+
+ // Total should be 4 rewards (3 point + 1 boss, or all point rewards if no boss)
+ const expectedCount = pointThresholds.length + (hasBossReward ? 1 : 0);
+ const actualCount = Object.keys(collected).length;
+
+ return allPointRewardsCollected && bossRewardCollected && actualCount === expectedCount;
+}
+
+// Check if boss is defeated
+function isBossDefeated(adventureInfo) {
+ const bossNode = adventureInfo.nodes.find(n => n.lastBoss === true);
+ if (!bossNode) return false;
+
+ return bossNode.state === "empty" && bossNode.pointsFarmed > 0;
+}
+
+// Usage:
+const userId = "35979991";
+const allRewardsCollected = hasAllRewardsCollected(adventureInfo, userId);
+const bossDefeated = isBossDefeated(adventureInfo);
+const userData = adventureInfo.users[userId];
+
+console.log(`Boss defeated: ${bossDefeated}`);
+console.log(`All rewards collected: ${allRewardsCollected}`);
+console.log(`Boss quest emitted: ${userData.bossQuestEmitted}`);
+console.log(`User left adventure: ${userData.left}`);
+console.log(`Rewards collected:`, userData.rewardsCollected);
+
+if (allRewardsCollected && bossDefeated) {
+ console.log("Adventure fully completed! All rewards redeemed and boss defeated.");
+
+ // Show reward summary
+ const collected = userData.rewardsCollected;
+ Object.entries(collected).forEach(([rewardId, rewardData]) => {
+ console.log(`Reward ${rewardId}:`);
+ if (rewardData.consumable) {
+ Object.entries(rewardData.consumable).forEach(([itemId, quantity]) => {
+ console.log(` Consumable ${itemId}: ${quantity}`);
+ });
+ }
+ if (rewardData.petGear) {
+ Object.entries(rewardData.petGear).forEach(([gearId, quantity]) => {
+ console.log(` Pet Gear ${gearId}: ${quantity}`);
+ });
+ }
+ });
+}
+```
+
+**Key Differences - Before vs After Completion:**
+
+| Field | Before Completion | After Completion (All Rewards Collected) |
+|-------|-------------------|------------------------------------------|
+| `rewardsCollected` | Empty array `[]` | Object with 4 keys: `{"260": {...}, "460": {...}, "660": {...}, "boss": {...}}` |
+| Boss node `state` | `"occupied"` | `"empty"` |
+| Boss node `pointsFarmed` | `0` | `20` (or higher) |
+| Boss team `isDead` | `false` | `true` |
+| Boss team `hp` | `> 0` | `0` |
+| `bossQuestEmitted` | `false` or missing | `true` |
+| `turnsLeft` | `> 0` | `0` (typically) |
+| `left` | `false` | `false` (still in) or `true` (exited) |
+
+#### adventure_turnStartBattle
+
+Start a battle in adventure.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "adventure_turnStartBattle",
+ args: {},
+ ident: "body"
+ }]
+}))
+```
+
+#### adventure_endBattle
+
+End a battle in adventure.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "adventure_endBattle",
+ args: {
+ result: {},
+ progress: {}
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### adventure_turnCollectBuff
+
+Collect a buff in adventure.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "adventure_turnCollectBuff",
+ args: {
+ buffId: 1
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### adventure_collectReward
+
+Collect a reward chest from adventure. Rewards are available based on point thresholds reached during the adventure. Each reward can only be collected once.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "adventure_collectReward",
+ args: {
+ rewardId: "220" // Point threshold as string (e.g., "220", "440", "660")
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+}))
+```
+
+**Request Parameters:**
+- `rewardId` (string): The point threshold that unlocks this reward. Common values:
+ - `"260"`: First point-based reward threshold (Hell difficulty)
+ - `"220"`: First point-based reward threshold (Hard difficulty or other variants)
+ - `"460"`: Second point-based reward threshold (Hell difficulty)
+ - `"440"`: Second point-based reward threshold (Hard difficulty or other variants)
+ - `"660"`: Third point-based reward threshold
+ - `"boss"`: Boss reward (collected after defeating the final boss)
+ - **Note**: The exact threshold values may vary by adventure difficulty and type. Check `adventure_getInfo` response under `rewards.points` to see the actual thresholds for your current adventure.
+
+**Response Fields:**
+- `response` (object): Reward data received
+ - `consumable` (object): Map of consumable item IDs to quantities received
+ - Example: `{"85": 1258}` means consumable ID 85 with quantity 1258
+ - Example: `{"85": 1614}` means consumable ID 85 with quantity 1614
+ - Example: `{"85": 2905}` means consumable ID 85 with quantity 2905
+ - Example: `{"85": 4357}` means consumable ID 85 with quantity 4357
+ - `petGear` (object, optional): Map of pet gear IDs to quantities received (typically only in boss reward)
+ - Example: `{"6": 1}` means pet gear ID 6 with quantity 1
+- `quests` (array, optional): Array of quest progress updates triggered by collecting this reward
+ - Each quest object:
+ - `id` (number): Quest ID
+ - `state` (number): Quest state (1 = in progress, 2 = completed)
+ - `progress` (number): Current progress value
+ - `reward` (object): Quest reward data
+ - `consumable` (object, optional): Consumable rewards
+ - `clanQuestsPoints` (number, optional): Clan quest points
+ - `prestige` (number, optional): Prestige points
+ - `createTime` (number): Quest creation timestamp
+
+**Example Usage:**
+```javascript
+// Collect first point reward (260 points threshold for Hell difficulty)
+const response = await Send(JSON.stringify({
+ calls: [{
+ name: "adventure_collectReward",
+ args: {
+ rewardId: "260" // Use actual threshold from adventure_getInfo
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+}));
+
+const result = response.results[0].result;
+const reward = result.response;
+
+// Check what consumables were received
+if (reward.consumable) {
+ for (const [itemId, quantity] of Object.entries(reward.consumable)) {
+ console.log(`Received ${quantity} of consumable ID ${itemId}`);
+ }
+}
+
+// Check pet gear (typically only in boss reward)
+if (reward.petGear) {
+ for (const [gearId, quantity] of Object.entries(reward.petGear)) {
+ console.log(`Received ${quantity} of pet gear ID ${gearId}`);
+ }
+}
+
+// Check quest progress updates
+if (result.quests && result.quests.length > 0) {
+ result.quests.forEach(quest => {
+ console.log(`Quest ${quest.id}: Progress ${quest.progress}, State ${quest.state}`);
+ if (quest.reward) {
+ console.log(`Quest reward:`, quest.reward);
+ }
+ });
+}
+```
+
+**Collecting Multiple Rewards:**
+Rewards can be collected in sequence. The `rewardId` corresponds to the point thresholds defined in `adventure_getInfo` response under `rewards.points`:
+
+```javascript
+// Get adventure info to see available rewards
+const adventureInfo = await Send(JSON.stringify({
+ calls: [{ name: "adventure_getInfo", args: {}, ident: "body" }]
+}));
+
+const rewards = adventureInfo.results[0].result.response.rewards;
+const userId = Object.keys(adventureInfo.results[0].result.response.users)[0];
+const userData = adventureInfo.results[0].result.response.users[userId];
+const collected = userData.rewardsCollected;
+
+// Collect point-based rewards (in order: 260, 460, 660 for Hell difficulty)
+const pointRewards = Object.keys(rewards.points).sort((a, b) => parseInt(a) - parseInt(b));
+for (const threshold of pointRewards) {
+ // Check if already collected
+ const isCollected = Array.isArray(collected)
+ ? collected.includes(threshold)
+ : (typeof collected === 'object' && collected[threshold]);
+
+ if (!isCollected) {
+ console.log(`Collecting reward for ${threshold} points...`);
+ const response = await Send(JSON.stringify({
+ calls: [{
+ name: "adventure_collectReward",
+ args: { rewardId: threshold },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+ }));
+
+ const result = response.results[0].result;
+ console.log(`Reward collected:`, result.response);
+
+ // Small delay between collections
+ await new Promise(resolve => setTimeout(resolve, 500));
+ }
+}
+
+// Collect boss reward (after defeating boss)
+// Boss reward typically includes petGear in addition to consumables
+if (rewards.boss) {
+ const bossCollected = Array.isArray(collected)
+ ? collected.includes("boss")
+ : (typeof collected === 'object' && collected["boss"]);
+
+ if (!bossCollected) {
+ console.log(`Collecting boss reward...`);
+ const response = await Send(JSON.stringify({
+ calls: [{
+ name: "adventure_collectReward",
+ args: { rewardId: "boss" },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+ }));
+
+ const result = response.results[0].result;
+ const bossReward = result.response;
+ console.log(`Boss reward collected:`, bossReward);
+
+ // Boss reward may include petGear
+ if (bossReward.petGear) {
+ console.log(`Pet gear received:`, bossReward.petGear);
+ }
+ }
+}
+```
+
+**Complete Reward Collection Sequence Example (Hell Difficulty):**
+```javascript
+// Collect all 4 rewards in order: 260, 460, 660, boss
+const rewardIds = ["260", "460", "660", "boss"];
+
+for (const rewardId of rewardIds) {
+ try {
+ const response = await Send(JSON.stringify({
+ calls: [{
+ name: "adventure_collectReward",
+ args: { rewardId: rewardId },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+ }));
+
+ const result = response.results[0].result;
+ const reward = result.response;
+
+ console.log(`✓ Collected reward ${rewardId}:`);
+ if (reward.consumable) {
+ console.log(` Consumables:`, reward.consumable);
+ }
+ if (reward.petGear) {
+ console.log(` Pet Gear:`, reward.petGear);
+ }
+
+ // Wait between collections
+ await new Promise(resolve => setTimeout(resolve, 500));
+ } catch (error) {
+ console.error(`Failed to collect reward ${rewardId}:`, error);
+ }
+}
+```
+
+**Error Handling:**
+- If reward has already been collected, the API may return an error
+- If `rewardId` doesn't exist or hasn't been unlocked yet, the API may return an error
+- Always check `adventure_getInfo` to verify which rewards are available and which have been collected
+
+**Note**: After collecting a reward, call `adventure_getInfo` again to refresh the `rewardsCollected` field, which will show the newly collected reward ID.
+
+#### adventureSolo_getInfo
+
+Get solo adventure information.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "adventureSolo_getInfo",
+ args: {},
+ ident: "adventureSolo_getInfo"
+ }]
+}))
+```
+
+#### adventureSolo_turnStartBattle
+
+Start a battle in solo adventure.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "adventureSolo_turnStartBattle",
+ args: {},
+ ident: "body"
+ }]
+}))
+```
+
+#### adventureSolo_endBattle
+
+End a battle in solo adventure.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "adventureSolo_endBattle",
+ args: {
+ result: {},
+ progress: {}
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### adventureSolo_turnCollectBuff
+
+Collect a buff in solo adventure.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "adventureSolo_turnCollectBuff",
+ args: {
+ buffId: 1
+ },
+ ident: "body"
+ }]
+}))
+```
+
+---
+
+### Brawls
+
+#### brawl_questGetInfo
+
+Get brawl quest information.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "brawl_questGetInfo",
+ args: {},
+ ident: "brawl_questGetInfo"
+ }]
+}))
+```
+
+#### brawl_findEnemies
+
+Find enemies in brawls.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "brawl_findEnemies",
+ args: {},
+ ident: "brawl_findEnemies"
+ }]
+}))
+```
+
+#### brawl_questFarm
+
+Farm brawl quest rewards.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "brawl_questFarm",
+ args: {},
+ ident: "brawl_questFarm"
+ }]
+}))
+```
+
+#### brawl_getInfo
+
+Get brawl information.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "brawl_getInfo",
+ args: {},
+ ident: "brawl_getInfo"
+ }]
+}))
+```
+
+---
+
+### Epic Brawl
+
+#### epicBrawl_endBattle
+
+End an epic brawl battle.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "epicBrawl_endBattle",
+ args: {
+ progress: {},
+ result: {}
+ },
+ ident: "epicBrawl_endBattle"
+ }]
+}))
+```
+
+#### epicBrawl_getWinStreak
+
+Get epic brawl win streak information.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "epicBrawl_getWinStreak",
+ args: {},
+ ident: "epicBrawl_getWinStreak"
+ }]
+}))
+```
+
+#### epicBrawl_farmWinStreak
+
+Farm epic brawl win streak rewards.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"epicBrawl_farmWinStreak","args":{},"ident":"body"}]}')
+```
+
+---
+
+### Boss/Outland
+
+#### bossGetAll
+
+Get all Outland boss information.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"bossGetAll","args":{},"ident":"bossGetAll"}]}')
+```
+
+#### topGet
+
+Get top rankings.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "topGet",
+ args: {
+ type: "bossRatingTop",
+ extraId: 0
+ },
+ ident: "body"
+ }]
+}))
+```
+
+**Arguments:**
+- `type` (string): Type of top list (e.g., "bossRatingTop")
+- `extraId` (number): Additional identifier
+
+---
+
+### Clan
+
+#### clanGetInfo
+
+Get clan information.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"clanGetInfo","args":{},"ident":"clanGetInfo"}]}')
+```
+
+**Response Structure:**
+```javascript
+{
+ stat: {
+ todayItemsActivity: number,
+ // ... other clan stats
+ },
+ // ... other clan data
+}
+```
+
+#### clanItemsForActivity
+
+Exchange items for clan activity.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "clanItemsForActivity",
+ args: {
+ items: {
+ [itemType]: {
+ [itemId]: count
+ }
+ }
+ },
+ ident: "body"
+ }]
+}))
+```
+
+**Example:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "clanItemsForActivity",
+ args: {
+ items: {
+ gear: {
+ "123": 100
+ }
+ }
+ },
+ ident: "body"
+ }]
+}))
+```
+
+---
+
+### Missions
+
+#### missionGetAll
+
+Get all mission information.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"missionGetAll","args":{},"ident":"missionGetAll"}]}')
+```
+
+---
+
+### Mail
+
+#### mailGetAll
+
+Get all mail/letters.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "mailGetAll",
+ args: {},
+ ident: "mailGetAll"
+ }]
+}))
+```
+
+**Response Structure:**
+```javascript
+{
+ letters: {
+ [letterId]: {
+ id: number,
+ reward: {},
+ // ... letter data
+ }
+ }
+}
+```
+
+#### mailCollect
+
+Collect mail rewards.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [
+ {
+ name: "mailCollect",
+ args: {
+ letterIds: [letterId1, letterId2, ...]
+ },
+ ident: "body"
+ }
+ ]
+}))
+```
+
+---
+
+### Special Offers
+
+#### specialOffer_getAll
+
+Get all special offers.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"specialOffer_getAll","args":{},"ident":"specialOffer_getAll"}]}')
+```
+
+#### specialOffer_farmReward
+
+**Description:** Claims/farms rewards from a special offer. This API is typically used for stage-based reward offers where players can claim rewards after completing certain stages. The API returns the claimed rewards and updates the special offers list.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: 'specialOffer_farmReward',
+ args: {
+ offerId: 1778001657
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: 'body'
+ }]
+})
+```
+
+**Request Parameters:**
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `offerId` | Number | Yes | The unique identifier of the special offer to claim rewards from |
+
+**Response Structure:**
+```javascript
+{
+ "date": 1763273826.1036711,
+ "results": [{
+ "ident": "body",
+ "result": {
+ "response": {
+ "starmoney": 100,
+ "coin": {
+ "1778001091": 1
+ }
+ },
+ "specialOffers": [
+ // Updated list of all active special offers
+ ],
+ "endSpecialOffers": [8] // Array of offer IDs that have ended
+ }
+ }]
+}
+```
+
+**Response Fields:**
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `results[].result.response` | Object | The claimed rewards |
+| `results[].result.response.starmoney` | Number | Amount of starmoney claimed |
+| `results[].result.response.coin` | Object | Object mapping coin IDs to amounts claimed |
+| `results[].result.specialOffers` | Array | Updated list of special offers |
+| `results[].result.endSpecialOffers` | Array | Array of special offer IDs that have ended |
+
+**Response Notes:**
+
+- The `response` object contains the actual rewards claimed (starmoney and coins)
+- The `specialOffers` array contains updated information about all active special offers
+- The `endSpecialOffers` array contains IDs of offers that have ended
+- Coin IDs in the `coin` object are strings representing different currency types
+
+**Example Usage:**
+```javascript
+const response = await Send({
+ calls: [{
+ name: 'specialOffer_farmReward',
+ args: {
+ offerId: 1778001657
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: 'body'
+ }]
+});
+
+// Access the claimed rewards
+const rewards = response.results[0].result.response;
+console.log('Starmoney:', rewards.starmoney);
+console.log('Coins:', rewards.coin);
+```
+
+#### specialOffer_check
+
+**Description:** Checks if a special offer is available. This API is used to verify the availability status of one or more special offers before attempting to claim rewards. Multiple offers can be checked in a single request.
+
+**Request:**
+```javascript
+Send({
+ calls: [
+ {
+ name: 'specialOffer_check',
+ args: { offerId: 1778001725 },
+ context: { actionTs: Date.now() },
+ ident: 'offer1'
+ },
+ {
+ name: 'specialOffer_check',
+ args: { offerId: 1778001678 },
+ context: { actionTs: Date.now() },
+ ident: 'offer2'
+ }
+ ]
+})
+```
+
+**Request Parameters:**
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `offerId` | Number | Yes | The unique identifier of the special offer to check |
+
+**Response Structure:**
+```javascript
+{
+ "date": 1763273827.9898541,
+ "results": [
+ {
+ "ident": "offer1",
+ "result": {
+ "response": {
+ "available": true,
+ "failedChecks": null
+ }
+ }
+ },
+ {
+ "ident": "offer2",
+ "result": {
+ "response": {
+ "available": false,
+ "failedChecks": {
+ "offerUnavailable": true
+ }
+ }
+ }
+ }
+ ]
+}
+```
+
+**Response Fields:**
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `results[].result.response.available` | Boolean | Whether the offer is available |
+| `results[].result.response.failedChecks` | Object/null | Object containing failed check reasons, or null if available |
+
+**Failed Checks:**
+
+When `available` is `false`, the `failedChecks` object may contain:
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `offerUnavailable` | Boolean | Set to `true` if the offer is not available (expired, not started, or already claimed) |
+
+**Example Usage:**
+```javascript
+// Check a single offer
+const response = await Send({
+ calls: [{
+ name: 'specialOffer_check',
+ args: {
+ offerId: 1778001725
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: 'body'
+ }]
+});
+
+const isAvailable = response.results[0].result.response.available;
+if (isAvailable) {
+ console.log('Offer is available');
+} else {
+ console.log('Offer is not available:', response.results[0].result.response.failedChecks);
+}
+
+// Check multiple offers at once
+const multiCheckResponse = await Send({
+ calls: [
+ {
+ name: 'specialOffer_check',
+ args: { offerId: 1778001725 },
+ context: { actionTs: Date.now() },
+ ident: 'offer1'
+ },
+ {
+ name: 'specialOffer_check',
+ args: { offerId: 1778001678 },
+ context: { actionTs: Date.now() },
+ ident: 'offer2'
+ }
+ ]
+});
+
+// Process each result
+multiCheckResponse.results.forEach(result => {
+ const offerId = result.ident;
+ const available = result.result.response.available;
+ console.log(`Offer ${offerId}: ${available ? 'Available' : 'Unavailable'}`);
+});
+```
+
+**Common Usage Patterns:**
+
+**Pattern 1: Check Before Claiming**
+```javascript
+async function claimRewardIfAvailable(offerId) {
+ // First check if the offer is available
+ const checkResponse = await Send({
+ calls: [{
+ name: 'specialOffer_check',
+ args: { offerId: offerId },
+ context: { actionTs: Date.now() },
+ ident: 'check'
+ }]
+ });
+
+ const isAvailable = checkResponse.results[0].result.response.available;
+
+ if (!isAvailable) {
+ console.log('Offer is not available');
+ return null;
+ }
+
+ // Claim the reward
+ const claimResponse = await Send({
+ calls: [{
+ name: 'specialOffer_farmReward',
+ args: { offerId: offerId },
+ context: { actionTs: Date.now() },
+ ident: 'claim'
+ }]
+ });
+
+ return claimResponse.results[0].result.response;
+}
+```
+
+**Pattern 2: Batch Check Multiple Offers**
+```javascript
+async function checkMultipleOffers(offerIds) {
+ const calls = offerIds.map((offerId, index) => ({
+ name: 'specialOffer_check',
+ args: { offerId: offerId },
+ context: { actionTs: Date.now() },
+ ident: `offer_${index}`
+ }));
+
+ const response = await Send({ calls });
+
+ return response.results.map((result, index) => ({
+ offerId: offerIds[index],
+ available: result.result.response.available,
+ failedChecks: result.result.response.failedChecks
+ }));
+}
+```
+
+**Pattern 3: Claim All Available Rewards**
+```javascript
+async function claimAllAvailableRewards(offerIds) {
+ // First check all offers
+ const checkCalls = offerIds.map((offerId, index) => ({
+ name: 'specialOffer_check',
+ args: { offerId: offerId },
+ context: { actionTs: Date.now() },
+ ident: `check_${index}`
+ }));
+
+ const checkResponse = await Send({ calls: checkCalls });
+
+ // Filter available offers
+ const availableOffers = checkResponse.results
+ .map((result, index) => ({
+ offerId: offerIds[index],
+ available: result.result.response.available
+ }))
+ .filter(offer => offer.available);
+
+ if (availableOffers.length === 0) {
+ console.log('No available offers');
+ return [];
+ }
+
+ // Claim all available rewards
+ const claimCalls = availableOffers.map((offer, index) => ({
+ name: 'specialOffer_farmReward',
+ args: { offerId: offer.offerId },
+ context: { actionTs: Date.now() },
+ ident: `claim_${index}`
+ }));
+
+ const claimResponse = await Send({ calls: claimCalls });
+
+ return claimResponse.results.map(result => result.result.response);
+}
+```
+
+**Error Handling:**
+
+Both APIs follow the standard Hero Wars API error response format. If an error occurs, the response will contain an error object instead of the expected result.
+
+**Common Error Scenarios:**
+
+1. **Invalid offerId**: The offer ID does not exist or is invalid
+2. **Offer already claimed**: Attempting to claim rewards from an offer that has already been claimed
+3. **Offer expired**: The offer has ended and is no longer available
+4. **Authentication failure**: Invalid or expired authentication headers
+
+---
+
+### Battle Pass
+
+#### battlePass_getInfo
+
+Get battle pass information.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "battlePass_getInfo",
+ args: {},
+ ident: "battlePass_getInfo"
+ }]
+}))
+```
+
+#### battlePass_getSpecial
+
+Get special battle pass information.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "battlePass_getSpecial",
+ args: {},
+ ident: "battlePass_getSpecial"
+ }]
+}))
+```
+
+---
+
+### Artifacts and Skins
+
+#### artifactChestOpen
+
+Open an artifact chest.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "artifactChestOpen",
+ args: {
+ // Arguments vary
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### ascensionChest_open
+
+Open an ascension chest.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "ascensionChest_open",
+ args: {
+ paid: false,
+ amount: 1
+ },
+ ident: "body"
+ }]
+})
+```
+
+**Arguments:**
+- `paid` (boolean): Whether to use paid currency
+- `amount` (number): Number of chests to open
+
+---
+
+### Events and Gifts
+
+#### newYearGiftGet
+
+Get new year gift information.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "newYearGiftGet",
+ args: {
+ type: 0
+ },
+ ident: "body"
+ }]
+})
+```
+
+#### newYearGiftOpen
+
+Open a new year gift.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "newYearGiftOpen",
+ args: {
+ giftId: giftId
+ },
+ ident: "body"
+ }]
+})
+```
+
+---
+
+### Expeditions
+
+#### expeditionGet
+
+Get expedition information.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "expeditionGet",
+ args: {},
+ ident: "expeditionGet"
+ }]
+}))
+```
+
+#### expeditionFarm
+
+Farm expedition rewards.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "expeditionFarm",
+ args: {},
+ ident: "body"
+ }]
+}))
+```
+
+#### expeditionSendHeroes
+
+Send heroes on an expedition.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "expeditionSendHeroes",
+ args: {
+ heroes: [heroId1, heroId2, ...]
+ },
+ ident: "body"
+ }]
+}))
+```
+
+---
+
+### Time
+
+#### getTime
+
+Get server time.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"getTime","args":{},"ident":"getTime"}]}')
+```
+
+---
+
+### Gacha
+
+#### gacha_refill
+
+Refill gacha (hero summoning system). This API call refreshes the gacha system and provides rewards.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "gacha_refill",
+ args: {
+ ident: "heroGacha" // Identifier for the gacha type
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+})
+```
+
+**Response Structure:**
+```javascript
+{
+ "date": 1763321343.088197,
+ "results": [
+ {
+ "ident": "body",
+ "result": {
+ "response": {
+ "coin": {
+ "38": 1 // Coin ID and amount received
+ }
+ }
+ }
+ }
+ ]
+}
+```
+
+**Response Fields:**
+- `coin`: Object containing coin rewards received
+ - Key: Coin ID (number)
+ - Value: Amount received (number)
+ - Example: `{"38": 1}` means 1 unit of coin ID 38 was received
+
+**Example Usage:**
+```javascript
+const response = await Send({
+ calls: [{
+ name: "gacha_refill",
+ args: { ident: "heroGacha" },
+ ident: "body"
+ }]
+});
+
+const coins = response.results[0].result.response.coin;
+console.log('Received coins:', coins);
+```
+
+---
+
+### Hero GotCha
+
+**Note:** The `heroGotCha` API was not found in the provided HAR file. This section will be updated when API calls for this feature are captured.
+
+If you have HAR file data containing `heroGotCha` API calls, please provide it for documentation.
+
+---
+
+### Subscription and Daily Rewards
+
+#### subscriptionFarm
+
+Farm subscription rewards.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"subscriptionFarm","args":{},"ident":"body"}]}')
+```
+
+#### zeppelinGiftFarm
+
+Farm zeppelin gift rewards.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"zeppelinGiftFarm","args":{},"ident":"zeppelinGiftFarm"}]}')
+```
+
+#### grandFarmCoins
+
+Farm grand coins.
+
+**Request:**
+```javascript
+Send('{"calls":[{"name":"grandFarmCoins","args":{},"ident":"grandFarmCoins"}]}')
+```
+
+---
+
+### Hero Talents
+
+#### heroTalent_getReward
+
+Get hero talent reward.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "heroTalent_getReward",
+ args: {
+ talentType: "tmntDungeonTalent",
+ reroll: false
+ },
+ ident: "body"
+ }]
+}))
+```
+
+#### heroTalent_farmReward
+
+Farm hero talent reward.
+
+**Request:**
+```javascript
+Send(JSON.stringify({
+ calls: [{
+ name: "heroTalent_farmReward",
+ args: {
+ talentType: "tmntDungeonTalent"
+ },
+ ident: "body"
+ }]
+}))
+```
+
+---
+
+## Common Patterns
+
+### Batching Multiple Calls
+
+When you need data from multiple APIs, batch them in a single request:
+
+```javascript
+const [userInfo, inventory, shops] = await Send({
+ calls: [
+ { name: 'userGetInfo', args: {}, ident: 'userGetInfo' },
+ { name: 'inventoryGet', args: {}, ident: 'inventoryGet' },
+ { name: 'shopGetAll', args: {}, ident: 'shopGetAll' }
+ ]
+}).then(e => e.results.map(r => r.result.response));
+```
+
+### Error Handling
+
+Always check for errors:
+
+```javascript
+const response = await Send({ calls: [...] });
+
+if (response.error) {
+ console.error('API Error:', response.error);
+ throw new Error(`API error: ${response.error.name} - ${response.error.description}`);
+}
+
+if (!response.results || !response.results[0] || !response.results[0].result) {
+ throw new Error('Invalid response format');
+}
+```
+
+### Using Caller Class
+
+The `Caller` class provides a cleaner interface:
+
+```javascript
+const caller = new Caller(['userGetInfo', 'inventoryGet']);
+await caller.send();
+const userInfo = caller.result('userGetInfo');
+const inventory = caller.result('inventoryGet');
+```
+
+### Dynamic Call Building
+
+Build calls dynamically based on conditions:
+
+```javascript
+const calls = [];
+for (const slot of availableSlots) {
+ if (!slot.bought && canAfford(slot)) {
+ calls.push({
+ name: "shopBuy",
+ args: {
+ shopId: shop.id,
+ slot: slot.id,
+ cost: slot.cost,
+ reward: slot.reward
+ },
+ ident: `shopBuy_${shop.id}_${slot.id}`
+ });
+ }
+}
+
+if (calls.length > 0) {
+ const results = await Send(JSON.stringify({ calls }))
+ .then(e => e.results.map(n => n.result.response));
+}
+```
+
+---
+
+## Notes
+
+1. **Timestamps**: The `context.actionTs` field is automatically added if missing, using `Math.floor(performance.now())`.
+
+2. **Identifiers**: For single calls, use `"body"` as the ident. For multiple calls, use unique identifiers to map responses correctly.
+
+3. **Response Mapping**: Responses are returned in the same order as requests, but use the `ident` field to reliably map responses.
+
+4. **Error Responses**: Always check for `response.error` before accessing `response.results`.
+
+5. **String vs Object**: The `Send` function accepts both JSON strings and JavaScript objects. Objects are automatically stringified.
+
+6. **Authentication**: All requests use authentication headers captured from previous intercepted requests. The `Send` function automatically handles header management including signature calculation.
+
+---
+
+## Specialized API Documentation
+
+The following sections provide detailed documentation for specialized game modes and features.
+
+---
+
+## Arena API
+
+### Overview
+
+The Arena API provides functionality for regular arena battles, including finding opponents, checking target availability, and executing attacks.
+
+### Endpoints
+
+#### arenaFindEnemies
+
+**Description:** Retrieves a list of available opponents in the arena with their complete team lineups, including heroes, pets, banners, and user information.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "arenaFindEnemies",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- No parameters required (empty `args` object)
+
+**Response Structure:**
+```javascript
+{
+ date: 1763940414.8791299, // Server timestamp
+ results: [{
+ ident: "body",
+ result: {
+ response: [
+ {
+ userId: "35918555", // Opponent's user ID (string)
+ place: "24", // Opponent's rank/position (string)
+ power: "1160718", // Total team power (string)
+ heroes: [ // Array of hero/pet objects (6 items: 5 heroes + 1 pet)
+ {
+ id: 6, // Hero ID
+ level: 130, // Hero level
+ color: 18, // Evolution color/rank
+ star: 6 // Star level
+ },
+ {
+ id: 9,
+ level: 130,
+ color: 18,
+ star: 6
+ },
+ {
+ id: 56,
+ level: 130,
+ color: 18,
+ star: 6
+ },
+ {
+ id: 49,
+ level: 130,
+ color: 18,
+ star: 6
+ },
+ {
+ id: 50,
+ level: 130,
+ color: 18,
+ star: 6
+ },
+ {
+ id: 6006, // Pet ID (6000-6999 range)
+ level: 130, // Pet level
+ color: 10, // Pet evolution
+ star: 6, // Pet star level
+ type: "pet" // Indicates this is a pet
+ }
+ ],
+ banners: [ // Array of banner configurations
+ {
+ id: 6, // Banner ID
+ slots: { // Banner slot configuration
+ "0": 66, // Slot 0 value
+ "1": 20, // Slot 1 value
+ "2": 31 // Slot 2 value
+ }
+ // OR alternative format:
+ // slots: [9, 19, 41] // Array format for slots
+ }
+ ],
+ user: { // Opponent's user information
+ id: "35918555", // User ID
+ name: "Мир", // Player name
+ lastLoginTime: "1763923472", // Last login timestamp
+ serverId: "218", // Server ID
+ level: "130", // Player level
+ clanId: "268348", // Clan ID
+ clanRole: "4", // Clan role (4 = member, etc.)
+ commander: true, // Is clan commander
+ avatarId: "992", // Avatar ID
+ isChatModerator: false, // Chat moderator status
+ frameId: 51, // Profile frame ID
+ leagueId: 3, // League ID
+ allowPm: "all", // PM permission setting
+ clanTitle: "МИР", // Clan name
+ clanIcon: { // Clan icon configuration
+ flagColor1: 0, // Flag color 1
+ flagColor2: 0, // Flag color 2
+ flagShape: 3, // Flag shape
+ iconColor: 19, // Icon color
+ iconShape: 44 // Icon shape
+ }
+ }
+ }
+ // ... more opponents
+ ]
+ }
+ }]
+}
+```
+
+**Response Fields:**
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `date` | Number | Server timestamp of the response |
+| `results[].ident` | String | Request identifier ("body") |
+| `results[].result.response` | Array | Array of opponent objects |
+| `response[].userId` | String | Opponent's user ID |
+| `response[].place` | String | Opponent's current rank/position in arena |
+| `response[].power` | String | Total team power |
+| `response[].heroes` | Array | Array of 6 objects (5 heroes + 1 pet) |
+| `response[].heroes[].id` | Number | Hero/Pet ID (1-999 for heroes, 6000-6999 for pets) |
+| `response[].heroes[].level` | Number | Hero/Pet level |
+| `response[].heroes[].color` | Number | Evolution color/rank |
+| `response[].heroes[].star` | Number | Star level |
+| `response[].heroes[].type` | String | "pet" if this is a pet (optional, only on pet objects) |
+| `response[].banners` | Array | Banner configurations |
+| `response[].banners[].id` | Number | Banner ID |
+| `response[].banners[].slots` | Object/Array | Banner slot configuration (object with keys "0", "1", "2" or array) |
+| `response[].user` | Object | Opponent's user information |
+| `response[].user.id` | String | User ID |
+| `response[].user.name` | String | Player name |
+| `response[].user.level` | String | Player level |
+| `response[].user.clanId` | String | Clan ID |
+| `response[].user.clanTitle` | String | Clan name |
+| `response[].user.clanIcon` | Object | Clan icon configuration |
+
+**Usage Notes:**
+- The `heroes` array always contains 6 items: the first 5 are heroes, the last one is the pet
+- Pet objects may have a `type: "pet"` field to distinguish them from heroes
+- Banner `slots` can be either an object with string keys ("0", "1", "2") or an array
+- The `place` field indicates the opponent's current rank, useful for sorting opponents by difficulty
+- The `power` field can be used to estimate battle difficulty
+
+#### arenaAttack
+
+**Description:** Initiates an attack against an opponent in regular arena.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "arenaAttack",
+ args: {
+ userId: 60332840,
+ heroes: [57, 31, 55, 40, 16],
+ pet: 6008,
+ favor: {
+ "16": 6004,
+ "31": 6006,
+ "55": 6001,
+ "57": 6003
+ },
+ banners: [6]
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `userId` (number): Target opponent's user ID
+- `heroes` (array): Array of 5 hero IDs
+- `pet` (number): Pet ID to use in battle
+- `favor` (object): Favor pet assignments (hero ID → pet ID mapping)
+- `banners` (array): Banner IDs to use in battle
+
+**Response:** Includes detailed battle information, battle results, updated arena state, and available enemies.
+
+#### arenaCheckTargetRange
+
+**Description:** Validates if target opponents are still in valid attack range. This is useful before attacking to ensure opponents haven't moved out of range.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "arenaCheckTargetRange",
+ args: {
+ ids: ["35918555", "59891179", "48582751"] // Array of user IDs as strings
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `ids` (array of strings): Array of opponent user IDs to check
+
+**Response Structure:**
+```javascript
+{
+ date: 1763940423.0623381, // Server timestamp
+ results: [{
+ ident: "body",
+ result: {
+ response: {
+ "35918555": true, // User ID → boolean (true = attackable, false = not in range)
+ "59891179": true,
+ "48582751": true
+ }
+ }
+ }]
+}
+```
+
+**Response Fields:**
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `date` | Number | Server timestamp of the response |
+| `results[].ident` | String | Request identifier ("body") |
+| `results[].result.response` | Object | Object mapping user IDs (strings) to boolean values |
+| `response[userId]` | Boolean | `true` if opponent is attackable, `false` if not in range |
+
+**Usage Notes:**
+- User IDs in the request must be strings (not numbers)
+- Returns `true` if the opponent is still in valid attack range
+- Returns `false` if the opponent has moved out of range or is no longer attackable
+- Always check this before attacking to avoid wasting attempts on invalid targets
+
+---
+
+## Grand Arena API
+
+### Overview
+
+The Grand Arena API provides functionality for Grand Arena battles, which use 3 teams instead of 1.
+
+### Key Differences from Regular Arena
+
+1. **Multiple Teams**: Grand Arena uses 3 teams instead of 1
+2. **Team Structure**: Heroes are organized in arrays of arrays (3 teams)
+3. **Pet Assignment**: Each team has its own pet configuration
+4. **Banner Configuration**: Each team can have different banners
+
+### Endpoints
+
+#### grandFindEnemies
+
+**Description:** Finds available opponents in Grand Arena with their complete team lineups (3 teams per opponent).
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "grandFindEnemies",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- No parameters required (empty `args` object)
+
+**Response Structure:**
+```javascript
+{
+ date: 1763940414.8791299, // Server timestamp
+ results: [{
+ ident: "body",
+ result: {
+ response: [
+ {
+ userId: "35918555", // Opponent's user ID (string)
+ place: "24", // Opponent's rank/position (string)
+ power: "1160718", // Total team power (string)
+ heroes: [ // Array of 3 teams (each team has 6 items: 5 heroes + 1 pet)
+ [ // Team 1
+ { id: 58, level: 130, color: 18, star: 6 },
+ { id: 1, level: 130, color: 18, star: 6 },
+ { id: 64, level: 130, color: 18, star: 6 },
+ { id: 13, level: 130, color: 18, star: 6 },
+ { id: 55, level: 130, color: 18, star: 6 },
+ { id: 6006, level: 130, color: 10, star: 6, type: "pet" }
+ ],
+ [ // Team 2
+ { id: 42, level: 130, color: 18, star: 6 },
+ { id: 56, level: 130, color: 18, star: 6 },
+ { id: 9, level: 130, color: 18, star: 6 },
+ { id: 62, level: 130, color: 18, star: 6 },
+ { id: 43, level: 130, color: 18, star: 6 },
+ { id: 6005, level: 130, color: 10, star: 6, type: "pet" }
+ ],
+ [ // Team 3
+ { id: 16, level: 130, color: 18, star: 6 },
+ { id: 31, level: 130, color: 18, star: 6 },
+ { id: 57, level: 130, color: 18, star: 6 },
+ { id: 40, level: 130, color: 18, star: 6 },
+ { id: 48, level: 130, color: 18, star: 6 },
+ { id: 6004, level: 130, color: 10, star: 6, type: "pet" }
+ ]
+ ],
+ banners: [ // Array of 3 banner configurations (one per team)
+ { id: 1, slots: {...} }, // Banner for team 1
+ { id: 6, slots: {...} }, // Banner for team 2
+ { id: 2, slots: {...} } // Banner for team 3
+ ],
+ user: { // Opponent's user information (same structure as arena)
+ id: "35918555",
+ name: "PlayerName",
+ // ... same user fields as arenaFindEnemies
+ }
+ }
+ // ... more opponents
+ ]
+ }
+ }]
+}
+```
+
+**Response Fields:**
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `date` | Number | Server timestamp of the response |
+| `results[].ident` | String | Request identifier ("body") |
+| `results[].result.response` | Array | Array of opponent objects |
+| `response[].userId` | String | Opponent's user ID |
+| `response[].place` | String | Opponent's current rank/position in grand arena |
+| `response[].power` | String | Total team power |
+| `response[].heroes` | Array | Array of 3 teams (each team is an array of 6 hero/pet objects) |
+| `response[].heroes[teamIndex]` | Array | Team array containing 5 heroes + 1 pet |
+| `response[].banners` | Array | Array of 3 banner configurations (one per team) |
+| `response[].user` | Object | Opponent's user information (same structure as arena) |
+
+**Usage Notes:**
+- Grand Arena uses 3 teams instead of 1
+- Each team in the `heroes` array contains 6 items: 5 heroes followed by 1 pet
+- The `banners` array contains 3 banner configurations, one for each team
+- Team structure: `heroes[0]` = Team 1, `heroes[1]` = Team 2, `heroes[2]` = Team 3
+
+#### grandAttack
+
+**Description:** Initiates a Grand Arena battle against an opponent.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "grandAttack",
+ args: {
+ userId: 47308606,
+ heroes: [
+ [58, 1, 64, 13, 55], // Team 1
+ [42, 56, 9, 62, 43], // Team 2
+ [16, 31, 57, 40, 48] // Team 3
+ ],
+ pets: [6006, 6005, 6004], // Pet for each team
+ favor: {
+ "1": 6002,
+ "9": 6005,
+ // ... more favor assignments
+ },
+ banners: [1, 6, 2] // Banner for each team
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `userId` (number): Target opponent's user ID
+- `heroes` (array): Array of 3 hero teams (each team is an array of 5 hero IDs)
+- `pets` (array): Array of 3 pet IDs (one for each team)
+- `favor` (object): Favor pet assignments across all teams
+- `banners` (array): Array of 3 banner IDs (one for each team)
+
+#### grandCheckTargetRange
+
+**Description:** Checks if specific opponents are still available for attack in Grand Arena. This is useful before attacking to ensure opponents haven't moved out of range.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "grandCheckTargetRange",
+ args: {
+ ids: ["48705148", "35986432", "47308606"] // Array of user IDs as strings
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `ids` (array of strings): Array of opponent user IDs to check
+
+**Response Structure:**
+```javascript
+{
+ date: 1763940423.0623381, // Server timestamp
+ results: [{
+ ident: "body",
+ result: {
+ response: {
+ "48705148": true, // User ID → boolean (true = attackable, false = not in range)
+ "35986432": true,
+ "47308606": true
+ }
+ }
+ }]
+}
+```
+
+**Response Fields:**
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `date` | Number | Server timestamp of the response |
+| `results[].ident` | String | Request identifier ("body") |
+| `results[].result.response` | Object | Object mapping user IDs (strings) to boolean values |
+| `response[userId]` | Boolean | `true` if opponent is attackable, `false` if not in range |
+
+**Usage Notes:**
+- User IDs in the request must be strings (not numbers)
+- Returns `true` if the opponent is still in valid attack range
+- Returns `false` if the opponent has moved out of range or is no longer attackable
+- Always check this before attacking to avoid wasting attempts on invalid targets
+
+---
+
+## Guild War API
+
+### Overview
+
+Guild War (API uses `clanWar` prefix) is a clan-based PvP system where clans compete against each other by attacking defensive slots. The system involves multiple API calls for getting war information, defense data, and executing attacks.
+
+**Note:** The API endpoints use the `clanWar` prefix (e.g., `clanWarGetInfo`, `clanWarAttack`), but this refers to the **Guild War** game mode.
+
+### Endpoints
+
+#### clanWarGetInfo / clanWarGetDefence
+
+**Description:** Retrieves current Guild War information including available slots and team data. These calls are typically combined in a single request.
+
+**Request:**
+```javascript
+Send({
+ calls: [
+ {
+ name: "clanWarGetDefence",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "body"
+ },
+ {
+ name: "clanWarGetInfo",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "clanWarGetInfo"
+ }
+ ]
+})
+```
+
+**Response States:**
+
+The API response structure differs depending on whether Guild War is **active** or **inactive**:
+
+**When Guild War is INACTIVE:**
+The response contains only basic timing information:
+- `season` (string): Current war season identifier (e.g., "202546")
+- `day` (string): Current day (typically "0" when inactive)
+- `endTime` (number): Unix timestamp when the last war ended
+- `nextWarTime` (number): Unix timestamp when the next war starts
+- `nextLockTime` (number): Unix timestamp when the next war locks
+
+**When Guild War is ACTIVE:**
+The response includes all the fields documented below, including enemy clan information, attack attempts (`myTries`), enemy slots, etc.
+
+**Response Fields (Active State Only):**
+
+**War Information:**
+- `avgLevel` (string): Average level of clan members
+- `season` (string): Current war season identifier (e.g., "202547")
+- `day` (string): Current day of the war (e.g., "1")
+- `league` (string): League level (e.g., "1")
+- `enemyId` (string): ID of the enemy clan
+- `points` (string): Your clan's current war points
+- `enemyPoints` (string): Enemy clan's current war points
+
+**Enemy Clan Information:**
+- `enemyClan` (object): Complete enemy clan information
+ - `id` (string): Clan ID
+ - `ownerId` (string): Clan owner's user ID
+ - `level` (string): Clan level
+ - `title` (string): Clan name
+ - `description` (string): Clan description
+ - `icon` (object): Clan icon configuration
+ - `flagColor1` (number): First flag color
+ - `flagColor2` (number): Second flag color
+ - `flagShape` (number): Flag shape ID
+ - `iconColor` (number): Icon color ID
+ - `iconShape` (number): Icon shape ID
+ - `country` (string): Country code
+ - `minLevel` (string): Minimum level requirement
+ - `serverId` (string): Server ID
+ - `membersCount` (string): Number of clan members
+ - `disbanding` (boolean): Whether clan is disbanding
+ - `topActivity` (string): Top activity score
+ - `topDungeon` (string): Top dungeon score
+ - `roleNames` (array): Role names array
+ - `frameId` (number): Frame ID
+
+**Enemy Clan Members:**
+- `enemyClanMembers` (object): Map of user IDs (as string keys) to member information
+ - **Structure:** Each key is a user ID string, and the value is a member object
+ - Each member object contains:
+ - `id` (string): User ID (same as the map key)
+ - `name` (string): Player name
+ - `lastLoginTime` (string): Unix timestamp of last login
+ - `serverId` (string): Server ID
+ - `level` (string): Player level
+ - `clanId` (string): Clan ID
+ - `clanRole` (string): Role in clan
+ - `"255"`: Clan leader/owner
+ - `"4"`: Commander
+ - `"3"`: Officer
+ - `"2"`: Member
+ - `commander` (boolean): Whether player is a commander (true for commanders and leaders)
+ - `avatarId` (string): Avatar ID
+ - `isChatModerator` (boolean): Chat moderator status
+ - `frameId` (number): Frame ID
+ - `leagueId` (number): League ID
+ - `allowPm` (string): PM permission setting ("all", "friends", "none")
+ - `clanTitle` (string): Clan name
+ - `clanIcon` (object): Clan icon configuration (same structure as enemyClan.icon)
+ - `flagColor1` (number): First flag color
+ - `flagColor2` (number): Second flag color
+ - `flagShape` (number): Flag shape ID
+ - `iconColor` (number): Icon color ID
+ - `iconShape` (number): Icon shape ID
+
+**Attack Attempts:**
+- `myTries` (number, **conditional**): **Number of remaining Guild War attack attempts** (not stored in refillable system)
+ - **Important:** This field only exists when ClanWar is **active**. If there is no active war, this field will not be present in the response.
+- `clanTries` (object): Map of user IDs to their remaining attack attempts
+ - Each user ID maps to a number (0-2, typically 2 max attempts per day)
+ - `clan` (number): Total clan attempts used
+- `enemyClanTries` (object): Map of enemy user IDs to their remaining attack attempts
+ - Same structure as `clanTries`
+ - `clan` (number): Total enemy clan attempts used
+
+**Enemy Defense Slots:**
+- `enemySlots` (object): Map of slot IDs (1-40) to slot defense information
+ - Each slot contains:
+ - `team` (array): Array of team members (heroes or titans)
+ - Each team member is an object with position key ("1", "2", etc.)
+ - `id` (number): Hero or titan ID
+ - `star` (number): Star level
+ - `color` (number): Color/ascension level
+ - `level` (number): Unit level
+ - `power` (number): Unit power
+ - `type` (string): "hero" or "titan"
+ - `state` (object): Current battle state
+ - `hp` (number): Current HP
+ - `energy` (number): Current energy
+ - `isDead` (boolean): Whether unit is dead
+ - `maxHp` (number): Maximum HP
+
+**Defense Teams (from clanWarGetDefence):**
+- `slots`: Map of slot IDs (1-40) to defending player IDs
+- `teams`: Team configurations for different players
+ - `clanDefence_heroes`: Hero defense team for Guild War
+ - `clanDefence_titans`: Titan defense team for Guild War
+- `arePointsMax`: Boolean indicating if maximum points have been reached
+
+**Note:** Unlike Arena and Grand Arena which track attempts in the `refillable` array, Guild War attempts are tracked directly in the `clanWarGetInfo` response as `myTries`.
+
+**Example Usage:**
+
+**Checking War Status:**
+```javascript
+const response = await Send({
+ calls: [
+ { name: "clanWarGetInfo", args: {}, ident: "clanWarGetInfo" }
+ ]
+});
+
+const guildWarInfo = response.results[0].result.response;
+
+// Check if war is active or inactive
+const isActive = 'myTries' in guildWarInfo || 'enemyClan' in guildWarInfo;
+
+if (!isActive) {
+ // War is inactive - only timing information available
+ console.log('Guild War is currently inactive');
+ console.log(`Season: ${guildWarInfo.season}`);
+ console.log(`Day: ${guildWarInfo.day}`);
+ console.log(`Last war ended: ${new Date(guildWarInfo.endTime * 1000).toLocaleString()}`);
+ console.log(`Next war starts: ${new Date(guildWarInfo.nextWarTime * 1000).toLocaleString()}`);
+ console.log(`Next war locks: ${new Date(guildWarInfo.nextLockTime * 1000).toLocaleString()}`);
+} else {
+ // War is active - full information available
+ console.log('Guild War is active');
+
+ // Check attack attempts (only exists when war is active)
+ if ('myTries' in guildWarInfo) {
+ const attemptsRemaining = guildWarInfo.myTries ?? 0;
+ console.log(`Guild War attempts remaining: ${attemptsRemaining}`);
+ }
+
+ // Get war information (only available when active)
+ if ('league' in guildWarInfo) {
+ console.log(`Season: ${guildWarInfo.season}, Day: ${guildWarInfo.day}, League: ${guildWarInfo.league}`);
+ console.log(`Points: ${guildWarInfo.points} vs ${guildWarInfo.enemyPoints}`);
+ }
+
+ // Get enemy clan information (only available when active)
+ if ('enemyClan' in guildWarInfo) {
+ const enemyClan = guildWarInfo.enemyClan;
+ console.log(`Enemy Clan: ${enemyClan.title} (Level ${enemyClan.level}, ${enemyClan.membersCount} members)`);
+
+ // Get enemy clan members (only available when active)
+ if ('enemyClanMembers' in guildWarInfo) {
+ const enemyMembers = guildWarInfo.enemyClanMembers;
+ console.log(`Enemy has ${Object.keys(enemyMembers).length} members`);
+
+ // Iterate through enemy clan members with detailed information
+ for (const [userId, member] of Object.entries(enemyMembers)) {
+ console.log(`\nMember: ${member.name} (ID: ${member.id})`);
+ console.log(` Level: ${member.level}`);
+ console.log(` Server ID: ${member.serverId}`);
+
+ // Role information
+ let roleName = 'Member';
+ if (member.clanRole === "255") roleName = 'Leader/Owner';
+ else if (member.clanRole === "4") roleName = 'Commander';
+ else if (member.clanRole === "3") roleName = 'Officer';
+ console.log(` Role: ${roleName} (clanRole: ${member.clanRole})`);
+ console.log(` Is Commander: ${member.commander}`);
+
+ // Activity information
+ const lastLogin = new Date(parseInt(member.lastLoginTime) * 1000);
+ const daysSinceLogin = Math.floor((Date.now() - lastLogin.getTime()) / (1000 * 60 * 60 * 24));
+ console.log(` Last Login: ${lastLogin.toLocaleString()} (${daysSinceLogin} days ago)`);
+
+ // Profile information
+ console.log(` Avatar ID: ${member.avatarId}`);
+ console.log(` Frame ID: ${member.frameId}`);
+ console.log(` League ID: ${member.leagueId}`);
+ console.log(` PM Allowed: ${member.allowPm}`);
+ console.log(` Chat Moderator: ${member.isChatModerator}`);
+
+ // Clan information
+ console.log(` Clan: ${member.clanTitle} (ID: ${member.clanId})`);
+ console.log(` Clan Icon: flagColor1=${member.clanIcon.flagColor1}, flagColor2=${member.clanIcon.flagColor2}, flagShape=${member.clanIcon.flagShape}`);
+ }
+
+ // Find commanders and leaders
+ const commanders = Object.values(enemyMembers).filter(m => m.commander);
+ const leaders = Object.values(enemyMembers).filter(m => m.clanRole === "255");
+ const officers = Object.values(enemyMembers).filter(m => m.clanRole === "3");
+ const regularMembers = Object.values(enemyMembers).filter(m => m.clanRole === "2");
+
+ console.log(`\nEnemy clan structure:`);
+ console.log(` Leaders: ${leaders.length}`);
+ console.log(` Commanders: ${commanders.length}`);
+ console.log(` Officers: ${officers.length}`);
+ console.log(` Regular Members: ${regularMembers.length}`);
+
+ // Find most active members (recent login)
+ const activeMembers = Object.values(enemyMembers)
+ .filter(m => {
+ const lastLogin = parseInt(m.lastLoginTime) * 1000;
+ const daysSinceLogin = (Date.now() - lastLogin) / (1000 * 60 * 60 * 24);
+ return daysSinceLogin <= 7; // Active within last 7 days
+ })
+ .sort((a, b) => parseInt(b.lastLoginTime) - parseInt(a.lastLoginTime));
+
+ console.log(`\nMost active members (last 7 days): ${activeMembers.length}`);
+ activeMembers.slice(0, 5).forEach(m => {
+ const lastLogin = new Date(parseInt(m.lastLoginTime) * 1000);
+ console.log(` ${m.name} (Level ${m.level}) - Last login: ${lastLogin.toLocaleString()}`);
+ });
+
+ // Find highest level members
+ const topLevelMembers = Object.values(enemyMembers)
+ .sort((a, b) => parseInt(b.level) - parseInt(a.level))
+ .slice(0, 5);
+
+ console.log(`\nTop 5 highest level members:`);
+ topLevelMembers.forEach(m => {
+ console.log(` ${m.name} - Level ${m.level}, Power: ${m.power || 'N/A'}`);
+ });
+ }
+
+ // Get enemy defense slots (only available when active)
+ if ('enemySlots' in guildWarInfo) {
+ const enemySlots = guildWarInfo.enemySlots;
+ for (const [slotId, slotData] of Object.entries(enemySlots)) {
+ const slotNum = parseInt(slotId);
+ const battleType = slotNum <= 20 ? 'Hero' : 'Titan';
+ const team = slotData.team;
+ console.log(`Slot ${slotId} (${battleType}): ${team.length} units`);
+
+ // Access individual team members
+ team.forEach((memberObj, index) => {
+ const position = Object.keys(memberObj)[0];
+ const member = memberObj[position];
+ console.log(` Position ${position}: ${member.type} ID ${member.id}, Level ${member.level}, Power ${member.power}`);
+ });
+ }
+ }
+
+ // Get clan attack attempts (only available when active)
+ if ('clanTries' in guildWarInfo) {
+ const clanTries = guildWarInfo.clanTries;
+ console.log(`Total clan attempts used: ${clanTries.clan}`);
+ if ('enemyClanTries' in guildWarInfo) {
+ console.log(`Enemy clan attempts used: ${guildWarInfo.enemyClanTries.clan}`);
+ }
+ }
+ }
+}
+```
+
+#### clanWarAttack
+
+**Description:** Executes an attack against a specific Guild War slot. Can be used for both hero battles and titan battles.
+
+**Request (Hero Battle):**
+```javascript
+Send({
+ calls: [{
+ name: "clanWarAttack",
+ args: {
+ slotId: 1,
+ heroes: [46, 9, 40, 16, 65],
+ pet: 6004,
+ favor: {
+ "9": 6006,
+ "16": 6004
+ },
+ banner: 1
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Request (Titan Battle):**
+```javascript
+Send({
+ calls: [{
+ name: "clanWarAttack",
+ args: {
+ slotId: 8,
+ heroes: [4033, 4003, 4001, 4032, 4000], // Titan IDs
+ favor: {}
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `slotId` (number): Target slot ID to attack
+- `heroes` (array): Array of 5 unit IDs (heroes or titans depending on battle type)
+- `pet` (number, optional): Pet ID to use in battle (hero battles only)
+- `favor` (object, optional): Favor pet assignments (empty for titan battles)
+- `banner` (number, optional): Banner ID to use in battle (hero battles only)
+
+**Response:** Returns complete battle data including battle seed, attacker/defender stats, and battle type.
+
+#### clanWarEndBattle
+
+**Description:** Submits the battle result after completing a Guild War battle.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "clanWarEndBattle",
+ args: {
+ result: {
+ win: false,
+ stars: 0
+ },
+ progress: [{
+ v: 272,
+ b: 0,
+ seed: 1906504079, // Must match clanWarAttack response seed
+ attackers: {
+ input: ["auto", 0, 0, "auto", 0, 0],
+ heroes: {
+ "9": { hp: 376777, energy: 594, isDead: false }
+ // ... more heroes
+ }
+ },
+ defenders: {
+ input: [],
+ heroes: {
+ "1": { hp: 58106758, energy: 1000, isDead: false }
+ // ... more defenders
+ }
+ }
+ }]
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Response:** Returns updated slot information, victory points, and clan points.
+
+#### clanWarGetDayHistory
+
+**Description:** Retrieves the complete battle history for the current Guild War day, including all attacks and defenses that occurred during the war day, along with detailed replay information.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "clanWarGetDayHistory",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Response Fields:**
+
+**Top Level:**
+- `date` (number): Unix timestamp of the response
+- `results` (array): Array containing the API response
+ - `results[0].ident` (string): Request identifier ("body")
+ - `results[0].result.response` (object): Day history data
+
+**Day History Response (`response`):**
+
+**Attack Records:**
+- `attack` (array): Array of attack records that occurred during the war day
+ - Each attack record contains:
+ - `time` (number): Unix timestamp when the attack occurred
+ - `slotId` (string): Slot ID that was attacked (1-40)
+ - **Bridge slots:** Slots 7, 8, 9, and 34 are bridge slots (all titan battles)
+ - `previousStatus` (string): Status of the slot before attack (e.g., "inBattle")
+ - `attackerId` (string): User ID of the attacker
+ - `defenderId` (string): User ID of the defender
+ - `replayId` (string): Unique replay ID for this battle
+ - `win` (boolean): Whether the attacker won
+ - `fortificationPoints` (number): Fortification points earned (0, 20, 40, or 60)
+ - `slotPoints` (number): Slot points earned (typically 20)
+
+**Defense Records:**
+- `defence` (array): Array of defense records (failed attacks against your clan)
+ - Each defense record contains the same structure as attack records:
+ - `time` (number): Unix timestamp when the defense occurred
+ - `slotId` (string): Slot ID that was defended
+ - `previousStatus` (string): Status of the slot before defense
+ - `attackerId` (string): User ID of the attacker (enemy)
+ - `defenderId` (string): User ID of the defender (your clan member)
+ - `replayId` (string): Unique replay ID for this battle
+ - `win` (boolean): Whether the defender won (false means attacker lost)
+ - `fortificationPoints` (number): Fortification points (typically 0 for failed attacks)
+ - `slotPoints` (number): Slot points (typically 0-3 for failed attacks)
+
+**Average Level:**
+- `avgLevel` (string): Average level of clan members
+
+**User Information:**
+- `users` (object): Map of user IDs (as string keys) to user information
+ - Each user object contains:
+ - `id` (string): User ID
+ - `name` (string): Player name
+ - `lastLoginTime` (string): Unix timestamp of last login
+ - `serverId` (string): Server ID
+ - `level` (string): Player level
+ - `clanId` (string): Clan ID
+ - `clanRole` (string): Role in clan ("255" = leader, "4" = commander, "3" = officer, "2" = member)
+ - `commander` (boolean): Whether player is a commander
+ - `avatarId` (string): Avatar ID
+ - `isChatModerator` (boolean): Chat moderator status
+ - `frameId` (number): Frame ID
+ - `leagueId` (number): League ID
+ - `allowPm` (string): PM permission setting ("all", "friends", "none")
+ - `clanTitle` (string): Clan name
+ - `clanIcon` (object): Clan icon configuration
+ - `flagColor1` (number): First flag color
+ - `flagColor2` (number): Second flag color
+ - `flagShape` (number): Flag shape ID
+ - `iconColor` (number): Icon color ID
+ - `iconShape` (number): Icon shape ID
+
+**Battle Replays:**
+- `replays` (array): Array of detailed battle replay objects
+ - Each replay contains:
+ - `userId` (string): User ID of the attacker
+ - `typeId` (string): User ID of the defender
+ - `attackers` (object): Attacker team data
+ - Contains hero/titan objects with detailed stats (ID, XP, level, star, skills, power, skins, artifacts, etc.)
+ - `defenders` (object): Defender team data
+ - Contains hero/titan objects with detailed stats and battle state
+ - `effects` (array): Battle effects applied
+ - `reward` (array): Rewards earned
+ - `startTime` (string): Battle start timestamp
+ - `seed` (string): Battle seed for replay
+ - `type` (string): Battle type (e.g., "clan_pvp_titan" for titan battles)
+ - `id` (string): Replay ID
+ - `progress` (array): Battle progress frames
+ - `endTime` (string): Battle end timestamp
+ - `result` (object): Battle result
+ - `win` (boolean): Whether attacker won
+ - `stars` (number): Stars earned (0-3)
+ - `serverVersion` (number): Server version used
+
+**Example Usage:**
+
+```javascript
+const response = await Send({
+ calls: [{
+ name: "clanWarGetDayHistory",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+});
+
+const history = response.results[0].result.response;
+
+// Get all attacks
+const attacks = history.attack || [];
+console.log(`Total attacks: ${attacks.length}`);
+
+// Analyze attack results
+const successfulAttacks = attacks.filter(a => a.win);
+const failedAttacks = attacks.filter(a => !a.win);
+console.log(`Successful: ${successfulAttacks.length}, Failed: ${failedAttacks.length}`);
+
+// Get fortification points earned
+const totalFortPoints = attacks.reduce((sum, a) => sum + a.fortificationPoints, 0);
+console.log(`Total fortification points: ${totalFortPoints}`);
+
+// Get defenses (failed enemy attacks)
+const defenses = history.defence || [];
+console.log(`Total defenses: ${defenses.length}`);
+
+// Get user information
+const users = history.users || {};
+console.log(`Users involved: ${Object.keys(users).length}`);
+
+// Get replays
+const replays = history.replays || [];
+console.log(`Total replays: ${replays.length}`);
+
+// Find attacks by specific user
+const userId = "35891708";
+const userAttacks = attacks.filter(a => a.attackerId === userId);
+console.log(`User ${userId} made ${userAttacks.length} attacks`);
+
+// Find attacks on specific slot
+const slotId = "8";
+const slotAttacks = attacks.filter(a => a.slotId === slotId);
+console.log(`Slot ${slotId} was attacked ${slotAttacks.length} times`);
+```
+
+**Notes:**
+- This API returns the complete history for the current war day
+- Attack records are sorted by timestamp (oldest first)
+- Replay data contains full battle information including team compositions, stats, and battle progress
+- User information includes both your clan members and enemy clan members who participated in battles
+- Fortification points are awarded based on slot type: 0 for regular slots, 20/40/60 for fortification slots
+- **Bridge slots:** Slots 7, 8, 9, and 34 are bridge slots (all titan battles), which are strategic positions that connect different areas of the war map
+
+---
+
+## Clan Raid API (Minions Attack)
+
+### Overview
+
+Clan Raid (also known as **Minions Attack** or **Minion Raid**) is a cooperative PvE mode where clan members work together to defeat raid bosses. Multiple clan members can fight the same boss simultaneously, with damage persisting across all attempts.
+
+### Endpoints
+
+#### clanRaid_getInfo
+
+**Description:** Retrieves complete clan raid information including current boss, all bosses/nodes, shop, buffs, user stats, and rewards.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "clanRaid_getInfo",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Response Fields:**
+- `boss`: Current active boss information with phases and HP
+- `nodes`: All raid bosses/nodes (numbered 1-9+)
+- `shop`: Raid shop items available for purchase
+- `buffs`: Currently active buffs
+- `stats`: Clan and user statistics
+- `userStats`: Player's damage, points, and rewards
+- `attempts`: Remaining free attempts
+- `bossAttempts`: Boss-specific attempts remaining
+
+#### clanRaid_startBossBattle
+
+**Description:** Initiates a battle against a clan raid boss.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "clanRaid_startBossBattle",
+ args: {
+ heroes: [50, 42, 58, 51, 9],
+ pet: 6005,
+ favor: {
+ "9": 6004,
+ "42": 6006,
+ "50": 6005,
+ "51": 6001,
+ "58": 6008
+ }
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Response:** Returns detailed battle information with boss stats (can have multiple phases), player hero stats, battle seed, and battle type.
+
+#### clanRaid_endBossBattle
+
+**Description:** Submits the battle result after completing/ending a clan raid boss battle.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "clanRaid_endBossBattle",
+ args: {
+ result: {
+ win: false,
+ stars: 0
+ },
+ progress: [{
+ v: 272,
+ b: 0,
+ seed: -557779724, // Must match startBossBattle response seed
+ attackers: {
+ input: ["auto", 0, 0, "auto", 0, 0],
+ heroes: {
+ "9": { hp: 376777, energy: 594, isDead: false }
+ // ... more heroes
+ }
+ },
+ defenders: {
+ input: [],
+ heroes: {
+ "1": {
+ hp: 58106758,
+ energy: 1000,
+ isDead: false,
+ extra: {
+ damageTaken: 5628015,
+ damageTakenNextLevel: 0
+ }
+ }
+ // ... more phases
+ }
+ }
+ }]
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Response:** Returns damage dealt, total cumulative damage, raid currency earned, and quest updates.
+
+#### clanRaid_usersInBossBattle
+
+**Description:** Retrieves information about other clan members currently fighting the same boss.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "clanRaid_usersInBossBattle",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Response:** Returns array of users currently in battle with their user IDs, names, levels, and start times.
+
+### Battle Mechanics
+
+**Raid Boss Structure:**
+- Multi-Phase Bosses: Bosses can have multiple phases (typically 2), each with separate HP pools
+- Massive HP Pools: Boss HP ranges from ~287M to ~448M per phase
+- Persistent Damage: Damage persists across all clan members' attempts
+- Time Limit: Battles have an end time (typically 3 minutes)
+
+**Raid Effects:**
+- `percentDamageBuff_any`: Overall damage buff percentage
+- `bossAstralMaterialAuraReduction`: Reduces boss astral material aura
+- `bossAstralAntihealAuraReduction`: Reduces boss anti-heal effects
+- `bossAstralHealOnAttack`: Heal amount on attack
+- `bossAstralSwitcherCDReduce`: Cooldown reduction for switching
+- `bossAstralParalyseHealReduction`: Reduces heal when paralyzed
+
+---
+
+## Cross Clan War (COW) API
+
+### Overview
+
+Cross Clan War is a competitive mode where clans battle against each other across multiple slots. Supports both hero battles and titan battles.
+
+### Endpoints
+
+#### crossClanWar_getInfo
+
+**Description:** Retrieves information about the current Cross Clan War status, including available battles, opponent clans, and war state.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "crossClanWar_getInfo",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "group_1_body"
+ }]
+})
+```
+
+**Response Fields:**
+
+**Season and Timing Information:**
+- `season` (number): Current season number (e.g., 13)
+- `plannedSeason` (number): Planned/upcoming season number
+- `seasonEndTime` (number): Unix timestamp when current season ends
+- `nextSeasonStartTime` (number): Unix timestamp when next season starts
+- `nextWarTime` (number): Unix timestamp of next war start
+- `nextLockTime` (number): Unix timestamp when war locks (defense setup deadline)
+
+**War Status:**
+- `war` (object): Current war information (null if no active war)
+ - `id` (number): War ID
+ - `endTime` (number): Unix timestamp when war ends
+ - `enemyClan` (object): Enemy clan information
+ - `id` (string): Enemy clan ID
+ - `serverId` (string): Server ID where enemy clan is located
+ - `title` (string): Enemy clan name
+ - `icon` (object): Enemy clan icon configuration
+ - `flagColor1` (number): First flag color
+ - `flagColor2` (number): Second flag color
+ - `flagShape` (number): Flag shape ID
+ - `iconColor` (number): Icon color ID
+ - `iconShape` (number): Icon shape ID
+ - `myTries` (object): Your attack attempts information
+ - `heroes` (number): Remaining hero battle attempts
+ - `titans` (number): Remaining titan battle attempts
+ - `usedHeroes` (array): Array of slot IDs where hero battles were used
+ - `usedTitans` (array): Array of slot IDs where titan battles were used
+ - `points` (string): Your clan's current war points
+ - `enemyPoints` (string): Enemy clan's current war points
+
+**Defense Status:**
+- `defendedSlots` (number): Number of slots currently defended
+- `requiredDefendedSlots` (number): Minimum number of slots required to be defended
+
+**Settings:**
+- `settings` (object): War configuration settings
+ - `restrictAttackForeignOrder` (boolean): Whether attacks must follow a specific order
+ - `fillDefenceByCommander` (boolean): Whether defense is auto-filled by commanders
+
+**Rating and League:**
+- `rating` (string): Current clan rating
+- `division` (number): Current division number
+- `league` (number): Current league level
+- `maxLeague` (number): Maximum league level achieved
+
+**Example Usage:**
+```javascript
+const response = await Send({
+ calls: [{
+ name: "crossClanWar_getInfo",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "group_1_body"
+ }]
+});
+
+const cowInfo = response.results[0].result.response;
+
+// Check if war is active
+if (cowInfo.war) {
+ const war = cowInfo.war;
+ console.log(`War ID: ${war.id}`);
+ console.log(`Enemy Clan: ${war.enemyClan.title} (Server ${war.enemyClan.serverId})`);
+ console.log(`Points: ${war.points} vs ${war.enemyPoints}`);
+
+ // Check attack attempts
+ const myTries = war.myTries;
+ console.log(`Hero attempts remaining: ${myTries.heroes}`);
+ console.log(`Titan attempts remaining: ${myTries.titans}`);
+ console.log(`Used hero slots: ${myTries.usedHeroes.join(', ')}`);
+ console.log(`Used titan slots: ${myTries.usedTitans.join(', ')}`);
+
+ // Check war timing
+ const now = Math.floor(Date.now() / 1000);
+ const timeRemaining = war.endTime - now;
+ console.log(`War ends in: ${Math.floor(timeRemaining / 3600)} hours`);
+} else {
+ console.log('No active war');
+}
+
+// Season information
+console.log(`Season: ${cowInfo.season}`);
+console.log(`Rating: ${cowInfo.rating}`);
+console.log(`League: ${cowInfo.league}/${cowInfo.maxLeague}`);
+console.log(`Division: ${cowInfo.division}`);
+
+// Defense status
+console.log(`Defended slots: ${cowInfo.defendedSlots}/${cowInfo.requiredDefendedSlots}`);
+
+// Next war timing
+const nextWarTime = new Date(cowInfo.nextWarTime * 1000);
+console.log(`Next war starts: ${nextWarTime.toLocaleString()}`);
+```
+
+#### crossClanWar_getAttackMap
+
+**Description:** Retrieves attack map information showing attack attempts for all clan members in the current Cross Clan War. This API provides detailed information about which heroes and titans have been used by each clan member.
+
+**Request:**
+```javascript
+const calls = [{
+ name: "crossClanWar_getAttackMap",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "body"
+}];
+
+const response = await Send(JSON.stringify({calls}));
+const attackMapData = response.results[0].result.response;
+```
+
+**Response Structure:**
+```json
+{
+ "clanTries": {
+ "35448204": {
+ "heroes": 3,
+ "titans": 2,
+ "usedHeroes": [],
+ "usedTitans": []
+ },
+ "35538758": {
+ "heroes": 3,
+ "titans": 1,
+ "usedHeroes": [],
+ "usedTitans": [4012, 4033, 4013, 4043, 4010]
+ },
+ "35621043": {
+ "heroes": 3,
+ "titans": 1,
+ "usedHeroes": [],
+ "usedTitans": [4042, 4023, 4043, 4024, 4020]
+ },
+ "35979991": {
+ "heroes": 0,
+ "titans": 0,
+ "usedHeroes": [62, 29, 58, 40, 56, 31, 55, 64, 13, 1, 46, 63, 9, 48, 16],
+ "usedTitans": [4033, 4003, 4001, 4032, 4000, 4013, 4043, 4031, 4010, 4030]
+ }
+ },
+ "targets": {
+ "1": {
+ "userId": 35979991,
+ "teamIndex": 2,
+ "state": 0
+ },
+ "7": {
+ "userId": 35979991,
+ "teamIndex": 0,
+ "state": 0
+ },
+ "16": {
+ "userId": 35538758,
+ "teamIndex": 0,
+ "state": 1
+ },
+ "17": {
+ "userId": 35449277,
+ "teamIndex": 1,
+ "state": 1
+ },
+ "24": {
+ "userId": 35979991,
+ "teamIndex": 1,
+ "state": 0
+ },
+ "42": {
+ "userId": 35979991,
+ "teamIndex": 1,
+ "state": 1
+ }
+ },
+ "enemySlots": {
+ "7": {
+ "id": 7,
+ "user": {
+ "id": "274748940",
+ "name": "Natan",
+ "level": "130",
+ "serverId": "319"
+ },
+ "team": {
+ "1": {
+ "state": {
+ "hp": 449732,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 449732
+ },
+ "id": 16,
+ "star": 6,
+ "level": 130,
+ "power": 203762,
+ "type": "hero"
+ },
+ "2": {
+ "state": {
+ "hp": 639192,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 639192
+ },
+ "id": 63,
+ "star": 6,
+ "level": 130,
+ "power": 206787,
+ "type": "hero"
+ }
+ },
+ "status": "ready",
+ "attackerId": null,
+ "pointsFarmed": 0,
+ "pointsTotal": 35
+ }
+ }
+}
+```
+
+**Response Fields:**
+
+- `clanTries` (object): Object mapping user IDs (as strings) to their attack attempt information
+ - Keys are user IDs as strings (e.g., `"35448204"`)
+ - Values are objects containing:
+ - `heroes` (number): Remaining hero battle attempts for this user
+ - `titans` (number): Remaining titan battle attempts for this user
+ - `usedHeroes` (array): Array of hero IDs that have been used in battles by this user
+ - Empty array `[]` if no heroes have been used yet
+ - Contains hero IDs (numbers) that have been deployed in battles
+ - `usedTitans` (array): Array of titan IDs that have been used in battles by this user
+ - Empty array `[]` if no titans have been used yet
+ - Contains titan IDs (numbers) that have been deployed in battles
+
+- `targets` (object): Object mapping target slot IDs to attack assignments in the Cross Clan War
+ - Keys are slot IDs as strings (e.g., `"1"`, `"7"`, `"16"`, `"17"`)
+ - Lower slot IDs (typically 1-16) are usually hero battles
+ - Higher slot IDs (typically 17+) are usually titan battles
+ - Each target object contains:
+ - `userId` (number): User ID of the player assigned to attack this target
+ - `teamIndex` (number): Index of the attacking player's defense team to use (0-based)
+ - **For hero battles**: Maps to the assigned player's `crossClanDefence_heroes[teamIndex]` from `teamGetAll` API
+ - `teamIndex: 0` → `crossClanDefence_heroes[0]` (first hero team of the assigned attacker)
+ - `teamIndex: 1` → `crossClanDefence_heroes[1]` (second hero team of the assigned attacker)
+ - `teamIndex: 2` → `crossClanDefence_heroes[2]` (third hero team of the assigned attacker)
+ - **For titan battles**: Maps to the assigned player's `crossClanDefence_titans[teamIndex]` from `teamGetAll` API
+ - `teamIndex: 0` → `crossClanDefence_titans[0]` (first titan team of the assigned attacker)
+ - `teamIndex: 1` → `crossClanDefence_titans[1]` (second titan team of the assigned attacker)
+ - `state` (number): Attack status
+ - `0` = Available for attack (not yet completed)
+ - `1` = Complete (attack has been finished)
+
+- `enemySlots` (object): Detailed information about enemy defense slots
+ - Keys are slot IDs as strings (e.g., `"7"`, `"16"`, `"17"`)
+ - Each slot object contains:
+ - `id` (number): Slot ID
+ - `user` (object): Enemy player information defending this slot
+ - `id` (string): User ID
+ - `name` (string): Player name
+ - `level` (string): Player level
+ - `serverId` (string): Server ID
+ - Additional user profile fields (avatarId, clanTitle, etc.)
+ - `team` (object): Defense team configuration
+ - Keys are position numbers as strings (`"1"`, `"2"`, `"3"`, `"4"`, `"5"`, `"6"`)
+ - Each position contains:
+ - `id` (number): Hero, titan, or pet ID
+ - `type` (string): Unit type - **"hero"**, **"titan"**, or **"pet"**
+ - **Important**: Check the `type` field to determine battle type
+ - If any unit has `type: "hero"` → Use `crossClanDefence_heroes` to attack
+ - If units have `type: "titan"` → Use `crossClanDefence_titans` to attack
+ - `state` (object): Current battle state of the unit
+ - `hp` (number): Current HP
+ - `energy` (number): Current energy
+ - `isDead` (boolean): Whether unit is dead
+ - `false` = Unit is alive, good to attack
+ - `true` = Unit is dead, already defeated
+ - `maxHp` (number): Maximum HP
+ - `star` (number): Star level
+ - `color` (number): Color/ascension level
+ - `level` (number): Unit level
+ - `power` (number): Unit power
+ - Additional fields for titans: `element`, `elementSpiritLevel`, `elementSpiritStar`, `elementSpiritSkills`
+ - `banner` (object, optional): Banner configuration for hero battles
+ - `id` (number): Banner ID
+ - `slots` (object): Banner stone slots
+ - `status` (string): Slot status
+ - `"ready"` = Available for attack
+ - Other statuses may indicate slot is locked or unavailable
+ - `attackerId` (number | null): User ID of player currently attacking this slot
+ - `null` = No one is currently attacking (available)
+ - Number = User ID of the attacker (slot is being attacked)
+ - `replayId` (number | null): Replay ID if battle has been completed
+ - `pointsFarmed` (number): Points already farmed from this slot
+ - `pointsTotal` (number): Total points available from this slot
+
+**Usage Notes:**
+
+- **User ID Format**: User IDs are returned as strings (not numbers) in the response
+- **Attack Attempts**: The `heroes` and `titans` fields show remaining attempts, not total attempts
+- **Used Units**: The `usedHeroes` and `usedTitans` arrays track which specific units have been deployed, not which slots were attacked
+- **Empty Arrays**: When a user hasn't used any heroes or titans yet, the arrays will be empty `[]`
+- **Zero Attempts**: When `heroes: 0` and `titans: 0`, the user has used all their attack attempts
+- **Clan Coordination**: Use this API to coordinate attacks and avoid duplicate unit usage across clan members
+
+**Example Usage:**
+```javascript
+const calls = [{
+ name: "crossClanWar_getAttackMap",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "body"
+}];
+
+const response = await Send(JSON.stringify({calls}));
+const attackMapData = response.results[0].result.response;
+const attackMap = attackMapData.clanTries;
+
+// Iterate through all clan members
+Object.keys(attackMap).forEach(userId => {
+ const memberTries = attackMap[userId];
+ console.log(`User ${userId}:`);
+ console.log(` Hero attempts remaining: ${memberTries.heroes}`);
+ console.log(` Titan attempts remaining: ${memberTries.titans}`);
+ console.log(` Used heroes: ${memberTries.usedHeroes.length > 0 ? memberTries.usedHeroes.join(', ') : 'None'}`);
+ console.log(` Used titans: ${memberTries.usedTitans.length > 0 ? memberTries.usedTitans.join(', ') : 'None'}`);
+});
+
+// Find members who still have hero attempts
+const membersWithHeroAttempts = Object.keys(attackMap).filter(userId => {
+ return attackMap[userId].heroes > 0;
+});
+console.log(`Members with hero attempts: ${membersWithHeroAttempts.length}`);
+
+// Find members who still have titan attempts
+const membersWithTitanAttempts = Object.keys(attackMap).filter(userId => {
+ return attackMap[userId].titans > 0;
+});
+console.log(`Members with titan attempts: ${membersWithTitanAttempts.length}`);
+
+// Check which heroes have been used across all members
+const allUsedHeroes = new Set();
+Object.values(attackMap).forEach(memberTries => {
+ memberTries.usedHeroes.forEach(heroId => allUsedHeroes.add(heroId));
+});
+console.log(`Total unique heroes used: ${allUsedHeroes.size}`);
+
+// Check which titans have been used across all members
+const allUsedTitans = new Set();
+Object.values(attackMap).forEach(memberTries => {
+ memberTries.usedTitans.forEach(titanId => allUsedTitans.add(titanId));
+});
+console.log(`Total unique titans used: ${allUsedTitans.size}`);
+
+// Find a member who hasn't used a specific hero
+const targetHeroId = 13;
+const availableMember = Object.keys(attackMap).find(userId => {
+ const memberTries = attackMap[userId];
+ return memberTries.heroes > 0 && !memberTries.usedHeroes.includes(targetHeroId);
+});
+if (availableMember) {
+ console.log(`Member ${availableMember} can use hero ${targetHeroId}`);
+}
+
+// Access targets (object with slot IDs as keys)
+const targets = attackMapData.targets || {};
+
+// Get team configurations from teamGetAll for all clan members
+const teamGetAllCalls = [{
+ name: "teamGetAll",
+ args: {},
+ ident: "teamGetAll"
+}];
+const teamGetAllResponse = await Send(JSON.stringify({calls: teamGetAllCalls}));
+const teamData = teamGetAllResponse.results[0].result.response;
+
+// Process each target slot
+Object.entries(targets).forEach(([slotId, target]) => {
+ const { state, teamIndex, userId } = target;
+
+ // Determine if this is a hero or titan battle based on slot ID
+ // Typically: slots 1-16 are hero battles, slots 17+ are titan battles
+ const slotNum = parseInt(slotId);
+ const isHeroBattle = slotNum <= 16;
+
+ if (state === 0) { // Available for attack
+ console.log(`Slot ${slotId}: Assigned to User ${userId}, Team Index ${teamIndex} (${isHeroBattle ? 'Hero' : 'Titan'} battle)`);
+
+ // Get the assigned player's team configuration
+ // Note: You would need to get each player's teamGetAll data, or use a cached version
+ // For this example, we'll show the structure:
+ if (isHeroBattle) {
+ // Hero battle - use assigned player's crossClanDefence_heroes[teamIndex]
+ console.log(` Use User ${userId}'s crossClanDefence_heroes[${teamIndex}] for attack`);
+ } else {
+ // Titan battle - use assigned player's crossClanDefence_titans[teamIndex]
+ console.log(` Use User ${userId}'s crossClanDefence_titans[${teamIndex}] for attack`);
+ }
+ } else if (state === 1) {
+ console.log(`Slot ${slotId}: Completed by User ${userId}`);
+ }
+});
+
+// Example: Get attack team configuration for a specific target
+// Note: This requires having each player's teamGetAll data
+function getAttackTeamForTarget(slotId, target, playerTeamData) {
+ const { teamIndex, userId, state } = target;
+
+ if (state === 1) {
+ return { status: 'completed' };
+ }
+
+ const slotNum = parseInt(slotId);
+ const isHeroBattle = slotNum <= 16;
+
+ if (!playerTeamData || !playerTeamData[userId]) {
+ return { error: `Team data not available for user ${userId}` };
+ }
+
+ const userTeamData = playerTeamData[userId];
+
+ if (isHeroBattle) {
+ // Hero battle - get from crossClanDefence_heroes
+ if (userTeamData.crossClanDefence_heroes && userTeamData.crossClanDefence_heroes[teamIndex]) {
+ const team = userTeamData.crossClanDefence_heroes[teamIndex];
+ return {
+ type: 'hero',
+ slotId: slotId,
+ userId: userId,
+ heroes: team.slice(0, 5),
+ pet: team[5],
+ teamIndex: teamIndex
+ };
+ }
+ } else {
+ // Titan battle - get from crossClanDefence_titans
+ if (userTeamData.crossClanDefence_titans && userTeamData.crossClanDefence_titans[teamIndex]) {
+ return {
+ type: 'titan',
+ slotId: slotId,
+ userId: userId,
+ titans: userTeamData.crossClanDefence_titans[teamIndex],
+ teamIndex: teamIndex
+ };
+ }
+ }
+
+ return { error: 'Team configuration not found' };
+}
+
+// Example: Find available targets for a specific user
+function getAvailableTargetsForUser(targets, userId) {
+ return Object.entries(targets)
+ .filter(([slotId, target]) => target.userId === userId && target.state === 0)
+ .map(([slotId, target]) => ({ slotId, ...target }));
+}
+
+// Example usage
+const myUserId = 35979991;
+const myTargets = getAvailableTargetsForUser(targets, myUserId);
+console.log(`User ${myUserId} has ${myTargets.length} available targets:`, myTargets);
+
+// Access enemySlots to get detailed enemy defense information
+const enemySlots = attackMapData.enemySlots || {};
+
+// Process each enemy slot to determine battle type and availability
+Object.entries(enemySlots).forEach(([slotId, slotData]) => {
+ const { team, status, attackerId, user } = slotData;
+
+ // Check if slot is available for attack
+ const isAvailable = status === "ready" && attackerId === null;
+
+ if (!isAvailable) {
+ console.log(`Slot ${slotId}: Not available (status: ${status}, attackerId: ${attackerId})`);
+ return;
+ }
+
+ // Determine battle type by checking team unit types
+ let battleType = null;
+ let hasHeroes = false;
+ let hasTitans = false;
+ let allUnitsAlive = true;
+
+ Object.values(team).forEach(unit => {
+ if (unit.type === "hero") {
+ hasHeroes = true;
+ } else if (unit.type === "titan") {
+ hasTitans = true;
+ }
+
+ // Check if unit is dead
+ if (unit.state && unit.state.isDead === true) {
+ allUnitsAlive = false;
+ }
+ });
+
+ // Determine battle type based on unit types
+ if (hasHeroes) {
+ battleType = "hero";
+ } else if (hasTitans) {
+ battleType = "titan";
+ }
+
+ console.log(`Slot ${slotId}:`);
+ console.log(` Enemy: ${user.name} (Level ${user.level})`);
+ console.log(` Battle Type: ${battleType}`);
+ console.log(` All Units Alive: ${allUnitsAlive}`);
+ console.log(` Status: ${status}`);
+ console.log(` Points Available: ${slotData.pointsTotal - slotData.pointsFarmed}`);
+
+ if (battleType === "hero") {
+ console.log(` → Use crossClanDefence_heroes to attack`);
+ } else if (battleType === "titan") {
+ console.log(` → Use crossClanDefence_titans to attack`);
+ }
+});
+
+// Helper function: Check if a slot is good to attack
+function isSlotGoodToAttack(slotId, enemySlots) {
+ const slot = enemySlots[slotId];
+ if (!slot) return false;
+
+ // Check if slot is ready and not being attacked
+ if (slot.status !== "ready" || slot.attackerId !== null) {
+ return false;
+ }
+
+ // Check if all units are alive (isDead: false)
+ const team = slot.team || {};
+ const allAlive = Object.values(team).every(unit => {
+ return unit.state && unit.state.isDead === false;
+ });
+
+ return allAlive;
+}
+
+// Helper function: Get battle type for a slot
+function getBattleTypeForSlot(slotId, enemySlots) {
+ const slot = enemySlots[slotId];
+ if (!slot || !slot.team) return null;
+
+ const team = slot.team;
+ let hasHeroes = false;
+ let hasTitans = false;
+
+ Object.values(team).forEach(unit => {
+ if (unit.type === "hero") {
+ hasHeroes = true;
+ } else if (unit.type === "titan") {
+ hasTitans = true;
+ }
+ });
+
+ if (hasHeroes) return "hero";
+ if (hasTitans) return "titan";
+ return null;
+}
+
+// Example: Find available hero battle slots
+const availableHeroSlots = Object.keys(enemySlots).filter(slotId => {
+ return isSlotGoodToAttack(slotId, enemySlots) &&
+ getBattleTypeForSlot(slotId, enemySlots) === "hero";
+});
+
+console.log(`Available hero battle slots: ${availableHeroSlots.join(', ')}`);
+
+// Example: Find available titan battle slots
+const availableTitanSlots = Object.keys(enemySlots).filter(slotId => {
+ return isSlotGoodToAttack(slotId, enemySlots) &&
+ getBattleTypeForSlot(slotId, enemySlots) === "titan";
+});
+
+console.log(`Available titan battle slots: ${availableTitanSlots.join(', ')}`);
+```
+
+**Notes:**
+- This API provides clan-wide coordination data for Cross Clan War attacks
+- Use this information to plan attacks and ensure optimal unit distribution across clan members
+- The `usedHeroes` and `usedTitans` arrays help track which units are still available for use
+- The `targets` object shows attack assignments for each target slot
+- **Target Structure**:
+ - Keys are slot IDs (strings like `"1"`, `"7"`, `"16"`, `"17"`)
+ - Lower slot IDs (typically 1-16) are usually hero battles
+ - Higher slot IDs (typically 17+) are usually titan battles
+- **Attack Assignment**:
+ - `userId` in each target is the player assigned to attack that target (not the defender)
+ - `teamIndex` refers to which team from the assigned attacker's `crossClanDefence_heroes` or `crossClanDefence_titans` to use
+ - To get the attack team, you need the assigned player's `teamGetAll` data and use their `crossClanDefence_heroes[teamIndex]` or `crossClanDefence_titans[teamIndex]`
+- **State Values**:
+ - `state: 0` = Target is available for attack (not yet completed)
+ - `state: 1` = Target attack is complete
+- Combine this with `crossClanWar_getInfo` and `teamGetAll` (for each assigned player) to get complete war status and attack team information
+- **Enemy Slots Information**: The `enemySlots` field provides detailed information about enemy defense slots
+ - Use `enemySlots[slotId].team[position].type` to determine battle type:
+ - If `type: "hero"` → Use `crossClanDefence_heroes` to attack
+ - If `type: "titan"` → Use `crossClanDefence_titans` to attack
+ - Check `enemySlots[slotId].team[position].state.isDead` to see if units are alive (`false` = good to attack)
+ - Check `enemySlots[slotId].status === "ready"` and `enemySlots[slotId].attackerId === null` to confirm slot is available
+
+---
+
+#### crossClanWar_startBattle
+
+**Description:** Initiates a battle in the Cross Clan War against a specific slot. Supports both hero battles and titan battles.
+
+**Request (Hero Battle):**
+```javascript
+Send({
+ calls: [{
+ name: "crossClanWar_startBattle",
+ args: {
+ slotId: 2,
+ favor: {
+ "13": 6008,
+ "16": 6004,
+ "29": 6006,
+ "64": 6005
+ },
+ team: {
+ units: [29, 64, 13, 40, 16],
+ pet: 6008
+ },
+ banner: 2
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Request (Titan Battle):**
+```javascript
+Send({
+ calls: [{
+ name: "crossClanWar_startBattle",
+ args: {
+ slotId: 16,
+ team: {
+ units: [4033, 4043, 4031, 4032, 4030]
+ }
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `slotId` (number): The battle slot ID to attack (1-16 for hero battles, higher for titan battles)
+- `team` (object): The attacking team configuration
+ - `team.units` (array): Array of hero IDs for the team (hero battles) or titan IDs (titan battles)
+ - `team.pet` (number, optional): Pet ID for the team (hero battles only)
+- `favor` (object, optional): Map of hero IDs to favor IDs (hero battles only)
+- `banner` (number, optional): Banner ID for the team (hero battles only)
+
+**Notes:**
+- Hero battles (typically slots 1-15) support pets, favors, and banners
+- Titan battles (typically slots 16+) only require unit IDs
+- The `favor` parameter is optional - can be an empty object `{}` if no favors are selected
+
+---
+
+## Secret Wealth Shop API
+
+### Overview
+
+The Secret Wealth Shop (Merchant Shop) is a shop where players can purchase items using consumables (such as pet potions) or GEMs (starmoney).
+
+### Endpoints
+
+#### shopBuy
+
+**Description:** Purchases an item from the Secret Wealth Shop.
+
+**Request (Purchase with Consumables):**
+```javascript
+Send({
+ calls: [{
+ name: "shopBuy",
+ args: {
+ shopId: 1576000026, // Secret Wealth Shop ID
+ slot: 6,
+ cost: {
+ consumable: {
+ "85": 40000 // 85: pet potion
+ }
+ },
+ reward: {
+ consumable: {
+ "55": 80 // 55: titan artifact sphere
+ }
+ }
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Request (Purchase with GEMs):**
+```javascript
+Send({
+ calls: [{
+ name: "shopBuy",
+ args: {
+ shopId: 1576000026,
+ slot: 3,
+ cost: {
+ starmoney: 890 // GEM payment
+ },
+ reward: {
+ consumable: {
+ "201": 100 // 201: Crystal
+ }
+ }
+ },
+ context: { actionTs: Date.now() },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `shopId` (number): Unique identifier for the shop instance (1576000026 for Secret Wealth Shop)
+- `slot` (number): The slot number of the item being purchased (typically 1-6)
+- `cost` (object): The cost of the item
+ - `cost.consumable` (object): Map of consumable IDs to amounts (when paying with consumables)
+ - `cost.starmoney` (number): GEM amount (when paying with GEMs)
+- `reward` (object): The reward being received (for validation)
+
+**Consumable ID Reference:**
+- `85`: Pet potion
+- `55`: Titan artifact sphere
+- `201`: Crystal
+
+**Response:** Returns purchase confirmation with rewarded items and quest updates.
+
+---
+
+## Titan Artifact Shop API
+
+### Overview
+
+The Titan Artifact Shop (shopId: 13) is a shop where players can purchase Titan Artifact fragments using coins. The shop supports bulk purchases.
+
+### Endpoints
+
+#### shopBuy
+
+**Description:** Purchases Titan Artifact fragments from the shop. Supports bulk purchases via the `amount` parameter.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "shopBuy",
+ args: {
+ shopId: 13,
+ slot: 24,
+ cost: {
+ coin: {
+ "18": 12
+ }
+ },
+ reward: {
+ fragmentTitanArtifact: {
+ "2005": 1
+ }
+ },
+ amount: 300 // Bulk purchase amount
+ },
+ context: { actionTs: Date.now() },
+ ident: "group_0_body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `shopId` (number): Fixed at `13` for the Titan Artifact Shop
+- `slot` (number): The slot number of the item being purchased (typically 1-25)
+- `cost.coin` (object): Map of coin type IDs to amounts (coin type `18` is standard)
+- `reward.fragmentTitanArtifact` (object): Map of fragment IDs to amounts
+- `amount` (number, optional): Number of items to purchase in bulk (defaults to 1)
+
+**Titan Artifact Fragment IDs:**
+- `1001-1016`: Standard Titan Artifact fragments
+- `1017-1020`: Additional Titan Artifact fragments
+- `2001-2005`: Advanced Titan Artifact fragments
+
+#### shopGet
+
+**Description:** Retrieves the current inventory and configuration of the Titan Artifact Shop.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "shopGet",
+ args: {
+ shopId: 13
+ },
+ context: { actionTs: Date.now() },
+ ident: "group_0_shopGet"
+ }]
+})
+```
+
+**Response:** Returns shop configuration including:
+- `slots`: Map of slot numbers to slot data
+- `slots[].reward`: Reward for this slot
+- `slots[].cost`: Cost object with coin type and amount
+- `slots[].bought`: Number of times this slot has been purchased
+- `slots[].staticShopMultiplePurchase`: Whether bulk purchase is enabled (1 = enabled)
+- `slots[].amountAvailable`: Available quantity (null = unlimited)
+
+---
+
+## TeamGetAll API
+
+### Overview
+
+The `teamGetAll` API provides comprehensive team configurations for all game modes in Hero Wars. It returns pre-configured teams that players have set up through the game's UI, including heroes, pets, and other team-related data.
+
+### Endpoint
+
+#### teamGetAll
+
+**Request:**
+```javascript
+const calls = [{
+ name: "teamGetAll",
+ args: {},
+ ident: "teamGetAll"
+}];
+
+const response = await Send(JSON.stringify({calls}));
+const teamData = response.results[0].result.response;
+```
+
+**Alternative Request Format (with context):**
+```javascript
+const calls = [{
+ name: "teamGetAll",
+ args: {},
+ context: { actionTs: Date.now() },
+ ident: "teamGetAll"
+}];
+
+const response = await Send(JSON.stringify({calls}));
+const teamData = response.results[0].result.response;
+```
+
+### Response Structure
+
+Each team configuration is an array where:
+- **First 5 elements**: Hero IDs (heroes with ID < 6000)
+- **6th element**: Pet ID (pets with ID >= 6000)
+
+### Team Configuration Fields
+
+```typescript
+{
+ // Adventure Mode
+ adventure_hero: number[]; // [hero1, hero2, hero3, hero4, hero5, pet]
+
+ // Arena Modes
+ arena: number[]; // [hero1, hero2, hero3, hero4, hero5, pet]
+ grand: number[][]; // [[team1], [team2], [team3]] - 3 teams for grand arena
+
+ // Dungeon Modes
+ dungeon_hero: number[]; // [hero1, hero2, hero3, hero4, hero5, pet]
+ dungeon_earth: number[]; // Titan team for earth dungeon
+ dungeon_fire: number[]; // Titan team for fire dungeon
+ dungeon_water: number[]; // Titan team for water dungeon
+ dungeon_neutral: number[]; // Titan team for neutral dungeon
+
+ // Tower Mode
+ tower: number[]; // [hero1, hero2, hero3, hero4, hero5, pet]
+
+ // Titan Arena
+ titan_arena: number[]; // [titan1, titan2, titan3, titan4, titan5]
+ titan_arena_def: number[]; // Defense team for titan arena
+ titan_mission: number[]; // Titan team for missions
+
+ // Clan/Team Modes
+ clanDefence_heroes: number[]; // Heroes for Guild War defense
+ clanDefence_titans: number[]; // Titans for Guild War defense
+ clanRaid_nodes: number[][]; // [[team1], [team2], [team3]] - 3 teams for clan raid nodes (Minions Attack)
+ clan_global_pvp: number[]; // Heroes for global clan PvP
+ clan_global_pvp_titan: number[]; // Titans for Clash of Worlds (global clan PvP)
+ clan_pvp_hero: number[]; // Heroes for clan PvP
+ clan_pvp_titan: number[]; // Titans for clan PvP
+
+ // Cross-Clan Defense
+ crossClanDefence_heroes: number[][]; // [[team1], [team2], [team3]] - 3 teams, each with [hero1, hero2, hero3, hero4, hero5, pet]
+ crossClanDefence_titans: number[][]; // [[team1], [team2]] - 2 titan teams, each with [titan1, titan2, titan3, titan4, titan5]
+
+ // Mission Mode
+ mission: number[]; // [hero1, hero2, hero3, hero4, hero5, pet]
+
+ // Boss Battles
+ boss_10: number[]; // Team for boss level 10
+ boss_11: number[]; // Team for boss level 11
+ boss_12: number[]; // Team for boss level 12
+
+ // Invasion Bosses (182-225, 394-417)
+ invasion_boss_182: number[]; // Team for invasion boss 182
+ // ... (continues for all invasion boss levels)
+
+ // Other Modes
+ brawl: number[]; // Team for brawls
+ challenge: number[]; // Team for challenges
+}
+```
+
+### Entity ID Ranges
+
+- **Heroes**: 1-999 (e.g., 46 = Aurora, 57 = K'arkh, 40 = Jorgen)
+- **Pets**: 6000-6999 (e.g., 6008 = Axel, 6004 = Oliver, 6006 = Cain)
+- **Titans**: 4000-4999 (e.g., 4033 = Hyperion, 4003 = Eden, 4043 = Sigurd)
+
+### Usage Patterns
+
+**Single Team Modes:**
+```javascript
+const arenaTeam = teamGetAll.arena; // [46, 57, 40, 16, 65, 6008]
+const heroes = arenaTeam.slice(0, 5); // [46, 57, 40, 16, 65]
+const pet = arenaTeam[5]; // 6008
+```
+
+**Multi-Team Modes:**
+```javascript
+const grandArenaTeams = teamGetAll.grand; // [[team1], [team2], [team3]]
+const clanRaidTeams = teamGetAll.clanRaid_nodes; // [[team1], [team2], [team3]]
+```
+
+**Titan-Only Modes:**
+```javascript
+const titanArenaTeam = teamGetAll.titan_arena; // [4033, 4003, 4043, 4032, 4030]
+```
+
+**Cross-Clan Defense Teams:**
+```javascript
+// Cross-Clan Defense Heroes: 3 teams, each with 6 elements (5 heroes + 1 pet)
+const crossClanDefenceHeroes = teamGetAll.crossClanDefence_heroes;
+// Example structure:
+// [
+// [58, 56, 62, 9, 40, 6006], // Team 1: [hero1, hero2, hero3, hero4, hero5, pet]
+// [16, 48, 13, 64, 29, 6008], // Team 2: [hero1, hero2, hero3, hero4, hero5, pet]
+// [1, 55, 31, 43, 63, 6005] // Team 3: [hero1, hero2, hero3, hero4, hero5, pet]
+// ]
+
+// Cross-Clan Defense Titans: 2 teams, each with 5 titans
+const crossClanDefenceTitans = teamGetAll.crossClanDefence_titans;
+// Example structure:
+// [
+// [4030, 4031, 4043, 4042, 4023], // Team 1: [titan1, titan2, titan3, titan4, titan5]
+// [4000, 4001, 4003, 4032, 4033] // Team 2: [titan1, titan2, titan3, titan4, titan5]
+// ]
+
+// Access individual teams
+const firstHeroTeam = crossClanDefenceHeroes[0]; // [58, 56, 62, 9, 40, 6006]
+const firstHeroTeamHeroes = firstHeroTeam.slice(0, 5); // [58, 56, 62, 9, 40]
+const firstHeroTeamPet = firstHeroTeam[5]; // 6006
+
+const firstTitanTeam = crossClanDefenceTitans[0]; // [4030, 4031, 4043, 4042, 4023]
+```
+
+### Complete Response Example
+
+**Request:**
+```javascript
+const calls = [{
+ name: "teamGetAll",
+ args: {},
+ ident: "teamGetAll"
+}];
+
+const response = await Send(JSON.stringify({calls}));
+const teamData = response.results[0].result.response;
+```
+
+**Response Example (partial):**
+```javascript
+{
+ // Regular Arena (single team)
+ arena: [46, 57, 40, 16, 65, 6008],
+
+ // Grand Arena (3 teams)
+ grand: [
+ [58, 1, 64, 13, 55, 6006],
+ [42, 56, 9, 62, 43, 6005],
+ [16, 31, 57, 40, 48, 6004]
+ ],
+
+ // Cross-Clan Defense Heroes (3 teams)
+ crossClanDefence_heroes: [
+ [58, 56, 62, 9, 40, 6006], // Team 1: 5 heroes + 1 pet
+ [16, 48, 13, 64, 29, 6008], // Team 2: 5 heroes + 1 pet
+ [1, 55, 31, 43, 63, 6005] // Team 3: 5 heroes + 1 pet
+ ],
+
+ // Cross-Clan Defense Titans (2 teams)
+ crossClanDefence_titans: [
+ [4030, 4031, 4043, 4042, 4023], // Team 1: 5 titans
+ [4000, 4001, 4003, 4032, 4033] // Team 2: 5 titans
+ ],
+
+ // Titan Arena (single team, 5 titans only, no pet)
+ titan_arena: [4033, 4003, 4043, 4032, 4030],
+
+ // Clan Raid Nodes (3 teams)
+ clanRaid_nodes: [
+ [46, 57, 40, 16, 65, 6008],
+ [58, 1, 64, 13, 55, 6006],
+ [42, 56, 9, 62, 43, 6005]
+ ],
+
+ // ... other team configurations
+}
+```
+
+---
+
+## Demo Battle API
+
+### Overview
+
+The Demo Battle API allows testing battle scenarios in Hero Wars without consuming actual battle attempts. This API simulates battles between attack and defense teams and returns detailed battle results.
+
+### Endpoint
+
+#### demoBattles_startBattle
+
+**Description:** Starts a demo battle simulation for testing purposes.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "demoBattles_startBattle",
+ args: {
+ mechanic: "arena", // Battle type: "arena", "grand_arena", "titan_war", etc.
+ defenceMaxUpgrade: false,
+ defenceTeam: {
+ units: [9, 40, 56, 16, 1],
+ pet: 6005
+ },
+ defenceBanner: 6,
+ defenceFavor: {
+ "1": 6004,
+ "9": 6005,
+ "16": 6000,
+ "56": 6006
+ },
+ maxUpgrade: false,
+ team: {
+ units: [62, 9, 40, 56, 42],
+ pet: 6008
+ },
+ banner: 6,
+ favor: {
+ "9": 6007,
+ "40": 6004,
+ "42": 6006,
+ "56": 6001,
+ "62": 6003
+ },
+ defenceBuffs: {},
+ buffs: {},
+ parentId: 0,
+ entryId: 0
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `mechanic` (string): Battle type (e.g., "arena", "grand_arena", "titan_war")
+- `defenceMaxUpgrade` (boolean): Whether defense team uses max upgrades
+- `defenceTeam` (object): Defense team configuration
+ - `units` (array): Array of hero IDs
+ - `pet` (number): Pet ID
+- `defenceBanner` (number): Defense team banner ID
+- `defenceFavor` (object): Defense team favor pets mapping
+- `maxUpgrade` (boolean): Whether attack team uses max upgrades
+- `team` (object): Attack team configuration
+ - `units` (array): Array of hero IDs
+ - `pet` (number): Pet ID
+- `banner` (number): Attack team banner ID
+- `favor` (object): Attack team favor pets mapping
+- `defenceBuffs` (object): Defense team buffs (empty object `{}`)
+- `buffs` (object): Attack team buffs (empty object `{}`)
+- `parentId` (number): Parent battle ID (0 for standalone battles)
+- `entryId` (number): Entry ID (0 for standalone battles)
+
+**Response:** Returns detailed battle data including:
+- Battle metadata (userId, typeId, startTime, seed, type)
+- Attackers data (detailed hero statistics)
+- Defenders data (battle state information)
+- Battle effects (buffs, debuffs, banner effects)
+
+**Notes:**
+- Demo battles do not consume actual battle attempts
+- Battle results are calculated server-side
+- Can be used for testing team compositions and strategies
+- Supports various battle mechanics (arena, grand arena, titan war, etc.)
+
+---
+
+## Area of Conquest (Clan Domination) API
+
+Area of Conquest (also known as Clan Domination) is a clan-based PvP mode where clans compete to control territories on a map.
+
+### clanDomination_getBattleJournal
+
+Get the battle journal/log for Area of Conquest battles.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "clanDomination_getBattleJournal",
+ args: {
+ type: "clan_domination",
+ limit: 40,
+ offset: 0
+ },
+ context: { actionTs: 1143874 },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `type` (string): Battle type, should be `"clan_domination"`
+- `limit` (number): Maximum number of events to return (default: 40)
+- `offset` (number): Number of events to skip (default: 0)
+
+**Example Response:**
+```json
+{
+ "date": 1762932596.002743,
+ "results": [{
+ "ident": "body",
+ "result": {
+ "response": {
+ "users": {
+ "8009806": {
+ "id": "8009806",
+ "name": "Hershey",
+ "lastLoginTime": "1762915250",
+ "serverId": "46",
+ "level": "130",
+ "clanId": "71205",
+ "clanRole": "4",
+ "commander": true,
+ "avatarId": "1514",
+ "isChatModerator": false,
+ "frameId": 154,
+ "leagueId": 3,
+ "allowPm": "all",
+ "clanTitle": "Fairy Tail",
+ "clanIcon": {
+ "flagColor1": 19,
+ "flagColor2": 19,
+ "flagShape": 14,
+ "iconColor": 0,
+ "iconShape": 17,
+ "frame": 2
+ }
+ },
+ "35979991": {
+ "id": "35979991",
+ "name": "One Peace",
+ "lastLoginTime": "1762931456",
+ "serverId": "218",
+ "level": "130",
+ "clanId": "328621",
+ "clanRole": "4",
+ "commander": false,
+ "avatarId": "690",
+ "isChatModerator": false,
+ "frameId": 136,
+ "leagueId": 3,
+ "allowPm": "all",
+ "clanTitle": "Peaks End",
+ "clanIcon": {
+ "flagColor1": 19,
+ "flagColor2": 19,
+ "flagShape": 12,
+ "iconColor": 7,
+ "iconShape": 14
+ }
+ }
+ },
+ "events": [
+ {
+ "replayId": "1762932389588391261",
+ "userId": 47417806,
+ "targetId": 35961156,
+ "result": "lose",
+ "reward": [],
+ "ctime": 1762932389,
+ "endTime": 1762932389
+ },
+ {
+ "replayId": "1762932203341325664",
+ "userId": 8009806,
+ "targetId": 35891708,
+ "result": "defence",
+ "reward": {
+ "coin": {
+ "46": 100
+ }
+ },
+ "ctime": 1762932203,
+ "endTime": 1762932203
+ },
+ {
+ "replayId": "1762929762844980034",
+ "userId": 35979991,
+ "targetId": 47429573,
+ "result": "win",
+ "reward": {
+ "coin": {
+ "46": 100
+ }
+ },
+ "ctime": 1762929762,
+ "endTime": 1762929762
+ },
+ {
+ "replayId": "1762924106898277273",
+ "userId": 35449277,
+ "targetId": 28415350,
+ "result": "conquer",
+ "reward": {
+ "coin": {
+ "46": 124
+ }
+ },
+ "ctime": 1762924106,
+ "endTime": 1762924106
+ }
+ ]
+ }
+ }
+ }]
+}
+```
+
+**Response Fields:**
+- `users`: Object mapping user IDs to user information
+- `events`: Array of battle events with results and rewards
+ - `result`: Battle outcome
+ - `"win"`: Attacker won
+ - `"lose"`: Attacker lost
+ - `"defence"`: Successfully defended
+ - `"conquer"`: Successfully conquered territory
+
+---
+
+### clanDomination_stats
+
+Get statistics for all clans participating in Area of Conquest.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "clanDomination_stats",
+ args: {},
+ context: { actionTs: 1144748 },
+ ident: "body"
+ }]
+})
+```
+
+**Example Response:**
+```json
+{
+ "date": 1762932596.871387,
+ "results": [{
+ "ident": "body",
+ "result": {
+ "response": {
+ "71205": {
+ "power": 196793770,
+ "coins": 131683,
+ "towns": 6,
+ "castle": 32
+ },
+ "368696": {
+ "power": 159466657,
+ "coins": 113165,
+ "towns": 10,
+ "castle": 39
+ },
+ "312133": {
+ "power": 127974259,
+ "coins": 125445,
+ "towns": 9,
+ "castle": 40
+ },
+ "328621": {
+ "power": 137851121,
+ "coins": 156356,
+ "towns": 6,
+ "castle": 40
+ }
+ }
+ }
+ }]
+}
+```
+
+**Response Fields:**
+- `power`: Total clan power
+- `coins`: Total coins collected
+- `towns`: Number of towns controlled
+- `castle`: Castle level/position
+
+---
+
+### clanDomination_move
+
+Move your character to a specific level/position on the map.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "clanDomination_move",
+ args: {
+ levelId: 7
+ },
+ context: { actionTs: 1203030 },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `levelId` (number): The level/position ID to move to
+
+**Example Response:**
+```json
+{
+ "date": 1762932655.203018,
+ "results": [{
+ "ident": "body",
+ "result": {
+ "response": {
+ "userId": 35979991,
+ "move": {
+ "2": 7
+ },
+ "visibleLevels": [696, 606, 612, 690, 702, 786, 792, 522, 528, 600, 534, 618, 684, 780, 708, 798, 882, 888, 894, 444, 450, 516, 456, 462, 540, 624, 372, 378, 438, 384, 390, 396, 468, 546, 630, 306, 312, 366, 318, 432, 324, 330, 277, 337, 403, 475, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 72, 77, 78, 83, 84, 89, 90, 95, 101, 102, 107, 108, 114, 120, 128, 131, 133, 137, 138, 143, 144, 149, 150, 156, 162, 163, 169, 170, 173, 175, 179, 180, 181, 185, 186, 191, 192, 197, 198, 204, 205, 210, 211, 216, 217, 218, 222, 223, 227, 228, 229, 233, 234, 235, 239, 240, 245, 246, 252, 253, 258, 259, 264, 265, 270, 271, 276, 282, 283, 287, 288, 289, 293, 294, 299, 300, 313, 319, 325, 329, 331, 336, 342, 343, 348, 349, 354, 355, 360, 379, 385, 389, 391, 395, 402, 408, 409, 414, 415, 420, 421, 426, 461, 467, 474, 480, 481, 486, 487, 493, 539, 545],
+ "changedLevels": null,
+ "castlePositions": null,
+ "userPositions": {
+ "7659541": 33,
+ "23385341": 51,
+ "24417949": 6,
+ "28103487": 101,
+ "28569253": 29,
+ "28572153": 426,
+ "59720486": 107,
+ "47570368": 26,
+ "35448204": 696,
+ "35449277": 336,
+ "35461323": 252,
+ "35473076": 696,
+ "35538758": 36,
+ "35538770": 696,
+ "35581685": 696,
+ "35621043": 696,
+ "35659090": 696,
+ "35695193": 198,
+ "35698714": 696,
+ "35718205": 294,
+ "35769428": 696,
+ "35776732": 696,
+ "35818082": 696,
+ "35891708": 1,
+ "35900525": 696,
+ "35902122": 696,
+ "35911013": 265,
+ "35961156": 185,
+ "35979991": 7,
+ "35986432": 150,
+ "36005478": 696,
+ "36039664": 223,
+ "36040671": 696,
+ "48705148": 696,
+ "59891179": 378,
+ "59895273": 468,
+ "60608426": 343
+ },
+ "townPositions": {
+ "1": {
+ "position": 1,
+ "status": 1,
+ "userId": 35891708,
+ "townId": 5,
+ "farmStart": 1762931386
+ },
+ "26": {
+ "position": 26,
+ "status": 1,
+ "userId": 47570368,
+ "townId": 3,
+ "farmStart": 1762920223
+ },
+ "29": {
+ "position": 29,
+ "status": 1,
+ "userId": 28569253,
+ "townId": 3,
+ "farmStart": 1762932617
+ },
+ "33": {
+ "position": 33,
+ "status": 1,
+ "userId": 7659541,
+ "townId": 3,
+ "farmStart": 1762932579
+ },
+ "36": {
+ "position": 36,
+ "status": 1,
+ "userId": 35538758,
+ "townId": 3,
+ "farmStart": 1762915383
+ },
+ "101": {
+ "position": 101,
+ "status": 1,
+ "userId": 28103487,
+ "townId": 4,
+ "farmStart": 1762932620
+ },
+ "336": {
+ "position": 336,
+ "status": 1,
+ "userId": 35449277,
+ "townId": 3,
+ "farmStart": 1762925012
+ },
+ "343": {
+ "position": 343,
+ "status": 1,
+ "userId": 60608426,
+ "townId": 2,
+ "farmStart": 1762928106
+ },
+ "378": {
+ "position": 378,
+ "status": 1,
+ "userId": 59891179,
+ "townId": 1,
+ "farmStart": 1762912781
+ },
+ "426": {
+ "position": 426,
+ "status": 1,
+ "userId": 28572153,
+ "townId": 2,
+ "farmStart": 1762931984
+ },
+ "468": {
+ "position": 468,
+ "status": 1,
+ "userId": 59895273,
+ "townId": 1,
+ "farmStart": 1762923004
+ }
+ },
+ "chestPositions": {
+ "264": {
+ "position": 264,
+ "farmed": true
+ },
+ "186": {
+ "position": 186,
+ "farmed": true
+ },
+ "235": {
+ "position": 235,
+ "farmed": true
+ },
+ "233": {
+ "position": 233,
+ "farmed": true
+ },
+ "319": {
+ "position": 319,
+ "farmed": true
+ }
+ },
+ "portalPositions": null,
+ "altarPositions": null,
+ "farmedChest": null,
+ "user": {
+ "id": "35979991",
+ "name": "One Peace",
+ "lastLoginTime": "1762931456",
+ "serverId": "218",
+ "level": "130",
+ "clanId": "328621",
+ "clanRole": "4",
+ "commander": false,
+ "avatarId": "690",
+ "isChatModerator": false,
+ "frameId": 136,
+ "leagueId": 3,
+ "allowPm": "all",
+ "clanTitle": "Peaks End",
+ "clanIcon": {
+ "flagColor1": 19,
+ "flagColor2": 19,
+ "flagShape": 12,
+ "iconColor": 7,
+ "iconShape": 14
+ }
+ },
+ "autoMove": false,
+ "refillable": {
+ "id": 55,
+ "amount": 11,
+ "lastRefill": 1762932312,
+ "boughtToday": 0,
+ "refillTime": 720
+ },
+ "mapVersion": 17100
+ }
+ }
+ }]
+}
+```
+
+**Response Fields:**
+- `move`: Object showing the new position for the user
+- `visibleLevels`: Array of level IDs that are visible/accessible
+- `userPositions`: Map of all user positions on the map
+- `townPositions`: Map of town positions with ownership and farming status
+- `chestPositions`: Map of chest positions and whether they've been farmed
+- `refillable`: Information about movement energy/charges
+ - `id`: Refillable item ID (55 for movement energy)
+ - `amount`: Current amount of energy
+ - `lastRefill`: Timestamp of last refill
+ - `refillTime`: Time in seconds until next refill
+
+---
+
+### clanDomination_getEnemyTeams
+
+Get enemy team information for a specific level/position.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "clanDomination_getEnemyTeams",
+ args: {
+ levelId: 6
+ },
+ context: { actionTs: 1204346 },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `levelId` (number): The level/position ID to get enemy teams for
+
+**Example Response:**
+```json
+{
+ "date": 1762932656.4668911,
+ "results": [{
+ "ident": "body",
+ "result": {
+ "response": [{
+ "userId": 24417949,
+ "defense": {
+ "powerSum": 1048206,
+ "units": {
+ "9": {
+ "id": 9,
+ "level": 130,
+ "star": 6,
+ "power": 169909,
+ "color": 18,
+ "favorPetId": 6006,
+ "favorPower": 11064
+ },
+ "48": {
+ "id": 48,
+ "level": 130,
+ "star": 6,
+ "power": 195511,
+ "color": 18,
+ "favorPetId": 6005,
+ "favorPower": 11064
+ },
+ "40": {
+ "id": 40,
+ "level": 130,
+ "star": 6,
+ "power": 139207,
+ "color": 18,
+ "favorPetId": 0,
+ "favorPower": 0
+ },
+ "43": {
+ "id": 43,
+ "level": 130,
+ "star": 6,
+ "power": 157387,
+ "color": 18,
+ "favorPetId": 6008,
+ "favorPower": 7301
+ },
+ "16": {
+ "id": 16,
+ "level": 130,
+ "star": 6,
+ "power": 204249,
+ "color": 18,
+ "favorPetId": 6004,
+ "favorPower": 10154
+ },
+ "6006": {
+ "id": 6006,
+ "level": 130,
+ "star": 6,
+ "power": 181943,
+ "color": 10,
+ "favorPetId": null,
+ "favorPower": null,
+ "type": "pet"
+ }
+ },
+ "banner": {
+ "id": 1,
+ "slots": {
+ "1": 29,
+ "2": 41,
+ "0": 65
+ }
+ }
+ },
+ "defenseState": {
+ "9": {
+ "hp": 348525,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 348525
+ },
+ "48": {
+ "hp": 566017,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 566017
+ },
+ "40": {
+ "hp": 386912,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 386912
+ },
+ "43": {
+ "hp": 343417,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 343417
+ },
+ "16": {
+ "hp": 449732,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 449732
+ },
+ "6006": {
+ "hp": -1,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": -1
+ }
+ },
+ "healed": null
+ }]
+ }
+ }]
+}
+```
+
+**Response Fields:**
+- Array of enemy teams at the specified level
+- `defense`: Defense team composition with heroes, pets, and banner
+- `defenseState`: Current state of defense team (HP, energy, etc.)
+
+---
+
+### clanDomination_startBattle
+
+Start a battle against a target player in Area of Conquest.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "clanDomination_startBattle",
+ args: {
+ targetId: "24417949"
+ },
+ context: { actionTs: 1205429 },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- `targetId` (string | number): The user ID of the target to attack
+
+**Example Response (truncated for readability):**
+```json
+{
+ "date": 1762932658.0034771,
+ "results": [{
+ "ident": "body",
+ "result": {
+ "response": {
+ "battle": {
+ "userId": "35979991",
+ "typeId": 24417949,
+ "attackers": {
+ "1": {
+ "id": 62,
+ "level": 130,
+ "star": 6,
+ "power": 163592,
+ "color": 18,
+ "petId": 6008,
+ "type": "hero",
+ "state": {
+ "hp": 426584,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 426584
+ }
+ },
+ "2": {
+ "id": 29,
+ "level": 130,
+ "star": 6,
+ "power": 96656,
+ "color": 18,
+ "petId": 6002,
+ "type": "hero",
+ "state": {
+ "hp": 341941,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 341941
+ }
+ },
+ "6": {
+ "id": 6008,
+ "level": 130,
+ "star": 6,
+ "power": 181943,
+ "type": "pet",
+ "state": {
+ "hp": -1,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": -1
+ }
+ }
+ },
+ "defenders": [{
+ "1": {
+ "id": 9,
+ "level": 130,
+ "star": 6,
+ "power": 169909,
+ "color": 18,
+ "petId": 6006,
+ "type": "hero",
+ "state": {
+ "hp": 348525,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 348525
+ }
+ },
+ "2": {
+ "id": 48,
+ "level": 130,
+ "star": 6,
+ "power": 195511,
+ "color": 18,
+ "petId": 6005,
+ "type": "hero",
+ "state": {
+ "hp": 566017,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 566017
+ }
+ }
+ }],
+ "effects": {
+ "defenders": {
+ "percentBuffByPerk_energyIncrease_4": 10,
+ "percentBuffAll_physicalAttack": 5,
+ "percentBuffAll_armor": 9.5,
+ "percentBuffAll_armorPenetration": 6
+ },
+ "defendersBanner": {
+ "id": 1,
+ "slots": {
+ "1": 29,
+ "2": 41,
+ "0": 65
+ }
+ },
+ "attackers": {
+ "levelDecreaseAuraOnEnemy_8_18_400": 2,
+ "percentBuffAll_magicPower": 16,
+ "redPatternScaling": 3,
+ "percentBuffAll_armor": 14,
+ "percentDebuffAllEnemy_physicalCritChance": 16.5
+ },
+ "attackersBanner": {
+ "id": 6,
+ "slots": [15, 45, 79]
+ }
+ },
+ "reward": [],
+ "startTime": 1762932657,
+ "seed": 3167111373,
+ "type": "clan_domination",
+ "id": "1762932657569315883",
+ "progress": [{
+ "v": 273,
+ "b": 0,
+ "seed": 3167111373,
+ "attackers": {
+ "input": [],
+ "heroes": {
+ "6": {
+ "hp": -1,
+ "energy": 506,
+ "isDead": false
+ },
+ "1": {
+ "hp": 374710,
+ "energy": 0,
+ "isDead": false
+ }
+ }
+ },
+ "defenders": {
+ "input": [],
+ "heroes": {
+ "6": {
+ "hp": -1,
+ "energy": 439,
+ "isDead": false
+ }
+ }
+ }
+ }],
+ "endTime": 1762932657,
+ "result": {
+ "win": true,
+ "stars": 3,
+ "serverVersion": 273
+ }
+ },
+ "reward": {
+ "coin": {
+ "46": 100
+ }
+ },
+ "attackersState": {
+ "62": {
+ "hp": 374710,
+ "energy": 0,
+ "isDead": false,
+ "maxHp": 426584
+ },
+ "29": {
+ "hp": 67773,
+ "energy": 603,
+ "isDead": false,
+ "maxHp": 341941
+ },
+ "58": {
+ "hp": 554696,
+ "energy": 1000,
+ "isDead": false,
+ "maxHp": 561958
+ },
+ "40": {
+ "hp": 410924,
+ "energy": 1000,
+ "isDead": false,
+ "maxHp": 470771
+ },
+ "56": {
+ "hp": 392296,
+ "energy": 552,
+ "isDead": false,
+ "maxHp": 397431
+ },
+ "6008": {
+ "hp": -1,
+ "energy": 506,
+ "isDead": false,
+ "maxHp": -1
+ }
+ },
+ "defendersState": {
+ "9": {
+ "isDead": true,
+ "hp": 0,
+ "energy": 0,
+ "maxHp": 348525
+ },
+ "48": {
+ "isDead": true,
+ "hp": 0,
+ "energy": 0,
+ "maxHp": 566017
+ },
+ "40": {
+ "isDead": true,
+ "hp": 0,
+ "energy": 0,
+ "maxHp": 386912
+ },
+ "43": {
+ "isDead": true,
+ "hp": 0,
+ "energy": 0,
+ "maxHp": 343417
+ },
+ "16": {
+ "isDead": true,
+ "hp": 0,
+ "energy": 0,
+ "maxHp": 449732
+ },
+ "6006": {
+ "hp": -1,
+ "energy": 439,
+ "isDead": false,
+ "maxHp": -1
+ }
+ },
+ "refillable": {
+ "id": 55,
+ "amount": 10,
+ "lastRefill": 1762932312,
+ "boughtToday": 0,
+ "refillTime": 720
+ },
+ "quests": [
+ {
+ "id": "1779403298",
+ "state": 3,
+ "progress": 11,
+ "reward": {
+ "coin": {
+ "46": "200"
+ }
+ },
+ "createTime": 1762740095
+ },
+ {
+ "id": "1779403303",
+ "state": 1,
+ "progress": 11,
+ "reward": {
+ "coin": {
+ "46": "1000"
+ },
+ "consumable": {
+ "470": "1"
+ }
+ },
+ "createTime": 1762740095
+ }
+ ]
+ }
+ }
+ }]
+}
+```
+
+**Response Fields:**
+- `battle`: Complete battle data including:
+ - `attackers`: Your team composition
+ - `defenders`: Enemy team composition
+ - `result`: Battle outcome with `win`, `stars`, and `serverVersion`
+- `reward`: Rewards earned from the battle
+- `attackersState`: Final state of your team after battle
+- `defendersState`: Final state of enemy team after battle
+- `refillable`: Updated movement energy/charges
+- `quests`: Updated quest progress
+
+**Notes:**
+- The battle result is calculated server-side and returned immediately
+- Battle consumes movement energy (refillable id: 55)
+- Winning battles can reward coins and contribute to quest progress
+- Battle type is `"clan_domination"`
+
+---
+
+### clanDomination_heal
+
+Heal your team in Area of Conquest. This API is used to restore HP to heroes after battles.
+
+**Request:**
+```javascript
+Send({
+ calls: [{
+ name: "clanDomination_heal",
+ args: {},
+ context: { actionTs: 1758453 },
+ ident: "body"
+ }]
+})
+```
+
+**Request Parameters:**
+- No parameters required (empty `args` object)
+
+**Example Response:**
+```json
+{
+ "date": 1762933210.8294661,
+ "results": [{
+ "ident": "body",
+ "result": {
+ "response": null
+ }
+ }]
+}
+```
+
+**Response Fields:**
+- `response`: Returns `null` on success
+
+**Notes:**
+- This API heals your team's heroes after battles
+- The response is `null` when the heal action is successful
+- Healing may have cooldown or resource requirements (check game mechanics)
+- Typically used after battles to restore HP before the next engagement
+
+---
+
+## Reference Tables
+
+### Hero ID Reference
+
+The following table provides a reference for Hero IDs used throughout the Hero Wars API. These IDs may be referenced in reward responses or other API calls.
+
+| ID | Hero Name |
+|----|-----------|
+| 1 | Aurora |
+| 2 | Galahad |
+| 3 | Keira |
+| 4 | Astaroth |
+| 5 | Kai |
+| 6 | Phobos |
+| 7 | Thea |
+| 8 | Daredevil |
+| 9 | Heidi |
+| 10 | Faceless |
+| 11 | Chabba |
+| 12 | Arachne |
+| 13 | Orion |
+| 14 | Fox |
+| 15 | Ginger |
+| 16 | Dante |
+| 17 | Mojo |
+| 18 | Judge |
+| 19 | Dark Star |
+| 20 | Artemis |
+| 21 | Markus |
+| 22 | Peppy |
+| 23 | Lian |
+| 24 | Cleaver |
+| 25 | Ishmael |
+| 26 | Lilith |
+| 27 | Luther |
+| 28 | Qing Mao |
+| 29 | Dorian |
+| 30 | Cornelius |
+| 31 | Jet |
+| 32 | Helios |
+| 33 | Lars |
+| 34 | Krista |
+| 35 | Jorgen |
+| 36 | Maya |
+| 37 | Jhu |
+| 38 | Elmir |
+| 39 | Ziri |
+| 40 | Nebula |
+| 41 | K'arkh |
+| 42 | Rufus |
+| 43 | Celeste |
+| 44 | Astrid and Lucas |
+| 45 | Satori |
+| 46 | Martha |
+| 47 | Andvari |
+| 48 | Sebastian |
+| 49 | Yasmine |
+| 50 | Corvus |
+| 51 | Morrigan |
+| 52 | Isaac |
+| 53 | Alvanor |
+| 54 | Tristan |
+| 55 | Iris |
+| 56 | Amira |
+| 57 | Fafnir |
+| 58 | Aidan |
+| 59 | Kayla |
+| 60 | Mushy and Shroom |
+| 61 | Julius |
+| 62 | Polaris |
+| 63 | Lara Croft |
+| 64 | Augustus |
+| 65 | Ninja Turtles |
+| 66 | Folio |
+| 67 | Lyria |
+| 68 | Guus |
+| 69 | Cascade |
+| 70 | Electra von Grave |
+| 71 | Fluffy |
+| 72 | Byrna |
+| 73 | Adam |
+| 74 | Somna |
+
+**Note:** For heroes added after this table was written, resolve display names in-game with `cheats.translate('LIB_HERO_NAME_' + id)` (e.g. ID 72 → `"Byrna"`).
+
+---
+
+### Titan ID Reference
+
+The following table provides a reference for Titan IDs used throughout the Hero Wars API. These IDs may be referenced in reward responses, team configurations, or other API calls.
+
+#### Water Titans
+
+| ID | Titan Name |
+|----|------------|
+| 4000 | Sigurd |
+| 4001 | Nova |
+| 4002 | Mairi |
+| 4003 | Hyperion |
+| 4004 | Tidus and Gelo |
+
+#### Fire Titans
+
+| ID | Titan Name |
+|----|------------|
+| 4010 | Moloch |
+| 4011 | Vulcan |
+| 4012 | Ignis |
+| 4013 | Araji |
+| 4014 | Asherona and Pyro |
+
+#### Earth Titans
+
+| ID | Titan Name |
+|----|------------|
+| 4020 | Angus |
+| 4021 | Sylva |
+| 4022 | Avalon |
+| 4023 | Eden |
+| 4024 | Verdoc and Phyto |
+
+#### Dark Titans
+
+| ID | Titan Name |
+|----|------------|
+| 4030 | Brustar |
+| 4031 | Keros |
+| 4032 | Mort |
+| 4033 | Tenebris |
+
+#### Light Titans
+
+| ID | Titan Name |
+|----|------------|
+| 4040 | Rigel |
+| 4041 | Amon |
+| 4042 | Iyari |
+| 4043 | Solaris |
+
+---
+
+### Timer/Cooldown ID Reference
+
+The following table provides a reference for Timer/Cooldown IDs used throughout the Hero Wars API. These IDs are used to track various game timers, cooldowns, and reset mechanisms.
+
+| ID | Identifier | Description |
+|----|------------|-------------|
+| 1 | stamina | Stamina refill timer |
+| 2 | skill_point | Skill point refill timer |
+| 3 | bronzeFreeChest | Bronze free chest timer |
+| 4 | goldFreeChest | Gold free chest timer |
+| 5 | arena_cooldown | Arena cooldown timer |
+| 6 | arena_battle | Arena battle attempts |
+| 7 | nicknameChangeCooldown | Nickname change cooldown |
+| 8 | timezoneChangeCooldown | Timezone change cooldown |
+| 9 | eliteMission | Elite mission attempts |
+| 10 | shopReset_merchant | Merchant shop reset |
+| 11 | shopReset_goblin | Goblin shop reset |
+| 12 | shopReset_godfather | Godfather shop reset |
+| 13 | shopReset_arena | Arena shop reset |
+| 14 | shopReset_grandArena | Grand Arena shop reset |
+| 15 | shopReset_crusade | Crusade shop reset |
+| 16 | shopReset_guild | Guild shop reset |
+| 17 | shopReset_soulShop | Soul shop reset |
+| 19 | alchemy | Alchemy attempts |
+| 20 | grand_arena_cooldown | Grand Arena cooldown timer |
+| 21 | grand_arena_battle | Grand Arena battle attempts |
+| 22 | shopReset_socialShop | Social shop reset |
+| 23 | trial_chrono_gold | Chrono Gold trial attempts |
+| 24 | trial_chrono_gold_cooldown | Chrono Gold trial cooldown |
+| 25 | trial_chrono_exp | Chrono EXP trial attempts |
+| 26 | trial_chrono_exp_cooldown | Chrono EXP trial cooldown |
+| 27 | trial_phys | Physical trial attempts |
+| 28 | trial_phys_cooldown | Physical trial cooldown |
+| 29 | trial_mag | Magic trial attempts |
+| 30 | trial_mag_cooldown | Magic trial cooldown |
+| 31 | trial_perk | Perk trial attempts |
+| 32 | trial_perk_cooldown | Perk trial cooldown |
+| 33 | clanReenter_cooldown | Clan re-enter cooldown |
+| 34 | clanAdmire | Clan admire attempts |
+| 35 | diamondFreeChest | Diamond free chest timer |
+| 36 | boss_battle | Boss battle attempts |
+| 37 | chest_town | Town chest reset |
+| 38 | shopReset_boss | Boss shop reset |
+| 39 | boss_cooldown | Boss cooldown timer |
+| 40 | lootBox_egg_blue | Blue egg loot box timer |
+| 41 | lootBox_egg_purple | Purple egg loot box timer |
+| 42 | lootBox_egg_orange | Orange egg loot box timer |
+| 43 | shopReset_gvg | Guild War shop reset |
+| 44 | shopReset_titanArtifact | Titan Artifact shop reset |
+| 45 | adventure | Adventure attempts |
+| 46 | shopReset_petSoulShop | Pet Soul shop reset |
+| 47 | ascensionChest_free | Free ascension chest timer |
+| 48 | brawl_battle | Brawl battle attempts |
+| 49 | newGacha_key | New gacha key timer |
+| 50 | shopReset_merchantPromo | Merchant promo shop reset |
+| 51 | shopReset_merchantPromoV2 | Merchant promo V2 shop reset |
+| 52 | epic_brawl_battle | Epic brawl battle attempts |
+| 53 | epic_brawl_battle_reroll | Epic brawl battle reroll |
+| 54 | rewardedVideo_cooldown | Rewarded video cooldown |
+| 55 | clan_domination | Clan domination timer |
+| 56 | leagueArena_battle | League Arena battle attempts |
+| 57 | leagueArena_enemyRefresh | League Arena enemy refresh timer |
+| 58 | leagueArena_wheelTicket | League Arena wheel ticket timer |
+| 59 | leagueArena_battle_altRefresh | League Arena battle alternate refresh |
+| 60 | tmntRerollCost | TMNT reroll cost |
+| 61 | LavkaRefill | Lavka refill timer |
+
+**Note:** Timer/Cooldown objects typically contain the following properties:
+- `id`: The timer ID
+- `ident`: The identifier string
+- `refillSeconds`: Time in seconds until refill (if applicable)
+- `maxValue`: Maximum value array
+- `maxRefillCount`: Maximum refill count array
+- `refillByReset`: Whether refill resets on daily reset (0 = no, 1 = yes)
+- `refillCountResetLocalTime`: Array indicating local time reset
+- `serverTimeRefill`: Whether server time is used for refill (if applicable)
+
+---
+
+## Additional Resources
+
+**Note:** All specialized API documentation has been consolidated into this document. The following separate documentation files are now deprecated:
+- `ARENA_API_DOCUMENTATION.md` - Consolidated into Arena API section
+- `GrandArenaAPI_Documentation.md` - Consolidated into Grand Arena API section
+- `GUILD_WAR_API_DOCUMENTATION.md` - Consolidated into Guild War API section
+- `CLAN_RAID_API_DOCUMENTATION.md` - Consolidated into Clan Raid API (Minions Attack) section
+- `COW_API_DOCUMENTATION.md` - Consolidated into Cross Clan War API section
+- `SECRET_WEALTH_SHOP_API_DOCUMENTATION.md` - Consolidated into Secret Wealth Shop API section
+- `TITAN_ARTIFACT_SHOP_API_DOCUMENTATION.md` - Consolidated into Titan Artifact Shop API section
+- `TEAMGETALL_API_DOCUMENTATION.md` - Consolidated into TeamGetAll API section
+- `DEMO_BATTLE_API_DOCUMENTATION.md` - Consolidated into Demo Battle API section
+- `REWARDS_API_DOCUMENTATION.md` - Consolidated into Special Offers section and Reference Tables section
+
+For the most up-to-date API documentation, refer to this consolidated document.
+
diff --git a/HWHExtension.user.js b/HWHExtension.user.js
new file mode 100644
index 0000000..889c220
--- /dev/null
+++ b/HWHExtension.user.js
@@ -0,0 +1,798 @@
+// ==UserScript==
+// @name HWHExtension
+// @name:en HWHExtension
+// @name:ru HWHExtension
+// @namespace HWHExtension
+// @version 1.0.2
+// @description Extension for HeroWarsHelper script
+// @description:en Extension for HeroWarsHelper script
+// @author ZingerY
+// @license Copyright ZingerY
+// @homepage https://zingery.ru/scripts/HWHExtension.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.org/scripts/523551/HWHExtension.user.js
+// @updateURL https://update.greasyfork.org/scripts/523551/HWHExtension.meta.js
+// ==/UserScript==
+
+(function () {
+
+if (!this.HWHClasses) {
+ console.log('%cObject for extension not found', 'color: red');
+ return;
+}
+
+console.log('%cStart Extension ' + GM_info.script.name + ', v' + GM_info.script.version + ' by ' + GM_info.script.author, 'color: red');
+const { addExtentionName } = HWHFuncs;
+addExtentionName(GM_info.script.name, GM_info.script.version, GM_info.script.author);
+
+const {
+ getInput,
+ setProgress,
+ hideProgress,
+ I18N,
+ send,
+ getTimer,
+ countdownTimer,
+ getUserInfo,
+ getSaveVal,
+ setSaveVal,
+ popup,
+ setIsCancalBattle,
+ random,
+} = HWHFuncs;
+
+function executeDungeon(resolve, reject) {
+ let countPredictionCard = 0;
+ let dungeonActivity = 0;
+ let startDungeonActivity = 0;
+ let maxDungeonActivity = 150;
+ let limitDungeonActivity = 30180;
+ let countShowStats = 1;
+ //let fastMode = isChecked('fastMode');
+ let end = false;
+
+ let countTeam = [];
+ let timeDungeon = {
+ all: new Date().getTime(),
+ findAttack: 0,
+ attackNeutral: 0,
+ attackEarthOrFire: 0,
+ };
+
+ let titansStates = {};
+ let bestBattle = {};
+
+ let teams = {
+ neutral: [],
+ water: [],
+ earth: [],
+ fire: [],
+ hero: [],
+ };
+
+ //тест
+ let talentMsg = '';
+ let talentMsgReward = '';
+
+ let callsExecuteDungeon = {
+ calls: [
+ {
+ name: 'dungeonGetInfo',
+ args: {},
+ ident: 'dungeonGetInfo',
+ },
+ {
+ name: 'teamGetAll',
+ args: {},
+ ident: 'teamGetAll',
+ },
+ {
+ name: 'teamGetFavor',
+ args: {},
+ ident: 'teamGetFavor',
+ },
+ {
+ name: 'clanGetInfo',
+ args: {},
+ ident: 'clanGetInfo',
+ },
+ {
+ name: 'inventoryGet',
+ args: {},
+ ident: 'inventoryGet',
+ },
+ ],
+ };
+
+ this.start = async function (titanit) {
+ //maxDungeonActivity = titanit > limitDungeonActivity ? limitDungeonActivity : titanit;
+ maxDungeonActivity = titanit || getInput('countTitanit');
+ send(JSON.stringify(callsExecuteDungeon), startDungeon);
+ };
+
+ /** Получаем данные по подземелью */
+ function startDungeon(e) {
+ stopDung = false; // стоп подземка
+ let res = e.results;
+ let dungeonGetInfo = res[0].result.response;
+ if (!dungeonGetInfo) {
+ endDungeon('noDungeon', res);
+ return;
+ }
+ console.log('Начинаем копать на фулл: ', new Date());
+ let teamGetAll = res[1].result.response;
+ let teamGetFavor = res[2].result.response;
+ dungeonActivity = res[3].result.response.stat.todayDungeonActivity;
+ startDungeonActivity = res[3].result.response.stat.todayDungeonActivity;
+ countPredictionCard = res[4].result.response.consumable[81];
+ titansStates = dungeonGetInfo.states.titans;
+
+ teams.hero = {
+ favor: teamGetFavor.dungeon_hero,
+ heroes: teamGetAll.dungeon_hero.filter((id) => id < 6000),
+ teamNum: 0,
+ };
+ let heroPet = teamGetAll.dungeon_hero.filter((id) => id >= 6000).pop();
+ if (heroPet) {
+ teams.hero.pet = heroPet;
+ }
+ teams.neutral = getTitanTeam('neutral');
+ teams.water = {
+ favor: {},
+ heroes: getTitanTeam('water'),
+ teamNum: 0,
+ };
+ teams.earth = {
+ favor: {},
+ heroes: getTitanTeam('earth'),
+ teamNum: 0,
+ };
+ teams.fire = {
+ favor: {},
+ heroes: getTitanTeam('fire'),
+ teamNum: 0,
+ };
+
+ checkFloor(dungeonGetInfo);
+ }
+
+ function getTitanTeam(type) {
+ switch (type) {
+ case 'neutral':
+ return [4023, 4022, 4012, 4021, 4011, 4010, 4020, 4024, 4014];
+ case 'water':
+ return [4000, 4001, 4002, 4003].filter((e) => !titansStates[e]?.isDead);
+ case 'earth':
+ return [4020, 4022, 4021, 4023,4024].filter((e) => !titansStates[e]?.isDead);
+ case 'fire':
+ return [4010, 4011, 4012, 4013, 4014].filter((e) => !titansStates[e]?.isDead);
+ }
+ }
+
+ /** Создать копию объекта */
+ function clone(a) {
+ return JSON.parse(JSON.stringify(a));
+ }
+
+ /** Находит стихию на этаже */
+ function findElement(floor, element) {
+ for (let i in floor) {
+ if (floor[i].attackerType === element) {
+ return i;
+ }
+ }
+ return undefined;
+ }
+
+ /** Проверяем этаж */
+ async function checkFloor(dungeonInfo) {
+ if (!('floor' in dungeonInfo) || dungeonInfo.floor?.state == 2) {
+ saveProgress();
+ return;
+ }
+ checkTalent(dungeonInfo);
+ // console.log(dungeonInfo, dungeonActivity);
+ maxDungeonActivity = getInput('countTitanit');
+ setProgress(`${I18N('DUNGEON')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity} ${talentMsg}`);
+ //setProgress('Dungeon: Титанит ' + dungeonActivity + '/' + maxDungeonActivity);
+ if (dungeonActivity >= maxDungeonActivity) {
+ endDungeon('Стоп подземка,', 'набрано титанита: ' + dungeonActivity + '/' + maxDungeonActivity);
+ return;
+ }
+ let activity = dungeonActivity - startDungeonActivity;
+ titansStates = dungeonInfo.states.titans;
+ if (stopDung) {
+ endDungeon('Стоп подземка,', 'набрано титанита: ' + dungeonActivity + '/' + maxDungeonActivity);
+ return;
+ }
+ /*if (activity / 1000 > countShowStats) {
+ countShowStats++;
+ showStats();
+ }*/
+ bestBattle = {};
+ let floorChoices = dungeonInfo.floor.userData;
+ if (floorChoices.length > 1) {
+ for (let element in teams) {
+ let teamNum = findElement(floorChoices, element);
+ if (!!teamNum) {
+ if (element == 'earth') {
+ teamNum = await chooseEarthOrFire(floorChoices);
+ if (teamNum < 0) {
+ endDungeon('Невозможно победить без потери Титана!', dungeonInfo);
+ return;
+ }
+ }
+ chooseElement(floorChoices[teamNum].attackerType, teamNum);
+ return;
+ }
+ }
+ } else {
+ chooseElement(floorChoices[0].attackerType, 0);
+ }
+ }
+ //тест черепахи
+ async function checkTalent(dungeonInfo) {
+ const talent = dungeonInfo.talent;
+ if (!talent) {
+ return;
+ }
+ const dungeonFloor = +dungeonInfo.floorNumber;
+ const talentFloor = +talent.floorRandValue;
+ let doorsAmount = 3 - talent.conditions.doorsAmount;
+
+ if (dungeonFloor === talentFloor && (!doorsAmount || !talent.conditions?.farmedDoors[dungeonFloor])) {
+ const reward = await Send({
+ calls: [
+ { name: 'heroTalent_getReward', args: { talentType: 'tmntDungeonTalent', reroll: false }, ident: 'group_0_body' },
+ { name: 'heroTalent_farmReward', args: { talentType: 'tmntDungeonTalent' }, ident: 'group_1_body' },
+ ],
+ }).then((e) => e.results[0].result.response);
+ const type = Object.keys(reward).pop();
+ const itemId = Object.keys(reward[type]).pop();
+ const count = reward[type][itemId];
+ const itemName = cheats.translate(`LIB_${type.toUpperCase()}_NAME_${itemId}`);
+ talentMsgReward += ` ${count} ${itemName}`;
+ doorsAmount++;
+ }
+ talentMsg = ` TMNT Talent: ${doorsAmount}/3 ${talentMsgReward} `;
+ }
+
+ /** Выбираем огнем или землей атаковать */
+ async function chooseEarthOrFire(floorChoices) {
+ bestBattle.recovery = -11;
+ let selectedTeamNum = -1;
+ for (let attempt = 0; selectedTeamNum < 0 && attempt < 4; attempt++) {
+ for (let teamNum in floorChoices) {
+ let attackerType = floorChoices[teamNum].attackerType;
+ selectedTeamNum = await attemptAttackEarthOrFire(teamNum, attackerType, attempt);
+ }
+ }
+ console.log('Выбор команды огня или земли: ', selectedTeamNum < 0 ? 'не сделан' : floorChoices[selectedTeamNum].attackerType);
+ return selectedTeamNum;
+ }
+
+ /** Попытка атаки землей и огнем */
+ async function attemptAttackEarthOrFire(teamNum, attackerType, attempt) {
+ let start = new Date();
+ let team = clone(teams[attackerType]);
+ let startIndex = team.heroes.length + attempt - 4;
+ if (startIndex >= 0) {
+ team.heroes = team.heroes.slice(startIndex);
+ let recovery = await getBestRecovery(teamNum, attackerType, team, 25);
+ if (recovery > bestBattle.recovery) {
+ bestBattle.recovery = recovery;
+ bestBattle.selectedTeamNum = teamNum;
+ bestBattle.team = team;
+ }
+ }
+ let workTime = new Date().getTime() - start.getTime();
+ timeDungeon.attackEarthOrFire += workTime;
+ if (bestBattle.recovery < -10) {
+ return -1;
+ }
+ return bestBattle.selectedTeamNum;
+ }
+
+ /** Выбираем стихию для атаки */
+ async function chooseElement(attackerType, teamNum) {
+ let result;
+ switch (attackerType) {
+ case 'hero':
+ case 'water':
+ result = await startBattle(teamNum, attackerType, teams[attackerType]);
+ break;
+ case 'earth':
+ case 'fire':
+ result = await attackEarthOrFire(teamNum, attackerType);
+ break;
+ case 'neutral':
+ result = await attackNeutral(teamNum, attackerType);
+ }
+ if (!!result && attackerType != 'hero') {
+ let recovery = (!!!bestBattle.recovery ? 10 * getRecovery(result) : bestBattle.recovery) * 100;
+ let titans = result.progress[0].attackers.heroes;
+ console.log('Проведен бой: ' + attackerType + ', recovery = ' + (recovery > 0 ? '+' : '') + Math.round(recovery) + '% \r\n', titans);
+ }
+ endBattle(result);
+ }
+
+ /** Атакуем Землей или Огнем */
+ async function attackEarthOrFire(teamNum, attackerType) {
+ if (!!!bestBattle.recovery) {
+ bestBattle.recovery = -11;
+ let selectedTeamNum = -1;
+ for (let attempt = 0; selectedTeamNum < 0 && attempt < 4; attempt++) {
+ selectedTeamNum = await attemptAttackEarthOrFire(teamNum, attackerType, attempt);
+ }
+ if (selectedTeamNum < 0) {
+ endDungeon('Невозможно победить без потери Титана!', attackerType);
+ return;
+ }
+ }
+ return findAttack(teamNum, attackerType, bestBattle.team);
+ }
+
+ /** Находим подходящий результат для атаки */
+ async function findAttack(teamNum, attackerType, team) {
+ let start = new Date();
+ let recovery = -1000;
+ let iterations = 0;
+ let result;
+ let correction = 0.01;
+ for (let needRecovery = bestBattle.recovery; recovery < needRecovery; needRecovery -= correction, iterations++) {
+ result = await startBattle(teamNum, attackerType, team);
+ recovery = getRecovery(result);
+ }
+ bestBattle.recovery = recovery;
+ let workTime = new Date().getTime() - start.getTime();
+ timeDungeon.findAttack += workTime;
+ return result;
+ }
+
+ /** Атакуем Нейтральной командой */
+ async function attackNeutral(teamNum, attackerType) {
+ let start = new Date();
+ let factors = calcFactor();
+ bestBattle.recovery = -0.2;
+ await findBestBattleNeutral(teamNum, attackerType, factors, true);
+ if (bestBattle.recovery < 0 || (bestBattle.recovery < 0.2 && factors[0].value < 0.5)) {
+ let recovery = 100 * bestBattle.recovery;
+ console.log(
+ 'Не удалось найти удачный бой в быстром режиме: ' +
+ attackerType +
+ ', recovery = ' +
+ (recovery > 0 ? '+' : '') +
+ Math.round(recovery) +
+ '% \r\n',
+ bestBattle.attackers
+ );
+ await findBestBattleNeutral(teamNum, attackerType, factors, false);
+ }
+ let workTime = new Date().getTime() - start.getTime();
+ timeDungeon.attackNeutral += workTime;
+ if (!!bestBattle.attackers) {
+ let team = getTeam(bestBattle.attackers);
+ return findAttack(teamNum, attackerType, team);
+ }
+ endDungeon('Не удалось найти удачный бой!', attackerType);
+ return undefined;
+ }
+
+ /** Находит лучшую нейтральную команду */
+ async function findBestBattleNeutral(teamNum, attackerType, factors, mode) {
+ let countFactors = factors.length < 4 ? factors.length : 4;
+ let aradgi = !titansStates['4013']?.isDead;
+ let edem = !titansStates['4023']?.isDead;
+ let dark = [4032, 4033].filter((e) => !titansStates[e]?.isDead);
+ let light = [4042].filter((e) => !titansStates[e]?.isDead);
+ let actions = [];
+ if (mode) {
+ for (let i = 0; i < countFactors; i++) {
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(factors[i].id)));
+ }
+ if (countFactors > 1) {
+ let firstId = factors[0].id;
+ let secondId = factors[1].id;
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4001, secondId)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4002, secondId)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4003, secondId)));
+ }
+ if (aradgi) {
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(4013)));
+ if (countFactors > 0) {
+ let firstId = factors[0].id;
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4000, 4013)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4001, 4013)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4002, 4013)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4003, 4013)));
+ }
+ if (edem) {
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(4023, 4000, 4013)));
+ }
+ }
+ } else {
+ if (mode) {
+ for (let i = 0; i < factors.length; i++) {
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(factors[i].id)));
+ }
+ } else {
+ countFactors = factors.length < 2 ? factors.length : 2;
+ }
+ for (let i = 0; i < countFactors; i++) {
+ let mainId = factors[i].id;
+ if (aradgi && (mode || i > 0)) {
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4000, 4013)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4001, 4013)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4002, 4013)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4003, 4013)));
+ }
+ for (let i = 0; i < dark.length; i++) {
+ let darkId = dark[i];
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4001, darkId)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4002, darkId)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4003, darkId)));
+ }
+ for (let i = 0; i < light.length; i++) {
+ let lightId = light[i];
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4001, lightId)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4002, lightId)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4003, lightId)));
+ }
+ let isFull = mode || i > 0;
+ for (let j = isFull ? i + 1 : 2; j < factors.length; j++) {
+ let extraId = factors[j].id;
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4000, extraId)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4001, extraId)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(mainId, 4002, extraId)));
+ }
+ }
+ if (aradgi) {
+ if (mode) {
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(4013)));
+ }
+ for (let i = 0; i < dark.length; i++) {
+ let darkId = dark[i];
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(darkId, 4001, 4013)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(darkId, 4002, 4013)));
+ }
+ for (let i = 0; i < light.length; i++) {
+ let lightId = light[i];
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(lightId, 4001, 4013)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(lightId, 4002, 4013)));
+ }
+ }
+ for (let i = 0; i < dark.length; i++) {
+ let firstId = dark[i];
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId)));
+ for (let j = i + 1; j < dark.length; j++) {
+ let secondId = dark[j];
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4001, secondId)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4002, secondId)));
+ }
+ }
+ for (let i = 0; i < light.length; i++) {
+ let firstId = light[i];
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId)));
+ for (let j = i + 1; j < light.length; j++) {
+ let secondId = light[j];
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4001, secondId)));
+ actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4002, secondId)));
+ }
+ }
+ }
+ for (let result of await Promise.all(actions)) {
+ let recovery = getRecovery(result);
+ if (recovery > bestBattle.recovery) {
+ bestBattle.recovery = recovery;
+ bestBattle.attackers = result.progress[0].attackers.heroes;
+ }
+ }
+ }
+
+ /** Получаем нейтральную команду */
+ function getNeutralTeam(id, swapId, addId) {
+ let neutralTeam = clone(teams.water);
+ let neutral = neutralTeam.heroes;
+ if (neutral.length == 4) {
+ if (!!swapId) {
+ for (let i in neutral) {
+ if (neutral[i] == swapId) {
+ neutral[i] = addId;
+ }
+ }
+ }
+ } else if (!!addId) {
+ neutral.push(addId);
+ }
+ neutral.push(id);
+ return neutralTeam;
+ }
+
+ /** Получить команду титанов */
+ function getTeam(titans) {
+ return {
+ favor: {},
+ heroes: Object.keys(titans).map((id) => parseInt(id)),
+ teamNum: 0,
+ };
+ }
+
+ /** Вычисляем фактор боеготовности титанов */
+ function calcFactor() {
+ let neutral = teams.neutral;
+ let factors = [];
+ for (let i in neutral) {
+ let titanId = neutral[i];
+ let titan = titansStates[titanId];
+ let factor = !!titan ? titan.hp / titan.maxHp + titan.energy / 10000.0 : 1;
+ if (factor > 0) {
+ factors.push({ id: titanId, value: factor });
+ }
+ }
+ factors.sort(function (a, b) {
+ return a.value - b.value;
+ });
+ return factors;
+ }
+
+ /** Возвращает наилучший результат из нескольких боев */
+ async function getBestRecovery(teamNum, attackerType, team, countBattle) {
+ let bestRecovery = -1000;
+ let actions = [];
+ for (let i = 0; i < countBattle; i++) {
+ actions.push(startBattle(teamNum, attackerType, team));
+ }
+ for (let result of await Promise.all(actions)) {
+ let recovery = getRecovery(result);
+ if (recovery > bestRecovery) {
+ bestRecovery = recovery;
+ }
+ }
+ return bestRecovery;
+ }
+
+ /** Возвращает разницу в здоровье атакующей команды после и до битвы и проверяет здоровье титанов на необходимый минимум*/
+ function getRecovery(result) {
+ if (result.result.stars < 3) {
+ return -100;
+ }
+ let beforeSumFactor = 0;
+ let afterSumFactor = 0;
+ let beforeTitans = result.battleData.attackers;
+ let afterTitans = result.progress[0].attackers.heroes;
+ for (let i in afterTitans) {
+ let titan = afterTitans[i];
+ let percentHP = titan.hp / beforeTitans[i].hp;
+ let energy = titan.energy;
+ let factor = checkTitan(i, energy, percentHP) ? getFactor(i, energy, percentHP) : -100;
+ afterSumFactor += factor;
+ }
+ for (let i in beforeTitans) {
+ let titan = beforeTitans[i];
+ let state = titan.state;
+ beforeSumFactor += !!state ? getFactor(i, state.energy, state.hp / titan.hp) : 1;
+ }
+ return afterSumFactor - beforeSumFactor;
+ }
+
+ /** Возвращает состояние титана*/
+ function getFactor(id, energy, percentHP) {
+ let elemantId = id.slice(2, 3);
+ let isEarthOrFire = elemantId == '1' || elemantId == '2';
+ let energyBonus = id == '4020' && energy == 1000 ? 0.1 : energy / 20000.0;
+ let factor = percentHP + energyBonus;
+ return isEarthOrFire ? factor : factor / 10;
+ }
+
+ /** Проверяет состояние титана*/
+ function checkTitan(id, energy, percentHP) {
+ switch (id) {
+ case '4020':
+ return percentHP > 0.25 || (energy == 1000 && percentHP > 0.05);
+ break;
+ case '4010':
+ return percentHP + energy / 2000.0 > 0.63;
+ break;
+ case '4000':
+ return percentHP > 0.62 || (energy < 1000 && ((percentHP > 0.45 && energy >= 400) || (percentHP > 0.3 && energy >= 670)));
+ }
+ return true;
+ }
+
+ /** Начинаем бой */
+ function startBattle(teamNum, attackerType, args) {
+ return new Promise(function (resolve, reject) {
+ args.teamNum = teamNum;
+ let startBattleCall = {
+ calls: [
+ {
+ name: 'dungeonStartBattle',
+ args,
+ ident: 'body',
+ },
+ ],
+ };
+ send(JSON.stringify(startBattleCall), resultBattle, {
+ resolve,
+ teamNum,
+ attackerType,
+ });
+ });
+ }
+
+ /** Возращает результат боя в промис */
+ /*function resultBattle(resultBattles, args) {
+ if (!!resultBattles && !!resultBattles.results) {
+ let battleData = resultBattles.results[0].result.response;
+ let battleType = "get_tower";
+ if (battleData.type == "dungeon_titan") {
+ battleType = "get_titan";
+ }
+ battleData.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }];//тест подземка правки
+ BattleCalc(battleData, battleType, function (result) {
+ result.teamNum = args.teamNum;
+ result.attackerType = args.attackerType;
+ args.resolve(result);
+ });
+ } else {
+ endDungeon('Потеряна связь с сервером игры!', 'break');
+ }
+ }*/
+ function resultBattle(resultBattles, args) {
+ battleData = resultBattles.results[0].result.response;
+ battleType = 'get_tower';
+ if (battleData.type == 'dungeon_titan') {
+ battleType = 'get_titan';
+ }
+ battleData.progress = [{ attackers: { input: ['auto', 0, 0, 'auto', 0, 0] } }];
+ BattleCalc(battleData, battleType, function (result) {
+ result.teamNum = args.teamNum;
+ result.attackerType = args.attackerType;
+ args.resolve(result);
+ });
+ }
+
+ /** Заканчиваем бой */
+
+ ////
+ async function endBattle(battleInfo) {
+ if (!!battleInfo) {
+ const args = {
+ result: battleInfo.result,
+ progress: battleInfo.progress,
+ };
+ if (battleInfo.result.stars < 3) {
+ endDungeon('Герой или Титан мог погибнуть в бою!', battleInfo);
+ return;
+ }
+ if (countPredictionCard > 0) {
+ args.isRaid = true;
+ countPredictionCard--;
+ } else {
+ const timer = getTimer(battleInfo.battleTime);
+ console.log(timer);
+ await countdownTimer(timer, `${I18N('DUNGEON')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity} ${talentMsg}`);
+ }
+ const calls = [
+ {
+ name: 'dungeonEndBattle',
+ args,
+ ident: 'body',
+ },
+ ];
+ lastDungeonBattleData = null;
+ send(JSON.stringify({ calls }), resultEndBattle);
+ } else {
+ endDungeon('dungeonEndBattle win: false\n', battleInfo);
+ }
+ }
+ /** Получаем и обрабатываем результаты боя */
+ function resultEndBattle(e) {
+ if (!!e && !!e.results) {
+ let battleResult = e.results[0].result.response;
+ if ('error' in battleResult) {
+ endDungeon('errorBattleResult', battleResult);
+ return;
+ }
+ let dungeonGetInfo = battleResult.dungeon ?? battleResult;
+ dungeonActivity += battleResult.reward.dungeonActivity ?? 0;
+ checkFloor(dungeonGetInfo);
+ } else {
+ endDungeon('Потеряна связь с сервером игры!', 'break');
+ }
+ }
+
+ /** Добавить команду титанов в общий список команд */
+ function addTeam(team) {
+ for (let i in countTeam) {
+ if (equalsTeam(countTeam[i].team, team)) {
+ countTeam[i].count++;
+ return;
+ }
+ }
+ countTeam.push({ team: team, count: 1 });
+ }
+
+ /** Сравнить команды на равенство */
+ function equalsTeam(team1, team2) {
+ if (team1.length == team2.length) {
+ for (let i in team1) {
+ if (team1[i] != team2[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ function saveProgress() {
+ let saveProgressCall = {
+ calls: [
+ {
+ name: 'dungeonSaveProgress',
+ args: {},
+ ident: 'body',
+ },
+ ],
+ };
+ send(JSON.stringify(saveProgressCall), resultEndBattle);
+ }
+
+ /** Выводит статистику прохождения подземелья */
+ function showStats() {
+ let activity = dungeonActivity - startDungeonActivity;
+ let workTime = clone(timeDungeon);
+ workTime.all = new Date().getTime() - workTime.all;
+ for (let i in workTime) {
+ workTime[i] = Math.round(workTime[i] / 1000);
+ }
+ countTeam.sort(function (a, b) {
+ return b.count - a.count;
+ });
+ console.log(titansStates);
+ console.log('Собрано титанита: ', activity);
+ console.log('Скорость сбора: ' + Math.round((3600 * activity) / workTime.all) + ' титанита/час');
+ console.log('Время раскопок: ');
+ for (let i in workTime) {
+ let timeNow = workTime[i];
+ console.log(
+ i + ': ',
+ Math.round(timeNow / 3600) + ' ч. ' + Math.round((timeNow % 3600) / 60) + ' мин. ' + (timeNow % 60) + ' сек.'
+ );
+ }
+ console.log('Частота использования команд: ');
+ for (let i in countTeam) {
+ let teams = countTeam[i];
+ console.log(teams.team + ': ', teams.count);
+ }
+ }
+
+ /** Заканчиваем копать подземелье */
+ function endDungeon(reason, info) {
+ if (!end) {
+ end = true;
+ console.log(reason, info);
+ showStats();
+ if (info == 'break') {
+ setProgress(
+ 'Dungeon stoped: Титанит ' + dungeonActivity + '/' + maxDungeonActivity + '\r\nПотеряна связь с сервером игры!',
+ false,
+ hideProgress
+ );
+ } else {
+ setProgress('Dungeon completed: Титанит ' + dungeonActivity + '/' + maxDungeonActivity, false, hideProgress);
+ }
+ setTimeout(cheats.refreshGame, 1000);
+ resolve();
+ }
+ }
+}
+
+this.HWHClasses.executeDungeon = executeDungeon;
+
+})();
diff --git a/HWHGiftOfTheElementsExt.user.js b/HWHGiftOfTheElementsExt.user.js
new file mode 100644
index 0000000..a85cec2
--- /dev/null
+++ b/HWHGiftOfTheElementsExt.user.js
@@ -0,0 +1,854 @@
+// ==UserScript==
+// @name HWHGiftOfTheElementsExt
+// @name:en HWHGiftOfTheElementsExt
+// @name:ru HWHGiftOfTheElementsExt
+// @namespace HWHGiftOfTheElementsExt
+// @version 4.0.1
+// @description Extension for HeroWarsHelper script
+// @description:en Extension for HeroWarsHelper script
+// @description:ru Расширение для скрипта HeroWarsHelper
+// @author Green
+// @license Copyright Green
+// @icon https://i.ibb.co/xtmhK7zS/icon.png
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @grant none
+// @run-at document-end
+// @downloadURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/HWHGiftOfTheElementsExt.user.js
+// @updateURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/HWHGiftOfTheElementsExt.user.js
+// ==/UserScript==
+
+(function () {
+ if (!this.HWHClasses) {
+ console.log('%cObject for extension not found', 'color: red');
+ return;
+ }
+
+ console.log('%cStart Extension ' + GM_info.script.name + ', v' + GM_info.script.version + ' by ' + GM_info.script.author, 'color: red');
+ const { addExtentionName } = HWHFuncs;
+ addExtentionName(GM_info.script.name, GM_info.script.version, GM_info.script.author);
+
+ const { popup, confShow, setProgress } = HWHFuncs;
+ const { i18nLangData } = HWHData;
+
+ // Constants
+ const POWER_LEVEL = [22, 22, 22, 22, 22, 66, 66, 66, 66, 66, 110, 110, 110, 110, 110, 154,
+ 154, 154, 154, 154, 198, 198, 198, 198, 198, 242, 242, 242, 242, 242];
+ const MAX_TITAN_GIFT_LEVEL = 30;
+ const TARGET_GIFT_LEVEL_GET_POWER = 29;
+ const MIN_USER_LEVEL = 30;
+ const CONSUMABLE_ID_TITAN_GIFT = 24;
+ const AUTO_EXECUTION_TIMEOUT = 100;
+ const AUTO_EXECUTION_DELAY = 3000;
+
+ const i18nLangDataEn = {
+ GIFT_OF_ELEMENTS: 'Gift of the Elements',
+ GIFT_OF_ELEMENTS_TITLE: 'Spend "Sparks of Power" or Reset "Gifts of the Elements"',
+ GOE_SPEND_SPARKS_OF_POWER: 'Spend "Sparks of Power"',
+ GOE_SPEND_SPARKS_OF_POWER_TITLE: 'Spend "Sparks of Power"',
+ GOE_RESET_GIFTS: 'Reset "Gifts of the Elements"',
+ GOE_RESET_GIFTS_LIGHT: 'Reset "Gifts of the Elements" level 1 - 29 ',
+ GOE_RESET_GIFTS_LIGHT_TITLE: 'Reset "Gifts of the Elements". You can\'t reset Gift of the Elements level 30.',
+ GOE_RESET_GIFTS_EXTREME: 'Reset "Gifts of the Elements" level 30 ',
+ GOE_RESET_GIFTS_EXTREME_TITLE: 'Reset "Gifts of the Elements". There is no limit to the level of the Gift of Elements.',
+ GOE_SELECT_ACTION: 'Select an action',
+ GOE_NOTHING_TO_IMPROVE_LVL30: 'Nothing to improve. Account hasn\'t reached team level 30',
+ GOE_NOTHING_TO_IMPROVE: 'Nothing to improve. All heroes have maximum elemental gift level',
+ GOE_SPEND_SPARKS_OF_POWER_MESSAGE:
+ 'Available {titanGift} sparks of power Specify how many sparks of power need to be spent',
+ GOE_INCORRECT_VALUE: 'Incorrect value',
+ GOE_IMPROVING_START: 'Improving the Gift of the Elements...',
+ GOE_NOT_ENOUGH_RESOURCES: 'Not enough gold or sparks of power',
+ GOE_ALL_HEROES_HAVE_30LVL: ' All heroes have reached level 30 in Gifts of the Elements',
+ GOE_GOLD_IS_GONE: ' The gold is gone ',
+ GOE_PROGRESS_OF_IMPROVEMENT_MESSAGE: 'Gift of the Elements has been upgraded to level {titanGiftLevel} ',
+ GOE_RESULT_OF_IMPROVEMENT: 'Gift of the Elements has been upgraded {counter} time(s)',
+ GOE_NOTHING_TO_RESET: 'Nothing to reset',
+ GOE_IMPOSSIBLE_TO_RESET: 'You don\'t have any heroes with Elemental Gift below level 30',
+ GOE_RESET_GIFTS_LIGHT_MESSAGE:
+ `Specify the maximum reset level for Gift of the Elements
+ Range: 1 to 29 `,
+ GOE_RESULT_RESET_GIFTS: 'Gift of the Elements has been reset for {counter} hero(es)',
+ GOE_RESET_GIFTS_EXTREME_MESSAGE:
+ `You have {level30} hero(es) with level 30 Gift of the Elements
+ Enter how many level 30 Gifts of the Elements to reset
+ The reset will start with the weakest hero Gifts of the Elements of lower levels will be reset automatically`,
+ GOE_EXTREME_DO_NOT_HAVE_HERO_30LVL:
+ 'You don\'t have any heroes with Gift of the Elements level 30 Reset Gifts of the Elements to a lower level?',
+ GOE_EXTREME_RESULT_RESET_GIFTS:
+ ' {counter30} of them are level 30 ',
+ GOE_GET_POWER: 'Get power',
+ GOE_GET_POWER_TITLE: 'Increase hero power by upgrading highest-power heroes\' Gifts of the Elements to level 29',
+ GOE_GET_POWER_MESSAGE:
+ `Upgrades the highest power hero's Gift of the Elements to level 29 first, then the next highest.
+ Maximum achievable hero power: {maxHeroPawer}
+ Specify how much hero power you want to get`,
+ GOE_GOT_POWER: ' Received {gotPower} hero power',
+ GOE_NOT_ENOUGH_GOLD:
+ `Not enough gold to get all available hero power
+ Heve gold: {haveGold} Gold needed: {goldIsNeeded} `,
+ GOE_AUTO_GET_POWER: 'Auto Get Power',
+ GOE_AUTO_GET_POWER_TITLE: 'Automatically get power when script loads',
+ GOE_AUTO_GET_POWER_AMOUNT: 'Auto Get Power Amount',
+ GOE_AUTO_GET_POWER_AMOUNT_TITLE: 'Amount of power to get automatically (0 = disabled)',
+ };
+
+ i18nLangData['en'] = Object.assign(i18nLangData['en'], i18nLangDataEn);
+
+ const i18nLangDataRu = {
+ GIFT_OF_ELEMENTS: 'Дар стихий',
+ GIFT_OF_ELEMENTS_TITLE: 'Потратить "Искры мощи" или Сбросить "Дары стихий"',
+ GOE_SPEND_SPARKS_OF_POWER: 'Потратить "Искры мощи"',
+ GOE_SPEND_SPARKS_OF_POWER_TITLE: 'Потратить "Искры мощи"',
+ GOE_RESET_GIFTS: 'Сбросить "Дары стихий"',
+ GOE_RESET_GIFTS_LIGHT: 'Сбросить "Дары стихий" 1 - 29 уровня ',
+ GOE_RESET_GIFTS_LIGHT_TITLE: 'Сбросить "Дары стихий". Не сбрасывается 30 уровень дара стихий',
+ GOE_RESET_GIFTS_EXTREME: 'Сбросить "Дары стихий" 30 уровня ',
+ GOE_RESET_GIFTS_EXTREME_TITLE: 'Сбросить "Дары стихий". Нет ограничений уровня дара стихий',
+ GOE_SELECT_ACTION: 'Выберите действие',
+ GOE_NOTHING_TO_IMPROVE_LVL30: 'Нечего улучшать. Аккаунт не достиг 30 уровня команды',
+ GOE_NOTHING_TO_IMPROVE: 'Нечего улучшать. У всех героев максимальный уровень дара стихий',
+ GOE_SPEND_SPARKS_OF_POWER_MESSAGE:
+ 'Доступно {titanGift} искр мощи Укажите сколько искр мощи потратить',
+ GOE_INCORRECT_VALUE: 'Некорректное значение',
+ GOE_IMPROVING_START: 'Улучшаем дар стихий...',
+ GOE_NOT_ENOUGH_RESOURCES: 'Недостаточно золота или искр мощи',
+ GOE_ALL_HEROES_HAVE_30LVL: ' Достигнут 30 уровень дара стихий у всех героев',
+ GOE_GOLD_IS_GONE: ' Закончилось золото ',
+ GOE_PROGRESS_OF_IMPROVEMENT_MESSAGE: 'Дар стихий улучшен до {titanGiftLevel} уровня',
+ GOE_RESULT_OF_IMPROVEMENT: 'Дар стихий улучшен {counter} раз(а)',
+ GOE_NOTHING_TO_RESET: 'Нечего сбрасывать',
+ GOE_IMPOSSIBLE_TO_RESET: 'Нет героев с даром стихий меньше 30 уровня',
+ GOE_RESET_GIFTS_LIGHT_MESSAGE:
+ `Укажите максимальный сбрасываемый уровень дара стихий
+ Диапазон от 1 до 29 `,
+ GOE_RESULT_RESET_GIFTS: 'Дар стихий сброшен у {counter} героев(я)',
+ GOE_RESET_GIFTS_EXTREME_MESSAGE:
+ `У Вас {level30} героя(ев) с 30 уровнем дара стихий
+ Укажите сколько даров стихий 30 уровня сбросить
+ Сброс начнется с самого слабого героя Дары стихий меньших уровней будут сброшены автоматически`,
+ GOE_EXTREME_DO_NOT_HAVE_HERO_30LVL:
+ 'У Вас нет героев с 30 уровнем дара стихий Сбросить дары стихий меньшего уровня?',
+ GOE_EXTREME_RESULT_RESET_GIFTS:
+ ' {counter30} из них 30 уровня',
+ GOE_GET_POWER: 'Увеличить мощь',
+ GOE_GET_POWER_TITLE: 'Увеличить мощь героев, улучшая дары стихий самых сильных героев до 29 уровня',
+ GOE_GET_POWER_MESSAGE:
+ `Сначала улучшает дар стихий самого сильного героя до 29 уровня, затем следующего по силе.
+ Максимально доступная мощь героев: {maxHeroPawer}
+ Укажите, сколько мощи героев необходимо получить`,
+ GOE_GOT_POWER: ' Получили мощи героев: {gotPower} ',
+ GOE_NOT_ENOUGH_GOLD:
+ `Не достаточно золота , чтобы получить всю доступную мощь героев
+ Имеем золота: {haveGold} Необходимо золота: {goldIsNeeded} `,
+ GOE_AUTO_GET_POWER: 'Авто получение мощи',
+ GOE_AUTO_GET_POWER_TITLE: 'Автоматически получать мощь при загрузке скрипта',
+ GOE_AUTO_GET_POWER_AMOUNT: 'Количество мощи для авто получения',
+ GOE_AUTO_GET_POWER_AMOUNT_TITLE: 'Количество мощи для автоматического получения (0 = отключено)',
+ };
+
+ i18nLangData['ru'] = Object.assign(i18nLangData['ru'], i18nLangDataRu);
+
+ // Settings
+ const { checkboxes, inputs } = HWHData;
+ checkboxes.autoGetPower = {
+ get label() {
+ return I18N('GOE_AUTO_GET_POWER');
+ },
+ cbox: null,
+ get title() {
+ return I18N('GOE_AUTO_GET_POWER_TITLE');
+ },
+ default: false,
+ };
+ inputs.autoGetPowerAmount = {
+ get title() {
+ return I18N('GOE_AUTO_GET_POWER_AMOUNT');
+ },
+ default: 0,
+ };
+
+ // Fix: Allow input of 0 in autoGetPowerAmount field
+ const fixInputValidation = setInterval(() => {
+ const { inputs } = HWHData;
+ if (inputs.autoGetPowerAmount?.input && HWHFuncs) {
+ clearInterval(fixInputValidation);
+
+ const input = inputs.autoGetPowerAmount.input;
+ const inputName = 'autoGetPowerAmount';
+ let userEnteredValue = null;
+
+ input.addEventListener('input', function () {
+ const rawValue = this.value;
+ const numValue = +rawValue;
+ if (rawValue === '' || !Number.isNaN(numValue)) {
+ userEnteredValue = rawValue === '' ? null : numValue;
+ }
+ }, true);
+
+ input.addEventListener('input', function () {
+ setTimeout(() => {
+ const numValue = +this.value;
+ if (userEnteredValue === 0 && numValue !== 0) {
+ this.value = 0;
+ HWHFuncs.setSaveVal?.(inputName, 0);
+ } else if (userEnteredValue !== null && !Number.isNaN(userEnteredValue) && numValue !== userEnteredValue) {
+ this.value = userEnteredValue;
+ HWHFuncs.setSaveVal?.(inputName, userEnteredValue);
+ }
+ }, 0);
+ }, false);
+
+ input.addEventListener('blur', function () {
+ const numValue = +this.value;
+ if (!Number.isNaN(numValue)) {
+ HWHFuncs.setSaveVal?.(inputName, numValue);
+ if (numValue === 0) {
+ this.value = 0;
+ }
+ }
+ });
+
+ console.log(`%c${GM_info.script.name}: Fixed input validation to allow 0`, 'color: green');
+ }
+ }, 200);
+
+ // Menu buttons
+ const { othersPopupButtons } = HWHData;
+ othersPopupButtons.push({
+ get msg() {
+ return I18N('GIFT_OF_ELEMENTS');
+ },
+ get title() {
+ return I18N('GIFT_OF_ELEMENTS_TITLE');
+ },
+ result: async function () {
+ await onClickGiftOfTheElements();
+ },
+ color: 'pink',
+ });
+
+ async function onClickGiftOfTheElements() {
+ const popupButtons = [
+ {
+ get msg() {
+ return I18N('GOE_SPEND_SPARKS_OF_POWER');
+ },
+ get title() {
+ return I18N('GOE_SPEND_SPARKS_OF_POWER_TITLE');
+ },
+ result: async function () {
+ await spendSparksPower();
+ },
+ color: 'green',
+ },
+ {
+ get msg() {
+ return I18N('GOE_GET_POWER');
+ },
+ get title() {
+ return I18N('GOE_GET_POWER_TITLE');
+ },
+ result: async function () {
+ await getPower();
+ },
+ color: 'green',
+ },
+ {
+ get msg() {
+ return I18N('GOE_RESET_GIFTS_LIGHT');
+ },
+ get title() {
+ return I18N('GOE_RESET_GIFTS_LIGHT_TITLE');
+ },
+ result: async function () {
+ await resetTitanGifts();
+ },
+ },
+ {
+ get msg() {
+ return I18N('GOE_RESET_GIFTS_EXTREME');
+ },
+ get title() {
+ return I18N('GOE_RESET_GIFTS_EXTREME_TITLE');
+ },
+ result: async function () {
+ await resetTitanGifts30LVL();
+ },
+ },
+ ];
+ popupButtons.push({ result: false, isClose: true });
+ const answer = await popup.confirm(`${I18N('GOE_SELECT_ACTION')}`, popupButtons);
+ if (typeof answer === 'function') {
+ answer();
+ }
+ }
+
+ // Helper: Validate user level and titan gift level
+ function validateUpgradeConditions(userLevel, minTitanGiftLevel, isAutoMode = false) {
+ if (userLevel < MIN_USER_LEVEL) {
+ if (!isAutoMode) {
+ confShow(`${I18N('GOE_NOTHING_TO_IMPROVE_LVL30')}`);
+ }
+ return false;
+ }
+ if (minTitanGiftLevel === MAX_TITAN_GIFT_LEVEL) {
+ if (!isAutoMode) {
+ confShow(`${I18N('GOE_NOTHING_TO_IMPROVE')}`);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ // Helper: Sort heroes by power descending (highest power first)
+ function sortHeroesByPowerDesc(heroes) {
+ return [...heroes].sort((a, b) => b.power - a.power);
+ }
+
+ // Helper: Calculate maximum possible power (highest-power hero first, up to level 29)
+ function findMaximumPossiblePowerHighestFirst(heroes, titanGift, gold, titanGiftLib) {
+ const result = { maximumPowerWeCanGet: 0, needGoldToGetMaxPower: 0 };
+ let remainingTitanGift = titanGift;
+ let remainingGold = gold;
+
+ for (const hero of sortHeroesByPowerDesc(heroes)) {
+ let level = hero.titanGiftLevel;
+ while (level < TARGET_GIFT_LEVEL_GET_POWER) {
+ const nextLevelCost = titanGiftLib[level + 1].cost;
+ const costTitanGift = nextLevelCost.consumable[CONSUMABLE_ID_TITAN_GIFT];
+ if (remainingTitanGift < costTitanGift || remainingGold < nextLevelCost.gold) {
+ return result;
+ }
+ remainingTitanGift -= costTitanGift;
+ remainingGold -= nextLevelCost.gold;
+ result.maximumPowerWeCanGet += POWER_LEVEL[level];
+ result.needGoldToGetMaxPower += nextLevelCost.gold;
+ level++;
+ }
+ }
+ return result;
+ }
+
+ // Core upgrade logic (used by spendSparksPower)
+ async function upgradeTitanGifts(options) {
+ const {
+ targetTitanGift = null,
+ isAutoMode = false,
+ showProgress = true,
+ } = options;
+
+ let [heroGetAll, inventory, user] = await new Caller(['heroGetAll', 'inventoryGet', 'userGetInfo']).execute();
+ let heroes = Object.values(heroGetAll).sort((a, b) => a.titanGiftLevel - b.titanGiftLevel);
+ const heroSumPowerStart = Object.values(heroGetAll).reduce((a, e) => a + e.power, 0);
+ const titanGiftLib = lib.getData('titanGift');
+ let titanGift = inventory.consumable[CONSUMABLE_ID_TITAN_GIFT];
+ const titanGiftMax = titanGift;
+ let gold = user.gold;
+ const userLevel = user.level;
+ const minTitanGiftLevel = heroes[0].titanGiftLevel;
+
+ if (!validateUpgradeConditions(userLevel, minTitanGiftLevel, isAutoMode)) {
+ return;
+ }
+
+ let targetTitanGiftAmount = null;
+ if (targetTitanGift !== null) {
+ targetTitanGiftAmount = targetTitanGift;
+ if (targetTitanGiftAmount > titanGiftMax || targetTitanGiftAmount < 0) {
+ if (!isAutoMode) {
+ confShow(`${I18N('GOE_INCORRECT_VALUE')}`);
+ }
+ return;
+ }
+ titanGift = targetTitanGiftAmount;
+ }
+
+ let calls = [];
+ let titanGiftLevel = minTitanGiftLevel;
+ let titanGiftUpgradeCounter = 0;
+ let message = '';
+
+ if (showProgress) {
+ setProgress(I18N('GOE_IMPROVING_START'), false);
+ }
+
+ let cycle = true;
+ while (cycle) {
+ for (const hero of heroes) {
+ if (titanGiftLevel >= MAX_TITAN_GIFT_LEVEL) {
+ message += I18N('GOE_ALL_HEROES_HAVE_30LVL');
+ cycle = false;
+ break;
+ }
+ if (hero.titanGiftLevel > titanGiftLevel) {
+ break;
+ }
+ const nextLevelCost = titanGiftLib[hero.titanGiftLevel + 1].cost;
+ const costTitanGift = nextLevelCost.consumable[CONSUMABLE_ID_TITAN_GIFT];
+
+ if (titanGift < costTitanGift || gold < nextLevelCost.gold) {
+ if (titanGiftUpgradeCounter === 0 && calls.length === 0) {
+ if (showProgress) {
+ setProgress('', true);
+ }
+ if (!isAutoMode) {
+ confShow(`${I18N('GOE_NOT_ENOUGH_RESOURCES')}`);
+ }
+ return;
+ }
+ if (gold < nextLevelCost.gold) {
+ message += I18N('GOE_GOLD_IS_GONE');
+ }
+ cycle = false;
+ break;
+ }
+
+ calls.push({ name: 'heroTitanGiftLevelUp', args: { heroId: hero.id } });
+ titanGift -= costTitanGift;
+ gold -= nextLevelCost.gold;
+
+ if (targetTitanGiftAmount !== null && titanGift <= 0) {
+ cycle = false;
+ break;
+ }
+ }
+
+ if (calls.length > 0) {
+ await Caller.send(calls);
+ titanGiftUpgradeCounter += calls.length;
+ heroGetAll = await new Caller('heroGetAll').execute();
+ heroes = Object.values(heroGetAll).sort((a, b) => a.titanGiftLevel - b.titanGiftLevel);
+ calls = [];
+ titanGiftLevel++;
+ if (showProgress) {
+ setProgress(I18N('GOE_PROGRESS_OF_IMPROVEMENT_MESSAGE', { titanGiftLevel }), false);
+ }
+ }
+ }
+
+ const heroSumPowerFinish = Object.values(heroGetAll).reduce((a, e) => a + e.power, 0);
+ message += I18N('GOE_GOT_POWER', { gotPower: (heroSumPowerFinish - heroSumPowerStart).toLocaleString() });
+
+ if (showProgress) {
+ setProgress('', true);
+ }
+
+ if (!isAutoMode) {
+ confShow(`${I18N('GOE_RESULT_OF_IMPROVEMENT', { counter: titanGiftUpgradeCounter })} ${message}`);
+ }
+ }
+
+ // Get power: upgrade highest-power heroes to level 29
+ async function upgradeTitanGiftsHighestPowerFirst(options = {}) {
+ const { targetPower = null, isAutoMode = false, showProgress = true } = options;
+
+ let [heroGetAll, inventory, user] = await new Caller(['heroGetAll', 'inventoryGet', 'userGetInfo']).execute();
+ let heroes = sortHeroesByPowerDesc(Object.values(heroGetAll));
+ const heroSumPowerStart = Object.values(heroGetAll).reduce((a, e) => a + e.power, 0);
+ const titanGiftLib = lib.getData('titanGift');
+ let titanGift = inventory.consumable[CONSUMABLE_ID_TITAN_GIFT];
+ let gold = user.gold;
+ const userLevel = user.level;
+ const minTitanGiftLevel = Math.min(...heroes.map((hero) => hero.titanGiftLevel));
+
+ if (!validateUpgradeConditions(userLevel, minTitanGiftLevel, isAutoMode)) {
+ return;
+ }
+
+ if (!heroes.some((hero) => hero.titanGiftLevel < TARGET_GIFT_LEVEL_GET_POWER)) {
+ if (!isAutoMode) {
+ confShow(`${I18N('GOE_NOTHING_TO_IMPROVE')}`);
+ }
+ return;
+ }
+
+ const result = findMaximumPossiblePowerHighestFirst(heroes, titanGift, gold, titanGiftLib);
+ if (result.maximumPowerWeCanGet === 0) {
+ if (!isAutoMode) {
+ confShow(`${I18N('GOE_NOT_ENOUGH_RESOURCES')}`);
+ }
+ return;
+ }
+
+ let calls = [];
+ let titanGiftUpgradeCounter = 0;
+ let gotHeroPower = 0;
+ let targetReached = false;
+ let message = '';
+
+ if (showProgress) {
+ setProgress(I18N('GOE_IMPROVING_START'), false);
+ }
+
+ const sendCalls = async () => {
+ if (calls.length === 0) {
+ return;
+ }
+ await Caller.send(calls);
+ titanGiftUpgradeCounter += calls.length;
+ heroGetAll = await new Caller('heroGetAll').execute();
+ heroes = sortHeroesByPowerDesc(Object.values(heroGetAll));
+ calls = [];
+ if (showProgress) {
+ const topHero = heroes.find((hero) => hero.titanGiftLevel < TARGET_GIFT_LEVEL_GET_POWER) || heroes[0];
+ setProgress(
+ I18N('GOE_PROGRESS_OF_IMPROVEMENT_MESSAGE', { titanGiftLevel: topHero?.titanGiftLevel ?? 0 }),
+ false
+ );
+ }
+ };
+
+ const heroIdsByPower = heroes.map((hero) => hero.id);
+
+ for (const heroId of heroIdsByPower) {
+ if (targetReached) {
+ break;
+ }
+
+ let hero = heroes.find((entry) => entry.id === heroId);
+ if (!hero || hero.titanGiftLevel >= TARGET_GIFT_LEVEL_GET_POWER) {
+ continue;
+ }
+
+ while (hero.titanGiftLevel < TARGET_GIFT_LEVEL_GET_POWER) {
+ const nextLevelCost = titanGiftLib[hero.titanGiftLevel + 1].cost;
+ const costTitanGift = nextLevelCost.consumable[CONSUMABLE_ID_TITAN_GIFT];
+
+ if (titanGift < costTitanGift || gold < nextLevelCost.gold) {
+ break;
+ }
+
+ if (targetPower !== null) {
+ gotHeroPower += POWER_LEVEL[hero.titanGiftLevel];
+ if (gotHeroPower >= targetPower) {
+ targetReached = true;
+ break;
+ }
+ }
+
+ calls.push({ name: 'heroTitanGiftLevelUp', args: { heroId: hero.id } });
+ titanGift -= costTitanGift;
+ gold -= nextLevelCost.gold;
+ hero.titanGiftLevel++;
+
+ if (calls.length >= 50) {
+ await sendCalls();
+ if (targetReached) {
+ break;
+ }
+ hero = heroes.find((entry) => entry.id === heroId);
+ if (!hero || hero.titanGiftLevel >= TARGET_GIFT_LEVEL_GET_POWER) {
+ break;
+ }
+ }
+ }
+ }
+
+ await sendCalls();
+
+ if (titanGiftUpgradeCounter === 0) {
+ if (!isAutoMode) {
+ confShow(`${I18N('GOE_NOT_ENOUGH_RESOURCES')}`);
+ }
+ if (showProgress) {
+ setProgress('', true);
+ }
+ return;
+ }
+
+ if (heroes.every((hero) => hero.titanGiftLevel >= TARGET_GIFT_LEVEL_GET_POWER)) {
+ message += I18N('GOE_ALL_HEROES_HAVE_30LVL');
+ } else if (gold <= 0 || titanGift <= 0) {
+ message += I18N('GOE_GOLD_IS_GONE');
+ }
+
+ const heroSumPowerFinish = Object.values(heroGetAll).reduce((a, e) => a + e.power, 0);
+ message += I18N('GOE_GOT_POWER', { gotPower: (heroSumPowerFinish - heroSumPowerStart).toLocaleString() });
+
+ if (showProgress) {
+ setProgress('', true);
+ }
+
+ if (!isAutoMode) {
+ confShow(`${I18N('GOE_RESULT_OF_IMPROVEMENT', { counter: titanGiftUpgradeCounter })} ${message}`);
+ }
+ }
+
+ // Get power (highest-power hero first, up to level 29, with optional target amount)
+ async function getPower(targetPower = null) {
+ const isAutoMode = targetPower !== null;
+
+ if (isAutoMode) {
+ const [heroGetAll, inventory, user] = await new Caller(['heroGetAll', 'inventoryGet', 'userGetInfo']).execute();
+ const heroes = Object.values(heroGetAll);
+ const titanGiftLib = lib.getData('titanGift');
+ const titanGift = inventory.consumable[CONSUMABLE_ID_TITAN_GIFT];
+ const gold = user.gold;
+ const result = findMaximumPossiblePowerHighestFirst(heroes, titanGift, gold, titanGiftLib);
+
+ if (targetPower === 0 || targetPower > result.maximumPowerWeCanGet) {
+ return;
+ }
+
+ await upgradeTitanGiftsHighestPowerFirst({
+ targetPower,
+ isAutoMode: true,
+ showProgress: false,
+ });
+ return;
+ }
+
+ const [heroGetAll, inventory, user] = await new Caller(['heroGetAll', 'inventoryGet', 'userGetInfo']).execute();
+ const heroes = Object.values(heroGetAll);
+ const titanGiftLib = lib.getData('titanGift');
+ const titanGift = inventory.consumable[CONSUMABLE_ID_TITAN_GIFT];
+ const gold = user.gold;
+ const userLevel = user.level;
+ const minTitanGiftLevel = Math.min(...heroes.map((hero) => hero.titanGiftLevel));
+
+ if (!validateUpgradeConditions(userLevel, minTitanGiftLevel, false)) {
+ return;
+ }
+
+ if (!heroes.some((hero) => hero.titanGiftLevel < TARGET_GIFT_LEVEL_GET_POWER)) {
+ confShow(`${I18N('GOE_NOTHING_TO_IMPROVE')}`);
+ return;
+ }
+
+ const result = findMaximumPossiblePowerHighestFirst(heroes, titanGift, gold, titanGiftLib);
+ if (result.maximumPowerWeCanGet === 0) {
+ confShow(`${I18N('GOE_NOT_ENOUGH_RESOURCES')}`);
+ return;
+ }
+
+ const notEnoughGold = result.needGoldToGetMaxPower > gold
+ ? I18N('GOE_NOT_ENOUGH_GOLD', {
+ haveGold: gold.toLocaleString(),
+ goldIsNeeded: result.needGoldToGetMaxPower.toLocaleString()
+ })
+ : '';
+
+ const confirmed = await popup.confirm(
+ `${I18N('GOE_GET_POWER_MESSAGE', { maxHeroPawer: result.maximumPowerWeCanGet.toLocaleString() })} ${notEnoughGold}`,
+ [
+ { result: 0, isClose: true },
+ { msg: `${I18N('GOE_GET_POWER')}`, isInput: true, default: result.maximumPowerWeCanGet, color: 'green' },
+ ]
+ );
+
+ const needHeroPower = +confirmed;
+ if (needHeroPower === 0 || !needHeroPower || needHeroPower < 0 || needHeroPower > result.maximumPowerWeCanGet) {
+ if (needHeroPower !== 0) {
+ confShow(`${I18N('GOE_INCORRECT_VALUE')}`);
+ }
+ return;
+ }
+
+ await upgradeTitanGiftsHighestPowerFirst({
+ targetPower: needHeroPower,
+ isAutoMode: false,
+ showProgress: true,
+ });
+ }
+
+ // Spend sparks of power
+ async function spendSparksPower() {
+ const [heroGetAll, inventory] = await new Caller(['heroGetAll', 'inventoryGet']).execute();
+ const heroes = Object.values(heroGetAll).sort((a, b) => a.titanGiftLevel - b.titanGiftLevel);
+ const titanGift = inventory.consumable[CONSUMABLE_ID_TITAN_GIFT];
+ const titanGiftMax = titanGift;
+
+ const titanGiftAmount = +(await popup.confirm(
+ I18N('GOE_SPEND_SPARKS_OF_POWER_MESSAGE', { titanGift: titanGift.toLocaleString() }),
+ [
+ { result: 0, isClose: true },
+ { msg: `${I18N('GOE_SPEND_SPARKS_OF_POWER')}`, isInput: true, default: titanGift.toString(), color: 'green' },
+ ]
+ ));
+
+ if (titanGiftAmount === 0 || !titanGiftAmount || titanGiftAmount < 0 || titanGiftAmount > titanGiftMax) {
+ if (titanGiftAmount !== 0) {
+ confShow(`${I18N('GOE_INCORRECT_VALUE')}`);
+ }
+ return;
+ }
+
+ await upgradeTitanGifts({
+ targetTitanGift: titanGiftAmount,
+ isAutoMode: false,
+ showProgress: true,
+ });
+ }
+
+ // Reset titan gifts (level 1-29)
+ async function resetTitanGifts() {
+ const [heroGetAll, user] = await new Caller(['heroGetAll', 'userGetInfo']).execute();
+ const heroes = Object.values(heroGetAll).sort((a, b) => a.titanGiftLevel - b.titanGiftLevel);
+ const userLevel = user.level;
+ let maxResetTitanGiftLevel = 0;
+
+ for (const hero of heroes) {
+ if (hero.titanGiftLevel > 0) {
+ maxResetTitanGiftLevel = hero.titanGiftLevel;
+ break;
+ }
+ }
+
+ if (userLevel < MIN_USER_LEVEL || maxResetTitanGiftLevel === 0) {
+ confShow(`${I18N('GOE_NOTHING_TO_RESET')}`);
+ return;
+ }
+
+ if (maxResetTitanGiftLevel === MAX_TITAN_GIFT_LEVEL) {
+ confShow(`${I18N('GOE_IMPOSSIBLE_TO_RESET')}`);
+ return;
+ }
+
+ maxResetTitanGiftLevel = +(await popup.confirm(I18N('GOE_RESET_GIFTS_LIGHT_MESSAGE'), [
+ { result: 0, isClose: true },
+ { msg: I18N('GOE_RESET_GIFTS'), isInput: true, default: maxResetTitanGiftLevel.toString(), color: 'green' },
+ ]));
+
+ if (maxResetTitanGiftLevel === 0 || !maxResetTitanGiftLevel || maxResetTitanGiftLevel < 0 || maxResetTitanGiftLevel > 29) {
+ if (maxResetTitanGiftLevel !== 0) {
+ confShow(`${I18N('GOE_INCORRECT_VALUE')}`);
+ }
+ return;
+ }
+
+ const calls = [];
+ for (const hero of heroes) {
+ if (hero.titanGiftLevel === 0) {
+ continue;
+ }
+ if (hero.titanGiftLevel > maxResetTitanGiftLevel || hero.titanGiftLevel === MAX_TITAN_GIFT_LEVEL) {
+ break;
+ }
+ calls.push({ name: 'heroTitanGiftDrop', args: { heroId: hero.id } });
+ }
+
+ if (calls.length === 0) {
+ confShow(`${I18N('GOE_NOTHING_TO_RESET')}`);
+ return;
+ }
+
+ await Caller.send(calls);
+ confShow(`${I18N('GOE_RESULT_RESET_GIFTS', { counter: calls.length })}`);
+ }
+
+ // Reset titan gifts level 30
+ async function resetTitanGifts30LVL() {
+ const [heroGetAll, user] = await new Caller(['heroGetAll', 'userGetInfo']).execute();
+ const heroes = Object.values(heroGetAll).sort((a, b) => a.titanGiftLevel - b.titanGiftLevel);
+ const userLevel = user.level;
+ const heroesLvl1_29 = Object.values(heroGetAll).filter((e) => e.titanGiftLevel > 0 && e.titanGiftLevel < MAX_TITAN_GIFT_LEVEL);
+ const heroesLvl30 = Object.values(heroGetAll)
+ .filter((e) => e.titanGiftLevel === MAX_TITAN_GIFT_LEVEL)
+ .sort((a, b) => a.power - b.power);
+
+ let maxResetTitanGiftLevel = 0;
+ for (const hero of heroes) {
+ if (hero.titanGiftLevel > 0) {
+ maxResetTitanGiftLevel = hero.titanGiftLevel;
+ break;
+ }
+ }
+
+ if (userLevel < MIN_USER_LEVEL || maxResetTitanGiftLevel === 0) {
+ confShow(`${I18N('GOE_NOTHING_TO_RESET')}`);
+ return;
+ }
+
+ const numberHeroesWithLevel30 = heroesLvl30.length;
+ let numberHeroesToReset = numberHeroesWithLevel30;
+
+ if (numberHeroesWithLevel30 === 0) {
+ const resultPopup = await popup.confirm(I18N('GOE_EXTREME_DO_NOT_HAVE_HERO_30LVL'), [
+ { msg: I18N('GOE_RESET_GIFTS'), result: true, color: 'green' },
+ { msg: I18N('BTN_CANCEL'), result: false, color: 'red' },
+ { isClose: true, result: false },
+ ]);
+ if (!resultPopup) {
+ return;
+ }
+ } else {
+ numberHeroesToReset = +(await popup.confirm(I18N('GOE_RESET_GIFTS_EXTREME_MESSAGE', { level30: numberHeroesWithLevel30 }), [
+ { result: 0, isClose: true },
+ { msg: I18N('GOE_RESET_GIFTS'), isInput: true, default: numberHeroesToReset.toString(), color: 'green' },
+ ]));
+
+ if (numberHeroesToReset === 0 || !numberHeroesToReset || numberHeroesToReset < 0 || numberHeroesToReset > numberHeroesWithLevel30) {
+ if (numberHeroesToReset !== 0) {
+ confShow(`${I18N('GOE_INCORRECT_VALUE')}`);
+ }
+ return;
+ }
+ }
+
+ const calls = [];
+ for (const hero of heroesLvl1_29) {
+ calls.push({ name: 'heroTitanGiftDrop', args: { heroId: hero.id } });
+ }
+ for (let i = 0; i < numberHeroesToReset; i++) {
+ calls.push({ name: 'heroTitanGiftDrop', args: { heroId: heroesLvl30[i].id } });
+ }
+
+ if (calls.length === 0) {
+ confShow(`${I18N('GOE_NOTHING_TO_RESET')}`);
+ return;
+ }
+
+ await Caller.send(calls);
+ confShow(
+ `${I18N('GOE_RESULT_RESET_GIFTS', { counter: calls.length })} ${I18N('GOE_EXTREME_RESULT_RESET_GIFTS', {
+ counter30: numberHeroesToReset,
+ })}`
+ );
+ }
+
+ // Auto-execution on script load
+ let checkCount = 0;
+ const waitForHWHReady = setInterval(() => {
+ checkCount++;
+ if (checkCount > AUTO_EXECUTION_TIMEOUT) {
+ clearInterval(waitForHWHReady);
+ console.log(`%c${GM_info.script.name}: Auto-execution timeout - HWH not ready after ${AUTO_EXECUTION_TIMEOUT * 0.2}s`, 'color: red');
+ return;
+ }
+
+ if (this.HWHClasses?.ScriptMenu && HWHFuncs && lib && cheats) {
+ const scriptMenu = this.HWHClasses.ScriptMenu.getInst();
+ if (scriptMenu?.mainMenu) {
+ clearInterval(waitForHWHReady);
+ console.log(`%c${GM_info.script.name}: HWH UI is ready, checking auto-execution settings...`, 'color: blue');
+
+ setTimeout(async () => {
+ try {
+ const { getSaveVal } = HWHFuncs;
+ let autoGetPower = getSaveVal('autoGetPower', false);
+ let autoGetPowerAmount = getSaveVal('autoGetPowerAmount', 0);
+
+ console.log(`%c${GM_info.script.name}: Auto-execution check - enabled: ${autoGetPower}, amount: ${autoGetPowerAmount}`, 'color: blue');
+
+ if (autoGetPower) {
+ if (autoGetPowerAmount > 0) {
+ console.log(`%c${GM_info.script.name}: Auto-executing getPower with target: ${autoGetPowerAmount}`, 'color: green');
+ await getPower(autoGetPowerAmount);
+ console.log(`%c${GM_info.script.name}: Auto-execution completed`, 'color: green');
+ }
+ } else {
+ console.log(`%c${GM_info.script.name}: Auto-execution skipped - enabled: ${autoGetPower}, amount: ${autoGetPowerAmount}`, 'color: orange');
+ }
+ } catch (error) {
+ console.error(`%c${GM_info.script.name}: Auto-execution error:`, 'color: red', error);
+ }
+ }, AUTO_EXECUTION_DELAY);
+ } else if (checkCount % 10 === 0) {
+ console.log(`%c${GM_info.script.name}: Waiting for HWH mainMenu... (check ${checkCount})`, 'color: gray');
+ }
+ } else if (checkCount % 10 === 0) {
+ console.log(`%c${GM_info.script.name}: Waiting for HWH components... (check ${checkCount})`, 'color: gray');
+ }
+ }, 200);
+})();
diff --git a/HWHhuntFragmentExt-0.0.49.user.js b/HWHhuntFragmentExt-0.0.49.user.js
new file mode 100644
index 0000000..2203593
--- /dev/null
+++ b/HWHhuntFragmentExt-0.0.49.user.js
@@ -0,0 +1,485 @@
+// ==UserScript==
+// @name HWHhuntFragmentExt
+// @name:en HWHhuntFragmentExt
+// @name:ru HWHhuntFragmentExt
+// @namespace HWHhuntFragmentExt
+// @version 0.0.49
+// @description Extension for HeroWarsHelper script
+// @description:en Extension for HeroWarsHelper script
+// @description:ru Расширение для скрипта HeroWarsHelper
+// @author dimaka1256
+// @license Copyright dimaka1256
+// @homepage none
+// @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.org/scripts/537105/HWHhuntFragmentExt.user.js
+// @updateURL https://update.greasyfork.org/scripts/537105/HWHhuntFragmentExt.meta.js
+// ==/UserScript==
+
+(function () {
+
+ if (!this.HWHClasses) {
+ console.log('%cObject for extension not found', 'color: red');
+ return;
+ }
+
+ console.log('%cStart Extension ' + GM_info.script.name + ', v' + GM_info.script.version + ' by ' + GM_info.script.author, 'color: red');
+ const { addExtentionName } = HWHFuncs;
+ addExtentionName(GM_info.script.name, GM_info.script.version, GM_info.script.author);
+
+ const {
+ setProgress,
+ getSaveVal,
+ setSaveVal,
+ popup,
+ I18N,
+ getUserInfo,
+ } = HWHFuncs;
+
+let {buttons,i18nLangData} = HWHData;
+
+let ruLang ={
+ FRAGMENT_HUNT: 'Слить энку',
+ FRAGMENT_HUNT_TITLE: 'Добывать фрагменты шмоток/рецептов',
+ FRAGMENT_HUNT_SETUP: '⚙️',
+ FRAGMENT_HUNT_SETUP_TITLE: 'Выбор фрагмента/миссии',
+ FRAGMENT_HUNT_ENERGY: '⚡️',
+ FRAGMENT_HUNT_FR: '🧩',
+ FRAGMENT_HUNT_MISSION: 'миссия',
+ FRAGMENT_HUNT_WORLD: 'Глава',
+ FRAGMENT_HUNT_SPENT: 'Потратили ',
+ FRAGMENT_HUNT_GOTFRAGMENTS: ', получили такой лут:',
+ FRAGMENT_HUNT_PCS: 'шт',
+ FRAGMENT_HUNT_WEHUNT: 'Добываем предметы',
+ FRAGMENT_HUNT_ENOUGH1: ', хватит на ',
+ FRAGMENT_HUNT_ENOUGH2: ' рейдов x10',
+ FRAGMENT_HUNT_CHANGE: 'Выбрать',
+ FRAGMENT_HUNT_PARTS1: 'Фиол шмот',
+ FRAGMENT_HUNT_PARTS2: 'Фиол свитки А-О',
+ FRAGMENT_HUNT_PARTS3: 'Фиол свитки П-Я',
+ FRAGMENT_HUNT_PARTS4: 'Жёлтый шмот',
+ FRAGMENT_HUNT_PARTS5: 'Жёлтые свитки А-К',
+ FRAGMENT_HUNT_PARTS6: 'Жёлтые свитки Л-Я',
+ FRAGMENT_HUNT_PARTS7: 'Красный шмот',
+ FRAGMENT_HUNT_PARTS8: 'Красные свитки',
+ FRAGMENT_HUNT_CHOOSEPART: 'Какую категорию шмотки ищём?',
+ FRAGMENT_HUNT_CHOOSEITEM: 'Какую шмотку ищём?',
+ FRAGMENT_HUNT_CHOOSEMISSION: 'В какой миссии? (см. ещё дроп)',
+ FRAGMENT_HUNT_NOTENOUGH: 'Недостаточно энергии',
+ FRAGMENT_HUNT_NOMISSION: 'Не выбрана миссия, нечего добывать',
+ FRAGMENT_HUNT_DOALL: 'Слить энку на миссию',
+ FRAGMENT_HUNT_NOVIP: 'Сорри, без VIP1 не работает',
+ FRAGMENT_HUNT_SMALLVIP: 'Без VIP5 бьёт одиночными рейдами',
+}
+let enLang ={
+ FRAGMENT_HUNT: 'Spend Stamina',
+ FRAGMENT_HUNT_TITLE: 'Hunt for Item/Scroll fragment',
+ FRAGMENT_HUNT_SETUP: '⚙️',
+ FRAGMENT_HUNT_SETUP_TITLE: 'Choose fragment/mission',
+ FRAGMENT_HUNT_ENERGY: '⚡️',
+ FRAGMENT_HUNT_SPENT: 'Spent ',
+ FRAGMENT_HUNT_FR: '🧩',
+ FRAGMENT_HUNT_MISSION: 'mission',
+ FRAGMENT_HUNT_WORLD: 'World',
+ FRAGMENT_HUNT_GOTFRAGMENTS: ',g ot this loot:',
+ FRAGMENT_HUNT_PCS: 'pcs',
+ FRAGMENT_HUNT_WEHUNT: 'We hunt for items',
+ FRAGMENT_HUNT_ENOUGH1: ', enough for ',
+ FRAGMENT_HUNT_ENOUGH2: ' raids x10',
+ FRAGMENT_HUNT_CHANGE: 'Choose',
+ FRAGMENT_HUNT_PARTS1: 'Purple gear',
+ FRAGMENT_HUNT_PARTS2: 'Purple scrolls 1',
+ FRAGMENT_HUNT_PARTS3: 'Purple scrolls 2',
+ FRAGMENT_HUNT_PARTS4: 'Yellow gear',
+ FRAGMENT_HUNT_PARTS5: 'Yellow scrolls 1',
+ FRAGMENT_HUNT_PARTS6: 'Yellow scrolls 2',
+ FRAGMENT_HUNT_PARTS7: 'Red gear',
+ FRAGMENT_HUNT_PARTS8: 'Red scrolls',
+ FRAGMENT_HUNT_CHOOSEPART: 'Choose fragment category:',
+ FRAGMENT_HUNT_CHOOSEITEM: 'Choose fragment:',
+ FRAGMENT_HUNT_CHOOSEMISSION: 'Choose mission(look at other drop)',
+ FRAGMENT_HUNT_NOTENOUGH: 'Not enough stamina',
+ FRAGMENT_HUNT_NOMISSION: 'Mission not chosen',
+ FRAGMENT_HUNT_DOALL: 'Consume stamina to a mission',
+ FRAGMENT_HUNT_NOVIP: 'This requires at least VIP1 to run',
+ FRAGMENT_HUNT_SMALLVIP: 'Only single raids without VIP5',
+}
+Object.assign(i18nLangData.ru, ruLang)
+Object.assign(i18nLangData.en, enLang)
+this.HWHData.i18nLangData = i18nLangData;
+
+const fragmentHuntButton = {
+ fragmentHuntButton: {
+ isCombine: true,
+ combineList: [
+ {
+ get name() { return I18N('FRAGMENT_HUNT'); },
+ get title() { return I18N('FRAGMENT_HUNT_TITLE'); },
+ onClick: gohuntFragment,
+ hide: false,
+ color: 'red'
+ },
+ {
+ get name() { return I18N('FRAGMENT_HUNT_SETUP'); },
+ get title() { return I18N('FRAGMENT_HUNT_SETUP_TITLE'); },
+ onClick: setuphuntFragment,
+ hide: false,
+ color: 'red'
+ },
+ ]
+}}
+
+Object.assign(buttons,fragmentHuntButton)
+this.HWHData.buttons = buttons;
+
+
+function setuphuntFragment() {
+ let fragment= new huntFragment();
+ fragment.setup();
+}
+
+function gohuntFragment() {
+ let fragment= new huntFragment();
+ fragment.start();
+}
+
+//добавить галочку в "сделать всё"
+//я буду гореть за это в аду
+const task = {
+ name: 'gohuntFragment',
+ label: I18N('FRAGMENT_HUNT_DOALL'),
+ checked: false
+ }
+const functions2 = {
+ gohuntFragment
+}
+
+const {doYourBest} = HWHClasses;
+const doIt = new doYourBest();
+ let myfuncList=doIt.funcList
+ myfuncList.splice (-3,0,task);
+ let myfunctions = doIt.functions
+ Object.assign(myfunctions, functions2)
+
+ class extdoYourBest extends doYourBest {
+ funcList = myfuncList
+ functions = myfunctions
+ }
+this.HWHClasses.doYourBest = extdoYourBest;
+
+
+class huntFragment {
+
+ inventoryGet = []
+ droptable = []
+ stamina = 0
+ raids = 0
+ missionID = 0
+ energyNeeded = 0
+
+missionEnergy (id) {
+ if (id == 0) {return 999999};
+ if (id > 145) {return 10};
+ if (id < 86) {return 6};
+ return 8
+}
+
+checkvip() {
+ let currentVipPoints = (getUserInfo()).vipPoints;
+ if (currentVipPoints >999) {return 5}
+ if (currentVipPoints >9) {return 1}
+ return 0
+}
+
+isWithinRange(value, min, max) {
+ return value >= min && value <= max;
+}
+
+generateArray(start, size) {
+ return Array.from({length: size}, (_, index) => index + start);
+}
+
+getType(id){
+ let type = "oops"
+ if (this.isWithinRange (id, 21, 55) || this.isWithinRange (id, 56, 99) || this.isWithinRange (id, 167, 178) || this.isWithinRange (id, 221, 232)) {type="Gear"}
+ if (this.isWithinRange (id, 141, 166) || this.isWithinRange (id, 190, 220) || this.isWithinRange (id, 244, 254)) {type="Scroll"}
+ return type
+}
+
+getColor(id){
+ if (this.isWithinRange (id, 21, 55)) {return "green"}
+ if (this.isWithinRange (id, 141, 145)) {return "green"}
+ if (this.isWithinRange (id, 56, 90)) {return "blue"}
+ if (this.isWithinRange (id, 91, 166)) {return "purple"}
+ if (this.isWithinRange (id, 167, 220)) {return "orange"}
+ if (this.isWithinRange (id, 221, 254)) {return "red"}
+ console.log ("getColor oops", id); return "white"
+}
+
+getName(id){
+ this.updatemyData()
+ let itemAvailable = 0
+ let fullitemAvailable = 0;
+ switch (this.getType(id)) {
+ case "Gear": {
+ itemAvailable = this.inventoryGet.fragmentGear[id] ?? 0;
+ fullitemAvailable = this.inventoryGet.gear[id] ?? 0; break;
+ }
+ case "Scroll": {
+ itemAvailable = this.inventoryGet.fragmentScroll[id] ?? 0;
+ fullitemAvailable = this.inventoryGet.scroll[id] ?? 0; break;
+ }
+ }
+
+ let color = this.getColor(id)
+ let name = cheats.translate(`LIB_${(this.getType(id)).toUpperCase()}_NAME_${id}`);
+ let words = name.split(" ")
+ for (let i in words) {if (words[i].length > 8){name=name.replace(words[i],(words[i]).substring(0,5)+".")}}
+ name = name.replace(" - Рецепт","-р");
+ name = name.replace(", уровень ","-");
+ name = name.replace(" уровень ","-");//эти сокращения локалить впадлу, совсем
+ let out =""
+ //out+=id //если нужен ид шмотки
+ out+=' '+name+' ('+fullitemAvailable+'+'+itemAvailable+I18N('FRAGMENT_HUNT_FR')+')';
+ return out
+}
+
+generateitembuttons(itemids) {
+ const buttons = [];
+ for (let itemid in itemids) {
+ let gearID = itemids[itemid]
+ let name = this.getName(gearID);
+ buttons.push({
+ msg: name,
+ result: itemids[itemid],
+ get title() { return name },
+ });
+ }
+ buttons.push({msg: I18N('BTN_CANCEL'), result: false, isCancel: true})
+ return buttons
+}
+
+generatemissionbuttons(missions, itemid) {
+ const buttons = [];
+ for (let mission in missions) {
+ let missionID = missions[mission]
+ let thismission = this.droptable.filter(m=>m.id===missionID)[0]
+ let otheritems = thismission.drop.filter(d=>d != itemid)
+ let name = I18N('FRAGMENT_HUNT_WORLD')+" "+thismission.world+" "+I18N('FRAGMENT_HUNT_MISSION')+" "+thismission.index+" "+this.missionEnergy(thismission.id)+I18N('FRAGMENT_HUNT_ENERGY')+" "
+ // I18N('FRAGMENT_HUNT_MISSION')+" "+ thismission.id
+ for(let i in otheritems) {if (this.getType(otheritems[i]) === "oops"){continue;};name+=this.getName(otheritems[i])+" ";}
+ buttons.push({
+ msg: name,
+ result: missionID,
+ get title() { return name },
+ });
+ }
+ buttons.push({msg: I18N('BTN_CANCEL'), result: false, isCancel: true})
+ return buttons
+}
+
+async updatemyData(){
+ let calls = [{
+ name: "userGetInfo",
+ args: {},
+ ident: "userGetInfo"
+ },{
+ name: "inventoryGet",
+ args: {},
+ ident: "inventoryGet"
+ }];
+ let result = await Send(JSON.stringify({ calls }));
+ let infos = result.results;
+ this.stamina = (infos[0].result.response.refillable.find(n => n.id == 1)).amount;
+ this.inventoryGet = infos[1].result.response;
+
+ this.energyNeeded = this.missionEnergy(this.missionID)
+ //if (this.missionID > 145) {this.energyNeeded = 10};
+ //if (this.missionID < 86) {this.energyNeeded = 6};
+ //console.log ("vip",this.checkvip())
+ switch (this.checkvip()){
+ case 5: {this.raids = Math.floor(this.stamina/(this.energyNeeded*10)); break;}
+ case 1: {this.raids = Math.floor(this.stamina/(this.energyNeeded)); break}
+ case 0: {this.raids = 0; setProgress(I18N('FRAGMENT_HUNT_NOVIP'))}
+ }
+ return "ok"
+}
+
+async makeMission(mission,count){
+ let nrg = this.energyNeeded*count;
+ let calls = []
+
+ switch (this.checkvip()){
+ case 5: {calls.push({name: "missionRaid",args: {id: mission.id,times: (count)},ident: "body"}); break;}
+ case 1: {
+ for (let i = 0; i < count; i++)
+ {
+ calls.push({name: "missionRaid",args: {id: mission.id,times: 1},ident: "body"+"i"})
+ }
+
+ }
+ }
+ let result = await Send({ calls })
+ console.log(result)
+ let loot2 = []
+ for (let j in result.results)
+ {
+ let loot = result.results[j].result.response
+ for (let i in loot){
+ if (!!loot[i].fragmentScroll) {loot2.push(loot[i].fragmentScroll)}
+ if (!!loot[i].fragmentGear) {loot2.push(loot[i].fragmentGear)}
+ }
+ }
+ let loot3=[]
+ //console.log("loot2",loot2)
+ for (let i in loot2) {
+ for (let j in Object.keys(loot2[i])) {loot3.push(Object.keys(loot2[i])[j])}
+ }
+ await this.updatemyData()
+ //console.log("loot3",loot3)
+ let str = I18N('FRAGMENT_HUNT_SPENT')+nrg+I18N('FRAGMENT_HUNT_ENERGY')+ I18N('FRAGMENT_HUNT_GOTFRAGMENTS')+" "
+ let usedids = []
+ let loot4=[]
+ for (let i in loot3) {
+ if (!usedids.includes(loot3[i])){loot4.push({id:loot3[i],qty:1}); usedids.push(loot3[i])}
+ else {(loot4.find(e => e.id== loot3[i])).qty++}
+ }
+ for (let i in loot4){
+ str+=this.getName(loot4[i].id)+": "+loot4[i].qty+ I18N('FRAGMENT_HUNT_PCS')+" "
+ }
+ setProgress(str)
+ //if (this.raids >0) {this.start(false)}
+}
+
+updateDroptable(){
+ const dropTable2 = ((types = ['gear', 'fragmentGear', 'scroll', 'fragmentScroll']) => {
+ return Object.values(lib.data.mission).map(e => {
+ const lastWave = e.normalMode?.waves?.at(-1);
+ const lastEnemy = lastWave?.enemies?.at(-1);
+ let dropList = lastEnemy?.drop ?? [];
+ let heromissions = []
+ for(const i of dropList) {
+ const type = Object.keys(i.reward)[0]
+ if (type == 'fragmentHero') {
+ //console.log('heromission',e.id);
+ heromissions.push(e.id)
+ }
+ }
+ const drop = [];
+ for(const d of dropList) {
+ const type = Object.keys(d.reward).pop()
+ if (d.chance && types.includes(type) && !(heromissions.includes(e.id))) {
+ const id = Object.keys(d.reward[type]).pop()
+ if (id>90) {drop.push(+id)}
+ }
+ }
+ return {id: e.id, world: e.world, index: e.index, drop}
+}).filter(n => n.drop.length)
+})()
+//console.log("dropTable2",dropTable2)
+this.droptable = dropTable2
+}
+
+async start() {
+ this.updateDroptable()
+ this.missionID = getSaveVal('huntFragmentMission', 999)
+ let mission = this.droptable.filter(m=>m.id===this.missionID)[0]
+ //console.log ("start missionID",this.missionID,mission)
+ if (!mission) {console.log("nomission"); this.setup();} //если в конфиге кака сначала настройка
+ await this.updatemyData()
+ console.log("Энки у нас",this.stamina," рейдов доступно",this.raids)
+
+ if (this.raids >0) {
+ if (this.checkvip() == 5){this.makeMission(mission,this.raids*10); }
+ else {this.makeMission(mission,this.raids); }
+ }
+ else {setProgress(I18N('FRAGMENT_HUNT_NOTENOUGH'))}
+ }
+
+async setup() {
+ this.updateDroptable()
+ //console.log("droptable2 updated",this.droptable)
+ this.missionID = getSaveVal('huntFragmentMission', 999)
+ let mission = this.droptable.filter(m=>m.id===this.missionID)[0]
+ //console.log ("setup missionID",this.missionID)
+ let message = ""; let maxraid =""; let target="";
+ if (this.checkvip() != 5) {message+=I18N('FRAGMENT_HUNT_SMALLVIP')}
+
+ if (!mission) {
+ this.raids = 0;
+ message = I18N('FRAGMENT_HUNT_NOMISSION')} //костыль чтоб точно выбрали миссию
+ else {
+ await this.updatemyData()
+ for (let i in mission.drop){let id=mission.drop[i]; target+=this.getName(id)+" ";}
+ maxraid = I18N('RAID')+" х"
+ if (this.checkvip()==5) {maxraid+=this.raids*10} else {maxraid+=this.raids}
+ message = I18N('FRAGMENT_HUNT_WEHUNT')+": "+target+" "+I18N('FRAGMENT_HUNT_WORLD')+" "+mission.world+" "+I18N('FRAGMENT_HUNT_MISSION')+" "+mission.index+" "+this.energyNeeded+" "+I18N('FRAGMENT_HUNT_ENERGY')+" "+I18N('FRAGMENT_HUNT_ENERGY')+" "+this.stamina+I18N('FRAGMENT_HUNT_ENOUGH1')+this.raids+I18N('FRAGMENT_HUNT_ENOUGH2')
+ }
+
+ let buttons0 = []
+ if (this.raids > 0 && this.checkvip()==5) {buttons0.push({msg: I18N('RAID')+" х10", result: "1"})}
+ if (this.raids > 1) {buttons0.push({msg: maxraid, result: "2"})}
+ buttons0.push({msg: I18N('FRAGMENT_HUNT_CHANGE'), result: "3"})
+ buttons0.push({msg: I18N('BTN_CANCEL'), result: false})
+
+ this.answerr = await popup.confirm(message, buttons0);
+
+ const parts = [I18N('FRAGMENT_HUNT_PARTS1'),I18N('FRAGMENT_HUNT_PARTS2'),I18N('FRAGMENT_HUNT_PARTS3'),I18N('FRAGMENT_HUNT_PARTS4'),I18N('FRAGMENT_HUNT_PARTS5'),I18N('FRAGMENT_HUNT_PARTS6'),I18N('FRAGMENT_HUNT_PARTS7'),I18N('FRAGMENT_HUNT_PARTS8')]
+ const buttons = [];
+ for (let i in parts ) {
+ buttons.push({
+ msg: parts[i],
+ result: i,
+ get title() { return "test" },
+ });
+ }
+ buttons.push({msg: I18N('BTN_CANCEL'), result: false, isCancel: true})
+ let answer=0;
+ switch (this.answerr) {
+ case "1": {
+ //console.log("тут будет рейд х10",mission.id);
+ this.makeMission(mission,10)
+ break;
+ }
+ case "2": {
+ //console.log("тут будет макс рейд",mission.id);
+ this.makeMission(mission,this.raids*10)
+ break;}
+ case "3": {answer = await popup.confirm(I18N('FRAGMENT_HUNT_CHOOSEPART'), buttons); break;}
+ default: {return;}
+ }
+ if (!answer) {return}
+
+ let array=[]
+ switch (answer) {
+ case "0": { array=this.generateArray(91,8);break;}
+ case "1": { array=[158,153,162,164,165,157,152,166]; break;} //(152,15)
+ case "2": { array=[163,159,154,161,156,160,155]; break;}
+ case "3": { array=this.generateArray(167,12);break;}
+ case "4": { array=[190,194,197,205,204,215,214,196,218,193,219,217,206]; break;}
+ case "5": { array=[198,220,195,192,216,191,199,200]; break;}
+ case "6": { array=this.generateArray(221,12); break;}
+ case "7": { array=this.generateArray(244,11); break;}
+ default: {console.log("oops"); break;}
+ }
+ let answer2 = await popup.confirm(I18N('FRAGMENT_HUNT_CHOOSEITEM'), this.generateitembuttons(array));
+ if (!answer2) {return}
+
+ let missions = this.droptable.filter(m=>m.drop.includes(answer2))
+ let missarray = []
+ for (let i in missions) {missarray.push(missions[i].id)}
+ let answer3 = await popup.confirm(I18N('FRAGMENT_HUNT_CHOOSEMISSION')+" "+this.getName(answer2), this.generatemissionbuttons(missarray,answer2));
+ if (!answer3) {return}
+
+ setSaveVal('huntFragmentMission', answer3)
+ this.setup()
+}
+}
+this.HWHClasses.huntFragment = huntFragment;
+})();
+
+//TODO:
+// формат "одной кнопки" для "сделать всё"
diff --git a/Hero Wars Stealther-1.006.txt b/Hero Wars Stealther-1.006.txt
new file mode 100644
index 0000000..1f393a9
--- /dev/null
+++ b/Hero Wars Stealther-1.006.txt
@@ -0,0 +1,1950 @@
+// ==UserScript==
+// @name Hero Wars Stealther
+// @name:de Hero Wars Stealther
+// @name:en Hero Wars Stealther
+// @namespace herowarsstealther
+// @author Mike Rohsoft
+// @version 1.006
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @run-at document-start
+// @grant unsafeWindow
+// @description Open Source Dungeon Runner for Hero Wars
+// @description:de Open Source Dungeon Runner for Hero Wars
+// @license Apache-2.0
+// @downloadURL https://update.greasyfork.org/scripts/572104/Hero%20Wars%20Stealther.user.js
+// @updateURL https://update.greasyfork.org/scripts/572104/Hero%20Wars%20Stealther.meta.js
+// ==/UserScript==
+
+// build.js
+const payload = (() => {
+ // src/globals/buffer.js
+ var GLOBAL_BUFFER = {
+ fixedBattle: {},
+ apiUrl: "",
+ lastRequestHeader: {},
+ requestCounter: 1,
+ fixedBattleTime: 0
+ };
+
+ // src/globals/game.js
+ var GAME = {
+ all: {}
+ };
+ function sign(callHeader, payload) {
+ const a = callHeader["X-Request-Id"];
+ const b = callHeader["X-Auth-Token"];
+ const c = callHeader["X-Auth-Session-Id"];
+ const d = callHeader["X-Env-Unique-Session-Id"];
+ const e = callHeader["X-Env-Unique-Session-Uuid"];
+ const f = `${a}:${b}:${c}:${payload}:LIBRARY-VERSION=1UNIQUE-SESSION-ID=${d}UNIQUE-SESSION-UUID=${e}`;
+ return GAME.MD5.encode(f);
+ }
+
+ // src/api/api_call.js
+ function normalizePayload(input) {
+ const list = Array.isArray(input) ? input : typeof input === "string" ? [{ name: input }] : [input];
+ return list.map((v, i) => {
+ if (typeof v === "string") {
+ v = { name: v };
+ }
+ return {
+ ident: v.ident ?? `ident_${i}_body`,
+ args: v.args ?? {},
+ context: {
+ actionTs: v.context?.actionTs ?? Math.floor(performance.now()),
+ ...v.context
+ },
+ ...v
+ };
+ });
+ }
+ function buildHeaders(baseHeader, payload) {
+ const headers = { ...baseHeader };
+ headers["X-Request-Id"] = (++GLOBAL_BUFFER.requestCounter).toString();
+ headers["X-Auth-Signature"] = sign(headers, payload);
+ return headers;
+ }
+ async function apiCall(payload, header = GLOBAL_BUFFER.lastRequestHeader) {
+ if (!header) {
+ return null;
+ }
+ const calls = normalizePayload(payload);
+ const body = JSON.stringify({ calls });
+ const headers = buildHeaders(header, body);
+ const res = await fetch(GLOBAL_BUFFER.apiUrl, {
+ method: "POST",
+ headers,
+ body
+ });
+ return res.json();
+ }
+
+ // src/ui/menu/menu.js
+ class CompactMenu {
+ constructor() {
+ if (CompactMenu._instance) {
+ return CompactMenu._instance;
+ }
+ CompactMenu._instance = this;
+ this.isOpen = false;
+ this.columns = [];
+ this.currentColumn = null;
+ this.elements = {};
+ this.title = "Hero Wars Stealther";
+ this.injectStyles();
+ this.createToggleButton();
+ this.createMenuContainer();
+ }
+ injectStyles() {
+ const style = document.createElement("style");
+ style.textContent = `.tm-toggle-btn{position:fixed;top:10px;left:50%;transform:translateX(-50%);z-index:999999;background:#fff;color:#000;border:none;padding:8px 20px;border-radius:6px;cursor:pointer;font-size:14px;font-weight:600;box-shadow:0 2px 8px rgba(0,0,0,.3);transition:all .2s}.tm-toggle-btn:hover{background:#FFAEC9;transform:translateX(-50%) translateY(-1px);box-shadow:0 4px 12px rgba(0,0,0,.4)}.tm-menu-container{position:fixed;top:50px;left:50%;transform:translateX(-50%);z-index:999998;background:#fff;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,.3);padding:15px;display:none;max-width:90vw}.tm-menu-container.open{display:block}.tm-menu-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;padding-bottom:10px;border-bottom:2px solid #ecf0f1}.tm-menu-title{font-size:16px;font-weight:700;color:#2c3e50;margin:0}.tm-close-btn{background:none;border:none;font-size:24px;cursor:pointer;color:#7f8c8d;padding:0;width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all .2s}.tm-close-btn:hover{background:#ecf0f1;color:#2c3e50}.tm-menu-content{display:flex;gap:20px}.tm-menu-column{display:flex;flex-direction:column;gap:10px;min-width:180px}.tm-toggle-item{display:flex;align-items:center;gap:8px;padding:6px 0}.tm-toggle-item input[type="checkbox"]{cursor:pointer;width:16px;height:16px}.tm-toggle-item label{cursor:pointer;font-size:14px;color:#2c3e50;user-select:none}.tm-value-item{display:flex;flex-direction:column;gap:4px}.tm-value-item input{padding:6px 10px;border:1px solid #bdc3c7;border-radius:4px;font-size:13px;width:100%;box-sizing:border-box}.tm-value-item input:focus{outline:none;border-color:#3498db}.tm-button-row{display:flex;gap:8px}.tm-button{background:#3498db;color:#fff;border:none;padding:8px 16px;border-radius:4px;cursor:pointer;font-size:13px;font-weight:600;transition:all .2s;flex:1}.tm-button:hover{background:#2980b9;transform:translateY(-1px)}`;
+ document.head.appendChild(style);
+ }
+ createNewElement(element, className, parent) {
+ const parentNode = parent || document.body;
+ this.newElement = document.createElement(element);
+ this.newElement.className = className;
+ return parentNode.appendChild(this.newElement);
+ }
+ createToggleButton() {
+ this.toggleBtn = this.createNewElement("button", "tm-toggle-btn", document.body);
+ this.toggleBtn.textContent = "Hero Wars Stealther";
+ this.toggleBtn.addEventListener("click", () => this.toggle());
+ }
+ setTitle(title) {
+ if (this.toggleBtn) {
+ this.toggleBtn.textContent = title;
+ }
+ }
+ createMenuContainer() {
+ this.menuContainer = this.createNewElement("div", "tm-menu-container", document.body);
+ const header = this.createNewElement("div", "tm-menu-header", this.menuContainer);
+ const title = this.createNewElement("h3", "tm-menu-title", header);
+ title.textContent = this.title;
+ const closeBtn = this.createNewElement("button", "tm-close-btn", header);
+ closeBtn.textContent = "×";
+ closeBtn.addEventListener("click", () => this.toggle());
+ this.menuContent = this.createNewElement("div", "tm-menu-content", this.menuContainer);
+ }
+ toggle() {
+ this.isOpen = !this.isOpen;
+ if (this.isOpen) {
+ this.menuContainer.classList.add("open");
+ this.toggleBtn.style.display = "none";
+ } else {
+ this.menuContainer.classList.remove("open");
+ this.toggleBtn.style.display = "block";
+ }
+ }
+ addColumn() {
+ const column = this.createNewElement("div", "tm-menu-column", this.menuContent);
+ this.columns.push(column);
+ this.currentColumn = column;
+ this.currentButtonRow = null;
+ return this;
+ }
+ addToggle(id, label, defaultValue, handler) {
+ if (!this.currentColumn) {
+ this.addColumn();
+ }
+ const item = this.createNewElement("div", "tm-toggle-item", this.currentColumn);
+ const checkbox = this.createNewElement("input", "", item);
+ checkbox.type = "checkbox";
+ checkbox.checked = defaultValue;
+ checkbox.id = `tm-${String(id)}`;
+ this.elements[id] = checkbox;
+ const labelEl = this.createNewElement("label", "", item);
+ labelEl.textContent = label;
+ labelEl.htmlFor = checkbox.id;
+ checkbox.addEventListener("change", (e) => handler(e.target.checked));
+ this.currentButtonRow = null;
+ return this;
+ }
+ addValue(id, placeholder, defaultValue, handler, type = "text") {
+ if (!this.currentColumn) {
+ this.addColumn();
+ }
+ const item = this.createNewElement("div", "tm-value-item", this.currentColumn);
+ const input = this.createNewElement("input", "", item);
+ input.type = type;
+ input.placeholder = placeholder;
+ input.value = defaultValue;
+ input.id = `tm-${String(id)}`;
+ this.elements[id] = input;
+ if (!!handler) {
+ input.addEventListener("change", (e) => handler(e.target.value));
+ input.addEventListener("input", (e) => handler(e.target.value));
+ }
+ this.currentButtonRow = null;
+ return this;
+ }
+ getChecked(id) {
+ if (!this.elements || !(id in this.elements)) {
+ return null;
+ }
+ return this.elements[id].checked;
+ }
+ getValue(id) {
+ if (!this.elements || !(id in this.elements)) {
+ return null;
+ }
+ return this.elements[id].value;
+ }
+ setChecked(id, checked) {
+ if (this.elements && id in this.elements) {
+ this.elements[id].checked = checked;
+ }
+ return this;
+ }
+ setValue(id, value) {
+ if (this.elements && id in this.elements) {
+ this.elements[id].value = value;
+ }
+ return this;
+ }
+ addButton(label, handler, inline = false) {
+ if (!this.currentColumn) {
+ this.addColumn();
+ }
+ const button = this.createNewElement("button", "tm-button", inline && this.currentButtonRow ? this.currentButtonRow : this.currentButtonRow = this.createNewElement("div", "tm-button-row", this.currentColumn));
+ button.textContent = label;
+ button.addEventListener("click", handler);
+ if (!inline) {
+ this.currentButtonRow = null;
+ }
+ return this;
+ }
+ }
+ CompactMenu._instance = undefined;
+
+ // src/utils/utils.js
+ var wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
+ var isIn = (obj, attributeName) => !!obj && Object.prototype.hasOwnProperty.call(obj, attributeName);
+ function parseJSON(text) {
+ let result;
+ let error = null;
+ try {
+ result = JSON.parse(text);
+ } catch (e) {
+ error = e;
+ }
+ return [error, result];
+ }
+ var err = (obj) => {
+ if (isIn(obj, "error")) {
+ const x = obj.error;
+ return `${x.name}: ${x.description}`;
+ }
+ };
+
+ // src/game/battle.js
+ function engineCall(obj, func) {
+ const callerMap = Object.getPrototypeOf(obj).__properties__;
+ const mapKey = Object.keys(callerMap).find((k) => callerMap[k] === func);
+ if (!mapKey) {
+ throw new Error(`Function ${func} not found in ${obj.constructor.name}`);
+ }
+ return obj[mapKey]();
+ }
+ var simulateBattle = (battleData, battleType) => new Promise((resolve, reject) => {
+ const MENU = new CompactMenu;
+ let battleInstantPlay;
+ let timeLimit;
+ if (!battleData) {
+ return MENU.setTitle("no battleData");
+ }
+ if (!battleType) {
+ return MENU.setTitle("no battleType");
+ }
+ try {
+ const battleConfig = engineCall(GAME.DataStorage[Object.keys(GAME.DataStorage)[24]], battleType);
+ if (!isIn(battleData, "progress")) {
+ battleData.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }];
+ }
+ const battlePresets = new GAME.BattlePresets(battleData.progress, false, true, battleConfig, false);
+ battleInstantPlay = new GAME.BattleInstantPlay(battleData, battlePresets);
+ timeLimit = engineCall(battlePresets, "get_timeLimit");
+ } catch (e) {
+ reject(e);
+ return;
+ }
+ if (!battleInstantPlay) {
+ reject();
+ return;
+ }
+ const responseHandler = function(instantBattle) {
+ const battleLogs = [];
+ const battleResults = engineCall(instantBattle, "get_result");
+ const battleData2 = engineCall(instantBattle, "get_rawBattleInfo");
+ const timers = [];
+ const battleResultKey = Object.keys(Object.getPrototypeOf(battleResults))[2];
+ let battles = 0;
+ let maxTime = 0;
+ for (const battleResult of battleResults[battleResultKey]) {
+ const battleLogReader = new GAME.BattleLogReader(battleResult);
+ const battleLog = GAME.BattleLogEncoder.read(battleLogReader);
+ battleLogs.push(battleLog);
+ battles += [...battleLog].length;
+ const newTimers = [...new Set(battleLog.map((e) => e.time < timeLimit && e.time !== 168.8 ? e.time : 0))];
+ timers.push(...newTimers);
+ maxTime += Math.max(...newTimers);
+ }
+ resolve({
+ battles,
+ timer: maxTime,
+ battleLogs,
+ timers,
+ battleData: battleData2,
+ progress: engineCall(battleResults, "get_progress"),
+ result: engineCall(battleResults, "get_result"),
+ limt: timeLimit
+ });
+ };
+ const responseHandlerKey = Object.keys(Object.getPrototypeOf(battleInstantPlay))[9];
+ battleInstantPlay[responseHandlerKey].add(responseHandler);
+ battleInstantPlay.start();
+ });
+
+ // src/globals/progress.js
+ var PROGRESS_BAR = [
+ "▒▒▒▒▒▒▒▒▒▒",
+ "█▒▒▒▒▒▒▒▒▒",
+ "██▒▒▒▒▒▒▒▒",
+ "███▒▒▒▒▒▒▒",
+ "████▒▒▒▒▒▒",
+ "█████▒▒▒▒▒",
+ "██████▒▒▒▒",
+ "███████▒▒▒",
+ "████████▒▒",
+ "█████████▒",
+ "██████████"
+ ];
+
+ // src/battle/pvp.js
+ class PvPBattleHandler {
+ constructor(battle = undefined, type = "get_clanPvp") {
+ this._type = type;
+ this.setBattle(battle);
+ }
+ setBattleType(value) {
+ this._type = value;
+ }
+ setBattle(battle) {
+ this._battle = battle;
+ this._counter = 0;
+ this._timers = undefined;
+ this._initialBattle = undefined;
+ this._lastBattle = undefined;
+ this._bestBattle = undefined;
+ this._maxBattles = 0;
+ this._errors = 0;
+ }
+ async init() {
+ this._initialBattle = await this.reCalculate(0);
+ this._maxBattles = this._initialBattle.timers.length;
+ this._timers = this._initialBattle.timers.sort(() => Math.random() - 0.5);
+ return this._initialBattle;
+ }
+ randomTime() {
+ const result = this._timers[this._counter % this._timers.length];
+ return result;
+ }
+ async reCalculate(timer = this.randomTime()) {
+ this._battle.progress = [{ attackers: { input: ["auto", 0, 0, "auto", this._counter, timer] } }];
+ const lastBattle = this._lastBattle;
+ try {
+ this._lastBattle = await simulateBattle(this._battle, this._type);
+ } catch (e) {
+ this._errors = this._errors + 1;
+ this._lastBattle = lastBattle;
+ }
+ this._counter = this._counter + 1;
+ return this._lastBattle;
+ }
+ count() {
+ return this._counter;
+ }
+ errors() {
+ return this._errors;
+ }
+ lastBattle() {
+ return this._lastBattle;
+ }
+ bestBattle() {
+ return this._bestBattle || this._lastBattle || this._initialBattle;
+ }
+ initialBattle() {
+ return this._initialBattle;
+ }
+ getFactor(before, after) {
+ let beforeSumFactor = 0;
+ for (let hero of Object.values(before)) {
+ const state = hero.state;
+ let factor = 1;
+ if (state) {
+ const hp = state.hp / hero.hp;
+ const energy = state.energy * 0.001;
+ factor = hp + energy * 0.05;
+ }
+ beforeSumFactor += factor;
+ }
+ let afterSumFactor = 0;
+ for (let [heroId, hero] of Object.entries(after)) {
+ const hp = hero.hp / (before?.[heroId]?.hp || hero.hp);
+ const energy = hero.energy * 0.001;
+ const factor = hp + energy * 0.05;
+ afterSumFactor += factor;
+ }
+ return afterSumFactor - beforeSumFactor;
+ }
+ getState(result) {
+ const beforeTitans = result.battleData?.defenders?.[0] || {};
+ const afterTitans = result.progress[0].defenders.heroes;
+ return this.getFactor(beforeTitans, afterTitans);
+ }
+ isWin() {
+ return this._initialBattle.result.win;
+ }
+ isBetter(bestBattle, thisBattle) {
+ if (!bestBattle || !thisBattle) {
+ return !!thisBattle;
+ }
+ if (thisBattle.result.win) {
+ return true;
+ }
+ const bestState = this.getState(bestBattle);
+ const thisState = this.getState(thisBattle);
+ if (!isFinite(thisState)) {
+ return false;
+ }
+ if (!isFinite(bestState)) {
+ return true;
+ }
+ return thisState < bestState;
+ }
+ success(bestBattle) {
+ return bestBattle.result.win;
+ }
+ max() {
+ return this._maxBattles;
+ }
+ async* bruteforce(endTime = Date.now() + 60000) {
+ if (endTime < Date.now()) {
+ endTime = Date.now() + 60000;
+ }
+ if (!this._initialBattle) {
+ this._initialBattle = await this.init();
+ }
+ while (Date.now() < endTime && this._counter < this._maxBattles) {
+ if (!(this._lastBattle = await this.reCalculate())) {
+ continue;
+ }
+ yield this._counter;
+ if (!this._bestBattle) {
+ this._bestBattle = this._lastBattle;
+ continue;
+ }
+ if (!this.isBetter(this._bestBattle, this._lastBattle)) {
+ continue;
+ }
+ this._bestBattle = this._lastBattle;
+ if (!this.success(this._bestBattle)) {
+ continue;
+ }
+ break;
+ }
+ return this._bestBattle;
+ }
+ async* calculateWinChance(times = 10) {
+ let wins = 0;
+ if (isNaN(times)) {
+ return;
+ }
+ const originalSeed = this._battle.seed;
+ let battleBuffer;
+ for (let i = 0;i < times; i++) {
+ this._battle.seed = Math.floor(Date.now() / 1000) + Math.random() * 1000;
+ battleBuffer = await simulateBattle(this._battle, this._type);
+ const win = battleBuffer?.result?.win;
+ if (win) {
+ wins++;
+ }
+ yield wins;
+ }
+ this._battle.seed = originalSeed;
+ }
+ static extractBattles(result) {
+ const battles = [];
+ if (isIn(result.result.response, "battles")) {
+ battles.push(...result.result.response.battles);
+ } else if (isIn(result.result.response, "battle")) {
+ battles.push(result.result.response.battle);
+ } else {
+ battles.push(result.result.response);
+ }
+ return battles;
+ }
+ }
+
+ // src/battle/handler.js
+ async function runBattleHandler(battleHandler, forceFix = false, skipPreCalc = false, overwriteWin = false, time = 120000) {
+ const MENU = new CompactMenu;
+ const initBattle = await battleHandler.init();
+ const endTimer = time - initBattle.timer * 1000;
+ let totalSeconds = Math.floor(endTimer / 1000).toString();
+ const minutes = Math.floor(+totalSeconds / 60).toString();
+ if (totalSeconds.length === 1) {
+ totalSeconds = `0${totalSeconds}`;
+ }
+ const seconds = +totalSeconds % 60;
+ const isWin = battleHandler.isWin();
+ const WinOrLose = isWin ? "✅" : "❌";
+ MENU.setTitle(`${WinOrLose} ${minutes}:${seconds}`);
+ await wait(0);
+ const trys = MENU.getValue("num_of_tries");
+ const num_of_tries = +(trys ?? 0);
+ let wins = 0;
+ if (num_of_tries > 0 && !skipPreCalc) {
+ let count = 1;
+ for await (const liveWins of battleHandler.calculateWinChance(num_of_tries)) {
+ MENU.setTitle(`${WinOrLose} (${liveWins}/${count}) ${minutes}:${seconds}`);
+ count++;
+ wins = liveWins;
+ await wait(0);
+ }
+ MENU.setTitle(`${WinOrLose} (${wins}/${num_of_tries}) ${minutes}:${seconds}`);
+ await wait(0);
+ }
+ if (!forceFix && (isWin || overwriteWin)) {
+ return {
+ initBattle,
+ bestBattle: null,
+ wins,
+ simuations: num_of_tries,
+ isWin,
+ minutes,
+ seconds,
+ isFixedBattle: false,
+ timer: initBattle.timer,
+ isFixedBattleWin: false,
+ timeDiff: 0
+ };
+ }
+ const max = battleHandler.max();
+ for await (const count of battleHandler.bruteforce()) {
+ const index = Math.floor(count / max * 10);
+ MENU.setTitle(` ${WinOrLose} (${wins}/${num_of_tries}) ${minutes}:${seconds} ${PROGRESS_BAR[index % PROGRESS_BAR.length]}`);
+ await wait(0);
+ }
+ const bestBattle = battleHandler.bestBattle();
+ const stateA = battleHandler.getState(initBattle);
+ const stateB = battleHandler.getState(bestBattle);
+ const diff = Math.round(bestBattle.timer - initBattle.timer) * 1000;
+ MENU.setTitle(` ${bestBattle.result.win ? "\uD83D\uDC26\uD83D\uDD25" : "❌"} [${battleHandler.count()}] (${wins}/${num_of_tries}) ${minutes}:${seconds} [${Math.floor(stateB - stateA) * 100}%] [+${diff}⏳]`);
+ return {
+ initBattle,
+ bestBattle,
+ wins,
+ simuations: num_of_tries,
+ isWin,
+ minutes,
+ seconds,
+ isFixedBattle: true,
+ timer: initBattle.timer,
+ isFixedBattleWin: bestBattle.result.win,
+ timeDiff: diff
+ };
+ }
+
+ // src/battle/runner.js
+ class ActivityRunner {
+ constructor() {
+ this._menu = new CompactMenu;
+ this._menu.toggle();
+ }
+ async init() {
+ let call = await apiCall([
+ "towerGetInfo",
+ "teamGetAll",
+ "teamGetFavor",
+ "heroGetAll",
+ "titanGetAll",
+ "dungeonGetInfo",
+ "clanRaid_getInfo"
+ ]);
+ if (!call || !call.results) {
+ return null;
+ }
+ let [
+ { result: { response: towerGetInfo } },
+ { result: { response: teamGetAll } },
+ { result: { response: teamGetFavor } },
+ { result: { response: heroGetAll } },
+ { result: { response: titanGetAll } },
+ { result: { response: dungeonGetInfo } },
+ { result: { response: clanRaidGetInfo } }
+ ] = call.results;
+ const titans = Object.values(titanGetAll).sort((x, y) => y.power - x.power);
+ const heroes = Object.values(heroGetAll).sort((x, y) => y.power - x.power);
+ return {
+ towerGetInfo,
+ teamGetAll,
+ teamGetFavor,
+ heroes,
+ titans,
+ dungeonGetInfo,
+ clanRaidGetInfo
+ };
+ }
+ getTitans(titans, states = {}) {
+ const all = titans.filter((x) => !states[x.id.toString()]?.isDead).sort((x, y) => y.power - x.power);
+ const water = [];
+ const fire = [];
+ const earth = [];
+ const dark = [];
+ const light = [];
+ const unknown = [];
+ for (const titan of all) {
+ switch (true) {
+ case titan.id < 4010:
+ water.push(titan);
+ break;
+ case titan.id < 4020:
+ fire.push(titan);
+ break;
+ case titan.id < 4030:
+ earth.push(titan);
+ break;
+ case titan.id < 4040:
+ dark.push(titan);
+ break;
+ case titan.id < 4050:
+ light.push(titan);
+ break;
+ default:
+ unknown.push(titan);
+ }
+ }
+ return {
+ all,
+ water: water.sort((x, y) => y.power - x.power),
+ earth: earth.sort((x, y) => y.power - x.power),
+ fire: fire.sort((x, y) => y.power - x.power),
+ dark: dark.sort((x, y) => y.power - x.power),
+ light: light.sort((x, y) => y.power - x.power),
+ elemental: [...dark, ...light, ...unknown].sort((x, y) => y.power - x.power)
+ };
+ }
+ getTitansForPotentialHealingTeam(aliveTitans, states = {}, index = 0) {
+ const normalize = (id) => Number(id);
+ if (aliveTitans.water.length < 3) {
+ return null;
+ }
+ const result = [];
+ const used = new Set;
+ const push = (id) => {
+ const n = normalize(id.toString());
+ if (!used.has(n) && result.length < 5) {
+ used.add(n);
+ result.push(n);
+ }
+ };
+ const allStates = [];
+ for (const [titanId, state] of Object.entries(states)) {
+ const id = normalize(titanId);
+ if (id < 4010 || id >= 4030) {
+ continue;
+ }
+ if (state.isDead) {
+ continue;
+ }
+ const diff = state.hp / state.maxHp;
+ if (diff === 1) {
+ continue;
+ }
+ allStates.push({ diff, id });
+ }
+ for (const titan of aliveTitans.water.slice(0, 4)) {
+ push(+titan.id);
+ }
+ if (allStates.length === 0) {
+ return null;
+ }
+ if (allStates.length > 0 && allStates[index]) {
+ const candidates = allStates.sort((a, b) => a.diff - b.diff);
+ push(candidates[index].id);
+ } else if (allStates.length > 0) {
+ return null;
+ }
+ if (result.length < 5) {
+ for (const titan of aliveTitans.elemental ?? []) {
+ if (result.length >= 5) {
+ break;
+ }
+ push(titan.id);
+ }
+ if (result.length < 5) {
+ const alive = [...aliveTitans.earth, ...aliveTitans.fire].sort((a, b) => b.power - a.power);
+ for (const titan of alive) {
+ if (result.length >= 5) {
+ break;
+ }
+ push(titan.id);
+ }
+ }
+ }
+ return result.length === 5 ? result : null;
+ }
+ getNeutralTitans(aliveTitans, strongest = false) {
+ const normalize = (id) => Number(id);
+ if (strongest) {
+ return aliveTitans.all.slice(0, 5).map((t) => normalize(t.id));
+ }
+ const result = [];
+ const used = new Set;
+ const waterPower = aliveTitans.water.reduce((sum, hero) => hero.power + sum, 0);
+ if (waterPower > 500000 && aliveTitans.water.length >= 4) {
+ for (const waterTitan of aliveTitans.water) {
+ if (result.length === 4) {
+ break;
+ }
+ result.push(waterTitan.id);
+ used.add(waterTitan.id);
+ }
+ }
+ const push = (id) => {
+ const n = normalize(id);
+ if (!used.has(n) && result.length < 5) {
+ used.add(n);
+ result.push(n);
+ }
+ };
+ const elementMap = {
+ water: { max: 4010, special: 4004 },
+ earth: { max: 4030, special: 4034 },
+ fire: { max: 4020, special: 4024 },
+ dark: { max: 4040 },
+ light: { max: 4050 }
+ };
+ for (const titan of aliveTitans.all) {
+ if (result.length >= 4) {
+ break;
+ }
+ const id = normalize(titan.id);
+ if (used.has(id)) {
+ continue;
+ }
+ if (id < elementMap.water.max) {
+ const group = aliveTitans.water.map((t) => normalize(t.id));
+ const specialAlive = group.includes(elementMap.water.special);
+ if (specialAlive) {
+ push(elementMap.water.special);
+ const partner = group.find((x) => x !== elementMap.water.special);
+ if (partner) {
+ push(partner);
+ }
+ } else {
+ push(id);
+ const others = group.filter((x) => x !== id);
+ for (let i = 0;i < others.length && result.length < 5 && i < 2; i++) {
+ push(others[i]);
+ }
+ }
+ } else if (id < elementMap.earth.max) {
+ const group = aliveTitans.earth.map((t) => normalize(t.id));
+ const specialAlive = group.includes(elementMap.earth.special);
+ if (specialAlive) {
+ push(elementMap.earth.special);
+ const partner = group.find((x) => x !== elementMap.earth.special);
+ if (partner) {
+ push(partner);
+ }
+ } else {
+ push(id);
+ const others = group.filter((x) => x !== id);
+ for (let i = 0;i < others.length && result.length < 5 && i < 2; i++) {
+ push(others[i]);
+ }
+ }
+ } else if (id < elementMap.fire.max) {
+ const group = aliveTitans.fire.map((t) => normalize(t.id));
+ const specialAlive = group.includes(elementMap.fire.special);
+ if (specialAlive) {
+ push(elementMap.fire.special);
+ const partner = group.find((x) => x !== elementMap.fire.special);
+ if (partner) {
+ push(partner);
+ }
+ } else {
+ push(id);
+ const others = group.filter((x) => x !== id);
+ for (let i = 0;i < others.length && result.length < 5 && i < 2; i++) {
+ push(others[i]);
+ }
+ }
+ } else if (id < elementMap.dark.max) {
+ push(id);
+ const partner = aliveTitans.dark.map((t) => normalize(t.id)).find((x) => x !== id);
+ if (partner) {
+ push(partner);
+ }
+ } else if (id < elementMap.light.max) {
+ push(id);
+ const partner = aliveTitans.light.map((t) => normalize(t.id)).find((x) => x !== id);
+ if (partner) {
+ push(partner);
+ }
+ }
+ }
+ if (result.length < 5) {
+ for (const titan of aliveTitans.all) {
+ if (result.length >= 5) {
+ break;
+ }
+ push(titan.id);
+ }
+ }
+ return result;
+ }
+ static getHeroTeam(currentTeam, heroes, allFavors = null, heroStates = {}) {
+ let allHeroes = [];
+ if (Array.isArray(heroes)) {
+ allHeroes = heroes;
+ } else {
+ allHeroes = Object.values(heroes).filter((x) => x.color > 1).sort((x, y) => y.power - x.power);
+ }
+ const newTeam = currentTeam.filter((x) => !!x && !heroStates?.[x]?.isDead && x < 2000);
+ const find = currentTeam.filter((x) => !!x && x >= 6000);
+ let pet = null;
+ if (find.length === 1) {
+ const newPet = find.shift();
+ if (newPet) {
+ pet = newPet;
+ }
+ }
+ const currentAliveCount = newTeam.length;
+ const rest = 5 - currentAliveCount;
+ if (rest !== 0) {
+ for (let i = 0;i < rest; i++) {
+ if (allHeroes.length === 0) {
+ continue;
+ }
+ const filler = allHeroes.shift();
+ if (heroStates?.[filler.id]?.isDead) {
+ continue;
+ }
+ newTeam.push(filler.id);
+ }
+ }
+ const favorArgs = {};
+ if (allFavors) {
+ for (const id of newTeam) {
+ if (!id || !isIn(allFavors, id)) {
+ continue;
+ }
+ favorArgs[id] = allFavors[id];
+ }
+ }
+ return {
+ heroes: newTeam,
+ favor: favorArgs,
+ pet
+ };
+ }
+ }
+
+ // src/ui/functions/run_,minion_nodes.js
+ class NodeRunner extends ActivityRunner {
+ constructor() {
+ super();
+ }
+ async run() {
+ const initData = await this.init();
+ if (!initData) {
+ return;
+ }
+ let maxAttempts = initData.clanRaidGetInfo.attempts;
+ if (maxAttempts === 0) {
+ return;
+ }
+ this._menu.setTitle(maxAttempts.toString());
+ for (let [index, node] of Object.entries(initData.clanRaidGetInfo.nodes)) {
+ let packs = 0;
+ let cleanNode = true;
+ for (const team of node.teams) {
+ packs++;
+ for (const stateObj of team.states) {
+ for (const state of Object.values(stateObj)) {
+ if (state.state.isDead) {
+ cleanNode = false;
+ break;
+ }
+ }
+ if (!cleanNode) {
+ break;
+ }
+ }
+ if (!cleanNode) {
+ break;
+ }
+ }
+ if (!cleanNode) {
+ continue;
+ }
+ const teams = [];
+ let favor = {};
+ for (let i = 0;i < packs; i++) {
+ if (!initData.teamGetAll.clanRaid_nodes[i]) {
+ break;
+ }
+ const heroes = [];
+ let pet;
+ for (const hero of initData.teamGetAll.clanRaid_nodes[i]) {
+ if (hero < 6000) {
+ heroes.push(hero);
+ if (initData.teamGetFavor.clanRaid_nodes[hero]) {
+ favor[hero] = initData.teamGetFavor.clanRaid_nodes[hero];
+ }
+ } else {
+ pet = hero;
+ }
+ }
+ teams.push({
+ heroes,
+ pet: pet || null,
+ battleIndex: i,
+ data: {}
+ });
+ }
+ const battleStartResult = await apiCall({ name: "clanRaid_startNodeBattles", args: { nodeId: index, teams, favor } });
+ if (battleStartResult.error) {
+ console.log(`failed to start battle for node ${index}`);
+ continue;
+ }
+ const result = battleStartResult.results[0];
+ let lostABattle = false, battleIndex = 0;
+ const battleResults = [];
+ for (const battle of PvPBattleHandler.extractBattles(result)) {
+ const runner = new PvPBattleHandler(battle, "get_clanPvp");
+ const { bestBattle } = await runBattleHandler(runner);
+ if (!bestBattle) {
+ continue;
+ }
+ if (!bestBattle.result.win) {
+ lostABattle = true;
+ }
+ battleResults.push(bestBattle);
+ battleIndex++;
+ }
+ if (lostABattle) {
+ for (let i = 0, v;v = battleResults[i]; i++) {
+ v.result.win = !v.result.win;
+ }
+ }
+ this._menu.setTitle(maxAttempts.toString());
+ await wait(1000);
+ for (let i = 0, v;v = battleResults[i]; i++) {
+ await apiCall({ name: "clanRaid_endNodeBattle", args: { nodeId: index, battleIndex: i, result: v.result, progress: v.progress } });
+ }
+ maxAttempts--;
+ this._menu.setTitle(maxAttempts.toString());
+ await wait(0);
+ }
+ this._menu.setTitle("✅");
+ }
+ }
+ async function runMinionNodes() {
+ const minions = new NodeRunner;
+ await minions.run();
+ }
+
+ // src/battle/dungeon.js
+ class DungeonBattleHandler extends PvPBattleHandler {
+ getState(result) {
+ if (!result.result.win) {
+ return -1000;
+ }
+ const beforeTitans = result.battleData?.attackers || {};
+ const afterTitans = result.progress[0].attackers.heroes;
+ return this.getFactor(beforeTitans, afterTitans);
+ }
+ success() {
+ return false;
+ }
+ isBetter(bestBattle, thisBattle) {
+ if (!bestBattle || !thisBattle) {
+ return !!thisBattle;
+ }
+ const bestState = this.getState(bestBattle);
+ const thisState = this.getState(thisBattle);
+ if (!thisBattle.result.win) {
+ return false;
+ }
+ if (!isFinite(thisState)) {
+ return false;
+ }
+ if (!isFinite(bestState)) {
+ return true;
+ }
+ return thisState > bestState;
+ }
+ }
+
+ // src/ui/functions/run_dungeon.js
+ class DungeonRunner extends ActivityRunner {
+ constructor() {
+ super();
+ if (DungeonRunner._instance) {
+ return DungeonRunner._instance;
+ }
+ DungeonRunner._instance = this;
+ this._isRestart = false;
+ this._lastError = null;
+ this._titans = null;
+ this._teamGetAll = null;
+ this._handler = null;
+ this._initialTitanLayout = null;
+ this._lastDebugString = null;
+ }
+ isRunning() {
+ return this._running;
+ }
+ getLastError() {
+ return this._lastError;
+ }
+ stop() {
+ this._running = false;
+ }
+ _setError(message) {
+ this._lastError = message;
+ this._menu.setTitle(message);
+ return false;
+ }
+ _getResponse(result) {
+ return result?.results?.[0]?.result?.response;
+ }
+ _createBattleArgs(teamNum, heroes, pet) {
+ return {
+ name: "dungeonStartBattle",
+ args: {
+ heroes,
+ favor: {},
+ teamNum,
+ ...pet ? { pet } : {}
+ }
+ };
+ }
+ async _initialize() {
+ if (this._lastDebugString) {
+ this._menu.setTitle(`Dungeon Runner - Last: ${this._lastDebugString}`);
+ } else {
+ this._menu.setTitle("Dungeon Runner");
+ }
+ const data = await this.init();
+ if (!data) {
+ return this._setError("Error fetching initial data");
+ }
+ this._titans = data.titans;
+ this._teamGetAll = data.teamGetAll;
+ this._initialTitanLayout = this.getTitans(this._titans);
+ const waterPower = this._initialTitanLayout.water.reduce((a, b) => a + b.power, 0);
+ const earthPower = this._initialTitanLayout.earth.reduce((a, b) => a + b.power, 0);
+ const firePower = this._initialTitanLayout.fire.reduce((a, b) => a + b.power, 0);
+ const waterStrongest = waterPower >= earthPower && waterPower >= firePower;
+ const waterWithin25Percent = earthPower <= waterPower * 1.25 && firePower <= waterPower * 1.25;
+ this._isAbleToHeal = waterStrongest || waterWithin25Percent;
+ console.log("Water %s | Earth %s | Fire %s| canHeal: ", waterPower, earthPower, firePower, this._isAbleToHeal);
+ return true;
+ }
+ async _fetchDungeonData() {
+ const result = await apiCall("dungeonGetInfo");
+ if (!Array.isArray(result.results)) {
+ return this._setError("Error fetching dungeonGetInfo");
+ }
+ const dungeonGetInfo = this._getResponse(result);
+ if (!dungeonGetInfo?.floor?.userData) {
+ return this._setError("No dungeon data");
+ }
+ return { dungeonGetInfo };
+ }
+ async _handleRestart(dungeonGetInfo) {
+ if (!dungeonGetInfo.floor && !this._isRestart) {
+ this._isRestart = true;
+ await apiCall("dungeonSaveProgress");
+ return true;
+ } else if (this._isRestart) {
+ this._lastError = "Error in dungeonGetInfo: missing floor";
+ return false;
+ }
+ this._isRestart = false;
+ return false;
+ }
+ async _startAndSimulate(teamNum, heroes, pet, attackerType) {
+ const raw = await apiCall(this._createBattleArgs(teamNum, heroes, pet));
+ const battleData = this._getResponse(raw);
+ if (!battleData) {
+ return null;
+ }
+ const isBruteForceBattle = attackerType !== "hero";
+ this._handler = new DungeonBattleHandler(battleData, isBruteForceBattle ? "get_titan" : "get_tower");
+ const handlerResult = await runBattleHandler(this._handler, isBruteForceBattle, isBruteForceBattle);
+ const battle = handlerResult.bestBattle ?? handlerResult.initBattle;
+ if (!battle) {
+ return null;
+ }
+ return {
+ teamNum,
+ heroes,
+ pet,
+ result: battle.result,
+ progress: battle.progress,
+ timer: battle.timer,
+ win: battle.result.win,
+ state: this._handler.getState(battle)
+ };
+ }
+ async _waitForBattle(heroes, attackerType, timer, debug = "") {
+ const display = this._getTeamDisplay(heroes, attackerType);
+ for (let rounds = Math.ceil(timer);rounds > 0; rounds--) {
+ if (!this._running) {
+ return;
+ }
+ let title = `${display} ... ${rounds}⏳`;
+ if (debug) {
+ title += ` | ${debug}`;
+ }
+ this._menu.setTitle(title);
+ await wait(1000);
+ }
+ }
+ _getTeamDisplay(heroes, type) {
+ if (type === "hero") {
+ return "\uD83E\uDDD9".repeat(heroes.length);
+ }
+ const result = [];
+ for (const id of heroes) {
+ switch (true) {
+ case id === 4004:
+ result.push("\uD83C\uDF00");
+ break;
+ case id === 4003:
+ result.push("\uD83C\uDF0A");
+ break;
+ case id === 4024:
+ result.push("☘️");
+ break;
+ case id === 4023:
+ result.push("\uD83C\uDF31");
+ break;
+ case id === 4014:
+ result.push("\uD83D\uDC26\uD83D\uDD25");
+ break;
+ case id === 4013:
+ result.push("❤️\uD83D\uDD25");
+ break;
+ case id < 4010:
+ result.push("\uD83D\uDCA7");
+ break;
+ case id < 4020:
+ result.push("\uD83D\uDD25");
+ break;
+ case id < 4030:
+ result.push("\uD83C\uDF43");
+ break;
+ case id < 4040:
+ result.push("\uD83C\uDF11");
+ break;
+ case id < 4050:
+ result.push("\uD83C\uDF15");
+ break;
+ default:
+ result.push("\uD83C\uDF12");
+ }
+ }
+ return result.join("");
+ }
+ _getOptionDisplay(type) {
+ switch (type) {
+ case "neutral":
+ return "\uD83C\uDF12";
+ case "water":
+ return "\uD83D\uDCA7";
+ case "fire":
+ return "\uD83D\uDD25";
+ case "earth":
+ return "\uD83C\uDF43";
+ case "hero":
+ return "\uD83E\uDDD9";
+ case "healing":
+ return "\uD83C\uDF0A";
+ default:
+ return "❓";
+ }
+ }
+ _getDeads(option) {
+ const after = option.progress?.[0]?.attackers?.heroes || {};
+ return option.heroes.length + Number(!!option.pet) - Object.keys(after).length;
+ }
+ _debugString(option, attackerType) {
+ if (!option) {
+ return "INVALID";
+ }
+ let s = `[${option.teamNum}]${this._getOptionDisplay(attackerType)}`;
+ const win = option.result?.win ? "✅" : "❌";
+ s += ` ${win}`;
+ const damage = option.state / option.heroes.length * 100;
+ s += ` ⚔️${damage.toFixed(0)}%`;
+ const after = option.progress?.[0]?.attackers?.heroes || {};
+ s += ` \uD83D\uDC80${this._getDeads(option)}`;
+ return s;
+ }
+ _isOptionBetter(bestOption, thisOption, states) {
+ if (!thisOption) {
+ return false;
+ }
+ if (!bestOption) {
+ return true;
+ }
+ const bestTeam = bestOption.heroes || [];
+ const thisTeam = thisOption.heroes || [];
+ const bestAvailable = bestTeam.length;
+ const thisAvailable = thisTeam.length;
+ if (thisAvailable !== bestAvailable) {
+ return thisAvailable > bestAvailable;
+ }
+ const bestState = bestOption.state ?? 0;
+ const thisState = thisOption.state ?? 0;
+ const bestNormalized = bestAvailable > 0 ? bestState * bestAvailable : 0;
+ const thisNormalized = thisAvailable > 0 ? thisState * thisAvailable : 0;
+ return thisNormalized > bestNormalized;
+ }
+ async _endBattle(option) {
+ const result = await apiCall({
+ name: "dungeonEndBattle",
+ args: {
+ result: option.result,
+ progress: option.progress
+ }
+ });
+ if (!Array.isArray(result.results)) {
+ return this._setError(`Error ending battle ${result.error}`);
+ }
+ const response = this._getResponse(result);
+ if (!response?.dungeon) {
+ await apiCall("dungeonSaveProgress");
+ }
+ this._menu.setTitle("⏭️");
+ return true;
+ }
+ async _executeOption(option, attackerType, debug = "") {
+ await this._waitForBattle(option.heroes, attackerType, option.timer, debug);
+ if (!this._running) {
+ return;
+ }
+ return this._endBattle(option);
+ }
+ _isHealingSuccessful(healingTeam, progress, states) {
+ const afterHeroes = progress?.[0]?.attackers?.heroes;
+ if (!afterHeroes) {
+ return false;
+ }
+ return healingTeam.filter((id) => id >= 4010).every((id) => {
+ const after = afterHeroes[id];
+ return after && after.hp > (states[id]?.hp || 0);
+ });
+ }
+ async _runStep() {
+ await wait(100);
+ if (!this._isRestart) {
+ this._lastDebugString = "";
+ }
+ const data = await this._fetchDungeonData();
+ if (!data || !this._titans) {
+ return false;
+ }
+ const { dungeonGetInfo } = data;
+ const didRestart = await this._handleRestart(dungeonGetInfo);
+ if (didRestart) {
+ return true;
+ }
+ if (this._lastError) {
+ return false;
+ }
+ const userData = dungeonGetInfo.floor?.userData;
+ if (!userData) {
+ return this._setError("No user data");
+ }
+ if (dungeonGetInfo.talent) {
+ await apiCall([
+ { name: "heroTalent_getReward", args: { talentType: "tmntDungeonTalent", reroll: false } },
+ { name: "heroTalent_farmReward", args: { talentType: "tmntDungeonTalent" } }
+ ]);
+ }
+ if (!dungeonGetInfo.elements) {
+ return this._setError("Error in dungeonGetInfo: missing primeElement");
+ }
+ if (!dungeonGetInfo.states) {
+ return this._setError("Error in dungeonGetInfo: missing states");
+ }
+ const states = dungeonGetInfo.states.titans;
+ const aliveTitans = this.getTitans(this._titans, states);
+ const heroBattleIndex = userData.findIndex((ud) => ud.attackerType === "hero");
+ if (heroBattleIndex !== -1) {
+ this._menu.setTitle("\uD83E\uDDD9");
+ const heroTeam = this._teamGetAll.dungeon_hero;
+ if (!Array.isArray(heroTeam)) {
+ return this._setError("No hero team");
+ }
+ const pet = heroTeam.find((v) => v > 6000) || null;
+ const heroes = heroTeam.filter((v) => v < 6000);
+ const option = await this._startAndSimulate(heroBattleIndex, heroes, pet, "hero");
+ if (!option) {
+ return this._setError("Failed to start hero battle");
+ }
+ if (!option.result?.win) {
+ return this._setError("Hero battle would lose");
+ }
+ this._lastDebugString += this._debugString(option, "hero");
+ return this._executeOption(option, "hero", this._lastDebugString);
+ }
+ this._menu.setTitle("⏳⏳");
+ const options = [];
+ for (let teamNum = 0;teamNum < userData.length; teamNum++) {
+ const { attackerType } = userData[teamNum];
+ let team = null;
+ let useHealingIndex = 0;
+ if (attackerType === "neutral") {
+ if (this._isAbleToHeal) {
+ let healingTeam = null;
+ let healingOption = null;
+ while (true) {
+ healingTeam = this.getTitansForPotentialHealingTeam(aliveTitans, states, useHealingIndex);
+ if (!healingTeam) {
+ break;
+ }
+ healingOption = await this._startAndSimulate(teamNum, healingTeam, null, attackerType);
+ if (!healingOption?.win) {
+ useHealingIndex++;
+ continue;
+ }
+ if (this._isHealingSuccessful(healingTeam, healingOption.progress, states) && this._getDeads(healingOption) === 0) {
+ team = { heroes: healingTeam, pet: null, isHealing: true, option: healingOption };
+ break;
+ }
+ useHealingIndex++;
+ }
+ }
+ if (!team) {
+ const neutralTeam = this.getNeutralTitans(aliveTitans, !this._isAbleToHeal);
+ team = neutralTeam.length > 0 ? { heroes: neutralTeam, pet: null } : null;
+ }
+ } else {
+ const pool = aliveTitans[attackerType] ? aliveTitans[attackerType] : aliveTitans.all;
+ const heroes = pool.slice(0, 5).map((t) => t.id);
+ team = heroes.length > 0 ? { heroes, pet: null } : null;
+ }
+ if (!team) {
+ options.push(null);
+ continue;
+ }
+ const option = team.option || await this._startAndSimulate(teamNum, team.heroes, team.pet, attackerType);
+ if (!option?.win) {
+ options.push(null);
+ continue;
+ }
+ if (team.isHealing && option.win) {
+ this._lastDebugString += this._debugString(option, attackerType) + " \uD83C\uDF0A";
+ return this._executeOption(option, attackerType, this._lastDebugString);
+ } else if (team.isHealing) {
+ const fallback = this.getNeutralTitans(aliveTitans);
+ if (fallback.length > 0) {
+ const fallbackOption = await this._startAndSimulate(teamNum, fallback, null, attackerType);
+ options.push(fallbackOption?.win ? { option: fallbackOption, attackerType } : null);
+ } else {
+ options.push(null);
+ }
+ continue;
+ }
+ options.push({ option, attackerType });
+ }
+ const valid = options.filter(Boolean);
+ if (valid.length === 0) {
+ return this._setError("No winnable battles available");
+ }
+ if (valid.length > 1) {
+ const statesArr = valid.map((v) => this._handler?.getState(v?.option));
+ this._lastDebugString += valid.map((v) => this._debugString(v?.option, v?.attackerType)).join(" | ");
+ } else if (valid[0]) {
+ const state = this._handler?.getState(valid[0].option);
+ this._lastDebugString += this._debugString(valid[0]?.option, valid[0].attackerType);
+ }
+ const best = valid.length === 1 ? valid[0] : valid.reduce((best2, current) => this._isOptionBetter(best2?.option, current?.option, states) ? current : best2);
+ if (!best || !best.option) {
+ return this._setError("No best battle found");
+ }
+ this._lastDebugString += ` -> ${best.option.teamNum} `;
+ if (best.option.teamNum !== userData.length - 1) {
+ const restarted = await this._startAndSimulate(best.option.teamNum, best.option.heroes, best.option.pet, best.attackerType);
+ if (!restarted?.win) {
+ return this._setError("Restart failed");
+ }
+ this._lastDebugString += this._debugString(restarted, best.attackerType) + " \uD83D\uDD01";
+ return this._executeOption(restarted, best.attackerType, this._lastDebugString);
+ }
+ return this._executeOption(best.option, best.attackerType, this._lastDebugString);
+ }
+ async run() {
+ if (this._running) {
+ return;
+ }
+ this._running = true;
+ this._lastError = null;
+ const initialized = await this._initialize();
+ if (!initialized) {
+ this._running = false;
+ return;
+ }
+ while (this._running) {
+ const success = await this._runStep();
+ if (!success) {
+ console.log(this._lastError ? `DungeonRunner stopped due to error: ${this._lastError}` : "DungeonRunner stopped");
+ break;
+ }
+ await wait(100);
+ }
+ this._running = false;
+ }
+ }
+ DungeonRunner._instance = undefined;
+
+ // src/ui/functions/run_tower.js
+ async function runTower() {
+ const MENU = new CompactMenu;
+ MENU.toggle();
+ let call = await apiCall(["teamGetAll", "towerGetInfo", "teamGetFavor", "heroGetAll"]);
+ let [
+ { result: { response: teamGetAll } },
+ { result: { response: towerGetInfo } },
+ { result: { response: teamGetFavor } },
+ { result: { response: heroGetAll } }
+ ] = call.results;
+ if (towerGetInfo.mayFullSkip && +towerGetInfo.floorNumber === 1) {
+ const calls = [];
+ for (let i = 0;i < 15; i++) {
+ calls.push("towerNextChest", { name: "towerOpenChest", args: { num: Math.floor(Math.random() * 3) } });
+ }
+ await apiCall(calls);
+ return MENU.setTitle("✅");
+ }
+ const canSkipToFloor = +towerGetInfo.maySkipFloor || 0;
+ const favors = Object.assign({}, teamGetFavor.tower, teamGetFavor.clanDefence_heroes, teamGetFavor.adventure_hero, teamGetFavor.arena_def, teamGetFavor.arena, teamGetFavor.crossClanDefence_heroes, teamGetFavor.grand, teamGetFavor.grand_def, teamGetFavor.clanRaid_nodes);
+ const { pet } = ActivityRunner.getHeroTeam(teamGetAll.tower, heroGetAll, favors, towerGetInfo.states.heroes);
+ let default_team = [];
+ let lastFloor = 0;
+ while (true) {
+ await wait(100);
+ call = await apiCall("towerGetInfo");
+ const error = err(call);
+ if (error) {
+ this._lastError = error;
+ return;
+ }
+ [{ result: { response: towerGetInfo } }] = call.results;
+ if (+towerGetInfo.floorNumber === 50) {
+ await apiCall("tower_farmSkullReward");
+ return MENU.setTitle("✅");
+ }
+ if (lastFloor >= +towerGetInfo.floorNumber) {
+ return MENU.setTitle(`❌ [${towerGetInfo.floorNumber}] 1`);
+ }
+ if (+towerGetInfo.floor.state === 2 || towerGetInfo.floorType === "buff") {
+ await apiCall("towerNextFloor");
+ } else if (towerGetInfo.floorType === "chest") {
+ await apiCall([{ name: "towerOpenChest", args: { num: Math.floor(Math.random() * 3) } }, "towerNextFloor"]);
+ } else if (towerGetInfo.floorType === "battle") {
+ if (+towerGetInfo.floorNumber < canSkipToFloor) {
+ await apiCall("towerSkipFloor");
+ continue;
+ }
+ const states = towerGetInfo.states.heroes || {};
+ const args = ActivityRunner.getHeroTeam(default_team, heroGetAll, favors, states);
+ let allDead = args.heroes.length === 0;
+ if (allDead) {
+ return MENU.setTitle("☠️");
+ }
+ const { heroes, favor } = args;
+ const call2 = await apiCall({ name: "towerStartBattle", args: { heroes, favor, pet } });
+ if (!call2 || !call2.results) {
+ return MENU.setTitle(`❌ [${towerGetInfo.floorNumber}] 2`);
+ }
+ const [{ result: { response: towerBattle } }] = call2.results;
+ const runner = new DungeonBattleHandler(towerBattle, "get_tower");
+ const { bestBattle, timer } = await runBattleHandler(runner, true, true);
+ if (!bestBattle) {
+ return MENU.setTitle(`❌ [${towerGetInfo.floorNumber}] 3`);
+ }
+ for (let rounds = Math.round(timer);rounds > 0; rounds--) {
+ MENU.setTitle(`[${towerGetInfo.floorNumber}] ... ${rounds}⏳`);
+ await wait(1000);
+ }
+ await apiCall({ name: "towerEndBattle", args: { result: bestBattle.result, progress: bestBattle.progress } });
+ }
+ }
+ }
+
+ // src/ui/menu_layout.js
+ function initMenu() {
+ const menu = new CompactMenu;
+ menu.addColumn().addValue("num_of_tries", "Number of Battle Pre-Calculations", "10").addButton("Dungeon", () => {
+ const runner = new DungeonRunner;
+ if (runner.isRunning()) {
+ return runner.stop();
+ }
+ runner.run();
+ }).addButton("Tower", runTower).addButton("Asgard Minions", runMinionNodes);
+ }
+
+ // src/hooks/game_hook.js
+ var HOOK_FUNCTION_NAME = "game.data.storage.DataStorage";
+ function GAME_HOOK(value) {
+ delete Object.prototype[HOOK_FUNCTION_NAME];
+ this[HOOK_FUNCTION_NAME] = value;
+ GAME.all = this;
+ GAME.DataStorage = this[HOOK_FUNCTION_NAME];
+ GAME.BattlePresets = this["game.battle.controller.thread.BattlePresets"];
+ GAME.BattleInstantPlay = this["game.battle.controller.instant.BattleInstantPlay"];
+ GAME.BattleLogReader = this["battle.log.BattleLogReader"];
+ GAME.BattleLogEncoder = this["battle.log.BattleLogEncoder"];
+ GAME.GameModel = this["game.model.GameModel"];
+ GAME.NextDayUpdatedManager = this["game.model.user.NextDayUpdatedManager"];
+ GAME.MD5 = this["haxe.crypto.Md5"];
+ for (const obj of Object.values(GAME)) {
+ console.assert(!!obj, `${obj} is not defined`);
+ }
+ GAME.GameModel.prototype._start = GAME.GameModel.prototype.start;
+ GAME.GameModel.prototype.start = function(a, b, c) {
+ for (const id of Object.keys(b.raw.seasonAdventure.level)) {
+ b.raw.seasonAdventure.level[id].clientData.graphics.fogged = b.raw.seasonAdventure.level[id].clientData.graphics.visible;
+ }
+ this._start(a, b, c);
+ };
+ initMenu();
+ }
+
+ // src/globals/header.js
+ var REQUEST_HEADER = {};
+
+ // src/hooks/request_open.js
+ function REQUEST_OPEN_HOOK(method, url, async, user, password) {
+ this.ignore = !/nextersglobal\.com\/api\/$/.test(url.toString());
+ if (this.ignore) {
+ this.send = this._send;
+ }
+ for (const key of Object.keys(REQUEST_HEADER)) {
+ delete REQUEST_HEADER[key];
+ }
+ if (!this.ignore && !GLOBAL_BUFFER.apiUrl) {
+ GLOBAL_BUFFER.apiUrl = url.toString();
+ }
+ this._open(method, url, async, user, password);
+ }
+
+ // src/mitm/requests/end_pvp_battle.js
+ async function request_endPvPBattle(call) {
+ let result = null;
+ if ((!call.args?.result?.win || call.name === "towerEndBattle" || call.name === "dungeonEndBattle") && isIn(GLOBAL_BUFFER.fixedBattle, "result")) {
+ call.args.progress = GLOBAL_BUFFER.fixedBattle?.progress;
+ call.args.result = GLOBAL_BUFFER.fixedBattle?.result;
+ result = call;
+ }
+ if (!call.args?.result?.win && (call.name.indexOf("adventure") !== -1 || call.name === "clanRaid_endNodeBattle") && confirm("Cancel Battle?")) {
+ call.args.result.win = true;
+ result = call;
+ }
+ if (result && (call.name === "invasion_bossEnd" || call.name === "dungeonEndBattle" || call.name === "missionEnd") && GLOBAL_BUFFER.fixedBattleTime > 0) {
+ const MENU = new CompactMenu;
+ for (let rounds = Math.round(GLOBAL_BUFFER.fixedBattleTime / 1000);rounds > 0; rounds--) {
+ MENU.setTitle(`... ${rounds}⏳`);
+ await wait(1000);
+ }
+ }
+ GLOBAL_BUFFER.fixedBattleTime = 0;
+ GLOBAL_BUFFER.fixedBattle = {};
+ return result;
+ }
+
+ // src/mitm/request_handler.js
+ var CALL_HANDLER = {
+ on: function(event, func) {
+ if (!Array.isArray(event)) {
+ this[event] = func;
+ return;
+ }
+ for (let e of event) {
+ this[e] = func;
+ }
+ }
+ };
+ CALL_HANDLER.on([
+ "adventure_endBattle",
+ "adventureSolo_endBattle",
+ "clanWarEndBattle",
+ "crossClanWar_endBattle",
+ "dungeonEndBattle",
+ "clanRaid_endBossBattle",
+ "invasion_bossEnd",
+ "titanArenaEndBattle",
+ "brawl_endBattle",
+ "clanRaid_endNodeBattle",
+ "towerEndBattle",
+ "missionEnd",
+ "bossEndBattle",
+ "epicBrawl_endBattle"
+ ], request_endPvPBattle);
+
+ // src/mitm/responses/auto_battle_start.js
+ async function startBattleAuto(result, call) {
+ const MENU = new CompactMenu;
+ let battleHandler = new PvPBattleHandler;
+ let captions = [];
+ for (const battle of PvPBattleHandler.extractBattles(result)) {
+ const battleType = battle?.effects?.battleConfig ?? battle?.type;
+ battleHandler.setBattleType("get_pvp");
+ battleHandler.setBattle(battle);
+ const { wins, simuations, minutes, seconds, isWin } = await runBattleHandler(battleHandler, false, false, true);
+ captions.push(`${isWin ? "✅" : "❌"} (${wins}/${simuations}) ${minutes}:${seconds}`);
+ }
+ MENU.setTitle(captions.join(" | "));
+ return null;
+ }
+
+ // src/mitm/responses/bassAttack.js
+ async function response_bossAttack(result, call) {
+ let battleHandler = new PvPBattleHandler;
+ for (const battle of PvPBattleHandler.extractBattles(result)) {
+ battleHandler.setBattleType("get_boss");
+ battleHandler.setBattle(battle);
+ const { bestBattle } = await runBattleHandler(battleHandler, false, false, false, 120000);
+ if (!bestBattle) {
+ continue;
+ }
+ if (bestBattle.result.win) {
+ GLOBAL_BUFFER.fixedBattle = {
+ progress: bestBattle.progress,
+ result: bestBattle.result
+ };
+ }
+ }
+ return null;
+ }
+
+ // src/mitm/responses/clan_raid_start_boss_battle.js
+ async function response_clanRaid_startBossBattle(result, call) {
+ const MENU = new CompactMenu;
+ const battleHandler = new PvPBattleHandler;
+ for (const battle of PvPBattleHandler.extractBattles(result)) {
+ battleHandler.setBattle(battle);
+ battleHandler.setBattleType("get_clanPvp");
+ const { initBattle } = await runBattleHandler(battleHandler, false, true, true, 300000);
+ let extraPointer = initBattle.progress[0].defenders.heroes[1].extra;
+ const damageA = new Intl.NumberFormat().format(extraPointer.damageTaken + extraPointer.damageTakenNextLevel);
+ MENU.setTitle(`${damageA}`);
+ }
+ return null;
+ }
+
+ // src/mitm/responses/invasion_battle_start.js
+ async function response_invasionBattleStart(result, call) {
+ const battleHandler = new PvPBattleHandler;
+ for (const battle of PvPBattleHandler.extractBattles(result)) {
+ let battleConfig = "get_invasion";
+ if (battle.effects.battleConfig.indexOf("titan") !== -1) {
+ battleConfig = "get_invasionTitan";
+ }
+ battleHandler.setBattleType(battleConfig);
+ battleHandler.setBattle(battle);
+ const { bestBattle, isFixedBattleWin, timeDiff } = await runBattleHandler(battleHandler, false, false, false, 300000);
+ if (!bestBattle) {
+ continue;
+ }
+ if (isFixedBattleWin) {
+ GLOBAL_BUFFER.fixedBattle = {
+ progress: bestBattle.progress,
+ result: bestBattle.result
+ };
+ GLOBAL_BUFFER.fixedBattleTime = timeDiff;
+ }
+ }
+ return null;
+ }
+
+ // src/mitm/responses/missionStart.js
+ async function response_missionStart(result, call) {
+ let battleHandler = new PvPBattleHandler;
+ for (const battle of PvPBattleHandler.extractBattles(result)) {
+ const battleType = battle?.effects?.battleConfig ?? battle?.type;
+ const isBossBattle = battleType?.indexOf("boss") !== -1;
+ battleHandler.setBattleType(isBossBattle ? "get_missionBoss" : "get_pve");
+ battleHandler.setBattle(battle);
+ const { bestBattle } = await runBattleHandler(battleHandler, false, false, false, isBossBattle ? 300000 : 120000);
+ if (!bestBattle) {
+ continue;
+ }
+ if (bestBattle.result.win) {
+ GLOBAL_BUFFER.fixedBattle = {
+ progress: bestBattle.progress,
+ result: bestBattle.result
+ };
+ }
+ }
+ return null;
+ }
+
+ // src/mitm/responses/mission_raid.js
+ async function response_missionRaid(result, call) {
+ if (call.args.times !== 1) {
+ return null;
+ }
+ const amount = prompt("Follow Raids", "0");
+ if (isNaN(Number(amount))) {
+ return null;
+ }
+ for (let i = 0;i < Number(amount); i++) {
+ await apiCall({
+ name: "missionRaid",
+ args: {
+ id: call.args.id,
+ times: 1
+ }
+ });
+ }
+ return null;
+ }
+
+ // src/mitm/responses/start_dungeon_battle.js
+ async function response_startDungeonBattle(result, call) {
+ const battleHandler = new DungeonBattleHandler(undefined, undefined);
+ for (const battle of DungeonBattleHandler.extractBattles(result)) {
+ const keys = Object.keys(battle.attackers);
+ const isHeroBattle = keys.length > 1 && battle.attackers[keys[0]].type === "hero";
+ const isDungeonBattle = call.name === "dungeonEndBattle";
+ const noForceFix = !(isDungeonBattle && isHeroBattle);
+ battleHandler.setBattleType(isHeroBattle ? "get_tower" : "get_titan");
+ battleHandler.setBattle(battle);
+ const { bestBattle, timeDiff } = await runBattleHandler(battleHandler, noForceFix, false, false);
+ if (!bestBattle) {
+ continue;
+ }
+ GLOBAL_BUFFER.fixedBattle = {
+ progress: bestBattle.progress,
+ result: bestBattle.result
+ };
+ GLOBAL_BUFFER.fixedBattleTime = timeDiff;
+ }
+ return null;
+ }
+
+ // src/mitm/responses/start_pvp_battle.js
+ async function startBattleManual(result, call) {
+ let battleHandler = new PvPBattleHandler;
+ for (const battle of PvPBattleHandler.extractBattles(result)) {
+ const battleType = battle?.effects?.battleConfig ?? battle?.type;
+ battleHandler.setBattleType(battleType?.indexOf("titan") !== -1 ? "get_titanPvpManual" : "get_clanPvp");
+ battleHandler.setBattle(battle);
+ const { bestBattle, timeDiff } = await runBattleHandler(battleHandler);
+ if (!bestBattle) {
+ continue;
+ }
+ GLOBAL_BUFFER.fixedBattle = {
+ progress: bestBattle.progress,
+ result: bestBattle.result
+ };
+ GLOBAL_BUFFER.fixedBattleTime = timeDiff;
+ }
+ return null;
+ }
+
+ // src/mitm/responses/titan_arena_start_battle.js
+ async function response_titanArenaStartBattle(result, call) {
+ let battleHandler = new PvPBattleHandler;
+ for (const battle of PvPBattleHandler.extractBattles(result)) {
+ battleHandler.setBattleType("get_titanPvpManual");
+ battleHandler.setBattle(battle);
+ await runBattleHandler(battleHandler, false, false, true);
+ }
+ return null;
+ }
+
+ // src/mitm/response_handler.js
+ var CALL_RESPONSE_HANDLER = {
+ on: function(event, func) {
+ if (!Array.isArray(event)) {
+ this[event] = func;
+ return;
+ }
+ for (let e of event) {
+ this[e] = func;
+ }
+ }
+ };
+ CALL_RESPONSE_HANDLER.on([
+ "adventure_turnStartBattle",
+ "adventureSolo_turnStartBattle",
+ "clanWarAttack",
+ "crossClanWar_startBattle",
+ "epicBrawl_startBattle",
+ "brawl_startBattle",
+ "clanRaid_startNodeBattles"
+ ], startBattleManual);
+ CALL_RESPONSE_HANDLER.on([
+ "arenaAttack",
+ "clanDomination_startBattle",
+ "grandAttack",
+ "demoBattles_startBattle",
+ "chatAcceptChallenge"
+ ], startBattleAuto);
+ CALL_RESPONSE_HANDLER.on("missionRaid", response_missionRaid);
+ CALL_RESPONSE_HANDLER.on(["dungeonStartBattle", "towerStartBattle"], response_startDungeonBattle);
+ CALL_RESPONSE_HANDLER.on("clanRaid_startBossBattle", response_clanRaid_startBossBattle);
+ CALL_RESPONSE_HANDLER.on("invasion_bossStart", response_invasionBattleStart);
+ CALL_RESPONSE_HANDLER.on("titanArenaStartBattle", response_titanArenaStartBattle);
+ CALL_RESPONSE_HANDLER.on("missionStart", response_missionStart);
+ CALL_RESPONSE_HANDLER.on("bossAttack", response_bossAttack);
+
+ // src/hooks/send.js
+ async function hooked_onReadyStateChange(e) {
+ if (this.readyState !== 4 || this.status !== 200) {
+ return this._onreadystatechange(e);
+ }
+ let err2;
+ let responseJS;
+ let rewrite = false;
+ [err2, responseJS] = parseJSON(this.responseText || this.response);
+ if (err2 || !isIn(responseJS, "results")) {
+ return this._onreadystatechange(e);
+ }
+ for (let call, index = 0;call = this._request.calls[index]; index++) {
+ if (!isIn(CALL_RESPONSE_HANDLER, call.name)) {
+ continue;
+ }
+ for (let i = 0, result;result = responseJS?.results[i]; i++) {
+ if (result.ident !== call.ident) {
+ continue;
+ }
+ const newResponse = await CALL_RESPONSE_HANDLER[call.name](result, call);
+ rewrite = !!newResponse;
+ if (Array.isArray(newResponse)) {
+ const array = newResponse;
+ result = array.shift();
+ responseJS?.results.push(...array);
+ }
+ if (rewrite) {
+ result = newResponse;
+ }
+ break;
+ }
+ }
+ if (rewrite) {
+ console.debug("rewriting response", responseJS);
+ Object.defineProperty(this, this.response ? "response" : "responseText", { writable: true, value: JSON.stringify(responseJS) });
+ }
+ return this._onreadystatechange(e);
+ }
+ async function SEND_HOOK(sourceData) {
+ let resign = false;
+ let tempBuffer;
+ let tempData = null;
+ let err2;
+ if (this.ignore) {
+ return this._send(sourceData);
+ }
+ if (sourceData instanceof ArrayBuffer) {
+ tempBuffer = new TextDecoder("utf-8").decode(sourceData);
+ } else if (ArrayBuffer.isView(sourceData)) {
+ tempBuffer = new TextDecoder("utf-8").decode(sourceData.buffer);
+ }
+ if (!tempBuffer) {
+ return this._send(sourceData);
+ }
+ [err2, tempData] = parseJSON(tempBuffer);
+ const header = Object.assign({}, REQUEST_HEADER);
+ if (err2 || !tempData) {} else if (isIn(tempData, "calls")) {
+ this._request = Object.assign({}, tempData);
+ let newCall;
+ const additoonalCalls = [];
+ for (let call, index = 0;call = tempData.calls[index]; index++) {
+ console.debug("Processing call", call.name, call);
+ if (isIn(CALL_RESPONSE_HANDLER, call.name)) {
+ this._onreadystatechange = this.onreadystatechange;
+ this.onreadystatechange = hooked_onReadyStateChange;
+ }
+ if (!isIn(CALL_HANDLER, call.name)) {
+ continue;
+ }
+ newCall = await CALL_HANDLER[call.name](call);
+ if (Array.isArray(newCall)) {
+ const array = newCall;
+ newCall = array.shift();
+ additoonalCalls.push(...array);
+ }
+ tempData.calls[index] = newCall || call;
+ resign = !!newCall;
+ }
+ tempData.calls.push(...additoonalCalls);
+ }
+ const xas = "X-Auth-Signature";
+ if (resign) {
+ sourceData = JSON.stringify(tempData);
+ header[xas] = sign(header, sourceData);
+ }
+ if (isIn(header, xas)) {
+ this._setRequestHeader(xas, header[xas]);
+ }
+ GLOBAL_BUFFER.lastRequestHeader = Object.assign({}, header);
+ return this._send(sourceData);
+ }
+
+ // src/hooks/set_request_header.js
+ function SET_REQUEST_HEADER_HOOK(name, value) {
+ if (this.ignore) {
+ return this._setRequestHeader(name, value);
+ }
+ REQUEST_HEADER[name] = value;
+ if (name === "X-Request-Id") {
+ GLOBAL_BUFFER.requestCounter = +value;
+ }
+ if (name !== "X-Auth-Signature") {
+ return this._setRequestHeader(name, value);
+ }
+ }
+
+ // src/index.js
+ var populateTo = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
+ Object.defineProperty(Object.prototype, HOOK_FUNCTION_NAME, { configurable: true, set: GAME_HOOK });
+ XMLHttpRequest.prototype._open = XMLHttpRequest.prototype.open;
+ XMLHttpRequest.prototype.open = REQUEST_OPEN_HOOK;
+ XMLHttpRequest.prototype._setRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
+ XMLHttpRequest.prototype.setRequestHeader = SET_REQUEST_HEADER_HOOK;
+ XMLHttpRequest.prototype._send = XMLHttpRequest.prototype.send;
+ XMLHttpRequest.prototype.send = SEND_HOOK;
+ populateTo.apiCall = apiCall;
+ populateTo.CALL_HANDLER = CALL_HANDLER;
+ populateTo.CALL_RESPONSE_HANDLER = CALL_RESPONSE_HANDLER;
+ populateTo.CompactMenu = CompactMenu;
+ populateTo.simulateBattle = simulateBattle;
+ populateTo.GAME = GAME;
+});
+
+// build.js
+if (unsafeWindow.Object !== Object) {
+ const script = document.createElement("script");
+ const scriptPayload = `(${payload.toString()})();`;
+ const blobUrl = URL.createObjectURL(new Blob([scriptPayload], { type: "text/javascript" }));
+ script.src = blobUrl;
+ document.documentElement.appendChild(script);
+ script.remove();
+ URL.revokeObjectURL(blobUrl);
+} else {
+ payload();
+}
diff --git a/HeroWarsHelper - Auto Daily Extension.user.js b/HeroWarsHelper - Auto Daily Extension.user.js
new file mode 100644
index 0000000..6cae8bc
--- /dev/null
+++ b/HeroWarsHelper - Auto Daily Extension.user.js
@@ -0,0 +1,2520 @@
+// ==UserScript==
+// @name HeroWarsHelper - Auto Daily Extension
+// @namespace http://tampermonkey.net/
+// @version 3.2.8
+// @description Adds an advanced auto-run panel for daily tasks and quests to HeroWarsHelper.
+// @author Your Name & Coding Partner
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @grant none
+// @run-at document-end
+// ==/UserScript==
+
+(function() {
+ 'use strict';
+
+ // --- CONFIGURATION ---
+ const EXTENSION_NAME = "Auto Daily Extension";
+ const EXTENSION_VERSION = "3.2.8";
+ const EXTENSION_AUTHOR = "You";
+
+ /** Verbose dungeon logs: `window.HWH_DEBUG_DUNGEON = true` before run. */
+ /** Per-end-battle prediction card count: `window.HWH_LOG_PREDICTION_CARDS = true` (HeroWarsHelper). */
+ /** Battle pre-calc count (0-25): `window.HWH_DUNGEON_NUM_TRIES` (default 10). */
+ /** Step delay between floors ms: `window.HWH_DUNGEON_STEP_DELAY_MS` (default 100). */
+ /** Brute-force budget ms per battle sim: `window.HWH_DUNGEON_BRUTEFORCE_MS` (default 60000, Stealther default). */
+ /** Optional override for door comparison only: `window.HWH_DUNGEON_EVAL_BRUTEFORCE_MS`. */
+ /** Optional override when restarting a non-last door: `window.HWH_DUNGEON_EXECUTE_BRUTEFORCE_MS`. */
+ /** Max timer slots per battle sim: `window.HWH_DUNGEON_MAX_TIMER_TRIES` (0 = unlimited, Stealther default). */
+
+ // ASCII-safe UI icons (avoids UTF-8 encoding issues in userscript managers)
+ const UI_ICON = {
+ fire: '\uD83D\uDD25',
+ pending: '\u23F3',
+ save: '\uD83D\uDCBE',
+ sync: '\uD83D\uDD04',
+ unavailable: '\uD83C\uDF11',
+ done: '\u2705',
+ };
+
+ // --- STATE VARIABLES ---
+ let executionState = {};
+ let hideButtonsState = {};
+ let othersSettingsState = {};
+ let isProviderActive = false;
+ let customOthersButton = null;
+ let combinedButton = null;
+ let cachedQuestData = null; // Cache for questGetAll results
+ let autoRunInProgress = false;
+ let dungeonRunning = false;
+
+ function setDungeonBattleOpen(isOpen) {
+ window.HWH_DUNGEON_BATTLE_OPEN = !!isOpen;
+ }
+
+ async function waitForAutoBattleIdle(maxWaitMs = 45 * 60 * 1000) {
+ const start = Date.now();
+ while (window.HWH_AUTOBATTLE_RUNNING && Date.now() - start < maxWaitMs) {
+ if (window.HWHFuncs?.setProgress) {
+ window.HWHFuncs.setProgress('Dungeon: waiting for AutoBattle to finish...', true);
+ }
+ await sleep(1000);
+ }
+ return !window.HWH_AUTOBATTLE_RUNNING;
+ }
+
+ // --- DUNGEON TITAN HEALTH SETTINGS ---
+ const defaultTitanHealthSettings = {
+ minOverallHP: 0.30,
+ titan4020HP: 0.40,
+ titan4020EnergyHP: 0.20,
+ titan4010Combined: 0.67,
+ titan4000HP: 0.63,
+ titan4000Energy400HP: 0.45,
+ titan4000Energy670HP: 0.34,
+ autoRefreshPage: false
+ };
+
+ let titanHealthSettings = {};
+ let stopDung = false; // External stop mechanism for dungeon
+
+ // External stop function for dungeon
+ window.stopHWDDungeon = () => {
+ if (typeof stopDung !== 'undefined') {
+ stopDung = true;
+ console.log('HWD Dungeon stop requested externally.');
+ } else {
+ console.log('stopDung variable not found or not in scope.');
+ }
+ };
+
+ function loadTitanHealthSettings() {
+ const { HWHFuncs } = window;
+ if (HWHFuncs && HWHFuncs.getSaveVal) {
+ titanHealthSettings = HWHFuncs.getSaveVal('titanHealthSettings', defaultTitanHealthSettings);
+ } else {
+ titanHealthSettings = Object.assign({}, defaultTitanHealthSettings);
+ }
+ }
+
+ function saveTitanHealthSettings() {
+ const { HWHFuncs } = window;
+ if (HWHFuncs && HWHFuncs.setSaveVal) {
+ HWHFuncs.setSaveVal('titanHealthSettings', titanHealthSettings);
+ }
+ }
+
+ function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ }
+
+ async function waitFor(predicate, { timeoutMs = 30000, intervalMs = 200 } = {}) {
+ const start = Date.now();
+ while (Date.now() - start < timeoutMs) {
+ try {
+ if (predicate()) return true;
+ } catch (e) {
+ // ignore predicate errors while waiting
+ }
+ await sleep(intervalMs);
+ }
+ return false;
+ }
+
+ async function withTimeout(promise, timeoutMs, timeoutMessage = 'Timed out') {
+ let t;
+ const timeoutPromise = new Promise((_, reject) => {
+ t = setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs);
+ });
+ try {
+ return await Promise.race([promise, timeoutPromise]);
+ } finally {
+ clearTimeout(t);
+ }
+ }
+
+ // --- QUEST DATA CACHE ---
+ async function getQuestData(forceRefresh = false) {
+ if (cachedQuestData && !forceRefresh) {
+ return cachedQuestData;
+ }
+ const { Send } = window;
+ const questResponse = await Send({ calls: [{ name: "questGetAll", args: {}, ident: "questGetAll" }] });
+ cachedQuestData = questResponse.results[0].result.response;
+ return cachedQuestData;
+ }
+
+ function invalidateQuestCache() {
+ cachedQuestData = null;
+ }
+
+ // --- REIMPLEMENTED CORE FUNCTIONS (WRAPPERS) ---
+ async function executeGetOutland() {
+ const { Send, HWHFuncs } = window;
+ HWHFuncs.setProgress('Executing: Outland', true);
+ try {
+ const data = await Send({ calls: [{ name: "bossGetAll", args: {}, ident: "bossGetAll" }] });
+ const bosses = data.results[0].result.response;
+ const calls = [];
+ for (const boss of bosses) {
+ if (boss.mayRaid) calls.push({ name: "bossRaid", args: { bossId: boss.id }, ident: "bossRaid_" + boss.id });
+ if (boss.chestId === 1 || boss.mayRaid) calls.push({ name: "bossOpenChest", args: { bossId: boss.id, amount: 1, starmoney: 0 }, ident: "bossOpenChest_" + boss.id });
+ }
+ if (calls.length > 0) await Send({ calls });
+ HWHFuncs.setProgress('Outland: Done!', true);
+ } catch (e) { console.error("Error in executeGetOutland", e); HWHFuncs.setProgress('Outland: Error!', true); }
+ }
+ async function executeTestTower() {
+ const { HWHClasses, HWHFuncs } = window;
+ HWHFuncs.setProgress('Executing: Tower', true);
+ return new Promise((resolve) => { new HWHClasses.executeTower(resolve, resolve).start(); });
+ }
+ async function executeCheckExpedition() {
+ const { HWHClasses, HWHFuncs } = window;
+ HWHFuncs.setProgress('Executing: Expeditions', true);
+ return new Promise((resolve) => { new HWHClasses.Expedition(resolve, resolve).start(); });
+ }
+ // Dungeon Algorithm - ported from Hero Wars Stealther 1.006 (Mike Rohsoft)
+ function executeDungeon(resolve, reject) {
+ const { HWHFuncs, Send, BattleCalc, cheats } = window;
+ const { getInput, setProgress, hideProgress, I18N, getTimer, countdownTimer } = HWHFuncs;
+
+ const DUNGEON_VERBOSE = typeof window !== 'undefined' && window.HWH_DEBUG_DUNGEON === true;
+ const NUM_TRIES = Math.max(0, Math.min(25, Number(window.HWH_DUNGEON_NUM_TRIES) || 10));
+ const BRUTEFORCE_MS = Math.max(5000, Math.min(120000, Number(window.HWH_DUNGEON_BRUTEFORCE_MS) || 60000));
+ const EVAL_BRUTEFORCE_MS = Math.max(5000, Math.min(120000, Number(window.HWH_DUNGEON_EVAL_BRUTEFORCE_MS) || BRUTEFORCE_MS));
+ const RESTART_BRUTEFORCE_MS = Math.max(5000, Math.min(120000, Number(window.HWH_DUNGEON_EXECUTE_BRUTEFORCE_MS) || BRUTEFORCE_MS));
+ const STEP_DELAY_MS = Math.max(0, Math.min(1000, Number(window.HWH_DUNGEON_STEP_DELAY_MS) || 100));
+ const MAX_TIMER_TRIES = window.HWH_DUNGEON_MAX_TIMER_TRIES != null
+ ? Math.max(0, Math.min(200, Number(window.HWH_DUNGEON_MAX_TIMER_TRIES) || 0))
+ : 0;
+ const SIM_YIELD_EVERY = 3;
+
+ function syncPredictionCardsFromInventory(invRes) {
+ const raw = invRes?.result?.response?.consumable?.[81];
+ const n = Math.max(0, Math.floor(Number(raw)) || 0);
+ if (window.HWHData) {
+ window.HWHData.countPredictionCard = n;
+ }
+ return n;
+ }
+
+ let dungeonActivity = 0;
+ let startDungeonActivity = 0;
+ let maxDungeonActivity = 150;
+ let end = false;
+ let talentMsg = '';
+ let talentMsgReward = '';
+ let titansList = [];
+ let teamGetAll = null;
+ let teamGetFavor = null;
+ let isAbleToHeal = false;
+ let isRestart = false;
+ let lastError = null;
+ let lastDebugString = '';
+ let lastBattleHandler = null;
+ let lastStartedTeamNum = -1;
+ let battleStartTime = 0;
+ let stepCount = 0;
+ let timeDungeon = { all: Date.now(), steps: 0 };
+
+ function getApiResult(result) {
+ if (!result) return null;
+ if (result.status >= 400 || result.errors) {
+ return { error: result, validation: result.errors };
+ }
+ if (result.error && !result.results) {
+ return { error: result.error };
+ }
+ return result?.results?.[0]?.result;
+ }
+
+ function getResponse(result) {
+ return getApiResult(result)?.response;
+ }
+
+ function buildHeroFavor(heroIds, favorMap) {
+ const favor = {};
+ if (!favorMap || !heroIds?.length) return favor;
+ for (const id of heroIds) {
+ const petId = favorMap[id] ?? favorMap[String(id)];
+ if (petId) {
+ favor[id] = petId;
+ }
+ }
+ return favor;
+ }
+
+ function getHeroTeamForBattle(heroStates) {
+ const heroTeam = teamGetAll?.dungeon_hero;
+ if (!Array.isArray(heroTeam)) {
+ return null;
+ }
+ const pet = heroTeam.find((v) => v > 6000) || null;
+ const heroes = heroTeam.filter((v) => {
+ if (!v || v <= 0 || v >= 6000) return false;
+ const state = heroStates?.[v] ?? heroStates?.[String(v)];
+ return !state?.isDead;
+ });
+ if (heroes.length === 0) {
+ return null;
+ }
+ return {
+ heroes,
+ pet,
+ favor: buildHeroFavor(heroes, teamGetFavor?.dungeon_hero),
+ };
+ }
+
+ function simulateBattle(battleData, battleType) {
+ return new Promise((resolveSim, rejectSim) => {
+ const data = structuredClone(battleData);
+ if (!data.progress) {
+ data.progress = [{ attackers: { input: ['auto', 0, 0, 'auto', 0, 0] } }];
+ }
+ try {
+ BattleCalc(data, battleType, (result) => {
+ if (result) resolveSim(result);
+ else rejectSim(new Error('BattleCalc returned empty result'));
+ });
+ } catch (e) {
+ rejectSim(e);
+ }
+ });
+ }
+
+ // Stealther uses battlePresets.get_timeLimit(); 180s covers standard dungeon/tower battles.
+ const BATTLE_TIME_LIMIT = 180;
+
+ function extractTimers(battleResult, maxTimerTries = MAX_TIMER_TRIES) {
+ const logs = battleResult.battleLogs?.[0] || [];
+ if (logs.length === 0) {
+ return [0];
+ }
+ const timers = [...new Set(logs.map((e) => (e.time > 0 && e.time < BATTLE_TIME_LIMIT && e.time !== 168.8 ? e.time : 0)))];
+ timers.sort(() => Math.random() - 0.5);
+ if (maxTimerTries > 0 && timers.length > maxTimerTries) {
+ return timers.slice(0, maxTimerTries);
+ }
+ return timers.length > 0 ? timers : [0];
+ }
+
+ class PvPBattleHandler {
+ constructor(battle = undefined, type = 'get_clanPvp') {
+ this._type = type;
+ this.setBattle(battle);
+ }
+
+ setBattle(battle) {
+ this._battle = battle ? structuredClone(battle) : undefined;
+ this._counter = 0;
+ this._timers = undefined;
+ this._initialBattle = undefined;
+ this._lastBattle = undefined;
+ this._bestBattle = undefined;
+ this._maxBattles = 0;
+ this._errors = 0;
+ }
+
+ async init(maxTimerTries = MAX_TIMER_TRIES) {
+ this._initialBattle = await this.reCalculate(0);
+ this._timers = extractTimers(this._initialBattle, maxTimerTries);
+ if (this._timers.length === 0) {
+ this._timers = [0];
+ }
+ this._timers.sort(() => Math.random() - 0.5);
+ this._maxBattles = this._timers.length;
+ return this._initialBattle;
+ }
+
+ randomTime() {
+ return this._timers[this._counter % this._timers.length];
+ }
+
+ async reCalculate(timer = this.randomTime()) {
+ const battle = structuredClone(this._battle);
+ battle.progress = [{ attackers: { input: ['auto', 0, 0, 'auto', this._counter, timer] } }];
+ const prev = this._lastBattle;
+ try {
+ this._lastBattle = await simulateBattle(battle, this._type);
+ this._lastBattle.timer = this._lastBattle.battleTime ?? 0;
+ } catch (e) {
+ this._errors++;
+ this._lastBattle = prev;
+ }
+ this._counter++;
+ return this._lastBattle;
+ }
+
+ count() {
+ return this._counter;
+ }
+
+ max() {
+ return this._maxBattles;
+ }
+
+ isWin() {
+ return !!this._initialBattle?.result?.win;
+ }
+
+ success(bestBattle) {
+ return !!bestBattle?.result?.win;
+ }
+
+ bestBattle() {
+ return this._bestBattle || this._lastBattle || this._initialBattle;
+ }
+
+ initialBattle() {
+ return this._initialBattle;
+ }
+
+ getFactor(before, after) {
+ let beforeSumFactor = 0;
+ for (const hero of Object.values(before || {})) {
+ const state = hero.state;
+ let factor = 1;
+ if (state) {
+ const hp = state.hp / hero.hp;
+ const energy = state.energy * 0.001;
+ factor = hp + energy * 0.05;
+ }
+ beforeSumFactor += factor;
+ }
+ let afterSumFactor = 0;
+ for (const [heroId, hero] of Object.entries(after || {})) {
+ const hp = hero.hp / (before?.[heroId]?.hp || hero.hp);
+ const energy = hero.energy * 0.001;
+ afterSumFactor += hp + energy * 0.05;
+ }
+ return afterSumFactor - beforeSumFactor;
+ }
+
+ isBetter(bestBattle, thisBattle) {
+ if (!bestBattle || !thisBattle) {
+ return !!thisBattle;
+ }
+ if (!thisBattle.result?.win) {
+ return false;
+ }
+ const bestState = this.getState(bestBattle);
+ const thisState = this.getState(thisBattle);
+ if (!isFinite(thisState)) {
+ return false;
+ }
+ if (!isFinite(bestState)) {
+ return true;
+ }
+ return thisState > bestState;
+ }
+
+ async *bruteforce(endTime = Date.now() + BRUTEFORCE_MS) {
+ if (endTime < Date.now()) {
+ endTime = Date.now() + BRUTEFORCE_MS;
+ }
+ if (!this._initialBattle) {
+ this._initialBattle = await this.init();
+ }
+ while (Date.now() < endTime && this._counter < this._maxBattles) {
+ if (stopDung || end) {
+ break;
+ }
+ if (!(this._lastBattle = await this.reCalculate())) {
+ continue;
+ }
+ if (this._counter % SIM_YIELD_EVERY === 0) {
+ await sleep(0);
+ }
+ yield this._counter;
+ if (!this._bestBattle) {
+ this._bestBattle = this._lastBattle;
+ continue;
+ }
+ if (!this.isBetter(this._bestBattle, this._lastBattle)) {
+ continue;
+ }
+ this._bestBattle = this._lastBattle;
+ if (!this.success(this._bestBattle)) {
+ continue;
+ }
+ break;
+ }
+ return this._bestBattle;
+ }
+
+ async *calculateWinChance(times = 10) {
+ let wins = 0;
+ if (isNaN(times) || times <= 0) {
+ return;
+ }
+ const originalSeed = this._battle?.seed;
+ for (let i = 0; i < times; i++) {
+ if (stopDung || end) {
+ break;
+ }
+ if (this._battle) {
+ this._battle.seed = Math.floor(Date.now() / 1000) + Math.random() * 1000;
+ }
+ const battleBuffer = await simulateBattle(structuredClone(this._battle), this._type);
+ if (battleBuffer?.result?.win) {
+ wins++;
+ }
+ if ((i + 1) % SIM_YIELD_EVERY === 0) {
+ await sleep(0);
+ }
+ yield wins;
+ }
+ if (this._battle && originalSeed !== undefined) {
+ this._battle.seed = originalSeed;
+ }
+ }
+ }
+
+ class DungeonBattleHandler extends PvPBattleHandler {
+ getState(result) {
+ if (!result.result?.win) {
+ return -1000;
+ }
+ const beforeTitans = result.battleData?.attackers || {};
+ const afterTitans = result.progress?.[0]?.attackers?.heroes || {};
+ return this.getFactor(beforeTitans, afterTitans);
+ }
+
+ isBetter(bestBattle, thisBattle) {
+ if (!bestBattle || !thisBattle) {
+ return !!thisBattle;
+ }
+ const bestState = this.getState(bestBattle);
+ const thisState = this.getState(thisBattle);
+ if (!thisBattle.result?.win) {
+ return false;
+ }
+ if (!isFinite(thisState)) {
+ return false;
+ }
+ if (!isFinite(bestState)) {
+ return true;
+ }
+ return thisState > bestState;
+ }
+
+ success() {
+ return false;
+ }
+ }
+
+ async function runBattleHandler(battleHandler, forceFix = false, skipPreCalc = false, simOpts = {}) {
+ const bruteMs = simOpts.bruteforceMs ?? BRUTEFORCE_MS;
+ const maxTimerTries = simOpts.maxTimerTries ?? MAX_TIMER_TRIES;
+ const initBattle = await battleHandler.init(maxTimerTries);
+ if (!initBattle) {
+ return { initBattle: null, bestBattle: null, isWin: false, timer: 0 };
+ }
+ const isWin = battleHandler.isWin();
+ let wins = 0;
+
+ if (NUM_TRIES > 0 && !skipPreCalc) {
+ let count = 1;
+ for await (const liveWins of battleHandler.calculateWinChance(NUM_TRIES)) {
+ wins = liveWins;
+ if (DUNGEON_VERBOSE) {
+ setProgress(`${I18N('DUNGEON')}: sim ${liveWins}/${count} ${talentMsg}`, true);
+ }
+ count++;
+ }
+ }
+
+ if (!forceFix && isWin) {
+ return { initBattle, bestBattle: null, isWin, timer: initBattle.battleTime ?? 0 };
+ }
+
+ if (bruteMs <= 0) {
+ const resolved = battleHandler.bestBattle() ?? initBattle;
+ return {
+ initBattle,
+ bestBattle: null,
+ isWin: !!resolved?.result?.win,
+ timer: resolved?.battleTime ?? 0,
+ };
+ }
+
+ for await (const _count of battleHandler.bruteforce(Date.now() + bruteMs)) {
+ if (stopDung || end) {
+ break;
+ }
+ }
+
+ const bestBattle = battleHandler.bestBattle();
+ const resolved = bestBattle ?? initBattle;
+ return {
+ initBattle,
+ bestBattle: bestBattle !== initBattle ? bestBattle : null,
+ isWin: !!resolved?.result?.win,
+ timer: resolved?.battleTime ?? 0,
+ };
+ }
+
+ function getTitans(titans, states = {}) {
+ const all = titans
+ .filter((x) => !states[x.id]?.isDead && !states[String(x.id)]?.isDead)
+ .sort((x, y) => (y.power || 0) - (x.power || 0));
+ const water = [];
+ const fire = [];
+ const earth = [];
+ const dark = [];
+ const light = [];
+ const unknown = [];
+ for (const titan of all) {
+ const id = titan.id;
+ if (id < 4010) water.push(titan);
+ else if (id < 4020) fire.push(titan);
+ else if (id < 4030) earth.push(titan);
+ else if (id < 4040) dark.push(titan);
+ else if (id < 4050) light.push(titan);
+ else unknown.push(titan);
+ }
+ const byPower = (a, b) => (b.power || 0) - (a.power || 0);
+ return {
+ all,
+ water: water.sort(byPower),
+ earth: earth.sort(byPower),
+ fire: fire.sort(byPower),
+ dark: dark.sort(byPower),
+ light: light.sort(byPower),
+ elemental: [...dark, ...light, ...unknown].sort(byPower),
+ };
+ }
+
+ function getTitansForPotentialHealingTeam(aliveTitans, states = {}, index = 0) {
+ const normalize = (id) => Number(id);
+ if (aliveTitans.water.length < 3) {
+ return null;
+ }
+ const result = [];
+ const used = new Set();
+ const push = (id) => {
+ const n = normalize(id);
+ if (!used.has(n) && result.length < 5) {
+ used.add(n);
+ result.push(n);
+ }
+ };
+ const allStates = [];
+ for (const [titanId, state] of Object.entries(states)) {
+ const id = normalize(titanId);
+ if (id < 4010 || id >= 4030 || state.isDead) continue;
+ const diff = state.hp / state.maxHp;
+ if (diff === 1) continue;
+ allStates.push({ diff, id });
+ }
+ for (const titan of aliveTitans.water.slice(0, 4)) {
+ push(titan.id);
+ }
+ if (allStates.length === 0) {
+ return null;
+ }
+ const candidates = allStates.sort((a, b) => a.diff - b.diff);
+ if (candidates[index]) {
+ push(candidates[index].id);
+ } else {
+ return null;
+ }
+ if (result.length < 5) {
+ for (const titan of aliveTitans.elemental ?? []) {
+ if (result.length >= 5) break;
+ push(titan.id);
+ }
+ if (result.length < 5) {
+ const alive = [...aliveTitans.earth, ...aliveTitans.fire].sort((a, b) => (b.power || 0) - (a.power || 0));
+ for (const titan of alive) {
+ if (result.length >= 5) break;
+ push(titan.id);
+ }
+ }
+ }
+ return result.length === 5 ? result : null;
+ }
+
+ function getNeutralTitans(aliveTitans, strongest = false) {
+ const normalize = (id) => Number(id);
+ if (strongest) {
+ return aliveTitans.all.slice(0, 5).map((t) => normalize(t.id));
+ }
+ const result = [];
+ const used = new Set();
+ const waterPower = aliveTitans.water.reduce((sum, hero) => hero.power + sum, 0);
+ if (waterPower > 500000 && aliveTitans.water.length >= 4) {
+ for (const waterTitan of aliveTitans.water) {
+ if (result.length === 4) break;
+ result.push(waterTitan.id);
+ used.add(waterTitan.id);
+ }
+ }
+ const push = (id) => {
+ const n = normalize(id);
+ if (!used.has(n) && result.length < 5) {
+ used.add(n);
+ result.push(n);
+ }
+ };
+ const elementMap = {
+ water: { max: 4010, special: 4004 },
+ earth: { max: 4030, special: 4034 },
+ fire: { max: 4020, special: 4024 },
+ dark: { max: 4040 },
+ light: { max: 4050 },
+ };
+ for (const titan of aliveTitans.all) {
+ if (result.length >= 4) break;
+ const id = normalize(titan.id);
+ if (used.has(id)) continue;
+ if (id < elementMap.water.max) {
+ const group = aliveTitans.water.map((t) => normalize(t.id));
+ if (group.includes(elementMap.water.special)) {
+ push(elementMap.water.special);
+ const partner = group.find((x) => x !== elementMap.water.special);
+ if (partner) push(partner);
+ } else {
+ push(id);
+ for (const other of group.filter((x) => x !== id).slice(0, 2)) {
+ if (result.length < 5) push(other);
+ }
+ }
+ } else if (id < elementMap.earth.max) {
+ const group = aliveTitans.earth.map((t) => normalize(t.id));
+ if (group.includes(elementMap.earth.special)) {
+ push(elementMap.earth.special);
+ const partner = group.find((x) => x !== elementMap.earth.special);
+ if (partner) push(partner);
+ } else {
+ push(id);
+ for (const other of group.filter((x) => x !== id).slice(0, 2)) {
+ if (result.length < 5) push(other);
+ }
+ }
+ } else if (id < elementMap.fire.max) {
+ const group = aliveTitans.fire.map((t) => normalize(t.id));
+ if (group.includes(elementMap.fire.special)) {
+ push(elementMap.fire.special);
+ const partner = group.find((x) => x !== elementMap.fire.special);
+ if (partner) push(partner);
+ } else {
+ push(id);
+ for (const other of group.filter((x) => x !== id).slice(0, 2)) {
+ if (result.length < 5) push(other);
+ }
+ }
+ } else if (id < elementMap.dark.max) {
+ push(id);
+ const partner = aliveTitans.dark.map((t) => normalize(t.id)).find((x) => x !== id);
+ if (partner) push(partner);
+ } else if (id < elementMap.light.max) {
+ push(id);
+ const partner = aliveTitans.light.map((t) => normalize(t.id)).find((x) => x !== id);
+ if (partner) push(partner);
+ }
+ }
+ if (result.length < 5) {
+ for (const titan of aliveTitans.all) {
+ if (result.length >= 5) break;
+ push(titan.id);
+ }
+ }
+ return result;
+ }
+
+ function getDeads(option) {
+ const after = option.progress?.[0]?.attackers?.heroes || {};
+ return option.heroes.length + Number(!!option.pet) - Object.keys(after).length;
+ }
+
+ function debugString(option, attackerType) {
+ if (!option) return 'INVALID';
+ let s = `[${option.teamNum}]${attackerType}`;
+ s += option.result?.win ? ' OK' : ' FAIL';
+ const damage = option.heroes.length ? (option.state / option.heroes.length) * 100 : 0;
+ s += ` dmg:${damage.toFixed(0)}% dead:${getDeads(option)}`;
+ return s;
+ }
+
+ function isOptionBetter(bestOption, thisOption) {
+ if (!thisOption) return false;
+ if (!bestOption) return true;
+ const bestTeam = bestOption.heroes || [];
+ const thisTeam = thisOption.heroes || [];
+ if (thisTeam.length !== bestTeam.length) {
+ return thisTeam.length > bestTeam.length;
+ }
+ const bestState = bestOption.state ?? 0;
+ const thisState = thisOption.state ?? 0;
+ const bestNorm = bestTeam.length ? bestState * bestTeam.length : 0;
+ const thisNorm = thisTeam.length ? thisState * thisTeam.length : 0;
+ return thisNorm > bestNorm;
+ }
+
+ function isHealingSuccessful(healingTeam, progress, states) {
+ const afterHeroes = progress?.[0]?.attackers?.heroes;
+ if (!afterHeroes) return false;
+ return healingTeam.filter((id) => id >= 4010).every((id) => {
+ const after = afterHeroes[id];
+ return after && after.hp > (states[id]?.hp || states[String(id)]?.hp || 0);
+ });
+ }
+
+ function createBattleArgs(teamNum, heroes, pet, favor = {}) {
+ return {
+ name: 'dungeonStartBattle',
+ args: {
+ heroes,
+ favor: favor || {},
+ teamNum: Number(teamNum),
+ ...(pet ? { pet } : {}),
+ },
+ ident: 'body',
+ };
+ }
+
+ async function startAndSimulate(teamNum, heroes, pet, attackerType, favor = {}, bruteforceMs = EVAL_BRUTEFORCE_MS) {
+ const raw = await Send({ calls: [createBattleArgs(teamNum, heroes, pet, favor)] });
+ const apiResult = getApiResult(raw);
+ if (apiResult?.error || apiResult?.validation) {
+ const errMsg = apiResult.validation
+ ? JSON.stringify(apiResult.validation)
+ : (typeof apiResult.error === 'string'
+ ? apiResult.error
+ : `${apiResult.error?.name || apiResult.error?.title || 'Error'}: ${apiResult.error?.description || apiResult.error?.title || ''}`);
+ console.warn(`[Dungeon] dungeonStartBattle failed (${attackerType}, team ${teamNum}):`, errMsg, raw);
+ return null;
+ }
+ const battleData = apiResult?.response;
+ if (!battleData) {
+ console.warn(`[Dungeon] dungeonStartBattle empty response (${attackerType}, team ${teamNum})`, raw);
+ return null;
+ }
+
+ lastStartedTeamNum = teamNum;
+ battleStartTime = Date.now();
+ setBattleOpen(true);
+
+ try {
+ return await simulateOnBattleData(
+ battleData,
+ teamNum,
+ heroes,
+ pet,
+ attackerType,
+ favor,
+ {
+ bruteforceMs,
+ maxTimerTries: MAX_TIMER_TRIES,
+ }
+ );
+ } catch (err) {
+ console.warn(`[Dungeon] BattleCalc failed (${attackerType}, team ${teamNum}):`, err);
+ setBattleOpen(false);
+ return null;
+ }
+ }
+
+ function isNotFoundError(err) {
+ if (!err) {
+ return false;
+ }
+ if (typeof err === 'string') {
+ return /not\s*found|NotFound/i.test(err);
+ }
+ const name = String(err.name || err.title || '');
+ const desc = String(err.description || err.message || '');
+ return /not\s*found|NotFound/i.test(name) || /not\s*found|NotFound/i.test(desc);
+ }
+
+ function setBattleOpen(isOpen) {
+ setDungeonBattleOpen(isOpen);
+ }
+
+ function wrapBattleOption(teamNum, heroes, pet, favor, battle, battleData, handler, handlerResult) {
+ const wrapped = {
+ ...battle,
+ battleData: battle.battleData ?? battleData,
+ };
+ return {
+ teamNum,
+ heroes,
+ pet,
+ favor,
+ result: wrapped.result,
+ progress: wrapped.progress,
+ timer: getTimer(wrapped.battleTime ?? handlerResult.timer ?? 0),
+ battleTime: wrapped.battleTime,
+ simTimer: battle.timer ?? battle.battleTime ?? handlerResult.timer ?? 0,
+ win: wrapped.result?.win,
+ state: handler.getState(wrapped),
+ };
+ }
+
+ async function simulateOnBattleData(battleData, teamNum, heroes, pet, attackerType, favor, simOpts = {}) {
+ const isBruteForceBattle = attackerType !== 'hero';
+ const battleType = battleData.type === 'dungeon_titan' ? 'get_titan' : 'get_tower';
+ const handler = new DungeonBattleHandler(battleData, battleType);
+ lastBattleHandler = handler;
+ const maxTimerTries = simOpts.maxTimerTries ?? MAX_TIMER_TRIES;
+
+ const handlerResult = await runBattleHandler(
+ handler,
+ isBruteForceBattle,
+ isBruteForceBattle,
+ {
+ bruteforceMs: simOpts.bruteforceMs ?? BRUTEFORCE_MS,
+ maxTimerTries,
+ }
+ );
+ const battle = handlerResult.bestBattle ?? handlerResult.initBattle;
+ if (!battle?.result) {
+ setBattleOpen(false);
+ return null;
+ }
+ return wrapBattleOption(teamNum, heroes, pet, favor, battle, battleData, handler, handlerResult);
+ }
+
+ async function waitForBattleFullTimer(option, debug = '') {
+ const predictionCards = Math.max(0, Math.floor(Number(window.HWHData?.countPredictionCard)) || 0);
+ if (predictionCards > 0) {
+ return;
+ }
+ const totalTimer = Math.ceil(option.timer ?? getTimer(option.battleTime ?? 0));
+ if (totalTimer <= 0) {
+ return;
+ }
+ if (DUNGEON_VERBOSE) {
+ console.log('[Dungeon] battle wait:', totalTimer, 's', debug || '');
+ }
+ const msg = `${I18N('DUNGEON')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity}${debug ? ' | ' + debug : ''} ${talentMsg}`;
+ await countdownTimer(totalTimer, msg);
+ }
+
+ async function executeChosenOption(option, attackerType, doorCount, debug = '', skipRestart = false) {
+ if (stopDung || end) {
+ return false;
+ }
+ let finalOption = option;
+ if (!skipRestart && option.teamNum !== doorCount - 1) {
+ const restarted = await startAndSimulate(
+ option.teamNum,
+ option.heroes,
+ option.pet,
+ attackerType,
+ option.favor || {},
+ RESTART_BRUTEFORCE_MS
+ );
+ if (!restarted?.win) {
+ lastError = 'Restart failed';
+ endDungeon(lastError);
+ return false;
+ }
+ finalOption = restarted;
+ lastDebugString += debugString(restarted, attackerType) + ' [restart]';
+ if (DUNGEON_VERBOSE) console.log('[Dungeon]', lastDebugString);
+ }
+ await waitForBattleFullTimer(finalOption, debug);
+ if (stopDung || end) {
+ return false;
+ }
+ return endBattleOption(finalOption, attackerType);
+ }
+
+ async function endBattleOption(option, attackerType, isRetry = false) {
+ if (!option?.result?.win) {
+ endDungeon('Hero or Titan may have died in battle!', option);
+ return false;
+ }
+ const args = {
+ result: option.result,
+ progress: option.progress,
+ };
+ const predictionCards = Math.max(0, Math.floor(Number(window.HWHData?.countPredictionCard)) || 0);
+ if (predictionCards > 0) {
+ args.isRaid = true;
+ }
+
+ let e;
+ try {
+ e = await Send({ calls: [{ name: 'dungeonEndBattle', args, ident: 'body' }] });
+ } catch (err) {
+ setBattleOpen(false);
+ if (!isRetry && isNotFoundError(err)) {
+ return retryEndBattleAfterNotFound(option, attackerType);
+ }
+ endDungeon('errorRequest', err);
+ return false;
+ }
+
+ if (e?.error) {
+ if (isNotFoundError(e.error)) {
+ setBattleOpen(false);
+ if (!isRetry) {
+ return retryEndBattleAfterNotFound(option, attackerType);
+ }
+ console.warn('[Dungeon] Battle not found after retry, refreshing floor state...', e.error);
+ return true;
+ }
+ setBattleOpen(false);
+ endDungeon('errorRequest', e.error);
+ return false;
+ }
+
+ if (!e?.results) {
+ setBattleOpen(false);
+ endDungeon('Lost connection to game server!', 'break');
+ return false;
+ }
+
+ const result = e.results[0]?.result;
+ if (!result) {
+ setBattleOpen(false);
+ if (!isRetry && isNotFoundError(e)) {
+ return retryEndBattleAfterNotFound(option, attackerType);
+ }
+ endDungeon('errorRequest', 'empty dungeonEndBattle result');
+ return false;
+ }
+ if (result.error) {
+ if (isNotFoundError(result.error)) {
+ setBattleOpen(false);
+ if (!isRetry) {
+ return retryEndBattleAfterNotFound(option, attackerType);
+ }
+ console.warn('[Dungeon] Battle not found in result after retry, refreshing floor state...', result.error);
+ return true;
+ }
+ setBattleOpen(false);
+ endDungeon('errorBattleResult', result.error);
+ return false;
+ }
+
+ const battleResult = result.response;
+ if (!battleResult) {
+ setBattleOpen(false);
+ console.warn('[Dungeon] No battle result, continuing...');
+ return true;
+ }
+
+ if (battleResult.error) {
+ setBattleOpen(false);
+ if (isNotFoundError(battleResult.error)) {
+ if (!isRetry) {
+ return retryEndBattleAfterNotFound(option, attackerType);
+ }
+ return true;
+ }
+ endDungeon('errorBattleResult', battleResult);
+ return false;
+ }
+
+ setBattleOpen(false);
+
+ if (!battleResult.dungeon && !battleResult.floor) {
+ try {
+ await Send({ calls: [{ name: 'dungeonSaveProgress', args: {}, ident: 'body' }] });
+ } catch (_) { /* ignore */ }
+ }
+
+ dungeonActivity += battleResult.reward?.dungeonActivity ?? 0;
+ return true;
+ }
+
+ async function retryEndBattleAfterNotFound(option, attackerType) {
+ console.warn('[Dungeon] NotFound on end battle — restarting battle on server and retrying once');
+ const refreshed = await startAndSimulate(
+ option.teamNum,
+ option.heroes,
+ option.pet,
+ attackerType,
+ option.favor || {},
+ RESTART_BRUTEFORCE_MS
+ );
+ if (!refreshed?.win) {
+ lastError = 'NotFound recovery failed (could not restart battle)';
+ return false;
+ }
+ await waitForBattleFullTimer(refreshed);
+ return endBattleOption(refreshed, attackerType, true);
+ }
+
+ async function checkTalent(dungeonInfo) {
+ const talent = dungeonInfo.talent;
+ if (!talent) return;
+ const dungeonFloor = +dungeonInfo.floorNumber;
+ const talentFloor = +talent.floorRandValue;
+ let doorsAmount = 3 - talent.conditions.doorsAmount;
+ if (dungeonFloor === talentFloor && (!doorsAmount || !talent.conditions?.farmedDoors[dungeonFloor])) {
+ const rewardRes = await Send({
+ calls: [
+ { name: 'heroTalent_getReward', args: { talentType: 'tmntDungeonTalent', reroll: false }, ident: 'group_0_body' },
+ { name: 'heroTalent_farmReward', args: { talentType: 'tmntDungeonTalent' }, ident: 'group_1_body' },
+ ],
+ });
+ const reward = rewardRes.results[0].result.response;
+ const type = Object.keys(reward).pop();
+ const itemId = Object.keys(reward[type]).pop();
+ const count = reward[type][itemId];
+ const itemName = cheats.translate(`LIB_${type.toUpperCase()}_NAME_${itemId}`);
+ talentMsgReward += ` ${count} ${itemName}`;
+ doorsAmount++;
+ }
+ talentMsg = ` TMNT Talent: ${doorsAmount}/3 ${talentMsgReward} `;
+ }
+
+ async function fetchDungeonData(retries = 3) {
+ for (let attempt = 1; attempt <= retries; attempt++) {
+ const result = await Send({ calls: [{ name: 'dungeonGetInfo', args: {}, ident: 'dungeonGetInfo' }] });
+ if (Array.isArray(result.results)) {
+ const dungeonGetInfo = getResponse(result);
+ if (dungeonGetInfo?.floor?.userData) {
+ return { dungeonGetInfo };
+ }
+ lastError = 'No dungeon data';
+ } else {
+ lastError = 'Error fetching dungeonGetInfo';
+ }
+ if (attempt < retries) {
+ console.warn(`[Dungeon] fetchDungeonData attempt ${attempt}/${retries} failed, retrying...`);
+ await sleep(500);
+ }
+ }
+ return null;
+ }
+
+ async function handleRestart(dungeonGetInfo) {
+ if (!dungeonGetInfo.floor && !isRestart) {
+ isRestart = true;
+ await Send({ calls: [{ name: 'dungeonSaveProgress', args: {}, ident: 'body' }] });
+ return true;
+ }
+ if (isRestart) {
+ lastError = 'Error in dungeonGetInfo: missing floor';
+ return false;
+ }
+ isRestart = false;
+ return false;
+ }
+
+ async function runStep() {
+ const stepStart = Date.now();
+ if (STEP_DELAY_MS > 0) {
+ await sleep(STEP_DELAY_MS);
+ }
+ if (!isRestart) {
+ lastDebugString = '';
+ }
+
+ maxDungeonActivity = getInput('countTitanit') || maxDungeonActivity;
+ setProgress(`${I18N('DUNGEON')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity} ${talentMsg}`, true);
+
+ if (dungeonActivity >= maxDungeonActivity) {
+ endDungeon('Dungeon stopped,', 'titanite collected: ' + dungeonActivity + '/' + maxDungeonActivity);
+ return false;
+ }
+ if (stopDung) {
+ endDungeon('Dungeon stopped,', 'titanite collected: ' + dungeonActivity + '/' + maxDungeonActivity);
+ return false;
+ }
+
+ const data = await fetchDungeonData();
+ if (!data) {
+ return false;
+ }
+ if (titansList.length === 0) {
+ lastError = lastError || 'No titans loaded';
+ return false;
+ }
+
+ const { dungeonGetInfo } = data;
+
+ if (!('floor' in dungeonGetInfo) || dungeonGetInfo.floor?.state === 2) {
+ await Send({ calls: [{ name: 'dungeonSaveProgress', args: {}, ident: 'body' }] });
+ endDungeon('Dungeon completed,', 'floor saved');
+ return false;
+ }
+
+ const didRestart = await handleRestart(dungeonGetInfo);
+ if (didRestart) {
+ return true;
+ }
+ if (lastError) {
+ return false;
+ }
+
+ await checkTalent(dungeonGetInfo);
+
+ if (!dungeonGetInfo.elements) {
+ lastError = 'Error in dungeonGetInfo: missing primeElement';
+ endDungeon(lastError);
+ return false;
+ }
+ if (!dungeonGetInfo.states) {
+ lastError = 'Error in dungeonGetInfo: missing states';
+ endDungeon(lastError);
+ return false;
+ }
+
+ const states = dungeonGetInfo.states.titans;
+ const aliveTitans = getTitans(titansList, states);
+ const userData = dungeonGetInfo.floor.userData;
+
+ const heroBattleIndex = userData.findIndex((ud) => ud.attackerType === 'hero');
+ if (heroBattleIndex !== -1) {
+ const heroBattle = getHeroTeamForBattle(dungeonGetInfo.states?.heroes);
+ if (!heroBattle) {
+ lastError = 'No alive heroes for hero battle';
+ endDungeon(lastError);
+ return false;
+ }
+ const option = await startAndSimulate(
+ heroBattleIndex,
+ heroBattle.heroes,
+ heroBattle.pet,
+ 'hero',
+ heroBattle.favor
+ );
+ if (!option) {
+ lastError = 'Failed to start hero battle (check console for API/BattleCalc details)';
+ endDungeon(lastError);
+ return false;
+ }
+ if (!option.result?.win) {
+ lastError = 'Hero battle would lose';
+ endDungeon(lastError, option);
+ return false;
+ }
+ lastDebugString += debugString(option, 'hero');
+ if (DUNGEON_VERBOSE) console.log('[Dungeon]', lastDebugString);
+ await waitForBattleFullTimer(option, lastDebugString);
+ if (stopDung || end) {
+ return false;
+ }
+ const ok = await endBattleOption(option, 'hero');
+ timeDungeon.steps += Date.now() - stepStart;
+ return ok !== false;
+ }
+
+ const options = [];
+ for (let teamNum = 0; teamNum < userData.length; teamNum++) {
+ if (stopDung) break;
+ const { attackerType } = userData[teamNum];
+ let team = null;
+ let useHealingIndex = 0;
+
+ if (attackerType === 'neutral') {
+ if (isAbleToHeal) {
+ let healingTeam = null;
+ let healingOption = null;
+ while (true) {
+ healingTeam = getTitansForPotentialHealingTeam(aliveTitans, states, useHealingIndex);
+ if (!healingTeam) break;
+ healingOption = await startAndSimulate(teamNum, healingTeam, null, attackerType);
+ if (!healingOption?.win) {
+ useHealingIndex++;
+ continue;
+ }
+ if (isHealingSuccessful(healingTeam, healingOption.progress, states) && getDeads(healingOption) === 0) {
+ team = { heroes: healingTeam, pet: null, isHealing: true, option: healingOption };
+ break;
+ }
+ useHealingIndex++;
+ }
+ }
+ if (!team) {
+ const neutralTeam = getNeutralTitans(aliveTitans, !isAbleToHeal);
+ team = neutralTeam.length > 0 ? { heroes: neutralTeam, pet: null } : null;
+ }
+ } else {
+ const pool = aliveTitans[attackerType] ? aliveTitans[attackerType] : aliveTitans.all;
+ const heroes = pool.slice(0, 5).map((t) => t.id);
+ team = heroes.length > 0 ? { heroes, pet: null } : null;
+ }
+
+ if (!team) {
+ options.push(null);
+ continue;
+ }
+
+ const option = team.option || (await startAndSimulate(teamNum, team.heroes, team.pet, attackerType, team.favor || {}));
+ if (!option?.win) {
+ options.push(null);
+ continue;
+ }
+
+ if (team.isHealing && option.win) {
+ lastDebugString += debugString(option, attackerType) + ' [heal]';
+ if (DUNGEON_VERBOSE) console.log('[Dungeon]', lastDebugString);
+ const ok = await executeChosenOption(option, attackerType, userData.length, lastDebugString, true);
+ timeDungeon.steps += Date.now() - stepStart;
+ return ok !== false;
+ }
+ if (team.isHealing) {
+ const fallback = getNeutralTitans(aliveTitans);
+ if (fallback.length > 0) {
+ const fallbackOption = await startAndSimulate(teamNum, fallback, null, attackerType);
+ options.push(fallbackOption?.win ? { option: fallbackOption, attackerType } : null);
+ } else {
+ options.push(null);
+ }
+ continue;
+ }
+ options.push({ option, attackerType });
+ }
+
+ const valid = options.filter(Boolean);
+ if (valid.length === 0) {
+ lastError = 'No winnable battles available';
+ endDungeon(lastError);
+ return false;
+ }
+
+ if (valid.length > 1) {
+ lastDebugString += valid.map((v) => debugString(v.option, v.attackerType)).join(' | ');
+ } else {
+ lastDebugString += debugString(valid[0].option, valid[0].attackerType);
+ }
+
+ const best = valid.length === 1
+ ? valid[0]
+ : valid.reduce((b, cur) => (isOptionBetter(b?.option, cur?.option) ? cur : b));
+
+ if (!best?.option) {
+ lastError = 'No best battle found';
+ endDungeon(lastError);
+ return false;
+ }
+
+ lastDebugString += ` -> ${best.option.teamNum} `;
+
+ if (DUNGEON_VERBOSE) console.log('[Dungeon]', lastDebugString);
+ const ok = await executeChosenOption(best.option, best.attackerType, userData.length, lastDebugString);
+ stepCount++;
+ timeDungeon.steps += Date.now() - stepStart;
+ return ok !== false;
+ }
+
+ function showStats() {
+ if (!DUNGEON_VERBOSE) return;
+ const activity = dungeonActivity - startDungeonActivity;
+ const totalSec = Math.round((Date.now() - timeDungeon.all) / 1000);
+ console.log('[Dungeon] Titanite collected:', activity);
+ console.log('[Dungeon] Steps:', stepCount);
+ if (totalSec > 0) {
+ console.log('[Dungeon] Speed:', Math.round((3600 * activity) / totalSec), 'titanite/hour');
+ }
+ console.log('[Dungeon] Sim time (ms):', timeDungeon.steps);
+ }
+
+ function endDungeon(reason, info) {
+ if (end) return;
+ end = true;
+ dungeonRunning = false;
+ window.HWH_DUNGEON_RUNNING = false;
+ setBattleOpen(false);
+ console.log('[Dungeon]', reason, info != null && info !== '' ? info : '');
+ showStats();
+ if (info === 'break') {
+ setProgress(
+ 'Dungeon stopped: Titanite ' + dungeonActivity + '/' + maxDungeonActivity + '\r\nLost connection to game server!',
+ false,
+ hideProgress
+ );
+ } else {
+ setProgress('Dungeon completed: Titanite ' + dungeonActivity + '/' + maxDungeonActivity, false, hideProgress);
+ }
+ const reasonText = String(reason || '');
+ const shouldRefresh = reasonText.includes('titanite collected')
+ || reasonText.includes('floor saved')
+ || reasonText.includes('Dungeon completed');
+ if (shouldRefresh) {
+ if (titanHealthSettings.autoRefreshPage) {
+ setTimeout(() => location.reload(), 1000);
+ } else {
+ setTimeout(cheats.refreshGame, 1000);
+ }
+ }
+ resolve();
+ }
+
+ async function initialize() {
+ const res = await Send({
+ calls: [
+ { name: 'dungeonGetInfo', args: {}, ident: 'dungeonGetInfo' },
+ { name: 'teamGetAll', args: {}, ident: 'teamGetAll' },
+ { name: 'teamGetFavor', args: {}, ident: 'teamGetFavor' },
+ { name: 'clanGetInfo', args: {}, ident: 'clanGetInfo' },
+ { name: 'titanGetAll', args: {}, ident: 'titanGetAll' },
+ { name: 'inventoryGet', args: {}, ident: 'inventoryGet' },
+ ],
+ });
+
+ const dungeonGetInfo = res.results[0]?.result?.response;
+ if (!dungeonGetInfo) {
+ lastError = 'noDungeon';
+ return false;
+ }
+
+ teamGetAll = res.results[1]?.result?.response;
+ teamGetFavor = res.results[2]?.result?.response;
+ const clanStat = res.results[3]?.result?.response?.stat;
+ const dungeonStat = dungeonGetInfo?.stat;
+ const todayAct = clanStat?.todayDungeonActivity ?? dungeonStat?.todayDungeonActivity ?? 0;
+ dungeonActivity = todayAct;
+ startDungeonActivity = todayAct;
+ syncPredictionCardsFromInventory(res.results[5]);
+
+ const titanRaw = res.results[4]?.result?.response;
+ titansList = Array.isArray(titanRaw)
+ ? titanRaw
+ : Object.values(titanRaw || {}).filter((t) => t && t.id != null);
+
+ const layout = getTitans(titansList);
+ const waterPower = layout.water.reduce((a, b) => a + (b.power || 0), 0);
+ const earthPower = layout.earth.reduce((a, b) => a + (b.power || 0), 0);
+ const firePower = layout.fire.reduce((a, b) => a + (b.power || 0), 0);
+ const waterStrongest = waterPower >= earthPower && waterPower >= firePower;
+ const waterWithin25Percent = earthPower <= waterPower * 1.25 && firePower <= waterPower * 1.25;
+ isAbleToHeal = waterStrongest || waterWithin25Percent;
+
+ if (DUNGEON_VERBOSE) {
+ console.log('[Dungeon] Water', waterPower, '| Earth', earthPower, '| Fire', firePower, '| canHeal:', isAbleToHeal);
+ console.log('[Dungeon] Starting full dungeon run:', new Date());
+ }
+ return true;
+ }
+
+ this.start = async function (titanit) {
+ await waitForAutoBattleIdle();
+ if (window.HWH_AUTOBATTLE_RUNNING) {
+ lastError = 'AutoBattle still running — dungeon aborted to avoid API conflict';
+ endDungeon(lastError);
+ return;
+ }
+
+ maxDungeonActivity = titanit || getInput('countTitanit');
+ stopDung = false;
+ end = false;
+ isRestart = false;
+ lastError = null;
+ lastStartedTeamNum = -1;
+ battleStartTime = 0;
+ stepCount = 0;
+ dungeonRunning = true;
+ window.HWH_DUNGEON_RUNNING = true;
+ timeDungeon = { all: Date.now(), steps: 0 };
+
+ try {
+ const ok = await initialize();
+ if (!ok) {
+ endDungeon('Failed to initialize dungeon', lastError);
+ return;
+ }
+ while (!end && !stopDung) {
+ const success = await runStep();
+ if (!success) {
+ if (lastError && !end) {
+ endDungeon(lastError);
+ }
+ break;
+ }
+ }
+ if (!end && stopDung) {
+ endDungeon('Dungeon stopped,', 'titanite collected: ' + dungeonActivity + '/' + maxDungeonActivity);
+ } else if (!end && !lastError) {
+ console.warn('[Dungeon] Run loop ended without error (possible silent stop)');
+ }
+ } catch (err) {
+ console.error('[Dungeon] Fatal error:', err);
+ endDungeon('Fatal dungeon error', err);
+ reject(err);
+ } finally {
+ if (dungeonRunning) {
+ dungeonRunning = false;
+ window.HWH_DUNGEON_RUNNING = false;
+ }
+ }
+ };
+ }
+
+ async function executeTestDungeon() {
+ const { HWHClasses, HWHFuncs } = window;
+
+ await waitForAutoBattleIdle();
+ if (window.HWH_AUTOBATTLE_RUNNING) {
+ console.warn('[Dungeon] AutoBattle still running — aborting dungeon start');
+ HWHFuncs.setProgress('Dungeon: AutoBattle still running', true);
+ return;
+ }
+
+ if (dungeonRunning || window.HWH_DUNGEON_RUNNING || window.HWH_DUNGEON_BATTLE_OPEN) {
+ console.warn('[Dungeon] Already running — ignoring duplicate start');
+ HWHFuncs.setProgress('Dungeon: already running', true);
+ return;
+ }
+ dungeonRunning = true;
+ window.HWH_DUNGEON_RUNNING = true;
+
+ if (window.HWHClasses && typeof executeDungeon === 'function') {
+ window.HWHClasses.executeDungeon = executeDungeon;
+ }
+
+ try {
+ const hasStealtherDungeon = await waitFor(() => typeof executeDungeon === 'function', { timeoutMs: 15000, intervalMs: 200 });
+ if (hasStealtherDungeon) {
+ HWHFuncs.setProgress('Executing: Dungeon (Stealther)', true);
+ return await withTimeout(
+ new Promise((resolve, reject) => {
+ try {
+ const dung = new executeDungeon(resolve, reject);
+ dung.start();
+ } catch (e) {
+ reject(e);
+ }
+ }),
+ 20 * 60 * 1000,
+ 'Dungeon timed out'
+ );
+ }
+
+ const hasNativeDungeon = await waitFor(() => typeof window.testDungeon === 'function', { timeoutMs: 5000, intervalMs: 200 });
+ if (hasNativeDungeon) {
+ HWHFuncs.setProgress('Executing: Dungeon (native fallback)', true);
+ return await withTimeout(window.testDungeon(), 20 * 60 * 1000, 'Dungeon timed out');
+ }
+
+ throw new Error('Dungeon API not ready (missing executeDungeon/testDungeon)');
+ } catch (err) {
+ dungeonRunning = false;
+ window.HWH_DUNGEON_RUNNING = false;
+ setDungeonBattleOpen(false);
+ throw err;
+ }
+ }
+
+ // --- DUNGEON SETTINGS GUI ---
+ function createDungeonSettingsGUI() {
+ if (document.getElementById('titanSettingsGUI')) return; // Already created
+
+ const style = document.createElement('style');
+ style.textContent = `
+ #titanSettingsGUI {
+ position: fixed;
+ top: 50px;
+ right: 10px;
+ width: 280px;
+ background-color: rgba(0, 0, 0, 0.85);
+ border: 1px solid #444;
+ border-radius: 10px;
+ padding: 15px 20px;
+ color: #E0E0E0;
+ font-family: 'Segoe UI', Arial, sans-serif;
+ font-size: 14px;
+ z-index: 10000;
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4);
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ transition: all 0.3s ease-in-out;
+ max-height: calc(100vh - 70px);
+ overflow-y: auto;
+ }
+ #titanSettingsGUI h3 {
+ margin-top: 0;
+ color: #FFD700;
+ text-align: center;
+ font-size: 18px;
+ border-bottom: 1px solid #555;
+ padding-bottom: 8px;
+ margin-bottom: 15px;
+ }
+ #titanSettingsGUI h4 {
+ margin-top: 5px;
+ margin-bottom: 8px;
+ color: #87CEEB;
+ font-size: 15px;
+ text-align: center;
+ }
+ #titanSettingsGUI label {
+ display: block;
+ margin-bottom: 4px;
+ color: #ADD8E6;
+ font-weight: bold;
+ }
+ #titanSettingsGUI input[type="number"] {
+ width: calc(100% - 22px);
+ padding: 9px 10px;
+ margin-bottom: 10px;
+ border: 1px solid #666;
+ border-radius: 5px;
+ background-color: #2a2a2a;
+ color: white;
+ box-sizing: border-box;
+ font-size: 14px;
+ -moz-appearance: textfield;
+ }
+ #titanSettingsGUI input[type="number"]::-webkit-outer-spin-button,
+ #titanSettingsGUI input[type="number"]::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+ }
+ #titanSettingsGUI button {
+ background-color: #32CD32;
+ color: white;
+ padding: 10px 15px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 16px;
+ font-weight: bold;
+ transition: background-color 0.3s ease, transform 0.1s ease;
+ margin-top: 10px;
+ }
+ #titanSettingsGUI button:hover {
+ background-color: #228B22;
+ transform: translateY(-1px);
+ }
+ #resetTitanSettings {
+ background-color: #FF6347;
+ width: fit-content;
+ margin: 10px auto;
+ display: block;
+ padding: 8px 12px;
+ font-size: 14px;
+ border-radius: 5px;
+ }
+ `;
+ document.head.appendChild(style);
+
+ const resetButton = document.createElement('button');
+ resetButton.id = 'resetTitanSettings';
+ resetButton.textContent = 'Reset to Defaults';
+ document.body.appendChild(resetButton);
+
+ const gui = document.createElement('div');
+ gui.id = 'titanSettingsGUI';
+ gui.innerHTML = `
+ Dungeon Cutoff Settings 1.0.7
+
+
+ Refresh(F5) after dungeon
+
+
+ General Thresholds (%) (>=30):
+
+
+ Titan 4020 - Agnus
+
+ Minimum HP (%) (>=25):
+
+
+
+ Minimum HP with Max Energy (%) (>=5):
+
+
+ Titan 4010 - Moloch
+
+ HP + Energy combined (%) (>=63):
+
+
+ Titan 4000 - Sigurd
+
+ Minimum HP (%) (>=62):
+
+
+
+ Minimum HP With Energy >= 400 (%) (>=45):
+
+
+
+ Minimum HP With Energy >= 670 (%) (>=30):
+
+
+ Save & Apply
+ `;
+ document.body.appendChild(gui);
+
+ gui.style.display = 'none';
+ resetButton.style.display = 'none';
+
+ function updateGUIFields() {
+ document.getElementById('minOverallHP').value = titanHealthSettings.minOverallHP * 100;
+ document.getElementById('titan4020HP').value = titanHealthSettings.titan4020HP * 100;
+ document.getElementById('titan4020EnergyHP').value = titanHealthSettings.titan4020EnergyHP * 100;
+ document.getElementById('titan4010Combined').value = titanHealthSettings.titan4010Combined * 100;
+ document.getElementById('titan4000HP').value = titanHealthSettings.titan4000HP * 100;
+ document.getElementById('titan4000Energy400HP').value = titanHealthSettings.titan4000Energy400HP * 100;
+ document.getElementById('titan4000Energy670HP').value = titanHealthSettings.titan4000Energy670HP * 100;
+ document.getElementById('autoRefreshPage').checked = titanHealthSettings.autoRefreshPage;
+ }
+
+ updateGUIFields();
+
+ document.getElementById('saveTitanSettings').addEventListener('click', () => {
+ titanHealthSettings.minOverallHP = parseFloat(document.getElementById('minOverallHP').value) / 100;
+ titanHealthSettings.titan4020HP = parseFloat(document.getElementById('titan4020HP').value) / 100;
+ titanHealthSettings.titan4020EnergyHP = parseFloat(document.getElementById('titan4020EnergyHP').value) / 100;
+ titanHealthSettings.titan4010Combined = parseFloat(document.getElementById('titan4010Combined').value) / 100;
+ titanHealthSettings.titan4000HP = parseFloat(document.getElementById('titan4000HP').value) / 100;
+ titanHealthSettings.titan4000Energy400HP = parseFloat(document.getElementById('titan4000Energy400HP').value) / 100;
+ titanHealthSettings.titan4000Energy670HP = parseFloat(document.getElementById('titan4000Energy670HP').value) / 100;
+ titanHealthSettings.autoRefreshPage = document.getElementById('autoRefreshPage').checked;
+ saveTitanHealthSettings();
+ const { HWHFuncs } = window;
+ if (HWHFuncs) HWHFuncs.setProgress('Dungeon settings saved!', true);
+ });
+
+ resetButton.addEventListener('click', () => {
+ if (confirm('Are you sure you want to reset to default values?')) {
+ titanHealthSettings = Object.assign({}, defaultTitanHealthSettings);
+ saveTitanHealthSettings();
+ updateGUIFields();
+ const { HWHFuncs } = window;
+ if (HWHFuncs) HWHFuncs.setProgress('Settings reset to defaults!', true);
+ }
+ });
+
+ const inputs = gui.querySelectorAll('input[type="number"], input[type="checkbox"]');
+ inputs.forEach(input => {
+ input.addEventListener('change', () => {
+ const id = input.id;
+ if (titanHealthSettings.hasOwnProperty(id)) {
+ if (input.type === 'checkbox') {
+ titanHealthSettings[id] = input.checked;
+ } else {
+ titanHealthSettings[id] = parseFloat(input.value) / 100;
+ }
+ }
+ saveTitanHealthSettings();
+ });
+ });
+
+ // Toggle GUI visibility (can be triggered from dungeon indicator if needed)
+ window.toggleDungeonSettingsGUI = () => {
+ if (gui.style.display === 'none') {
+ gui.style.display = 'flex';
+ resetButton.style.display = 'block';
+ } else {
+ gui.style.display = 'none';
+ resetButton.style.display = 'none';
+ }
+ };
+ }
+
+ async function executeOfferFarmAllReward() {
+ const { Send, HWHFuncs } = window;
+ HWHFuncs.setProgress('Executing: Easter Eggs', true);
+ try {
+ const data = await Send({ calls: [{ name: "offerGetAll", args: {}, ident: "offerGetAll" }] });
+ const offers = data.results[0].result.response.filter(e => e.type == "reward" && !e.freeRewardObtained && e.reward);
+ if (offers.length === 0) return;
+ const calls = offers.map(reward => ({ name: "offerFarmReward", args: { offerId: reward.id }, ident: `offerFarmReward_${reward.id}` }));
+ await Send({ calls });
+ HWHFuncs.setProgress('Easter Eggs: Done!', true);
+ } catch (e) { console.error("Error in executeOfferFarmAllReward", e); HWHFuncs.setProgress('Easter Eggs: Error!', true); }
+ }
+ async function executeQuestAllFarm() {
+ const { Send } = window;
+ // Get current quest state from cache - following API documentation pattern
+ const quests = await getQuestData();
+
+ // Filter quests that are completed and ready to collect (state === 2)
+ // Only process regular daily quests (id < 1000000)
+ // According to API docs: state 0 = not started, 1 = in progress, 2 = completed (ready to collect)
+ // After collection, quest should be removed or state should change
+ const questsToFarm = quests.filter(q => {
+ // Only collect if quest exists, is a regular daily quest, and is completed (state === 2)
+ return q && typeof q.id !== 'undefined' && q.id < 1000000 && q.state === 2;
+ });
+
+ if (questsToFarm.length === 0) {
+ // No quests ready to collect - all done
+ return;
+ }
+
+ // Collect the quest rewards
+ const questCalls = questsToFarm.map(q => ({
+ name: "questFarm",
+ args: { questId: q.id },
+ ident: `questFarm_${q.id}`
+ }));
+
+ await Send({ calls: questCalls });
+ // Invalidate cache after collecting quest rewards to get fresh data
+ invalidateQuestCache();
+ }
+ async function executeMailGetAll() {
+ const { Send, HWHClasses } = window;
+ const mailData = await Send({ calls: [{ name: "mailGetAll", args: {}, ident: "body" }] });
+ const letters = mailData.results[0].result.response.letters;
+ const letterIds = HWHClasses.Letters.filter(letters);
+ if (letterIds.length > 0) await Send({ calls: [{ name: "mailFarm", args: { letterIds }, ident: "body" }] });
+ }
+ async function executeRewardsAndMailFarm() {
+ const { HWHFuncs } = window;
+ HWHFuncs.setProgress('Executing: Rewards & Mail', true);
+ try {
+ await executeQuestAllFarm();
+ await executeMailGetAll();
+ HWHFuncs.setProgress('Rewards & Mail: Done!', true);
+ } catch (e) { console.error("Error in executeRewardsAndMailFarm", e); HWHFuncs.setProgress('Rewards & Mail: Error!', true); }
+ }
+ async function executeRollAscension() {
+ const { Send, HWHFuncs } = window;
+ HWHFuncs.setProgress('Executing: Seer', true);
+ try {
+ const data = await Send({ calls: [{ name: "userGetInfo", args: {}, ident: "userGetInfo" }] });
+ const refillable = data.results[0].result.response.refillable;
+ const seerCharges = refillable.find(i => i.id == 47);
+ if (seerCharges && seerCharges.amount > 0) await Send({ calls: [{ name: "ascensionChest_open", args: { paid: false, amount: 1 }, ident: "body" }] });
+ HWHFuncs.setProgress('Seer: Done!', true);
+ } catch (e) { console.error("Error in executeRollAscension", e); HWHFuncs.setProgress('Seer: Error!', true); }
+ }
+ // NEWLY ADDED FUNCTION - Reuses doYourBest functions from HeroWarsHelper
+async function executeGetDailyBonus() {
+ const { HWHClasses, HWHFuncs } = window;
+ HWHFuncs.setProgress('Executing: Daily Bonus', true);
+ try {
+ // Reuse getDailyBonus from doYourBest class if available
+ if (HWHClasses && HWHClasses.doYourBest) {
+ const doYourBestInstance = new HWHClasses.doYourBest(() => {}, () => {});
+ if (doYourBestInstance.functions && doYourBestInstance.functions.getDailyBonus) {
+ await doYourBestInstance.functions.getDailyBonus();
+
+ // Also collect subscription and zeppelin gifts using collectAllStuff pattern
+ if (doYourBestInstance.functions.collectAllStuff) {
+ // collectAllStuff includes: offerFarmAllReward, subscriptionFarm, zeppelinGiftFarm, grandFarmCoins, gacha_refill
+ // But we only want subscriptionFarm and zeppelinGiftFarm here
+ const { Send } = window;
+ await Send({
+ calls: [
+ { name: "subscriptionFarm", args: {}, context: { actionTs: Math.floor(performance.now()) }, ident: "body" },
+ { name: "zeppelinGiftFarm", args: {}, context: { actionTs: Math.floor(performance.now()) }, ident: "zeppelinGiftFarm" }
+ ]
+ });
+ }
+
+ HWHFuncs.setProgress('Daily Bonus: Done!', true);
+ return;
+ }
+ }
+
+ // Fallback: implement our own if doYourBest is not available
+ const { Send, lib } = window;
+ const response = await Send({
+ calls: [
+ { name: "dailyBonusGetInfo", args: {}, ident: "dailyBonus" },
+ { name: "userGetInfo", args: {}, ident: "userInfo" }
+ ]
+ });
+
+ const dailyBonusInfo = response.results.find(r => r.ident === 'dailyBonus').result.response;
+ const userInfo = response.results.find(r => r.ident === 'userInfo').result.response;
+
+ if (!dailyBonusInfo.availableToday) {
+ HWHFuncs.setProgress('Daily Bonus already collected', true);
+ return;
+ }
+
+ const vipInfo = lib.getData('level').vip;
+ let currentVipLevel = 0;
+ for (let i in vipInfo) {
+ if (+userInfo.vipPoints >= vipInfo[i].vipPoints) {
+ currentVipLevel = vipInfo[i].level;
+ }
+ }
+ const dailyBonusStat = lib.getData('dailyBonusStatic');
+ const vipLevelDouble = dailyBonusStat[`${dailyBonusInfo.currentDay}_0_0`].vipLevelDouble;
+ const collectVipBonus = dailyBonusInfo.availableVip && currentVipLevel >= vipLevelDouble;
+
+ await Send({
+ calls: [
+ { name: "dailyBonusFarm", args: { vip: collectVipBonus ? 1 : 0 }, context: { actionTs: Math.floor(performance.now()) }, ident: "body" },
+ { name: "subscriptionFarm", args: {}, context: { actionTs: Math.floor(performance.now()) }, ident: "body" },
+ { name: "zeppelinGiftFarm", args: {}, context: { actionTs: Math.floor(performance.now()) }, ident: "zeppelinGiftFarm" }
+ ]
+ });
+
+ HWHFuncs.setProgress('Daily Bonus: Done!', true);
+ } catch (e) {
+ console.error("Error in executeGetDailyBonus", e);
+ HWHFuncs.setProgress('Daily Bonus: Error!', true);
+ }
+}
+ async function executeGachaRefill() {
+ const { Send, HWHFuncs } = window;
+ HWHFuncs.setProgress('Executing: Gacha Refill', true);
+ try {
+ await Send({
+ calls: [{
+ name: "gacha_refill",
+ args: {
+ ident: "heroGacha"
+ },
+ ident: "body"
+ }]
+ });
+ HWHFuncs.setProgress('Gacha Refill: Done!', true);
+ } catch (e) {
+ console.error("Error in executeGachaRefill", e);
+ HWHFuncs.setProgress('Gacha Refill: Error!', true);
+ }
+ }
+
+ // --- DATA STRUCTURES ---
+ const doAllTasks = [
+ { id: 'getOutland', label: 'Outland', func: executeGetOutland }, { id: 'testTower', label: 'Tower', func: executeTestTower },
+ { id: 'testDungeon', label: 'Dungeon', func: executeTestDungeon }, { id: 'checkExpedition', label: 'Expeditions', func: executeCheckExpedition },
+ { id: 'offerFarmAllReward', label: 'Easter Eggs', func: executeOfferFarmAllReward },
+ { id: 'questAllFarm', label: 'Rewards', func: executeQuestAllFarm }, { id: 'mailGetAll', label: 'Mail', func: executeMailGetAll },
+ { id: 'rewardsAndMailFarm', label: 'Rewards & Mail', func: executeRewardsAndMailFarm }, { id: 'rollAscension', label: 'Seer', func: executeRollAscension },
+ { id: 'getDailyBonus', label: 'Daily Bonus', func: executeGetDailyBonus },
+ { id: 'gachaRefill', label: 'Gacha Refill', func: executeGachaRefill }
+ ];
+ const upgradeTasks = [
+ { id: '10001', label: 'Upgrade Skills' }, { id: '10018', label: 'Use EXP Potion' },
+ { id: '10023', label: 'Gift of Elements (x2)' }, { id: '10024', label: 'Upgrade Artifact' },
+ { id: '10028', label: 'Upgrade Titan Artifact' }, { id: '10030', label: 'Upgrade Skin' },
+ ];
+ const questTasks = [
+ { id: '10003', label: 'Heroic Missions' }, { id: '10006', label: 'Exchange Emeralds' },
+ { id: '10007', label: 'Soul Atrium' }, { id: '10016', label: 'Send Gifts' },
+ { id: '10020', label: 'Outland Chests' }, { id: '10022', label: 'Guild Dungeon' },
+ { id: '10029', label: 'Titan Artifact Orbs' }, { id: '10044', label: 'Summon Pets' },
+ { id: '10047', label: 'Guild Activity' }
+ ];
+ const othersTasks = [
+ { id: 'GET_ENERGY', label: 'Get Energy' }, { id: 'ITEM_EXCHANGE', label: 'Item Exchange' },
+ { id: 'BUY_SOULS', label: 'Buy Souls' }, { id: 'BUY_FOR_GOLD', label: 'Buy for Gold' },
+ { id: 'BUY_OUTLAND', label: 'Buy Outland' }, { id: 'CLAN_STAT', label: 'Clan Statistics' },
+ { id: 'EPIC_BRAWL', label: 'Cosmic Battle' }, { id: 'ARTIFACTS_UPGRADE', label: 'Artifacts Upgrade' },
+ { id: 'SKINS_UPGRADE', label: 'Skins Upgrade' }, { id: 'SEASON_REWARD', label: 'Season Rewards' },
+ { id: 'SELL_HERO_SOULS', label: 'Sell Souls' }
+ ];
+
+ // --- STATE MANAGEMENT ---
+ function loadAllSettings() {
+ const { HWHFuncs } = window;
+ if (typeof window.getAutoDailySettings === 'function') {
+ console.log(`${EXTENSION_NAME}: Settings Provider found. Loading settings from Provider.`);
+ isProviderActive = true;
+ const providerSettings = window.getAutoDailySettings();
+ executionState = providerSettings.executionState || {};
+ hideButtonsState = providerSettings.hideButtonsState || {};
+ othersSettingsState = providerSettings.othersSettingsState || {};
+ } else {
+ console.log(`${EXTENSION_NAME}: Settings Provider not found. Loading account-specific settings.`);
+ isProviderActive = false;
+ executionState = HWHFuncs.getSaveVal('autoDaily_executionState', {});
+ hideButtonsState = HWHFuncs.getSaveVal('autoDaily_hideButtonsState', { doAll: false, quests: false, actions: false, newSync: false });
+ othersSettingsState = HWHFuncs.getSaveVal('autoDaily_othersSettingsState', othersTasks.reduce((acc, task) => { acc[task.id] = true; return acc; }, {}));
+ }
+ }
+
+ function saveAllSettings() {
+ const { HWHFuncs } = window;
+ // We only save if the provider is NOT active. The provider is the source of truth when present.
+ if (!isProviderActive) {
+ HWHFuncs.setSaveVal('autoDaily_executionState', executionState);
+ HWHFuncs.setSaveVal('autoDaily_hideButtonsState', hideButtonsState);
+ HWHFuncs.setSaveVal('autoDaily_othersSettingsState', othersSettingsState);
+ }
+ }
+
+ // --- UI & CORE LOGIC ---
+ function waitForHWH(callback) {
+ const interval = setInterval(() => {
+ if (window.HWHData && window.HWHClasses && window.HWHFuncs && window.HWHData.buttons.doActions && window.HWHData.buttons.doActions.button) {
+ clearInterval(interval);
+ callback();
+ }
+ }, 500);
+ }
+
+ function createPopup() {
+ if (document.getElementById('auto-daily-popup-container')) return;
+ const styles = `
+ .auto-daily-popup-backdrop { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 10001; }
+ .auto-daily-popup-main { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #190e08e6; border: 3px #ce9767 solid; border-radius: 10px; z-index: 10002; color: #fce1ac; padding: 20px; min-width: 900px; max-height: 80vh; overflow-y: auto; display: flex; flex-direction: column; gap: 20px; }
+ .auto-daily-columns-container { display: flex; gap: 20px; flex-grow: 1; }
+ .auto-daily-popup-main h2 { text-align: center; margin-top: 0; border-bottom: 1px solid #ce9767; padding-bottom: 10px; }
+ .auto-daily-popup-column { flex: 1; }
+ .auto-daily-task-list { list-style: none; padding: 0; margin: 0; }
+ .auto-daily-task-item { display: flex; align-items: center; justify-content: space-between; padding: 8px 5px; border-bottom: 1px solid #4a3422; }
+ .auto-daily-task-item:last-child { border-bottom: none; }
+ .auto-daily-task-item label { display: flex; align-items: center; gap: 10px; cursor: pointer; flex-grow: 1; color: #fce1ac; }
+ .auto-daily-fire-btn { cursor: pointer; font-size: 20px; background: none; border: none; padding: 0 5px; transition: transform 0.2s; color: #fce1ac;}
+ .auto-daily-fire-btn:hover { transform: scale(1.2); }
+ .auto-daily-status-icon { font-size: 20px; min-width: 28px; text-align: center; }
+ .auto-daily-close-btn { position: absolute; top: 5px; right: 10px; font-size: 24px; color: #ce9767; cursor: pointer; border: none; background: none;}
+ .auto-daily-footer { border-top: 1px solid #ce9767; margin-top: 15px; padding-top: 15px; display: flex; flex-wrap: wrap; gap: 15px 20px; font-size: 14px; align-items: center; }
+ .auto-daily-footer a, .auto-daily-footer .sync-button { color: #fce1ac; text-decoration: none; cursor: pointer; background: none; border: none; font-size: 20px; padding: 0; margin-right: 10px; }
+ .auto-daily-footer a:hover { text-decoration: underline; }
+ .sync-settings-popup-main, .others-settings-popup-main { min-width: 400px !important; }
+ .sync-settings-footer, .others-settings-footer { display: flex; justify-content: space-around; margin-top: 15px; }
+ `;
+ const styleSheet = document.createElement("style");
+ styleSheet.innerText = styles;
+ document.head.appendChild(styleSheet);
+ const backdrop = document.createElement('div');
+ backdrop.className = 'auto-daily-popup-backdrop';
+ backdrop.id = 'auto-daily-popup-container';
+ const popup = document.createElement('div');
+ popup.className = 'auto-daily-popup-main';
+ popup.innerHTML = `
+ ×
+
+
+ `;
+ backdrop.appendChild(popup);
+ document.body.appendChild(backdrop);
+ populateList('auto-daily-doall-list', doAllTasks, false);
+ populateList('auto-daily-quests-list', questTasks, true);
+ populateList('auto-daily-upgrade-list', upgradeTasks, true);
+ function populateList(listId, tasks, isQuest) {
+ const list = document.getElementById(listId);
+ tasks.forEach(task => {
+ const li = document.createElement('li');
+ li.className = 'auto-daily-task-item';
+ li.dataset.taskId = task.id;
+ const checkboxHTML = `${task.label} `;
+ const actionHTML = isQuest
+ ? `${UI_ICON.pending}
`
+ : `${UI_ICON.fire} `;
+ li.innerHTML = checkboxHTML + actionHTML;
+ list.appendChild(li);
+ });
+ }
+ backdrop.addEventListener('click', (e) => { if (e.target === backdrop || e.target.classList.contains('auto-daily-close-btn')) backdrop.remove(); });
+ popup.addEventListener('change', (e) => {
+ if (e.target.type === 'checkbox') {
+ const taskId = e.target.dataset.taskId;
+ if (taskId) {
+ executionState[taskId] = e.target.checked;
+ saveAllSettings();
+ }
+ }
+ });
+ popup.addEventListener('click', (e) => {
+ const button = e.target.closest('.auto-daily-fire-btn');
+ if (button) {
+ const taskId = button.dataset.taskId;
+ const task = [...doAllTasks, ...questTasks, ...upgradeTasks].find(t => t.id === taskId);
+ if (task) executeSingleTask(task);
+ }
+ });
+ document.getElementById('hide-doall-btn').addEventListener('change', (e) => { hideButtonsState.doAll = e.target.checked; saveAllSettings(); applyButtonVisibility(); });
+ document.getElementById('hide-quests-btn').addEventListener('change', (e) => { hideButtonsState.quests = e.target.checked; saveAllSettings(); applyButtonVisibility(); });
+ document.getElementById('hide-actions-btn').addEventListener('change', (e) => { hideButtonsState.actions = e.target.checked; saveAllSettings(); applyButtonVisibility(); });
+ document.getElementById('new-sync-btn').addEventListener('change', (e) => { hideButtonsState.newSync = e.target.checked; saveAllSettings(); applySyncButtonState(); });
+ document.getElementById('other-settings-link').addEventListener('click', createOthersPopup);
+ document.getElementById('sync-settings-btn').addEventListener('click', createSyncPopup);
+ updateQuestStatus();
+ }
+ function createOthersPopup() {
+ if (document.getElementById('others-settings-popup-container')) return;
+ const backdrop = document.createElement('div');
+ backdrop.className = 'auto-daily-popup-backdrop';
+ backdrop.id = 'others-settings-popup-container';
+ const popup = document.createElement('div');
+ popup.className = 'auto-daily-popup-main others-settings-popup-main';
+ let listHTML = othersTasks.map(task => `
+
+ ${task.label}
+ `).join('');
+ popup.innerHTML = `
+ ×
+ `;
+ backdrop.appendChild(popup);
+ document.body.appendChild(backdrop);
+ backdrop.addEventListener('click', (e) => { if (e.target === backdrop || e.target.classList.contains('auto-daily-close-btn')) backdrop.remove(); });
+ popup.addEventListener('change', (e) => {
+ if (e.target.type === 'checkbox') {
+ othersSettingsState[e.target.dataset.taskId] = e.target.checked;
+ saveAllSettings();
+ applyOthersVisibility();
+ }
+ });
+ document.getElementById('others-select-all').addEventListener('click', () => {
+ popup.querySelectorAll('input[type="checkbox"]').forEach(cb => { cb.checked = true; othersSettingsState[cb.dataset.taskId] = true; });
+ saveAllSettings();
+ applyOthersVisibility();
+ });
+ document.getElementById('others-select-none').addEventListener('click', () => {
+ popup.querySelectorAll('input[type="checkbox"]').forEach(cb => { cb.checked = false; othersSettingsState[cb.dataset.taskId] = false; });
+ saveAllSettings();
+ applyOthersVisibility();
+ });
+ }
+ function createSyncPopup() {
+ if (document.getElementById('sync-settings-popup-container')) return;
+ const backdrop = document.createElement('div');
+ backdrop.className = 'auto-daily-popup-backdrop';
+ backdrop.id = 'sync-settings-popup-container';
+ const popup = document.createElement('div');
+ popup.className = 'auto-daily-popup-main sync-settings-popup-main';
+ popup.innerHTML = `
+ ×
+
+
Import / Export Settings
+
+ ${isProviderActive ? `
+ ` : ''}
+ `;
+ backdrop.appendChild(popup);
+ document.body.appendChild(backdrop);
+ backdrop.addEventListener('click', (e) => { if (e.target === backdrop || e.target.classList.contains('auto-daily-close-btn')) backdrop.remove(); });
+ document.getElementById('export-settings-btn').addEventListener('click', handleExport);
+ document.getElementById('import-settings-btn').addEventListener('click', handleImport);
+ if (isProviderActive) {
+ document.getElementById('set-as-default-btn').addEventListener('click', handleSetAsDefault);
+ }
+ }
+ function handleExport() {
+ const { HWHFuncs } = window;
+ const settingsToExport = { executionState, hideButtonsState, othersSettingsState };
+ const settingsJSON = JSON.stringify(settingsToExport, null, 2);
+ const blob = new Blob([settingsJSON], {type: 'application/json'});
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `daily_quests.json`;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ HWHFuncs.setProgress('Settings exported!', true);
+ }
+ function handleImport() {
+ const { HWHFuncs } = window;
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = '.json';
+ input.onchange = e => {
+ const file = e.target.files[0];
+ const reader = new FileReader();
+ reader.onload = readerEvent => {
+ try {
+ const importedSettings = JSON.parse(readerEvent.target.result);
+ if (importedSettings.executionState && importedSettings.hideButtonsState && importedSettings.othersSettingsState) {
+ executionState = importedSettings.executionState;
+ hideButtonsState = importedSettings.hideButtonsState;
+ othersSettingsState = importedSettings.othersSettingsState;
+ saveAllSettings();
+ applyButtonVisibility();
+ applyOthersVisibility();
+ applySyncButtonState();
+ const mainPopup = document.getElementById('auto-daily-popup-container');
+ if (mainPopup) mainPopup.remove();
+ createPopup();
+ HWHFuncs.setProgress('Settings imported successfully!', true);
+ } else { throw new Error("Invalid file structure."); }
+ } catch (err) { alert('Error importing file: ' + err.message); }
+ };
+ reader.readAsText(file, 'UTF-8');
+ };
+ input.click();
+ }
+ function handleSetAsDefault() {
+ const { HWHFuncs } = window;
+ if (typeof window.setAutoDailySettings === 'function') {
+ const allSettings = { executionState, hideButtonsState, othersSettingsState };
+ window.setAutoDailySettings(allSettings);
+ HWHFuncs.setProgress('Current settings saved as default for the Provider!', true);
+ } else {
+ alert('Settings Provider script is not active or is missing the set function.');
+ }
+ }
+ function applyButtonVisibility() {
+ const { getOutland, dailyQuests, doActions } = window.HWHData.buttons;
+ if(getOutland && getOutland.button) getOutland.button.style.display = hideButtonsState.doAll ? 'none' : 'flex';
+ if(dailyQuests && dailyQuests.button) dailyQuests.button.style.display = hideButtonsState.quests ? 'none' : 'flex';
+ if(doActions && doActions.button) doActions.button.style.display = hideButtonsState.actions ? 'none' : 'flex';
+ }
+ function applyOthersVisibility() {
+ const isAnyChecked = Object.values(othersSettingsState).some(value => value === true);
+ if (customOthersButton) customOthersButton.style.display = isAnyChecked ? 'flex' : 'none';
+ }
+ async function onCustomOthersClick() {
+ const { HWHFuncs, I18N, HWHClasses } = window;
+ const visibleTasks = othersTasks.filter(task => othersSettingsState[task.id]);
+ if (visibleTasks.length === 0) return;
+ const popupButtons = visibleTasks.map(task => ({
+ msg: I18N(task.id),
+ title: I18N(task.id + '_TITLE'),
+ result: async () => {
+ if (HWHClasses.executeBrawls && HWHClasses.executeBrawls.isBrawlsAutoStart) return;
+ // Use HWHData.buttons.doOthers functionality if available, otherwise show error
+ const { HWHData, HWHFuncs } = window;
+ if (HWHData && HWHData.buttons && HWHData.buttons.doOthers && HWHData.buttons.doOthers.button) {
+ // Trigger the original doOthers button click handler
+ HWHData.buttons.doOthers.button.click();
+ } else {
+ HWHFuncs.setProgress(`${task.label}: Function not available. Use main menu "Others" button.`, true);
+ console.warn(`[Auto Daily] Others task ${task.id} (${task.label}) - handler not available`);
+ }
+ }
+ }));
+ popupButtons.push({ result: false, isClose: true });
+ const answer = await HWHFuncs.popup.confirm(I18N('CHOOSE_ACTION'), popupButtons);
+ if (typeof answer === 'function') answer();
+ }
+ function applySyncButtonState() {
+ const { HWHClasses, HWHData, HWHFuncs } = window;
+ const { newDay } = HWHData.buttons;
+ const autoDailyButton = document.querySelector('[data-extension-button="auto-daily"]');
+ if (!autoDailyButton) return;
+ const scriptMenuContainer = HWHData.buttons.doActions.button?.parentElement;
+ if (!scriptMenuContainer) return;
+ if (combinedButton) { combinedButton.remove(); combinedButton = null; }
+ autoDailyButton.style.display = 'flex';
+ if(newDay && newDay.button) newDay.button.style.display = 'flex';
+ if (hideButtonsState.newSync) {
+ if(newDay && newDay.button) newDay.button.style.display = 'none';
+ autoDailyButton.style.display = 'none';
+ const buttonList = [{
+ name: 'Auto Daily', onClick: createPopup, title: 'Open the Auto Daily control panel',
+ }, {
+ name: UI_ICON.sync,
+ onClick: () => { HWHFuncs.setProgress('Syncing...', true); window.cheats.refreshGame(); },
+ title: 'Run Sync', color: 'green',
+ }];
+ combinedButton = HWHClasses.ScriptMenu.getInst().addCombinedButton(buttonList, scriptMenuContainer);
+ if (!combinedButton) return;
+ const autoDailyCombined = combinedButton.children[0];
+ const syncCombined = combinedButton.children[1];
+ if (autoDailyCombined) {
+ autoDailyCombined.style.flexGrow = '1';
+ const buttonText = autoDailyCombined.querySelector('.scriptMenu_buttonText');
+ if (buttonText) buttonText.style.whiteSpace = 'nowrap';
+ }
+ if (syncCombined) {
+ syncCombined.style.flexGrow = '0';
+ syncCombined.style.width = '45px';
+ }
+ if (HWHData.buttons.doActions.button && scriptMenuContainer.contains(HWHData.buttons.doActions.button)) {
+ scriptMenuContainer.insertBefore(combinedButton, HWHData.buttons.doActions.button);
+ } else {
+ scriptMenuContainer.appendChild(combinedButton);
+ }
+ }
+ }
+ async function updateQuestStatus() {
+ const { HWHClasses } = window;
+ // Check quest completion status using cached data - following API documentation pattern
+ // API docs: state 0 = not started, 1 = in progress, 2 = completed
+ const allQuests = await getQuestData();
+
+ const questManager = new HWHClasses.dailyQuests();
+ await questManager.autoInit();
+ [...questTasks, ...upgradeTasks].forEach(task => {
+ // Convert task.id to number for comparison (API returns numeric IDs)
+ const questId = parseInt(task.id, 10);
+ // Use direct API call result with proper ID comparison
+ const questData = allQuests.find(q => q.id === questId);
+ const questUI = document.querySelector(`.auto-daily-status-icon[data-task-id="${task.id}"]`);
+ if (!questUI) return;
+ let iconHTML = `${UI_ICON.unavailable} `;
+ if (questData) {
+ // Check if quest is completed (state === 2) - following API documentation
+ if (questData.state === 2) {
+ iconHTML = `${UI_ICON.done} `;
+ } else {
+ // Try numeric key first (as that's what the API uses), then string key
+ const questHandler = questManager.dataQuests[questId] || questManager.dataQuests[task.id];
+ if (questHandler) {
+ // Handle quests with doItFunc (like dungeon quest 10022)
+ // These quests can be executed even if isWeCanDo returns false
+ if (questHandler.doItFunc && questData.state === 1) {
+ iconHTML = `${UI_ICON.fire} `;
+ } else if (questHandler.isWeCanDo && typeof questHandler.isWeCanDo === 'function') {
+ try {
+ if (questHandler.isWeCanDo.call(questManager)) {
+ iconHTML = `${UI_ICON.fire} `;
+ }
+ } catch (e) {
+ // If isWeCanDo check fails, just show as not available
+ console.warn(`[updateQuestStatus] Quest ${task.id} isWeCanDo check failed:`, e);
+ }
+ }
+ }
+ }
+ }
+ questUI.innerHTML = iconHTML;
+ });
+ }
+ async function executeSingleTask(task) {
+ const { HWHFuncs, Send, HWHClasses } = window;
+ const isDungeonTask = task.id === 'testDungeon' || task.id === '10022';
+ if (!isDungeonTask && (dungeonRunning || window.HWH_DUNGEON_RUNNING || window.HWH_DUNGEON_BATTLE_OPEN)) {
+ HWHFuncs.setProgress(`${task.label}: skipped (dungeon running)`, true);
+ return;
+ }
+ if (isDungeonTask && window.HWH_AUTOBATTLE_RUNNING) {
+ await waitForAutoBattleIdle();
+ if (window.HWH_AUTOBATTLE_RUNNING) {
+ HWHFuncs.setProgress(`${task.label}: skipped (AutoBattle still running)`, true);
+ return;
+ }
+ }
+ try {
+ if (task.func) {
+ if (task.id === 'testDungeon') {
+ await sleep(2000);
+ }
+ HWHFuncs.setProgress(`Executing: ${task.label}`, true);
+ await task.func();
+ } else {
+ // Check quest completion status using cached data - following API documentation pattern
+ // API docs: state 0 = not started, 1 = in progress, 2 = completed
+ const allQuests = await getQuestData();
+
+ // Convert task.id to number for comparison (API returns numeric IDs)
+ const questId = parseInt(task.id, 10);
+ const questData = allQuests.find(q => q.id === questId);
+
+ if (!questData) {
+ // Quest not found - this is normal if quest is not available or not unlocked
+ HWHFuncs.setProgress(`${task.label}: No Quest`, true);
+ return;
+ }
+
+ // Check quest state and show appropriate message
+ // Following API documentation pattern: state 0 = not started, 1 = in progress, 2 = completed
+ if (questData.state === 2) {
+ // Quest is already completed
+ HWHFuncs.setProgress(`${task.label}: Already completed`, true);
+ return;
+ }
+
+ if (questData.state === 0) {
+ // Quest not started yet
+ HWHFuncs.setProgress(`${task.label}: Not started yet`, true);
+ return;
+ }
+
+ // Quest is in progress (state === 1) - proceed with execution
+ HWHFuncs.setProgress(`Executing: ${task.label}`, true);
+
+ // Initialize quest manager for execution
+ const questManager = new HWHClasses.dailyQuests();
+ await questManager.autoInit();
+
+ // Use either string or numeric key to get the quest handler
+ // Try numeric key first (as that's what the API uses)
+ const questHandler = questManager.dataQuests[questId] || questManager.dataQuests[task.id];
+
+ if (!questHandler) {
+ console.warn(`[executeSingleTask] Quest ${task.id} (${task.label}) not found in dataQuests!`);
+ HWHFuncs.setProgress(`${task.label}: Handler not found`, true);
+ return;
+ }
+
+ // Handle quests with doItFunc (like dungeon quest 10022)
+ if (questHandler.doItFunc) {
+ // Quest uses a function instead of API calls
+ if (task.id === '10022') {
+ // Special handling for dungeon quest - ensure it executes last
+ await sleep(2000);
+ await executeTestDungeon();
+ invalidateQuestCache();
+ return;
+ } else {
+ // For other doItFunc quests, call the function directly
+ await questHandler.doItFunc();
+ invalidateQuestCache();
+ return;
+ }
+ }
+
+ // Check if quest can be done (only for doItCall quests)
+ const isWeCanDo = questHandler.isWeCanDo;
+ if (!isWeCanDo || typeof isWeCanDo !== 'function') {
+ console.warn(`[executeSingleTask] Quest ${task.id} (${task.label}) has no isWeCanDo function!`);
+ HWHFuncs.setProgress(`${task.label}: Invalid handler`, true);
+ return;
+ }
+
+ let canDo = false;
+ try {
+ canDo = isWeCanDo.call(questManager);
+ } catch (e) {
+ console.error(`[executeSingleTask] Quest ${task.id} isWeCanDo check failed:`, e);
+ HWHFuncs.setProgress(`${task.label}: Check failed - ${e.message}`, true);
+ return;
+ }
+
+ if (!canDo) {
+ HWHFuncs.setProgress(`${task.label}: Cannot execute now (requirements not met)`, true);
+ return;
+ }
+
+ let calls = [];
+ if (task.id === '10023') {
+ const heroId = questManager.getHeroIdTitanGift();
+ calls = [
+ { name: 'heroTitanGiftLevelUp', args: { heroId }, ident: 'up_1' }, { name: 'heroTitanGiftDrop', args: { heroId }, ident: 'drop_1' },
+ { name: 'heroTitanGiftLevelUp', args: { heroId }, ident: 'up_2' }, { name: 'heroTitanGiftDrop', args: { heroId }, ident: 'drop_2' }
+ ];
+ } else if (questHandler.doItCall) {
+ calls = questHandler.doItCall.call(questManager);
+ } else {
+ HWHFuncs.setProgress(`${task.label}: No execution method available`, true);
+ return;
+ }
+
+ if(calls.length > 0) {
+ await Send({ calls });
+ // Invalidate cache after executing quest to get fresh data
+ invalidateQuestCache();
+ } else {
+ HWHFuncs.setProgress(`${task.label}: No actions available`, true);
+ return;
+ }
+ }
+ HWHFuncs.setProgress(`${task.label} finished!`, true);
+ } catch (e) {
+ console.error(`[executeSingleTask] ERROR executing task ${task.id} (${task.label}):`, e);
+ HWHFuncs.setProgress(`Error with ${task.label}!`, true);
+ }
+ }
+ function scheduleAutoRuns() {
+ const doAllChecked = doAllTasks.filter(task => executionState[task.id]);
+ const questsAndUpgradeChecked = [...questTasks, ...upgradeTasks].filter(task => executionState[task.id]);
+ if (doAllChecked.length === 0 && questsAndUpgradeChecked.length === 0) return;
+
+ // Dungeon and dungeon-quest should run last to avoid interference with other API/UI flows.
+ const doAllNonDungeon = doAllChecked.filter(t => t.id !== 'testDungeon');
+ const doAllDungeon = doAllChecked.filter(t => t.id === 'testDungeon');
+
+ // Quest 10022 is "Guild Dungeon" in the quest list; it also triggers dungeon logic.
+ const questsNonDungeon = questsAndUpgradeChecked.filter(t => t.id !== '10022');
+ const questsDungeon = questsAndUpgradeChecked.filter(t => t.id === '10022');
+
+ const ordered = [
+ ...doAllNonDungeon,
+ ...questsNonDungeon,
+ ...doAllDungeon,
+ ...questsDungeon,
+ ];
+
+ // Run sequentially (await each). The previous setTimeout-based scheduler could overlap long tasks and stall mid-run.
+ setTimeout(async () => {
+ const { HWHFuncs } = window;
+ if (autoRunInProgress || dungeonRunning || window.HWH_DUNGEON_RUNNING || window.HWH_DUNGEON_BATTLE_OPEN) {
+ console.log('[Auto Daily] Skipping auto-run — dungeon already in progress');
+ return;
+ }
+ autoRunInProgress = true;
+ try {
+ for (const task of ordered) {
+ if (dungeonRunning || window.HWH_DUNGEON_RUNNING || window.HWH_DUNGEON_BATTLE_OPEN) {
+ console.log('[Auto Daily] Stopping auto-run — dungeon started');
+ break;
+ }
+ const isDungeonTask = task.id === 'testDungeon' || task.id === '10022';
+ if (isDungeonTask && window.HWH_AUTOBATTLE_RUNNING) {
+ await waitForAutoBattleIdle();
+ }
+ await executeSingleTask(task);
+ await sleep(500);
+ }
+ HWHFuncs.setProgress('Auto Daily: All selected tasks finished!', true);
+ } catch (e) {
+ console.error('[Auto Daily] Auto-run failed:', e);
+ HWHFuncs.setProgress(`Auto Daily: Stopped (${e.message || 'error'})`, true);
+ } finally {
+ autoRunInProgress = false;
+ }
+ }, 7000);
+ }
+ function createCustomOthersButton() {
+ const { HWHClasses, HWHData, I18N } = window;
+ const origOthersButton = HWHData.buttons.doOthers.button;
+ if (!origOthersButton) return;
+ const scriptMenuContainer = origOthersButton.parentElement;
+ if (!scriptMenuContainer) return;
+ origOthersButton.style.display = 'none';
+ customOthersButton = HWHClasses.ScriptMenu.getInst().addButton({
+ name: I18N('OTHERS'),
+ title: I18N('OTHERS_TITLE'),
+ onClick: onCustomOthersClick
+ }, scriptMenuContainer);
+ const referenceButton = HWHData.buttons.testTitanArena.button || HWHData.buttons.testDungeon.button;
+ if (referenceButton && scriptMenuContainer.contains(referenceButton)) {
+ scriptMenuContainer.insertBefore(customOthersButton, referenceButton);
+ } else {
+ scriptMenuContainer.appendChild(customOthersButton);
+ }
+ }
+
+ function maindaily() {
+ const { HWHFuncs, HWHData, HWHClasses } = window;
+
+ loadAllSettings();
+ loadTitanHealthSettings(); // Load dungeon titan health settings
+ console.log(`${EXTENSION_NAME} v${EXTENSION_VERSION} is loading...`);
+ HWHFuncs.addExtentionName(EXTENSION_NAME, EXTENSION_VERSION, EXTENSION_AUTHOR);
+
+ if (window.HWHClasses) {
+ window.HWHClasses.executeDungeon = executeDungeon;
+ }
+
+ // Create dungeon settings GUI
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', createDungeonSettingsGUI);
+ } else {
+ createDungeonSettingsGUI();
+ }
+
+ const scriptMenuContainer = HWHData.buttons.doActions.button.parentElement;
+ const actionsButton = HWHData.buttons.doActions.button;
+
+ const autoDailyButton = HWHClasses.ScriptMenu.getInst().addButton({
+ name: 'Auto Daily',
+ onClick: createPopup,
+ title: 'Open the Auto Daily control panel',
+ }, scriptMenuContainer);
+ autoDailyButton.dataset.extensionButton = "auto-daily";
+
+ scriptMenuContainer.insertBefore(autoDailyButton, actionsButton);
+ createCustomOthersButton();
+
+ applyButtonVisibility();
+ applyOthersVisibility();
+ applySyncButtonState();
+
+ setTimeout(updateQuestStatus, 9000);
+ scheduleAutoRuns();
+
+ console.log(`${EXTENSION_NAME} initialized successfully.`);
+ }
+
+ waitForHWH(maindaily);
+
+})();
diff --git a/HeroWarsHelper.user.js b/HeroWarsHelper.user.js
index c71c235..3cc494e 100644
--- a/HeroWarsHelper.user.js
+++ b/HeroWarsHelper.user.js
@@ -3,7 +3,7 @@
// @name:en HeroWarsHelper
// @name:ru HeroWarsHelper
// @namespace HeroWarsHelper
-// @version 2.376
+// @version 2.454
// @description Automation of actions for the game Hero Wars
// @description:en Automation of actions for the game Hero Wars
// @description:ru Автоматизация действий для игры Хроники Хаоса
@@ -90,6 +90,7 @@
function getUserInfo() {
return userInfo;
}
+
/**
* Original methods for working with AJAX
*
@@ -186,7 +187,11 @@
* Простой расчет боя доступный через консоль
*/
this.Calc = function (data) {
- const type = getBattleType(data?.type);
+ 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);
@@ -225,6 +230,8 @@
ENDLESS_CARDS_TITLE: 'Disable Divination Cards wasting',
AUTO_EXPEDITION: 'Auto Expedition',
AUTO_EXPEDITION_TITLE: 'Auto-sending expeditions',
+ AUTO_ARENA_TRAINING: 'Auto Arena Training',
+ AUTO_ARENA_TRAINING_TITLE: 'Auto-start arena training loop 1 minute after game load (demo battles, no attempts)',
CANCEL_FIGHT: 'Cancel battle',
CANCEL_FIGHT_TITLE: 'Ability to cancel manual combat on GW, CoW and Asgard',
GIFTS: 'Gifts',
@@ -263,18 +270,11 @@
SEER_TITLE: 'Roll the Seer',
TOWER: 'Tower',
TOWER_TITLE: 'Pass the tower',
- ARENA: 'Arena',
- ARENA_TITLE: 'Automatically battle in Arena',
- GRAND_ARENA: 'Grand Arena',
- GRAND_ARENA_TITLE: 'Automatically battle in Grand Arena',
- AUTO_ARENAS: 'Auto Arena & Grand Arena',
- AUTO_ARENAS_TITLE: 'Automatically battle in both Arena and Grand Arena',
EXPEDITIONS: 'Expeditions',
EXPEDITIONS_TITLE: 'Sending and collecting expeditions',
SYNC: 'Sync',
SYNC_TITLE: 'Partial synchronization of game data without reloading the page',
ARCHDEMON: 'Archdemon',
- FURNACE_OF_SOULS: 'Furnace of souls',
ARCHDEMON_TITLE: 'Hitting kills and collecting rewards',
ESTER_EGGS: 'Easter eggs',
ESTER_EGGS_TITLE: 'Collect all Easter eggs or rewards',
@@ -581,7 +581,6 @@
SELL_HERO_SOULS: 'Sell souls',
SELL_HERO_SOULS_TITLE: 'Exchanges all absolute star hero souls for gold',
GOLD_RECEIVED: 'Gold received: {gold}',
- OPEN_ALL_EQUIP_BOXES: 'Open all Equipment Fragment Box?',
SERVER_NOT_ACCEPT: 'The server did not accept the result',
INVASION_BOSS_BUFF: 'For {bossLvl} boss need buff {needBuff} you have {haveBuff}',
HERO_POWER: 'Hero Power',
@@ -592,6 +591,42 @@
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',
+ USE_KEYBOARD: 'Enter the value using the keyboard',
+ SEERGAME: 'Seer Game',
+ SEERGAME_TITLE: 'Completes main quests for the "Seer Game" event',
+ SEERGAME_MSG:
+ 'This script does not allow you to win the game. It only completes the quest of winning 30 times in automatic mode by repeatedly playing the first 6 rounds until it achieves 30 consecutive wins. The probability of completing the quest this way is very high, but not 100%. If you are very unlucky, you may lose coins and fail the quest. Good luck!',
+ SEERGAME_NOT_ENOUGH_COINS_CONTINUE: 'Not enough coins to continue the game in case of a mistake. You can continue playing manually.',
+ SEERGAME_NOT_ENOUGH_COINS_START: 'Not enough coins to start the game',
+ SEERGAME_SUCCESS: 'Success! Continuing the game...',
+ SEERGAME_FAILURE: 'Failure!',
+ SEERGAME_CONTINUE: 'Continuing the game for {cost} coins',
+ SEERGAME_NEW: 'Starting! Coins: {coins}',
+ SEERGAME_START: 'Starting a new game for {cost} coins',
+ SEERGAME_END: 'Ending the game and claiming rewards',
+ SEERGAME_FINISH: 'SeerGame completed, spent {spentCoins} coins',
+ SEERGAME_RESTART: 'Restarting the game...',
+ SEERGAME_PROGRESS: 'Round {round}, Consecutive wins: {streak}',
},
ru: {
/* Чекбоксы */
@@ -601,6 +636,8 @@
ENDLESS_CARDS_TITLE: 'Отключить трату карт предсказаний',
AUTO_EXPEDITION: 'АвтоЭкспедиции',
AUTO_EXPEDITION_TITLE: 'Автоотправка экспедиций',
+ AUTO_ARENA_TRAINING: 'Авто-тренировка арены',
+ AUTO_ARENA_TRAINING_TITLE: 'Автозапуск цикла тренировки арены через 1 минуту после загрузки (демо-бои, без попыток)',
CANCEL_FIGHT: 'Отмена боя',
CANCEL_FIGHT_TITLE: 'Возможность отмены ручного боя на ВГ, СМ и в Асгарде',
GIFTS: 'Подарки',
@@ -639,18 +676,11 @@
SEER_TITLE: 'Покрутить Провидца',
TOWER: 'Башня',
TOWER_TITLE: 'Автопрохождение башни',
- ARENA: 'Арена',
- ARENA_TITLE: 'Автоматические бои в Арене',
- GRAND_ARENA: 'Великая Арена',
- GRAND_ARENA_TITLE: 'Автоматические бои в Великой Арене',
- AUTO_ARENAS: 'Авто Арена и Великая Арена',
- AUTO_ARENAS_TITLE: 'Автоматические бои в обеих аренах',
EXPEDITIONS: 'Экспедиции',
EXPEDITIONS_TITLE: 'Отправка и сбор экспедиций',
SYNC: 'Синхронизация',
SYNC_TITLE: 'Частичная синхронизация данных игры без перезагрузки сатраницы',
ARCHDEMON: 'Архидемон',
- FURNACE_OF_SOULS: 'Горнило душ',
ARCHDEMON_TITLE: 'Набивает килы и собирает награду',
ESTER_EGGS: 'Пасхалки',
ESTER_EGGS_TITLE: 'Собрать все пасхалки или награды',
@@ -956,7 +986,6 @@
SELL_HERO_SOULS: 'Продать души',
SELL_HERO_SOULS_TITLE: 'Обменивает все души героев с абсолютной звездой на золото',
GOLD_RECEIVED: 'Получено золота: {gold}',
- OPEN_ALL_EQUIP_BOXES: 'Открыть все ящики фрагментов экипировки?',
SERVER_NOT_ACCEPT: 'Сервер не принял результат',
INVASION_BOSS_BUFF: 'Для {bossLvl} босса нужен баф {needBuff} у вас {haveBuff}',
HERO_POWER: 'Сила героев',
@@ -967,6 +996,42 @@
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: 'Открыть сундуки с артефактными эссенсиями, свитакми, металлами',
+ USE_KEYBOARD: 'Введите значение с помощью клавиатуры',
+ SEERGAME: 'Игра Провидицы',
+ SEERGAME_TITLE: 'Выполяет основные квесты для ивента "Игра Провидицы"',
+ SEERGAME_MSG:
+ 'Этот скрипт не позволяет выйграть игру, он всего лиш проходит квест победить 30 раз в автоматическом режиме, для этого он проходит первые 6 раундов снова и снова пока не наберет 30 побед подряд. Вероятность выполнить квест таким образом очень высокая, но она не 100%. Если вам сильно не повезет вы можете потерять монеты и не выполнить квест. Удачи!',
+ SEERGAME_NOT_ENOUGH_COINS_CONTINUE: 'Недостаточно монет для продолжения игры в случае ошибки. Вы можете продолжить игру вручную.',
+ SEERGAME_NOT_ENOUGH_COINS_START: 'Недостаточно монет для старта игры',
+ SEERGAME_SUCCESS: 'Успех! Продолжаем игру...',
+ SEERGAME_FAILURE: 'Неудача!',
+ SEERGAME_CONTINUE: 'Продолжаем игру за {cost} монет',
+ SEERGAME_NEW: 'Стартуем! Монеты: {coins}',
+ SEERGAME_START: 'Стартуем новую игру за {cost} монет',
+ SEERGAME_END: 'Завершаем игру и забираем награды',
+ SEERGAME_FINISH: 'SeerGame завершена, потрачено {spentCoins} монет',
+ SEERGAME_RESTART: 'Перезапуск игры...',
+ SEERGAME_PROGRESS: 'Раунд {round}, Побед подряд: {streak}',
},
};
@@ -997,7 +1062,7 @@
}
console.warn('Language constant not found', {constant, replace});
if (i18nLangData['en'][constant]) {
- const result = i18nLangData[selectLang][constant];
+ const result = i18nLangData['en'][constant];
if (replace) {
return result.sprintf(replace);
}
@@ -1043,6 +1108,12 @@
get title() { return I18N('AUTO_EXPEDITION_TITLE'); },
default: false,
},
+ autoArenaTraining: {
+ get label() { return I18N('AUTO_ARENA_TRAINING'); },
+ cbox: null,
+ get title() { return I18N('AUTO_ARENA_TRAINING_TITLE'); },
+ default: false,
+ },
cancelBattle: {
get label() { return I18N('CANCEL_FIGHT'); },
cbox: null,
@@ -1225,13 +1296,21 @@
*/
const buttons = {
getOutland: {
- get name() { return I18N('TO_DO_EVERYTHING'); },
- get title() { return I18N('TO_DO_EVERYTHING_TITLE'); },
+ 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'); },
+ get name() {
+ return I18N('ACTIONS');
+ },
+ get title() {
+ return I18N('ACTIONS_TITLE');
+ },
onClick: async function () {
const { actionsPopupButtons } = HWHData;
actionsPopupButtons.push({ result: false, isClose: true });
@@ -1242,8 +1321,12 @@
},
},
doOthers: {
- get name() { return I18N('OTHERS'); },
- get title() { return I18N('OTHERS_TITLE'); },
+ get name() {
+ return I18N('OTHERS');
+ },
+ get title() {
+ return I18N('OTHERS_TITLE');
+ },
onClick: async function () {
const { othersPopupButtons } = HWHData;
othersPopupButtons.push({ result: false, isClose: true });
@@ -1257,8 +1340,12 @@
isCombine: true,
combineList: [
{
- get name() { return I18N('TITAN_ARENA'); },
- get title() { return I18N('TITAN_ARENA_TITLE'); },
+ get name() {
+ return I18N('TITAN_ARENA');
+ },
+ get title() {
+ return I18N('TITAN_ARENA_TITLE');
+ },
onClick: function () {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('TITAN_ARENA')}?`, testTitanArena);
},
@@ -1266,46 +1353,33 @@
{
name: '>>',
onClick: cheats.goTitanValley,
- get title() { return I18N('TITAN_VALLEY_TITLE'); },
+ get title() {
+ return I18N('TITAN_VALLEY_TITLE');
+ },
color: 'green',
},
],
},
- testArena: {
- get name() { return I18N('ARENA'); },
- get title() { return I18N('ARENA_TITLE'); },
- onClick: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('ARENA')}?`, testArena);
- },
- },
- testGrandArena: {
- get name() { return I18N('GRAND_ARENA'); },
- get title() { return I18N('GRAND_ARENA_TITLE'); },
- onClick: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('GRAND_ARENA')}?`, testGrandArena);
- },
- },
- testBothArenas: {
- get name() { return I18N('AUTO_ARENAS'); },
- get title() { return I18N('AUTO_ARENAS_TITLE'); },
- onClick: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('AUTO_ARENAS')}?`, testBothArenas);
- },
- },
testDungeon: {
isCombine: true,
combineList: [
{
- get name() { return I18N('DUNGEON'); },
+ get name() {
+ return I18N('DUNGEON');
+ },
onClick: function () {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('DUNGEON')}?`, testDungeon);
},
- get title() { return I18N('DUNGEON_TITLE'); },
+ get title() {
+ return I18N('DUNGEON_TITLE');
+ },
},
{
name: '>>',
onClick: cheats.goClanIsland,
- get title() { return I18N('GUILD_ISLAND_TITLE'); },
+ get title() {
+ return I18N('GUILD_ISLAND_TITLE');
+ },
color: 'green',
},
],
@@ -1314,61 +1388,94 @@
isCombine: true,
combineList: [
{
- get name() { return I18N('ADVENTURE'); },
+ get name() {
+ return I18N('ADVENTURE');
+ },
onClick: () => {
testAdventure();
},
- get title() { return I18N('ADVENTURE_TITLE'); },
+ get title() {
+ return I18N('ADVENTURE_TITLE');
+ },
},
{
- get name() { return I18N('AUTO_RAID_ADVENTURE'); },
- onClick: autoRaidAdventure,
- get title() { return I18N('AUTO_RAID_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'); },
+ get title() {
+ return I18N('SANCTUARY_TITLE');
+ },
color: 'green',
},
],
},
rewardsAndMailFarm: {
- get name() { return I18N('REWARDS_AND_MAIL'); },
- get title() { return I18N('REWARDS_AND_MAIL_TITLE'); },
+ 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'); },
+ 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'); },
+ get name() {
+ return I18N('DAILY_QUESTS');
+ },
+ get title() {
+ return I18N('DAILY_QUESTS_TITLE');
+ },
onClick: async function () {
const quests = new dailyQuests(
() => {},
() => {}
);
- await quests.autoInit(true);
+ await quests.autoInit();
quests.start();
},
},
newDay: {
- get name() { return I18N('SYNC'); },
- get title() { return I18N('SYNC_TITLE'); },
+ 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'); },
+ get name() {
+ return I18N('ARCHDEMON');
+ },
+ get title() {
+ return I18N('ARCHDEMON_TITLE');
+ },
onClick: function () {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('ARCHDEMON')}?`, bossRatingEvent);
},
@@ -1377,12 +1484,76 @@
},
// Горнило душ
bossRatingEventSouls: {
- get name() { return I18N('FURNACE_OF_SOULS'); },
- get title() { return I18N('ARCHDEMON_TITLE'); },
+ 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 () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('FURNACE_OF_SOULS')}?`, bossRatingEventSouls);
+ 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();
+ });
},
- hide: true,
color: 'red',
},
};
@@ -1395,109 +1566,135 @@
const actionsPopupButtons = [
{
get msg() {
- return I18N('OUTLAND')
+ return I18N('TIDY_INVENTORY');
+ },
+ async result() {
+ const { InventoryTidier } = HWHClasses;
+ await new InventoryTidier().run();
+ },
+ get title() {
+ return I18N('TIDY_INVENTORY_TITLE');
+ },
+ color: 'orange',
+ },
+ {
+ get msg() {
+ return I18N('OUTLAND');
},
- result: function () {
+ result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('OUTLAND')}?`, getOutland);
},
get title() {
return I18N('OUTLAND_TITLE');
},
+ color: 'green',
+ isOneSocket: true,
},
{
get msg() {
- return I18N('TOWER')
+ return I18N('TOWER');
},
- result: function () {
+ result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('TOWER')}?`, testTower);
},
get title() {
return I18N('TOWER_TITLE');
},
+ color: 'graphite',
},
{
get msg() {
- return I18N('EXPEDITIONS')
+ return I18N('EXPEDITIONS');
},
- result: function () {
+ result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('EXPEDITIONS')}?`, checkExpedition);
},
get title() {
return I18N('EXPEDITIONS_TITLE');
},
+ color: 'blue',
},
{
get msg() {
- return I18N('MINIONS')
+ return I18N('MINIONS');
},
- result: function () {
+ result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('MINIONS')}?`, testRaidNodes);
},
get title() {
return I18N('MINIONS_TITLE');
},
+ color: 'red',
},
{
get msg() {
- return I18N('ESTER_EGGS')
+ return I18N('ESTER_EGGS');
},
- result: function () {
+ result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('ESTER_EGGS')}?`, offerFarmAllReward);
},
get title() {
return I18N('ESTER_EGGS_TITLE');
},
+ color: 'yellow',
},
{
get msg() {
- return I18N('STORM')
+ return I18N('STORM');
},
- result: function () {
+ result() {
testAdventure('solo');
},
get title() {
return I18N('STORM_TITLE');
},
+ color: 'indigo',
},
{
get msg() {
- return I18N('REWARDS')
+ return I18N('REWARDS');
},
- result: function () {
+ result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('REWARDS')}?`, questAllFarm);
},
get title() {
return I18N('REWARDS_TITLE');
},
+ color: 'orange',
},
{
get msg() {
- return I18N('MAIL')
+ return I18N('MAIL');
},
- result: function () {
+ result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('MAIL')}?`, mailGetAll);
},
get title() {
return I18N('MAIL_TITLE');
},
+ color: 'beige',
},
{
get msg() {
- return I18N('SEER')
+ return I18N('SEER');
},
- result: function () {
+ result() {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('SEER')}?`, rollAscension);
},
get title() {
return I18N('SEER_TITLE');
},
+ color: 'violet',
},
// {
// get msg() {
- // return I18N('NY_GIFTS')
+ // return I18N('NY_GIFTS');
// },
// result: getGiftNewYear,
- // get title() { return I18N('NY_GIFTS_TITLE'); },
+ // get title() {
+ // return I18N('NY_GIFTS_TITLE');
+ // },
+ // color: 'pink',
// },
];
@@ -1509,65 +1706,72 @@
const othersPopupButtons = [
{
get msg() {
- return I18N('GET_ENERGY')
+ return I18N('GET_ENERGY');
},
result: farmStamina,
get title() {
return I18N('GET_ENERGY_TITLE');
},
+ color: 'green',
+ isOneSocket: true,
},
{
get msg() {
- return I18N('ITEM_EXCHANGE')
+ return I18N('ITEM_EXCHANGE');
},
result: fillActive,
get title() {
return I18N('ITEM_EXCHANGE_TITLE');
},
+ color: 'beige',
},
{
get msg() {
- return I18N('BUY_SOULS')
+ return I18N('BUY_SOULS');
},
- result: function () {
+ 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')
+ return I18N('BUY_FOR_GOLD');
},
- result: function () {
+ 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')
+ return I18N('BUY_OUTLAND');
},
result: bossOpenChestPay,
get title() {
return I18N('BUY_OUTLAND_TITLE');
},
+ color: 'orange',
},
{
get msg() {
- return I18N('CLAN_STAT')
+ return I18N('CLAN_STAT');
},
result: clanStatistic,
get title() {
return I18N('CLAN_STAT_TITLE');
},
+ color: 'blue',
},
{
get msg() {
- return I18N('EPIC_BRAWL')
+ return I18N('EPIC_BRAWL');
},
result: async function () {
confShow(`${I18N('RUN_SCRIPT')} ${I18N('EPIC_BRAWL')}?`, () => {
@@ -1578,78 +1782,87 @@
get title() {
return I18N('EPIC_BRAWL_TITLE');
},
+ color: 'red',
},
{
get msg() {
- return I18N('ARTIFACTS_UPGRADE')
+ return I18N('ARTIFACTS_UPGRADE');
},
result: updateArtifacts,
get title() {
return I18N('ARTIFACTS_UPGRADE_TITLE');
},
+ color: 'indigo',
},
{
get msg() {
- return I18N('SKINS_UPGRADE')
+ return I18N('SKINS_UPGRADE');
},
result: updateSkins,
get title() {
return I18N('SKINS_UPGRADE_TITLE');
},
+ color: 'pink',
},
{
get msg() {
- return I18N('SEASON_REWARD')
+ return I18N('SEASON_REWARD');
},
result: farmBattlePass,
get title() {
return I18N('SEASON_REWARD_TITLE');
},
+ color: 'graphite',
},
{
get msg() {
- return I18N('SELL_HERO_SOULS')
+ return I18N('SELL_HERO_SOULS');
},
result: sellHeroSoulsForGold,
get title() {
return I18N('SELL_HERO_SOULS_TITLE');
},
+ color: 'brown',
},
{
get msg() {
- return I18N('CHANGE_MAP')
+ return I18N('CHANGE_MAP');
},
result: async function () {
const maps = Object.values(lib.data.seasonAdventure.list)
- .filter((e) => e.map.cells.length > 3)
+ .filter((e) => e.startCondition.time.value + e.duration > Date.now() / 1000)
.map((i) => ({
msg: I18N('MAP_NUM', { num: i.id }),
result: i.id,
}));
+ maps.push({
+ msg: I18N('MAP_NUM', { num: 'online' }),
+ result: 'online',
+ });
+
const result = await popup.confirm(I18N('SELECT_ISLAND_MAP'), [...maps, { result: false, isClose: true }]);
if (result) {
- cheats.changeIslandMap(result);
+ if (result === 'online') {
+ window.open('https://hwmap.online/', '_blank');
+ } else {
+ cheats.changeIslandMap(result);
+ }
}
},
get title() {
return I18N('CHANGE_MAP_TITLE');
},
+ color: 'blue',
},
{
get msg() {
- return I18N('HERO_POWER')
+ return I18N('HERO_POWER');
},
result: async () => {
- const calls = ['userGetInfo', 'heroGetAll'].map((name) => ({
- name,
- args: {},
- ident: name,
- }));
- const [maxHeroSumPower, heroSumPower] = await Send({ calls }).then((e) => [
- e.results[0].result.response.maxSumPower.heroes,
- Object.values(e.results[1].result.response).reduce((a, e) => a + e.power, 0),
- ]);
+ const [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() }) +
@@ -1657,11 +1870,32 @@
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 }]);
+ await popup.confirm(msg, [{ msg: I18N('BTN_OK'), result: 0, color: 'green' }]);
},
get title() {
return I18N('HERO_POWER_TITLE');
},
+ color: 'green',
+ },
+ {
+ get msg() {
+ return I18N('SEERGAME');
+ },
+ result: async () => {
+ const message = I18N('SEERGAME_MSG');
+ const result = await popup.confirm(message, [
+ { msg: I18N('BTN_CANCEL'), result: false, isCancel: true, color: 'red' },
+ { msg: I18N('BTN_DO_IT'), result: true, color: 'green' },
+ ]);
+ if (result) {
+ const { SeerGame } = this.HWHClasses;
+ new SeerGame().start();
+ }
+ },
+ get title() {
+ return I18N('SEERGAME_TITLE');
+ },
+ color: 'violet',
},
];
/**
@@ -1760,6 +1994,31 @@
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
*
@@ -1814,18 +2073,7 @@
* Ответ на последний вопрос викторины
*/
let lastAnswer = null;
- /**
- * Flag for opening keys or titan artifact spheres
- *
- * Флаг открытия ключей или сфер артефактов титанов
- */
- let artifactChestOpen = false;
- /**
- * The name of the function to open keys or orbs of titan artifacts
- *
- * Имя функции открытия ключей или сфер артефактов титанов
- */
- let artifactChestOpenCallName = '';
+
let correctShowOpenArtifact = 0;
/**
* Data for the last battle in the dungeon
@@ -1921,14 +2169,12 @@
noCallback = noCallback || (() => {});
if (yesCallback) {
buts = [
- { msg: I18N('BTN_RUN'), result: true},
- { msg: I18N('BTN_CANCEL'), result: false, isCancel: true},
+ { 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},
- ];
+ buts = [{ msg: I18N('BTN_OK'), result: true, color: 'green' }];
}
popup.confirm(message, buts).then((e) => {
// dialogPromice = null;
@@ -1948,15 +2194,28 @@
if (!this.isSetOnMessage) {
const oldOnmessage = this.onmessage;
this.onmessage = function (event) {
+ let parsedData = null;
+ let messageType = null;
+
try {
- const data = JSON.parse(event.data);
- if (!this.isWebSocketLogin && data.result.type == "iframeEvent.login") {
+ parsedData = JSON.parse(event.data);
+ messageType = parsedData?.result?.type;
+ } catch (e) {}
+
+ if (parsedData) {
+ if (!this.isWebSocketLogin && messageType === 'iframeEvent.login') {
this.isWebSocketLogin = true;
- } else if (data.result.type == "iframeEvent.login") {
+ } else if (messageType === 'iframeEvent.login') {
return;
}
- } catch (e) { }
- return oldOnmessage.apply(this, arguments);
+ }
+
+ // Вызов обработчиков
+ Events.emit('WSMessage', messageType, parsedData?.result, event);
+
+ if (typeof oldOnmessage === 'function') {
+ return oldOnmessage.apply(this, arguments);
+ }
}
this.isSetOnMessage = true;
}
@@ -2059,13 +2318,12 @@
if (isChecked('dailyQuests')) {
testDailyQuests();
}
-
- // Auto run Do All function with all tasks checked
- testDoYourBest();
-
+
if (isChecked('buyForGold')) {
buyInStoreForGold();
}
+
+ Events.emit('startGame', this);
}
/**
* Outgoing request data processing
@@ -2178,8 +2436,8 @@
const showMsg = async function (msg, ansF, ansS) {
if (typeof popup == 'object') {
return await popup.confirm(msg, [
- {msg: ansF, result: false},
- {msg: ansS, result: true},
+ { 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')})`);
@@ -2192,48 +2450,48 @@
*/
const showMsgs = async function (msg, ansF, ansS, ansT) {
return await popup.confirm(msg, [
- {msg: ansF, result: 0},
- {msg: ansS, result: 1},
- {msg: ansT, result: 2},
+ { msg: ansF, result: 0, color: 'green' },
+ { msg: ansS, result: 1, color: 'red' },
+ { msg: ansT, result: 2 },
]);
}
- let changeRequest = false;
+ this._isChangeRequest = false;
const testData = JSON.parse(tempData);
for (const call of testData.calls) {
- if (!artifactChestOpen) {
- requestHistory[this.uniqid].calls[call.name] = call.ident;
- }
+ 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) {
+ 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') &&
+ 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) {
+ //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;
@@ -2250,17 +2508,17 @@
if (result.result?.win) {
call.args.result = result.result;
call.args.progress = result.progress;
- changeRequest = true;
+ 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 },
- { msg: I18N('BTN_ACCEPT'), result: 1 },
+ { 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;
- changeRequest = true;
+ this._isChangeRequest = true;
}
}
} catch (error) {
@@ -2268,21 +2526,29 @@
}
}
- if (isChecked('tryFixIt_v2') && !call.args.result.win && call.name == 'invasion_bossEnd' && lastBattleInfo) {
- setProgress(I18N('LETS_FIX'), false);
+ if (call.name == 'invasion_bossEnd' && lastBattleInfo) {
const cloneBattle = structuredClone(lastBattleInfo);
- const bFix = new WinFixBattle(cloneBattle);
- const 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');
- changeRequest = true;
+ let result = null;
+ const defId = cloneBattle?.defenders?.[0]?.[1]?.id;
+ if (!call.args.result.win && isChecked('tryFixIt_v2') && defId != 2010) {
+ 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);
}
- setProgress(msgResult, false, hideProgress);
- if (lastBattleInfo.seed === 8888) {
+ 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);
@@ -2306,7 +2572,7 @@
resultPopup = await showMsgs(I18N('MSG_HAVE_BEEN_DEFEATED'), I18N('BTN_OK'), I18N('BTN_CANCEL'), I18N('BTN_AUTO'));
} else if (call.name == 'clanWarEndBattle' || call.name == 'crossClanWar_endBattle') {
resultPopup = await showMsg(I18N('MSG_HAVE_BEEN_DEFEATED'), I18N('BTN_OK'), I18N('BTN_AUTO_F5'));
- } else if (call.name !== 'epicBrawl_endBattle' && call.name !== 'titanArenaEndBattle') {
+ } 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) {
@@ -2315,7 +2581,7 @@
}
fixBattle(call.args.progress[0].attackers.heroes);
fixBattle(call.args.progress[0].defenders.heroes);
- changeRequest = true;
+ this._isChangeRequest = true;
if (resultPopup > 1) {
this.onReadySuccess = testAutoBattle;
// setTimeout(bossBattle, 1000);
@@ -2326,7 +2592,7 @@
if (resultPopup) {
fixBattle(call.args.progress[0].attackers.heroes);
fixBattle(call.args.progress[0].defenders.heroes);
- changeRequest = true;
+ this._isChangeRequest = true;
if (resultPopup > 1) {
this.onReadySuccess = testAutoBattle;
}
@@ -2336,9 +2602,9 @@
if (isChecked('autoBrawls') && !HWHClasses.executeBrawls.isBrawlsAutoStart && call.name == 'brawl_endBattle') {
}
}
- /**
+ /**
* Save pack for Brawls
- *
+ *
* Сохраняем пачку для потасовок
*/
if (isChecked('autoBrawls') && !HWHClasses.executeBrawls.isBrawlsAutoStart && call.name == 'brawl_startBattle') {
@@ -2348,8 +2614,8 @@
await popup.confirm(
I18N('START_AUTO_BRAWLS'),
[
- { msg: I18N('BTN_NO'), result: false },
- { msg: I18N('BTN_YES'), result: true },
+ { msg: I18N('BTN_NO'), result: false, color: 'red' },
+ { msg: I18N('BTN_YES'), result: true, color: 'green' },
],
[
{
@@ -2386,7 +2652,7 @@
const resultPopup = await popup.confirm(
`${I18N('MSG_YOU_APPLIED')} ${lastDamage.toLocaleString()} ${I18N('MSG_DAMAGE')}.`,
[
- { msg: I18N('BTN_OK'), result: false },
+ { msg: I18N('BTN_OK'), result: false, color: 'green' },
{ msg: I18N('BTN_AUTO_F5'), result: 1 },
//{ msg: I18N('BTN_TRY_FIX_IT'), result: 2 },
...testFunc,
@@ -2394,7 +2660,9 @@
[
{
name: 'isStat',
- get label() { return I18N('CALC_STAT'); },
+ get label() {
+ return I18N('CALC_STAT');
+ },
checked: false,
},
]
@@ -2458,7 +2726,7 @@
fixBattle(call.args.progress[0].attackers.heroes);
fixBattle(call.args.progress[0].defenders.heroes);
}
- changeRequest = true;
+ this._isChangeRequest = true;
}
const isStat = popup.getCheckBoxes().find((e) => e.name === 'isStat');
if (isStat.checked) {
@@ -2474,7 +2742,7 @@
}
/**
* Saving the request to start the last battle
- * Сохранение запроса начала последнего боя
+ * Сохранение запроса начала последнего боя
*/
if (
call.name == 'clanWarAttack' ||
@@ -2490,13 +2758,16 @@
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,
- }));
+ 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));
@@ -2505,22 +2776,6 @@
}
lastBossBattleStart = Date.now();
}
- if (call.name == 'invasion_bossEnd') {
- const lastBattle = lastBattleInfo;
- if (lastBattle && call.args.result.win) {
- if (lastBattle.seed === 8008) {
- lastBattle.progress = call.args.progress;
- const result = await Calc(lastBattle);
- let timer = getTimer(result.battleTime, 1) + addBattleTimer;
- const period = Math.ceil((Date.now() - lastBossBattleStart) / 1000);
- console.log(timer, period);
- if (period < timer) {
- timer = timer - period;
- await countdownTimer(timer);
- }
- }
- }
- }
/**
* Disable spending divination cards
* Отключить трату карт предсказаний
@@ -2529,7 +2784,7 @@
if (call.args.isRaid) {
if (HWHData.countPredictionCard <= 0) {
delete call.args.isRaid;
- changeRequest = true;
+ this._isChangeRequest = true;
} else if (HWHData.countPredictionCard > 0) {
HWHData.countPredictionCard--;
}
@@ -2541,18 +2796,18 @@
*/
const lastBattle = lastDungeonBattleData;
if (lastBattle && !call.args.isRaid) {
- if (changeRequest) {
- lastBattle.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }];
+ 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 (changeRequest) {
+ 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);
@@ -2574,7 +2829,7 @@
if (lastAnswer && isChecked('getAnswer')) {
call.args.answerId = lastAnswer;
lastAnswer = null;
- changeRequest = true;
+ this._isChangeRequest = true;
}
}
/**
@@ -2589,8 +2844,8 @@
let startTimer = false;
if (!call.args.result.win) {
startTimer = await popup.confirm(I18N('DEFEAT_TURN_TIMER'), [
- { msg: I18N('BTN_NO'), result: false },
- { msg: I18N('BTN_YES'), result: true },
+ { msg: I18N('BTN_NO'), result: false, color: 'red' },
+ { msg: I18N('BTN_YES'), result: true, color: 'green' },
]);
}
@@ -2614,19 +2869,21 @@
* Getting mission data for auto-repeat
* Получение данных миссии для автоповтора
*/
- if (isChecked('repeatMission') &&
- call.name == 'missionEnd') {
+ if (isChecked('repeatMission') && call.name == 'missionEnd') {
let missionInfo = {
id: call.args.id,
result: call.args.result,
heroes: call.args.progress[0].attackers.heroes,
count: 0,
- }
+ };
setTimeout(async () => {
- if (!isSendsMission && await popup.confirm(I18N('MSG_REPEAT_MISSION'), [
- { msg: I18N('BTN_REPEAT'), result: true},
- { msg: I18N('BTN_NO'), result: false},
- ])) {
+ 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);
@@ -2642,74 +2899,50 @@
lastMissionStart = call.args;
lastMissionBattleStart = Date.now();
}
-
+
/**
* Specify the quantity for Titan Orbs and Pet Eggs
* Указать количество для сфер титанов и яиц петов
*/
- if (isChecked('countControl') &&
+ if (
+ isChecked('countControl') &&
(call.name == 'pet_chestOpen' ||
- call.name == 'titanUseSummonCircle') &&
- call.args.amount > 1) {
+ 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},
- ]);
+ const result = await popup.confirm(I18N('MSG_SPECIFY_QUANT'), [{ msg: I18N('BTN_OPEN'), isInput: true, default: 1, color: 'green' }]);
if (result) {
- const item = call.name == 'pet_chestOpen' ? { id: 90, type: 'consumable' } : { id: 13, type: 'coin' };
+ 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;
- changeRequest = true;
- }
- }
- /**
- * Specify the amount for keys and spheres of titan artifacts
- * Указать колличество для ключей и сфер артефактов титанов
- */
- if (isChecked('countControl') &&
- (call.name == 'artifactChestOpen' ||
- call.name == 'titanArtifactChestOpen') &&
- call.args.amount > 1 &&
- call.args.free &&
- !changeRequest) {
- artifactChestOpenCallName = call.name;
- const startAmount = call.args.amount;
- let result = await popup.confirm(I18N('MSG_SPECIFY_QUANT'), [
- { msg: I18N('BTN_OPEN'), isInput: true, default: 1 },
- ]);
- if (result) {
- const openChests = result;
- let sphere = result < 10 ? 1 : 10;
- call.args.amount = sphere;
- for (let count = openChests - sphere; count > 0; count -= sphere) {
- if (count < 10) sphere = 1;
- const ident = artifactChestOpenCallName + "_" + count;
- testData.calls.push({
- name: artifactChestOpenCallName,
- args: {
- amount: sphere,
- free: true,
- },
- ident: ident
- });
- if (!Array.isArray(requestHistory[this.uniqid].calls[call.name])) {
- requestHistory[this.uniqid].calls[call.name] = [requestHistory[this.uniqid].calls[call.name]];
- }
- requestHistory[this.uniqid].calls[call.name].push(ident);
- }
+ this._isChangeRequest = true;
- const consumableId = call.name == 'artifactChestOpen' ? 45 : 55;
- cheats.updateInventory({
- consumable: {
- [consumableId]: -(openChests - startAmount),
- },
- });
- artifactChestOpen = true;
- changeRequest = true;
+ correctShowOpenArtifact = 0;
+ if ((call.name == 'artifactChestOpen' || call.name == 'titanArtifactChestOpen') && call.args.amount > 20) {
+ correctShowOpenArtifact = 3;
+ }
}
}
if (call.name == 'consumableUseLootBox') {
@@ -2720,16 +2953,12 @@
*/
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')) {
+ 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 },
+ { msg: I18N('BTN_OPEN'), isInput: true, default: call.args.amount, color: 'green' },
]);
call.args.amount = result;
- changeRequest = true;
- }
- if (isChecked('countControl') && call.args.libId >= 362 && call.args.libId <= 389) {
- this.massOpen = call.args.libId;
+ this._isChangeRequest = true;
}
}
if (call.name == 'invasion_bossStart' && isChecked('tryFixIt_v2')) {
@@ -2750,7 +2979,7 @@
call.args.pet = pack.pet;
call.args.heroes = pack.heroes;
call.args.favor = pack.favor;
- changeRequest = true;
+ this._isChangeRequest = true;
}
}
}
@@ -2788,13 +3017,15 @@
// { msg: I18N('BTN_RUN'), isInput: true, default: call.args.times },
// ]));
// call.args.times = result > call.args.times ? call.args.times : result;
- // changeRequest = true;
+ // this._isChangeRequest = true;
// }
// }
+
+ Events.emit('checkChangeSend', this, call);
}
let headers = requestHistory[this.uniqid].headers;
- if (changeRequest) {
+ if (this._isChangeRequest) {
sourceData = JSON.stringify(testData);
headers['X-Auth-Signature'] = getSignature(headers, sourceData);
}
@@ -2815,8 +3046,7 @@
*/
async function checkChangeResponse(response) {
try {
- isChange = false;
- let nowTime = Math.round(Date.now() / 1000);
+ this._isChangeResponse = false;
callsIdent = requestHistory[this.uniqid].calls;
respond = JSON.parse(response);
/**
@@ -2824,7 +3054,7 @@
* Если запрос вернул ошибку удаляет ошибку (убирает ошибки синхронизации)
*/
if (respond.error) {
- isChange = true;
+ this._isChangeResponse = true;
console.error(respond.error);
if (isChecked('showErrors')) {
popup.confirm(I18N('ERROR_MSG', {
@@ -2837,12 +3067,10 @@
respond.results = [];
}
}
- let mainReward = null;
const allReward = {};
- let countTypeReward = 0;
let readQuestInfo = false;
for (const call of respond.results) {
- /**
+ /**
* Obtaining initial data for completing quests
* Получение исходных данных для выполнения квестов
*/
@@ -2872,22 +3100,20 @@
if (billings && bundle) {
call.result.response.billings = call.result.response.billings.filter((e) => ['repeatableOffer'].includes(e.type));
call.result.response.bundle = [];
- isChange = true;
+ this._isChangeResponse = true;
}
}
/**
* Hiding donation offers 2
* Скрываем предложения доната 2
*/
- if (getSaveVal('noOfferDonat') &&
- (call.ident == callsIdent['offerGetAll'] ||
- call.ident == callsIdent['specialOffer_getAll'])) {
+ 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)
+ (e) => !['addBilling', 'bundleCarousel'].includes(e.type) || ['idleResource', 'stagesOffer'].includes(e.offerType),
);
- isChange = true;
+ this._isChangeResponse = true;
}
}
/**
@@ -2896,18 +3122,18 @@
*/
if (getSaveVal('noOfferDonat') && call.result?.bundleUpdate) {
delete call.result.bundleUpdate;
- isChange = true;
+ this._isChangeResponse = true;
}
/**
* Hiding donation offers 4
- * Скрываем предложения доната 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)
+ (e) => !['addBilling', 'bundleCarousel'].includes(e.type) || ['idleResource', 'stagesOffer'].includes(e.offerType),
);
- isChange = true;
+ this._isChangeResponse = true;
}
/**
* Copies a quiz question to the clipboard
@@ -2917,21 +3143,27 @@
let quest = call.result.response;
console.log(quest.question);
copyText(quest.question);
- setProgress(I18N('QUESTION_COPY'), true);
+ //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');
+ let showText = I18N('QUESTION_COPY') + ' ';
+ try {
+ const answer = await getAnswer(lastQuestion);
+ if (answer) {
+ lastAnswer = answer;
+ console.log(answer);
+ showText += `${I18N('ANSWER_KNOWN')}: ${answer}`;
+ } else {
+ lastAnswer = null;
+ showText += I18N('ANSWER_NOT_KNOWN');
+ }
+ } catch (error) {
+ lastAnswer = null;
+ showText += error == 'Access denied' ? error : 'Error';
}
try {
@@ -2941,7 +3173,7 @@
}
} catch (e) {}
- setProgress(showText, true);
+ setProgress(showText, false, hideProgress);
}
}
/**
@@ -2976,70 +3208,80 @@
questsInfo['userGetInfo'] = user;
}
}
+ /**
+ * 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'] ||
+ if (
+ call.ident == callsIdent['clanWarAttack'] ||
call.ident == callsIdent['crossClanWar_startBattle'] ||
call.ident == callsIdent['bossAttack'] ||
- call.ident == callsIdent['battleGetReplay'] ||
call.ident == callsIdent['brawl_startBattle'] ||
call.ident == callsIdent['adventureSolo_turnStartBattle'] ||
call.ident == callsIdent['invasion_bossStart'] ||
call.ident == callsIdent['titanArenaStartBattle'] ||
call.ident == callsIdent['towerStartBattle'] ||
call.ident == callsIdent['epicBrawl_startBattle'] ||
- call.ident == callsIdent['adventure_turnStartBattle']) {
+ 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'] ||
+ if (
+ call.ident == callsIdent['brawl_startBattle'] ||
call.ident == callsIdent['bossAttack'] ||
call.ident == callsIdent['towerStartBattle'] ||
- call.ident == callsIdent['invasion_bossStart']) {
+ call.ident == callsIdent['invasion_bossStart']
+ ) {
battle = call.result.response;
}
lastBattleInfo = battle;
- if (call.ident == callsIdent['battleGetReplay'] && call.result.response.replay.type === "clan_raid") {
- if (call?.result?.response?.replay?.result?.damage) {
- const damages = Object.values(call.result.response.replay.result.damage);
- const bossDamage = damages.reduce((a, v) => a + v, 0);
- setProgress(I18N('BOSS_DAMAGE') + bossDamage.toLocaleString(), false, hideProgress);
- continue;
- }
- }
- if (!isChecked('preCalcBattle')) {
- continue;
- }
- const preCalcBattle = structuredClone(battle);
- setProgress(I18N('BEING_RECALC'));
- let battleDuration = 120;
- try {
- const typeBattle = getBattleType(preCalcBattle.type);
- battleDuration = +lib.data.battleConfig[typeBattle.split('_')[1]].config.battleDuration;
- } catch (e) { }
- //console.log(battle.type);
- function getBattleInfo(battle, isRandSeed) {
- return new Promise(function (resolve) {
- if (isRandSeed) {
- battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3);
+ const defId = battle?.defenders?.[0]?.[1]?.id
+ if (isChecked('preCalcBattle') && defId != 2010) {
+ 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);
}
- BattleCalc(battle, getBattleType(battle.type), 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}));
+ 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);
@@ -3049,9 +3291,18 @@
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)
+ 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
@@ -3061,7 +3312,7 @@
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 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);
}
@@ -3075,18 +3326,26 @@
for (let n in chains) {
chains[n] = 9999;
}
- isChange = true;
+ this._isChangeResponse = true;
}
/**
- * Opening keys and spheres of titan artifacts
- * Открытие ключей и сфер артефактов титанов
+ * Sum the result of opening Pet Eggs
+ * Суммирование результата открытия яиц питомцев
*/
- if (artifactChestOpen &&
- (call.ident == callsIdent[artifactChestOpenCallName] ||
- (callsIdent[artifactChestOpenCallName] && callsIdent[artifactChestOpenCallName].includes(call.ident)))) {
- let reward = call.result.response[artifactChestOpenCallName == 'artifactChestOpen' ? 'chestReward' : 'reward'];
-
- reward.forEach(e => {
+ if (isChecked('countControl') && call.ident == callsIdent['pet_chestOpen']) {
+ const rewards = call.result.response.rewards;
+ if (rewards.length > 10) {
+ /**
+ * Removing pet cards
+ * Убираем карточки петов
+ */
+ for (const reward of rewards) {
+ if (reward.petCard) {
+ delete reward.petCard;
+ }
+ }
+ }
+ rewards.forEach((e) => {
for (let f in e) {
if (!allReward[f]) {
allReward[f] = {};
@@ -3094,58 +3353,14 @@
for (let o in e[f]) {
if (!allReward[f][o]) {
allReward[f][o] = e[f][o];
- countTypeReward++;
} else {
allReward[f][o] += e[f][o];
}
}
}
});
-
- if (!call.ident.includes(artifactChestOpenCallName)) {
- mainReward = call.result.response;
- }
- }
-
- if (countTypeReward > 20) {
- correctShowOpenArtifact = 3;
- } else {
- correctShowOpenArtifact = 0;
- }
-
- /**
- * Sum the result of opening Pet Eggs
- * Суммирование результата открытия яиц питомцев
- */
- if (isChecked('countControl') && call.ident == callsIdent['pet_chestOpen']) {
- const rewards = call.result.response.rewards;
- if (rewards.length > 10) {
- /**
- * Removing pet cards
- * Убираем карточки петов
- */
- for (const reward of rewards) {
- if (reward.petCard) {
- delete reward.petCard;
- }
- }
- }
- rewards.forEach(e => {
- for (let f in e) {
- if (!allReward[f]) {
- allReward[f] = {};
- }
- for (let o in e[f]) {
- if (!allReward[f][o]) {
- allReward[f][o] = e[f][o];
- } else {
- allReward[f][o] += e[f][o];
- }
- }
- }
- });
- call.result.response.rewards = [allReward];
- isChange = true;
+ call.result.response.rewards = [allReward];
+ this._isChangeResponse = true;
}
/**
* Removing titan cards
@@ -3158,7 +3373,7 @@
delete reward.titanCard;
}
}
- isChange = true;
+ this._isChangeResponse = true;
}
}
/**
@@ -3176,56 +3391,17 @@
if (
newCount &&
(await popup.confirm(`${I18N('BTN_OPEN')} ${newCount} ${I18N('OPEN_DOLLS')}?`, [
- { msg: I18N('BTN_OPEN'), result: true },
- { msg: I18N('BTN_NO'), result: false, isClose: true },
+ { 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);
- isChange = true;
- }
-
- if (this.massOpen) {
- if (
- await popup.confirm(I18N('OPEN_ALL_EQUIP_BOXES'), [
- { msg: I18N('BTN_OPEN'), result: true },
- { msg: I18N('BTN_NO'), result: false, isClose: true },
- ])
- ) {
- const consumable = await Send({ calls: [{ name: 'inventoryGet', args: {}, ident: 'inventoryGet' }] }).then((e) =>
- Object.entries(e.results[0].result.response.consumable)
- );
- const calls = [];
- const deleteItems = {};
- for (const [libId, amount] of consumable) {
- if (libId != this.massOpen && libId >= 362 && libId <= 389) {
- calls.push({
- name: 'consumableUseLootBox',
- args: { libId, amount },
- ident: 'consumableUseLootBox_' + libId,
- });
- deleteItems[libId] = -amount;
- }
- }
- const responses = await Send({ calls }).then((e) => e.results.map((r) => r.result.response).flat());
-
- for (const loot of responses) {
- const [count, result] = Object.entries(loot).pop();
- countLootBox += +count;
-
- mergeItemsObj(lootBox, result);
- }
- isChange = true;
-
- this.onReadySuccess = () => {
- cheats.updateInventory({ consumable: deleteItems });
- cheats.refreshInventory();
- };
- }
+ this._isChangeResponse = true;
}
- if (isChange) {
+ if (this._isChangeResponse) {
call.result.response = {
[countLootBox]: lootBox,
};
@@ -3239,7 +3415,7 @@
lastDungeonBattleData = call.result.response;
lastDungeonBattleStart = Date.now();
}
- /**
+ /**
* Getting the number of prediction cards
* Получение количества карт предсказаний
*/
@@ -3272,9 +3448,9 @@
* Скрытие лишних серверов
*/
if (call.ident == callsIdent['serverGetAll'] && isChecked('hideServers')) {
- let servers = call.result.response.users.map(s => s.serverId)
- call.result.response.servers = call.result.response.servers.filter(s => servers.includes(s.id));
- isChange = true;
+ 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
@@ -3289,7 +3465,7 @@
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) {
@@ -3302,13 +3478,13 @@
* Автоматический запуск рейда при окончании приключения
*/
if (call.ident == callsIdent['adventure_end']) {
- autoRaidAdventure()
+ autoRaidAdventure();
}
/** Удаление лавки редкостей */
if (call.ident == callsIdent['missionRaid']) {
if (call.result?.heroesMerchant) {
delete call.result.heroesMerchant;
- isChange = true;
+ this._isChangeResponse = true;
}
}
/** missionTimer */
@@ -3325,48 +3501,44 @@
calls.push({
name: 'hallOfFameFarmTrophyReward',
args: { trophyId: week, rewardType: 'champion' },
- ident: 'body_champion_' + week,
});
}
if (Object.keys(trophy.clanReward).length && !trophy.clanRewardFarmed) {
calls.push({
name: 'hallOfFameFarmTrophyReward',
args: { trophyId: week, rewardType: 'clan' },
- ident: 'body_clan_' + week,
});
}
}
if (calls.length) {
- Send({ calls })
- .then((e) => e.results.map((e) => e.result.response))
- .then(async results => {
- let coin18 = 0,
- coin19 = 0,
- gold = 0,
- starmoney = 0;
- for (const r of results) {
- coin18 += r?.coin ? +r.coin[18] : 0;
- coin19 += r?.coin ? +r.coin[19] : 0;
- gold += r?.gold ? +r.gold : 0;
- starmoney += r?.starmoney ? +r.starmoney : 0;
- }
+ 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} `;
- }
+ 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} `;
+ }
- await popup.confirm(msg, [{ msg: I18N('BTN_OK'), result: 0 }]);
- });
+ return popup.confirm(msg, [{ msg: I18N('BTN_OK'), result: 0, color: 'green' }]);
+ });
}
}
if (call.ident == callsIdent['clanDomination_getInfo']) {
@@ -3397,7 +3569,7 @@
needBuff: pack.buff,
haveBuff: invasionInfo.buff,
}),
- false
+ false,
);
}
}
@@ -3418,7 +3590,7 @@
needBuff: pack.buff,
haveBuff: invasionInfo.buff,
}),
- false
+ false,
);
}
}
@@ -3508,25 +3680,18 @@
userPositions[townPos.userId] = townPos.position;
}
}
- isChange = true;
+ this._isChangeResponse = true;
}
*/
- }
-
- if (mainReward && artifactChestOpen) {
- console.log(allReward);
- mainReward[artifactChestOpenCallName == 'artifactChestOpen' ? 'chestReward' : 'reward'] = [allReward];
- artifactChestOpen = false;
- artifactChestOpenCallName = '';
- isChange = true;
+ Events.emit('checkChangeResponse', this, call, callsIdent);
}
} catch(err) {
console.log("Request(response, " + this.uniqid + "):\n", "Error:\n", response, err);
}
- if (isChange) {
+ if (this._isChangeResponse) {
Object.defineProperty(this, 'responseText', {
- writable: true
+ writable: true,
});
this.responseText = JSON.stringify(respond);
}
@@ -3538,20 +3703,24 @@
* Запрос ответа на вопрос
*/
async function getAnswer(question) {
- // c29tZSBzdHJhbmdlIHN5bWJvbHM=
+ // eW91dHUuYmUvZFF3NHc5V2dYY1E=
const quizAPI = new ZingerYWebsiteAPI('getAnswer.php', arguments, { question });
- return new Promise((resolve, reject) => {
- quizAPI.request().then((data) => {
+ 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);
+ })
+ .catch((error) => {
+ //console.error(error);
+ const reason = error.message == 'Access denied' ? error.message : 'Error';
+ reject(reason);
});
- })
+ });
}
/**
@@ -3560,13 +3729,19 @@
* Отправка вопроса и ответа в базу данных
*/
function sendAnswerInfo(answerInfo) {
- // c29tZSBub25zZW5zZQ==
+ // MTIzNDU2Nzg5MA==
const quizAPI = new ZingerYWebsiteAPI('setAnswer.php', arguments, { answerInfo });
- quizAPI.request().then((data) => {
- if (data.result) {
- console.log(I18N('SENT_QUESTION'));
- }
- });
+ quizAPI
+ .request()
+ .then((data) => {
+ if (data.result) {
+ console.log(I18N('SENT_QUESTION'));
+ }
+ })
+ .catch((error) => {
+ console.error(error);
+ const reason = error.message == 'Access denied' ? error.message : 'Error';
+ });
}
/**
@@ -3588,6 +3763,7 @@
case 'brawl_titan':
case 'challenge_titan':
case 'titan_mission':
+ case 'epic_brawl_titan':
return 'get_titanPvpManual';
case 'clan_raid': // Asgard Boss // Босс асгарда
case 'adventure': // Adventures // Приключения
@@ -3618,6 +3794,9 @@
return 'get_core';
default: {
if (strBattleType.includes('invasion')) {
+ if (strBattleType.includes('titan')) {
+ return 'get_invasionTitan';
+ }
return 'get_invasion';
}
if (strBattleType.includes('boss')) {
@@ -3664,6 +3843,9 @@
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);
}
@@ -3798,7 +3980,7 @@
}
}
- let extintionsList = [];
+ const extentionsList = [];
/**
* Creates an interface
*
@@ -3811,14 +3993,15 @@
scriptMenu.init();
scriptMenu.addHeader(GM_info.script.name, justInfo);
const versionHeader = scriptMenu.addHeader('v' + GM_info.script.version);
- if (extintionsList.length) {
+ const { extentionsList } = HWHData;
+ if (extentionsList.length) {
versionHeader.title = '';
versionHeader.style.color = 'red';
- for (const extintion of extintionsList) {
- const { name, ver, author } = extintion;
+ for (const extention of extentionsList) {
+ const { name, ver, author } = extention;
versionHeader.title += name + ', v' + ver + ' by ' + author + '\n';
}
- versionHeader.innerText += ` [${extintionsList.length}]`;
+ versionHeader.innerText += ` [${extentionsList.length}]`;
}
// AutoClicker
const hkm = new HotkeyManager();
@@ -3845,7 +4028,8 @@
}
function addExtentionName(name, ver, author) {
- extintionsList.push({
+ const { extentionsList } = HWHData;
+ extentionsList.push({
name,
ver,
author,
@@ -4013,7 +4197,6 @@
function hideProgress(timeout) {
const { ScriptMenu } = HWHClasses;
const scriptMenu = ScriptMenu.getInst();
- timeout = timeout || 0;
clearTimeout(hideTimeoutProgress);
hideTimeoutProgress = setTimeout(function () {
scriptMenu.setStatus('');
@@ -4030,7 +4213,10 @@
scriptMenu.setStatus(text, onclick);
hide = hide || false;
if (hide) {
- hideProgress(3000);
+ if (typeof hide != 'number') {
+ hide = 3000;
+ }
+ hideProgress(hide);
}
}
@@ -4045,6 +4231,15 @@
scriptMenu.addStatus(text);
}
+ /**
+ * Check Valkyrie's Blessing subscription activity
+ *
+ * Проверяет активность подписки на Благославление валькирии
+ */
+ function isSubActive() {
+ return subEndTime > Date.now();
+ }
+
/**
* Returns the timer value depending on the subscription
*
@@ -4052,7 +4247,7 @@
*/
function getTimer(time, div) {
let speedDiv = 5;
- if (subEndTime < Date.now()) {
+ if (!isSubActive()) {
speedDiv = div || 1.5;
}
return Math.max(Math.ceil(time / speedDiv + 1.5), 4);
@@ -4082,6 +4277,7 @@
hideProgress,
setProgress,
addProgress,
+ isSubActive,
getTimer,
addExtentionName,
getUserInfo,
@@ -4104,6780 +4300,6282 @@
countPredictionCard,
actionsPopupButtons,
othersPopupButtons,
+ /**
+ * @deprecated Use extentionsList
+ * @see extentionsList
+ */
+ extintionsList: extentionsList,
+ extentionsList,
};
/**
- * Calculates HASH MD5 from string
- *
- * Расчитывает HASH MD5 из строки
+ * Game Library
*
- * [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 _}))}();
-
- class Caller {
- static globalHooks = {
- onError: null,
- };
+ class Library {
+ defaultLibUrl = 'https://heroesru-a.akamaihd.net/vk/v1101/lib/lib.json';
- constructor(calls = null) {
- this.calls = [];
- this.results = {};
- this.sideResults = {};
- if (calls) {
- this.add(calls);
+ constructor() {
+ if (!Library.instance) {
+ Library.instance = this;
}
+
+ return Library.instance;
}
- static setGlobalHook(event, callback) {
- if (this.globalHooks[event] !== undefined) {
- this.globalHooks[event] = callback;
- } else {
- throw new Error(`Unknown event: ${event}`);
+ 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)
}
}
- addCall(call) {
- const { name = call, args = {} } = typeof call === 'object' ? call : { name: call };
- this.calls.push({ name, args });
- return this;
+ 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) {}
}
- add(name) {
- if (Array.isArray(name)) {
- name.forEach((call) => this.addCall(call));
- } else {
- this.addCall(name);
- }
- return this;
+ getData(id) {
+ return this.data[id];
}
- handleError(error) {
- const errorName = error.name;
- const errorDescription = error.description;
-
- if (Caller.globalHooks.onError) {
- const shouldThrow = Caller.globalHooks.onError(error);
- if (shouldThrow === false) {
- return;
- }
- }
-
- if (error.call) {
- const callInfo = error.call;
- throw new Error(`${errorName} in ${callInfo.name}: ${errorDescription}\n` + `Args: ${JSON.stringify(callInfo.args)}\n`);
- } else if (errorName === 'common\\rpc\\exception\\InvalidRequest') {
- throw new Error(`Invalid request: ${errorDescription}`);
- } else {
- throw new Error(`Unknown error: ${errorName} - ${errorDescription}`);
- }
+ setData(data) {
+ this.data = data;
}
+ }
- async send() {
- if (!this.calls.length) {
- throw new Error('No calls to send.');
- }
-
- const identToNameMap = {};
- const callsWithIdent = this.calls.map((call, index) => {
- const ident = this.calls.length === 1 ? 'body' : `group_${index}_body`;
- identToNameMap[ident] = call.name;
- return { ...call, ident };
- });
+ this.lib = new Library();
+ /**
+ * Database
+ *
+ * База данных
+ */
+ class Database {
+ constructor(dbName, storeName) {
+ this.dbName = dbName;
+ this.storeName = storeName;
+ this.db = null;
+ }
- try {
- const response = await Send({ calls: callsWithIdent });
+ async open() {
+ return new Promise((resolve, reject) => {
+ const request = indexedDB.open(this.dbName);
- if (response.error) {
- this.handleError(response.error);
- }
+ request.onerror = () => {
+ reject(new Error(`Failed to open database ${this.dbName}`));
+ };
- if (!response.results) {
- throw new Error('Invalid response format: missing "results" field');
- }
+ request.onsuccess = () => {
+ this.db = request.result;
+ resolve();
+ };
- response.results.forEach((result) => {
- const name = identToNameMap[result.ident];
- if (!this.results[name]) {
- this.results[name] = [];
- this.sideResults[name] = [];
- }
- this.results[name].push(result.result.response);
- const sideResults = {};
- for (const key of Object.keys(result.result)) {
- if (key === 'response') continue;
- sideResults[key] = result.result[key];
+ request.onupgradeneeded = (event) => {
+ const db = event.target.result;
+ if (!db.objectStoreNames.contains(this.storeName)) {
+ db.createObjectStore(this.storeName);
}
- this.sideResults[name].push(sideResults);
- });
- } catch (error) {
- throw error;
- }
- return this;
+ };
+ });
}
- result(name, forceArray = false) {
- const results = name ? this.results[name] || [] : Object.values(this.results).flat();
- return forceArray || results.length !== 1 ? results : results[0];
- }
+ async 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);
- sideResult(name, forceArray = false) {
- const results = name ? this.sideResults[name] || [] : Object.values(this.sideResults).flat();
- return forceArray || results.length !== 1 ? results : results[0];
+ request.onerror = () => {
+ reject(new Error(`Failed to save value with key ${key}`));
+ };
+
+ request.onsuccess = () => {
+ resolve();
+ };
+ });
}
- async execute(name) {
- try {
- await this.send();
- return this.result(name);
- } catch (error) {
- throw error;
- }
- }
+ 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);
- clear() {
- this.calls = [];
- this.results = {};
- return this;
- }
+ request.onerror = () => {
+ resolve(def);
+ };
- isEmpty() {
- return this.calls.length === 0 && Object.keys(this.results).length === 0;
+ request.onsuccess = () => {
+ resolve(request.result);
+ };
+ });
}
- static async send(calls) {
- return new Caller(calls).execute();
+ 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();
+ };
+ });
}
}
- this.Caller = Caller;
-
- /*
- // Примеры использования
- (async () => {
- // Короткий вызов
- await new Caller('inventoryGet').execute();
- // Простой вызов
- let result = await new Caller().add('inventoryGet').execute();
- console.log('Inventory Get Result:', result);
+ /**
+ * Returns the stored value
+ *
+ * Возвращает сохраненное значение
+ */
+ function getSaveVal(saveName, def) {
+ const result = storage.get(saveName, def);
+ return result;
+ }
+ this.HWHFuncs.getSaveVal = getSaveVal;
- // Сложный вызов
- let caller = new Caller();
- await caller
- .add([
- {
- name: 'inventoryGet',
- args: {},
- },
- {
- name: 'heroGetAll',
- args: {},
- },
- ])
- .send();
- console.log('Inventory Get Result:', caller.result('inventoryGet'));
- console.log('Hero Get All Result:', caller.result('heroGetAll'));
+ /**
+ * Stores value
+ *
+ * Сохраняет значение
+ */
+ function setSaveVal(saveName, value) {
+ storage.set(saveName, value);
+ }
+ this.HWHFuncs.setSaveVal = setSaveVal;
- // Очистка всех данных
- caller.clear();
- })();
- */
+ /**
+ * Database initialization
+ *
+ * Инициализация базы данных
+ */
+ const db = new Database(GM_info.script.name, 'settings');
/**
- * Script for beautiful dialog boxes
+ * Data store
*
- * Скрипт для красивых диалоговых окошек
+ * Хранилище данных
*/
- const popup = new (function () {
- this.popUp, this.downer, this.custom, this.middle, this.msgText, (this.buttons = []);
- this.checkboxes = [];
- this.dialogPromice = null;
- this.isInit = false;
+ 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];
+ },
+ };
- this.init = function () {
- if (this.isInit) {
- return;
- }
- addStyle();
- addBlocks();
- addEventListeners();
- this.isInit = true;
+ /**
+ * 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;
+ }
- const addEventListeners = () => {
- document.addEventListener('keyup', (e) => {
- if (e.key == 'Escape') {
- if (this.dialogPromice) {
- const { func, result } = this.dialogPromice;
- this.dialogPromice = null;
- popup.hide();
- func(result);
- }
- }
- });
+ /**
+ * 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);
- const addStyle = () => {
- let style = document.createElement('style');
- style.innerText = `
- .PopUp_ {
- position: fixed;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
- min-width: 300px;
- max-width: 80%;
- max-height: 80%;
- background-color: #190e08e6;
- z-index: 10001;
- border: 3px #ce9767 solid;
- border-radius: 10px;
- display: flex;
- flex-direction: column;
- justify-content: space-around;
- padding: 15px 9px;
- box-sizing: border-box;
+ if (settings) {
+ storage.values = settings;
+ return;
}
- .PopUp_back {
- position: absolute;
- background-color: #00000066;
- width: 100%;
- height: 100%;
- z-index: 10000;
- top: 0;
- left: 0;
+ 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);
+ }
- .PopUp_close {
- width: 40px;
- height: 40px;
- position: absolute;
- right: -18px;
- top: -18px;
- border: 3px solid #c18550;
- border-radius: 20px;
- background: radial-gradient(circle, rgba(190,30,35,1) 0%, rgba(0,0,0,1) 100%);
- background-position-y: 3px;
- box-shadow: -1px 1px 3px black;
- cursor: pointer;
- box-sizing: border-box;
- }
+ /**
+ * Миксин EventEmitter
+ * @param {Class} BaseClass Базовый класс (по умолчанию Object)
+ * @returns {Class} Класс с методами EventEmitter
+ */
+ const EventEmitterMixin = (BaseClass = Object) =>
+ class EventEmitter extends BaseClass {
+ constructor(...args) {
+ super(...args);
+ this._events = new Map();
+ }
- .PopUp_close:hover {
- filter: brightness(1.2);
- }
+ /**
+ * Подписаться на событие
+ * @param {string} event Имя события
+ * @param {function} listener Функция-обработчик
+ * @returns {this} Возвращает экземпляр для чейнинга
+ */
+ on(event, listener) {
+ if (typeof listener !== 'function') {
+ throw new TypeError('Listener must be a function');
+ }
- .PopUp_crossClose {
- width: 100%;
- height: 100%;
- background-size: 65%;
- background-position: center;
- background-repeat: no-repeat;
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%23f4cd73' d='M 0.826 12.559 C 0.431 12.963 3.346 15.374 3.74 14.97 C 4.215 15.173 8.167 10.457 7.804 10.302 C 7.893 10.376 11.454 14.64 11.525 14.372 C 12.134 15.042 15.118 12.086 14.638 11.689 C 14.416 11.21 10.263 7.477 10.402 7.832 C 10.358 7.815 11.731 7.101 14.872 3.114 C 14.698 2.145 13.024 1.074 12.093 1.019 C 11.438 0.861 8.014 5.259 8.035 5.531 C 7.86 5.082 3.61 1.186 3.522 1.59 C 2.973 1.027 0.916 4.611 1.17 4.873 C 0.728 4.914 5.088 7.961 5.61 7.995 C 5.225 7.532 0.622 12.315 0.826 12.559 Z'/%3e%3c/svg%3e")
- }
+ if (!this._events.has(event)) {
+ this._events.set(event, new Set());
+ }
+ this._events.get(event).add(listener);
+ return this;
+ }
- .PopUp_blocks {
- width: 100%;
- height: 50%;
- display: flex;
- justify-content: space-evenly;
- align-items: center;
- flex-wrap: wrap;
- justify-content: center;
- }
+ /**
+ * Отписаться от события
+ * @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;
+ }
- .PopUp_blocks:last-child {
- margin-top: 25px;
- }
+ /**
+ * Вызвать событие
+ * @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);
+ }
+ });
- .PopUp_buttons {
- display: flex;
- margin: 7px 10px;
- flex-direction: column;
- }
+ return true;
+ }
- .PopUp_button {
- background-color: #52A81C;
- border-radius: 5px;
- box-shadow: inset 0px -4px 10px, inset 0px 3px 2px #99fe20, 0px 0px 4px, 0px -3px 1px #d7b275, 0px 0px 0px 3px #ce9767;
- cursor: pointer;
- padding: 4px 12px 6px;
- }
+ /**
+ * Подписаться на событие один раз
+ * @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);
+ }
- .PopUp_input {
- text-align: center;
- font-size: 16px;
- height: 27px;
- border: 1px solid #cf9250;
- border-radius: 9px 9px 0px 0px;
- background: transparent;
- color: #fce1ac;
- padding: 1px 10px;
- box-sizing: border-box;
- box-shadow: 0px 0px 4px, 0px 0px 0px 3px #ce9767;
- }
+ /**
+ * Удалить все обработчики для события
+ * @param {string} [event] Имя события (если не указано - очистить все)
+ * @returns {this} Возвращает экземпляр для чейнинга
+ */
+ removeAllListeners(event) {
+ if (event) {
+ this._events.delete(event);
+ } else {
+ this._events.clear();
+ }
+ return this;
+ }
- .PopUp_checkboxes {
- display: flex;
- flex-direction: column;
- margin: 15px 15px -5px 15px;
- align-items: flex-start;
- }
+ /**
+ * Получить количество обработчиков для события
+ * @param {string} event Имя события
+ * @returns {number} Количество обработчиков
+ */
+ listenerCount(event) {
+ return this._events.has(event) ? this._events.get(event).size : 0;
+ }
+ };
- .PopUp_ContCheckbox {
- margin: 2px 0px;
- }
+ this.HWHFuncs.EventEmitterMixin = EventEmitterMixin;
- .PopUp_checkbox {
- position: absolute;
- z-index: -1;
- opacity: 0;
- }
- .PopUp_checkbox+label {
- display: inline-flex;
- align-items: center;
- user-select: none;
+ class GlobalEventHub extends EventEmitterMixin() {}
+ const Events = new GlobalEventHub();
+ this.HWHFuncs.Events = Events;
- font-size: 15px;
- font-family: sans-serif;
- font-weight: 600;
- font-stretch: condensed;
- letter-spacing: 1px;
- color: #fce1ac;
- text-shadow: 0px 0px 1px;
- }
- .PopUp_checkbox+label::before {
- content: '';
- display: inline-block;
- width: 20px;
- height: 20px;
- border: 1px solid #cf9250;
- border-radius: 7px;
- margin-right: 7px;
- }
- .PopUp_checkbox:checked+label::before {
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2388cb13' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e");
- }
+ class TaskManager {
+ isConsoleLog = false;
+ functionRegistry = {};
- .PopUp_input::placeholder {
- color: #fce1ac75;
- }
+ constructor() {
+ if (!TaskManager.inst) {
+ console.log('intiTaskManager timeout: 1');
+ this.tasks = new Map();
+ this.setTimeout(1);
+ TaskManager.inst = this;
+ }
- .PopUp_input:focus {
- outline: 0;
+ return TaskManager.inst;
}
- .PopUp_input + .PopUp_button {
- border-radius: 0px 0px 5px 5px;
- padding: 2px 18px 5px;
- }
+ 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);
+ }
- .PopUp_button:hover {
- filter: brightness(1.2);
+ 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();
}
- .PopUp_button:active {
- box-shadow: inset 0px 5px 10px, inset 0px 1px 2px #99fe20, 0px 0px 4px, 0px -3px 1px #d7b275, 0px 0px 0px 3px #ce9767;
+ 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);
}
- .PopUp_text {
- font-size: 22px;
- font-family: sans-serif;
- font-weight: 600;
- font-stretch: condensed;
- letter-spacing: 1px;
- text-align: center;
+ 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;
}
- .PopUp_buttonText {
- color: #E4FF4C;
- text-shadow: 0px 1px 2px black;
+ executeError(error, task) {
+ console.error('Task error:', error);
+ console.log('Faulty task:', task);
}
- .PopUp_msgText {
- color: #FDE5B6;
- text-shadow: 0px 0px 2px;
+ 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();
}
- .PopUp_hideBlock {
- display: none;
+ registerFunction(name, fn) {
+ if (name in this.functionRegistry) {
+ console.log('Функция с таким именем уже есть');
+ return false;
+ }
+ this.functionRegistry[name] = fn;
+ return true;
}
- .PopUp_Container {
- max-height: 80vh;
- overflow-y: auto;
- overflow-x: hidden;
- scrollbar-width: thin;
- scrollbar-color: #774d10 #05040300;
- padding: 0 1rem;
+ async sleep() {
+ return new Promise((r) => {
+ this.worker.postMessage(this.timeout);
+ this.worker.onmessage = r;
+ });
}
- `;
- document.head.appendChild(style);
+
+ static add(execute, executeAt, repeat) {
+ new TaskManager().addTask({ execute, executeAt, repeat });
+ return task;
}
+ }
- const addBlocks = () => {
- this.back = document.createElement('div');
- this.back.classList.add('PopUp_back');
- this.back.classList.add('PopUp_hideBlock');
- document.body.append(this.back);
+ this.HWHClasses.TaskManager = TaskManager;
- this.popUp = document.createElement('div');
- this.popUp.classList.add('PopUp_');
- this.back.append(this.popUp);
- let upper = document.createElement('div')
- upper.classList.add('PopUp_blocks');
- this.popUp.append(upper);
+ /**
+ * 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 _}))}();
- this.middle = document.createElement('div')
- this.middle.classList.add('PopUp_blocks');
- this.middle.classList.add('PopUp_checkboxes');
- this.popUp.append(this.middle);
+ class MinimalVirtualInput {
+ static #currentHandler = null;
+ static #getValue = null;
+ static #setValue = null;
- this.custom = document.createElement('div');
- this.custom.classList.add('PopUp_Container');
- this.popUp.append(this.custom);
+ // Метод для установки обработчика с новыми колбэками
+ static addKeyEvent({ getValue, setValue }) {
+ // Сохраняем новые колбэки
+ MinimalVirtualInput.#getValue = getValue;
+ MinimalVirtualInput.#setValue = setValue;
- this.downer = document.createElement('div')
- this.downer.classList.add('PopUp_blocks');
- this.popUp.append(this.downer);
+ // Удаляем старый обработчик если был
+ MinimalVirtualInput.removeKeyEvent();
- this.msgText = document.createElement('div');
- this.msgText.classList.add('PopUp_text', 'PopUp_msgText');
- upper.append(this.msgText);
+ // Устанавливаем новый обработчик
+ MinimalVirtualInput.#currentHandler = MinimalVirtualInput.#handleKeyEvent.bind(MinimalVirtualInput);
+ document.addEventListener('keydown', MinimalVirtualInput.#currentHandler);
}
- this.showBack = function () {
- this.back.classList.remove('PopUp_hideBlock');
+ // Метод для удаления обработчика
+ static removeKeyEvent() {
+ if (MinimalVirtualInput.#currentHandler) {
+ document.removeEventListener('keydown', MinimalVirtualInput.#currentHandler);
+ MinimalVirtualInput.#currentHandler = null;
+ }
}
- this.hideBack = function () {
- this.back.classList.add('PopUp_hideBlock');
- }
-
- this.show = function () {
- if (this.checkboxes.length) {
- this.middle.classList.remove('PopUp_hideBlock');
+ static #handleKeyEvent(event) {
+ if (!MinimalVirtualInput.#getValue || !MinimalVirtualInput.#setValue) {
+ return;
}
- this.showBack();
- this.popUp.classList.remove('PopUp_hideBlock');
- }
- this.hide = function () {
- this.hideBack();
- this.popUp.classList.add('PopUp_hideBlock');
- }
+ const key = event.key;
+ // Преобразуем число в строку для работы с символами
+ const currentValue = MinimalVirtualInput.#getValue().toString();
- this.addAnyButton = (option) => {
- const contButton = document.createElement('div');
- contButton.classList.add('PopUp_buttons');
- this.downer.append(contButton);
+ switch (key) {
+ case 'Backspace':
+ // Удаляем последний символ
+ if (currentValue.length > 0) {
+ const newValueString = currentValue.slice(0, -1);
+ // Преобразуем обратно в число, если строка не пустая
+ const newValueNumber = newValueString === '' ? 0 : Number(newValueString);
+ MinimalVirtualInput.#setValue(isNaN(newValueNumber) ? 0 : newValueNumber);
+ }
+ break;
+ case 'Delete':
+ // Очищаем значение
+ MinimalVirtualInput.#setValue(0);
+ MinimalVirtualInput.#setValue(0);
+ break;
- let inputField = {
- value: option.result || option.default
- }
- if (option.isInput) {
- inputField = document.createElement('input');
- inputField.type = 'text';
- if (option.placeholder) {
- inputField.placeholder = option.placeholder;
- }
- if (option.default) {
- inputField.value = option.default;
- }
- inputField.classList.add('PopUp_input');
- contButton.append(inputField);
+ case 'v':
+ if (event.ctrlKey || event.metaKey) {
+ navigator.clipboard?.readText().then((text) => {
+ if (text) {
+ // Вставляем текст в конец и преобразуем в число
+ const newValueString = currentValue + text;
+ const newValueNumber = Number(newValueString);
+ MinimalVirtualInput.#setValue(isNaN(newValueNumber) ? 0 : newValueNumber);
+ }
+ });
+ event.preventDefault();
+ return;
+ }
+ // Если не Ctrl+V, то обрабатываем как обычный символ
+ default:
+ // Проверяем что символ - цифра
+ if (key.length === 1 && !event.ctrlKey && !event.metaKey && /^\d$/.test(key)) {
+ const newValueString = currentValue + key;
+ const newValueNumber = Number(newValueString);
+ MinimalVirtualInput.#setValue(isNaN(newValueNumber) ? 0 : newValueNumber);
+ }
+ break;
}
- const button = document.createElement('div');
- button.classList.add('PopUp_button');
- button.title = option.title || '';
- contButton.append(button);
+ event.preventDefault();
+ }
+ }
- const buttonText = document.createElement('div');
- buttonText.classList.add('PopUp_text', 'PopUp_buttonText');
- buttonText.innerHTML = option.msg;
- button.append(buttonText);
+ function hackGame() {
+ const self = this;
+ selfGame = null;
+ bindId = 1e9;
+ this.libGame = null;
+ this.doneLibLoad = () => {};
- return { button, contButton, inputField };
- }
+ /**
+ * 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' },
- this.addCloseButton = () => {
- let button = document.createElement('div')
- button.classList.add('PopUp_close');
- this.popUp.append(button);
+ { 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' },
+ ];
- let crossClose = document.createElement('div')
- crossClose.classList.add('PopUp_crossClose');
- button.append(crossClose);
+ /**
+ * 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;
+ },
+ };
- return { button, contButton: button };
+ /**
+ * 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 + '_'];
+ },
+ });
+ }
}
- this.addButton = (option, buttonClick) => {
-
- const { button, contButton, inputField } = option.isClose ? this.addCloseButton() : this.addAnyButton(option);
- if (option.isClose) {
- this.dialogPromice = { func: buttonClick, result: option.result };
+ /**
+ * 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);
}
- button.addEventListener('click', () => {
- let result = '';
- if (option.isInput) {
- result = inputField.value;
- }
- if (option.isClose || option.isCancel) {
- this.dialogPromice = null;
+ 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;
}
- buttonClick(result);
+ callback({
+ battleLogs,
+ battleTime,
+ battleTimer,
+ battleData,
+ progress: battleResults[getF(Game.MultiBattleResult, 'get_progress')](),
+ result: battleResults[getF(Game.MultiBattleResult, 'get_result')](),
+ });
});
+ battleInstantPlay.start();
+ };
- this.buttons.push(contButton);
- }
-
- this.clearButtons = () => {
- while (this.buttons.length) {
- this.buttons.pop().remove();
+ /**
+ * 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];
}
}
- this.addCheckBox = (checkBox) => {
- const contCheckbox = document.createElement('div');
- contCheckbox.classList.add('PopUp_ContCheckbox');
- this.middle.append(contCheckbox);
-
- const checkbox = document.createElement('input');
- checkbox.type = 'checkbox';
- checkbox.id = 'PopUpCheckbox' + this.checkboxes.length;
- checkbox.dataset.name = checkBox.name;
- checkbox.checked = checkBox.checked;
- checkbox.label = checkBox.label;
- checkbox.title = checkBox.title || '';
- checkbox.classList.add('PopUp_checkbox');
- contCheckbox.appendChild(checkbox)
-
- const checkboxLabel = document.createElement('label');
- checkboxLabel.innerText = checkBox.label;
- checkboxLabel.title = checkBox.title || '';
- checkboxLabel.setAttribute('for', checkbox.id);
- contCheckbox.appendChild(checkboxLabel);
-
- this.checkboxes.push(checkbox);
+ /**
+ * 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];
}
- this.clearCheckBox = () => {
- this.middle.classList.add('PopUp_hideBlock');
- while (this.checkboxes.length) {
- this.checkboxes.pop().parentNode.remove();
- }
+ /**
+ * 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];
}
- this.clearCustomBlock = () => {
- this.custom.innerHTML = '';
- };
-
- this.setMsgText = (text) => {
- this.msgText.innerHTML = text;
+ /**
+ * 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];
}
- this.getCheckBoxes = () => {
- const checkBoxes = [];
-
- for (const checkBox of this.checkboxes) {
- checkBoxes.push({
- name: checkBox.dataset.name,
- label: checkBox.label,
- checked: checkBox.checked
- });
- }
-
- return checkBoxes;
+ 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;
}
-
- this.confirm = async (msg, buttOpt, checkBoxes = []) => {
- if (!this.isInit) {
- this.init();
- }
- this.clearButtons();
- this.clearCheckBox();
- this.clearCustomBlock();
- return new Promise((complete, failed) => {
- this.setMsgText(msg);
- if (!buttOpt) {
- buttOpt = [{ msg: 'Ok', result: true, isInput: false }];
- }
- for (const checkBox of checkBoxes) {
- this.addCheckBox(checkBox);
- }
- for (let butt of buttOpt) {
- this.addButton(butt, (result) => {
- result = result || butt.result;
- complete(result);
- popup.hide();
- });
- if (butt.isCancel) {
- this.dialogPromice = { func: complete, result: butt.result };
+ /**
+ * 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;
}
- }
- this.show();
- });
- }
-
- this.customPopup = async (customFunc) => {
- if (!this.isInit) {
- this.init();
- }
- this.clearButtons();
- this.clearCheckBox();
- this.clearCustomBlock();
- return new Promise((complete, failed) => {
- customFunc(complete);
- });
- };
- });
-
- this.HWHFuncs.popup = popup;
-
- /**
- * Миксин 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;
- }
+ try {
+ this[getProtoFn(Game.PlayerMissionData, 9)] = new Game.PlayerMissionBattle(a, b, c);
- /**
- * Отписаться от события
- * @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);
+ 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);
}
- }
- 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) => {
+ 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 {
- listener.apply(this, args);
- } catch (e) {
- console.error(`Error in event handler for "${event}":`, e);
+ 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);
}
- });
-
- 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;
-
- /**
- * Script control panel
- *
- * Панель управления скриптом
- */
- class ScriptMenu extends EventEmitterMixin() {
- constructor() {
- if (ScriptMenu.instance) {
- return ScriptMenu.instance;
- }
- super();
- this.mainMenu = null;
- this.buttons = [];
- this.checkboxes = [];
- this.option = {
- showMenu: true,
- showDetails: {},
- };
- ScriptMenu.instance = this;
- return this;
- }
-
- static getInst() {
- if (!ScriptMenu.instance) {
- new ScriptMenu();
- }
- return ScriptMenu.instance;
- }
-
- init(option = {}) {
- this.emit('beforeInit', option);
- this.option = Object.assign(this.option, option);
- const saveOption = this.loadSaveOption();
- this.option = Object.assign(this.option, saveOption);
- this.addStyle();
- this.addBlocks();
- this.emit('afterInit', option);
- }
-
- addStyle() {
- const style = document.createElement('style');
- style.innerText = `
- .scriptMenu_status {
- position: absolute;
- z-index: 10001;
- top: -1px;
- left: 30%;
- cursor: pointer;
- border-radius: 0px 0px 10px 10px;
- background: #190e08e6;
- border: 1px #ce9767 solid;
- font-size: 18px;
- font-family: sans-serif;
- font-weight: 600;
- font-stretch: condensed;
- letter-spacing: 1px;
- color: #fce1ac;
- text-shadow: 0px 0px 1px;
- transition: 0.5s;
- padding: 2px 10px 3px;
- }
- .scriptMenu_statusHide {
- top: -35px;
- height: 30px;
- overflow: hidden;
- }
- .scriptMenu_label {
- position: absolute;
- top: 30%;
- left: -4px;
- z-index: 9999;
- cursor: pointer;
- width: 30px;
- height: 30px;
- background: radial-gradient(circle, #47a41b 0%, #1a2f04 100%);
- border: 1px solid #1a2f04;
- border-radius: 5px;
- box-shadow:
- inset 0px 2px 4px #83ce26,
- inset 0px -4px 6px #1a2f04,
- 0px 0px 2px black,
- 0px 0px 0px 2px #ce9767;
- }
- .scriptMenu_label:hover {
- filter: brightness(1.2);
- }
- .scriptMenu_arrowLabel {
- width: 100%;
- height: 100%;
- background-size: 75%;
- background-position: center;
- background-repeat: no-repeat;
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%2388cb13' d='M7.596 7.304a.802.802 0 0 1 0 1.392l-6.363 3.692C.713 12.69 0 12.345 0 11.692V4.308c0-.653.713-.998 1.233-.696l6.363 3.692Z'/%3e%3cpath fill='%2388cb13' d='M15.596 7.304a.802.802 0 0 1 0 1.392l-6.363 3.692C8.713 12.69 8 12.345 8 11.692V4.308c0-.653.713-.998 1.233-.696l6.363 3.692Z'/%3e%3c/svg%3e");
- box-shadow: 0px 1px 2px #000;
- border-radius: 5px;
- filter: drop-shadow(0px 1px 2px #000D);
- }
- .scriptMenu_main {
- position: absolute;
- max-width: 285px;
- z-index: 9999;
- top: 50%;
- transform: translateY(-40%);
- background: #190e08e6;
- border: 1px #ce9767 solid;
- border-radius: 0px 10px 10px 0px;
- border-left: none;
- box-sizing: border-box;
- font-size: 15px;
- font-family: sans-serif;
- font-weight: 600;
- font-stretch: condensed;
- letter-spacing: 1px;
- color: #fce1ac;
- text-shadow: 0px 0px 1px;
- transition: 1s;
- }
- .scriptMenu_conteiner {
- max-height: 80vh;
- overflow: scroll;
- scrollbar-width: none; /* Для Firefox */
- -ms-overflow-style: none; /* Для Internet Explorer и Edge */
- display: flex;
- flex-direction: column;
- flex-wrap: nowrap;
- padding: 5px 10px 5px 5px;
- }
- .scriptMenu_conteiner::-webkit-scrollbar {
- display: none; /* Для Chrome, Safari и Opera */
- }
- .scriptMenu_showMenu {
- display: none;
- }
- .scriptMenu_showMenu:checked~.scriptMenu_main {
- left: 0px;
- }
- .scriptMenu_showMenu:not(:checked)~.scriptMenu_main {
- left: -300px;
- }
- .scriptMenu_divInput {
- margin: 2px;
- }
- .scriptMenu_divInputText {
- margin: 2px;
- align-self: center;
- display: flex;
- }
- .scriptMenu_checkbox {
- position: absolute;
- z-index: -1;
- opacity: 0;
- }
- .scriptMenu_checkbox+label {
- display: inline-flex;
- align-items: center;
- user-select: none;
- }
- .scriptMenu_checkbox+label::before {
- content: '';
- display: inline-block;
- width: 20px;
- height: 20px;
- border: 1px solid #cf9250;
- border-radius: 7px;
- margin-right: 7px;
- }
- .scriptMenu_checkbox:checked+label::before {
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2388cb13' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e");
- }
- .scriptMenu_close {
- width: 40px;
- height: 40px;
- position: absolute;
- right: -18px;
- top: -18px;
- border: 3px solid #c18550;
- border-radius: 20px;
- background: radial-gradient(circle, rgba(190,30,35,1) 0%, rgba(0,0,0,1) 100%);
- background-position-y: 3px;
- box-shadow: -1px 1px 3px black;
- cursor: pointer;
- box-sizing: border-box;
- }
- .scriptMenu_close:hover {
- filter: brightness(1.2);
- }
- .scriptMenu_crossClose {
- width: 100%;
- height: 100%;
- background-size: 65%;
- background-position: center;
- background-repeat: no-repeat;
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%23f4cd73' d='M 0.826 12.559 C 0.431 12.963 3.346 15.374 3.74 14.97 C 4.215 15.173 8.167 10.457 7.804 10.302 C 7.893 10.376 11.454 14.64 11.525 14.372 C 12.134 15.042 15.118 12.086 14.638 11.689 C 14.416 11.21 10.263 7.477 10.402 7.832 C 10.358 7.815 11.731 7.101 14.872 3.114 C 14.698 2.145 13.024 1.074 12.093 1.019 C 11.438 0.861 8.014 5.259 8.035 5.531 C 7.86 5.082 3.61 1.186 3.522 1.59 C 2.973 1.027 0.916 4.611 1.17 4.873 C 0.728 4.914 5.088 7.961 5.61 7.995 C 5.225 7.532 0.622 12.315 0.826 12.559 Z'/%3e%3c/svg%3e")
- }
- .scriptMenu_button {
- user-select: none;
- cursor: pointer;
- padding: 5px 14px 8px;
- }
- .scriptMenu_button:hover {
- filter: brightness(1.2);
- }
- .scriptMenu_buttonText {
- color: #fce5b7;
- text-shadow: 0px 1px 2px black;
- text-align: center;
- }
- .scriptMenu_header {
- text-align: center;
- align-self: center;
- font-size: 15px;
- margin: 0px 15px;
- }
- .scriptMenu_header a {
- color: #fce5b7;
- text-decoration: none;
- }
- .scriptMenu_InputText {
- text-align: center;
- width: 130px;
- height: 24px;
- border: 1px solid #cf9250;
- border-radius: 9px;
- background: transparent;
- color: #fce1ac;
- padding: 0px 10px;
- box-sizing: border-box;
- }
- .scriptMenu_InputText:focus {
- filter: brightness(1.2);
- outline: 0;
- }
- .scriptMenu_InputText::placeholder {
- color: #fce1ac75;
- }
- .scriptMenu_Summary {
- cursor: pointer;
- margin-left: 7px;
- }
- .scriptMenu_Details {
- align-self: center;
- }
- .scriptMenu_buttonGroup {
- display: flex;
- justify-content: center;
- user-select: none;
- cursor: pointer;
- padding: 0;
- margin: 3px 0;
- }
- .scriptMenu_buttonGroup .scriptMenu_button {
- width: 100%;
- padding: 5px 8px 8px;
- }
- .scriptMenu_mainButton {
- border-radius: 5px;
- margin: 3px 0;
- }
- .scriptMenu_combineButtonLeft {
- border-top-left-radius: 5px;
- border-bottom-left-radius: 5px;
- margin-right: 2px;
- }
- .scriptMenu_combineButtonCenter {
- border-radius: 0px;
- margin-right: 2px;
- }
- .scriptMenu_combineButtonRight {
- border-top-right-radius: 5px;
- border-bottom-right-radius: 5px;
- }
- .scriptMenu_beigeButton {
- border: 1px solid #442901;
- background: radial-gradient(circle, rgba(165,120,56,1) 80%, rgba(0,0,0,1) 110%);
- box-shadow: inset 0px 2px 4px #e9b282, inset 0px -4px 6px #442901, inset 0px 1px 6px #442901, inset 0px 0px 6px, 0px 0px 2px black, 0px 0px 0px 1px #ce9767;
- }
- .scriptMenu_beigeButton:active {
- box-shadow: inset 0px 4px 6px #442901, inset 0px 4px 6px #442901, inset 0px 0px 6px, 0px 0px 4px, 0px 0px 0px 1px #ce9767;
- }
- .scriptMenu_greenButton {
- border: 1px solid #1a2f04;
- background: radial-gradient(circle, #47a41b 0%, #1a2f04 150%);
- box-shadow: inset 0px 2px 4px #83ce26, inset 0px -4px 6px #1a2f04, 0px 0px 2px black, 0px 0px 0px 1px #ce9767;
- }
- .scriptMenu_greenButton:active {
- box-shadow: inset 0px 4px 6px #1a2f04, inset 0px 4px 6px #1a2f04, inset 0px 0px 6px, 0px 0px 4px, 0px 0px 0px 1px #ce9767;
- }
- .scriptMenu_redButton {
- border: 1px solid #440101;
- background: radial-gradient(circle, rgb(198, 34, 34) 80%, rgb(0, 0, 0) 110%);
- box-shadow: inset 0px 2px 4px #e98282, inset 0px -4px 6px #440101, inset 0px 1px 6px #440101, inset 0px 0px 6px, 0px 0px 2px black, 0px 0px 0px 1px #ce9767;
- }
- .scriptMenu_redButton:active {
- box-shadow: inset 0px 4px 6px #440101, inset 0px 4px 6px #440101, inset 0px 0px 6px, 0px 0px 4px, 0px 0px 0px 1px #ce9767;
- }
- .scriptMenu_attention {
- position: relative;
- }
- .scriptMenu_attention .scriptMenu_dot {
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .scriptMenu_dot {
- position: absolute;
- top: -7px;
- right: -7px;
- width: 20px;
- height: 20px;
- border-radius: 50%;
- border: 1px solid #c18550;
- background: radial-gradient(circle, #f000 25%, black 100%);
- box-shadow: 0px 0px 2px black;
- background-position: 0px -1px;
- font-size: 10px;
- text-align: center;
- color: white;
- text-shadow: 1px 1px 1px black;
- box-sizing: border-box;
- display: none;
- }
- `;
- document.head.appendChild(style);
- }
+ 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'),
+ );
- addBlocks() {
- const main = document.createElement('div');
- document.body.appendChild(main);
+ 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.status = document.createElement('div');
- this.status.classList.add('scriptMenu_status');
- this.setStatus('');
- main.appendChild(this.status);
+ 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);
+ }
+ };
- const label = document.createElement('label');
- label.classList.add('scriptMenu_label');
- label.setAttribute('for', 'checkbox_showMenu');
- main.appendChild(label);
+ 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);
+ }
+ };
+ },
- const arrowLabel = document.createElement('div');
- arrowLabel.classList.add('scriptMenu_arrowLabel');
- label.appendChild(arrowLabel);
+ /**
+ * Acceleration button without Valkyries favor
+ *
+ * Кнопка ускорения без Покровительства Валькирий
+ */
+ battleFastKey: function () {
+ const BGM_45 = getProtoFn(Game.BattleGuiMediator, 45);
+ const oldBattleFastKey = Game.BattleGuiMediator.prototype[BGM_45];
+ Game.BattleGuiMediator.prototype[BGM_45] = 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 checkbox = document.createElement('input');
- checkbox.type = 'checkbox';
- checkbox.id = 'checkbox_showMenu';
- checkbox.checked = this.option.showMenu;
- checkbox.classList.add('scriptMenu_showMenu');
- checkbox.addEventListener('change', () => {
- this.option.showMenu = checkbox.checked;
- this.saveSaveOption();
- });
- main.appendChild(checkbox);
+ 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 Slider = selfGame['feathers.controls.Slider'];
+ const set_minimum = getF(Slider, 'set_minimum', true);
+ const set_step = getF(Slider, 'set_step', true);
+ const BuyItemPopup = selfGame['game.view.popup.shop.buy.BuyItemPopup'];
+ const BIP_4 = getProtoFn(BuyItemPopup, 4);
+ const BIP_2 = getProtoFn(BuyItemPopup, 2);
+ 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_6 = getProtoFn(BuyItemPopupMediator, 6);
+ const BIPM_8 = getProtoFn(BuyItemPopupMediator, 8);
+ const BIPM_10 = getProtoFn(BuyItemPopupMediator, 10);
- const mainMenu = document.createElement('div');
- mainMenu.classList.add('scriptMenu_main');
- main.appendChild(mainMenu);
+ let need = Math.min(this[BTAP_0][BTAM_1](), this[BTAP_0][BIPM_8]);
+ need = need ? need : 60;
+ this[BTAP_0][BIPM_10] = need;
+ this[BTAP_0][BIPM_6] = 10;
+
+ const set_amount = getF(BuyItemPopupMediator, 'set_amount');
+ MinimalVirtualInput.addKeyEvent({
+ getValue: () => this[BTAP_0][BIPM_10],
+ setValue: (value) => {
+ this[BIP_2].set_minimum(1);
+ this[BIP_2].set_step(1);
+ this[BTAP_0][set_amount](Math.min(value, this[BTAP_0][BIPM_8]));
+ },
+ });
+ setProgress(I18N('USE_KEYBOARD'), 3000);
+ }
+ }
+ oldFunc.call(this);
+ };
+ },
+ BuyTitanArtifactClose: function () {
+ const BuyTitanArtifactMediator = selfGame['game.mediator.gui.popup.shop.buy.BuyTitanArtifactItemPopupMediator'];
+ const PopupMediatorBase = selfGame['game.mediator.gui.popup.PopupMediatorBase'];
+ const oldFunc = PopupMediatorBase.prototype.close;
+ PopupMediatorBase.prototype.close = function () {
+ const BTAM_0 = getProtoFn(BuyTitanArtifactMediator, 0);
+ if (this[BTAM_0]) {
+ MinimalVirtualInput.removeKeyEvent();
+ }
+ 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);
+ };
+ },
+ };
- this.mainMenu = document.createElement('div');
- this.mainMenu.classList.add('scriptMenu_conteiner');
- mainMenu.appendChild(this.mainMenu);
+ /**
+ * 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);
+ }
+ }
+ };
- const closeButton = document.createElement('label');
- closeButton.classList.add('scriptMenu_close');
- closeButton.setAttribute('for', 'checkbox_showMenu');
- this.mainMenu.appendChild(closeButton);
+ /**
+ * Returns the game object
+ *
+ * Возвращает объект игры
+ */
+ this.getSelfGame = function () {
+ return selfGame;
+ };
- const crossClose = document.createElement('div');
- crossClose.classList.add('scriptMenu_crossClose');
- closeButton.appendChild(crossClose);
- }
+ /** Возвращает объект игры */
+ this.getGame = function () {
+ return Game;
+ };
- getButtonColor(color) {
- const buttonColors = {
- green: 'scriptMenu_greenButton',
- red: 'scriptMenu_redButton',
- beige: 'scriptMenu_beigeButton',
- };
- return buttonColors[color] || buttonColors['beige'];
- }
+ /**
+ * Updates game data
+ *
+ * Обновляет данные игры
+ */
+ this.refreshGame = function () {
+ new Game.NextDayUpdatedManager()[getProtoFn(Game.NextDayUpdatedManager, 6)]();
+ try {
+ cheats.refreshInventory();
+ } catch (e) {}
+ };
- setStatus(text, onclick) {
- if (this._currentStatusClickHandler) {
- this.status.removeEventListener('click', this._currentStatusClickHandler);
- this._currentStatusClickHandler = null;
- }
+ /**
+ * 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);
+ };
- if (!text) {
- this.status.classList.add('scriptMenu_statusHide');
- this.status.innerHTML = '';
- } else {
- this.status.classList.remove('scriptMenu_statusHide');
- this.status.innerHTML = text;
- }
+ 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);
+ };
- if (typeof onclick === 'function') {
- this.status.addEventListener('click', onclick, { once: true });
- this._currentStatusClickHandler = onclick;
- }
- }
+ /**
+ * 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);
+ };
- addStatus(text) {
- if (!this.status.innerHTML) {
- this.status.classList.remove('scriptMenu_statusHide');
- }
- this.status.innerHTML += text;
- }
+ /**
+ * Move to the sanctuary cheats.goSanctuary()
+ *
+ * Переместиться в святилище cheats.goSanctuary()
+ */
+ this.goSanctuary = () => {
+ this.goNavigtor('SANCTUARY');
+ };
- addHeader(text, onClick, main = this.mainMenu) {
- this.emit('beforeAddHeader', text, onClick, main);
- const header = document.createElement('div');
- header.classList.add('scriptMenu_header');
- header.innerHTML = text;
- if (typeof onClick === 'function') {
- header.addEventListener('click', onClick);
- }
- main.appendChild(header);
- this.emit('afterAddHeader', text, onClick, main);
- return header;
- }
+ /** Перейти в Долину титанов */
+ this.goTitanValley = () => {
+ this.goNavigtor('TITAN_VALLEY');
+ };
- addButton(btn, main = this.mainMenu) {
- this.emit('beforeAddButton', btn, main);
- const { name, onClick, title, color, dot, classes = [], isCombine } = btn;
- const button = document.createElement('div');
- if (!isCombine) {
- classes.push('scriptMenu_mainButton');
- }
- button.classList.add('scriptMenu_button', this.getButtonColor(color), ...classes);
- button.title = title;
- button.addEventListener('click', onClick);
- main.appendChild(button);
+ /**
+ * 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();
+ };
- const buttonText = document.createElement('div');
- buttonText.classList.add('scriptMenu_buttonText');
- buttonText.innerText = name;
- button.appendChild(buttonText);
+ /** Перейти к Острову гильдии */
+ 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();
+ };
- if (dot) {
- const dotAtention = document.createElement('div');
- dotAtention.classList.add('scriptMenu_dot');
- dotAtention.title = dot;
- button.appendChild(dotAtention);
- }
+ /**
+ * 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);
- this.buttons.push(button);
- this.emit('afterAddButton', button, btn);
- return button;
- }
+ 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']());
+ };
- addCombinedButton(buttonList, main = this.mainMenu) {
- this.emit('beforeAddCombinedButton', buttonList, main);
- const buttonGroup = document.createElement('div');
- buttonGroup.classList.add('scriptMenu_buttonGroup');
- let count = 0;
+ /**
+ * 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);
- for (const btn of buttonList) {
- btn.isCombine = true;
- btn.classes ??= [];
- if (count === 0) {
- btn.classes.push('scriptMenu_combineButtonLeft');
- } else if (count === buttonList.length - 1) {
- btn.classes.push('scriptMenu_combineButtonRight');
- } else {
- btn.classes.push('scriptMenu_combineButtonCenter');
- }
- this.addButton(btn, buttonGroup);
- count++;
- }
+ const player = Game.GameModel[instance]()[GM_0];
+ return player[P_36][PSD_0][IM_0];
+ };
- const dotAtention = document.createElement('div');
- dotAtention.classList.add('scriptMenu_dot');
- buttonGroup.appendChild(dotAtention);
+ /**
+ * 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;
+ };
- main.appendChild(buttonGroup);
- this.emit('afterAddCombinedButton', buttonGroup, buttonList);
- return buttonGroup;
- }
+ /**
+ * 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 } });
- addCheckbox(label, title, main = this.mainMenu) {
- this.emit('beforeAddCheckbox', label, title, main);
- const divCheckbox = document.createElement('div');
- divCheckbox.classList.add('scriptMenu_divInput');
- divCheckbox.title = title;
- main.appendChild(divCheckbox);
+ 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']());
+ };
- const checkbox = document.createElement('input');
- checkbox.type = 'checkbox';
- checkbox.id = 'scriptMenuCheckbox' + this.checkboxes.length;
- checkbox.classList.add('scriptMenu_checkbox');
- divCheckbox.appendChild(checkbox);
+ /**
+ * Game library availability tracker
+ *
+ * Отслеживание доступности игровой библиотеки
+ */
+ function checkLibLoad() {
+ timeout = setTimeout(() => {
+ if (Game.GameModel) {
+ changeLib();
+ } else {
+ checkLibLoad();
+ }
+ }, 100);
+ }
- const checkboxLabel = document.createElement('label');
- checkboxLabel.innerText = label;
- checkboxLabel.setAttribute('for', checkbox.id);
- divCheckbox.appendChild(checkboxLabel);
+ /**
+ * 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';
- this.checkboxes.push(checkbox);
- this.emit('afterAddCheckbox', label, title, main);
- return checkbox;
+ 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);
+ };
}
- addInputText(title, placeholder, main = this.mainMenu) {
- this.emit('beforeAddCheckbox', title, placeholder, main);
- const divInputText = document.createElement('div');
- divInputText.classList.add('scriptMenu_divInputText');
- divInputText.title = title;
- main.appendChild(divInputText);
+ this.LibLoad = function () {
+ return new Promise((e) => {
+ this.doneLibLoad = e;
+ });
+ };
- const newInputText = document.createElement('input');
- newInputText.type = 'text';
- if (placeholder) {
- newInputText.placeholder = placeholder;
- }
- newInputText.classList.add('scriptMenu_InputText');
- divInputText.appendChild(newInputText);
- this.emit('afterAddCheckbox', title, placeholder, main);
- return newInputText;
- }
+ /**
+ * Returns the value of a language constant
+ *
+ * Возвращает значение языковой константы
+ * @param {*} langConst language constant // языковая константа
+ * @returns
+ */
+ this.translate = function (langConst) {
+ return Game.Translate.translate(langConst);
+ };
- addDetails(summaryText, name = null) {
- this.emit('beforeAddDetails', summaryText, name);
- const details = document.createElement('details');
- details.classList.add('scriptMenu_Details');
- this.mainMenu.appendChild(details);
+ connectGame();
+ checkLibLoad();
+ }
- const summary = document.createElement('summary');
- summary.classList.add('scriptMenu_Summary');
- summary.innerText = summaryText;
- if (name) {
- details.open = this.option.showDetails[name] ?? false;
- details.dataset.name = name;
- details.addEventListener('toggle', () => {
- this.option.showDetails[details.dataset.name] = details.open;
- this.saveSaveOption();
- });
- }
+ /**
+ * Auto collection of gifts
+ *
+ * Автосбор подарков
+ */
+ function getAutoGifts() {
+ // bmF0cmlidS5vcmc=
+ let valName = 'giftSendIds_' + userInfo.id;
- details.appendChild(summary);
- this.emit('afterAddDetails', summaryText, name);
- return details;
+ if (!localStorage['clearGift' + userInfo.id]) {
+ localStorage[valName] = '';
+ localStorage['clearGift' + userInfo.id] = '+';
}
- saveSaveOption() {
- try {
- localStorage.setItem('scriptMenu_saveOption', JSON.stringify(this.option));
- } catch (e) {
- console.log('¯\\_(ツ)_/¯');
- }
+ if (!localStorage[valName]) {
+ localStorage[valName] = '';
}
- loadSaveOption() {
- let saveOption = null;
- try {
- saveOption = localStorage.getItem('scriptMenu_saveOption');
- } catch (e) {
- console.log('¯\\_(ツ)_/¯');
- }
-
- if (!saveOption) {
- return {};
- }
-
- try {
- saveOption = JSON.parse(saveOption);
- } catch (e) {
- return {};
- }
-
- return saveOption;
- }
- }
-
- this.HWHClasses.ScriptMenu = ScriptMenu;
+ 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,
+ });
+ });
- //const scriptMenu = ScriptMenu.getInst();
+ if (!freebieCheckCalls.calls.length) {
+ return;
+ }
- /**
- * Пример использования
- const scriptMenu = ScriptMenu.getInst();
- scriptMenu.init();
- scriptMenu.addHeader('v1.508');
- scriptMenu.addCheckbox('testHack', 'Тестовый взлом игры!');
- scriptMenu.addButton({
- text: 'Запуск!',
- onClick: () => console.log('click'),
- title: 'подсказака',
- });
- scriptMenu.addInputText('input подсказака');
- scriptMenu.on('beforeInit', (option) => {
- console.log('beforeInit', option);
- })
- scriptMenu.on('beforeAddHeader', (text, onClick, main) => {
- console.log('beforeAddHeader', text, onClick, main);
- });
- scriptMenu.on('beforeAddButton', (btn, main) => {
- console.log('beforeAddButton', btn, main);
- });
- scriptMenu.on('beforeAddCombinedButton', (buttonList, main) => {
- console.log('beforeAddCombinedButton', buttonList, main);
- });
- scriptMenu.on('beforeAddCheckbox', (label, title, main) => {
- console.log('beforeAddCheckbox', label, title, main);
- });
- scriptMenu.on('beforeAddDetails', (summaryText, name) => {
- console.log('beforeAddDetails', summaryText, name);
- });
- */
+ 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}`);
+ setProgress(`${I18N('GIFTS')}: ${countGetGifts}`, true);
+ });
+ })
+ .catch((error) => {
+ console.error(error);
+ const reason = error.message == 'Access denied' ? error.message : 'Error';
+ setProgress(`${I18N('GIFTS')}: ${reason}`, true);
+ });
+ }
/**
- * Game Library
- *
- * Игровая библиотека
+ * To fill the kills in the Forge of Souls
+ *
+ * Набить килов в горниле душ
*/
- class Library {
- defaultLibUrl = 'https://heroesru-a.akamaihd.net/vk/v1101/lib/lib.json';
+ 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;
- constructor() {
- if (!Library.instance) {
- Library.instance = this;
- }
+ const [battleGetReplay, heroGetAll, pet_getAll, offerGetAll] = await Caller.send([
+ { name: 'battleGetReplay', args: { id: replayId } },
+ 'heroGetAll',
+ 'pet_getAll',
+ 'offerGetAll',
+ ]);
- return Library.instance;
+ 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);
- 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)
+ 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;
}
}
-
- 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) {}
+ if (args.heroes.length) {
+ calls.push({
+ name: 'bossRating_startBattle',
+ args,
+ });
}
-
- getData(id) {
- return this.data[id];
+ /**
+ * 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++;
+ }
}
- setData(data) {
- this.data = data;
+ if (!calls.length) {
+ setProgress(`${I18N('NO_HEROES')}`, true);
+ return;
}
- }
- this.lib = new Library();
+ console.log(await Caller.send(calls));
+ rewardBossRatingEvent();
+ }
/**
- * Database
+ * Collecting Rewards from the Forge of Souls
*
- * База данных
+ * Сбор награды из Горнила Душ
*/
- class Database {
- constructor(dbName, storeName) {
- this.dbName = dbName;
- this.storeName = storeName;
- this.db = null;
- }
+ 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;
+ }
- async open() {
- return new Promise((resolve, reject) => {
- const request = indexedDB.open(this.dbName);
+ let farmedChests = bossEventInfo.progress.farmedChests;
+ let score = bossEventInfo.progress.score;
+ setProgress(`${I18N('DAMAGE_AMOUNT')}: ${score}`);
+ let revard = bossEventInfo.reward;
- request.onerror = () => {
- reject(new Error(`Failed to open database ${this.dbName}`));
- };
+ let getRewardCall = {
+ calls: []
+ }
- request.onsuccess = () => {
- this.db = request.result;
- resolve();
- };
+ 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;
+ }
- request.onupgradeneeded = (event) => {
- const db = event.target.result;
- if (!db.objectStoreNames.contains(this.storeName)) {
- db.createObjectStore(this.storeName);
- }
- };
+ 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;
}
- 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);
+ 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.bosses;
- request.onerror = () => {
- reject(new Error(`Failed to save value with key ${key}`));
+ let bossRaidOpenChestCall = {
+ calls: []
};
- 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);
+ 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
+ });
+ }
+ }
- request.onerror = () => {
- reject(new Error(`Failed to delete value with key ${key}`));
- };
+ if (!bossRaidOpenChestCall.calls.length) {
+ setProgress(`${I18N('OUTLAND')} ${I18N('NOTHING_TO_COLLECT')}`, true);
+ resolve();
+ return;
+ }
- request.onsuccess = () => {
+ send(bossRaidOpenChestCall, e => {
+ setProgress(`${I18N('OUTLAND')} ${I18N('COLLECTED')}`, true);
resolve();
- };
+ });
});
- }
+ });
}
/**
- * Returns the stored value
+ * Collect all rewards
*
- * Возвращает сохраненное значение
+ * Собрать все награды
*/
- function getSaveVal(saveName, def) {
- const result = storage.get(saveName, def);
- return result;
- }
- this.HWHFuncs.getSaveVal = getSaveVal;
+ 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++;
+ }
+ }
- /**
- * Stores value
- *
- * Сохраняет значение
- */
- function setSaveVal(saveName, value) {
- storage.set(saveName, value);
- }
- this.HWHFuncs.setSaveVal = setSaveVal;
+ if (!questAllFarmCall.calls.length) {
+ setProgress(`${I18N('COLLECTED')} ${number} ${I18N('REWARD')}`, true);
+ resolve();
+ return;
+ }
- /**
- * Database initialization
- *
- * Инициализация базы данных
- */
- const db = new Database(GM_info.script.name, 'settings');
+ send(questAllFarmCall, function (res) {
+ console.log(res);
+ setProgress(`${I18N('COLLECTED')} ${number} ${I18N('REWARD')}`, true);
+ resolve();
+ });
+ });
+ })
+ }
/**
- * Data store
+ * Mission auto repeat
*
- * Хранилище данных
- */
- const storage = {
- userId: 0,
+ * Автоповтор миссии
+ * 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();
/**
- * Default values
+ * Mission Request
*
- * Значения по умолчанию
+ * Запрос на выполнение мисии
*/
- 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];
- },
- };
+ let battle;
+ try {
+ battle = await Caller.send({
+ name: 'missionStart',
+ args: lastMissionStart,
+ });
+ } catch (e) {
+ isSendsMission = false;
+ console.error(e);
+ setProgress('');
+ return;
+ }
- /**
- * 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 });
+ 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;
}
}
- return values;
- }
- /**
- * Opens or migrates to a database
- *
- * Открывает или мигрирует в базу данных
- */
- async function openOrMigrateDatabase(userId) {
- storage.init();
- storage.userId = userId;
+ let r;
try {
- await db.open();
- } catch(e) {
+ 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;
}
- let settings = await db.get(userId, false);
- if (settings) {
- storage.values = settings;
+ 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;
}
- 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);
- }
+ param.count++;
+ setProgress(`${I18N('MISSIONS_PASSED')}: ${param.count} (${I18N('STOP')})`, false, () => {
+ isStopSendMission = true;
+ });
+ setTimeout(sendsMission, 1, param);
+ };
- class ZingerYWebsiteAPI {
- /**
- * Class for interaction with the API of the zingery.ru website
- * Intended only for use with the HeroWarsHelper script:
- * https://greasyfork.org/ru/scripts/450693-herowarshelper
- * Copyright ZingerY
- */
- url = 'https://zingery.ru/heroes/';
- // YWJzb2x1dGVseSB1c2VsZXNzIGxpbmU=
- constructor(urn, env, data = {}) {
- this.urn = urn;
- this.fd = {
- now: Date.now(),
- fp: this.constructor.toString().replaceAll(/\s/g, ''),
- env: env.callee.toString().replaceAll(/\s/g, ''),
- info: (({ name, version, author }) => [name, version, author])(GM_info.script),
- ...data,
- };
- }
+ /**
+ * Opening of russian dolls
+ *
+ * Открытие матрешек
+ */
+ async function openRussianDolls(libId, amount) {
+ let sum = 0;
+ const sumResult = {};
+ let count = 0;
- sign() {
- return md5([...this.fd.info, ~(this.fd.now % 1e3), this.fd.fp].join('_'));
- }
+ 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;
- encode(data) {
- return btoa(encodeURIComponent(JSON.stringify(data)));
- }
+ if (result?.consumable && result.consumable[libId]) {
+ newCount = result.consumable[libId];
+ delete result.consumable[libId];
+ }
- decode(data) {
- return JSON.parse(decodeURIComponent(atob(data)));
+ mergeItemsObj(sumResult, result);
+ amount = newCount;
}
- headers() {
- return {
- 'X-Request-Signature': this.sign(),
- 'X-Script-Name': GM_info.script.name,
- 'X-Script-Version': GM_info.script.version,
- 'X-Script-Author': GM_info.script.author,
- 'X-Script-ZingerY': 42,
- };
- }
+ setProgress(`${I18N('TOTAL_OPEN')} ${sum}`, 5000);
+ return [count, sumResult];
+ }
- async request() {
- try {
- const response = await fetch(this.url + this.urn, {
- method: 'POST',
- headers: this.headers(),
- body: this.encode(this.fd),
- });
- const text = await response.text();
- return this.decode(text);
- } catch (e) {
- console.error(e);
- return [];
+ 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];
}
}
- /**
- * Класс для взаимодействия с API сайта zingery.ru
- * Предназначен только для использования со скриптом HeroWarsHelper:
- * https://greasyfork.org/ru/scripts/450693-herowarshelper
- * Copyright ZingerY
- */
+
+ return obj1;
}
/**
- * Sending expeditions
+ * Collect all mail, except letters with energy and charges of the portal
*
- * Отправка экспедиций
+ * Собрать всю почту, кроме писем с энергией и зарядами портала
*/
- function checkExpedition() {
- const { Expedition } = HWHClasses;
- return new Promise((resolve, reject) => {
- const expedition = new Expedition(resolve, reject);
- expedition.start();
- });
- }
-
- class Expedition {
- checkExpedInfo = {
- calls: [
- {
- name: 'expeditionGet',
- args: {},
- ident: 'expeditionGet',
- },
- {
- name: 'heroGetAll',
- args: {},
- ident: 'heroGetAll',
- },
- ],
- };
+ 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;
+ }
- constructor(resolve, reject) {
- this.resolve = resolve;
- this.reject = reject;
+ const lettersIds = await Caller.send({
+ name: 'mailFarm',
+ args: { letterIds },
+ });
+ if (lettersIds) {
+ const countLetters = Object.keys(lettersIds).length;
+ setProgress(`${I18N('RECEIVED')} ${countLetters} ${I18N('LETTERS')}`, true);
}
+ }
- async start() {
- const data = await Send(JSON.stringify(this.checkExpedInfo));
+ class Letters {
+ /**
+ * Максимальное оставшееся время для автоматического сбора письма (24 часа)
+ */
+ static MAX_TIME_LEFT = 24 * 60 * 60 * 1000;
- const expedInfo = data.results[0].result.response;
- const dataHeroes = data.results[1].result.response;
- const dataExped = { useHeroes: [], exped: [] };
- const calls = [];
+ /**
+ * Фильтрует получаемые письма
+ * @param {Array} letters - Массив писем для фильтрации
+ * @returns {Array} - Массив ID писем, которые нужно собрать
+ */
+ static filter(letters) {
+ const { Letters } = HWHClasses;
+ const lettersIds = [];
- /**
- * Adding expeditions to collect
- * Добавляем экспедиции для сбора
- */
- let countGet = 0;
- for (var n in expedInfo) {
- const exped = expedInfo[n];
- const dateNow = Date.now() / 1000;
- if (exped.status == 2 && exped.endTime != 0 && dateNow > exped.endTime) {
- countGet++;
- calls.push({
- name: 'expeditionFarm',
- args: { expeditionId: exped.id },
- ident: 'expeditionFarm_' + exped.id,
- });
- } else {
- dataExped.useHeroes = dataExped.useHeroes.concat(exped.heroes);
- }
- if (exped.status == 1) {
- dataExped.exped.push({ id: exped.id, power: exped.power });
- }
- }
- dataExped.exped = dataExped.exped.sort((a, b) => b.power - a.power);
+ for (let l in letters) {
+ const letter = letters[l];
+ const reward = letter?.reward;
- /**
- * Putting together a list of heroes
- * Собираем список героев
- */
- const heroesArr = [];
- for (let n in dataHeroes) {
- const hero = dataHeroes[n];
- if (hero.power > 0 && !dataExped.useHeroes.includes(hero.id)) {
- let heroPower = hero.power;
- // Лара Крофт * 3
- if (hero.id == 63 && hero.color >= 16) {
- heroPower *= 3;
- }
- heroesArr.push({ id: hero.id, power: heroPower });
+ if (!reward || !Object.keys(reward).length) {
+ continue;
}
- }
- /**
- * Adding expeditions to send
- * Добавляем экспедиции для отправки
- */
- let countSend = 0;
- heroesArr.sort((a, b) => a.power - b.power);
- for (const exped of dataExped.exped) {
- let heroesIds = this.selectionHeroes(heroesArr, exped.power);
- if (heroesIds && heroesIds.length > 4) {
- for (let q in heroesArr) {
- if (heroesIds.includes(heroesArr[q].id)) {
- delete heroesArr[q];
- }
- }
- countSend++;
- calls.push({
- name: 'expeditionSendHeroes',
- args: {
- expeditionId: exped.id,
- heroes: heroesIds,
- },
- ident: 'expeditionSendHeroes_' + exped.id,
- });
+ if (Letters.shouldCollectLetter(reward)) {
+ lettersIds.push(~~letter.id);
+ continue;
}
- }
-
- if (calls.length) {
- await Send({ calls });
- this.end(I18N('EXPEDITIONS_SENT', {countGet, countSend}));
- return;
- }
-
- this.end(I18N('EXPEDITIONS_NOTHING'));
- }
- /**
- * Selection of heroes for expeditions
- *
- * Подбор героев для экспедиций
- */
- selectionHeroes(heroes, power) {
- const resultHeroers = [];
- const heroesIds = [];
- for (let q = 0; q < 5; q++) {
- for (let i in heroes) {
- let hero = heroes[i];
- if (heroesIds.includes(hero.id)) {
- continue;
- }
+ // Проверка времени до окончания годности письма
+ const availableUntil = +letter?.availableUntil;
+ if (availableUntil) {
+ const timeLeft = new Date(availableUntil * 1000) - new Date();
+ console.log('Time left:', timeLeft);
- const summ = resultHeroers.reduce((acc, hero) => acc + hero.power, 0);
- const need = Math.round((power - summ) / (5 - resultHeroers.length));
- if (hero.power > need) {
- resultHeroers.push(hero);
- heroesIds.push(hero.id);
- break;
+ if (timeLeft < Letters.MAX_TIME_LEFT) {
+ lettersIds.push(~~letter.id);
}
}
}
- const summ = resultHeroers.reduce((acc, hero) => acc + hero.power, 0);
- if (summ < power) {
- return false;
- }
- return heroesIds;
+ return lettersIds;
}
/**
- * Ends expedition script
- *
- * Завершает скрипт экспедиции
+ * Определяет, нужно ли собирать письмо (может быть переопределен в дочерних классах)
+ * @param {Object} reward - Награда письма
+ * @returns {boolean} - Нужно ли собирать письмо
*/
- end(msg) {
- setProgress(msg, true);
- this.resolve();
+ 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.Expedition = Expedition;
+ this.HWHClasses.Letters = Letters;
- /**
- * Walkthrough of the dungeon
- *
- * Прохождение подземелья
- */
- function testDungeon() {
- const { executeDungeon } = HWHClasses;
- return new Promise((resolve, reject) => {
- const dung = new executeDungeon(resolve, reject);
- const titanit = getInput('countTitanit');
- dung.start(titanit);
- });
+ 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;
+ }
}
/**
- * Walkthrough of the dungeon
+ * Displaying information about the areas of the portal and attempts on the VG
*
- * Прохождение подземелья
+ * Отображение информации о сферах портала и попытках на ВГ
*/
- function executeDungeon(resolve, reject) {
- dungeonActivity = 0;
- let maxDungeonActivity = 150;
+ async function justInfo() {
+ return new Promise(async (resolve, reject) => {
+ const [userGetInfo, clanWarGetInfo, titanArenaGetStatus] = await Caller.send([
+ 'userGetInfo',
+ 'clanWarGetInfo',
+ 'titanArenaGetStatus',
+ 'quest_completeEasterEggQuest',
+ ]);
- titanGetAll = [];
+ 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
- teams = {
- heroes: [],
- earth: [],
- fire: [],
- neutral: [],
- water: [],
+ 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');
}
- titanStats = [];
+ 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';
- titansStates = {};
+ setProgress(' ' + `${portalSphere.amount} ${I18N('GUILD_WAR')}: ${clanWarMyTries}`, true);
+ resolve();
+ });
+ }
- let talentMsg = '';
- let talentMsgReward = '';
+ async function getDailyBonus() {
+ const dailyBonusInfo = await Caller.send('dailyBonusGetInfo');
+ const { availableToday, availableVip, currentDay } = dailyBonusInfo;
- callsExecuteDungeon = {
- calls: [{
- name: "dungeonGetInfo",
- args: {},
- ident: "dungeonGetInfo"
- }, {
- name: "teamGetAll",
- args: {},
- ident: "teamGetAll"
- }, {
- name: "teamGetFavor",
- args: {},
- ident: "teamGetFavor"
- }, {
- name: "clanGetInfo",
- args: {},
- ident: "clanGetInfo"
- }, {
- name: "titanGetAll",
- args: {},
- ident: "titanGetAll"
- }, {
- name: "inventoryGet",
- args: {},
- ident: "inventoryGet"
- }]
+ if (!availableToday) {
+ console.log('Уже собрано');
+ return;
}
- this.start = function(titanit) {
- maxDungeonActivity = titanit || getInput('countTitanit');
- send(JSON.stringify(callsExecuteDungeon), startDungeon);
+ 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];
+
+ /** Добавить другие ящики */
/**
- * Getting data on the dungeon
- *
- * Получаем данные по подземелью
+ * 144 - медная шкатулка
+ * 145 - бронзовая шкатулка
+ * 148 - платиновая шкатулка
*/
- function startDungeon(e) {
- res = e.results;
- dungeonGetInfo = res[0].result.response;
- if (!dungeonGetInfo) {
- endDungeon('noDungeon', res);
- return;
- }
- teamGetAll = res[1].result.response;
- teamGetFavor = res[2].result.response;
- dungeonActivity = res[3].result.response.stat.todayDungeonActivity;
- titanGetAll = Object.values(res[4].result.response);
- HWHData.countPredictionCard = res[5].result.response.consumable[81];
+ if (!lootBox) {
+ setProgress(I18N('NO_BOXES'), true);
+ return;
+ }
- teams.hero = {
- favor: teamGetFavor.dungeon_hero,
- heroes: teamGetAll.dungeon_hero.filter(id => id < 6000),
- teamNum: 0,
- }
- heroPet = teamGetAll.dungeon_hero.filter(id => id >= 6000).pop();
- if (heroPet) {
- teams.hero.pet = heroPet;
+ 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);
}
+ }
- teams.neutral = {
- favor: {},
- heroes: getTitanTeam(titanGetAll, 'neutral'),
- teamNum: 0,
- };
- teams.water = {
- favor: {},
- heroes: getTitanTeam(titanGetAll, 'water'),
- teamNum: 0,
- };
- teams.fire = {
- favor: {},
- heroes: getTitanTeam(titanGetAll, 'fire'),
- teamNum: 0,
- };
- teams.earth = {
- favor: {},
- heroes: getTitanTeam(titanGetAll, 'earth'),
- teamNum: 0,
- };
+ setProgress(I18N('BOXES_OVER'), true);
+ }
+ async function fillActive() {
+ const [quests, inv, clanInfo] = await Caller.send(['questGetAll', 'inventoryGet', 'clanGetInfo']);
- checkFloor(dungeonGetInfo);
+ const stat = clanInfo.stat;
+ const maxActive = 2000 - stat.todayItemsActivity;
+ if (maxActive <= 0) {
+ setProgress(I18N('NO_MORE_ACTIVITY'), true);
+ return;
}
- function getTitanTeam(titans, type) {
- switch (type) {
- case 'neutral':
- return titans.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
- case 'water':
- return titans.filter(e => e.id.toString().slice(2, 3) == '0').map(e => e.id);
- case 'fire':
- return titans.filter(e => e.id.toString().slice(2, 3) == '1').map(e => e.id);
- case 'earth':
- return titans.filter(e => e.id.toString().slice(2, 3) == '2').map(e => e.id);
- }
+ let countGetActive = 0;
+ const quest = quests.find((e) => e.id > 10046 && e.id < 10051);
+ if (quest) {
+ countGetActive = 1750 - quest.progress;
}
- function getNeutralTeam() {
- const titans = titanGetAll.filter(e => !titansStates[e.id]?.isDead)
- return titans.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
+ if (countGetActive <= 0) {
+ countGetActive = maxActive;
}
+ console.log(countGetActive);
- function fixTitanTeam(titans) {
- titans.heroes = titans.heroes.filter(e => !titansStates[e]?.isDead);
- return titans;
+ 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;
}
- /**
- * Checking the floor
- *
- * Проверяем этаж
- */
- async function checkFloor(dungeonInfo) {
- if (!('floor' in dungeonInfo) || dungeonInfo.floor?.state == 2) {
- saveProgress();
- 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,
+ });
}
- checkTalent(dungeonInfo);
- // console.log(dungeonInfo, dungeonActivity);
- maxDungeonActivity = +getInput('countTitanit');
- setProgress(`${I18N('DUNGEON')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity} ${talentMsg}`);
- if (dungeonActivity >= maxDungeonActivity) {
- endDungeon('endDungeon', 'maxActive ' + dungeonActivity + '/' + maxDungeonActivity);
- return;
+ 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,
+ });
}
- titansStates = dungeonInfo.states.titans;
- titanStats = titanObjToArray(titansStates);
- const floorChoices = dungeonInfo.floor.userData;
- const floorType = dungeonInfo.floorType;
- //const primeElement = dungeonInfo.elements.prime;
- if (floorType == "battle") {
- const calls = [];
- for (let teamNum in floorChoices) {
- attackerType = floorChoices[teamNum].attackerType;
- const args = fixTitanTeam(teams[attackerType]);
- if (attackerType == 'neutral') {
- args.heroes = getNeutralTeam();
- }
- if (!args.heroes.length) {
- continue;
- }
- args.teamNum = teamNum;
- calls.push({
- name: "dungeonStartBattle",
- args,
- ident: "body_" + teamNum
- })
+ }
+ 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 (!calls.length) {
- endDungeon('endDungeon', 'All Dead');
- return;
+ /* Не душа героя */
+ if (!('fragmentHero' in slot.reward)) {
+ continue;
}
- const battleDatas = await Send(JSON.stringify({ calls }))
- .then(e => e.results.map(n => n.result.response))
- const battleResults = [];
- for (n in battleDatas) {
- battleData = battleDatas[n]
- battleData.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }];
- battleResults.push(await Calc(battleData).then(result => {
- result.teamNum = n;
- result.attackerType = floorChoices[n].attackerType;
- return result;
- }));
+ 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;
}
- processingPromises(battleResults)
+ inv[coin][coinId] -= slot.cost[coin][coinId];
+ calls.push({
+ name: 'shopBuy',
+ args: {
+ shopId: shop.id,
+ slot: slot.id,
+ cost: slot.cost,
+ reward: slot.reward,
+ },
+ });
}
}
- async function checkTalent(dungeonInfo) {
- const talent = dungeonInfo.talent;
- if (!talent) {
- return;
- }
- const dungeonFloor = +dungeonInfo.floorNumber;
- const talentFloor = +talent.floorRandValue;
- let doorsAmount = 3 - talent.conditions.doorsAmount;
+ if (!calls.length) {
+ setProgress(I18N('NO_PURCHASABLE_HERO_SOULS'), true);
+ return;
+ }
- if (dungeonFloor === talentFloor && (!doorsAmount || !talent.conditions?.farmedDoors[dungeonFloor])) {
- const reward = await Send({
- calls: [
- { name: 'heroTalent_getReward', args: { talentType: 'tmntDungeonTalent', reroll: false }, ident: 'group_0_body' },
- { name: 'heroTalent_farmReward', args: { talentType: 'tmntDungeonTalent' }, ident: 'group_1_body' },
- ],
- }).then((e) => e.results[0].result.response);
- const type = Object.keys(reward).pop();
- const itemId = Object.keys(reward[type]).pop();
- const count = reward[type][itemId];
- const itemName = cheats.translate(`LIB_${type.toUpperCase()}_NAME_${itemId}`);
- talentMsgReward += ` ${count} ${itemName}`;
- doorsAmount++;
- }
- talentMsg = ` TMNT Talent: ${doorsAmount}/3 ${talentMsgReward} `;
+ 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);
+ }
- function processingPromises(results) {
- let selectBattle = results[0];
- if (results.length < 2) {
- // console.log(selectBattle);
- if (!selectBattle.result.win) {
- endDungeon('dungeonEndBattle\n', selectBattle);
- return;
- }
- endBattle(selectBattle);
- return;
- }
+ /** Открыть платные сундуки в Запределье за 90 */
+ async function bossOpenChestPay() {
+ const [user, bosses, offers, time] = await Caller.send(['userGetInfo', 'bossGetAll', 'specialOffer_getAll', 'getTime']);
+ const boses = bosses.bosses;
- selectBattle = false;
- let bestState = -1000;
- for (const result of results) {
- const recovery = getState(result);
- if (recovery > bestState) {
- bestState = recovery;
- selectBattle = result
- }
- }
- // console.log(selectBattle.teamNum, results);
- if (!selectBattle || bestState <= -1000) {
- endDungeon('dungeonEndBattle\n', results);
- return;
- }
+ const discountOffer = offers.find((e) => e.offerType == 'costReplaceOutlandChest');
- startBattle(selectBattle.teamNum, selectBattle.attackerType)
- .then(endBattle);
+ let discount = 1;
+ if (discountOffer && discountOffer.endTime > time) {
+ discount = 1 - discountOffer.offerData.outlandChest.discountPercent / 100;
}
- /**
- * Let's start the fight
- *
- * Начинаем бой
- */
- function startBattle(teamNum, attackerType) {
- return new Promise(function (resolve, reject) {
- args = fixTitanTeam(teams[attackerType]);
- args.teamNum = teamNum;
- if (attackerType == 'neutral') {
- const titans = titanGetAll.filter(e => !titansStates[e.id]?.isDead)
- args.heroes = titans.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
- }
- startBattleCall = {
- calls: [{
- name: "dungeonStartBattle",
- args,
- ident: "body"
- }]
- }
- send(JSON.stringify(startBattleCall), resultBattle, {
- resolve,
- teamNum,
- attackerType
- });
+ 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',
});
}
- /**
- * Returns the result of the battle in a promise
- *
- * Возращает резульат боя в промис
- */
- function resultBattle(resultBattles, args) {
- battleData = resultBattles.results[0].result.response;
- battleType = "get_tower";
- if (battleData.type == "dungeon_titan") {
- battleType = "get_titan";
- }
- battleData.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }];
- BattleCalc(battleData, battleType, function (result) {
- result.teamNum = args.teamNum;
- result.attackerType = args.attackerType;
- args.resolve(result);
+
+ if (currentStarMoney >= cost18chests) {
+ buttons.push({
+ msg: I18N('BUY_OUTLAND_BTN', { count: 18, countEmerald: cost18chests, imgEmerald }),
+ result: [costFirstChest, costFirstChest, 0, costSecondChest, costSecondChest, 0],
+ color: 'green',
});
}
- /**
- * Finishing the fight
- *
- * Заканчиваем бой
- */
- async function endBattle(battleInfo) {
- if (battleInfo.result.win) {
- const args = {
- result: battleInfo.result,
- progress: battleInfo.progress,
- }
- if (HWHData.countPredictionCard > 0) {
- args.isRaid = true;
- } else {
- const timer = getTimer(battleInfo.battleTime);
- console.log(timer);
- await countdownTimer(timer, `${I18N('DUNGEON')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity} ${talentMsg}`);
- }
- const calls = [{
- name: "dungeonEndBattle",
- args,
- ident: "body"
- }];
- lastDungeonBattleData = null;
- send(JSON.stringify({ calls }), resultEndBattle);
- } else {
- endDungeon('dungeonEndBattle win: false\n', battleInfo);
- }
- }
-
- /**
- * Getting and processing battle results
- *
- * Получаем и обрабатываем результаты боя
- */
- function resultEndBattle(e) {
- if ('error' in e) {
- popup.confirm(I18N('ERROR_MSG', {
- name: e.error.name,
- description: e.error.description,
- }));
- endDungeon('errorRequest', e);
- return;
- }
- battleResult = e.results[0].result.response;
- if ('error' in battleResult) {
- endDungeon('errorBattleResult', battleResult);
- return;
- }
- dungeonGetInfo = battleResult.dungeon ?? battleResult;
- dungeonActivity += battleResult.reward.dungeonActivity ?? 0;
- checkFloor(dungeonGetInfo);
- }
- /**
- * Returns the coefficient of condition of the
- * difference in titanium before and after the battle
- *
- * Возвращает коэффициент состояния титанов после боя
- */
- function getState(result) {
- if (!result.result.win) {
- return -1000;
- }
+ const answer = await popup.confirm(`${I18N('BUY_OUTLAND')}
`, buttons);
- let beforeSumFactor = 0;
- const beforeTitans = result.battleData.attackers;
- for (let titanId in beforeTitans) {
- const titan = beforeTitans[titanId];
- const state = titan.state;
- let factor = 1;
- if (state) {
- const hp = state.hp / titan.hp;
- const energy = state.energy / 1e3;
- factor = hp + energy / 20
- }
- beforeSumFactor += factor;
+ if (!answer) {
+ return;
+ }
+ const callBoss = [];
+ let n = 0;
+ for (let boss of boses) {
+ const bossId = boss.id;
+ if (boss.chestNum != 2) {
+ continue;
}
-
- let afterSumFactor = 0;
- const afterTitans = result.progress[0].attackers.heroes;
- for (let titanId in afterTitans) {
- const titan = afterTitans[titanId];
- const hp = titan.hp / beforeTitans[titanId].hp;
- const energy = titan.energy / 1e3;
- const factor = hp + energy / 20;
- afterSumFactor += factor;
+ const calls = [];
+ for (const starmoney of answer) {
+ calls.push({
+ name: 'bossOpenChest',
+ args: {
+ amount: 1,
+ bossId,
+ starmoney,
+ },
+ });
}
- return afterSumFactor - beforeSumFactor;
+ callBoss.push(calls);
}
- /**
- * Converts an object with IDs to an array with IDs
- *
- * Преобразует объект с идетификаторами в массив с идетификаторами
- */
- function titanObjToArray(obj) {
- let titans = [];
- for (let id in obj) {
- obj[id].id = id;
- titans.push(obj[id]);
- }
- return titans;
+ if (!callBoss.length) {
+ setProgress(I18N('CHESTS_NOT_AVAILABLE'), true);
+ return;
}
- function saveProgress() {
- let saveProgressCall = {
- calls: [{
- name: "dungeonSaveProgress",
- args: {},
- ident: "body"
- }]
+ let count = 0;
+ let errors = 0;
+ for (const calls of callBoss) {
+ try {
+ const results = await Caller.send(calls);
+ count += results.length;
+ } catch (e) {
+ errors++;
}
- send(JSON.stringify(saveProgressCall), resultEndBattle);
- }
-
- function endDungeon(reason, info) {
- console.warn(reason, info);
- setProgress(`${I18N('DUNGEON')} ${I18N('COMPLETED')}`, true);
- resolve();
}
- }
- this.HWHClasses.executeDungeon = executeDungeon;
-
- /**
- * Passing the tower
- *
- * Прохождение башни
- */
- function testTower() {
- const { executeTower } = HWHClasses;
- return new Promise((resolve, reject) => {
- tower = new executeTower(resolve, reject);
- tower.start();
- });
+ setProgress(`${I18N('OUTLAND_CHESTS_RECEIVED')}: ${count}`, true);
}
- /**
- * Passing the tower
- *
- * Прохождение башни
- */
- function executeTower(resolve, reject) {
- lastTowerInfo = {};
+ async function autoRaidAdventure(countRaid = 0) {
+ const [userGetInfo, adventure_raidGetInfo] = await Caller.send(['userGetInfo', 'adventure_raidGetInfo']);
- scullCoin = 0;
+ const portalSphere = userGetInfo.refillable.find((n) => n.id == 45);
+ const adventureRaid = Object.entries(adventure_raidGetInfo.raid)
+ .filter((e) => e[1])
+ .pop();
+ const adventureId = adventureRaid ? adventureRaid[0] : 0;
- heroGetAll = [];
+ if (!portalSphere.amount || !adventureId) {
+ setProgress(I18N('RAID_NOT_AVAILABLE'), true);
+ return;
+ }
- heroesStates = {};
+ if (!countRaid) {
+ countRaid = +(await popup.confirm(I18N('RAID_ADVENTURE', { adventureId }), [
+ { result: false, isClose: true },
+ { msg: I18N('RAID'), isInput: true, default: portalSphere.amount, color: 'green' },
+ ]));
+ }
- argsBattle = {
- heroes: [],
- favor: {},
- };
+ if (!countRaid) {
+ return;
+ }
- callsExecuteTower = {
- calls: [{
- name: "towerGetInfo",
- args: {},
- ident: "towerGetInfo"
- }, {
- name: "teamGetAll",
- args: {},
- ident: "teamGetAll"
- }, {
- name: "teamGetFavor",
- args: {},
- ident: "teamGetFavor"
- }, {
- name: "inventoryGet",
- args: {},
- ident: "inventoryGet"
- }, {
- name: "heroGetAll",
- args: {},
- ident: "heroGetAll"
- }]
+ if (countRaid > portalSphere.amount) {
+ countRaid = portalSphere.amount;
}
- buffIds = [
- {id: 0, cost: 0, isBuy: false}, // plug // заглушка
- {id: 1, cost: 1, isBuy: true}, // 3% attack // 3% атака
- {id: 2, cost: 6, isBuy: true}, // 2% attack // 2% атака
- {id: 3, cost: 16, isBuy: true}, // 4% attack // 4% атака
- {id: 4, cost: 40, isBuy: true}, // 8% attack // 8% атака
- {id: 5, cost: 1, isBuy: true}, // 10% armor // 10% броня
- {id: 6, cost: 6, isBuy: true}, // 5% armor // 5% броня
- {id: 7, cost: 16, isBuy: true}, // 10% armor // 10% броня
- {id: 8, cost: 40, isBuy: true}, // 20% armor // 20% броня
- { id: 9, cost: 1, isBuy: true }, // 10% protection from magic // 10% защита от магии
- { id: 10, cost: 6, isBuy: true }, // 5% protection from magic // 5% защита от магии
- { id: 11, cost: 16, isBuy: true }, // 10% protection from magic // 10% защита от магии
- { id: 12, cost: 40, isBuy: true }, // 20% protection from magic // 20% защита от магии
- { id: 13, cost: 1, isBuy: false }, // 40% health hero // 40% здоровья герою
- { id: 14, cost: 6, isBuy: false }, // 40% health hero // 40% здоровья герою
- { id: 15, cost: 16, isBuy: false }, // 80% health hero // 80% здоровья герою
- { id: 16, cost: 40, isBuy: false }, // 40% health to all heroes // 40% здоровья всем героям
- { id: 17, cost: 1, isBuy: false }, // 40% energy to the hero // 40% энергии герою
- { id: 18, cost: 3, isBuy: false }, // 40% energy to the hero // 40% энергии герою
- { id: 19, cost: 8, isBuy: false }, // 80% energy to the hero // 80% энергии герою
- { id: 20, cost: 20, isBuy: false }, // 40% energy to all heroes // 40% энергии всем героям
- { id: 21, cost: 40, isBuy: false }, // Hero Resurrection // Воскрешение героя
- ]
+ const resultRaid = await Caller.send(
+ Array(countRaid)
+ .fill()
+ .map(() => ({
+ name: 'adventure_raid',
+ args: { adventureId },
+ })),
+ );
- this.start = function () {
- send(JSON.stringify(callsExecuteTower), startTower);
+ if (!resultRaid.length && countRaid > 1) {
+ console.log(resultRaid);
+ setProgress(I18N('SOMETHING_WENT_WRONG'), true);
+ return;
}
- /**
- * Getting data on the Tower
- *
- * Получаем данные по башне
- */
- function startTower(e) {
- res = e.results;
- towerGetInfo = res[0].result.response;
- if (!towerGetInfo) {
- endTower('noTower', res);
- return;
- }
- teamGetAll = res[1].result.response;
- teamGetFavor = res[2].result.response;
- inventoryGet = res[3].result.response;
- heroGetAll = Object.values(res[4].result.response);
-
- scullCoin = inventoryGet.coin[7] ?? 0;
+ console.log(resultRaid, adventureId, portalSphere.amount);
+ setProgress(I18N('ADVENTURE_COMPLETED', { adventureId, times: countRaid }), true);
+ }
- argsBattle.favor = teamGetFavor.tower;
- argsBattle.heroes = heroGetAll.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
- pet = teamGetAll.tower.filter(id => id >= 6000).pop();
- if (pet) {
- argsBattle.pet = pet;
- }
+ /** Вывести всю клановую статистику в консоль браузера */
+ async function clanStatistic() {
+ const [dataClanInfo, dataClanStat, dataClanLog] = await Caller.send(['clanGetInfo', 'clanGetWeeklyStat', 'clanGetLog']);
- checkFloor(towerGetInfo);
+ const membersStat = {};
+ for (let i = 0; i < dataClanStat.stat.length; i++) {
+ membersStat[dataClanStat.stat[i].id] = dataClanStat.stat[i];
}
- function fixHeroesTeam(argsBattle) {
- let fixHeroes = argsBattle.heroes.filter(e => !heroesStates[e]?.isDead);
- if (fixHeroes.length < 5) {
- heroGetAll = heroGetAll.filter(e => !heroesStates[e.id]?.isDead);
- fixHeroes = heroGetAll.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
- Object.keys(argsBattle.favor).forEach(e => {
- if (!fixHeroes.includes(+e)) {
- delete argsBattle.favor[e];
- }
- })
+ const joinStat = {};
+ historyLog = dataClanLog.history;
+ for (let j in historyLog) {
+ his = historyLog[j];
+ if (his.event == 'join') {
+ joinStat[his.userId] = his.ctime;
}
- argsBattle.heroes = fixHeroes;
- return argsBattle;
}
- /**
- * Check the floor
- *
- * Проверяем этаж
- */
- function checkFloor(towerInfo) {
- lastTowerInfo = towerInfo;
- maySkipFloor = +towerInfo.maySkipFloor;
- floorNumber = +towerInfo.floorNumber;
- heroesStates = towerInfo.states.heroes;
- floorInfo = towerInfo.floor;
+ const infoArr = [];
+ const members = dataClanInfo.clan.members;
+ for (let n in members) {
+ var member = [
+ n,
+ members[n].name,
+ members[n].level,
+ dataClanInfo.clan.warriors.includes(+n) ? 1 : 0,
+ (new Date(members[n].lastLoginTime * 1000)).toLocaleString().replace(',', ''),
+ joinStat[n] ? (new Date(joinStat[n] * 1000)).toLocaleString().replace(',', '') : '',
+ membersStat[n].activity.reverse().join('\t'),
+ membersStat[n].adventureStat.reverse().join('\t'),
+ membersStat[n].clanGifts.reverse().join('\t'),
+ membersStat[n].clanWarStat.reverse().join('\t'),
+ membersStat[n].dungeonActivity.reverse().join('\t'),
+ ];
+ infoArr.push(member);
+ }
+ const info = infoArr.sort((a, b) => (b[2] - a[2])).map((e) => e.join('\t')).join('\n');
+ console.log(info);
+ copyText(info);
+ setProgress(I18N('CLAN_STAT_COPY'), true);
+ }
- /**
- * Is there at least one chest open on the floor
- * Открыт ли на этаже хоть один сундук
- */
- isOpenChest = false;
- if (towerInfo.floorType == "chest") {
- isOpenChest = towerInfo.floor.chests.reduce((n, e) => n + e.opened, 0);
- }
+ async function buyInStoreForGold() {
+ const [shops, user] = await Caller.send(['shopGetAll', 'userGetInfo']);
- setProgress(`${I18N('TOWER')}: ${I18N('FLOOR')} ${floorNumber}`);
- if (floorNumber > 49) {
- if (isOpenChest) {
- endTower('alreadyOpenChest 50 floor', floorNumber);
- return;
- }
- }
- /**
- * If the chest is open and you can skip floors, then move on
- * Если сундук открыт и можно скипать этажи, то переходим дальше
- */
- if (towerInfo.mayFullSkip && +towerInfo.teamLevel == 130) {
- if (floorNumber == 1) {
- fullSkipTower();
- return;
- }
- if (isOpenChest) {
- nextOpenChest(floorNumber);
- } else {
- nextChestOpen(floorNumber);
- }
- return;
- }
+ let gold = user.gold;
+ const calls = [];
- // console.log(towerInfo, scullCoin);
- switch (towerInfo.floorType) {
- case "battle":
- if (floorNumber <= maySkipFloor) {
- skipFloor();
- return;
- }
- if (floorInfo.state == 2) {
- nextFloor();
- return;
+ if (shops[17]) {
+ const slots = shops[17].slots;
+ for (let i = 1; i <= 2; i++) {
+ if (!slots[i].bought) {
+ const costGold = slots[i].cost?.gold || 0;
+ if (gold < costGold) {
+ continue;
}
- startBattle().then(endBattle);
- return;
- case "buff":
- checkBuff(towerInfo);
- return;
- case "chest":
- openChest(floorNumber);
- return;
- default:
- console.log('!', towerInfo.floorType, towerInfo);
- break;
- }
- }
-
- /**
- * Let's start the fight
- *
- * Начинаем бой
- */
- function startBattle() {
- return new Promise(function (resolve, reject) {
- towerStartBattle = {
- calls: [{
- name: "towerStartBattle",
- args: fixHeroesTeam(argsBattle),
- ident: "body"
- }]
- }
- send(JSON.stringify(towerStartBattle), resultBattle, resolve);
- });
- }
- /**
- * Returns the result of the battle in a promise
- *
- * Возращает резульат боя в промис
- */
- function resultBattle(resultBattles, resolve) {
- battleData = resultBattles.results[0].result.response;
- battleType = "get_tower";
- BattleCalc(battleData, battleType, function (result) {
- resolve(result);
- });
- }
- /**
- * Finishing the fight
- *
- * Заканчиваем бой
- */
- function endBattle(battleInfo) {
- if (battleInfo.result.stars >= 3) {
- endBattleCall = {
- calls: [{
- name: "towerEndBattle",
+ gold -= costGold;
+ calls.push({
+ name: 'shopBuy',
args: {
- result: battleInfo.result,
- progress: battleInfo.progress,
+ shopId: 17,
+ slot: i,
+ cost: slots[i].cost,
+ reward: slots[i].reward,
},
- ident: "body"
- }]
+ });
}
- send(JSON.stringify(endBattleCall), resultEndBattle);
- } else {
- endTower('towerEndBattle win: false\n', battleInfo);
- }
- }
-
- /**
- * Getting and processing battle results
- *
- * Получаем и обрабатываем результаты боя
- */
- function resultEndBattle(e) {
- battleResult = e.results[0].result.response;
- if ('error' in battleResult) {
- endTower('errorBattleResult', battleResult);
- return;
- }
- if ('reward' in battleResult) {
- scullCoin += battleResult.reward?.coin[7] ?? 0;
- }
- nextFloor();
- }
-
- function nextFloor() {
- nextFloorCall = {
- calls: [{
- name: "towerNextFloor",
- args: {},
- ident: "body"
- }]
}
- send(JSON.stringify(nextFloorCall), checkDataFloor);
}
- function openChest(floorNumber) {
- floorNumber = floorNumber || 0;
- openChestCall = {
- calls: [{
- name: "towerOpenChest",
+ const slots = shops[1].slots;
+ for (let i = 4; i <= 6; i++) {
+ if (!slots[i].bought && slots[i]?.cost?.gold) {
+ const costGold = slots[i].cost.gold;
+ if (gold < costGold) {
+ continue;
+ }
+ gold -= costGold;
+ calls.push({
+ name: 'shopBuy',
args: {
- num: 2
+ shopId: 1,
+ slot: i,
+ cost: slots[i].cost,
+ reward: slots[i].reward,
},
- ident: "body"
- }]
+ });
}
- send(JSON.stringify(openChestCall), floorNumber < 50 ? nextFloor : lastChest);
}
- function lastChest() {
- endTower('openChest 50 floor', floorNumber);
+ if (!calls.length) {
+ setProgress(I18N('NOTHING_BUY'), true);
+ return;
}
- function skipFloor() {
- skipFloorCall = {
- calls: [{
- name: "towerSkipFloor",
- args: {},
- ident: "body"
- }]
- }
- send(JSON.stringify(skipFloorCall), checkDataFloor);
- }
+ const resultBuy = await Caller.send(calls);
+ console.log(resultBuy);
+ const countBuy = resultBuy.length;
+ setProgress(I18N('LOTS_BOUGHT', { countBuy }), true);
+ }
- function checkBuff(towerInfo) {
- buffArr = towerInfo.floor;
- promises = [];
- for (let buff of buffArr) {
- buffInfo = buffIds[buff.id];
- if (buffInfo.isBuy && buffInfo.cost <= scullCoin) {
- scullCoin -= buffInfo.cost;
- promises.push(buyBuff(buff.id));
- }
- }
- Promise.all(promises).then(nextFloor);
- }
+ async function rewardsAndMailFarm(isFarmMail = true) {
+ try {
+ const [questGetAll, mailGetAll, specialOffer, battlePassInfo, battlePassSpecial] = await Caller.send([
+ 'questGetAll',
+ 'mailGetAll',
+ 'specialOffer_getAll',
+ 'battlePass_getInfo',
+ 'battlePass_getSpecial',
+ ]);
+ const questsFarm = questGetAll.filter((e) => e.state == 2);
+ const mailFarm = mailGetAll?.letters || [];
+ const stagesOffers = specialOffer.filter(e => e.offerType === "stagesOffer" && e.farmedStage == -1);
- function buyBuff(buffId) {
- return new Promise(function (resolve, reject) {
- buyBuffCall = {
- calls: [{
- name: "towerBuyBuff",
- args: {
- buffId
- },
- ident: "body"
- }]
- }
- send(JSON.stringify(buyBuffCall), resolve);
- });
- }
+ const listBattlePass = {
+ [battlePassInfo.id]: battlePassInfo.battlePass,
+ ...battlePassSpecial,
+ };
- function checkDataFloor(result) {
- towerInfo = result.results[0].result.response;
- if ('reward' in towerInfo && towerInfo.reward?.coin) {
- scullCoin += towerInfo.reward?.coin[7] ?? 0;
- }
- if ('tower' in towerInfo) {
- towerInfo = towerInfo.tower;
- }
- if ('skullReward' in towerInfo) {
- scullCoin += towerInfo.skullReward?.coin[7] ?? 0;
- }
- checkFloor(towerInfo);
- }
- /**
- * Getting tower rewards
- *
- * Получаем награды башни
- */
- function farmTowerRewards(reason) {
- let { pointRewards, points } = lastTowerInfo;
- let pointsAll = Object.getOwnPropertyNames(pointRewards);
- let farmPoints = pointsAll.filter(e => +e <= +points && !pointRewards[e]);
- if (!farmPoints.length) {
- return;
- }
- let farmTowerRewardsCall = {
- calls: [{
- name: "tower_farmPointRewards",
- args: {
- points: farmPoints
- },
- ident: "tower_farmPointRewards"
- }]
+ for (const passId in listBattlePass) {
+ const battlePass = listBattlePass[passId];
+ const levels = Object.values(lib.data.battlePass.level).filter((x) => x.battlePass == passId);
+ battlePass.level = Math.max(...levels.filter((p) => battlePass.exp >= p.experience).map((p) => p.level));
}
- if (scullCoin > 0) {
- farmTowerRewardsCall.calls.push({
- name: "tower_farmSkullReward",
- args: {},
- ident: "tower_farmSkullReward"
- });
- }
+ const specialQuests = lib.getData('quest').special;
+ const questBattlePass = lib.getData('quest').battlePass;
+ const { questChain: questChainBPass } = lib.getData('battlePass');
+ const currentTime = Date.now();
- send(JSON.stringify(farmTowerRewardsCall), () => { });
- }
+ const farmCaller = new Caller();
- function fullSkipTower() {
- /**
- * Next chest
- *
- * Следующий сундук
- */
- function nextChest(n) {
- return {
- name: "towerNextChest",
- args: {},
- ident: "group_" + n + "_body"
- }
- }
- /**
- * Open chest
- *
- * Открыть сундук
- */
- function openChest(n) {
- return {
- name: "towerOpenChest",
- args: {
- "num": 2
- },
- ident: "group_" + n + "_body"
+ for (const offer of stagesOffers) {
+ const offerId = offer.id;
+ //const stage = 0 - offer.farmedStage;
+ for (const stage of offer.offerData.stages) {
+ if (stage.billingId) {
+ break;
+ }
+ farmCaller.add({
+ name: 'specialOffer_farmReward',
+ args: { offerId },
+ });
}
}
- const fullSkipTowerCall = {
- calls: []
- }
+ const farmQuestIds = [];
+ const questIds = [];
+ for (let quest of questsFarm) {
+ const questId = +quest.id;
- let n = 0;
- for (let i = 0; i < 15; i++) {
- // 15 сундуков
- fullSkipTowerCall.calls.push(nextChest(++n));
- fullSkipTowerCall.calls.push(openChest(++n));
- // +5 сундуков, 250 изюма // towerOpenChest
- // if (i < 5) {
- // fullSkipTowerCall.calls.push(openChest(++n, 2));
- // }
- }
+ /*
+ if ([20010001, 20010002, 20010004].includes(questId)) {
+ farmCaller.add({
+ name: 'questFarm',
+ args: { questId },
+ });
+ farmQuestIds.push(questId);
+ continue;
+ }
+ */
- fullSkipTowerCall.calls.push({
- name: 'towerGetInfo',
- args: {},
- ident: 'group_' + ++n + '_body',
- });
+ if (questId >= 2001e4 && questId < 14e8) {
+ continue;
+ }
- send(JSON.stringify(fullSkipTowerCall), data => {
- for (const r of data.results) {
- const towerInfo = r?.result?.response;
- if (towerInfo && 'skullReward' in towerInfo) {
- scullCoin += towerInfo.skullReward?.coin[7] ?? 0;
+ if (quest.reward?.battlePassExp && !specialQuests[questId]) {
+ const questInfo = questBattlePass[questId];
+ if (!questInfo) {
+ continue;
+ }
+ const chain = questChainBPass[questInfo.chain];
+ const battlePass = listBattlePass[chain.battlePass];
+ if (!battlePass) {
+ continue;
+ }
+ // Наличие золотого билета
+ if (chain.requirement?.battlePassTicket && !battlePass.ticket) {
+ continue;
+ }
+ // Соответствие требований по уровню
+ if (chain.requirement?.battlePassLevel && battlePass.level < chain.requirement.battlePassLevel) {
+ continue;
+ }
+ const startTime = battlePass.startDate * 1e3;
+ const endTime = battlePass.endDate * 1e3;
+ // Соответствие даты проведения
+ if (startTime > currentTime || endTime < currentTime) {
+ continue;
}
}
- data.results[0] = data.results[data.results.length - 1];
- checkDataFloor(data);
- });
- }
- function nextChestOpen(floorNumber) {
- const calls = [{
- name: "towerOpenChest",
- args: {
- num: 2
- },
- ident: "towerOpenChest"
- }];
+ if (questId >= 2e7 && questId < 14e8) {
+ questIds.push(questId);
+ farmQuestIds.push(questId);
+ continue;
+ }
- Send(JSON.stringify({ calls })).then(e => {
- nextOpenChest(floorNumber);
- });
- }
+ farmCaller.add({
+ name: 'questFarm',
+ args: { questId },
+ });
+ farmQuestIds.push(questId);
+ }
- function nextOpenChest(floorNumber) {
- if (floorNumber > 49) {
- endTower('openChest 50 floor', floorNumber);
- return;
+ if (questIds.length) {
+ farmCaller.add({
+ name: 'quest_questsFarm',
+ args: { questIds },
+ });
}
- let nextOpenChestCall = {
- calls: [{
- name: "towerNextChest",
- args: {},
- ident: "towerNextChest"
- }, {
- name: "towerOpenChest",
- args: {
- num: 2
- },
- ident: "towerOpenChest"
- }]
+ if (isFarmMail) {
+ const { Letters } = HWHClasses;
+ const letterIds = Letters.filter(mailFarm);
+ if (letterIds.length) {
+ farmCaller.add({
+ name: 'mailFarm',
+ args: { letterIds },
+ });
+ }
}
- send(JSON.stringify(nextOpenChestCall), checkDataFloor);
- }
- function endTower(reason, info) {
- console.log(reason, info);
- if (reason != 'noTower') {
- farmTowerRewards(reason);
+ if (farmCaller.isEmpty()) {
+ setProgress(I18N('NOTHING_TO_COLLECT'), true);
+ return;
}
- setProgress(`${I18N('TOWER')} ${I18N('COMPLETED')}!`, true);
- resolve();
- }
- }
- this.HWHClasses.executeTower = executeTower;
+ const farmResults = await farmCaller.send();
- /**
- * Passage of the arena of the titans
- *
- * Прохождение арены титанов
- */
- function testTitanArena() {
- const { executeTitanArena } = HWHClasses;
- return new Promise((resolve, reject) => {
- titAren = new executeTitanArena(resolve, reject);
- titAren.start();
- });
- }
+ let countQuests = 0;
+ let countMail = 0;
+ let questsIds = [];
- /**
- * Passage of the arena of the titans
- *
- * Прохождение арены титанов
- */
- function executeTitanArena(resolve, reject) {
- let titan_arena = [];
- let finishListBattle = [];
- /**
- * ID of the current batch
- *
- * Идетификатор текущей пачки
- */
- let currentRival = 0;
- /**
- * Number of attempts to finish off the pack
- *
- * Количество попыток добития пачки
- */
- let attempts = 0;
- /**
- * Was there an attempt to finish off the current shooting range
- *
- * Была ли попытка добития текущего тира
- */
- let isCheckCurrentTier = false;
- /**
- * Current shooting range
- *
- * Текущий тир
- */
- let currTier = 0;
- /**
- * Number of battles on the current dash
- *
- * Количество битв на текущем тире
- */
- let countRivalsTier = 0;
+ const questFarm = farmResults.result('questFarm', true);
+ countQuests += questFarm.length;
+ countQuests += questIds.length;
+ countMail += Object.keys(farmResults.result('mailFarm')).length;
- let callsStart = {
- calls: [{
- name: "titanArenaGetStatus",
- args: {},
- ident: "titanArenaGetStatus"
- }, {
- name: "teamGetAll",
- args: {},
- ident: "teamGetAll"
- }]
+ const sideResult = farmResults.sideResult('questFarm', true);
+ sideResult.push(...farmResults.sideResult('quest_questsFarm', true));
+
+ for (let side of sideResult) {
+ const quests = [...(side.newQuests ?? []), ...(side.quests ?? [])];
+ for (let quest of quests) {
+ if ((quest.id < 1e6 || (quest.id >= 2e7 && quest.id < 2001e4)) && quest.state == 2) {
+ questsIds.push(quest.id);
+ }
+ }
+ }
+ questsIds = [...new Set(questsIds)];
+
+ while (questsIds.length) {
+ const recursiveCaller = new Caller();
+ const newQuestIds = [];
+
+ for (let questId of questsIds) {
+ if (farmQuestIds.includes(questId)) {
+ continue;
+ }
+ if (questId < 1e6) {
+ recursiveCaller.add({
+ name: 'questFarm',
+ args: { questId },
+ });
+ farmQuestIds.push(questId);
+ countQuests++;
+ } else if (questId >= 2e7 && questId < 2001e4) {
+ farmQuestIds.push(questId);
+ newQuestIds.push(questId);
+ countQuests++;
+ }
+ }
+
+ if (newQuestIds.length) {
+ recursiveCaller.add({
+ name: 'quest_questsFarm',
+ args: { questIds: newQuestIds },
+ });
+ }
+
+ questsIds = [];
+ if (recursiveCaller.isEmpty()) {
+ break;
+ }
+
+ await recursiveCaller.send();
+ const sideResult = recursiveCaller.sideResult('questFarm', true);
+ sideResult.push(...recursiveCaller.sideResult('quest_questsFarm', true));
+
+ for (let side of sideResult) {
+ const quests = [...(side.newQuests ?? []), ...(side.quests ?? [])];
+ for (let quest of quests) {
+ if ((quest.id < 1e6 || (quest.id >= 2e7 && quest.id < 2001e4)) && quest.state == 2) {
+ questsIds.push(quest.id);
+ }
+ }
+ }
+ questsIds = [...new Set(questsIds)];
+ }
+
+ setProgress(I18N('COLLECT_REWARDS_AND_MAIL', { countQuests, countMail }), true);
+ } catch (error) {
+ console.error('Error in questAllFarm:', error);
}
+ }
- this.start = function () {
- send(JSON.stringify(callsStart), startTitanArena);
+ function countdownTimer(seconds, message, onClick = null, autoHide = true) {
+ message = message || I18N('TIMER');
+ const stopTimer = Date.now() + seconds * 1e3;
+ const isOnClick = typeof onClick === 'function';
+ return new Promise((resolve) => {
+ const interval = setInterval(async () => {
+ const now = Date.now();
+ const remaining = (stopTimer - now) / 1000;
+ const clickHandler = isOnClick
+ ? () => {
+ onClick();
+ clearInterval(interval);
+ setProgress('', true);
+ resolve(false);
+ }
+ : undefined;
+
+ setProgress(`${message} ${remaining.toFixed(2)}`, false, clickHandler);
+ if (now > stopTimer) {
+ clearInterval(interval);
+ if (autoHide) {
+ setProgress('', true);
+ }
+ resolve(true);
+ }
+ }, 100);
+ });
+ }
+
+ this.HWHFuncs.countdownTimer = countdownTimer;
+
+ /** Набить килов в горниле душк */
+ async function bossRatingEventSouls() {
+ const [heroGetAll, offerGetAll, pet_getAll] = await Caller.send(['heroGetAll', 'offerGetAll', 'pet_getAll']);
+ let bossEventInfo = offerGetAll.find((e) => e.offerType == 'bossEvent');
+ if (!bossEventInfo) {
+ setProgress(I18N('EVENT_IS_OVER'), true);
+ return;
}
- function startTitanArena(data) {
- let titanArena = data.results[0].result.response;
- if (titanArena.status == 'disabled') {
- endTitanArena('disabled', titanArena);
- return;
- }
+ const countKills = +(await popup.confirm(I18N('SET_COUNT_KILLS'), [
+ { msg: I18N('BTN_GO'), isInput: true, default: 250, color: 'green' },
+ { result: false, isClose: true },
+ ]));
- let teamGetAll = data.results[1].result.response;
- titan_arena = teamGetAll.titan_arena;
+ if (!countKills) {
+ return;
+ }
- checkTier(titanArena)
+ if (bossEventInfo.progress.score > countKills) {
+ setProgress(I18N('MORE_ENEMIES_KILLED', { countKills }));
+ setTimeout(rewardBossRatingEventSouls, 2500, bossEventInfo);
+ return;
}
+ const availablePets = Object.values(pet_getAll).map((e) => e.id);
+ const usedHeroes = bossEventInfo.progress.usedHeroes;
+ const heroList = [];
- function checkTier(titanArena) {
- if (titanArena.status == "peace_time") {
- endTitanArena('Peace_time', titanArena);
- return;
- }
- currTier = titanArena.tier;
- if (currTier) {
- setProgress(`${I18N('TITAN_ARENA')}: ${I18N('LEVEL')} ${currTier}`);
+ for (let heroId in heroGetAll) {
+ let hero = heroGetAll[heroId];
+ if (usedHeroes.includes(hero.id)) {
+ continue;
}
+ heroList.push(hero.id);
+ }
- if (titanArena.status == "completed_tier") {
- titanArenaCompleteTier();
- return;
+ if (!heroList.length) {
+ setProgress(I18N('NO_HEROES'), true);
+ return;
+ }
+
+ const pet = availablePets.includes(6005) ? 6005 : availablePets[Math.floor(Math.random() * availablePets.length)];
+ const petLib = lib.getData('pet');
+ let count = 1;
+
+ for (const heroId of heroList) {
+ const args = {
+ heroes: [heroId],
+ pet,
+ };
+ /** Поиск питомца для героя */
+ for (const petId of availablePets) {
+ if (petLib[petId].favorHeroes.includes(heroId)) {
+ args.favor = {
+ [heroId]: petId,
+ };
+ break;
+ }
}
- /**
- * Checking for the possibility of a raid
- * Проверка на возможность рейда
- */
- if (titanArena.canRaid) {
- titanArenaStartRaid();
+
+ let battleInfo, offerGetAll;
+ try {
+ [battleInfo, offerGetAll] = await Caller.send([
+ {
+ name: 'bossRating_startBattle',
+ args,
+ },
+ 'offerGetAll',
+ ]);
+ count++;
+ } catch(e) {
+ console.error(e);
+ setProgress(I18N('RESTART_TRY_AGAIN_LATER'), true);
return;
}
- /**
- * Check was an attempt to achieve the current shooting range
- * Проверка была ли попытка добития текущего тира
- */
- if (!isCheckCurrentTier) {
- checkRivals(titanArena.rivals);
- return;
+
+ bossEventInfo = offerGetAll.find((e) => e.offerType == 'bossEvent');
+ if (bossEventInfo.progress.score > countKills) {
+ break;
}
+ setProgress(I18N('ENEMIES_KILLED_AND_HEROES_USED', { score: bossEventInfo.progress.score, count }));
+ }
- endTitanArena('Done or not canRaid', titanArena);
+ rewardBossRatingEventSouls(bossEventInfo);
+ }
+ /** Сбор награды из Горнила Душ */
+ async function rewardBossRatingEventSouls(bossEventInfo) {
+ if (!bossEventInfo) {
+ setProgress(I18N('EVENT_IS_OVER'), true);
+ return;
}
- /**
- * Submit dash information for verification
- *
- * Отправка информации о тире на проверку
- */
- function checkResultInfo(data) {
- let titanArena = data.results[0].result.response;
- checkTier(titanArena);
+
+ const farmedChests = bossEventInfo.progress.farmedChests;
+ const score = bossEventInfo.progress.score;
+ // setProgress('Количество убитых врагов: ' + score);
+ const revard = bossEventInfo.reward;
+
+ const caller = new Caller();
+ for (let i = 1; i < 10; i++) {
+ if (farmedChests.includes(i)) {
+ continue;
+ }
+ if (score < revard[i].score) {
+ break;
+ }
+ caller.add({
+ name: 'bossRating_getReward',
+ args: {
+ rewardId: i,
+ },
+ });
}
- /**
- * Finish the current tier
- *
- * Завершить текущий тир
- */
- function titanArenaCompleteTier() {
- isCheckCurrentTier = false;
- let calls = [{
- name: "titanArenaCompleteTier",
- args: {},
- ident: "body"
- }];
- send(JSON.stringify({calls}), checkResultInfo);
+
+ if (caller.isEmpty()) {
+ setProgress(I18N('NOTHING_TO_COLLECT'), true);
+ return;
}
- /**
- * Gathering points to be completed
- *
- * Собираем точки которые нужно добить
- */
- function checkRivals(rivals) {
- finishListBattle = [];
- for (let n in rivals) {
- if (rivals[n].attackScore < 250) {
- finishListBattle.push(n);
- }
- }
- console.log('checkRivals', finishListBattle);
- countRivalsTier = finishListBattle.length;
- roundRivals();
+ try {
+ await caller.send()
+ } catch(e) {
+ console.error(e);
+ setProgress(I18N('RESTART_TRY_AGAIN_LATER'), true);
+ return;
}
- /**
- * Selecting the next point to finish off
- *
- * Выбор следующей точки для добития
- */
- function roundRivals() {
- let countRivals = finishListBattle.length;
- if (!countRivals) {
- /**
- * Whole range checked
- *
- * Весь тир проверен
- */
- isCheckCurrentTier = true;
- titanArenaGetStatus();
- return;
- }
- // setProgress('TitanArena: Уровень ' + currTier + ' Бои: ' + (countRivalsTier - countRivals + 1) + '/' + countRivalsTier);
- currentRival = finishListBattle.pop();
- attempts = +currentRival;
- // console.log('roundRivals', currentRival);
- titanArenaStartBattle(currentRival);
+
+ const results = caller.result(false, true);
+ console.log(results);
+ if (results?.length) {
+ setProgress(`${I18N('COLLECTED')} ${results?.length} ${I18N('REWARD')}`, true);
}
- /**
- * The start of a solo battle
- *
- * Начало одиночной битвы
- */
- function titanArenaStartBattle(rivalId) {
- let calls = [{
- name: "titanArenaStartBattle",
- args: {
- rivalId: rivalId,
- titans: titan_arena
- },
- ident: "body"
- }];
- send(JSON.stringify({calls}), calcResult);
+ }
+ /**
+ * Spin the Seer
+ *
+ * Покрутить провидца
+ */
+ async function rollAscension() {
+ const user = await Caller.send('userGetInfo');
+ const i47 = user.refillable.find((i) => i.id == 47);
+ if (i47?.amount) {
+ await Caller.send({ name: 'ascensionChest_open', args: { paid: false, amount: 1 } });
+ setProgress(I18N('DONE'), true);
+ } else {
+ setProgress(I18N('NOT_ENOUGH_AP'), true);
}
- /**
- * Calculation of the results of the battle
- *
- * Расчет результатов боя
- */
- function calcResult(data) {
- let battlesInfo = data.results[0].result.response.battle;
- /**
- * If attempts are equal to the current battle number we make
- * Если попытки равны номеру текущего боя делаем прерасчет
- */
- if (attempts == currentRival) {
- preCalcBattle(battlesInfo);
- return;
- }
- /**
- * If there are still attempts, we calculate a new battle
- * Если попытки еще есть делаем расчет нового боя
- */
- if (attempts > 0) {
- attempts--;
- calcBattleResult(battlesInfo)
- .then(resultCalcBattle);
- return;
- }
- /**
- * Otherwise, go to the next opponent
- * Иначе переходим к следующему сопернику
- */
- roundRivals();
+ }
+
+ /**
+ * Collect gifts for the New Year
+ *
+ * Собрать подарки на новый год
+ */
+ async function getGiftNewYear() {
+ const response = await Caller.send({ name: 'newYearGiftGet', args: { type: 0 } });
+ const gifts = response.gifts;
+ const calls = gifts
+ .filter((e) => e.opened == 0)
+ .map((e) => ({
+ name: 'newYearGiftOpen',
+ args: { giftId: e.id },
+ }));
+
+ if (!calls.length) {
+ setProgress(I18N('NY_NO_GIFTS'), 5000);
+ return;
}
- /**
- * Processing the results of the battle calculation
- *
- * Обработка результатов расчета битвы
- */
- async function resultCalcBattle(resultBattle) {
- // console.log('resultCalcBattle', currentRival, attempts, resultBattle.result.win);
- /**
- * If the current calculation of victory is not a chance or the attempt ended with the finish the battle
- * Если текущий расчет победа или шансов нет или попытки кончились завершаем бой
- */
- if (resultBattle.result.win || !attempts) {
- let { progress, result } = resultBattle;
- /*
- if (!resultBattle.result.win && isChecked('tryFixIt_v2')) {
- const bFix = new BestOrWinFixBattle(resultBattle.battleData);
- bFix.isGetTimer = false;
- bFix.maxTimer = 100;
- const resultFix = await bFix.start(Date.now() + 6e4, 500);
- if (resultFix.value > 0) {
- progress = resultFix.progress;
- result = resultFix.result;
- }
- }
- */
- titanArenaEndBattle({
- progress,
- result,
- rivalId: resultBattle.battleData.typeId,
- });
- return;
- }
- /**
- * If not victory and there are attempts we start a new battle
- * Если не победа и есть попытки начинаем новый бой
- */
- titanArenaStartBattle(resultBattle.battleData.typeId);
+
+ const results = await Caller.send(calls);
+ console.log(results);
+ const msg = I18N('NY_GIFTS_COLLECTED', { count: results.length });
+ console.log(msg);
+ setProgress(msg, 5000);
+ }
+
+ async function updateArtifacts() {
+ const count = +(await popup.confirm(I18N('SET_NUMBER_LEVELS'), [
+ { msg: I18N('BTN_GO'), isInput: true, default: 10, color: 'green' },
+ { result: false, isClose: true },
+ ]));
+ if (!count) {
+ return;
}
- /**
- * Returns the promise of calculating the results of the battle
- *
- * Возращает промис расчета результатов битвы
- */
- function getBattleInfo(battle, isRandSeed) {
- return new Promise(function (resolve) {
- battle = structuredClone(battle);
- if (isRandSeed) {
- battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3);
+ const quest = new questRun();
+ await quest.autoInit();
+ const heroes = Object.values(quest.questInfo['heroGetAll']);
+ const inventory = quest.questInfo['inventoryGet'];
+ const calls = [];
+ for (let i = count; i > 0; i--) {
+ const upArtifact = quest.getUpgradeArtifact();
+ if (!upArtifact.heroId) {
+ if (
+ await popup.confirm(I18N('POSSIBLE_IMPROVE_LEVELS', { count: calls.length }), [
+ { msg: I18N('BTN_YES'), result: true, color: 'green' },
+ { result: false, isClose: true },
+ ])
+ ) {
+ break;
+ } else {
+ return;
}
- // console.log(battle.seed);
- BattleCalc(battle, "get_titanClanPvp", e => resolve(e));
- });
- }
- /**
- * Recalculate battles
- *
- * Прерасчтет битвы
- */
- function preCalcBattle(battle) {
- let actions = [getBattleInfo(battle, false)];
- const countTestBattle = getInput('countTestBattle');
- for (let i = 0; i < countTestBattle; i++) {
- actions.push(getBattleInfo(battle, true));
}
- Promise.all(actions)
- .then(resultPreCalcBattle);
+ const hero = heroes.find((e) => e.id == upArtifact.heroId);
+ hero.artifacts[upArtifact.slotId].level++;
+ inventory[upArtifact.costCurrency][upArtifact.costId] -= upArtifact.costValue;
+ calls.push({
+ name: 'heroArtifactLevelUp',
+ args: {
+ heroId: upArtifact.heroId,
+ slotId: upArtifact.slotId,
+ },
+ });
}
- /**
- * Processing the results of the battle recalculation
- *
- * Обработка результатов прерасчета битвы
- */
- function resultPreCalcBattle(e) {
- let wins = e.map(n => n.result.win);
- let firstBattle = e.shift();
- let countWin = wins.reduce((w, s) => w + s);
- const countTestBattle = getInput('countTestBattle');
- console.log('resultPreCalcBattle', `${countWin}/${countTestBattle}`)
- if (countWin > 0) {
- attempts = getInput('countAutoBattle');
- } else {
- attempts = 0;
- }
- resultCalcBattle(firstBattle);
+
+ if (!calls.length) {
+ console.log(I18N('NOT_ENOUGH_RESOURECES'));
+ setProgress(I18N('NOT_ENOUGH_RESOURECES'), false);
+ return;
}
- /**
- * Complete an arena battle
- *
- * Завершить битву на арене
- */
- function titanArenaEndBattle(args) {
- let calls = [{
- name: "titanArenaEndBattle",
- args,
- ident: "body"
- }];
- send(JSON.stringify({calls}), resultTitanArenaEndBattle);
+ try {
+ const results = await Caller.send(calls);
+ console.log(I18N('IMPROVED_LEVELS', { count: results.length }));
+ setProgress(I18N('IMPROVED_LEVELS', { count: results.length }), false);
+ } catch (e) {
+ console.log(I18N('NOT_ENOUGH_RESOURECES'));
+ setProgress(I18N('NOT_ENOUGH_RESOURECES'), false);
}
+ }
- function resultTitanArenaEndBattle(e) {
- let attackScore = e.results[0].result.response.attackScore;
- let numReval = countRivalsTier - finishListBattle.length;
- setProgress(`${I18N('TITAN_ARENA')}: ${I18N('LEVEL')} ${currTier} ${I18N('BATTLES')}: ${numReval}/${countRivalsTier} - ${attackScore}`);
- // console.log('resultTitanArenaEndBattle', e)
- console.log('resultTitanArenaEndBattle', numReval + '/' + countRivalsTier, attempts)
- roundRivals();
+ window.sign = a => {
+ const i = this['\x78\x79\x7a'];
+ return md5([i['\x6e\x61\x6d\x65'], i['\x76\x65\x72\x73\x69\x6f\x6e'], i['\x61\x75\x74\x68\x6f\x72'], ~(a % 1e3)]['\x6a\x6f\x69\x6e']('\x5f'))
+ }
+
+ async function updateSkins() {
+ const count = +(await popup.confirm(I18N('SET_NUMBER_LEVELS'), [
+ { msg: I18N('BTN_GO'), isInput: true, default: 10, color: 'green' },
+ { result: false, isClose: true },
+ ]));
+ if (!count) {
+ return;
}
- /**
- * Arena State
- *
- * Состояние арены
- */
- function titanArenaGetStatus() {
- let calls = [{
- name: "titanArenaGetStatus",
- args: {},
- ident: "body"
- }];
- send(JSON.stringify({calls}), checkResultInfo);
- }
- /**
- * Arena Raid Request
- *
- * Запрос рейда арены
- */
- function titanArenaStartRaid() {
- let calls = [{
- name: "titanArenaStartRaid",
+
+ const quest = new questRun();
+ await quest.autoInit();
+ const heroes = Object.values(quest.questInfo['heroGetAll']);
+ const inventory = quest.questInfo['inventoryGet'];
+ const calls = [];
+ for (let i = count; i > 0; i--) {
+ const upSkin = quest.getUpgradeSkin();
+ if (!upSkin.heroId) {
+ if (
+ await popup.confirm(I18N('POSSIBLE_IMPROVE_LEVELS', { count: calls.length }), [
+ { msg: I18N('BTN_YES'), result: true, color: 'green' },
+ { result: false, isClose: true },
+ ])
+ ) {
+ break;
+ } else {
+ return;
+ }
+ }
+ const hero = heroes.find((e) => e.id == upSkin.heroId);
+ hero.skins[upSkin.skinId]++;
+ inventory[upSkin.costCurrency][upSkin.costCurrencyId] -= upSkin.cost;
+ calls.push({
+ name: 'heroSkinUpgrade',
args: {
- titans: titan_arena
+ heroId: upSkin.heroId,
+ skinId: upSkin.skinId,
},
- ident: "body"
- }];
- send(JSON.stringify({calls}), calcResults);
+ });
}
- function calcResults(data) {
- let battlesInfo = data.results[0].result.response;
- let {attackers, rivals} = battlesInfo;
-
- let promises = [];
- for (let n in rivals) {
- rival = rivals[n];
- promises.push(calcBattleResult({
- attackers: attackers,
- defenders: [rival.team],
- seed: rival.seed,
- typeId: n,
- }));
- }
-
- Promise.all(promises)
- .then(results => {
- const endResults = {};
- for (let info of results) {
- let id = info.battleData.typeId;
- endResults[id] = {
- progress: info.progress,
- result: info.result,
- }
- }
- titanArenaEndRaid(endResults);
- });
+ if (!calls.length) {
+ console.log(I18N('NOT_ENOUGH_RESOURECES'));
+ setProgress(I18N('NOT_ENOUGH_RESOURECES'), false);
+ return;
}
- function calcBattleResult(battleData) {
- return new Promise(function (resolve, reject) {
- BattleCalc(battleData, "get_titanClanPvp", resolve);
- });
+ try {
+ const results = await Caller.send(calls);
+ console.log(I18N('IMPROVED_LEVELS', { count: results.length }));
+ setProgress(I18N('IMPROVED_LEVELS', { count: results.length }), false);
+ } catch (e) {
+ console.log(I18N('NOT_ENOUGH_RESOURECES'));
+ setProgress(I18N('NOT_ENOUGH_RESOURECES'), false);
}
+ }
- /**
- * Sending Raid Results
- *
- * Отправка результатов рейда
- */
- function titanArenaEndRaid(results) {
- titanArenaEndRaidCall = {
- calls: [{
- name: "titanArenaEndRaid",
- args: {
- results
- },
- ident: "body"
- }]
- }
- send(JSON.stringify(titanArenaEndRaidCall), checkRaidResults);
+ function getQuestionInfo(img, nameOnly = false) {
+ const libHeroes = Object.values(lib.data.hero);
+ const parts = img.split(':');
+ const id = parts[1];
+ switch (parts[0]) {
+ case 'titanArtifact_id':
+ return cheats.translate("LIB_TITAN_ARTIFACT_NAME_" + id);
+ case 'titan':
+ return cheats.translate("LIB_HERO_NAME_" + id);
+ case 'skill':
+ return cheats.translate("LIB_SKILL_" + id);
+ case 'inventoryItem_gear':
+ return cheats.translate("LIB_GEAR_NAME_" + id);
+ case 'inventoryItem_coin':
+ return cheats.translate("LIB_COIN_NAME_" + id);
+ case 'artifact':
+ if (nameOnly) {
+ return cheats.translate("LIB_ARTIFACT_NAME_" + id);
+ }
+ heroes = libHeroes.filter(h => h.id < 100 && h.artifacts.includes(+id));
+ return {
+ /** Как называется этот артефакт? */
+ name: cheats.translate("LIB_ARTIFACT_NAME_" + id),
+ /** Какому герою принадлежит этот артефакт? */
+ heroes: heroes.map(h => cheats.translate("LIB_HERO_NAME_" + h.id))
+ };
+ case 'hero':
+ if (nameOnly) {
+ return cheats.translate("LIB_HERO_NAME_" + id);
+ }
+ artifacts = lib.data.hero[id].artifacts;
+ return {
+ /** Как зовут этого героя? */
+ name: cheats.translate("LIB_HERO_NAME_" + id),
+ /** Какой артефакт принадлежит этому герою? */
+ artifact: artifacts.map(a => cheats.translate("LIB_ARTIFACT_NAME_" + a))
+ };
}
+ }
- function checkRaidResults(data) {
- results = data.results[0].result.response.results;
- isSucsesRaid = true;
- for (let i in results) {
- isSucsesRaid &&= (results[i].attackScore >= 250);
+ function hintQuest(quest) {
+ const result = {};
+ if (quest?.questionIcon) {
+ const info = getQuestionInfo(quest.questionIcon);
+ if (info?.heroes) {
+ /** Какому герою принадлежит этот артефакт? */
+ result.answer = quest.answers.filter(e => info.heroes.includes(e.answerText.slice(1)));
}
-
- if (isSucsesRaid) {
- titanArenaCompleteTier();
+ if (info?.artifact) {
+ /** Какой артефакт принадлежит этому герою? */
+ result.answer = quest.answers.filter(e => info.artifact.includes(e.answerText.slice(1)));
+ }
+ if (typeof info == 'string') {
+ result.info = { name: info };
} else {
- titanArenaGetStatus();
+ result.info = info;
}
}
- function titanArenaFarmDailyReward() {
- titanArenaFarmDailyRewardCall = {
- calls: [{
- name: "titanArenaFarmDailyReward",
- args: {},
- ident: "body"
- }]
- }
- send(JSON.stringify(titanArenaFarmDailyRewardCall), () => {console.log('Done farm daily reward')});
+ if (quest.answers[0]?.answerIcon) {
+ result.answer = quest.answers.filter(e => quest.question.includes(getQuestionInfo(e.answerIcon, true)))
}
- function endTitanArena(reason, info) {
- if (!['Peace_time', 'disabled'].includes(reason)) {
- titanArenaFarmDailyReward();
- }
- console.log(reason, info);
- setProgress(`${I18N('TITAN_ARENA')} ${I18N('COMPLETED')}!`, true);
- resolve();
+ if ((!result?.answer || !result.answer.length) && !result.info?.name) {
+ return false;
}
- }
-
- /**
- * Arena battle calculator helper functions
- *
- * Вспомогательные функции для расчета битв арены
- */
- /**
- * Calculate win rate for arena battle
- *
- * Расчет вероятности победы в битве арены
- */
- async function calcArenaBattleWinRate(attackers, defenders, battleType = 'arena') {
- return new Promise((resolve, reject) => {
- const battleData = {
- attackers: attackers,
- defenders: defenders,
- seed: Math.random(),
- typeId: battleType
- };
-
- BattleCalc(battleData, getBattleType(battleType), (result) => {
- if (result && result.result) {
- resolve(result.result.win ? 1 : 0);
- } else {
- resolve(0);
- }
- });
- });
- }
+ let resultText = '';
+ if (result?.info) {
+ resultText += I18N('PICTURE') + result.info.name;
+ }
+ console.log(result);
+ if (result?.answer && result.answer.length) {
+ resultText += I18N('ANSWER') + result.answer[0].id + (!result.answer[0].answerIcon ? ' - ' + result.answer[0].answerText : '');
+ }
- /**
- * Select best team for opponent
- *
- * Выбор лучшей команды против соперника
- */
- async function selectBestTeamForOpponent(availableTeams, opponentTeam, battleType = 'arena') {
- let bestTeam = null;
- let bestWinRate = 0;
-
- // Try current team first
- if (availableTeams.current) {
- const winRate = await calcArenaBattleWinRate(availableTeams.current, opponentTeam, battleType);
- if (winRate > bestWinRate) {
- bestTeam = availableTeams.current;
- bestWinRate = winRate;
- }
- }
-
- // Try alternative teams if current team has low win rate
- if (bestWinRate < 0.5 && availableTeams.alternatives) {
- for (const team of availableTeams.alternatives) {
- const winRate = await calcArenaBattleWinRate(team, opponentTeam, battleType);
- if (winRate > bestWinRate) {
- bestTeam = team;
- bestWinRate = winRate;
- }
- }
- }
-
- return { team: bestTeam, winRate: bestWinRate };
+ return resultText;
}
- /**
- * Evaluate opponent difficulty
- *
- * Оценка сложности соперника
- */
- function evaluateOpponentDifficulty(opponent) {
- // Calculate power ratio (opponent power / your power)
- const powerRatio = opponent.power / (userInfo.power || 1);
-
- // Lower rank = easier opponent
- const rankDifficulty = opponent.rank || 999999;
-
- // Return difficulty score (lower = easier)
- return {
- opponent: opponent,
- difficulty: powerRatio + (rankDifficulty / 1000000),
- powerRatio: powerRatio,
- rank: rankDifficulty
+ async function farmBattlePass() {
+ const isFarmReward = (reward) => {
+ return !(reward?.buff || reward?.fragmentHero || reward?.bundleHeroReward);
};
- }
- /**
- * Arena auto-attack execution
- *
- * Автоматическое прохождение арены
- */
- function executeArena(resolve, reject) {
- this.resolve = resolve;
- this.reject = reject;
- this.arenaType = 'arena';
- this.attemptsRemaining = 0;
- this.victories = 0;
- this.arenaInfo = null;
- this.teamInfo = null;
- this.opponents = [];
-
- this.start = async function(arenaType = 'arena') {
- this.arenaType = arenaType;
- setProgress(`${I18N('ARENA')}: ${I18N('INITIALIZING')}...`);
-
- try {
- // Get arena status and team data
- await this.getArenaStatus();
- await this.getAvailableTeams();
-
- if (this.attemptsRemaining <= 0) {
- this.end('No attempts remaining');
- return;
- }
-
- // Find and sort opponents by difficulty
- this.findEasiestOpponents();
-
- // Execute battles
- await this.executeBattles();
-
- } catch (error) {
- console.error('Arena execution error:', error);
- this.end('Error: ' + error.message);
+ const battlePassProcess = (pass) => {
+ if (!pass.id) {
+ return [];
}
- }
-
- this.getArenaStatus = async function() {
- const apiName = this.arenaType === 'grand' ? 'grandGetInfo' : 'arenaGetInfo';
- const calls = [{
- name: apiName,
- args: {},
- ident: apiName
- }];
-
- const response = await Send(JSON.stringify({calls}));
- this.arenaInfo = response.results[0].result.response;
- this.attemptsRemaining = this.arenaInfo.attempts || 0;
- this.opponents = this.arenaInfo.rivals || [];
-
- setProgress(`${I18N('ARENA')}: ${I18N('ATTEMPTS')} ${this.attemptsRemaining}`);
- }
-
- this.getAvailableTeams = async function() {
- const calls = [{
- name: "teamGetAll",
- args: {},
- ident: "teamGetAll"
- }, {
- name: "teamGetFavor",
- args: {},
- ident: "teamGetFavor"
- }, {
- name: "heroGetAll",
- args: {},
- ident: "heroGetAll"
- }];
-
- const response = await Send(JSON.stringify({calls}));
- this.teamInfo = {
- teams: response.results[0].result.response,
- favor: response.results[1].result.response,
- heroes: Object.values(response.results[2].result.response)
- };
- }
-
- this.findEasiestOpponents = function() {
- // Sort opponents by difficulty (easiest first)
- this.opponents = this.opponents
- .map(opponent => evaluateOpponentDifficulty(opponent))
- .sort((a, b) => a.difficulty - b.difficulty);
-
- console.log('Sorted opponents by difficulty:', this.opponents.map(o => ({
- rank: o.rank,
- power: o.opponent.power,
- difficulty: o.difficulty
- })));
- }
-
- this.executeBattles = async function() {
- for (let i = 0; i < this.attemptsRemaining && this.opponents.length > 0; i++) {
- const opponent = this.opponents.shift();
- setProgress(`${I18N('ARENA')}: ${I18N('BATTLE')} ${i + 1}/${this.attemptsRemaining} - ${I18N('RANK')} ${opponent.rank}`);
-
- try {
- const result = await this.executeBattle(opponent);
- if (result.win) {
- this.victories++;
+ const levels = Object.values(lib.data.battlePass.level).filter((x) => x.battlePass == pass.id);
+ const last_level = levels[levels.length - 1];
+ let actual = Math.max(...levels.filter((p) => pass.exp >= p.experience).map((p) => p.level));
+
+ if (pass.exp > last_level.experience) {
+ actual = last_level.level + (pass.exp - last_level.experience) / last_level.experienceByLevel;
+ }
+ const calls = [];
+ for (let i = 1; i <= actual; i++) {
+ const level = i >= last_level.level ? last_level : levels.find((l) => l.level === i);
+ const reward = { free: level?.freeReward, paid: level?.paidReward };
+
+ if (!pass.rewards[i]?.free && isFarmReward(reward.free)) {
+ const args = { level: i, free: true };
+ if (!pass.gold) {
+ args.id = pass.id;
}
- } catch (error) {
- console.error('Battle error:', error);
+ calls.push({ name: 'battlePass_farmReward', args });
+ }
+ if (pass.ticket && !pass.rewards[i]?.paid && isFarmReward(reward.paid)) {
+ const args = { level: i, free: false };
+ if (!pass.gold) {
+ args.id = pass.id;
+ }
+ calls.push({ name: 'battlePass_farmReward', args });
}
}
-
- this.end(`Completed ${this.victories}/${this.attemptsRemaining} victories`);
- }
-
- this.executeBattle = async function(opponent) {
- // Get available teams
- const availableTeams = this.getAvailableTeamsForBattle();
-
- // Select best team
- const teamSelection = await selectBestTeamForOpponent(
- availableTeams,
- opponent.opponent.team,
- this.arenaType
- );
-
- if (!teamSelection.team || teamSelection.winRate < 0.3) {
- console.log('Skipping opponent - no winning team found');
- return { win: false };
- }
-
- // Start battle
- const battleResult = await this.startArenaBattle(opponent.opponent.id, teamSelection.team);
-
- // End battle
- await this.endArenaBattle(battleResult);
-
- return battleResult;
+ return calls;
+ };
+
+ const [battlePassInfo, battlePassSpecial] = await Caller.send(['battlePass_getInfo', 'battlePass_getSpecial']);
+
+ const passes = [{ ...battlePassInfo?.battlePass, gold: true }, ...Object.values(battlePassSpecial)];
+
+ const calls = passes.flatMap((p) => battlePassProcess(p));
+
+ if (!calls.length) {
+ setProgress(I18N('NOTHING_TO_COLLECT'));
+ return;
}
-
- this.getAvailableTeamsForBattle = function() {
- const arenaTeamKey = this.arenaType === 'grand' ? 'grand' : 'arena';
- const currentTeam = this.teamInfo.teams[arenaTeamKey];
- const favor = this.teamInfo.favor[arenaTeamKey] || {};
-
- // Create current team structure
- const currentTeamData = {
- heroes: currentTeam.filter(id => id < 6000),
- pet: currentTeam.filter(id => id >= 6000).pop(),
- favor: favor
- };
-
- // Create alternative teams (top 5 heroes by power)
- const topHeroes = this.teamInfo.heroes
- .sort((a, b) => b.power - a.power)
- .slice(0, 5)
- .map(h => h.id);
-
- const alternativeTeam = {
- heroes: topHeroes,
- pet: currentTeamData.pet,
- favor: favor
- };
-
- return {
- current: currentTeamData,
- alternatives: [alternativeTeam]
- };
+
+ try {
+ const results = await Caller.send(calls);
+ setProgress(I18N('SEASON_REWARD_COLLECTED', { count: results.length }), true);
+ } catch (error) {
+ console.log(error);
+ setProgress(I18N('SOMETHING_WENT_WRONG'));
}
-
- this.startArenaBattle = async function(rivalId, team) {
- const apiName = this.arenaType === 'grand' ? 'grandStartBattle' : 'arenaStartBattle';
- const calls = [{
- name: apiName,
- args: {
- rivalId: rivalId,
- heroes: team.heroes,
- pet: team.pet,
- favor: team.favor
- },
- ident: apiName
- }];
-
- const response = await Send(JSON.stringify({calls}));
- const battleData = response.results[0].result.response.battle;
-
- // Calculate battle result
- return new Promise((resolve) => {
- BattleCalc(battleData, getBattleType(this.arenaType), (result) => {
- resolve({
- win: result.result.win,
- progress: result.progress,
- result: result.result
- });
+ }
+
+ async function sellHeroSoulsForGold() {
+ const [inventory, heroes] = await Caller.send(['inventoryGet', 'heroGetAll']);
+
+ const calls = [];
+ for (let i in inventory.fragmentHero) {
+ if (heroes[i] && heroes[i].star == 6) {
+ calls.push({
+ name: 'inventorySell',
+ args: {
+ type: 'hero',
+ libId: i,
+ amount: inventory.fragmentHero[i],
+ fragment: true,
+ },
});
- });
- }
-
- this.endArenaBattle = async function(battleResult) {
- const apiName = this.arenaType === 'grand' ? 'grandEndBattle' : 'arenaEndBattle';
- const calls = [{
- name: apiName,
- args: {
- progress: battleResult.progress,
- result: battleResult.result
- },
- ident: apiName
- }];
-
- await Send(JSON.stringify({calls}));
- }
-
- this.end = function(message) {
- console.log('Arena execution ended:', message);
- setProgress(`${I18N('ARENA')}: ${message}`, true);
- this.resolve();
+ }
}
- }
- /**
- * Wrapper functions for arena battles
- *
- * Функции-обертки для битв арены
- */
+ if (!calls.length) {
+ console.log(0);
+ return 0;
+ }
- function testArena() {
- const { executeArena } = HWHClasses;
- return new Promise((resolve, reject) => {
- const arena = new executeArena(resolve, reject);
- arena.start('arena');
- });
+ const rewards = await Caller.send(calls);
+ const gold = rewards.reduce((sum, r) => sum + (r?.gold || 0), 0);
+ setProgress(I18N('GOLD_RECEIVED', { gold }), true);
}
- function testGrandArena() {
- const { executeArena } = HWHClasses;
- return new Promise((resolve, reject) => {
- const arena = new executeArena(resolve, reject);
- arena.start('grand');
- });
- }
+ class Caller {
+ static globalHooks = {
+ onError: null,
+ };
- function testBothArenas() {
- return new Promise(async (resolve, reject) => {
- try {
- await testArena();
- await testGrandArena();
- resolve();
- } catch (error) {
- reject(error);
+ constructor(calls = null) {
+ this.calls = [];
+ this.results = {};
+ this.sideResults = {};
+ if (calls) {
+ this.add(calls);
}
- });
- }
+ }
- this.HWHClasses.executeTitanArena = executeTitanArena;
- this.HWHClasses.executeArena = executeArena;
+ static setGlobalHook(event, callback) {
+ if (this.globalHooks[event] !== undefined) {
+ this.globalHooks[event] = callback;
+ } else {
+ throw new Error(`Unknown event: ${event}`);
+ }
+ }
- function hackGame() {
- const self = this;
- selfGame = null;
- bindId = 1e9;
- this.libGame = null;
- this.doneLibLoad = () => {};
+ addCall(call) {
+ const { name = call, args = {} } = typeof call === 'object' ? call : { name: call };
+ this.calls.push({ name, args });
+ return this;
+ }
- /**
- * 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' },
+ add(name) {
+ if (Array.isArray(name)) {
+ name.forEach((call) => this.addCall(call));
+ } else {
+ this.addCall(name);
+ }
+ return this;
+ }
- { 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' },
- ];
+ handleError(error) {
+ const errorName = error.name;
+ const errorDescription = error.description;
- /**
- * 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;
- },
- };
+ if (Caller.globalHooks.onError) {
+ const shouldThrow = Caller.globalHooks.onError(error);
+ if (shouldThrow === false) {
+ return;
+ }
+ }
- /**
- * 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 + '_'];
- },
- });
+ if (error.call) {
+ const callInfo = error.call;
+ throw new Error(`${errorName} in ${callInfo.name}: ${errorDescription}\n` + `Args: ${JSON.stringify(callInfo.args)}\n`);
+ } else if (errorName === 'common\\rpc\\exception\\InvalidRequest') {
+ throw new Error(`Invalid request: ${errorDescription}`);
+ } else {
+ throw new Error(`Unknown error: ${errorName} - ${errorDescription}`);
}
}
- /**
- * 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);
+ async send() {
+ if (!this.calls.length) {
+ throw new Error('No calls to send.');
}
- 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')](),
- });
+
+ const identToNameMap = {};
+ const callsWithIdent = this.calls.map((call, index) => {
+ const ident = this.calls.length === 1 ? 'body' : `group_${index}_body`;
+ identToNameMap[ident] = call.name;
+ return { ...call, ident };
});
- 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];
+ try {
+ const response = await Send({ calls: callsWithIdent });
+
+ if (response.error) {
+ this.handleError(response.error);
+ }
+
+ if (!response.results) {
+ throw new Error('Invalid response format: missing "results" field');
+ }
+
+ response.results.forEach((result) => {
+ const name = identToNameMap[result.ident];
+ if (!this.results[name]) {
+ this.results[name] = [];
+ this.sideResults[name] = [];
+ }
+ this.results[name].push(result.result.response);
+ const sideResults = {};
+ for (const key of Object.keys(result.result)) {
+ if (key === 'response') continue;
+ sideResults[key] = result.result[key];
+ }
+ this.sideResults[name].push(sideResults);
+ });
+ } catch (error) {
+ throw error;
}
+ return this;
}
- /**
- * 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];
+ result(name, forceArray = false) {
+ const results = name ? this.results[name] || [] : Object.values(this.results).flat();
+ return forceArray || results.length !== 1 ? results : results[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];
+ sideResult(name, forceArray = false) {
+ const results = name ? this.sideResults[name] || [] : Object.values(this.sideResults).flat();
+ return forceArray || results.length !== 1 ? results : results[0];
}
- /**
- * 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];
+ async execute(name) {
+ try {
+ await this.send();
+ return this.result(name);
+ } catch (error) {
+ throw error;
+ }
}
- 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;
+ clear() {
+ this.calls = [];
+ this.results = {};
+ return this;
}
- /**
- * 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);
+ isEmpty() {
+ return this.calls.length === 0 && Object.keys(this.results).length === 0;
+ }
- 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);
- }
- };
+ static async send(calls) {
+ return new Caller(calls).execute();
+ }
+ }
- 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);
- }
- };
+ this.Caller = Caller;
- 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')
- );
+ /*
+ // Примеры использования
+ (async () => {
+ // Короткий вызов
+ await new Caller('inventoryGet').execute();
+ // Простой вызов
+ let result = await new Caller().add('inventoryGet').execute();
+ console.log('Inventory Get Result:', result);
- 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)]))
- );
+ // Сложный вызов
+ let caller = new Caller();
+ await caller
+ .add([
+ {
+ name: 'inventoryGet',
+ args: {},
+ },
+ {
+ name: 'heroGetAll',
+ args: {},
+ },
+ ])
+ .send();
+ console.log('Inventory Get Result:', caller.result('inventoryGet'));
+ console.log('Hero Get All Result:', caller.result('heroGetAll'));
- 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);
- }
- };
+ // Очистка всех данных
+ caller.clear();
+ })();
+ */
- 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);
- };
+ /**
+ * Script for beautiful dialog boxes
+ *
+ * Скрипт для красивых диалоговых окошек
+ */
+ const popup = new (function () {
+ this.popUp, this.downer, this.custom, this.middle, this.msgText, (this.buttons = []);
+ this.checkboxes = [];
+ this.dialogPromice = null;
+ this.isInit = false;
- 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);
+ this.init = function () {
+ if (this.isInit) {
+ return;
+ }
+ addStyle();
+ addBlocks();
+ addEventListeners();
+ this.isInit = true;
+ }
- 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;
- }
+ const addEventListeners = () => {
+ document.addEventListener('keyup', (e) => {
+ if (e.key == 'Escape') {
+ if (this.dialogPromice) {
+ const { func, result } = this.dialogPromice;
+ this.dialogPromice = null;
+ popup.hide();
+ func(result);
}
- 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;
- };
+ const addStyle = () => {
+ let style = document.createElement('style');
+ style.innerText = `
+ .PopUp_ {
+ position: fixed;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ min-width: 300px;
+ max-width: 80%;
+ max-height: 80%;
+ background-color: #190e08e6;
+ z-index: 10001;
+ border: 3px #ce9767 solid;
+ border-radius: 10px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-around;
+ padding: 15px 9px;
+ box-sizing: border-box;
+ }
- /**
- * Updates game data
- *
- * Обновляет данные игры
- */
- this.refreshGame = function () {
- new Game.NextDayUpdatedManager()[getProtoFn(Game.NextDayUpdatedManager, 6)]();
- try {
- cheats.refreshInventory();
- } catch (e) {}
- };
+ .PopUp_back {
+ position: absolute;
+ background-color: #00000066;
+ width: 100%;
+ height: 100%;
+ z-index: 10000;
+ top: 0;
+ left: 0;
+ }
- /**
- * Update inventory
- *
- * Обновляет инвентарь
- */
- this.refreshInventory = async function () {
- const GM_INST = getFnP(Game.GameModel, 'get_instance');
- const GM_0 = getProtoFn(Game.GameModel, 0);
- const P_24 = getProtoFn(selfGame['game.model.user.Player'], 24);
- const Player = Game.GameModel[GM_INST]()[GM_0];
- Player[P_24] = new selfGame['game.model.user.inventory.PlayerInventory']();
- Player[P_24].init(await Send({ calls: [{ name: 'inventoryGet', args: {}, ident: 'body' }] }).then((e) => e.results[0].result.response));
- };
- this.updateInventory = function (reward) {
- const GM_INST = getFnP(Game.GameModel, 'get_instance');
- const GM_0 = getProtoFn(Game.GameModel, 0);
- const P_24 = getProtoFn(selfGame['game.model.user.Player'], 24);
- const Player = Game.GameModel[GM_INST]()[GM_0];
- Player[P_24].init(reward);
- };
+ .PopUp_close {
+ width: 40px;
+ height: 40px;
+ position: absolute;
+ right: -18px;
+ top: -18px;
+ border: 3px solid #c18550;
+ border-radius: 20px;
+ background: radial-gradient(circle, rgba(190,30,35,1) 0%, rgba(0,0,0,1) 100%);
+ background-position-y: 3px;
+ box-shadow: -1px 1px 3px black;
+ cursor: pointer;
+ box-sizing: border-box;
+ }
- 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);
- };
+ .PopUp_close:hover {
+ filter: brightness(1.2);
+ }
- /**
- * 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);
- };
+ .PopUp_crossClose {
+ width: 100%;
+ height: 100%;
+ background-size: 65%;
+ background-position: center;
+ background-repeat: no-repeat;
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%23f4cd73' d='M 0.826 12.559 C 0.431 12.963 3.346 15.374 3.74 14.97 C 4.215 15.173 8.167 10.457 7.804 10.302 C 7.893 10.376 11.454 14.64 11.525 14.372 C 12.134 15.042 15.118 12.086 14.638 11.689 C 14.416 11.21 10.263 7.477 10.402 7.832 C 10.358 7.815 11.731 7.101 14.872 3.114 C 14.698 2.145 13.024 1.074 12.093 1.019 C 11.438 0.861 8.014 5.259 8.035 5.531 C 7.86 5.082 3.61 1.186 3.522 1.59 C 2.973 1.027 0.916 4.611 1.17 4.873 C 0.728 4.914 5.088 7.961 5.61 7.995 C 5.225 7.532 0.622 12.315 0.826 12.559 Z'/%3e%3c/svg%3e")
+ }
- /**
- * Move to the sanctuary cheats.goSanctuary()
- *
- * Переместиться в святилище cheats.goSanctuary()
- */
- this.goSanctuary = () => {
- this.goNavigtor('SANCTUARY');
- };
+ .PopUp_blocks {
+ width: 100%;
+ height: 50%;
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+ flex-wrap: wrap;
+ }
- /** Перейти в Долину титанов */
- this.goTitanValley = () => {
- this.goNavigtor('TITAN_VALLEY');
- };
+ .PopUp_blocks:last-child {
+ margin-top: 25px;
+ }
- /**
- * Go to Guild War
- *
- * Перейти к Войне Гильдий
- */
- this.goClanWar = function () {
- let instance = getFnP(Game.GameModel, 'get_instance');
- let player = Game.GameModel[instance]().A;
- let clanWarSelect = selfGame['game.mechanics.cross_clan_war.popup.selectMode.CrossClanWarSelectModeMediator'];
- new clanWarSelect(player).open();
- };
+ .PopUp_input {
+ text-align: center;
+ font-size: 16px;
+ height: 27px;
+ width: 100%;
+ border: 0px solid #cf9250;
+ border-radius: 9px 9px 0px 0px;
+ background: #170d07;
+ color: #fce1ac;
+ box-sizing: border-box;
+ }
- /** Перейти к Острову гильдии */
- this.goClanIsland = function () {
- let instance = getFnP(Game.GameModel, 'get_instance');
- let player = Game.GameModel[instance]().A;
- let clanIslandSelect = selfGame['game.view.gui.ClanIslandPopupMediator'];
- new clanIslandSelect(player).open();
- };
+ .PopUp_checkboxes {
+ display: flex;
+ flex-direction: column;
+ margin: 15px 15px -5px 15px;
+ align-items: flex-start;
+ }
- /**
- * Go to BrawlShop
- *
- * Переместиться в BrawlShop
- */
- this.goBrawlShop = () => {
- const instance = getFnP(Game.GameModel, 'get_instance');
- const P_36 = getProtoFn(selfGame['game.model.user.Player'], 36);
- const PSD_0 = getProtoFn(selfGame['game.model.user.shop.PlayerShopData'], 0);
- const IM_0 = getProtoFn(selfGame['haxe.ds.IntMap'], 0);
- const PSDE_4 = getProtoFn(selfGame['game.model.user.shop.PlayerShopDataEntry'], 4);
+ .PopUp_ContCheckbox {
+ margin: 2px 0px;
+ }
- const player = Game.GameModel[instance]().A;
- const shop = player[P_36][PSD_0][IM_0][1038][PSDE_4];
- const shopPopup = new selfGame['game.mechanics.brawl.mediator.BrawlShopPopupMediator'](player, shop);
- shopPopup.open(new selfGame['game.mediator.gui.popup.PopupStashEventParams']());
- };
+ .PopUp_checkbox {
+ position: absolute;
+ z-index: -1;
+ opacity: 0;
+ }
+ .PopUp_checkbox+label {
+ display: inline-flex;
+ align-items: center;
+ user-select: none;
- /**
- * Returns all stores from game data
- *
- * Возвращает все магазины из данных игры
- */
- this.getShops = () => {
- const instance = getFnP(Game.GameModel, 'get_instance');
- const P_36 = getProtoFn(selfGame['game.model.user.Player'], 36);
- const PSD_0 = getProtoFn(selfGame['game.model.user.shop.PlayerShopData'], 0);
- const IM_0 = getProtoFn(selfGame['haxe.ds.IntMap'], 0);
+ font-size: 15px;
+ font-family: sans-serif;
+ font-weight: 600;
+ font-stretch: condensed;
+ letter-spacing: 1px;
+ color: #fce1ac;
+ text-shadow: 0px 0px 1px;
+ }
+ .PopUp_checkbox+label::before {
+ content: '';
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ border: 1px solid #cf9250;
+ border-radius: 7px;
+ margin-right: 7px;
+ }
+ .PopUp_checkbox:checked+label::before {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2388cb13' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e");
+ }
+ .PopUp_checkbox[class*="radio_"] + label::before {
+ border-radius: 50%;
+ }
- const player = Game.GameModel[instance]().A;
- return player[P_36][PSD_0][IM_0];
- };
+ .PopUp_input::placeholder {
+ color: #fce1ac75;
+ }
- /**
- * 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;
- };
+ .PopUp_input:focus {
+ outline: 0;
+ }
- /**
- * 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 } });
+ .PopUp_input + .PopUp_button {
+ border-radius: 0px 0px 5px 5px;
+ padding: 2px 18px 5px;
+ }
- 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']());
- };
+ .PopUp_text {
+ font-size: 22px;
+ font-family: sans-serif;
+ font-weight: 600;
+ font-stretch: condensed;
+ letter-spacing: 1px;
+ text-align: center;
+ color: #FDE5B6;
+ text-shadow: 0px 0px 2px;
+ margin: 0 20px;
+ }
- /**
- * Game library availability tracker
- *
- * Отслеживание доступности игровой библиотеки
- */
- function checkLibLoad() {
- timeout = setTimeout(() => {
- if (Game.GameModel) {
- changeLib();
- } else {
- checkLibLoad();
- }
- }, 100);
+ .PopUp_hideBlock {
+ display: none;
}
- /**
- * Game library data spoofing
- *
- * Подмена данных игровой библиотеки
- */
- function changeLib() {
- console.log('lib connect');
- const originalStartFunc = Game.GameModel.prototype.start;
- Game.GameModel.prototype.start = function (a, b, c) {
- self.libGame = b.raw;
- self.doneLibLoad(self.libGame);
- try {
- const levels = b.raw.seasonAdventure.level;
- for (const id in levels) {
- const level = levels[id];
- level.clientData.graphics.fogged = level.clientData.graphics.visible;
- }
- const adv = b.raw.seasonAdventure.list[1];
- adv.clientData.asset = 'dialog_season_adventure_tiles';
- } catch (e) {
- console.warn(e);
- }
- originalStartFunc.call(this, a, b, c);
- };
+ .PopUp_Container {
+ max-height: 80vh;
+ overflow-y: auto;
+ overflow-x: hidden;
+ scrollbar-width: thin;
+ scrollbar-color: #774d10 #05040300;
}
- this.LibLoad = function () {
- return new Promise((e) => {
- this.doneLibLoad = e;
- });
- };
+ /* === НОВЫЕ КНОПКИ с цветовыми переменными === */
+ .PopUp_btnSocket {
+ margin: 3px 1px;
+ position: relative;
+ display: flex;
+ padding: 4px 4px 4px 3px;
+ flex-direction: column;
+ align-items: flex-start;
+ border-radius: 9px;
+ background: #a37738;
+ box-shadow: 0px -1px 1px 0px #7d5b3a inset, 0px 1px 1px 0px #e1a960 inset,
+ -1px 0px 1px 0px #311d13 inset;
+ }
+ .PopUp_btnRow {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ align-self: stretch;
+ width: 100%;
+ flex-wrap: wrap;
+ }
+ .PopUp_btnGap {
+ position: relative;
+ display: flex;
+ padding: 0px 1px 4px 1px;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 2px;
+ border-radius: 5px;
+ cursor: pointer;
+ flex: auto;
+ transition: all 0.1s ease;
+
+ /* Цветовые переменные по умолчанию — коричневый */
+ --h: 36;
+ --s: 60%;
+ --l: 10%;
+ --pl: 50%;
+ --pcl: 90%;
+ --phl: 55%;
+ --pal: 34%;
+ --pacl: 71%;
+ --sc: hsl(36, 88%, 7%);
+ }
+ .PopUp_btnGap:first-child,
+ .PopUp_btnGap.left {
+ padding-left: 2px;
+ }
+ .PopUp_btnGap:last-child,
+ .PopUp_btnGap.right {
+ padding-right: 3px;
+ }
+ .PopUp_btnGap {
+ background: hsl(var(--h), var(--s), var(--l));
+ box-shadow: 0px 0px 2px 0px var(--sc), 0px -1px 2px 0px var(--sc),
+ 0px 1px 1px 0px hsl(var(--h), var(--s), 12%);
+ }
+ .PopUp_btnPlate {
+ display: flex;
+ height: 13px;
+ padding: 12px 10px;
+ box-sizing: content-box;
+ justify-content: center;
+ align-items: center;
+ align-self: stretch;
+ gap: 10px;
+ border-radius: 4px;
+ filter: blur(0.2px);
+ transition: all 0.1s ease;
+ text-shadow: 0px 1px 0px rgba(0, 0, 0, 0.92);
+ font-family: Arial;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ color: hsla(var(--h), var(--s), var(--pcl), 1);
+ background: hsla(var(--h), var(--s), var(--pl), 1);
+ box-shadow: 0px 10px 12px 0px hsla(var(--h), 58%, 67%, 0.2) inset,
+ 0px 2px 1px 0px hsl(var(--h), 78%, 77%) inset,
+ 0px 2px 0px 0px hsl(var(--h), 78%, 37%) inset,
+ -8px 3px 15px 0px hsl(var(--h), 94%, 15%) inset,
+ 8px -7px 15px 0px hsla(var(--h), 94%, 15%, 0.7) inset,
+ 0px 0px 2px 0px hsl(var(--h), 68%, 23%),
+ 0px -3px 8px 0px hsl(var(--h), 94%, 20%) inset;
+ min-width: 65px;
+ }
+ .PopUp_btnPlate:hover {
+ color: hsla(0, 0%, 96%, 1);
+ background: hsla(var(--h), 49%, var(--phl), 1);
+ }
+ .PopUp_btnGap:active {
+ padding-top: 1px;
+ padding-bottom: 3px;
+ background: hsl(var(--h), var(--s), var(--l));
+ box-shadow: 0px 0px 2px 0px var(--sc), 0px -1px 2px 0px var(--sc),
+ 0px 1px 1px 0px hsl(var(--h), var(--s), 12%);
+ }
+ .PopUp_btnPlate:active {
+ color: hsla(var(--h), 47%, var(--pacl), 1);
+ background: hsl(var(--h), 46%, var(--pal));
+ }
+
+ /* === Цветовые переопределения === */
+ .PopUp_btnGap.brown {
+ --pl: 40%;
+ --pcl: 85%;
+ --s: 60%;
+ }
+ .PopUp_btnGap.green {
+ --h: 120;
+ }
+ .PopUp_btnGap.blue {
+ --h: 207;
+ }
+ .PopUp_btnGap.violet {
+ --h: 272;
+ }
+ .PopUp_btnGap.yellow {
+ --h: 45;
+ }
+ .PopUp_btnGap.orange {
+ --h: 20;
+ }
+ .PopUp_btnGap.indigo {
+ --h: 255;
+ }
+ .PopUp_btnGap.black {
+ --h: 0;
+ --s: 0%;
+ --l: 10%;
+ --pl: 20%;
+ --pcl: 85%;
+ --phl: 25%;
+ --pal: 15%;
+ --pacl: 71%;
+ --sc: hsl(0, 0%, 5%);
+ }
+ .PopUp_btnGap.pink {
+ --h: 330;
+ }
+ .PopUp_btnGap.red {
+ --h: 0;
+ }
+ .PopUp_btnGap.graphite {
+ background: hsl(0, 0%, 12%);
+ box-shadow: 0px 0px 2px 0px hsl(0, 0%, 7%), 0px -1px 2px 0px hsl(0, 0%, 7%),
+ 0px 1px 1px 0px hsl(0, 0%, 12%);
+ }
+ .PopUp_btnGap.graphite .PopUp_btnPlate {
+ color: hsla(0, 0%, 85%, 1);
+ background: hsla(0, 0%, 54%, 1);
+ box-shadow: 0px 10px 12px 0px hsla(0, 0%, 67%, 0.2) inset,
+ 0px 2px 1px 0px hsl(0, 0%, 67%) inset,
+ -8px 3px 15px 0px hsla(0, 0%, 15%, 0.7) inset,
+ 8px -7px 15px 0px hsla(0, 0%, 15%, 0.7) inset,
+ 0px 0px 2px 0px hsla(0, 0%, 23%, 0.3),
+ 0px -3px 8px 0px hsla(0, 0%, 13%, 0.3) inset;
+ }
+ .PopUp_btnGap.graphite .PopUp_btnPlate:hover {
+ color: hsla(0, 0%, 96%, 1);
+ background: hsla(0, 0%, 62%, 1);
+ }
+ .PopUp_btnGap.graphite:active {
+ background: hsl(0, 0%, 12%);
+ box-shadow: 0px 0px 2px 0px hsl(0, 0%, 7%), 0px -1px 2px 0px hsl(0, 0%, 7%),
+ 0px 1px 1px 0px hsl(0, 0%, 12%);
+ }
+ .PopUp_btnGap.graphite .PopUp_btnPlate:active {
+ color: hsla(0, 0%, 71%, 1);
+ background: hsl(0, 0%, 34%);
+ }
+ `;
+ document.head.appendChild(style);
+ }
- /**
- * Returns the value of a language constant
- *
- * Возвращает значение языковой константы
- * @param {*} langConst language constant // языковая константа
- * @returns
- */
- this.translate = function (langConst) {
- return Game.Translate.translate(langConst);
- };
+ const addBlocks = () => {
+ this.back = document.createElement('div');
+ this.back.classList.add('PopUp_back');
+ this.back.classList.add('PopUp_hideBlock');
+ document.body.append(this.back);
- connectGame();
- checkLibLoad();
- }
+ this.popUp = document.createElement('div');
+ this.popUp.classList.add('PopUp_');
+ this.back.append(this.popUp);
- /**
- * Auto collection of gifts
- *
- * Автосбор подарков
- */
- function getAutoGifts() {
- // c3ltYm9scyB0aGF0IG1lYW4gbm90aGluZw==
- let valName = 'giftSendIds_' + userInfo.id;
+ this.popUpContainer = document.createElement('div');
+ this.popUpContainer.classList.add('PopUp_Container');
+ this.popUp.append(this.popUpContainer);
- if (!localStorage['clearGift' + userInfo.id]) {
- localStorage[valName] = '';
- localStorage['clearGift' + userInfo.id] = '+';
- }
+ let upper = document.createElement('div');
+ upper.classList.add('PopUp_blocks');
+ this.popUpContainer.append(upper);
- if (!localStorage[valName]) {
- localStorage[valName] = '';
- }
+ this.middle = document.createElement('div');
+ this.middle.classList.add('PopUp_blocks');
+ this.middle.classList.add('PopUp_checkboxes');
+ this.popUpContainer.append(this.middle);
- 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,
- });
- });
+ this.custom = document.createElement('div');
+ this.custom.classList.add('PopUp_custom');
+ this.popUpContainer.append(this.custom);
- if (!freebieCheckCalls.calls.length) {
- return;
- }
+ this.downer = document.createElement('div');
+ this.downer.classList.add('PopUp_blocks');
+ this.popUpContainer.append(this.downer);
- send(JSON.stringify(freebieCheckCalls), (e) => {
- let countGetGifts = 0;
- const gifts = [];
- for (check of e.results) {
- gifts.push(check.ident);
- if (check.result.response != null) {
- countGetGifts++;
- }
- }
- const saveGifts = localStorage[valName].split(';');
- localStorage[valName] = [...saveGifts, ...gifts].slice(-50).join(';');
- console.log(`${I18N('GIFTS')}: ${countGetGifts}`);
- });
- });
- }
+ this.msgText = document.createElement('div');
+ this.msgText.classList.add('PopUp_text', 'PopUp_msgText');
+ upper.append(this.msgText);
+ }
- /**
- * To fill the kills in the Forge of Souls
- *
- * Набить килов в горниле душ
- */
- async function bossRatingEvent() {
- const topGet = await Send(JSON.stringify({ calls: [{ name: "topGet", args: { type: "bossRatingTop", extraId: 0 }, ident: "body" }] }));
- if (!topGet || !topGet.results[0].result.response[0]) {
- setProgress(`${I18N('EVENT')} ${I18N('NOT_AVAILABLE')}`, true);
- return;
+ this.showBack = function () {
+ this.back.classList.remove('PopUp_hideBlock');
}
- const replayId = topGet.results[0].result.response[0].userData.replayId;
- const result = await Send(JSON.stringify({
- calls: [
- { name: "battleGetReplay", args: { id: replayId }, ident: "battleGetReplay" },
- { name: "heroGetAll", args: {}, ident: "heroGetAll" },
- { name: "pet_getAll", args: {}, ident: "pet_getAll" },
- { name: "offerGetAll", args: {}, ident: "offerGetAll" }
- ]
- }));
- const bossEventInfo = result.results[3].result.response.find(e => e.offerType == "bossEvent");
- if (!bossEventInfo) {
- setProgress(`${I18N('EVENT')} ${I18N('NOT_AVAILABLE')}`, true);
- return;
+
+ this.hideBack = function () {
+ this.back.classList.add('PopUp_hideBlock');
}
- const usedHeroes = bossEventInfo.progress.usedHeroes;
- const party = Object.values(result.results[0].result.response.replay.attackers);
- const availableHeroes = Object.values(result.results[1].result.response).map(e => e.id);
- const availablePets = Object.values(result.results[2].result.response).map(e => e.id);
- const calls = [];
- /**
- * First pack
- *
- * Первая пачка
- */
- const args = {
- heroes: [],
- favor: {}
+
+ this.show = function () {
+ if (this.checkboxes.length) {
+ this.middle.classList.remove('PopUp_hideBlock');
+ }
+ this.showBack();
+ this.popUp.classList.remove('PopUp_hideBlock');
}
- for (let hero of party) {
- if (hero.id >= 6000 && availablePets.includes(hero.id)) {
- args.pet = hero.id;
- continue;
+
+ this.hide = function () {
+ this.hideBack();
+ this.popUp.classList.add('PopUp_hideBlock');
+ }
+
+ this.addAnyButton = (option) => {
+ if (option.isOneSocket) {
+ this.isOneSocket = true;
}
- if (!availableHeroes.includes(hero.id) || usedHeroes.includes(hero.id)) {
- continue;
+
+ if (!this.btnSocket || !this.isOneSocket) {
+ this.btnSocket = document.createElement('div');
+ this.btnSocket.classList.add('PopUp_btnSocket');
+ this.downer.append(this.btnSocket);
+ option.isNewSocket = false;
+
+ this.btnRow = document.createElement('div');
+ this.btnRow.classList.add('PopUp_btnRow');
+ if (option.isWrap) {
+ this.btnRow.style.flexWrap = 'wrap';
+ }
+ this.btnSocket.append(this.btnRow);
+ option.isNewRow = false;
}
- args.heroes.push(hero.id);
- if (hero.favorPetId) {
- args.favor[hero.id] = hero.favorPetId;
+
+ if (option.isNewSocket) {
+ this.btnSocket = document.createElement('div');
+ this.btnSocket.classList.add('PopUp_btnSocket');
+ this.downer.append(this.btnSocket);
+ option.isNewRow = true;
+ }
+
+ if (option.isNewRow) {
+ this.btnRow = document.createElement('div');
+ this.btnRow.classList.add('PopUp_btnRow');
+ this.btnSocket.append(this.btnRow);
+ }
+
+ let inputField = {
+ value: option.result || option.default,
+ };
+ if (option.isInput) {
+ inputField = document.createElement('input');
+ inputField.type = 'text';
+ if (option.placeholder) {
+ inputField.placeholder = option.placeholder;
+ }
+ if (option.default) {
+ inputField.value = option.default;
+ }
+ inputField.classList.add('PopUp_input');
+ this.btnRow.append(inputField);
+ }
+
+ const button = document.createElement('div');
+ const classes = option.classes ?? [];
+ button.classList.add('PopUp_btnGap', option.color ?? 'indigo', ...classes);
+ button.title = option.title || '';
+ this.btnRow.append(button);
+
+ const buttonText = document.createElement('div');
+ buttonText.classList.add('PopUp_btnPlate');
+ buttonText.innerHTML = option.msg;
+ button.append(buttonText);
+
+ const contButton = this.btnSocket;
+ if (option.isInput) {
+ this.btnSocket = null;
}
+
+ return { button, contButton, inputField };
+ };
+
+ this.addCloseButton = () => {
+ let button = document.createElement('div')
+ button.classList.add('PopUp_close');
+ this.popUp.append(button);
+
+ let crossClose = document.createElement('div')
+ crossClose.classList.add('PopUp_crossClose');
+ button.append(crossClose);
+
+ return { button, contButton: button };
}
- if (args.heroes.length) {
- calls.push({
- name: 'bossRating_startBattle',
- args,
- ident: 'body_0',
+
+ this.addButton = (option, buttonClick) => {
+ const { button, contButton, inputField } = option.isClose ? this.addCloseButton() : this.addAnyButton(option);
+ if (option.isClose) {
+ this.dialogPromice = { func: buttonClick, result: option.result };
+ }
+ button.addEventListener('click', () => {
+ let result = '';
+ if (option.isInput) {
+ result = inputField.value;
+ }
+ if (option.isClose || option.isCancel) {
+ this.dialogPromice = null;
+ }
+ buttonClick(result);
});
+
+ this.buttons.push(contButton);
}
- /**
- * Other packs
- *
- * Другие пачки
- */
- let heroes = [];
- let count = 1;
- while (heroId = availableHeroes.pop()) {
- if (args.heroes.includes(heroId) || usedHeroes.includes(heroId)) {
- continue;
+
+ this.clearButtons = () => {
+ this.isOneSocket = null;
+ this.btnSocket = null;
+ this.btnRow = null;
+ while (this.buttons.length) {
+ this.buttons.pop().remove();
}
- heroes.push(heroId);
- if (heroes.length == 5) {
- calls.push({
- name: 'bossRating_startBattle',
- args: {
- heroes: [...heroes],
- pet: availablePets[Math.floor(Math.random() * availablePets.length)],
- },
- ident: 'body_' + count,
+ }
+
+ this.addCheckBox = (checkBox) => {
+ const contCheckbox = document.createElement('div');
+ contCheckbox.classList.add('PopUp_ContCheckbox');
+ this.middle.append(contCheckbox);
+
+ const checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.id = 'PopUpCheckbox' + this.checkboxes.length;
+ checkbox.dataset.name = checkBox.name;
+ checkbox.checked = checkBox.checked;
+ checkbox.label = checkBox.label;
+ checkbox.title = checkBox.title || '';
+ checkbox.classList.add('PopUp_checkbox');
+ contCheckbox.appendChild(checkbox)
+
+ if (checkBox.radio) {
+ checkbox.classList.add(`radio_${checkBox.radio}`);
+ checkbox.addEventListener('change', function () {
+ if (this.checked) {
+ document.querySelectorAll(`.PopUp_checkbox.radio_${checkBox.radio}`).forEach((cb) => {
+ if (cb !== this) cb.checked = false;
+ });
+ } else {
+ this.checked = true;
+ }
});
- heroes = [];
- count++;
}
+
+ const checkboxLabel = document.createElement('label');
+ checkboxLabel.innerHTML = checkBox.label;
+ checkboxLabel.title = checkBox.title || '';
+ checkboxLabel.setAttribute('for', checkbox.id);
+ contCheckbox.appendChild(checkboxLabel);
+
+ this.checkboxes.push(checkbox);
}
- if (!calls.length) {
- setProgress(`${I18N('NO_HEROES')}`, true);
- return;
+ this.clearCheckBox = () => {
+ this.middle.classList.add('PopUp_hideBlock');
+ while (this.checkboxes.length) {
+ this.checkboxes.pop().parentNode.remove();
+ }
}
- const resultBattles = await Send(JSON.stringify({ calls }));
- console.log(resultBattles);
- rewardBossRatingEvent();
- }
+ this.clearCustomBlock = () => {
+ this.custom.innerHTML = '';
+ };
- /**
- * 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;
- }
+ this.setMsgText = (text) => {
+ this.msgText.innerHTML = text;
+ }
- let farmedChests = bossEventInfo.progress.farmedChests;
- let score = bossEventInfo.progress.score;
- setProgress(`${I18N('DAMAGE_AMOUNT')}: ${score}`);
- let revard = bossEventInfo.reward;
+ this.getCheckBoxes = () => {
+ const checkBoxes = [];
- let getRewardCall = {
- calls: []
+ for (const checkBox of this.checkboxes) {
+ checkBoxes.push({
+ name: checkBox.dataset.name,
+ label: checkBox.label,
+ checked: checkBox.checked
+ });
}
- let count = 0;
- for (let i = 1; i < 10; i++) {
- if (farmedChests.includes(i)) {
- continue;
+ return checkBoxes;
+ }
+
+ this.confirm = async (msg, buttOpt, checkBoxes = []) => {
+ if (!this.isInit) {
+ this.init();
+ }
+ this.clearButtons();
+ this.clearCheckBox();
+ this.clearCustomBlock();
+ return new Promise((complete, failed) => {
+ this.setMsgText(msg);
+ if (!buttOpt) {
+ buttOpt = [{ msg: 'Ok', result: true, isInput: false, color: 'green' }];
}
- if (score < revard[i].score) {
- break;
+ for (const checkBox of checkBoxes) {
+ this.addCheckBox(checkBox);
}
- getRewardCall.calls.push({
- name: 'bossRating_getReward',
- args: {
- rewardId: i,
- },
- ident: 'body_' + i,
- });
- count++;
- }
- if (!count) {
- setProgress(`${I18N('NOTHING_TO_COLLECT')}`, true);
- return;
- }
+ for (let butt of buttOpt) {
+ this.addButton(butt, (result) => {
+ result = result || butt.result;
+ complete(result);
+ popup.hide();
+ });
+ if (butt.isCancel) {
+ this.dialogPromice = { func: complete, result: butt.result };
+ }
+ }
+ this.show();
+ });
+ }
- send(JSON.stringify(getRewardCall), e => {
- console.log(e);
- setProgress(`${I18N('COLLECTED')} ${e?.results?.length} ${I18N('REWARD')}`, true);
+ this.customPopup = async (customFunc) => {
+ if (!this.isInit) {
+ this.init();
+ }
+ this.clearButtons();
+ this.clearCheckBox();
+ this.clearCustomBlock();
+ return new Promise((complete, failed) => {
+ customFunc(complete);
});
- });
- }
+ };
+ });
+
+ this.HWHFuncs.popup = popup;
/**
- * Collect Easter eggs and event rewards
+ * Script control panel
*
- * Собрать пасхалки и награды событий
+ * Панель управления скриптом
+ *
+ * Дизайн и стили кнопок
+ * Anton Nazarov
+ * https://t.me/antiokh
*/
- function offerFarmAllReward() {
- const offerGetAllCall = '{"calls":[{"name":"offerGetAll","args":{},"ident":"offerGetAll"}]}';
- return Send(offerGetAllCall).then((data) => {
- const offerGetAll = data.results[0].result.response.filter(e => e.type == "reward" && !e?.freeRewardObtained && e.reward);
- if (!offerGetAll.length) {
- setProgress(`${I18N('NOTHING_TO_COLLECT')}`, true);
- return;
+ class ScriptMenu extends EventEmitterMixin() {
+ constructor() {
+ if (ScriptMenu.instance) {
+ return ScriptMenu.instance;
}
+ super();
+ this.mainMenu = null;
+ this.buttons = [];
+ this.checkboxes = [];
+ this.option = {
+ showMenu: true,
+ showDetails: {},
+ };
+ ScriptMenu.instance = this;
+ return this;
+ }
- const calls = [];
- for (let reward of offerGetAll) {
- calls.push({
- name: "offerFarmReward",
- args: {
- offerId: reward.id
- },
- ident: "offerFarmReward_" + reward.id
- });
+ static getInst() {
+ if (!ScriptMenu.instance) {
+ new ScriptMenu();
}
+ return ScriptMenu.instance;
+ }
- return Send(JSON.stringify({ calls })).then(e => {
- console.log(e);
- setProgress(`${I18N('COLLECTED')} ${e?.results?.length} ${I18N('REWARD')}`, true);
- });
- });
- }
+ init(option = {}) {
+ this.emit('beforeInit', option);
+ this.option = Object.assign(this.option, option);
+ const saveOption = this.loadSaveOption();
+ this.option = Object.assign(this.option, saveOption);
+ this.addStyle();
+ this.addBlocks();
+ this.emit('afterInit', option);
+ }
- /**
- * 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;
+ addStyle() {
+ const style = document.createElement('style');
+ style.innerText = `
+ /* === Статус и переключатель меню === */
+ .scriptMenu_status {
+ position: absolute;
+ z-index: 10001;
+ top: -1px;
+ left: 30%;
+ cursor: pointer;
+ border-radius: 0px 0px 10px 10px;
+ background: #190e08e6;
+ border: 1px #ce9767 solid;
+ font-size: 18px;
+ font-family: sans-serif;
+ font-weight: 600;
+ color: #fce1ac;
+ text-shadow: 0px 0px 1px;
+ transition: 0.5s;
+ padding: 2px 10px 3px;
+ }
+ .scriptMenu_statusHide {
+ top: -35px;
+ height: 30px;
+ overflow: hidden;
+ }
+ .scriptMenu_label {
+ position: absolute;
+ top: 30%;
+ left: -4px;
+ z-index: 9999;
+ cursor: pointer;
+ width: 30px;
+ height: 30px;
+ background: radial-gradient(circle, #47a41b 0%, #1a2f04 100%);
+ border: 1px solid #1a2f04;
+ border-radius: 5px;
+ box-shadow:
+ inset 0px 2px 4px #83ce26,
+ inset 0px -4px 6px #1a2f04,
+ 0px 0px 2px black,
+ 0px 0px 0px 2px #ce9767;
+ }
+ .scriptMenu_label:hover {
+ filter: brightness(1.2);
+ }
+ .scriptMenu_arrowLabel {
+ width: 100%;
+ height: 100%;
+ background-size: 75%;
+ background-position: center;
+ background-repeat: no-repeat;
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%2388cb13' d='M7.596 7.304a.802.802 0 0 1 0 1.392l-6.363 3.692C.713 12.69 0 12.345 0 11.692V4.308c0-.653.713-.998 1.233-.696l6.363 3.692Z'/%3e%3cpath fill='%2388cb13' d='M15.596 7.304a.802.802 0 0 1 0 1.392l-6.363 3.692C8.713 12.69 8 12.345 8 11.692V4.308c0-.653.713-.998 1.233-.696l6.363 3.692Z'/%3e%3c/svg%3e");
+ box-shadow: 0px 1px 2px #000;
+ border-radius: 5px;
+ filter: drop-shadow(0px 1px 2px #000D);
+ }
+ .scriptMenu_main {
+ position: absolute;
+ max-width: 285px;
+ z-index: 9999;
+ top: 50%;
+ transform: translateY(-40%);
+ background: #190e08e6;
+ border: 1px #ce9767 solid;
+ border-radius: 0px 10px 10px 0px;
+ border-left: none;
+ box-sizing: border-box;
+ font-size: 15px;
+ font-family: sans-serif;
+ font-weight: 600;
+ color: #fce1ac;
+ text-shadow: 0px 0px 1px;
+ transition: 1s;
+ }
+ .scriptMenu_conteiner {
+ max-height: 80vh;
+ overflow: scroll;
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE/Edge */
+ display: flex;
+ flex-direction: column;
+ flex-wrap: nowrap;
+ padding: 5px 10px 5px 5px;
+ }
+ .scriptMenu_conteiner::-webkit-scrollbar {
+ display: none; /* Chrome/Safari/Opera */
+ }
+ .scriptMenu_showMenu {
+ display: none;
+ }
+ .scriptMenu_showMenu:checked ~ .scriptMenu_main {
+ left: 0px;
+ }
+ .scriptMenu_showMenu:not(:checked) ~ .scriptMenu_main {
+ left: -300px;
+ }
+
+ /* === Чекбоксы и инпуты === */
+ .scriptMenu_divInput {
+ margin: 2px;
+ }
+ .scriptMenu_divInputText {
+ margin: 2px;
+ align-self: center;
+ display: flex;
+ }
+ .scriptMenu_checkbox {
+ position: absolute;
+ z-index: -1;
+ opacity: 0;
+ }
+ .scriptMenu_checkbox + label {
+ display: inline-flex;
+ align-items: center;
+ user-select: none;
+ }
+ .scriptMenu_checkbox + label::before {
+ content: '';
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ border: 1px solid #cf9250;
+ border-radius: 7px;
+ margin-right: 7px;
+ }
+ .scriptMenu_checkbox:checked + label::before {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2388cb13' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e");
+ }
+
+ /* === Закрытие меню === */
+ .scriptMenu_close {
+ width: 40px;
+ height: 40px;
+ position: absolute;
+ right: -18px;
+ top: -18px;
+ border: 3px solid #c18550;
+ border-radius: 20px;
+ background: radial-gradient(circle, rgba(190,30,35,1) 0%, rgba(0,0,0,1) 100%);
+ background-position-y: 3px;
+ box-shadow: -1px 1px 3px black;
+ cursor: pointer;
+ box-sizing: border-box;
+ z-index: 1;
+ }
+ .scriptMenu_close:hover {
+ filter: brightness(1.2);
+ }
+ .scriptMenu_crossClose {
+ width: 100%;
+ height: 100%;
+ background-size: 65%;
+ background-position: center;
+ background-repeat: no-repeat;
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%23f4cd73' d='M 0.826 12.559 C 0.431 12.963 3.346 15.374 3.74 14.97 C 4.215 15.173 8.167 10.457 7.804 10.302 C 7.893 10.376 11.454 14.64 11.525 14.372 C 12.134 15.042 15.118 12.086 14.638 11.689 C 14.416 11.21 10.263 7.477 10.402 7.832 C 10.358 7.815 11.731 7.101 14.872 3.114 C 14.698 2.145 13.024 1.074 12.093 1.019 C 11.438 0.861 8.014 5.259 8.035 5.531 C 7.86 5.082 3.61 1.186 3.522 1.59 C 2.973 1.027 0.916 4.611 1.17 4.873 C 0.728 4.914 5.088 7.961 5.61 7.995 C 5.225 7.532 0.622 12.315 0.826 12.559 Z'/%3e%3c/svg%3e")
+ }
+
+ /* === Заголовки и детали === */
+ .scriptMenu_header {
+ text-align: center;
+ align-self: center;
+ font-size: 15px;
+ margin: 0px 15px;
+ }
+ .scriptMenu_header a {
+ color: #fce5b7;
+ text-decoration: none;
+ }
+ .scriptMenu_InputText {
+ text-align: center;
+ width: 130px;
+ height: 24px;
+ border: 1px solid #cf9250;
+ border-radius: 9px;
+ background: transparent;
+ color: #fce1ac;
+ padding: 0px 10px;
+ box-sizing: border-box;
+ }
+ .scriptMenu_InputText:focus {
+ filter: brightness(1.2);
+ outline: 0;
+ }
+ .scriptMenu_InputText::placeholder {
+ color: #fce1ac75;
+ }
+ .scriptMenu_Summary {
+ cursor: pointer;
+ margin-left: 7px;
+ }
+ .scriptMenu_Details {
+ align-self: center;
+ }
+
+ /* === НОВЫЕ КНОПКИ с цветовыми переменными === */
+ .scriptMenu_btnSocket {
+ position: relative;
+ display: flex;
+ padding: 4px 4px 4px 3px;
+ flex-direction: column;
+ align-items: flex-start;
+ border-radius: 9px;
+ background: #a37738;
+ box-shadow: 0px -1px 1px 0px #7d5b3a inset, 0px 1px 1px 0px #e1a960 inset,
+ -1px 0px 1px 0px #311d13 inset;
+ }
+ .scriptMenu_btnRow {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ align-self: stretch;
+ width: 100%;
+ }
+ .scriptMenu_btnGap {
+ position: relative;
+ display: flex;
+ padding: 0px 1px 4px 1px;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 2px;
+ border-radius: 5px;
+ cursor: pointer;
+ flex: auto;
+ transition: all 0.1s ease;
+
+ /* Цветовые переменные по умолчанию — коричневый */
+ --h: 36;
+ --s: 60%;
+ --l: 10%;
+ --pl: 50%;
+ --pcl: 90%;
+ --phl: 55%;
+ --pal: 34%;
+ --pacl: 71%;
+ --sc: hsl(36, 88%, 7%);
+ }
+ .scriptMenu_btnGap:first-child,
+ .scriptMenu_btnGap.left {
+ padding-left: 2px;
+ }
+ .scriptMenu_btnGap:last-child,
+ .scriptMenu_btnGap.right {
+ padding-right: 3px;
+ }
+ .scriptMenu_btnGap {
+ background: hsl(var(--h), var(--s), var(--l));
+ box-shadow: 0px 0px 2px 0px var(--sc), 0px -1px 2px 0px var(--sc),
+ 0px 1px 1px 0px hsl(var(--h), var(--s), 12%);
+ }
+ .scriptMenu_btnPlate {
+ display: flex;
+ height: 13px;
+ padding: 12px 10px;
+ box-sizing: content-box;
+ justify-content: center;
+ align-items: center;
+ align-self: stretch;
+ gap: 10px;
+ border-radius: 4px;
+ filter: blur(0.2px);
+ transition: all 0.1s ease;
+ text-shadow: 0px 1px 0px rgba(0, 0, 0, 0.92);
+ font-family: Arial;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ color: hsla(var(--h), var(--s), var(--pcl), 1);
+ background: hsla(var(--h), var(--s), var(--pl), 1);
+ box-shadow: 0px 10px 12px 0px hsla(var(--h), 58%, 67%, 0.2) inset,
+ 0px 2px 1px 0px hsl(var(--h), 78%, 77%) inset,
+ 0px 2px 0px 0px hsl(var(--h), 78%, 37%) inset,
+ -8px 3px 15px 0px hsl(var(--h), 94%, 15%) inset,
+ 8px -7px 15px 0px hsla(var(--h), 94%, 15%, 0.7) inset,
+ 0px 0px 2px 0px hsl(var(--h), 68%, 23%),
+ 0px -3px 8px 0px hsl(var(--h), 94%, 20%) inset;
+ }
+ .scriptMenu_btnPlate:hover {
+ color: hsla(0, 0%, 96%, 1);
+ background: hsla(var(--h), 49%, var(--phl), 1);
+ }
+ .scriptMenu_btnGap:active {
+ padding-top: 1px;
+ padding-bottom: 3px;
+ background: hsl(var(--h), var(--s), var(--l));
+ box-shadow: 0px 0px 2px 0px var(--sc), 0px -1px 2px 0px var(--sc),
+ 0px 1px 1px 0px hsl(var(--h), var(--s), 12%);
+ }
+ .scriptMenu_btnPlate:active {
+ color: hsla(var(--h), 47%, var(--pacl), 1);
+ background: hsl(var(--h), 46%, var(--pal));
+ }
+
+ /* === Цветовые переопределения === */
+ .scriptMenu_btnGap.brown {
+ --pl: 40%;
+ --pcl: 85%;
+ --s: 60%;
+ }
+ .scriptMenu_btnGap.green {
+ --h: 120;
+ }
+ .scriptMenu_btnGap.blue {
+ --h: 207;
+ }
+ .scriptMenu_btnGap.violet {
+ --h: 272;
+ }
+ .scriptMenu_btnGap.yellow {
+ --h: 45;
+ }
+ .scriptMenu_btnGap.orange {
+ --h: 20;
+ }
+ .scriptMenu_btnGap.indigo {
+ --h: 255;
+ }
+ .scriptMenu_btnGap.black {
+ --h: 0;
+ --s: 0%;
+ --l: 10%;
+ --pl: 20%;
+ --pcl: 85%;
+ --phl: 25%;
+ --pal: 15%;
+ --pacl: 71%;
+ --sc: hsl(0, 0%, 5%);
+ }
+ .scriptMenu_btnGap.pink {
+ --h: 330;
+ }
+ .scriptMenu_btnGap.red {
+ --h: 0;
+ }
+ .scriptMenu_btnGap.graphite {
+ background: hsl(0, 0%, 12%);
+ box-shadow: 0px 0px 2px 0px hsl(0, 0%, 7%), 0px -1px 2px 0px hsl(0, 0%, 7%),
+ 0px 1px 1px 0px hsl(0, 0%, 12%);
+ }
+ .scriptMenu_btnGap.graphite .scriptMenu_btnPlate {
+ color: hsla(0, 0%, 85%, 1);
+ background: hsla(0, 0%, 54%, 1);
+ box-shadow: 0px 10px 12px 0px hsla(0, 0%, 67%, 0.2) inset,
+ 0px 2px 1px 0px hsl(0, 0%, 67%) inset,
+ -8px 3px 15px 0px hsla(0, 0%, 15%, 0.7) inset,
+ 8px -7px 15px 0px hsla(0, 0%, 15%, 0.7) inset,
+ 0px 0px 2px 0px hsla(0, 0%, 23%, 0.3),
+ 0px -3px 8px 0px hsla(0, 0%, 13%, 0.3) inset;
+ }
+ .scriptMenu_btnGap.graphite .scriptMenu_btnPlate:hover {
+ color: hsla(0, 0%, 96%, 1);
+ background: hsla(0, 0%, 62%, 1);
+ }
+ .scriptMenu_btnGap.graphite:active {
+ background: hsl(0, 0%, 12%);
+ box-shadow: 0px 0px 2px 0px hsl(0, 0%, 7%), 0px -1px 2px 0px hsl(0, 0%, 7%),
+ 0px 1px 1px 0px hsl(0, 0%, 12%);
+ }
+ .scriptMenu_btnGap.graphite .scriptMenu_btnPlate:active {
+ color: hsla(0, 0%, 71%, 1);
+ background: hsl(0, 0%, 34%);
+ }
+
+ /* === Индикаторы (оставляем как было) === */
+ .scriptMenu_attention {
+ position: relative;
+ }
+ .scriptMenu_attention .scriptMenu_dot {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+ .scriptMenu_dot {
+ position: absolute;
+ top: -7px;
+ right: -7px;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ border: 1px solid #c18550;
+ background: radial-gradient(circle, #f000 25%, black 100%);
+ box-shadow: 0px 0px 2px black;
+ background-position: 0px -1px;
+ font-size: 10px;
+ text-align: center;
+ color: white;
+ text-shadow: 1px 1px 1px black;
+ box-sizing: border-box;
+ display: none;
+ }
+ `;
+ document.head.appendChild(style);
+ }
- let bossRaidOpenChestCall = {
- calls: []
- };
+ addBlocks() {
+ const main = document.createElement('div');
+ document.body.appendChild(main);
- 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
- });
- }
- }
+ this.status = document.createElement('div');
+ this.status.classList.add('scriptMenu_status');
+ this.setStatus('');
+ main.appendChild(this.status);
- if (!bossRaidOpenChestCall.calls.length) {
- setProgress(`${I18N('OUTLAND')} ${I18N('NOTHING_TO_COLLECT')}`, true);
- resolve();
- return;
- }
+ const label = document.createElement('label');
+ label.classList.add('scriptMenu_label');
+ label.setAttribute('for', 'checkbox_showMenu');
+ main.appendChild(label);
- send(JSON.stringify(bossRaidOpenChestCall), e => {
- setProgress(`${I18N('OUTLAND')} ${I18N('COLLECTED')}`, true);
- resolve();
- });
+ const arrowLabel = document.createElement('div');
+ arrowLabel.classList.add('scriptMenu_arrowLabel');
+ label.appendChild(arrowLabel);
+
+ const checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.id = 'checkbox_showMenu';
+ checkbox.checked = this.option.showMenu;
+ checkbox.classList.add('scriptMenu_showMenu');
+ checkbox.addEventListener('change', () => {
+ this.option.showMenu = checkbox.checked;
+ this.saveSaveOption();
});
- });
- }
+ main.appendChild(checkbox);
- /**
- * Collect all rewards
- *
- * Собрать все награды
- */
- function questAllFarm() {
- return new Promise(function (resolve, reject) {
- let questGetAllCall = {
- calls: [{
- name: "questGetAll",
- args: {},
- ident: "body"
- }]
- }
- send(JSON.stringify(questGetAllCall), function (data) {
- let questGetAll = data.results[0].result.response;
- const questAllFarmCall = {
- calls: []
- }
- let number = 0;
- for (let quest of questGetAll) {
- if (quest.id < 1e6 && quest.state == 2) {
- questAllFarmCall.calls.push({
- name: "questFarm",
- args: {
- questId: quest.id
- },
- ident: `group_${number}_body`
- });
- number++;
- }
- }
+ const mainMenu = document.createElement('div');
+ mainMenu.classList.add('scriptMenu_main');
+ main.appendChild(mainMenu);
- if (!questAllFarmCall.calls.length) {
- setProgress(`${I18N('COLLECTED')} ${number} ${I18N('REWARD')}`, true);
- resolve();
- return;
- }
+ this.mainMenu = document.createElement('div');
+ this.mainMenu.classList.add('scriptMenu_conteiner');
+ mainMenu.appendChild(this.mainMenu);
- send(JSON.stringify(questAllFarmCall), function (res) {
- console.log(res);
- setProgress(`${I18N('COLLECTED')} ${number} ${I18N('REWARD')}`, true);
- resolve();
- });
- });
- })
- }
+ const closeButton = document.createElement('label');
+ closeButton.classList.add('scriptMenu_close');
+ closeButton.setAttribute('for', 'checkbox_showMenu');
+ this.mainMenu.appendChild(closeButton);
- /**
- * 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
- }, ])
- }
- if (isStopSendMission) {
- stopMission();
- return;
- }
- lastMissionBattleStart = Date.now();
- let missionStartCall = {
- "calls": [{
- "name": "missionStart",
- "args": lastMissionStart,
- "ident": "body"
- }]
+ const crossClose = document.createElement('div');
+ crossClose.classList.add('scriptMenu_crossClose');
+ closeButton.appendChild(crossClose);
}
- /**
- * Mission Request
- *
- * Запрос на выполнение мисии
- */
- SendRequest(JSON.stringify(missionStartCall), async e => {
- if (e['error']) {
- isSendsMission = false;
- console.log(e['error']);
- setProgress('');
- let msg = e['error'].name + ' ' + e['error'].description + ` ${I18N('REPETITIONS')}: ${param.count}`;
- await popup.confirm(msg, [
- {msg: 'Ok', result: true},
- ])
- return;
- }
- /**
- * Mission data calculation
- *
- * Расчет данных мисии
- */
- BattleCalc(e.results[0].result.response, 'get_tower', async r => {
- /** missionTimer */
- let timer = getTimer(r.battleTime) + 5;
- const period = Math.ceil((Date.now() - lastMissionBattleStart) / 1000);
- if (period < timer) {
- timer = timer - period;
- const isSuccess = await countdownTimer(timer, `${I18N('MISSIONS_PASSED')}: ${param.count}`, () => {
- isStopSendMission = true;
- });
- if (!isSuccess) {
- stopMission();
- return;
- }
- }
-
- let missionEndCall = {
- "calls": [{
- "name": "missionEnd",
- "args": {
- "id": param.id,
- "result": r.result,
- "progress": r.progress
- },
- "ident": "body"
- }]
- }
- /**
- * Mission Completion Request
- *
- * Запрос на завершение миссии
- */
- SendRequest(JSON.stringify(missionEndCall), async (e) => {
- if (e['error']) {
- isSendsMission = false;
- console.log(e['error']);
- setProgress('');
- let msg = e['error'].name + ' ' + e['error'].description + ` ${I18N('REPETITIONS')}: ${param.count}`;
- await popup.confirm(msg, [
- {msg: 'Ok', result: true},
- ])
- return;
- }
- r = e.results[0].result.response;
- if (r['error']) {
- isSendsMission = false;
- console.log(r['error']);
- setProgress('');
- await popup.confirm(` ${I18N('REPETITIONS')}: ${param.count}` + ' 3 ' + r['error'], [
- {msg: 'Ok', result: true},
- ])
- return;
- }
- param.count++;
- setProgress(`${I18N('MISSIONS_PASSED')}: ${param.count} (${I18N('STOP')})`, false, () => {
- isStopSendMission = true;
- });
- setTimeout(sendsMission, 1, param);
- });
- })
- });
- }
+ getButtonColor(color) {
+ const buttonColors = {
+ green: 'green',
+ beige: 'brown',
+ blue: 'blue',
+ violet: 'violet',
+ yellow: 'yellow',
+ orange: 'orange',
+ indigo: 'indigo',
+ pink: 'pink',
+ red: 'red',
+ graphite: 'graphite',
+ };
+ return buttonColors[color] || buttonColors['beige'];
+ }
- /**
- * Opening of russian dolls
- *
- * Открытие матрешек
- */
- async function openRussianDolls(libId, amount) {
- let sum = 0;
- const sumResult = {};
- let count = 0;
+ setStatus(text, onclick) {
+ if (this._currentStatusClickHandler) {
+ this.status.removeEventListener('click', this._currentStatusClickHandler);
+ this._currentStatusClickHandler = null;
+ }
- while (amount) {
- sum += amount;
- setProgress(`${I18N('TOTAL_OPEN')} ${sum}`);
- const calls = [
- {
- name: 'consumableUseLootBox',
- args: { libId, amount },
- ident: 'body',
- },
- ];
- const response = await Send(JSON.stringify({ calls })).then((e) => e.results[0].result.response);
- let [countLootBox, result] = Object.entries(response).pop();
- count += +countLootBox;
- let newCount = 0;
+ if (!text) {
+ this.status.classList.add('scriptMenu_statusHide');
+ this.status.innerHTML = '';
+ } else {
+ this.status.classList.remove('scriptMenu_statusHide');
+ this.status.innerHTML = text;
+ }
- if (result?.consumable && result.consumable[libId]) {
- newCount = result.consumable[libId];
- delete result.consumable[libId];
+ if (typeof onclick === 'function') {
+ this.status.addEventListener('click', onclick, { once: true });
+ this._currentStatusClickHandler = onclick;
}
+ }
- mergeItemsObj(sumResult, result);
- amount = newCount;
+ addStatus(text) {
+ if (!this.status.innerHTML) {
+ this.status.classList.remove('scriptMenu_statusHide');
+ }
+ this.status.innerHTML += text;
}
- 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];
+ addHeader(text, onClick, main = this.mainMenu) {
+ this.emit('beforeAddHeader', text, onClick, main);
+ if (this.btnSocket) {
+ this.btnSocket = null;
+ }
+ const header = document.createElement('div');
+ header.classList.add('scriptMenu_header');
+ header.innerHTML = text;
+ if (typeof onClick === 'function') {
+ header.addEventListener('click', onClick);
}
+ main.appendChild(header);
+ this.emit('afterAddHeader', text, onClick, main);
+ return header;
}
- return obj1;
- }
-
- /**
- * Collect all mail, except letters with energy and charges of the portal
- *
- * Собрать всю почту, кроме писем с энергией и зарядами портала
- */
- function mailGetAll() {
- const getMailInfo = '{"calls":[{"name":"mailGetAll","args":{},"ident":"body"}]}';
+ addBtnSocket(back) {
+ this.btnSocket = document.createElement('div');
+ this.btnSocket.classList.add('scriptMenu_btnSocket');
+ (back ?? this.mainMenu).appendChild(this.btnSocket);
+ return this.btnSocket;
+ }
- return Send(getMailInfo).then(dataMail => {
- const { Letters } = HWHClasses;
- const letters = dataMail.results[0].result.response.letters;
- const letterIds = Letters.filter(letters);
- if (!letterIds.length) {
- setProgress(I18N('NOTHING_TO_COLLECT'), true);
- return;
+ addButton(btn, main = this.btnSocket) {
+ this.emit('beforeAddButton', btn, main);
+ //debugger;
+ let back = null;
+ if (!this.btnSocket) {
+ back = main;
+ main = this.addBtnSocket(back);
+ this.btnSocket = main;
}
+ let isOneButton = false;
- const calls = [
- { name: "mailFarm", args: { letterIds }, ident: "body" }
- ];
+ if (!main.classList.contains('scriptMenu_btnRow')) {
+ main = document.createElement('div');
+ main.classList.add('scriptMenu_btnRow');
+ isOneButton = true;
+ }
- return Send(JSON.stringify({ calls })).then(res => {
- const lettersIds = res.results[0].result.response;
- if (lettersIds) {
- const countLetters = Object.keys(lettersIds).length;
- setProgress(`${I18N('RECEIVED')} ${countLetters} ${I18N('LETTERS')}`, true);
- }
- });
- });
- }
+ const { name, onClick, title, color, dot, classes = [], isCombine } = btn;
+ const button = document.createElement('div');
+ button.classList.add('scriptMenu_btnGap', this.getButtonColor(color), ...classes);
+ button.title = title;
+ button.addEventListener('click', onClick);
+ main.appendChild(button);
- class Letters {
- /**
- * Максимальное оставшееся время для автоматического сбора письма (24 часа)
- */
- static MAX_TIME_LEFT = 24 * 60 * 60 * 1000;
+ const buttonText = document.createElement('div');
+ buttonText.classList.add('scriptMenu_btnPlate', this.getButtonColor(color));
+ buttonText.innerHTML = name;
+ button.appendChild(buttonText);
- /**
- * Фильтрует получаемые письма
- * @param {Array} letters - Массив писем для фильтрации
- * @returns {Array} - Массив ID писем, которые нужно собрать
- */
- static filter(letters) {
- const { Letters } = HWHClasses;
- const lettersIds = [];
+ if (dot) {
+ this.addIndicator(button, dot);
+ }
- for (let l in letters) {
- const letter = letters[l];
- const reward = letter?.reward;
+ if (isOneButton) {
+ this.btnSocket.appendChild(main);
+ //this.btnSocket.appendChild(main);
+ }
- if (!reward || !Object.keys(reward).length) {
- continue;
- }
+ this.buttons.push(button);
- if (Letters.shouldCollectLetter(reward)) {
- lettersIds.push(~~letter.id);
- continue;
- }
+ this.emit('afterAddButton', button, btn);
+ return button;
+ }
- // Проверка времени до окончания годности письма
- const availableUntil = +letter?.availableUntil;
- if (availableUntil) {
- const timeLeft = new Date(availableUntil * 1000) - new Date();
- console.log('Time left:', timeLeft);
+ addCombinedButton(buttonList, main = this.btnSocket) {
+ this.emit('beforeAddCombinedButton', buttonList, main);
+ let back = null;
+ if (!this.btnSocket) {
+ back = main;
+ main = this.addBtnSocket(back);
+ this.btnSocket = main;
+ }
+ const buttonGroup = document.createElement('div');
+ buttonGroup.classList.add('scriptMenu_btnRow');
+ let count = 0;
- if (timeLeft < Letters.MAX_TIME_LEFT) {
- lettersIds.push(~~letter.id);
- }
+ for (const btn of buttonList) {
+ btn.isCombine = true;
+ btn.classes ??= [];
+ if (count === 0) {
+ btn.classes.push('left');
+ } else if (count === buttonList.length - 1) {
+ btn.classes.push('right');
+ } else {
+ btn.classes.push('center');
}
+ this.addButton(btn, buttonGroup);
+ count++;
}
- return lettersIds;
- }
+ this.addIndicator(buttonGroup);
- /**
- * Определяет, нужно ли собирать письмо (может быть переопределен в дочерних классах)
- * @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.btnSocket.appendChild(buttonGroup);
+ this.emit('afterAddCombinedButton', buttonGroup, buttonList);
+ return buttonGroup;
}
- }
- this.HWHClasses.Letters = Letters;
+ addIndicator(btnSocket, title) {
+ const dotAtention = document.createElement('div');
+ dotAtention.classList.add('scriptMenu_dot');
+ dotAtention.title = title;
+ btnSocket.appendChild(dotAtention);
+ /*
+ const miniSocket = document.createElement('div');
+ miniSocket.classList.add('scriptMenu_miniSocket');
- 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;
- }
- }
+ const miniGap = document.createElement('div');
+ miniGap.classList.add('scriptMenu_miniGap');
+ miniSocket.appendChild(miniGap);
- 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;
+ const indicator = document.createElement('div');
+ indicator.classList.add('scriptMenu_indicator', 'scriptMenu_dot');
+ indicator.title = title;
+ indicator.innerHTML = '22';
+ miniGap.appendChild(indicator);
+
+ btnSocket.appendChild(miniSocket);
+ */
}
- }
- /**
- * Displaying information about the areas of the portal and attempts on the VG
- *
- * Отображение информации о сферах портала и попытках на ВГ
- */
- async function justInfo() {
- return new Promise(async (resolve, reject) => {
- const calls = [
- {
- name: 'userGetInfo',
- args: {},
- ident: 'userGetInfo',
- },
- {
- name: 'clanWarGetInfo',
- args: {},
- ident: 'clanWarGetInfo',
- },
- {
- name: 'titanArenaGetStatus',
- args: {},
- ident: 'titanArenaGetStatus',
- },
- {
- name: 'quest_completeEasterEggQuest',
- args: {},
- ident: 'quest_completeEasterEggQuest',
- },
- ];
- const result = await Send(JSON.stringify({ calls }));
- const infos = result.results;
- const portalSphere = infos[0].result.response.refillable.find(n => n.id == 45);
- const clanWarMyTries = infos[1].result.response?.myTries ?? 0;
- const arePointsMax = infos[1].result.response?.arePointsMax;
- const titansLevel = +(infos[2].result.response?.tier ?? 0);
- const titansStatus = infos[2].result.response?.status; //peace_time || battle
-
- 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');
+ addCheckbox(label, title, main = this.mainMenu) {
+ this.emit('beforeAddCheckbox', label, title, main);
+ if (this.btnSocket) {
+ this.btnSocket = null;
}
+ const divCheckbox = document.createElement('div');
+ divCheckbox.classList.add('scriptMenu_divInput');
+ divCheckbox.title = title;
+ main.appendChild(divCheckbox);
- 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();
- });
- }
+ const checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.id = 'scriptMenuCheckbox' + this.checkboxes.length;
+ checkbox.classList.add('scriptMenu_checkbox');
+ divCheckbox.appendChild(checkbox);
- async function getDailyBonus() {
- const dailyBonusInfo = await Send(JSON.stringify({
- calls: [{
- name: "dailyBonusGetInfo",
- args: {},
- ident: "body"
- }]
- })).then(e => e.results[0].result.response);
- const { availableToday, availableVip, currentDay } = dailyBonusInfo;
+ const checkboxLabel = document.createElement('label');
+ checkboxLabel.innerHTML = label;
+ checkboxLabel.setAttribute('for', checkbox.id);
+ divCheckbox.appendChild(checkboxLabel);
- if (!availableToday) {
- console.log('Уже собрано');
- return;
+ this.checkboxes.push(checkbox);
+ this.emit('afterAddCheckbox', label, title, main);
+ return checkbox;
}
- 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;
+ addInputText(title, placeholder, main = this.mainMenu) {
+ this.emit('beforeAddCheckbox', title, placeholder, main);
+ if (this.btnSocket) {
+ this.btnSocket = null;
}
- }
- const vipLevelDouble = dailyBonusStat[`${currentDay}_0_0`].vipLevelDouble;
-
- const calls = [{
- name: "dailyBonusFarm",
- args: {
- vip: availableVip && currentVipLevel >= vipLevelDouble ? 1 : 0
- },
- ident: "body"
- }];
+ const divInputText = document.createElement('div');
+ divInputText.classList.add('scriptMenu_divInputText');
+ divInputText.title = title;
+ main.appendChild(divInputText);
- const result = await Send(JSON.stringify({ calls }));
- if (result.error) {
- console.error(result.error);
- return;
+ const newInputText = document.createElement('input');
+ newInputText.type = 'text';
+ if (placeholder) {
+ newInputText.placeholder = placeholder;
+ }
+ newInputText.classList.add('scriptMenu_InputText');
+ divInputText.appendChild(newInputText);
+ this.emit('afterAddCheckbox', title, placeholder, main);
+ return newInputText;
}
- const reward = result.results[0].result.response;
- const type = Object.keys(reward).pop();
- const itemId = Object.keys(reward[type]).pop();
- const count = reward[type][itemId];
- const itemName = cheats.translate(`LIB_${type.toUpperCase()}_NAME_${itemId}`);
-
- console.log(`Ежедневная награда: Получено ${count} ${itemName}`, reward);
- }
-
- async function farmStamina(lootBoxId = 148) {
- const lootBox = await Send('{"calls":[{"name":"inventoryGet","args":{},"ident":"inventoryGet"}]}')
- .then(e => e.results[0].result.response.consumable[148]);
-
- /** Добавить другие ящики */
- /**
- * 144 - медная шкатулка
- * 145 - бронзовая шкатулка
- * 148 - платиновая шкатулка
- */
- if (!lootBox) {
- setProgress(I18N('NO_BOXES'), true);
- return;
- }
+ addDetails(summaryText, name = null) {
+ this.emit('beforeAddDetails', summaryText, name);
+ if (this.btnSocket) {
+ this.btnSocket = null;
+ }
+ const details = document.createElement('details');
+ details.classList.add('scriptMenu_Details');
+ this.mainMenu.appendChild(details);
- let maxFarmEnergy = getSaveVal('maxFarmEnergy', 100);
- const result = await popup.confirm(I18N('OPEN_LOOTBOX', { lootBox }), [
- { result: false, isClose: true },
- { msg: I18N('BTN_YES'), result: true },
- { msg: I18N('STAMINA'), isInput: true, default: maxFarmEnergy },
- ]);
-
- if (!+result) {
- return;
- }
+ const summary = document.createElement('summary');
+ summary.classList.add('scriptMenu_Summary');
+ summary.innerText = summaryText;
+ if (name) {
+ details.open = this.option.showDetails[name] ?? false;
+ details.dataset.name = name;
+ details.addEventListener('toggle', () => {
+ this.option.showDetails[details.dataset.name] = details.open;
+ this.saveSaveOption();
+ });
+ }
- if ((typeof result) !== 'boolean' && Number.parseInt(result)) {
- maxFarmEnergy = +result;
- setSaveVal('maxFarmEnergy', maxFarmEnergy);
- } else {
- maxFarmEnergy = 0;
+ details.appendChild(summary);
+ this.emit('afterAddDetails', summaryText, name);
+ return details;
}
- let collectEnergy = 0;
- for (let count = lootBox; count > 0; count--) {
- const response = await Send('{"calls":[{"name":"consumableUseLootBox","args":{"libId":148,"amount":1},"ident":"body"}]}').then(
- (e) => e.results[0].result.response
- );
- const result = Object.values(response).pop();
- if ('stamina' in result) {
- setProgress(`${I18N('OPEN')}: ${lootBox - count}/${lootBox} ${I18N('STAMINA')} +${result.stamina} ${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);
+ saveSaveOption() {
+ try {
+ localStorage.setItem('scriptMenu_saveOption', JSON.stringify(this.option));
+ } catch (e) {
+ console.log('¯\\_(ツ)_/¯');
}
}
- setProgress(I18N('BOXES_OVER'), true);
- }
-
- async function fillActive() {
- const data = await Send(JSON.stringify({
- calls: [{
- name: "questGetAll",
- args: {},
- ident: "questGetAll"
- }, {
- name: "inventoryGet",
- args: {},
- ident: "inventoryGet"
- }, {
- name: "clanGetInfo",
- args: {},
- ident: "clanGetInfo"
+ loadSaveOption() {
+ let saveOption = null;
+ try {
+ saveOption = localStorage.getItem('scriptMenu_saveOption');
+ } catch (e) {
+ console.log('¯\\_(ツ)_/¯');
}
- ]
- })).then(e => e.results.map(n => n.result.response));
- const quests = data[0];
- const inv = data[1];
- const stat = data[2].stat;
- const maxActive = 2000 - stat.todayItemsActivity;
- if (maxActive <= 0) {
- setProgress(I18N('NO_MORE_ACTIVITY'), true);
- return;
- }
-
- let countGetActive = 0;
- const quest = quests.find(e => e.id > 10046 && e.id < 10051);
- if (quest) {
- countGetActive = 1750 - quest.progress;
- }
-
- if (countGetActive <= 0) {
- countGetActive = maxActive;
- }
- console.log(countGetActive);
+ if (!saveOption) {
+ return {};
+ }
- countGetActive = +(await popup.confirm(I18N('EXCHANGE_ITEMS', { maxActive }), [
- { result: false, isClose: true },
- { msg: I18N('GET_ACTIVITY'), isInput: true, default: countGetActive.toString() },
- ]));
+ try {
+ saveOption = JSON.parse(saveOption);
+ } catch (e) {
+ return {};
+ }
- if (!countGetActive) {
- return;
+ return saveOption;
}
+ }
- if (countGetActive > maxActive) {
- countGetActive = maxActive;
- }
+ this.HWHClasses.ScriptMenu = ScriptMenu;
- const items = lib.getData('inventoryItem');
+ //const scriptMenu = ScriptMenu.getInst();
- 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 scriptMenu = ScriptMenu.getInst();
+ scriptMenu.init();
+ scriptMenu.addHeader('v1.508');
+ scriptMenu.addCheckbox('testHack', 'Тестовый взлом игры!');
+ scriptMenu.addButton({
+ text: 'Запуск!',
+ onClick: () => console.log('click'),
+ title: 'подсказака',
+ });
+ scriptMenu.addInputText('input подсказака');
+ scriptMenu.on('beforeInit', (option) => {
+ console.log('beforeInit', option);
+ })
+ scriptMenu.on('beforeAddHeader', (text, onClick, main) => {
+ console.log('beforeAddHeader', text, onClick, main);
+ });
+ scriptMenu.on('beforeAddButton', (btn, main) => {
+ console.log('beforeAddButton', btn, main);
+ });
+ scriptMenu.on('beforeAddCombinedButton', (buttonList, main) => {
+ console.log('beforeAddCombinedButton', buttonList, main);
+ });
+ scriptMenu.on('beforeAddCheckbox', (label, title, main) => {
+ console.log('beforeAddCheckbox', label, title, main);
+ });
+ scriptMenu.on('beforeAddDetails', (summaryText, name) => {
+ console.log('beforeAddDetails', summaryText, name);
+ });
+ */
- await Send(JSON.stringify({
- calls: [{
- name: "clanItemsForActivity",
- args: {
- items: {
- [activeItem.type]: {
- [activeItem.id]: countItem
- }
- }
- },
- ident: "body"
- }]
- })).then(e => {
- /** TODO: Вывести потраченые предметы */
- console.log(e);
- setProgress(`${I18N('ACTIVITY_RECEIVED')}: ` + e.results[0].result.response, true);
+ /**
+ * Sending expeditions
+ *
+ * Отправка экспедиций
+ */
+ function checkExpedition() {
+ const { Expedition } = HWHClasses;
+ return new Promise((resolve, reject) => {
+ const expedition = new Expedition(resolve, reject);
+ expedition.start();
});
}
- async function buyHeroFragments() {
- const result = await Send('{"calls":[{"name":"inventoryGet","args":{},"ident":"inventoryGet"},{"name":"shopGetAll","args":{},"ident":"shopGetAll"}]}')
- .then(e => e.results.map(n => n.result.response));
- const inv = result[0];
- const shops = Object.values(result[1]).filter(shop => [4, 5, 6, 8, 9, 10, 17].includes(shop.id));
- const calls = [];
+ class Expedition {
+ constructor(resolve, reject) {
+ this.resolve = resolve;
+ this.reject = reject;
+ }
- 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;
+ async start() {
+ const [expedInfo, dataHeroes] = await Caller.send(['expeditionGet', 'heroGetAll']);
+ const dataExped = { useHeroes: [], exped: [] };
+ const calls = [];
+
+ /**
+ * Adding expeditions to collect
+ * Добавляем экспедиции для сбора
+ */
+ let countGet = 0;
+ for (var n in expedInfo) {
+ const exped = expedInfo[n];
+ const dateNow = Date.now() / 1000;
+ if (exped.status == 2 && exped.endTime != 0 && dateNow > exped.endTime) {
+ countGet++;
+ calls.push({
+ name: 'expeditionFarm',
+ args: { expeditionId: exped.id },
+ ident: 'expeditionFarm_' + exped.id,
+ });
+ } else {
+ dataExped.useHeroes = dataExped.useHeroes.concat(exped.heroes);
}
- 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;
+ if (exped.status == 1) {
+ dataExped.exped.push({ id: exped.id, power: exped.power });
}
- inv[coin][coinId] -= slot.cost[coin][coinId];
- calls.push({
- name: "shopBuy",
- args: {
- shopId: shop.id,
- slot: slot.id,
- cost: slot.cost,
- reward: slot.reward,
- },
- ident: `shopBuy_${shop.id}_${slot.id}`,
- })
}
- }
+ dataExped.exped = dataExped.exped.sort((a, b) => b.power - a.power);
- if (!calls.length) {
- setProgress(I18N('NO_PURCHASABLE_HERO_SOULS'), true);
- return;
- }
-
- const bought = await Send(JSON.stringify({ calls })).then(e => e.results.map(n => n.result.response));
- if (!bought) {
- console.log('что-то пошло не так')
- return;
- }
-
- let countHeroSouls = 0;
- for (const buy of bought) {
- countHeroSouls += +Object.values(Object.values(buy).pop()).pop();
- }
- console.log(countHeroSouls, bought, calls);
- setProgress(I18N('PURCHASED_HERO_SOULS', { countHeroSouls }), true);
- }
-
- /** Открыть платные сундуки в Запределье за 90 */
- async function bossOpenChestPay() {
- const callsNames = ['userGetInfo', 'bossGetAll', 'specialOffer_getAll', 'getTime'];
- const info = await Send({ calls: callsNames.map((name) => ({ name, args: {}, ident: name })) }).then((e) =>
- e.results.map((n) => n.result.response)
- );
+ /**
+ * Putting together a list of heroes
+ * Собираем список героев
+ */
+ const heroesArr = [];
+ for (let n in dataHeroes) {
+ const hero = dataHeroes[n];
+ if (hero.power > 0 && !dataExped.useHeroes.includes(hero.id)) {
+ let heroPower = hero.power;
+ // Лара Крофт * 3
+ if (hero.id == 63 && hero.color >= 16) {
+ heroPower *= 3;
+ }
+ heroesArr.push({ id: hero.id, power: heroPower });
+ }
+ }
- const user = info[0];
- const boses = info[1];
- const offers = info[2];
- const time = info[3];
+ /**
+ * Adding expeditions to send
+ * Добавляем экспедиции для отправки
+ */
+ let countSend = 0;
+ heroesArr.sort((a, b) => a.power - b.power);
+ for (const exped of dataExped.exped) {
+ let heroesIds = this.selectionHeroes(heroesArr, exped.power);
+ if (heroesIds && heroesIds.length > 4) {
+ for (let q in heroesArr) {
+ if (heroesIds.includes(heroesArr[q].id)) {
+ delete heroesArr[q];
+ }
+ }
+ countSend++;
+ calls.push({
+ name: 'expeditionSendHeroes',
+ args: {
+ expeditionId: exped.id,
+ heroes: heroesIds,
+ },
+ });
+ }
+ }
- const discountOffer = offers.find((e) => e.offerType == 'costReplaceOutlandChest');
+ if (calls.length) {
+ await Caller.send(calls);
+ this.end(I18N('EXPEDITIONS_SENT', {countGet, countSend}));
+ return;
+ }
- let discount = 1;
- if (discountOffer && discountOffer.endTime > time) {
- discount = 1 - discountOffer.offerData.outlandChest.discountPercent / 100;
+ this.end(I18N('EXPEDITIONS_NOTHING'));
}
- cost9chests = 540 * discount;
- cost18chests = 1740 * discount;
- costFirstChest = 90 * discount;
- costSecondChest = 200 * discount;
-
- const currentStarMoney = user.starMoney;
- if (currentStarMoney < cost9chests) {
- setProgress('Недостаточно изюма, нужно ' + cost9chests + ' у Вас ' + currentStarMoney, true);
- return;
- }
+ /**
+ * Selection of heroes for expeditions
+ *
+ * Подбор героев для экспедиций
+ */
+ selectionHeroes(heroes, power) {
+ const resultHeroers = [];
+ const heroesIds = [];
+ for (let q = 0; q < 5; q++) {
+ for (let i in heroes) {
+ let hero = heroes[i];
+ if (heroesIds.includes(hero.id)) {
+ continue;
+ }
- const imgEmerald =
- " ";
+ const summ = resultHeroers.reduce((acc, hero) => acc + hero.power, 0);
+ const need = Math.round((power - summ) / (5 - resultHeroers.length));
+ if (hero.power > need) {
+ resultHeroers.push(hero);
+ heroesIds.push(hero.id);
+ break;
+ }
+ }
+ }
- if (currentStarMoney < cost9chests) {
- setProgress(I18N('NOT_ENOUGH_EMERALDS_540', { currentStarMoney, imgEmerald }), true);
- return;
+ const summ = resultHeroers.reduce((acc, hero) => acc + hero.power, 0);
+ if (summ < power) {
+ return false;
+ }
+ return heroesIds;
}
- 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],
- });
+ /**
+ * Ends expedition script
+ *
+ * Завершает скрипт экспедиции
+ */
+ end(msg) {
+ setProgress(msg, true);
+ this.resolve();
}
+ }
- if (currentStarMoney >= cost18chests) {
- buttons.push({
- msg: I18N('BUY_OUTLAND_BTN', { count: 18, countEmerald: cost18chests, imgEmerald }),
- result: [costFirstChest, costFirstChest, 0, costSecondChest, costSecondChest, 0],
- });
- }
+ this.HWHClasses.Expedition = Expedition;
- const answer = await popup.confirm(`${I18N('BUY_OUTLAND')}
`, buttons);
+ /**
+ * Walkthrough of the dungeon
+ *
+ * Прохождение подземелья
+ */
+ function testDungeon() {
+ const { executeDungeon } = HWHClasses;
+ return new Promise((resolve, reject) => {
+ const dung = new executeDungeon(resolve, reject);
+ const titanit = getInput('countTitanit');
+ dung.start(titanit);
+ });
+ }
- if (!answer) {
- return;
- }
+ /**
+ * Walkthrough of the dungeon
+ *
+ * Прохождение подземелья
+ */
+ function executeDungeon(resolve, reject) {
+ dungeonActivity = 0;
+ let maxDungeonActivity = 150;
- const callBoss = [];
- let n = 0;
- for (let boss of boses) {
- const bossId = boss.id;
- if (boss.chestNum != 2) {
- continue;
- }
- const calls = [];
- for (const starmoney of answer) {
- calls.push({
- name: 'bossOpenChest',
- args: {
- amount: 1,
- bossId,
- starmoney,
- },
- ident: 'bossOpenChest_' + ++n,
- });
- }
- callBoss.push(calls);
- }
+ titanGetAll = [];
- if (!callBoss.length) {
- setProgress(I18N('CHESTS_NOT_AVAILABLE'), true);
- return;
+ teams = {
+ heroes: [],
+ earth: [],
+ fire: [],
+ neutral: [],
+ water: [],
}
- let count = 0;
- let errors = 0;
- for (const calls of callBoss) {
- const result = await Send({ calls });
- console.log(result);
- if (result?.results) {
- count += result.results.length;
- } else {
- errors++;
- }
- }
+ titanStats = [];
- setProgress(`${I18N('OUTLAND_CHESTS_RECEIVED')}: ${count}`, true);
- }
+ titansStates = {};
- async function autoRaidAdventure() {
- const calls = [
- {
- name: "userGetInfo",
+ let talentMsg = '';
+ let talentMsgReward = '';
+
+ callsExecuteDungeon = {
+ calls: [{
+ name: "dungeonGetInfo",
args: {},
- ident: "userGetInfo"
- },
- {
- name: "adventure_raidGetInfo",
+ ident: "dungeonGetInfo"
+ }, {
+ name: "teamGetAll",
args: {},
- ident: "adventure_raidGetInfo"
- }
- ];
- const result = await Send(JSON.stringify({ calls }))
- .then(e => e.results.map(n => n.result.response));
-
- const portalSphere = result[0].refillable.find(n => n.id == 45);
- const adventureRaid = Object.entries(result[1].raid).filter(e => e[1]).pop()
- const adventureId = adventureRaid ? adventureRaid[0] : 0;
-
- if (!portalSphere.amount || !adventureId) {
- setProgress(I18N('RAID_NOT_AVAILABLE'), true);
- return;
- }
-
- const countRaid = +(await popup.confirm(I18N('RAID_ADVENTURE', { adventureId }), [
- { result: false, isClose: true },
- { msg: I18N('RAID'), isInput: true, default: portalSphere.amount },
- ]));
-
- if (!countRaid) {
- return;
+ ident: "teamGetAll"
+ }, {
+ name: "teamGetFavor",
+ args: {},
+ ident: "teamGetFavor"
+ }, {
+ name: "clanGetInfo",
+ args: {},
+ ident: "clanGetInfo"
+ }, {
+ name: "titanGetAll",
+ args: {},
+ ident: "titanGetAll"
+ }, {
+ name: "inventoryGet",
+ args: {},
+ ident: "inventoryGet"
+ }]
}
- if (countRaid > portalSphere.amount) {
- countRaid = portalSphere.amount;
+ this.start = function(titanit) {
+ maxDungeonActivity = titanit || getInput('countTitanit');
+ send(callsExecuteDungeon, startDungeon);
}
- const resultRaid = await Send(JSON.stringify({
- calls: [...Array(countRaid)].map((e, i) => ({
- name: "adventure_raid",
- args: {
- adventureId
- },
- ident: `body_${i}`
- }))
- })).then(e => e.results.map(n => n.result.response));
-
- if (!resultRaid.length) {
- console.log(resultRaid);
- setProgress(I18N('SOMETHING_WENT_WRONG'), true);
- return;
- }
+ /**
+ * Getting data on the dungeon
+ *
+ * Получаем данные по подземелью
+ */
+ function startDungeon(e) {
+ res = e.results;
+ dungeonGetInfo = res[0].result.response;
+ if (!dungeonGetInfo) {
+ endDungeon('noDungeon', res);
+ return;
+ }
+ teamGetAll = res[1].result.response;
+ teamGetFavor = res[2].result.response;
+ dungeonActivity = res[3].result.response.stat.todayDungeonActivity;
+ titanGetAll = Object.values(res[4].result.response);
+ HWHData.countPredictionCard = res[5].result.response.consumable[81];
- console.log(resultRaid, adventureId, portalSphere.amount);
- setProgress(I18N('ADVENTURE_COMPLETED', { adventureId, times: resultRaid.length }), true);
- }
+ teams.hero = {
+ favor: teamGetFavor.dungeon_hero,
+ heroes: teamGetAll.dungeon_hero.filter(id => id < 6000),
+ teamNum: 0,
+ }
+ heroPet = teamGetAll.dungeon_hero.filter(id => id >= 6000).pop();
+ if (heroPet) {
+ teams.hero.pet = heroPet;
+ }
- /** Вывести всю клановую статистику в консоль браузера */
- async function clanStatistic() {
- const [dataClanInfo, dataClanStat, dataClanLog] = await Caller.send(['clanGetInfo', 'clanGetWeeklyStat', 'clanGetLog']);
+ teams.neutral = {
+ favor: {},
+ heroes: getTitanTeam(titanGetAll, 'neutral'),
+ teamNum: 0,
+ };
+ teams.water = {
+ favor: {},
+ heroes: getTitanTeam(titanGetAll, 'water'),
+ teamNum: 0,
+ };
+ teams.fire = {
+ favor: {},
+ heroes: getTitanTeam(titanGetAll, 'fire'),
+ teamNum: 0,
+ };
+ teams.earth = {
+ favor: {},
+ heroes: getTitanTeam(titanGetAll, 'earth'),
+ teamNum: 0,
+ };
- const membersStat = {};
- for (let i = 0; i < dataClanStat.stat.length; i++) {
- membersStat[dataClanStat.stat[i].id] = dataClanStat.stat[i];
+
+ checkFloor(dungeonGetInfo);
}
- const joinStat = {};
- historyLog = dataClanLog.history;
- for (let j in historyLog) {
- his = historyLog[j];
- if (his.event == 'join') {
- joinStat[his.userId] = his.ctime;
+ function getTitanTeam(titans, type) {
+ switch (type) {
+ case 'neutral':
+ return titans.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
+ case 'water':
+ return titans.filter(e => e.id.toString().slice(2, 3) == '0').map(e => e.id);
+ case 'fire':
+ return titans.filter(e => e.id.toString().slice(2, 3) == '1').map(e => e.id);
+ case 'earth':
+ return titans.filter(e => e.id.toString().slice(2, 3) == '2').map(e => e.id);
}
}
- const infoArr = [];
- const members = dataClanInfo.clan.members;
- for (let n in members) {
- var member = [
- n,
- members[n].name,
- members[n].level,
- dataClanInfo.clan.warriors.includes(+n) ? 1 : 0,
- (new Date(members[n].lastLoginTime * 1000)).toLocaleString().replace(',', ''),
- joinStat[n] ? (new Date(joinStat[n] * 1000)).toLocaleString().replace(',', '') : '',
- membersStat[n].activity.reverse().join('\t'),
- membersStat[n].adventureStat.reverse().join('\t'),
- membersStat[n].clanGifts.reverse().join('\t'),
- membersStat[n].clanWarStat.reverse().join('\t'),
- membersStat[n].dungeonActivity.reverse().join('\t'),
- ];
- infoArr.push(member);
+ function getNeutralTeam() {
+ const titans = titanGetAll.filter(e => !titansStates[e.id]?.isDead)
+ return titans.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
}
- const info = infoArr.sort((a, b) => (b[2] - a[2])).map((e) => e.join('\t')).join('\n');
- console.log(info);
- copyText(info);
- setProgress(I18N('CLAN_STAT_COPY'), true);
- }
- async function buyInStoreForGold() {
- const result = await Send('{"calls":[{"name":"shopGetAll","args":{},"ident":"body"},{"name":"userGetInfo","args":{},"ident":"userGetInfo"}]}').then(e => e.results.map(n => n.result.response));
- const shops = result[0];
- const user = result[1];
- let gold = user.gold;
- const calls = [];
- if (shops[17]) {
- const slots = shops[17].slots;
- for (let i = 1; i <= 2; i++) {
- if (!slots[i].bought) {
- const costGold = slots[i].cost.gold;
- if ((gold - costGold) < 0) {
+ function fixTitanTeam(titans) {
+ titans.heroes = titans.heroes.filter(e => !titansStates[e]?.isDead);
+ return titans;
+ }
+
+ /**
+ * Checking the floor
+ *
+ * Проверяем этаж
+ */
+ async function checkFloor(dungeonInfo) {
+ if (!('floor' in dungeonInfo) || dungeonInfo.floor?.state == 2) {
+ saveProgress();
+ return;
+ }
+ checkTalent(dungeonInfo);
+ // console.log(dungeonInfo, dungeonActivity);
+ maxDungeonActivity = +getInput('countTitanit');
+ setProgress(`${I18N('DUNGEON')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity} ${talentMsg}`);
+ if (dungeonActivity >= maxDungeonActivity) {
+ endDungeon('endDungeon', 'maxActive ' + dungeonActivity + '/' + maxDungeonActivity);
+ return;
+ }
+ titansStates = dungeonInfo.states.titans;
+ titanStats = titanObjToArray(titansStates);
+ const floorChoices = dungeonInfo.floor.userData;
+ const floorType = dungeonInfo.floorType;
+ //const primeElement = dungeonInfo.elements.prime;
+ if (floorType == "battle") {
+ const calls = [];
+ for (let teamNum in floorChoices) {
+ attackerType = floorChoices[teamNum].attackerType;
+ const args = fixTitanTeam(teams[attackerType]);
+ if (attackerType == 'neutral') {
+ args.heroes = getNeutralTeam();
+ }
+ if (!args.heroes.length) {
continue;
}
- gold -= costGold;
+ args.teamNum = teamNum;
calls.push({
- name: "shopBuy",
- args: {
- shopId: 17,
- slot: i,
- cost: slots[i].cost,
- reward: slots[i].reward,
- },
- ident: 'body_' + i,
+ name: "dungeonStartBattle",
+ args,
+ ident: "body_" + teamNum
})
}
- }
- }
- const slots = shops[1].slots;
- for (let i = 4; i <= 6; i++) {
- if (!slots[i].bought && slots[i]?.cost?.gold) {
- const costGold = slots[i].cost.gold;
- if ((gold - costGold) < 0) {
- continue;
+ if (!calls.length) {
+ endDungeon('endDungeon', 'All Dead');
+ return;
}
- gold -= costGold;
- calls.push({
- name: "shopBuy",
- args: {
- shopId: 1,
- slot: i,
- cost: slots[i].cost,
- reward: slots[i].reward,
- },
- ident: 'body_' + i,
- })
+ const battleDatas = await Send({ calls })
+ .then(e => e.results.map(n => n.result.response))
+ const battleResults = [];
+ for (n in battleDatas) {
+ battleData = battleDatas[n]
+ battleData.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }];
+ battleResults.push(await Calc(battleData).then(result => {
+ result.teamNum = n;
+ result.attackerType = floorChoices[n].attackerType;
+ return result;
+ }));
+ }
+ processingPromises(battleResults)
}
}
- if (!calls.length) {
- setProgress(I18N('NOTHING_BUY'), true);
- return;
- }
-
- const resultBuy = await Send(JSON.stringify({ calls })).then(e => e.results.map(n => n.result.response));
- console.log(resultBuy);
- const countBuy = resultBuy.length;
- setProgress(I18N('LOTS_BOUGHT', { countBuy }), true);
- }
-
- async function rewardsAndMailFarm() {
- try {
- const [questGetAll, mailGetAll, specialOffer, battlePassInfo, battlePassSpecial] = await Caller.send([
- 'questGetAll',
- 'mailGetAll',
- 'specialOffer_getAll',
- 'battlePass_getInfo',
- 'battlePass_getSpecial',
- ]);
- const questsFarm = questGetAll.filter((e) => e.state == 2);
- const mailFarm = mailGetAll?.letters || [];
- const stagesOffers = specialOffer.filter(e => e.offerType === "stagesOffer" && e.farmedStage == -1);
-
- const listBattlePass = {
- [battlePassInfo.id]: battlePassInfo.battlePass,
- ...battlePassSpecial,
- };
-
- for (const passId in listBattlePass) {
- const battlePass = listBattlePass[passId];
- const levels = Object.values(lib.data.battlePass.level).filter((x) => x.battlePass == passId);
- battlePass.level = Math.max(...levels.filter((p) => battlePass.exp >= p.experience).map((p) => p.level));
+ async function checkTalent(dungeonInfo) {
+ const talent = dungeonInfo.talent;
+ if (!talent) {
+ return;
}
+ const dungeonFloor = +dungeonInfo.floorNumber;
+ const talentFloor = +talent.floorRandValue;
+ let doorsAmount = 3 - talent.conditions.doorsAmount;
- const questBattlePass = lib.getData('quest').battlePass;
- const { questChain: questChainBPass } = lib.getData('battlePass');
- const currentTime = Date.now();
-
- const farmCaller = new Caller();
+ if (dungeonFloor === talentFloor && (!doorsAmount || !talent.conditions?.farmedDoors[dungeonFloor])) {
+ const [reward] = await Caller.send([
+ { name: 'heroTalent_getReward', args: { talentType: 'tmntDungeonTalent', reroll: false } },
+ { name: 'heroTalent_farmReward', args: { talentType: 'tmntDungeonTalent' } },
+ ]);
+ const type = Object.keys(reward).pop();
+ const itemId = +Object.keys(reward[type]).pop();
+ const count = reward[type][itemId];
+ const itemName = cheats.translate(`LIB_${type.toUpperCase()}_NAME_${itemId}`);
+ talentMsgReward += ` ${count} ${itemName} `;
+ doorsAmount++;
+ }
+ talentMsg = ` TMNT Talent: ${doorsAmount}/3 ${talentMsgReward} `;
+ }
- for (const offer of stagesOffers) {
- const offerId = offer.id;
- //const stage = 0 - offer.farmedStage;
- for (const stage of offer.offerData.stages) {
- if (stage.billingId) {
- break;
- }
- farmCaller.add({
- name: 'specialOffer_farmReward',
- args: { offerId },
- });
+ function processingPromises(results) {
+ let selectBattle = results[0];
+ if (results.length < 2) {
+ // console.log(selectBattle);
+ if (!selectBattle.result.win) {
+ endDungeon('dungeonEndBattle\n', selectBattle);
+ return;
}
+ endBattle(selectBattle);
+ return;
}
- const farmQuestIds = [];
- const questIds = [];
- for (let quest of questsFarm) {
- const questId = +quest.id;
-
- /*
- if ([20010001, 20010002, 20010004].includes(questId)) {
- farmCaller.add({
- name: 'questFarm',
- args: { questId },
- });
- farmQuestIds.push(questId);
- continue;
+ selectBattle = false;
+ let bestState = -1000;
+ for (const result of results) {
+ const recovery = getState(result);
+ if (recovery > bestState) {
+ bestState = recovery;
+ selectBattle = result
}
- */
+ }
+ // console.log(selectBattle.teamNum, results);
+ if (!selectBattle || bestState <= -1000) {
+ endDungeon('dungeonEndBattle\n', results);
+ return;
+ }
- if (questId >= 2001e4 && questId < 14e8) {
- continue;
- }
+ startBattle(selectBattle.teamNum, selectBattle.attackerType)
+ .then(endBattle);
+ }
- if (quest.reward?.battlePassExp) {
- const questInfo = questBattlePass[questId];
- const chain = questChainBPass[questInfo.chain];
- const battlePass = listBattlePass[chain.battlePass];
- if (!battlePass) {
- continue;
- }
- // Наличие золотого билета
- if (chain.requirement?.battlePassTicket && !battlePass.ticket) {
- continue;
- }
- // Соответствие требований по уровню
- if (chain.requirement?.battlePassLevel && battlePass.level < chain.requirement.battlePassLevel) {
- continue;
- }
- const startTime = battlePass.startDate * 1e3;
- const endTime = battlePass.endDate * 1e3;
- // Соответствие даты проведения
- if (startTime > currentTime || endTime < currentTime) {
- continue;
- }
+ /**
+ * Let's start the fight
+ *
+ * Начинаем бой
+ */
+ function startBattle(teamNum, attackerType) {
+ return new Promise(function (resolve, reject) {
+ args = fixTitanTeam(teams[attackerType]);
+ args.teamNum = teamNum;
+ if (attackerType == 'neutral') {
+ const titans = titanGetAll.filter(e => !titansStates[e.id]?.isDead)
+ args.heroes = titans.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
}
-
- if (questId >= 2e7 && questId < 14e8) {
- questIds.push(questId);
- farmQuestIds.push(questId);
- continue;
+ startBattleCall = {
+ calls: [{
+ name: "dungeonStartBattle",
+ args,
+ ident: "body"
+ }]
}
-
- farmCaller.add({
- name: 'questFarm',
- args: { questId },
+ send(startBattleCall, resultBattle, {
+ resolve,
+ teamNum,
+ attackerType
});
- farmQuestIds.push(questId);
+ });
+ }
+ /**
+ * Returns the result of the battle in a promise
+ *
+ * Возращает резульат боя в промис
+ */
+ function resultBattle(resultBattles, args) {
+ battleData = resultBattles.results[0].result.response;
+ battleType = "get_tower";
+ if (battleData.type == "dungeon_titan") {
+ battleType = "get_titan";
}
-
- if (questIds.length) {
- farmCaller.add({
- name: 'quest_questsFarm',
- args: { questIds },
- });
+ battleData.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }];
+ BattleCalc(battleData, battleType, function (result) {
+ result.teamNum = args.teamNum;
+ result.attackerType = args.attackerType;
+ args.resolve(result);
+ });
+ }
+ /**
+ * Finishing the fight
+ *
+ * Заканчиваем бой
+ */
+ async function endBattle(battleInfo) {
+ if (battleInfo.result.win) {
+ const args = {
+ result: battleInfo.result,
+ progress: battleInfo.progress,
+ }
+ if (HWHData.countPredictionCard > 0) {
+ args.isRaid = true;
+ } else {
+ const timer = getTimer(battleInfo.battleTime);
+ console.log(timer);
+ await countdownTimer(timer, `${I18N('DUNGEON')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity} ${talentMsg}`);
+ }
+ const calls = [{
+ name: "dungeonEndBattle",
+ args,
+ ident: "body"
+ }];
+ lastDungeonBattleData = null;
+ send({ calls }, resultEndBattle);
+ } else {
+ endDungeon('dungeonEndBattle win: false\n', battleInfo);
}
+ }
- const { Letters } = HWHClasses;
- const letterIds = Letters.filter(mailFarm);
- if (letterIds.length) {
- farmCaller.add({
- name: 'mailFarm',
- args: { letterIds },
- });
+ /**
+ * Getting and processing battle results
+ *
+ * Получаем и обрабатываем результаты боя
+ */
+ function resultEndBattle(e) {
+ if ('error' in e) {
+ popup.confirm(I18N('ERROR_MSG', {
+ name: e.error.name,
+ description: e.error.description,
+ }));
+ endDungeon('errorRequest', e);
+ return;
}
-
- if (farmCaller.isEmpty()) {
- setProgress(I18N('NOTHING_TO_COLLECT'), true);
+ battleResult = e.results[0].result.response;
+ if ('error' in battleResult) {
+ endDungeon('errorBattleResult', battleResult);
return;
}
+ dungeonGetInfo = battleResult.dungeon ?? battleResult;
+ dungeonActivity += battleResult.reward.dungeonActivity ?? 0;
+ checkFloor(dungeonGetInfo);
+ }
- const farmResults = await farmCaller.send();
+ /**
+ * Returns the coefficient of condition of the
+ * difference in titanium before and after the battle
+ *
+ * Возвращает коэффициент состояния титанов после боя
+ */
+ function getState(result) {
+ if (!result.result.win) {
+ return -1000;
+ }
- let countQuests = 0;
- let countMail = 0;
- let questsIds = [];
+ let beforeSumFactor = 0;
+ const beforeTitans = result.battleData.attackers;
+ for (let titanId in beforeTitans) {
+ const titan = beforeTitans[titanId];
+ const state = titan.state;
+ let factor = 1;
+ if (state) {
+ const hp = state.hp / titan.hp;
+ const energy = state.energy / 1e3;
+ factor = hp + energy / 20
+ }
+ beforeSumFactor += factor;
+ }
- const questFarm = farmResults.result('questFarm', true);
- countQuests += questFarm.length;
- countQuests += questIds.length;
- countMail += Object.keys(farmResults.result('mailFarm')).length;
+ let afterSumFactor = 0;
+ const afterTitans = result.progress[0].attackers.heroes;
+ for (let titanId in afterTitans) {
+ const titan = afterTitans[titanId];
+ const hp = titan.hp / beforeTitans[titanId].hp;
+ const energy = titan.energy / 1e3;
+ const factor = hp + energy / 20;
+ afterSumFactor += factor;
+ }
+ return afterSumFactor - beforeSumFactor;
+ }
- const sideResult = farmResults.sideResult('questFarm', true);
- sideResult.push(...farmResults.sideResult('quest_questsFarm', true));
+ /**
+ * Converts an object with IDs to an array with IDs
+ *
+ * Преобразует объект с идетификаторами в массив с идетификаторами
+ */
+ function titanObjToArray(obj) {
+ let titans = [];
+ for (let id in obj) {
+ obj[id].id = id;
+ titans.push(obj[id]);
+ }
+ return titans;
+ }
- for (let side of sideResult) {
- const quests = [...(side.newQuests ?? []), ...(side.quests ?? [])];
- for (let quest of quests) {
- if ((quest.id < 1e6 || (quest.id >= 2e7 && quest.id < 2001e4)) && quest.state == 2) {
- questsIds.push(quest.id);
- }
- }
+ function saveProgress() {
+ let saveProgressCall = {
+ calls: [{
+ name: "dungeonSaveProgress",
+ args: {},
+ ident: "body"
+ }]
}
- questsIds = [...new Set(questsIds)];
+ send(saveProgressCall, resultEndBattle);
+ }
- while (questsIds.length) {
- const recursiveCaller = new Caller();
- const newQuestIds = [];
+ function endDungeon(reason, info) {
+ console.warn(reason, info);
+ setProgress(`${I18N('DUNGEON')} ${I18N('COMPLETED')}`, true);
+ resolve();
+ }
+ }
- for (let questId of questsIds) {
- if (farmQuestIds.includes(questId)) {
- continue;
- }
- if (questId < 1e6) {
- recursiveCaller.add({
- name: 'questFarm',
- args: { questId },
- });
- farmQuestIds.push(questId);
- countQuests++;
- } else if (questId >= 2e7 && questId < 2001e4) {
- farmQuestIds.push(questId);
- newQuestIds.push(questId);
- countQuests++;
- }
- }
+ this.HWHClasses.executeDungeon = executeDungeon;
- if (newQuestIds.length) {
- recursiveCaller.add({
- name: 'quest_questsFarm',
- args: { questIds: newQuestIds },
- });
- }
+ /**
+ * Passing the tower
+ *
+ * Прохождение башни
+ */
+ function testTower() {
+ const { executeTower } = HWHClasses;
+ return new Promise((resolve, reject) => {
+ tower = new executeTower(resolve, reject);
+ tower.start();
+ });
+ }
- questsIds = [];
- if (recursiveCaller.isEmpty()) {
- break;
- }
+ /**
+ * Passing the tower
+ *
+ * Прохождение башни
+ */
+ function executeTower(resolve, reject) {
+ lastTowerInfo = {};
- await recursiveCaller.send();
- const sideResult = recursiveCaller.sideResult('questFarm', true);
- sideResult.push(...recursiveCaller.sideResult('quest_questsFarm', true));
+ scullCoin = 0;
- for (let side of sideResult) {
- const quests = [...(side.newQuests ?? []), ...(side.quests ?? [])];
- for (let quest of quests) {
- if ((quest.id < 1e6 || (quest.id >= 2e7 && quest.id < 2001e4)) && quest.state == 2) {
- questsIds.push(quest.id);
- }
- }
- }
- questsIds = [...new Set(questsIds)];
- }
+ heroGetAll = [];
- setProgress(I18N('COLLECT_REWARDS_AND_MAIL', { countQuests, countMail }), true);
- } catch (error) {
- console.error('Error in questAllFarm:', error);
- }
- }
+ heroesStates = {};
- class epicBrawl {
- timeout = null;
- time = null;
+ argsBattle = {
+ heroes: [],
+ favor: {},
+ };
- constructor() {
- if (epicBrawl.inst) {
- return epicBrawl.inst;
- }
- epicBrawl.inst = this;
- return this;
+ callsExecuteTower = {
+ calls: [{
+ name: "towerGetInfo",
+ args: {},
+ ident: "towerGetInfo"
+ }, {
+ name: "teamGetAll",
+ args: {},
+ ident: "teamGetAll"
+ }, {
+ name: "teamGetFavor",
+ args: {},
+ ident: "teamGetFavor"
+ }, {
+ name: "inventoryGet",
+ args: {},
+ ident: "inventoryGet"
+ }, {
+ name: "heroGetAll",
+ args: {},
+ ident: "heroGetAll"
+ }]
}
- runTimeout(func, timeDiff) {
- const worker = new Worker(URL.createObjectURL(new Blob([`
- self.onmessage = function(e) {
- const timeDiff = e.data;
+ buffIds = [
+ {id: 0, cost: 0, isBuy: false}, // plug // заглушка
+ {id: 1, cost: 1, isBuy: true}, // 3% attack // 3% атака
+ {id: 2, cost: 6, isBuy: true}, // 2% attack // 2% атака
+ {id: 3, cost: 16, isBuy: true}, // 4% attack // 4% атака
+ {id: 4, cost: 40, isBuy: true}, // 8% attack // 8% атака
+ {id: 5, cost: 1, isBuy: true}, // 10% armor // 10% броня
+ {id: 6, cost: 6, isBuy: true}, // 5% armor // 5% броня
+ {id: 7, cost: 16, isBuy: true}, // 10% armor // 10% броня
+ {id: 8, cost: 40, isBuy: true}, // 20% armor // 20% броня
+ { id: 9, cost: 1, isBuy: true }, // 10% protection from magic // 10% защита от магии
+ { id: 10, cost: 6, isBuy: true }, // 5% protection from magic // 5% защита от магии
+ { id: 11, cost: 16, isBuy: true }, // 10% protection from magic // 10% защита от магии
+ { id: 12, cost: 40, isBuy: true }, // 20% protection from magic // 20% защита от магии
+ { id: 13, cost: 1, isBuy: false }, // 40% health hero // 40% здоровья герою
+ { id: 14, cost: 6, isBuy: false }, // 40% health hero // 40% здоровья герою
+ { id: 15, cost: 16, isBuy: false }, // 80% health hero // 80% здоровья герою
+ { id: 16, cost: 40, isBuy: false }, // 40% health to all heroes // 40% здоровья всем героям
+ { id: 17, cost: 1, isBuy: false }, // 40% energy to the hero // 40% энергии герою
+ { id: 18, cost: 3, isBuy: false }, // 40% energy to the hero // 40% энергии герою
+ { id: 19, cost: 8, isBuy: false }, // 80% energy to the hero // 80% энергии герою
+ { id: 20, cost: 20, isBuy: false }, // 40% energy to all heroes // 40% энергии всем героям
+ { id: 21, cost: 40, isBuy: false }, // Hero Resurrection // Воскрешение героя
+ ]
- if (timeDiff > 0) {
- setTimeout(() => {
- self.postMessage(1);
- self.close();
- }, timeDiff);
- }
- };
- `])));
- worker.postMessage(timeDiff);
- worker.onmessage = () => {
- func();
- };
- return true;
+ this.start = function () {
+ send(callsExecuteTower, startTower);
}
- timeDiff(date1, date2) {
- const date1Obj = new Date(date1);
- const date2Obj = new Date(date2);
-
- const timeDiff = Math.abs(date2Obj - date1Obj);
+ /**
+ * Getting data on the Tower
+ *
+ * Получаем данные по башне
+ */
+ function startTower(e) {
+ res = e.results;
+ towerGetInfo = res[0].result.response;
+ if (!towerGetInfo) {
+ endTower('noTower', res);
+ return;
+ }
+ teamGetAll = res[1].result.response;
+ teamGetFavor = res[2].result.response;
+ inventoryGet = res[3].result.response;
+ heroGetAll = Object.values(res[4].result.response);
- const totalSeconds = timeDiff / 1000;
- const minutes = Math.floor(totalSeconds / 60);
- const seconds = Math.floor(totalSeconds % 60);
+ scullCoin = inventoryGet.coin[7] ?? 0;
- const formattedMinutes = String(minutes).padStart(2, '0');
- const formattedSeconds = String(seconds).padStart(2, '0');
+ argsBattle.favor = teamGetFavor.tower;
+ argsBattle.heroes = heroGetAll.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
+ pet = teamGetAll.tower.filter(id => id >= 6000).pop();
+ if (pet) {
+ argsBattle.pet = pet;
+ }
- return `${formattedMinutes}:${formattedSeconds}`;
+ checkFloor(towerGetInfo);
}
- check() {
- console.log(new Date(this.time))
- if (Date.now() > this.time) {
- this.timeout = null;
- this.start()
- return;
+ function fixHeroesTeam(argsBattle) {
+ let fixHeroes = argsBattle.heroes.filter(e => !heroesStates[e]?.isDead);
+ if (fixHeroes.length < 5) {
+ heroGetAll = heroGetAll.filter(e => !heroesStates[e.id]?.isDead);
+ fixHeroes = heroGetAll.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
+ Object.keys(argsBattle.favor).forEach(e => {
+ if (!fixHeroes.includes(+e)) {
+ delete argsBattle.favor[e];
+ }
+ })
}
- this.timeout = this.runTimeout(() => this.check(), 6e4);
- return this.timeDiff(this.time, Date.now())
+ argsBattle.heroes = fixHeroes;
+ return argsBattle;
}
- async start() {
- if (this.timeout) {
- const time = this.timeDiff(this.time, Date.now());
- console.log(new Date(this.time))
- setProgress(I18N('TIMER_ALREADY', { time }), false, hideProgress);
- return;
- }
- setProgress(I18N('EPIC_BRAWL'), false, hideProgress);
- const teamInfo = await Send('{"calls":[{"name":"teamGetAll","args":{},"ident":"teamGetAll"},{"name":"teamGetFavor","args":{},"ident":"teamGetFavor"},{"name":"userGetInfo","args":{},"ident":"userGetInfo"}]}').then(e => e.results.map(n => n.result.response));
- const refill = teamInfo[2].refillable.find(n => n.id == 52)
- this.time = (refill.lastRefill + 3600) * 1000
- const attempts = refill.amount;
- if (!attempts) {
- console.log(new Date(this.time));
- const time = this.check();
- setProgress(I18N('NO_ATTEMPTS_TIMER_START', { time }), false, hideProgress);
- return;
- }
+ /**
+ * Check the floor
+ *
+ * Проверяем этаж
+ */
+ function checkFloor(towerInfo) {
+ lastTowerInfo = towerInfo;
+ maySkipFloor = +towerInfo.maySkipFloor;
+ floorNumber = +towerInfo.floorNumber;
+ heroesStates = towerInfo.states.heroes;
+ floorInfo = towerInfo.floor;
- if (!teamInfo[0].epic_brawl) {
- setProgress(I18N('NO_HEROES_PACK'), false, hideProgress);
- return;
+ /**
+ * Is there at least one chest open on the floor
+ * Открыт ли на этаже хоть один сундук
+ */
+ isOpenChest = false;
+ if (towerInfo.floorType == "chest") {
+ isOpenChest = towerInfo.floor.chests.reduce((n, e) => n + e.opened, 0);
}
- const args = {
- heroes: teamInfo[0].epic_brawl.filter(e => e < 1000),
- pet: teamInfo[0].epic_brawl.filter(e => e > 6000).pop(),
- favor: teamInfo[1].epic_brawl,
+ setProgress(`${I18N('TOWER')}: ${I18N('FLOOR')} ${floorNumber}`);
+ if (floorNumber > 49) {
+ if (isOpenChest) {
+ endTower('alreadyOpenChest 50 floor', floorNumber);
+ return;
+ }
}
-
- let wins = 0;
- let coins = 0;
- let streak = { progress: 0, nextStage: 0 };
- for (let i = attempts; i > 0; i--) {
- const info = await Send(JSON.stringify({
- calls: [
- { name: "epicBrawl_getEnemy", args: {}, ident: "epicBrawl_getEnemy" }, { name: "epicBrawl_startBattle", args, ident: "epicBrawl_startBattle" }
- ]
- })).then(e => e.results.map(n => n.result.response));
-
- const { progress, result } = await Calc(info[1].battle);
- const endResult = await Send(JSON.stringify({ calls: [{ name: "epicBrawl_endBattle", args: { progress, result }, ident: "epicBrawl_endBattle" }, { name: "epicBrawl_getWinStreak", args: {}, ident: "epicBrawl_getWinStreak" }] })).then(e => e.results.map(n => n.result.response));
-
- const resultInfo = endResult[0].result;
- streak = endResult[1];
-
- wins += resultInfo.win;
- coins += resultInfo.reward ? resultInfo.reward.coin[39] : 0;
-
- console.log(endResult[0].result)
- if (endResult[1].progress == endResult[1].nextStage) {
- const farm = await Send('{"calls":[{"name":"epicBrawl_farmWinStreak","args":{},"ident":"body"}]}').then(e => e.results[0].result.response);
- coins += farm.coin[39];
+ /**
+ * If the chest is open and you can skip floors, then move on
+ * Если сундук открыт и можно скипать этажи, то переходим дальше
+ */
+ if (towerInfo.mayFullSkip && +towerInfo.teamLevel == 130) {
+ if (floorNumber == 1) {
+ fullSkipTower();
+ return;
}
-
- setProgress(I18N('EPIC_BRAWL_RESULT', {
- i, wins, attempts, coins,
- progress: streak.progress,
- nextStage: streak.nextStage,
- end: '',
- }), false, hideProgress);
+ if (isOpenChest) {
+ nextOpenChest(floorNumber);
+ } else {
+ nextChestOpen(floorNumber);
+ }
+ return;
}
- console.log(new Date(this.time));
- const time = this.check();
- setProgress(I18N('EPIC_BRAWL_RESULT', {
- wins, attempts, coins,
- i: '',
- progress: streak.progress,
- nextStage: streak.nextStage,
- end: I18N('ATTEMPT_ENDED', { time }),
- }), false, hideProgress);
- }
- }
-
- function countdownTimer(seconds, message, onClick = null) {
- message = message || I18N('TIMER');
- const stopTimer = Date.now() + seconds * 1e3;
- const isOnClick = typeof onClick === 'function';
- return new Promise((resolve) => {
- const interval = setInterval(async () => {
- const now = Date.now();
- const remaining = (stopTimer - now) / 1000;
- const clickHandler = isOnClick
- ? () => {
- onClick();
- clearInterval(interval);
- setProgress('', true);
- resolve(false);
- }
- : undefined;
+ // console.log(towerInfo, scullCoin);
+ switch (towerInfo.floorType) {
+ case "battle":
+ if (floorNumber <= maySkipFloor) {
+ skipFloor();
+ return;
+ }
+ if (floorInfo.state == 2) {
+ nextFloor();
+ return;
+ }
+ startBattle().then(endBattle);
+ return;
+ case "buff":
+ checkBuff(towerInfo);
+ return;
+ case "chest":
+ openChest(floorNumber);
+ return;
+ default:
+ console.log('!', towerInfo.floorType, towerInfo);
+ break;
+ }
+ }
- setProgress(`${message} ${remaining.toFixed(2)}`, false, clickHandler);
- if (now > stopTimer) {
- clearInterval(interval);
- setProgress('', true);
- resolve(true);
+ /**
+ * Let's start the fight
+ *
+ * Начинаем бой
+ */
+ function startBattle() {
+ return new Promise(function (resolve, reject) {
+ towerStartBattle = {
+ calls: [{
+ name: "towerStartBattle",
+ args: fixHeroesTeam(argsBattle),
+ ident: "body"
+ }]
}
- }, 100);
- });
- }
-
- this.HWHFuncs.countdownTimer = countdownTimer;
-
- /** Набить килов в горниле душк */
- async function bossRatingEventSouls() {
- const data = await Send({
- calls: [
- { name: "heroGetAll", args: {}, ident: "teamGetAll" },
- { name: "offerGetAll", args: {}, ident: "offerGetAll" },
- { name: "pet_getAll", args: {}, ident: "pet_getAll" },
- ]
- });
- const bossEventInfo = data.results[1].result.response.find(e => e.offerType == "bossEvent");
- if (!bossEventInfo) {
- setProgress('Эвент завершен', true);
- return;
+ send(towerStartBattle, resultBattle, resolve);
+ });
}
-
- if (bossEventInfo.progress.score > 250) {
- setProgress('Уже убито больше 250 врагов');
- rewardBossRatingEventSouls();
- return;
+ /**
+ * Returns the result of the battle in a promise
+ *
+ * Возращает резульат боя в промис
+ */
+ function resultBattle(resultBattles, resolve) {
+ battleData = resultBattles.results[0].result.response;
+ battleType = "get_tower";
+ BattleCalc(battleData, battleType, function (result) {
+ resolve(result);
+ });
}
- const availablePets = Object.values(data.results[2].result.response).map(e => e.id);
- const heroGetAllList = data.results[0].result.response;
- const usedHeroes = bossEventInfo.progress.usedHeroes;
- const heroList = [];
-
- for (let heroId in heroGetAllList) {
- let hero = heroGetAllList[heroId];
- if (usedHeroes.includes(hero.id)) {
- continue;
+ /**
+ * Finishing the fight
+ *
+ * Заканчиваем бой
+ */
+ function endBattle(battleInfo) {
+ if (battleInfo.result.stars >= 3) {
+ endBattleCall = {
+ calls: [{
+ name: "towerEndBattle",
+ args: {
+ result: battleInfo.result,
+ progress: battleInfo.progress,
+ },
+ ident: "body"
+ }]
+ }
+ send(endBattleCall, resultEndBattle);
+ } else {
+ endTower('towerEndBattle win: false\n', battleInfo);
}
- heroList.push(hero.id);
- }
-
- if (!heroList.length) {
- setProgress('Нет героев', true);
- return;
}
- const pet = availablePets.includes(6005) ? 6005 : availablePets[Math.floor(Math.random() * availablePets.length)];
- const petLib = lib.getData('pet');
- let count = 1;
-
- for (const heroId of heroList) {
- const args = {
- heroes: [heroId],
- pet
+ /**
+ * Getting and processing battle results
+ *
+ * Получаем и обрабатываем результаты боя
+ */
+ function resultEndBattle(e) {
+ battleResult = e.results[0].result.response;
+ if ('error' in battleResult) {
+ endTower('errorBattleResult', battleResult);
+ return;
}
- /** Поиск питомца для героя */
- for (const petId of availablePets) {
- if (petLib[petId].favorHeroes.includes(heroId)) {
- args.favor = {
- [heroId]: petId
- }
- break;
- }
+ if ('reward' in battleResult) {
+ scullCoin += battleResult.reward?.coin[7] ?? 0;
}
+ nextFloor();
+ }
- const calls = [{
- name: "bossRatingEvent_startBattle",
- args,
- ident: "body"
- }, {
- name: "offerGetAll",
- args: {},
- ident: "offerGetAll"
- }];
-
- const res = await Send({ calls });
- count++;
-
- if ('error' in res) {
- console.error(res.error);
- setProgress('Перезагрузите игру и попробуйте позже', true);
- return;
+ function nextFloor() {
+ nextFloorCall = {
+ calls: [{
+ name: "towerNextFloor",
+ args: {},
+ ident: "body"
+ }]
}
+ send(nextFloorCall, checkDataFloor);
+ }
- const eventInfo = res.results[1].result.response.find(e => e.offerType == "bossEvent");
- if (eventInfo.progress.score > 250) {
- break;
+ function openChest(floorNumber) {
+ floorNumber = floorNumber || 0;
+ openChestCall = {
+ calls: [{
+ name: "towerOpenChest",
+ args: {
+ num: 2
+ },
+ ident: "body"
+ }]
}
- setProgress('Количество убитых врагов: ' + eventInfo.progress.score + ' Использовано ' + count + ' героев');
+ send(openChestCall, floorNumber < 50 ? nextFloor : lastChest);
}
- rewardBossRatingEventSouls();
- }
- /** Сбор награды из Горнила Душ */
- async function rewardBossRatingEventSouls() {
- const data = await Send({
- calls: [
- { name: "offerGetAll", args: {}, ident: "offerGetAll" }
- ]
- });
-
- const bossEventInfo = data.results[0].result.response.find(e => e.offerType == "bossEvent");
- if (!bossEventInfo) {
- setProgress('Эвент завершен', true);
- return;
+ function lastChest() {
+ endTower('openChest 50 floor', floorNumber);
}
- const farmedChests = bossEventInfo.progress.farmedChests;
- const score = bossEventInfo.progress.score;
- // setProgress('Количество убитых врагов: ' + score);
- const revard = bossEventInfo.reward;
- const calls = [];
-
- let count = 0;
- for (let i = 1; i < 10; i++) {
- if (farmedChests.includes(i)) {
- continue;
- }
- if (score < revard[i].score) {
- break;
+ function skipFloor() {
+ skipFloorCall = {
+ calls: [{
+ name: "towerSkipFloor",
+ args: {},
+ ident: "body"
+ }]
}
- calls.push({
- name: "bossRatingEvent_getReward",
- args: {
- rewardId: i
- },
- ident: "body_" + i
- });
- count++;
- }
- if (!count) {
- setProgress('Нечего собирать', true);
- return;
+ send(skipFloorCall, checkDataFloor);
}
- Send({ calls }).then(e => {
- console.log(e);
- setProgress('Собрано ' + e?.results?.length + ' наград', true);
- })
- }
- /**
- * Spin the Seer
- *
- * Покрутить провидца
- */
- async function rollAscension() {
- const refillable = await Send({calls:[
- {
- name:"userGetInfo",
- args:{},
- ident:"userGetInfo"
+ function checkBuff(towerInfo) {
+ buffArr = towerInfo.floor;
+ promises = [];
+ for (let buff of buffArr) {
+ buffInfo = buffIds[buff.id];
+ if (buffInfo.isBuy && buffInfo.cost <= scullCoin) {
+ scullCoin -= buffInfo.cost;
+ promises.push(buyBuff(buff.id));
+ }
}
- ]}).then(e => e.results[0].result.response.refillable);
- const i47 = refillable.find(i => i.id == 47);
- if (i47?.amount) {
- await Send({ calls: [{ name: "ascensionChest_open", args: { paid: false, amount: 1 }, ident: "body" }] });
- setProgress(I18N('DONE'), true);
- } else {
- setProgress(I18N('NOT_ENOUGH_AP'), true);
+ Promise.all(promises).then(nextFloor);
}
- }
- /**
- * Collect gifts for the New Year
- *
- * Собрать подарки на новый год
- */
- function getGiftNewYear() {
- Send({ calls: [{ name: "newYearGiftGet", args: { type: 0 }, ident: "body" }] }).then(e => {
- const gifts = e.results[0].result.response.gifts;
- const calls = gifts.filter(e => e.opened == 0).map(e => ({
- name: "newYearGiftOpen",
- args: {
- giftId: e.id
- },
- ident: `body_${e.id}`
- }));
- if (!calls.length) {
- setProgress(I18N('NY_NO_GIFTS'), 5000);
- return;
- }
- Send({ calls }).then(e => {
- console.log(e.results)
- const msg = I18N('NY_GIFTS_COLLECTED', { count: e.results.length });
- console.log(msg);
- setProgress(msg, 5000);
- });
- })
- }
-
- async function updateArtifacts() {
- const count = +await popup.confirm(I18N('SET_NUMBER_LEVELS'), [
- { msg: I18N('BTN_GO'), isInput: true, default: 10 },
- { result: false, isClose: true }
- ]);
- if (!count) {
- return;
- }
- const quest = new questRun;
- await quest.autoInit();
- const heroes = Object.values(quest.questInfo['heroGetAll']);
- const inventory = quest.questInfo['inventoryGet'];
- const calls = [];
- for (let i = count; i > 0; i--) {
- const upArtifact = quest.getUpgradeArtifact();
- if (!upArtifact.heroId) {
- if (await popup.confirm(I18N('POSSIBLE_IMPROVE_LEVELS', { count: calls.length }), [
- { msg: I18N('YES'), result: true },
- { result: false, isClose: true }
- ])) {
- break;
- } else {
- return;
- }
- }
- const hero = heroes.find(e => e.id == upArtifact.heroId);
- hero.artifacts[upArtifact.slotId].level++;
- inventory[upArtifact.costCurrency][upArtifact.costId] -= upArtifact.costValue;
- calls.push({
- name: "heroArtifactLevelUp",
- args: {
- heroId: upArtifact.heroId,
- slotId: upArtifact.slotId
- },
- ident: `heroArtifactLevelUp_${i}`
+ function buyBuff(buffId) {
+ return new Promise(function (resolve, reject) {
+ buyBuffCall = {
+ calls: [{
+ name: "towerBuyBuff",
+ args: {
+ buffId
+ },
+ ident: "body"
+ }]
+ }
+ send(buyBuffCall, resolve);
});
}
- if (!calls.length) {
- console.log(I18N('NOT_ENOUGH_RESOURECES'));
- setProgress(I18N('NOT_ENOUGH_RESOURECES'), false);
- return;
+ function checkDataFloor(result) {
+ towerInfo = result.results[0].result.response;
+ if ('reward' in towerInfo && towerInfo.reward?.coin) {
+ scullCoin += towerInfo.reward?.coin[7] ?? 0;
+ }
+ if ('tower' in towerInfo) {
+ towerInfo = towerInfo.tower;
+ }
+ if ('skullReward' in towerInfo) {
+ scullCoin += towerInfo.skullReward?.coin[7] ?? 0;
+ }
+ checkFloor(towerInfo);
}
-
- await Send(JSON.stringify({ calls })).then(e => {
- if ('error' in e) {
- console.log(I18N('NOT_ENOUGH_RESOURECES'));
- setProgress(I18N('NOT_ENOUGH_RESOURECES'), false);
- } else {
- console.log(I18N('IMPROVED_LEVELS', { count: e.results.length }));
- setProgress(I18N('IMPROVED_LEVELS', { count: e.results.length }), false);
+ /**
+ * Getting tower rewards
+ *
+ * Получаем награды башни
+ */
+ function farmTowerRewards(reason) {
+ let { pointRewards, points } = lastTowerInfo;
+ let pointsAll = Object.getOwnPropertyNames(pointRewards);
+ let farmPoints = pointsAll.filter(e => +e <= +points && !pointRewards[e]);
+ if (!farmPoints.length) {
+ return;
+ }
+ let farmTowerRewardsCall = {
+ calls: [{
+ name: "tower_farmPointRewards",
+ args: {
+ points: farmPoints
+ },
+ ident: "tower_farmPointRewards"
+ }]
}
- });
- }
- window.sign = a => {
- const i = this['\x78\x79\x7a'];
- return md5([i['\x6e\x61\x6d\x65'], i['\x76\x65\x72\x73\x69\x6f\x6e'], i['\x61\x75\x74\x68\x6f\x72'], ~(a % 1e3)]['\x6a\x6f\x69\x6e']('\x5f'))
- }
+ if (scullCoin > 0) {
+ farmTowerRewardsCall.calls.push({
+ name: "tower_farmSkullReward",
+ args: {},
+ ident: "tower_farmSkullReward"
+ });
+ }
- async function updateSkins() {
- const count = +await popup.confirm(I18N('SET_NUMBER_LEVELS'), [
- { msg: I18N('BTN_GO'), isInput: true, default: 10 },
- { result: false, isClose: true }
- ]);
- if (!count) {
- return;
+ send(farmTowerRewardsCall, () => { });
}
- const quest = new questRun;
- await quest.autoInit();
- const heroes = Object.values(quest.questInfo['heroGetAll']);
- const inventory = quest.questInfo['inventoryGet'];
- const calls = [];
- for (let i = count; i > 0; i--) {
- const upSkin = quest.getUpgradeSkin();
- if (!upSkin.heroId) {
- if (await popup.confirm(I18N('POSSIBLE_IMPROVE_LEVELS', { count: calls.length }), [
- { msg: I18N('YES'), result: true },
- { result: false, isClose: true }
- ])) {
- break;
- } else {
- return;
+ function fullSkipTower() {
+ /**
+ * Next chest
+ *
+ * Следующий сундук
+ */
+ function nextChest(n) {
+ return {
+ name: "towerNextChest",
+ args: {},
+ ident: "group_" + n + "_body"
+ }
+ }
+ /**
+ * Open chest
+ *
+ * Открыть сундук
+ */
+ function openChest(n) {
+ return {
+ name: "towerOpenChest",
+ args: {
+ "num": 2
+ },
+ ident: "group_" + n + "_body"
}
}
- const hero = heroes.find(e => e.id == upSkin.heroId);
- hero.skins[upSkin.skinId]++;
- inventory[upSkin.costCurrency][upSkin.costCurrencyId] -= upSkin.cost;
- calls.push({
- name: "heroSkinUpgrade",
- args: {
- heroId: upSkin.heroId,
- skinId: upSkin.skinId
- },
- ident: `heroSkinUpgrade_${i}`
- })
- }
- if (!calls.length) {
- console.log(I18N('NOT_ENOUGH_RESOURECES'));
- setProgress(I18N('NOT_ENOUGH_RESOURECES'), false);
- return;
- }
+ const fullSkipTowerCall = {
+ calls: []
+ }
- await Send(JSON.stringify({ calls })).then(e => {
- if ('error' in e) {
- console.log(I18N('NOT_ENOUGH_RESOURECES'));
- setProgress(I18N('NOT_ENOUGH_RESOURECES'), false);
- } else {
- console.log(I18N('IMPROVED_LEVELS', { count: e.results.length }));
- setProgress(I18N('IMPROVED_LEVELS', { count: e.results.length }), false);
+ let n = 0;
+ for (let i = 0; i < 15; i++) {
+ // 15 сундуков
+ fullSkipTowerCall.calls.push(nextChest(++n));
+ fullSkipTowerCall.calls.push(openChest(++n));
+ // +5 сундуков, 250 изюма // towerOpenChest
+ // if (i < 5) {
+ // fullSkipTowerCall.calls.push(openChest(++n, 2));
+ // }
}
- });
- }
- function getQuestionInfo(img, nameOnly = false) {
- const libHeroes = Object.values(lib.data.hero);
- const parts = img.split(':');
- const id = parts[1];
- switch (parts[0]) {
- case 'titanArtifact_id':
- return cheats.translate("LIB_TITAN_ARTIFACT_NAME_" + id);
- case 'titan':
- return cheats.translate("LIB_HERO_NAME_" + id);
- case 'skill':
- return cheats.translate("LIB_SKILL_" + id);
- case 'inventoryItem_gear':
- return cheats.translate("LIB_GEAR_NAME_" + id);
- case 'inventoryItem_coin':
- return cheats.translate("LIB_COIN_NAME_" + id);
- case 'artifact':
- if (nameOnly) {
- return cheats.translate("LIB_ARTIFACT_NAME_" + id);
- }
- heroes = libHeroes.filter(h => h.id < 100 && h.artifacts.includes(+id));
- return {
- /** Как называется этот артефакт? */
- name: cheats.translate("LIB_ARTIFACT_NAME_" + id),
- /** Какому герою принадлежит этот артефакт? */
- heroes: heroes.map(h => cheats.translate("LIB_HERO_NAME_" + h.id))
- };
- case 'hero':
- if (nameOnly) {
- return cheats.translate("LIB_HERO_NAME_" + id);
+ fullSkipTowerCall.calls.push({
+ name: 'towerGetInfo',
+ args: {},
+ ident: 'group_' + ++n + '_body',
+ });
+
+ send(fullSkipTowerCall, data => {
+ for (const r of data.results) {
+ const towerInfo = r?.result?.response;
+ if (towerInfo && 'skullReward' in towerInfo) {
+ scullCoin += towerInfo.skullReward?.coin[7] ?? 0;
+ }
}
- artifacts = lib.data.hero[id].artifacts;
- return {
- /** Как зовут этого героя? */
- name: cheats.translate("LIB_HERO_NAME_" + id),
- /** Какой артефакт принадлежит этому герою? */
- artifact: artifacts.map(a => cheats.translate("LIB_ARTIFACT_NAME_" + a))
- };
+ data.results[0] = data.results[data.results.length - 1];
+ checkDataFloor(data);
+ });
}
- }
- function hintQuest(quest) {
- const result = {};
- if (quest?.questionIcon) {
- const info = getQuestionInfo(quest.questionIcon);
- if (info?.heroes) {
- /** Какому герою принадлежит этот артефакт? */
- result.answer = quest.answers.filter(e => info.heroes.includes(e.answerText.slice(1)));
- }
- if (info?.artifact) {
- /** Какой артефакт принадлежит этому герою? */
- result.answer = quest.answers.filter(e => info.artifact.includes(e.answerText.slice(1)));
+ function nextChestOpen(floorNumber) {
+ const calls = [{
+ name: "towerOpenChest",
+ args: {
+ num: 2
+ },
+ ident: "towerOpenChest"
+ }];
+
+ Send({ calls }).then(e => {
+ nextOpenChest(floorNumber);
+ });
+ }
+
+ function nextOpenChest(floorNumber) {
+ if (floorNumber > 49) {
+ endTower('openChest 50 floor', floorNumber);
+ return;
}
- if (typeof info == 'string') {
- result.info = { name: info };
- } else {
- result.info = info;
+
+ let nextOpenChestCall = {
+ calls: [{
+ name: "towerNextChest",
+ args: {},
+ ident: "towerNextChest"
+ }, {
+ name: "towerOpenChest",
+ args: {
+ num: 2
+ },
+ ident: "towerOpenChest"
+ }]
}
+ send(nextOpenChestCall, checkDataFloor);
}
- if (quest.answers[0]?.answerIcon) {
- result.answer = quest.answers.filter(e => quest.question.includes(getQuestionInfo(e.answerIcon, true)))
+ function endTower(reason, info) {
+ console.log(reason, info);
+ if (reason != 'noTower') {
+ farmTowerRewards(reason);
+ }
+ setProgress(`${I18N('TOWER')} ${I18N('COMPLETED')}!`, true);
+ resolve();
}
+ }
- if ((!result?.answer || !result.answer.length) && !result.info?.name) {
- return false;
- }
-
- let resultText = '';
- if (result?.info) {
- resultText += I18N('PICTURE') + result.info.name;
- }
- console.log(result);
- if (result?.answer && result.answer.length) {
- resultText += I18N('ANSWER') + result.answer[0].id + (!result.answer[0].answerIcon ? ' - ' + result.answer[0].answerText : '');
- }
-
- return resultText;
- }
-
- async function farmBattlePass() {
- const isFarmReward = (reward) => {
- return !(reward?.buff || reward?.fragmentHero || reward?.bundleHeroReward);
- };
-
- const battlePassProcess = (pass) => {
- if (!pass.id) {return []}
- const levels = Object.values(lib.data.battlePass.level).filter(x => x.battlePass == pass.id)
- const last_level = levels[levels.length - 1];
- let actual = Math.max(...levels.filter(p => pass.exp >= p.experience).map(p => p.level))
-
- if (pass.exp > last_level.experience) {
- actual = last_level.level + (pass.exp - last_level.experience) / last_level.experienceByLevel;
- }
- const calls = [];
- for(let i = 1; i <= actual; i++) {
- const level = i >= last_level.level ? last_level : levels.find(l => l.level === i);
- const reward = {free: level?.freeReward, paid:level?.paidReward};
-
- if (!pass.rewards[i]?.free && isFarmReward(reward.free)) {
- const args = {level: i, free:true};
- if (!pass.gold) { args.id = pass.id }
- calls.push({ name: 'battlePass_farmReward', args, ident: `${pass.gold ? 'body' : 'spesial'}_free_${args.id}_${i}` });
- }
- if (pass.ticket && !pass.rewards[i]?.paid && isFarmReward(reward.paid)) {
- const args = {level: i, free:false};
- if (!pass.gold) { args.id = pass.id}
- calls.push({ name: 'battlePass_farmReward', args, ident: `${pass.gold ? 'body' : 'spesial'}_paid_${args.id}_${i}` });
- }
- }
- return calls;
- }
-
- const passes = await Send({
- calls: [
- { name: 'battlePass_getInfo', args: {}, ident: 'getInfo' },
- { name: 'battlePass_getSpecial', args: {}, ident: 'getSpecial' },
- ],
- }).then((e) => [{...e.results[0].result.response?.battlePass, gold: true}, ...Object.values(e.results[1].result.response)]);
-
- const calls = passes.map(p => battlePassProcess(p)).flat()
-
- if (!calls.length) {
- setProgress(I18N('NOTHING_TO_COLLECT'));
- return;
- }
-
- let results = await Send({calls});
- if (results.error) {
- console.log(results.error);
- setProgress(I18N('SOMETHING_WENT_WRONG'));
- } else {
- setProgress(I18N('SEASON_REWARD_COLLECTED', {count: results.results.length}), true);
- }
- }
-
- async function sellHeroSoulsForGold() {
- let { fragmentHero, heroes } = await Send({
- calls: [
- { name: 'inventoryGet', args: {}, ident: 'inventoryGet' },
- { name: 'heroGetAll', args: {}, ident: 'heroGetAll' },
- ],
- })
- .then((e) => e.results.map((r) => r.result.response))
- .then((e) => ({ fragmentHero: e[0].fragmentHero, heroes: e[1] }));
-
- const calls = [];
- for (let i in fragmentHero) {
- if (heroes[i] && heroes[i].star == 6) {
- calls.push({
- name: 'inventorySell',
- args: {
- type: 'hero',
- libId: i,
- amount: fragmentHero[i],
- fragment: true,
- },
- ident: 'inventorySell_' + i,
- });
- }
- }
- if (!calls.length) {
- console.log(0);
- return 0;
- }
- const rewards = await Send({ calls }).then((e) => e.results.map((r) => r.result?.response?.gold || 0));
- const gold = rewards.reduce((e, a) => e + a, 0);
- setProgress(I18N('GOLD_RECEIVED', { gold }), true);
- }
+ this.HWHClasses.executeTower = executeTower;
/**
- * Attack of the minions of Asgard
+ * Passage of the arena of the titans
*
- * Атака прислужников Асгарда
+ * Прохождение арены титанов
*/
- function testRaidNodes() {
- const { executeRaidNodes } = HWHClasses;
+ function testTitanArena() {
+ const { executeTitanArena } = HWHClasses;
return new Promise((resolve, reject) => {
- const tower = new executeRaidNodes(resolve, reject);
- tower.start();
+ titAren = new executeTitanArena(resolve, reject);
+ titAren.start();
});
}
/**
- * Attack of the minions of Asgard
+ * Passage of the arena of the titans
*
- * Атака прислужников Асгарда
+ * Прохождение арены титанов
*/
- function executeRaidNodes(resolve, reject) {
- let raidData = {
- teams: [],
- favor: {},
- nodes: [],
- attempts: 0,
- countExecuteBattles: 0,
- cancelBattle: 0,
- }
+ function executeTitanArena(resolve, reject) {
+ let titan_arena = [];
+ let finishListBattle = [];
+ /**
+ * ID of the current batch
+ *
+ * Идетификатор текущей пачки
+ */
+ let currentRival = 0;
+ /**
+ * Number of attempts to finish off the pack
+ *
+ * Количество попыток добития пачки
+ */
+ let attempts = 0;
+ /**
+ * Was there an attempt to finish off the current shooting range
+ *
+ * Была ли попытка добития текущего тира
+ */
+ let isCheckCurrentTier = false;
+ /**
+ * Current shooting range
+ *
+ * Текущий тир
+ */
+ let currTier = 0;
+ /**
+ * Number of battles on the current dash
+ *
+ * Количество битв на текущем тире
+ */
+ let countRivalsTier = 0;
- callsExecuteRaidNodes = {
+ let callsStart = {
calls: [{
- name: "clanRaid_getInfo",
+ name: "titanArenaGetStatus",
args: {},
- ident: "clanRaid_getInfo"
+ ident: "titanArenaGetStatus"
}, {
name: "teamGetAll",
args: {},
ident: "teamGetAll"
- }, {
- name: "teamGetFavor",
- args: {},
- ident: "teamGetFavor"
}]
}
this.start = function () {
- send(JSON.stringify(callsExecuteRaidNodes), startRaidNodes);
+ send(callsStart, startTitanArena);
}
- async function startRaidNodes(data) {
- res = data.results;
- clanRaidInfo = res[0].result.response;
- teamGetAll = res[1].result.response;
- teamGetFavor = res[2].result.response;
-
- let index = 0;
- let isNotFullPack = false;
- for (let team of teamGetAll.clanRaid_nodes) {
- if (team.length < 6) {
- isNotFullPack = true;
- }
- raidData.teams.push({
- data: {},
- heroes: team.filter(id => id < 6000),
- pet: team.filter(id => id >= 6000).pop(),
- battleIndex: index++
- });
- }
- raidData.favor = teamGetFavor.clanRaid_nodes;
+ function startAgain() {
+ send(callsStart, startTitanArena);
+ }
- if (isNotFullPack) {
- if (await popup.confirm(I18N('MINIONS_WARNING'), [
- { msg: I18N('BTN_NO'), result: true },
- { msg: I18N('BTN_YES'), result: false },
- ])) {
- endRaidNodes('isNotFullPack');
- return;
- }
+ function startTitanArena(data) {
+ let titanArena = data.results[0].result.response;
+ if (titanArena.status == 'disabled') {
+ endTitanArena('disabled', titanArena);
+ return;
}
- raidData.nodes = clanRaidInfo.nodes;
- raidData.attempts = clanRaidInfo.attempts;
- setIsCancalBattle(false);
+ let teamGetAll = data.results[1].result.response;
+ titan_arena = teamGetAll.titan_arena;
- checkNodes();
+ checkTier(titanArena)
}
- function getAttackNode() {
- for (let nodeId in raidData.nodes) {
- let node = raidData.nodes[nodeId];
- let points = 0
- for (team of node.teams) {
- points += team.points;
- }
- let now = Date.now() / 1000;
- if (!points && now > node.timestamps.start && now < node.timestamps.end) {
- let countTeam = node.teams.length;
- delete raidData.nodes[nodeId];
- return {
- nodeId,
- countTeam
- };
- }
+ function checkTier(titanArena) {
+ if (titanArena.status == "peace_time") {
+ endTitanArena('Peace_time', titanArena);
+ return;
+ }
+ currTier = titanArena.tier;
+ if (currTier) {
+ setProgress(`${I18N('TITAN_ARENA')}: ${I18N('LEVEL')} ${currTier}`);
}
- return null;
- }
- function checkNodes() {
- setProgress(`${I18N('REMAINING_ATTEMPTS')}: ${raidData.attempts}`);
- let nodeInfo = getAttackNode();
- if (nodeInfo && raidData.attempts) {
- startNodeBattles(nodeInfo);
+ if (titanArena.status == "completed_tier") {
+ titanArenaCompleteTier();
+ return;
+ }
+ /**
+ * Checking for the possibility of a raid
+ * Проверка на возможность рейда
+ */
+ if (titanArena.canRaid) {
+ titanArenaStartRaid();
+ return;
+ }
+ /**
+ * Check was an attempt to achieve the current shooting range
+ * Проверка была ли попытка добития текущего тира
+ */
+ if (!isCheckCurrentTier) {
+ checkRivals(titanArena.rivals);
return;
}
- endRaidNodes('EndRaidNodes');
+ endTitanArena('Done or not canRaid', titanArena);
}
-
- function startNodeBattles(nodeInfo) {
- let {nodeId, countTeam} = nodeInfo;
- let teams = raidData.teams.slice(0, countTeam);
- let heroes = raidData.teams.map(e => e.heroes).flat();
- let favor = {...raidData.favor};
- for (let heroId in favor) {
- if (!heroes.includes(+heroId)) {
- delete favor[heroId];
- }
+ /**
+ * Submit dash information for verification
+ *
+ * Отправка информации о тире на проверку
+ */
+ function checkResultInfo(data) {
+ if (!data?.results) {
+ console.error(data);
+ startAgain();
+ return;
}
-
+ let titanArena = data.results[0].result.response;
+ checkTier(titanArena);
+ }
+ /**
+ * Finish the current tier
+ *
+ * Завершить текущий тир
+ */
+ function titanArenaCompleteTier() {
+ isCheckCurrentTier = false;
let calls = [{
- name: "clanRaid_startNodeBattles",
- args: {
- nodeId,
- teams,
- favor
- },
+ name: "titanArenaCompleteTier",
+ args: {},
ident: "body"
}];
-
- send(JSON.stringify({calls}), resultNodeBattles);
- }
-
- function resultNodeBattles(e) {
- if (e['error']) {
- endRaidNodes('nodeBattlesError', e['error']);
- return;
- }
-
- console.log(e);
- let battles = e.results[0].result.response.battles;
- let promises = [];
- let battleIndex = 0;
- for (let battle of battles) {
- battle.battleIndex = battleIndex++;
- promises.push(calcBattleResult(battle));
- }
-
- Promise.all(promises)
- .then(results => {
- const endResults = {};
- let isAllWin = true;
- for (let r of results) {
- isAllWin &&= r.result.win;
- }
- if (!isAllWin) {
- cancelEndNodeBattle(results[0]);
- return;
- }
- raidData.countExecuteBattles = results.length;
- let timeout = 500;
- for (let r of results) {
- setTimeout(endNodeBattle, timeout, r);
- timeout += 500;
- }
- });
+ send({calls}, checkResultInfo);
}
/**
- * Returns the battle calculation promise
+ * Gathering points to be completed
*
- * Возвращает промис расчета боя
+ * Собираем точки которые нужно добить
*/
- function calcBattleResult(battleData) {
- return new Promise(function (resolve, reject) {
- BattleCalc(battleData, "get_clanPvp", resolve);
- });
+ function checkRivals(rivals) {
+ finishListBattle = [];
+ for (let n in rivals) {
+ if (rivals[n].attackScore < 250) {
+ finishListBattle.push(n);
+ }
+ }
+ console.log('checkRivals', finishListBattle);
+ countRivalsTier = finishListBattle.length;
+ roundRivals();
}
/**
- * Cancels the fight
+ * Selecting the next point to finish off
*
- * Отменяет бой
+ * Выбор следующей точки для добития
*/
- function cancelEndNodeBattle(r) {
- const fixBattle = function (heroes) {
- for (const ids in heroes) {
- hero = heroes[ids];
- hero.energy = random(1, 999);
- if (hero.hp > 0) {
- hero.hp = random(1, hero.hp);
- }
- }
+ function roundRivals() {
+ let countRivals = finishListBattle.length;
+ if (!countRivals) {
+ /**
+ * Whole range checked
+ *
+ * Весь тир проверен
+ */
+ isCheckCurrentTier = true;
+ titanArenaGetStatus();
+ return;
}
- fixBattle(r.progress[0].attackers.heroes);
- fixBattle(r.progress[0].defenders.heroes);
- endNodeBattle(r);
+ // setProgress('TitanArena: Уровень ' + currTier + ' Бои: ' + (countRivalsTier - countRivals + 1) + '/' + countRivalsTier);
+ currentRival = finishListBattle.pop();
+ attempts = +currentRival;
+ // console.log('roundRivals', currentRival);
+ titanArenaStartBattle(currentRival);
}
/**
- * Ends the fight
+ * The start of a solo battle
*
- * Завершает бой
+ * Начало одиночной битвы
*/
- function endNodeBattle(r) {
- let nodeId = r.battleData.result.nodeId;
- let battleIndex = r.battleData.battleIndex;
+ function titanArenaStartBattle(rivalId) {
let calls = [{
- name: "clanRaid_endNodeBattle",
+ name: "titanArenaStartBattle",
args: {
- nodeId,
- battleIndex,
- result: r.result,
- progress: r.progress
+ rivalId: rivalId,
+ titans: titan_arena
},
ident: "body"
- }]
-
- SendRequest(JSON.stringify({calls}), battleResult);
+ }];
+ send({calls}, calcResult);
}
/**
- * Processing the results of the battle
+ * Calculation of the results of the battle
*
- * Обработка результатов боя
+ * Расчет результатов боя
*/
- function battleResult(e) {
- if (e['error']) {
- endRaidNodes('missionEndError', e['error']);
+ function calcResult(data) {
+ let battlesInfo = data.results[0].result.response.battle;
+ /**
+ * If attempts are equal to the current battle number we make
+ * Если попытки равны номеру текущего боя делаем прерасчет
+ */
+ if (attempts == currentRival) {
+ preCalcBattle(battlesInfo);
return;
}
- r = e.results[0].result.response;
- if (r['error']) {
- if (r.reason == "invalidBattle") {
- raidData.cancelBattle++;
- checkNodes();
- } else {
- endRaidNodes('missionEndError', e['error']);
- }
+ /**
+ * If there are still attempts, we calculate a new battle
+ * Если попытки еще есть делаем расчет нового боя
+ */
+ if (attempts > 0) {
+ attempts--;
+ calcBattleResult(battlesInfo)
+ .then(resultCalcBattle);
return;
}
-
- if (!(--raidData.countExecuteBattles)) {
- raidData.attempts--;
- checkNodes();
- }
+ /**
+ * Otherwise, go to the next opponent
+ * Иначе переходим к следующему сопернику
+ */
+ roundRivals();
}
/**
- * Completing a task
+ * Processing the results of the battle calculation
*
- * Завершение задачи
+ * Обработка результатов расчета битвы
*/
- function endRaidNodes(reason, info) {
- setIsCancalBattle(true);
- let textCancel = raidData.cancelBattle ? ` ${I18N('BATTLES_CANCELED')}: ${raidData.cancelBattle}` : '';
- setProgress(`${I18N('MINION_RAID')} ${I18N('COMPLETED')}! ${textCancel}`, true);
- console.log(reason, info);
- resolve();
- }
- }
-
- this.HWHClasses.executeRaidNodes = executeRaidNodes;
-
- /**
- * Asgard Boss Attack Replay
- *
- * Повтор атаки босса Асгарда
- */
- function testBossBattle() {
- const { executeBossBattle } = HWHClasses;
- return new Promise((resolve, reject) => {
- const bossBattle = new executeBossBattle(resolve, reject);
- bossBattle.start(lastBossBattle);
- });
- }
-
- /**
- * Asgard Boss Attack Replay
- *
- * Повтор атаки босса Асгарда
- */
- function executeBossBattle(resolve, reject) {
-
- this.start = function (battleInfo) {
- preCalcBattle(battleInfo);
+ async function resultCalcBattle(resultBattle) {
+ // console.log('resultCalcBattle', currentRival, attempts, resultBattle.result.win);
+ /**
+ * If the current calculation of victory is not a chance or the attempt ended with the finish the battle
+ * Если текущий расчет победа или шансов нет или попытки кончились завершаем бой
+ */
+ if (resultBattle.result.win || !attempts) {
+ let { progress, result } = resultBattle;
+ /*
+ if (!resultBattle.result.win && isChecked('tryFixIt_v2')) {
+ const bFix = new BestOrWinFixBattle(resultBattle.battleData);
+ bFix.isGetTimer = false;
+ bFix.maxTimer = 100;
+ const resultFix = await bFix.start(Date.now() + 6e4, 500);
+ if (resultFix.value > 0) {
+ progress = resultFix.progress;
+ result = resultFix.result;
+ }
+ }
+ */
+ titanArenaEndBattle({
+ progress,
+ result,
+ rivalId: resultBattle.battleData.typeId,
+ });
+ return;
+ }
+ /**
+ * If not victory and there are attempts we start a new battle
+ * Если не победа и есть попытки начинаем новый бой
+ */
+ titanArenaStartBattle(resultBattle.battleData.typeId);
}
-
- function getBattleInfo(battle) {
+ /**
+ * Returns the promise of calculating the results of the battle
+ *
+ * Возращает промис расчета результатов битвы
+ */
+ function getBattleInfo(battle, isRandSeed) {
return new Promise(function (resolve) {
- battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3);
- BattleCalc(battle, getBattleType(battle.type), e => {
- let extra = e.progress[0].defenders.heroes[1].extra;
- resolve(extra.damageTaken + extra.damageTakenNextLevel);
- });
+ battle = structuredClone(battle);
+ if (isRandSeed) {
+ battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3);
+ }
+ // console.log(battle.seed);
+ BattleCalc(battle, "get_titanClanPvp", e => resolve(e));
});
}
-
+ /**
+ * Recalculate battles
+ *
+ * Прерасчтет битвы
+ */
function preCalcBattle(battle) {
- let actions = [];
+ let actions = [getBattleInfo(battle, false)];
const countTestBattle = getInput('countTestBattle');
for (let i = 0; i < countTestBattle; i++) {
actions.push(getBattleInfo(battle, true));
@@ -10885,751 +10583,739 @@
Promise.all(actions)
.then(resultPreCalcBattle);
}
-
- async function resultPreCalcBattle(damages) {
- let maxDamage = 0;
- let minDamage = 1e10;
- let avgDamage = 0;
- for (let damage of damages) {
- avgDamage += damage
- if (damage > maxDamage) {
- maxDamage = damage;
- }
- if (damage < minDamage) {
- minDamage = damage;
- }
+ /**
+ * Processing the results of the battle recalculation
+ *
+ * Обработка результатов прерасчета битвы
+ */
+ function resultPreCalcBattle(e) {
+ let wins = e.map(n => n.result.win);
+ let firstBattle = e.shift();
+ let countWin = wins.reduce((w, s) => w + s);
+ const countTestBattle = getInput('countTestBattle');
+ console.log('resultPreCalcBattle', `${countWin}/${countTestBattle}`)
+ if (countWin > 0) {
+ attempts = getInput('countAutoBattle');
+ } else {
+ attempts = 0;
}
- avgDamage /= damages.length;
- console.log(damages.map(e => e.toLocaleString()).join('\n'), avgDamage, maxDamage);
+ resultCalcBattle(firstBattle);
+ }
- await popup.confirm(
- `${I18N('ROUND_STAT')} ${damages.length} ${I18N('BATTLE')}:` +
- ` ${I18N('MINIMUM')}: ` + minDamage.toLocaleString() +
- ` ${I18N('MAXIMUM')}: ` + maxDamage.toLocaleString() +
- ` ${I18N('AVERAGE')}: ` + avgDamage.toLocaleString()
- , [
- { msg: I18N('BTN_OK'), result: 0},
- ])
- endBossBattle(I18N('BTN_CANCEL'));
+ /**
+ * Complete an arena battle
+ *
+ * Завершить битву на арене
+ */
+ function titanArenaEndBattle(args) {
+ let calls = [{
+ name: "titanArenaEndBattle",
+ args,
+ ident: "body"
+ }];
+ send({calls}, resultTitanArenaEndBattle);
}
+ function resultTitanArenaEndBattle(e) {
+ let attackScore = e.results[0].result.response.attackScore;
+ let numReval = countRivalsTier - finishListBattle.length;
+ setProgress(`${I18N('TITAN_ARENA')}: ${I18N('LEVEL')} ${currTier} ${I18N('BATTLES')}: ${numReval}/${countRivalsTier} - ${attackScore}`);
+ // console.log('resultTitanArenaEndBattle', e)
+ console.log('resultTitanArenaEndBattle', numReval + '/' + countRivalsTier, attempts)
+ roundRivals();
+ }
/**
- * Completing a task
+ * Arena State
*
- * Завершение задачи
+ * Состояние арены
*/
- function endBossBattle(reason, info) {
- console.log(reason, info);
- resolve();
+ function titanArenaGetStatus() {
+ let calls = [{
+ name: "titanArenaGetStatus",
+ args: {},
+ ident: "body"
+ }];
+ send({calls}, checkResultInfo);
+ }
+ /**
+ * Arena Raid Request
+ *
+ * Запрос рейда арены
+ */
+ function titanArenaStartRaid() {
+ let calls = [{
+ name: "titanArenaStartRaid",
+ args: {
+ titans: titan_arena
+ },
+ ident: "body"
+ }];
+ send({calls}, calcResults);
}
- }
- this.HWHClasses.executeBossBattle = executeBossBattle;
+ function calcResults(data) {
+ let battlesInfo = data.results[0].result.response;
+ let {attackers, rivals} = battlesInfo;
- class FixBattle {
- minTimer = 1.3;
- maxTimer = 15.3;
+ let promises = [];
+ for (let n in rivals) {
+ rival = rivals[n];
+ promises.push(calcBattleResult({
+ attackers: attackers,
+ defenders: [rival.team],
+ seed: rival.seed,
+ typeId: n,
+ }));
+ }
- constructor(battle, isTimeout = true) {
- this.battle = structuredClone(battle);
- this.isTimeout = isTimeout;
- this.isGetTimer = true;
+ Promise.all(promises)
+ .then(results => {
+ const endResults = {};
+ for (let info of results) {
+ let id = info.battleData.typeId;
+ endResults[id] = {
+ progress: info.progress,
+ result: info.result,
+ }
+ }
+ titanArenaEndRaid(endResults);
+ });
}
- timeout(callback, timeout) {
- if (this.isTimeout) {
- this.worker.postMessage(timeout);
- this.worker.onmessage = callback;
- } else {
- callback();
- }
+ function calcBattleResult(battleData) {
+ return new Promise(function (resolve, reject) {
+ BattleCalc(battleData, "get_titanClanPvp", resolve);
+ });
}
- randTimer() {
- return Math.random() * (this.maxTimer - this.minTimer + 1) + this.minTimer;
+ /**
+ * Sending Raid Results
+ *
+ * Отправка результатов рейда
+ */
+ function titanArenaEndRaid(results) {
+ titanArenaEndRaidCall = {
+ calls: [{
+ name: "titanArenaEndRaid",
+ args: {
+ results
+ },
+ ident: "body"
+ }]
+ }
+ send(titanArenaEndRaidCall, checkRaidResults);
}
- getTimer() {
- if (this.count === 1) {
- this.initTimers();
+ function checkRaidResults(data) {
+ results = data.results[0].result.response.results;
+ isSucsesRaid = true;
+ for (let i in results) {
+ isSucsesRaid &&= (results[i].attackScore >= 250);
}
- return this.battleLogTimers[this.count];
+ if (isSucsesRaid) {
+ titanArenaCompleteTier();
+ } else {
+ titanArenaGetStatus();
+ }
}
- setAvgTime(startTime) {
- this.fixTime += Date.now() - startTime;
- this.avgTime = this.fixTime / this.count;
+ function titanArenaFarmDailyReward() {
+ titanArenaFarmDailyRewardCall = {
+ calls: [{
+ name: "titanArenaFarmDailyReward",
+ args: {},
+ ident: "body"
+ }]
+ }
+ send(titanArenaFarmDailyRewardCall, () => {console.log('Done farm daily reward')});
}
- initTimers() {
- const timers = [...new Set(this.lastResult.battleLogs[0].map((e) => e.time))];
- this.battleLogTimers = timers.sort(() => Math.random() - 0.5);
- this.maxCount = Math.min(this.maxCount, this.battleLogTimers.length);
- console.log('maxCount', this.maxCount);
+ function endTitanArena(reason, info) {
+ if (!['Peace_time', 'disabled'].includes(reason)) {
+ titanArenaFarmDailyReward();
+ }
+ console.log(reason, info);
+ setProgress(`${I18N('TITAN_ARENA')} ${I18N('COMPLETED')}!`, true);
+ resolve();
}
+ }
- init() {
- this.fixTime = 0;
- this.lastTimer = 0;
- this.index = 0;
- this.lastBossDamage = 0;
- this.bestResult = {
- count: 0,
- timer: 0,
- value: -Infinity,
- result: null,
- progress: null,
- };
- this.lastBattleResult = {
- win: false,
- };
- this.worker = new Worker(
- URL.createObjectURL(
- new Blob([
- `self.onmessage = function(e) {
- const timeout = e.data;
- setTimeout(() => {
- self.postMessage(1);
- }, timeout);
- };`,
- ])
- )
- );
+ this.HWHClasses.executeTitanArena = executeTitanArena;
+
+ /**
+ * Attack of the minions of Asgard
+ *
+ * Атака прислужников Асгарда
+ */
+ function testRaidNodes() {
+ const { executeRaidNodes } = HWHClasses;
+ return new Promise((resolve, reject) => {
+ const tower = new executeRaidNodes(resolve, reject);
+ tower.start();
+ });
+ }
+
+ /**
+ * Attack of the minions of Asgard
+ *
+ * Атака прислужников Асгарда
+ */
+ function executeRaidNodes(resolve, reject) {
+ let raidData = {
+ teams: [],
+ favor: {},
+ nodes: [],
+ attempts: 0,
+ countExecuteBattles: 0,
+ cancelBattle: 0,
}
- async start(endTime = Date.now() + 6e4, maxCount = 100) {
- this.endTime = endTime;
- this.maxCount = maxCount;
- this.init();
- return await new Promise((resolve) => {
- this.resolve = resolve;
- this.count = 0;
- this.loop();
- });
+ callsExecuteRaidNodes = {
+ calls: [{
+ name: "clanRaid_getInfo",
+ args: {},
+ ident: "clanRaid_getInfo"
+ }, {
+ name: "teamGetAll",
+ args: {},
+ ident: "teamGetAll"
+ }, {
+ name: "teamGetFavor",
+ args: {},
+ ident: "teamGetFavor"
+ }]
}
- endFix() {
- this.bestResult.maxCount = this.count;
- this.worker.terminate();
- console.log('endFix', this.bestResult);
- this.resolve(this.bestResult);
+ this.start = function () {
+ send(callsExecuteRaidNodes, startRaidNodes);
}
- async loop() {
- const start = Date.now();
- if (this.isEndLoop()) {
- this.endFix();
- return;
- }
- this.count++;
- try {
- this.lastResult = await Calc(this.battle);
- } catch (e) {
- this.updateProgressTimer(this.index++);
- this.timeout(this.loop.bind(this), 0);
- return;
- }
- const { progress, result } = this.lastResult;
- this.lastBattleResult = result;
- this.lastBattleProgress = progress;
- this.setAvgTime(start);
- this.checkResult();
- this.showResult();
- this.updateProgressTimer();
- this.timeout(this.loop.bind(this), 0);
- }
-
- isEndLoop() {
- return this.count >= this.maxCount || this.endTime < Date.now();
- }
-
- updateProgressTimer(index = 0) {
- this.lastTimer = this.isGetTimer ? this.getTimer() : this.randTimer();
- this.battle.progress = [{ attackers: { input: ['auto', 0, 0, 'auto', index, this.lastTimer] } }];
- }
-
- showResult() {
- console.log(
- this.count,
- this.avgTime.toFixed(2),
- (this.endTime - Date.now()) / 1000,
- this.lastTimer.toFixed(2),
- this.lastBossDamage.toLocaleString(),
- this.bestResult.value.toLocaleString()
- );
- }
+ async function startRaidNodes(data) {
+ res = data.results;
+ clanRaidInfo = res[0].result.response;
+ teamGetAll = res[1].result.response;
+ teamGetFavor = res[2].result.response;
- checkResult() {
- const { damageTaken, damageTakenNextLevel } = this.lastBattleProgress[0].defenders.heroes[1].extra;
- this.lastBossDamage = damageTaken + damageTakenNextLevel;
- if (this.lastBossDamage > this.bestResult.value) {
- this.bestResult = {
- count: this.count,
- timer: this.lastTimer,
- value: this.lastBossDamage,
- result: structuredClone(this.lastBattleResult),
- progress: structuredClone(this.lastBattleProgress),
- };
+ let index = 0;
+ let isNotFullPack = false;
+ for (let team of teamGetAll.clanRaid_nodes) {
+ if (team.length < 6) {
+ isNotFullPack = true;
+ }
+ raidData.teams.push({
+ data: {},
+ heroes: team.filter(id => id < 6000),
+ pet: team.filter(id => id >= 6000).pop(),
+ battleIndex: index++
+ });
}
- }
-
- stopFix() {
- this.endTime = 0;
- }
- }
-
- this.HWHClasses.FixBattle = FixBattle;
+ raidData.favor = teamGetFavor.clanRaid_nodes;
- class WinFixBattle extends FixBattle {
- checkResult() {
- if (this.lastBattleResult.win) {
- this.bestResult = {
- count: this.count,
- timer: this.lastTimer,
- value: this.lastBattleResult.stars,
- result: structuredClone(this.lastBattleResult),
- progress: structuredClone(this.lastBattleProgress),
- battleTimer: this.lastResult.battleTimer,
- };
+ if (isNotFullPack) {
+ if (
+ await popup.confirm(I18N('MINIONS_WARNING'), [
+ { msg: I18N('BTN_NO'), result: true, color: 'red' },
+ { msg: I18N('BTN_YES'), result: false, color: 'green' },
+ ])
+ ) {
+ endRaidNodes('isNotFullPack');
+ return;
+ }
}
- }
- setWinTimer(value) {
- this.winTimer = value;
- }
+ raidData.nodes = clanRaidInfo.nodes;
+ raidData.attempts = clanRaidInfo.attempts;
+ setIsCancalBattle(false);
- setMaxTimer(value) {
- this.maxTimer = value;
+ checkNodes();
}
- randTimer() {
- if (this.winTimer) {
- return this.winTimer;
+ function getAttackNode() {
+ for (let nodeId in raidData.nodes) {
+ let node = raidData.nodes[nodeId];
+ let points = 0
+ for (team of node.teams) {
+ points += team.points;
+ }
+ let now = Date.now() / 1000;
+ if (!points && now > node.timestamps.start && now < node.timestamps.end) {
+ let countTeam = node.teams.length;
+ delete raidData.nodes[nodeId];
+ return {
+ nodeId,
+ countTeam
+ };
+ }
}
- return super.randTimer();
+ return null;
}
- isEndLoop() {
- return super.isEndLoop() || this.bestResult.result?.win;
- }
+ function checkNodes() {
+ setProgress(`${I18N('REMAINING_ATTEMPTS')}: ${raidData.attempts}`);
+ let nodeInfo = getAttackNode();
+ if (nodeInfo && raidData.attempts) {
+ startNodeBattles(nodeInfo);
+ return;
+ }
- showResult() {
- console.log(
- this.count,
- this.avgTime.toFixed(2),
- (this.endTime - Date.now()) / 1000,
- this.lastResult.battleTime,
- this.lastTimer,
- this.bestResult.value
- );
- const endTime = ((this.endTime - Date.now()) / 1000).toFixed(2);
- const avgTime = this.avgTime.toFixed(2);
- const msg = `${I18N('LETS_FIX')} ${this.count}/${this.maxCount} ${endTime}s ${avgTime}ms`;
- setProgress(msg, false, this.stopFix.bind(this));
+ endRaidNodes('EndRaidNodes');
}
- }
-
- this.HWHClasses.WinFixBattle = WinFixBattle;
- class BestOrWinFixBattle extends WinFixBattle {
- isNoMakeWin = false;
-
- getState(result) {
- let beforeSumFactor = 0;
- const beforeHeroes = result.battleData.defenders[0];
- for (let heroId in beforeHeroes) {
- const hero = beforeHeroes[heroId];
- const state = hero.state;
- let factor = 1;
- if (state) {
- const hp = state.hp / (hero?.hp || 1);
- const energy = state.energy / 1e3;
- factor = hp + energy / 20;
+ function startNodeBattles(nodeInfo) {
+ let {nodeId, countTeam} = nodeInfo;
+ let teams = raidData.teams.slice(0, countTeam);
+ let heroes = raidData.teams.map(e => e.heroes).flat();
+ let favor = {...raidData.favor};
+ for (let heroId in favor) {
+ if (!heroes.includes(+heroId)) {
+ delete favor[heroId];
}
- beforeSumFactor += factor;
}
- let afterSumFactor = 0;
- const afterHeroes = result.progress[0].defenders.heroes;
- for (let heroId in afterHeroes) {
- const hero = afterHeroes[heroId];
- const hp = hero.hp / (beforeHeroes[heroId]?.hp || 1);
- const energy = hero.energy / 1e3;
- const factor = hp + energy / 20;
- afterSumFactor += factor;
- }
- return 100 - Math.floor((afterSumFactor / beforeSumFactor) * 1e4) / 100;
- }
+ let calls = [{
+ name: "clanRaid_startNodeBattles",
+ args: {
+ nodeId,
+ teams,
+ favor
+ },
+ ident: "body"
+ }];
- setNoMakeWin(value) {
- this.isNoMakeWin = value;
+ send({calls}, resultNodeBattles);
}
- checkResult() {
- const state = this.getState(this.lastResult);
- console.log(state);
-
- if (state > this.bestResult.value) {
- if (!(this.isNoMakeWin && this.lastBattleResult.win)) {
- this.bestResult = {
- count: this.count,
- timer: this.lastTimer,
- value: state,
- result: structuredClone(this.lastBattleResult),
- progress: structuredClone(this.lastBattleProgress),
- battleTimer: this.lastResult.battleTimer,
- };
- }
+ function resultNodeBattles(e) {
+ if (e['error']) {
+ endRaidNodes('nodeBattlesError', e['error']);
+ return;
}
- }
- }
- this.HWHClasses.BestOrWinFixBattle = BestOrWinFixBattle;
+ console.log(e);
+ let battles = e.results[0].result.response.battles;
+ let promises = [];
+ let battleIndex = 0;
+ for (let battle of battles) {
+ battle.battleIndex = battleIndex++;
+ promises.push(calcBattleResult(battle));
+ }
- class BossFixBattle extends FixBattle {
- showResult() {
- super.showResult();
- //setTimeout(() => {
- const best = this.bestResult;
- const maxDmg = best.value.toLocaleString();
- const avgTime = this.avgTime.toLocaleString();
- const msg = `${I18N('LETS_FIX')} ${this.count}/${this.maxCount} ${maxDmg} ${avgTime}ms`;
- setProgress(msg, false, this.stopFix.bind(this));
- //}, 0);
+ Promise.all(promises)
+ .then(results => {
+ const endResults = {};
+ let isAllWin = true;
+ for (let r of results) {
+ isAllWin &&= r.result.win;
+ }
+ if (!isAllWin) {
+ cancelEndNodeBattle(results[0]);
+ return;
+ }
+ raidData.countExecuteBattles = results.length;
+ let timeout = 500;
+ for (let r of results) {
+ setTimeout(endNodeBattle, timeout, r);
+ timeout += 500;
+ }
+ });
}
- }
-
- this.HWHClasses.BossFixBattle = BossFixBattle;
-
- class DungeonFixBattle extends FixBattle {
- init() {
- super.init();
- this.isTimeout = false;
- this.bestResult = {
- count: 0,
- timer: 0,
- value: {
- hp: -Infinity,
- energy: -Infinity,
- },
- result: null,
- progress: null,
- };
+ /**
+ * Returns the battle calculation promise
+ *
+ * Возвращает промис расчета боя
+ */
+ function calcBattleResult(battleData) {
+ return new Promise(function (resolve, reject) {
+ BattleCalc(battleData, "get_clanPvp", resolve);
+ });
+ }
+ /**
+ * Cancels the fight
+ *
+ * Отменяет бой
+ */
+ function cancelEndNodeBattle(r) {
+ const fixBattle = function (heroes) {
+ for (const ids in heroes) {
+ hero = heroes[ids];
+ hero.energy = random(1, 999);
+ if (hero.hp > 0) {
+ hero.hp = random(1, hero.hp);
+ }
+ }
+ }
+ fixBattle(r.progress[0].attackers.heroes);
+ fixBattle(r.progress[0].defenders.heroes);
+ endNodeBattle(r);
}
+ /**
+ * Ends the fight
+ *
+ * Завершает бой
+ */
+ function endNodeBattle(r) {
+ let nodeId = r.battleData.result.nodeId;
+ let battleIndex = r.battleData.battleIndex;
+ let calls = [{
+ name: "clanRaid_endNodeBattle",
+ args: {
+ nodeId,
+ battleIndex,
+ result: r.result,
+ progress: r.progress
+ },
+ ident: "body"
+ }]
- setState() {
- const result = this.lastResult;
- const isAllDead = Object.values(result.progress[0].attackers.heroes).every((item) => item.isDead);
- if (isAllDead) {
- this.lastState = {
- hp: -Infinity,
- energy: -Infinity,
- };
+ SendRequest(JSON.stringify({calls}), battleResult);
+ }
+ /**
+ * Processing the results of the battle
+ *
+ * Обработка результатов боя
+ */
+ function battleResult(e) {
+ if (e['error']) {
+ endRaidNodes('missionEndError', e['error']);
return;
}
- let beforeHP = 0;
- let beforeEnergy = 0;
- const beforeTitans = result.battleData.attackers;
- for (let titanId in beforeTitans) {
- const titan = beforeTitans[titanId];
- const state = titan.state;
- if (state) {
- beforeHP += state.hp / titan.hp;
- beforeEnergy += state.energy / 1e3;
+ r = e.results[0].result.response;
+ if (r['error']) {
+ if (r.reason == "invalidBattle") {
+ raidData.cancelBattle++;
+ checkNodes();
+ } else {
+ endRaidNodes('missionEndError', e['error']);
}
+ return;
}
- let afterHP = 0;
- let afterEnergy = 0;
- const afterTitans = result.progress[0].attackers.heroes;
- for (let titanId in afterTitans) {
- const titan = afterTitans[titanId];
- afterHP += titan.hp / beforeTitans[titanId].hp;
- afterEnergy += titan.energy / 1e3;
- }
-
- this.lastState = {
- hp: afterHP - beforeHP,
- energy: afterEnergy - beforeEnergy,
- };
- }
-
- checkResult() {
- this.setState();
- if (
- this.lastState.hp > this.bestResult.value.hp ||
- (this.lastState.hp === this.bestResult.value.hp && this.lastState.energy > this.bestResult.value.energy)
- ) {
- this.bestResult = {
- count: this.count,
- timer: this.lastTimer,
- value: this.lastState,
- result: this.lastResult.result,
- progress: this.lastResult.progress,
- };
+ if (!(--raidData.countExecuteBattles)) {
+ raidData.attempts--;
+ checkNodes();
}
}
-
- showResult() {
- if (this.isShowResult) {
- console.log(this.count, this.lastTimer.toFixed(2), JSON.stringify(this.lastState), JSON.stringify(this.bestResult.value));
- }
+ /**
+ * Completing a task
+ *
+ * Завершение задачи
+ */
+ function endRaidNodes(reason, info) {
+ setIsCancalBattle(true);
+ let textCancel = raidData.cancelBattle ? ` ${I18N('BATTLES_CANCELED')}: ${raidData.cancelBattle}` : '';
+ setProgress(`${I18N('MINION_RAID')} ${I18N('COMPLETED')}! ${textCancel}`, true);
+ console.log(reason, info);
+ resolve();
}
}
- this.HWHClasses.DungeonFixBattle = DungeonFixBattle;
-
- const masterWsMixin = {
- wsStart() {
- const socket = new WebSocket(this.url);
+ this.HWHClasses.executeRaidNodes = executeRaidNodes;
- socket.onopen = () => {
- console.log('Connected to server');
+ /**
+ * Asgard Boss Attack Replay
+ *
+ * Повтор атаки босса Асгарда
+ */
+ function testBossBattle() {
+ const { executeBossBattle } = HWHClasses;
+ return new Promise((resolve, reject) => {
+ const bossBattle = new executeBossBattle(resolve, reject);
+ bossBattle.start(lastBossBattle);
+ });
+ }
- // Пример создания новой задачи
- const newTask = {
- type: 'newTask',
- battle: this.battle,
- endTime: this.endTime - 1e4,
- maxCount: this.maxCount,
- };
- socket.send(JSON.stringify(newTask));
- };
+ /**
+ * Asgard Boss Attack Replay
+ *
+ * Повтор атаки босса Асгарда
+ */
+ function executeBossBattle(resolve, reject) {
- socket.onmessage = this.onmessage.bind(this);
+ this.start = function (battleInfo) {
+ preCalcBattle(battleInfo);
+ }
- socket.onclose = () => {
- console.log('Disconnected from server');
- };
+ function getBattleInfo(battle) {
+ return new Promise(function (resolve) {
+ battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3);
+ BattleCalc(battle, getBattleType(battle.type), e => {
+ let extra = e.progress[0].defenders.heroes[1].extra;
+ resolve(extra.damageTaken + extra.damageTakenNextLevel);
+ });
+ });
+ }
- this.ws = socket;
- },
+ function preCalcBattle(battle) {
+ let actions = [];
+ const countTestBattle = getInput('countTestBattle');
+ for (let i = 0; i < countTestBattle; i++) {
+ actions.push(getBattleInfo(battle, true));
+ }
+ Promise.all(actions)
+ .then(resultPreCalcBattle);
+ }
- onmessage(event) {
- const data = JSON.parse(event.data);
- switch (data.type) {
- case 'newTask': {
- console.log('newTask:', data);
- this.id = data.id;
- this.countExecutor = data.count;
- break;
- }
- case 'getSolTask': {
- console.log('getSolTask:', data);
- this.endFix(data.solutions);
- break;
+ async function resultPreCalcBattle(damages) {
+ let maxDamage = 0;
+ let minDamage = 1e10;
+ let avgDamage = 0;
+ for (let damage of damages) {
+ avgDamage += damage
+ if (damage > maxDamage) {
+ maxDamage = damage;
}
- case 'resolveTask': {
- console.log('resolveTask:', data);
- if (data.id === this.id && data.solutions.length === this.countExecutor) {
- this.worker.terminate();
- this.endFix(data.solutions);
- }
- break;
+ if (damage < minDamage) {
+ minDamage = damage;
}
- default:
- console.log('Unknown message type:', data.type);
}
- },
+ avgDamage /= damages.length;
+ console.log(damages.map(e => e.toLocaleString()).join('\n'), avgDamage, maxDamage);
- getTask() {
- this.ws.send(
- JSON.stringify({
- type: 'getSolTask',
- id: this.id,
- })
+ await popup.confirm(
+ `${I18N('ROUND_STAT')} ${damages.length} ${I18N('BATTLE')}:` +
+ ` ${I18N('MINIMUM')}: ` +
+ minDamage.toLocaleString() +
+ ` ${I18N('MAXIMUM')}: ` +
+ maxDamage.toLocaleString() +
+ ` ${I18N('AVERAGE')}: ` +
+ avgDamage.toLocaleString(),
+ [{ msg: I18N('BTN_OK'), result: 0, color: 'green' }]
);
- },
- };
+ endBossBattle(I18N('BTN_CANCEL'));
+ }
- /*
- mFix = new action.masterFixBattle(battle)
- await mFix.start(Date.now() + 6e4, 1);
- */
- class masterFixBattle extends FixBattle {
- constructor(battle, url = 'wss://localho.st:3000') {
- super(battle, true);
- this.url = url;
+ /**
+ * Completing a task
+ *
+ * Завершение задачи
+ */
+ function endBossBattle(reason, info) {
+ console.log(reason, info);
+ resolve();
}
+ }
- async start(endTime, maxCount) {
- this.endTime = endTime;
- this.maxCount = maxCount;
- this.init();
- this.wsStart();
- return await new Promise((resolve) => {
- this.resolve = resolve;
- const timeout = this.endTime - Date.now();
- this.timeout(this.getTask.bind(this), timeout);
- });
+ this.HWHClasses.executeBossBattle = executeBossBattle;
+
+ class FixBattle {
+ minTimer = 1.3;
+ maxTimer = 15.3;
+
+ constructor(battle, isTimeout = true) {
+ this.battle = structuredClone(battle);
+ this.isTimeout = isTimeout;
+ this.isGetTimer = true;
}
- async endFix(solutions) {
- this.ws.close();
- let maxCount = 0;
- for (const solution of solutions) {
- maxCount += solution.maxCount;
- if (solution.value > this.bestResult.value) {
- this.bestResult = solution;
- }
+ timeout(callback, timeout) {
+ if (this.isTimeout) {
+ this.worker.postMessage(timeout);
+ this.worker.onmessage = callback;
+ } else {
+ callback();
}
- this.count = maxCount;
- super.endFix();
}
- }
- Object.assign(masterFixBattle.prototype, masterWsMixin);
+ randTimer() {
+ return Math.random() * (this.maxTimer - this.minTimer + 1) + this.minTimer;
+ }
- this.HWHClasses.masterFixBattle = masterFixBattle;
+ getTimer() {
+ if (this.count === 1) {
+ this.initTimers();
+ }
- class masterWinFixBattle extends WinFixBattle {
- constructor(battle, url = 'wss://localho.st:3000') {
- super(battle, true);
- this.url = url;
+ return this.battleLogTimers[this.count];
}
- async start(endTime, maxCount) {
+ setAvgTime(startTime) {
+ this.fixTime += Date.now() - startTime;
+ this.avgTime = this.fixTime / this.count;
+ }
+
+ initTimers() {
+ const timers = [...new Set(this.lastResult.battleLogs[0].map((e) => e.time))];
+ this.battleLogTimers = timers.sort(() => Math.random() - 0.5);
+ this.maxCount = Math.min(this.maxCount, this.battleLogTimers.length);
+ console.log('maxCount', this.maxCount);
+ }
+
+ init() {
+ this.fixTime = 0;
+ this.lastTimer = 0;
+ this.index = 0;
+ this.lastBossDamage = 0;
+ this.bestResult = {
+ count: 0,
+ timer: 0,
+ value: -Infinity,
+ result: null,
+ progress: null,
+ };
+ this.lastBattleResult = {
+ win: false,
+ };
+ this.worker = new Worker(
+ URL.createObjectURL(
+ new Blob([
+ `self.onmessage = function(e) {
+ const timeout = e.data;
+ setTimeout(() => {
+ self.postMessage(1);
+ }, timeout);
+ };`,
+ ])
+ )
+ );
+ }
+
+ async start(endTime = Date.now() + 6e4, maxCount = 100) {
this.endTime = endTime;
this.maxCount = maxCount;
this.init();
- this.wsStart();
return await new Promise((resolve) => {
this.resolve = resolve;
- const timeout = this.endTime - Date.now();
- this.timeout(this.getTask.bind(this), timeout);
+ this.count = 0;
+ this.loop();
});
}
- async endFix(solutions) {
- this.ws.close();
- let maxCount = 0;
- for (const solution of solutions) {
- maxCount += solution.maxCount;
- if (solution.value > this.bestResult.value) {
- this.bestResult = solution;
- }
+ endFix() {
+ this.bestResult.maxCount = this.count;
+ this.worker.terminate();
+ console.log('endFix', this.bestResult);
+ this.resolve(this.bestResult);
+ }
+
+ async loop() {
+ const start = Date.now();
+ if (this.isEndLoop()) {
+ this.endFix();
+ return;
}
- this.count = maxCount;
- super.endFix();
+ this.count++;
+ try {
+ this.lastResult = await Calc(this.battle);
+ } catch (e) {
+ this.updateProgressTimer(this.index++);
+ this.timeout(this.loop.bind(this), 0);
+ return;
+ }
+ const { progress, result } = this.lastResult;
+ this.lastBattleResult = result;
+ this.lastBattleProgress = progress;
+ this.setAvgTime(start);
+ this.checkResult();
+ this.showResult();
+ this.updateProgressTimer();
+ this.timeout(this.loop.bind(this), 0);
}
- }
- Object.assign(masterWinFixBattle.prototype, masterWsMixin);
+ isEndLoop() {
+ return this.count >= this.maxCount || this.endTime < Date.now();
+ }
- this.HWHClasses.masterWinFixBattle = masterWinFixBattle;
+ updateProgressTimer(index = 0) {
+ this.lastTimer = this.isGetTimer ? this.getTimer() : this.randTimer();
+ this.battle.progress = [{ attackers: { input: ['auto', 0, 0, 'auto', index, this.lastTimer] } }];
+ }
- const slaveWsMixin = {
- wsStop() {
- this.ws.close();
- },
+ showResult() {
+ console.log(
+ this.count,
+ this.avgTime.toFixed(2),
+ (this.endTime - Date.now()) / 1000,
+ this.lastTimer.toFixed(2),
+ this.lastBossDamage.toLocaleString(),
+ this.bestResult.value.toLocaleString()
+ );
+ }
- wsStart() {
- const socket = new WebSocket(this.url);
+ checkResult() {
+ const { damageTaken, damageTakenNextLevel } = this.lastBattleProgress[0].defenders.heroes[1].extra;
+ this.lastBossDamage = damageTaken + damageTakenNextLevel;
+ if (this.lastBossDamage > this.bestResult.value) {
+ this.bestResult = {
+ count: this.count,
+ timer: this.lastTimer,
+ value: this.lastBossDamage,
+ result: structuredClone(this.lastBattleResult),
+ progress: structuredClone(this.lastBattleProgress),
+ };
+ }
+ }
- socket.onopen = () => {
- console.log('Connected to server');
- };
- socket.onmessage = this.onmessage.bind(this);
- socket.onclose = () => {
- console.log('Disconnected from server');
- };
+ stopFix() {
+ this.endTime = 0;
+ }
+ }
- this.ws = socket;
- },
+ this.HWHClasses.FixBattle = FixBattle;
- async onmessage(event) {
- const data = JSON.parse(event.data);
- switch (data.type) {
- case 'newTask': {
- console.log('newTask:', data.task);
- const { battle, endTime, maxCount } = data.task;
- this.battle = battle;
- const id = data.task.id;
- const solution = await this.start(endTime, maxCount);
- this.ws.send(
- JSON.stringify({
- type: 'resolveTask',
- id,
- solution,
- })
- );
- break;
- }
- default:
- console.log('Unknown message type:', data.type);
+ class WinFixBattle extends FixBattle {
+ checkResult() {
+ if (this.lastBattleResult.win) {
+ this.bestResult = {
+ count: this.count,
+ timer: this.lastTimer,
+ value: this.lastBattleResult.stars,
+ result: structuredClone(this.lastBattleResult),
+ progress: structuredClone(this.lastBattleProgress),
+ battleTimer: this.lastResult.battleTimer,
+ };
}
- },
- };
- /*
- sFix = new action.slaveFixBattle();
- sFix.wsStart()
- */
- class slaveFixBattle extends FixBattle {
- constructor(url = 'wss://localho.st:3000') {
- super(null, false);
- this.isTimeout = false;
- this.url = url;
}
- }
- Object.assign(slaveFixBattle.prototype, slaveWsMixin);
+ setWinTimer(value) {
+ this.winTimer = value;
+ }
- this.HWHClasses.slaveFixBattle = slaveFixBattle;
+ setMaxTimer(value) {
+ this.maxTimer = value;
+ }
- class slaveWinFixBattle extends WinFixBattle {
- constructor(url = 'wss://localho.st:3000') {
- super(null, false);
- this.isTimeout = false;
- this.url = url;
- }
- }
-
- Object.assign(slaveWinFixBattle.prototype, slaveWsMixin);
-
- this.HWHClasses.slaveWinFixBattle = slaveWinFixBattle;
- /**
- * Auto-repeat attack
- *
- * Автоповтор атаки
- */
- function testAutoBattle() {
- const { executeAutoBattle } = HWHClasses;
- return new Promise((resolve, reject) => {
- const bossBattle = new executeAutoBattle(resolve, reject);
- bossBattle.start(lastBattleArg, lastBattleInfo);
- });
- }
-
- /**
- * Auto-repeat attack
- *
- * Автоповтор атаки
- */
- function executeAutoBattle(resolve, reject) {
- let battleArg = {};
- let countBattle = 0;
- let countError = 0;
- let findCoeff = 0;
- let dataNotEeceived = 0;
- let stopAutoBattle = false;
-
- let isSetWinTimer = false;
- const svgJustice = ' ';
- const svgBoss = ' ';
- const svgAttempt = ' ';
-
- this.start = function (battleArgs, battleInfo) {
- battleArg = battleArgs;
- if (nameFuncStartBattle == 'invasion_bossStart') {
- startBattle();
- return;
+ randTimer() {
+ if (this.winTimer) {
+ return this.winTimer;
}
- preCalcBattle(battleInfo);
- }
- /**
- * Returns a promise for combat recalculation
- *
- * Возвращает промис для прерасчета боя
- */
- function getBattleInfo(battle) {
- return new Promise(function (resolve) {
- battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3);
- Calc(battle).then(e => {
- e.coeff = calcCoeff(e, 'defenders');
- resolve(e);
- });
- });
+ return super.randTimer();
}
- /**
- * Battle recalculation
- *
- * Прерасчет боя
- */
- function preCalcBattle(battle) {
- let actions = [];
- const countTestBattle = getInput('countTestBattle');
- for (let i = 0; i < countTestBattle; i++) {
- actions.push(getBattleInfo(battle));
- }
- Promise.all(actions)
- .then(resultPreCalcBattle);
+
+ isEndLoop() {
+ return super.isEndLoop() || this.bestResult.result?.win;
}
- /**
- * Processing the results of the battle recalculation
- *
- * Обработка результатов прерасчета боя
- */
- async function resultPreCalcBattle(results) {
- let countWin = results.reduce((s, w) => w.result.win + s, 0);
- setProgress(`${I18N('CHANCE_TO_WIN')} ${Math.floor(countWin / results.length * 100)}% (${results.length})`, false, hideProgress);
- if (countWin > 0) {
- setIsCancalBattle(false);
- startBattle();
- return;
- }
- let minCoeff = 100;
- let maxCoeff = -100;
- let avgCoeff = 0;
- results.forEach(e => {
- if (e.coeff < minCoeff) minCoeff = e.coeff;
- if (e.coeff > maxCoeff) maxCoeff = e.coeff;
- avgCoeff += e.coeff;
- });
- avgCoeff /= results.length;
+ showResult() {
+ console.log(
+ this.count,
+ this.avgTime.toFixed(2),
+ (this.endTime - Date.now()) / 1000,
+ this.lastResult.battleTime,
+ this.lastTimer,
+ this.bestResult.value
+ );
+ const endTime = ((this.endTime - Date.now()) / 1000).toFixed(2);
+ const avgTime = this.avgTime.toFixed(2);
+ const msg = `${I18N('LETS_FIX')} ${this.count}/${this.maxCount} ${endTime}s ${avgTime}ms`;
+ setProgress(msg, false, this.stopFix.bind(this));
+ }
+ }
- if (nameFuncStartBattle == 'invasion_bossStart' ||
- nameFuncStartBattle == 'bossAttack') {
- const result = await popup.confirm(
- I18N('BOSS_VICTORY_IMPOSSIBLE', { battles: results.length }), [
- { msg: I18N('BTN_CANCEL'), result: false, isCancel: true },
- { msg: I18N('BTN_DO_IT'), result: true },
- ])
- if (result) {
- setIsCancalBattle(false);
- startBattle();
- return;
- }
- setProgress(I18N('NOT_THIS_TIME'), true);
- endAutoBattle('invasion_bossStart');
- return;
- }
+ this.HWHClasses.WinFixBattle = WinFixBattle;
- const result = await popup.confirm(
- I18N('VICTORY_IMPOSSIBLE') +
- ` ${I18N('ROUND_STAT')} ${results.length} ${I18N('BATTLE')}:` +
- ` ${I18N('MINIMUM')}: ` + minCoeff.toLocaleString() +
- ` ${I18N('MAXIMUM')}: ` + maxCoeff.toLocaleString() +
- ` ${I18N('AVERAGE')}: ` + avgCoeff.toLocaleString() +
- ` ${I18N('FIND_COEFF')} ` + avgCoeff.toLocaleString(), [
- { msg: I18N('BTN_CANCEL'), result: 0, isCancel: true },
- { msg: I18N('BTN_GO'), isInput: true, default: Math.round(avgCoeff * 1000) / 1000 },
- ])
- if (result) {
- findCoeff = result;
- setIsCancalBattle(false);
- startBattle();
- return;
- }
- setProgress(I18N('NOT_THIS_TIME'), true);
- endAutoBattle(I18N('NOT_THIS_TIME'));
- }
+ class BestOrWinFixBattle extends WinFixBattle {
+ isNoMakeWin = false;
- /**
- * Calculation of the combat result coefficient
- *
- * Расчет коэфициента результата боя
- */
- function calcCoeff(result, packType) {
+ getState(result) {
let beforeSumFactor = 0;
- const beforePack = result.battleData[packType][0];
- for (let heroId in beforePack) {
- const hero = beforePack[heroId];
+ const beforeHeroes = result.battleData.defenders[0];
+ for (let heroId in beforeHeroes) {
+ const hero = beforeHeroes[heroId];
const state = hero.state;
let factor = 1;
if (state) {
- const hp = state.hp / state.maxHp;
+ const hp = state.hp / (hero?.hp || 1);
const energy = state.energy / 1e3;
factor = hp + energy / 20;
}
@@ -11637,357 +11323,849 @@
}
let afterSumFactor = 0;
- const afterPack = result.progress[0][packType].heroes;
- for (let heroId in afterPack) {
- const hero = afterPack[heroId];
- const stateHp = beforePack[heroId]?.state?.hp || beforePack[heroId]?.stats?.hp;
- const hp = hero.hp / stateHp;
+ const afterHeroes = result.progress[0].defenders.heroes;
+ for (let heroId in afterHeroes) {
+ const hero = afterHeroes[heroId];
+ const hp = hero.hp / (beforeHeroes[heroId]?.hp || 1);
const energy = hero.energy / 1e3;
const factor = hp + energy / 20;
afterSumFactor += factor;
}
- const resultCoeff = -(afterSumFactor - beforeSumFactor);
- return Math.round(resultCoeff * 1000) / 1000;
+ return 100 - Math.floor((afterSumFactor / beforeSumFactor) * 1e4) / 100;
}
- /**
- * Start battle
- *
- * Начало боя
- */
- function startBattle() {
- countBattle++;
- const countMaxBattle = getInput('countAutoBattle');
- // setProgress(countBattle + '/' + countMaxBattle);
- if (countBattle > countMaxBattle) {
- setProgress(`${I18N('RETRY_LIMIT_EXCEEDED')}: ${countMaxBattle}`, true);
- endAutoBattle(`${I18N('RETRY_LIMIT_EXCEEDED')}: ${countMaxBattle}`)
- return;
- }
- if (stopAutoBattle) {
- setProgress(I18N('STOPPED'), true);
- endAutoBattle('STOPPED');
- return;
- }
- send({calls: [{
- name: nameFuncStartBattle,
- args: battleArg,
- ident: "body"
- }]}, calcResultBattle);
+
+ setNoMakeWin(value) {
+ this.isNoMakeWin = value;
}
- /**
- * Battle calculation
- *
- * Расчет боя
- */
- async function calcResultBattle(e) {
- if (!e) {
- console.log('данные не были получены');
- if (dataNotEeceived < 10) {
- dataNotEeceived++;
- startBattle();
- return;
- }
- endAutoBattle('Error', 'данные не были получены ' + dataNotEeceived + ' раз');
- return;
- }
- if ('error' in e) {
- if (e.error.description === 'too many tries') {
- invasionTimer += 100;
- countBattle--;
- countError++;
- console.log(`Errors: ${countError}`, e.error);
- startBattle();
- return;
- }
- const result = await popup.confirm(I18N('ERROR_DURING_THE_BATTLE') + ' ' + e.error.description, [
- { msg: I18N('BTN_OK'), result: false },
- { msg: I18N('RELOAD_GAME'), result: true },
- ]);
- endAutoBattle('Error', e.error);
- if (result) {
- location.reload();
+
+ checkResult() {
+ const state = this.getState(this.lastResult);
+ console.log(state);
+
+ if (state > this.bestResult.value) {
+ if (!(this.isNoMakeWin && this.lastBattleResult.win)) {
+ this.bestResult = {
+ count: this.count,
+ timer: this.lastTimer,
+ value: state,
+ result: structuredClone(this.lastBattleResult),
+ progress: structuredClone(this.lastBattleProgress),
+ battleTimer: this.lastResult.battleTimer,
+ };
}
- return;
- }
- let battle = e.results[0].result.response.battle
- if (nameFuncStartBattle == 'towerStartBattle' ||
- nameFuncStartBattle == 'bossAttack' ||
- nameFuncStartBattle == 'invasion_bossStart') {
- battle = e.results[0].result.response;
}
- lastBattleInfo = battle;
- BattleCalc(battle, getBattleType(battle.type), resultBattle);
}
- /**
- * Processing the results of the battle
- *
- * Обработка результатов боя
- */
- async function resultBattle(e) {
- const isWin = e.result.win;
- if (isWin) {
- endBattle(e, false);
- return;
- } else if (isChecked('tryFixIt_v2')) {
- const { WinFixBattle } = HWHClasses;
- const cloneBattle = structuredClone(e.battleData);
- const bFix = new WinFixBattle(cloneBattle);
- let attempts = Infinity;
- if (nameFuncStartBattle == 'invasion_bossStart' && !isSetWinTimer) {
- const { invasionInfo, invasionDataPacks } = HWHData;
+ }
-
- let timer = '0';
- const pack = invasionDataPacks[invasionInfo.bossLvl];
- if (pack && pack.timer && (pack.buff == invasionInfo.buff)) {
- timer = pack.timer;
- }
+ this.HWHClasses.BestOrWinFixBattle = BestOrWinFixBattle;
- let winTimer = await popup.confirm(`Secret number:`, [
- { result: false, isClose: true },
- { msg: 'Go', isInput: true, default: timer },
- ]);
- winTimer = Number.parseFloat(winTimer);
- if (winTimer) {
- attempts = 5;
- bFix.setWinTimer(winTimer);
- }
- isSetWinTimer = true;
- }
- let endTime = Date.now() + 6e4;
- if (nameFuncStartBattle == 'invasion_bossStart') {
- endTime = Date.now() + 6e4 * 4;
- bFix.isGetTimer = false;
- bFix.setMaxTimer(120.3);
- }
- const result = await bFix.start(endTime, attempts);
- console.log(result);
- if (result.result?.win) {
- endBattle(result, false);
- return;
- }
- }
- const countMaxBattle = getInput('countAutoBattle');
- if (findCoeff) {
- const coeff = calcCoeff(e, 'defenders');
- setProgress(`${countBattle}/${countMaxBattle}, ${coeff}`);
- if (coeff > findCoeff) {
- endBattle(e, false);
- return;
- }
- } else {
- if (nameFuncStartBattle == 'invasion_bossStart') {
- const bossLvl = lastBattleInfo.typeId >= 130 ? lastBattleInfo.typeId : '';
- const justice = lastBattleInfo?.effects?.attackers?.percentInOutDamageModAndEnergyIncrease_any_99_100_300_99_1000_30 || 0;
- setProgress(`${svgBoss} ${bossLvl} ${svgJustice} ${justice} ${svgAttempt} ${countBattle}/${countMaxBattle}`, false, () => {
- stopAutoBattle = true;
- });
- await new Promise((resolve) => setTimeout(resolve, 5000));
- } else {
- setProgress(`${countBattle}/${countMaxBattle}`);
- }
- }
- if (nameFuncStartBattle == 'towerStartBattle' ||
- nameFuncStartBattle == 'bossAttack' ||
- nameFuncStartBattle == 'invasion_bossStart') {
- startBattle();
+ class BossFixBattle extends FixBattle {
+ showResult() {
+ super.showResult();
+ //setTimeout(() => {
+ const best = this.bestResult;
+ const maxDmg = best.value.toLocaleString();
+ const avgTime = this.avgTime.toLocaleString();
+ const msg = `${I18N('LETS_FIX')} ${this.count}/${this.maxCount} ${maxDmg} ${avgTime}ms`;
+ setProgress(msg, false, this.stopFix.bind(this));
+ //}, 0);
+ }
+ }
+
+ this.HWHClasses.BossFixBattle = BossFixBattle;
+
+ class DungeonFixBattle extends FixBattle {
+ init() {
+ super.init();
+ this.isTimeout = false;
+ this.bestResult = {
+ count: 0,
+ timer: 0,
+ value: {
+ hp: -Infinity,
+ energy: -Infinity,
+ },
+ result: null,
+ progress: null,
+ };
+ }
+
+ setState() {
+ const result = this.lastResult;
+ const isAllDead = Object.values(result.progress[0].attackers.heroes).every((item) => item.isDead);
+ if (isAllDead) {
+ this.lastState = {
+ hp: -Infinity,
+ energy: -Infinity,
+ };
return;
}
- cancelEndBattle(e);
- }
- /**
- * Cancel fight
- *
- * Отмена боя
- */
- function cancelEndBattle(r) {
- const fixBattle = function (heroes) {
- for (const ids in heroes) {
- hero = heroes[ids];
- hero.energy = random(1, 999);
- if (hero.hp > 0) {
- hero.hp = random(1, hero.hp);
- }
+ let beforeHP = 0;
+ let beforeEnergy = 0;
+ const beforeTitans = result.battleData.attackers;
+ for (let titanId in beforeTitans) {
+ const titan = beforeTitans[titanId];
+ const state = titan.state;
+ if (state) {
+ beforeHP += state.hp / titan.hp;
+ beforeEnergy += state.energy / 1e3;
}
}
- fixBattle(r.progress[0].attackers.heroes);
- fixBattle(r.progress[0].defenders.heroes);
- endBattle(r, true);
- }
- /**
- * End of the fight
- *
- * Завершение боя */
- function endBattle(battleResult, isCancal) {
- let calls = [{
- name: nameFuncEndBattle,
- args: {
- result: battleResult.result,
- progress: battleResult.progress
- },
- ident: "body"
- }];
- if (nameFuncStartBattle == 'invasion_bossStart') {
- calls[0].args.id = lastBattleArg.id;
+ let afterHP = 0;
+ let afterEnergy = 0;
+ const afterTitans = result.progress[0].attackers.heroes;
+ for (let titanId in afterTitans) {
+ const titan = afterTitans[titanId];
+ afterHP += titan.hp / beforeTitans[titanId].hp;
+ afterEnergy += titan.energy / 1e3;
}
- send(JSON.stringify({
- calls
- }), async e => {
- console.log(e);
- if (isCancal) {
- startBattle();
- return;
- }
-
- setProgress(`${I18N('SUCCESS')}!`, 5000)
- if (nameFuncStartBattle == 'invasion_bossStart' ||
- nameFuncStartBattle == 'bossAttack') {
- const countMaxBattle = getInput('countAutoBattle');
- const bossLvl = lastBattleInfo.typeId >= 130 ? lastBattleInfo.typeId : '';
- const justice = lastBattleInfo?.effects?.attackers?.percentInOutDamageModAndEnergyIncrease_any_99_100_300_99_1000_30 || 0;
- let winTimer = '';
- if (nameFuncStartBattle == 'invasion_bossStart') {
- const timer = battleResult.progress[0].attackers.input[5];
- winTimer += ' Secret number: ' + timer;
- winTimer +=
- ' ' +
- battleArg.heroes
- .map((id) => `${cheats.translate('LIB_HERO_NAME_' + id)}(${cheats.translate('LIB_HERO_NAME_' + battleArg.favor[id])})`)
- .join(' ') +
- ' ' +
- (battleArg.pet ? cheats.translate('LIB_HERO_NAME_' + battleArg.pet) : '');
- console.log(bossLvl, {
- buff: justice,
- pet: battleArg.pet,
- heroes: battleArg.heroes,
- favor: battleArg.favor,
- timer,
- });
- }
- const result = await popup.confirm(
- I18N('BOSS_HAS_BEEN_DEF_TEXT', {
- bossLvl: `${svgBoss} ${bossLvl} ${svgJustice} ${justice}`,
- countBattle: svgAttempt + ' ' + countBattle,
- countMaxBattle,
- winTimer,
- }),
- [
- { msg: I18N('BTN_OK'), result: 0 },
- { msg: I18N('MAKE_A_SYNC'), result: 1 },
- { msg: I18N('RELOAD_GAME'), result: 2 },
- ]
- );
- if (result) {
- if (result == 1) {
- cheats.refreshGame();
- }
- if (result == 2) {
- location.reload();
- }
- }
+ this.lastState = {
+ hp: afterHP - beforeHP,
+ energy: afterEnergy - beforeEnergy,
+ };
+ }
- }
- endAutoBattle(`${I18N('SUCCESS')}!`)
- });
+ checkResult() {
+ this.setState();
+ if (
+ this.lastState.hp > this.bestResult.value.hp ||
+ (this.lastState.hp === this.bestResult.value.hp && this.lastState.energy > this.bestResult.value.energy)
+ ) {
+ this.bestResult = {
+ count: this.count,
+ timer: this.lastTimer,
+ value: this.lastState,
+ result: this.lastResult.result,
+ progress: this.lastResult.progress,
+ };
+ }
}
- /**
- * Completing a task
- *
- * Завершение задачи
- */
- function endAutoBattle(reason, info) {
- setIsCancalBattle(true);
- console.log(reason, info);
- resolve();
+
+ showResult() {
+ if (this.isShowResult) {
+ console.log(this.count, this.lastTimer.toFixed(2), JSON.stringify(this.lastState), JSON.stringify(this.bestResult.value));
+ }
}
}
- this.HWHClasses.executeAutoBattle = executeAutoBattle;
-
- function testDailyQuests() {
- const { dailyQuests } = HWHClasses;
- return new Promise(async (resolve, reject) => {
- const quests = new dailyQuests(resolve, reject);
- await quests.autoInit(true);
- quests.start();
- });
- }
+ this.HWHClasses.DungeonFixBattle = DungeonFixBattle;
- /**
- * Automatic completion of daily quests
+ const masterWsMixin = {
+ wsStart() {
+ const socket = new WebSocket(this.url);
+
+ socket.onopen = () => {
+ console.log('Connected to server');
+
+ // Пример создания новой задачи
+ const newTask = {
+ type: 'newTask',
+ battle: this.battle,
+ endTime: this.endTime - 1e4,
+ maxCount: this.maxCount,
+ };
+ socket.send(newTask);
+ };
+
+ socket.onmessage = this.onmessage.bind(this);
+
+ socket.onclose = () => {
+ console.log('Disconnected from server');
+ };
+
+ this.ws = socket;
+ },
+
+ onmessage(event) {
+ const data = JSON.parse(event.data);
+ switch (data.type) {
+ case 'newTask': {
+ console.log('newTask:', data);
+ this.id = data.id;
+ this.countExecutor = data.count;
+ break;
+ }
+ case 'getSolTask': {
+ console.log('getSolTask:', data);
+ this.endFix(data.solutions);
+ break;
+ }
+ case 'resolveTask': {
+ console.log('resolveTask:', data);
+ if (data.id === this.id && data.solutions.length === this.countExecutor) {
+ this.worker.terminate();
+ this.endFix(data.solutions);
+ }
+ break;
+ }
+ default:
+ console.log('Unknown message type:', data.type);
+ }
+ },
+
+ getTask() {
+ this.ws.send(
+ JSON.stringify({
+ type: 'getSolTask',
+ id: this.id,
+ })
+ );
+ },
+ };
+
+ /*
+ mFix = new action.masterFixBattle(battle)
+ await mFix.start(Date.now() + 6e4, 1);
+ */
+ class masterFixBattle extends FixBattle {
+ constructor(battle, url = 'wss://localho.st:3000') {
+ super(battle, true);
+ this.url = url;
+ }
+
+ async start(endTime, maxCount) {
+ this.endTime = endTime;
+ this.maxCount = maxCount;
+ this.init();
+ this.wsStart();
+ return await new Promise((resolve) => {
+ this.resolve = resolve;
+ const timeout = this.endTime - Date.now();
+ this.timeout(this.getTask.bind(this), timeout);
+ });
+ }
+
+ async endFix(solutions) {
+ this.ws.close();
+ let maxCount = 0;
+ for (const solution of solutions) {
+ maxCount += solution.maxCount;
+ if (solution.value > this.bestResult.value) {
+ this.bestResult = solution;
+ }
+ }
+ this.count = maxCount;
+ super.endFix();
+ }
+ }
+
+ Object.assign(masterFixBattle.prototype, masterWsMixin);
+
+ this.HWHClasses.masterFixBattle = masterFixBattle;
+
+ class masterWinFixBattle extends WinFixBattle {
+ constructor(battle, url = 'wss://localho.st:3000') {
+ super(battle, true);
+ this.url = url;
+ }
+
+ async start(endTime, maxCount) {
+ this.endTime = endTime;
+ this.maxCount = maxCount;
+ this.init();
+ this.wsStart();
+ return await new Promise((resolve) => {
+ this.resolve = resolve;
+ const timeout = this.endTime - Date.now();
+ this.timeout(this.getTask.bind(this), timeout);
+ });
+ }
+
+ async endFix(solutions) {
+ this.ws.close();
+ let maxCount = 0;
+ for (const solution of solutions) {
+ maxCount += solution.maxCount;
+ if (solution.value > this.bestResult.value) {
+ this.bestResult = solution;
+ }
+ }
+ this.count = maxCount;
+ super.endFix();
+ }
+ }
+
+ Object.assign(masterWinFixBattle.prototype, masterWsMixin);
+
+ this.HWHClasses.masterWinFixBattle = masterWinFixBattle;
+
+ const slaveWsMixin = {
+ wsStop() {
+ this.ws.close();
+ },
+
+ wsStart() {
+ const socket = new WebSocket(this.url);
+
+ socket.onopen = () => {
+ console.log('Connected to server');
+ };
+ socket.onmessage = this.onmessage.bind(this);
+ socket.onclose = () => {
+ console.log('Disconnected from server');
+ };
+
+ this.ws = socket;
+ },
+
+ async onmessage(event) {
+ const data = JSON.parse(event.data);
+ switch (data.type) {
+ case 'newTask': {
+ console.log('newTask:', data.task);
+ const { battle, endTime, maxCount } = data.task;
+ this.battle = battle;
+ const id = data.task.id;
+ const solution = await this.start(endTime, maxCount);
+ this.ws.send(
+ JSON.stringify({
+ type: 'resolveTask',
+ id,
+ solution,
+ })
+ );
+ break;
+ }
+ default:
+ console.log('Unknown message type:', data.type);
+ }
+ },
+ };
+ /*
+ sFix = new action.slaveFixBattle();
+ sFix.wsStart()
+ */
+ class slaveFixBattle extends FixBattle {
+ constructor(url = 'wss://localho.st:3000') {
+ super(null, false);
+ this.isTimeout = false;
+ this.url = url;
+ }
+ }
+
+ Object.assign(slaveFixBattle.prototype, slaveWsMixin);
+
+ this.HWHClasses.slaveFixBattle = slaveFixBattle;
+
+ class slaveWinFixBattle extends WinFixBattle {
+ constructor(url = 'wss://localho.st:3000') {
+ super(null, false);
+ this.isTimeout = false;
+ this.url = url;
+ }
+ }
+
+ Object.assign(slaveWinFixBattle.prototype, slaveWsMixin);
+
+ this.HWHClasses.slaveWinFixBattle = slaveWinFixBattle;
+ /**
+ * Auto-repeat attack
*
- * Автоматическое выполнение ежедневных квестов
+ * Автоповтор атаки
*/
- class dailyQuests {
+ function testAutoBattle() {
+ const { executeAutoBattle } = HWHClasses;
+ return new Promise((resolve, reject) => {
+ const bossBattle = new executeAutoBattle(resolve, reject);
+ bossBattle.start(lastBattleArg, lastBattleInfo);
+ });
+ }
+
+ /**
+ * Auto-repeat attack
+ *
+ * Автоповтор атаки
+ */
+ function executeAutoBattle(resolve, reject) {
+ let battleArg = {};
+ let countBattle = 0;
+ let countError = 0;
+ let findCoeff = 0;
+ let dataNotEeceived = 0;
+ let stopAutoBattle = false;
+
+ let isSetWinTimer = false;
+ const svgJustice = ' ';
+ const svgBoss = ' ';
+ const svgAttempt = ' ';
+
+ this.start = function (battleArgs, battleInfo) {
+ battleArg = battleArgs;
+ if (nameFuncStartBattle == 'invasion_bossStart') {
+ startBattle();
+ return;
+ }
+ preCalcBattle(battleInfo);
+ }
/**
- * Send(' {"calls":[{"name":"userGetInfo","args":{},"ident":"body"}]}').then(e => console.log(e))
- * Send(' {"calls":[{"name":"heroGetAll","args":{},"ident":"body"}]}').then(e => console.log(e))
- * Send(' {"calls":[{"name":"titanGetAll","args":{},"ident":"body"}]}').then(e => console.log(e))
- * Send(' {"calls":[{"name":"inventoryGet","args":{},"ident":"body"}]}').then(e => console.log(e))
- * Send(' {"calls":[{"name":"questGetAll","args":{},"ident":"body"}]}').then(e => console.log(e))
- * Send(' {"calls":[{"name":"bossGetAll","args":{},"ident":"body"}]}').then(e => console.log(e))
+ * Returns a promise for combat recalculation
+ *
+ * Возвращает промис для прерасчета боя
*/
- callsList = ['userGetInfo', 'heroGetAll', 'titanGetAll', 'inventoryGet', 'questGetAll', 'bossGetAll', 'missionGetAll'];
-
- dataQuests = {
- 10001: {
- description: 'Улучши умения героев 3 раза', // ++++++++++++++++
- doItCall: () => {
- const upgradeSkills = this.getUpgradeSkills();
- return upgradeSkills.map(({ heroId, skill }, index) => ({
- name: 'heroUpgradeSkill',
- args: { heroId, skill },
- ident: `heroUpgradeSkill_${index}`,
- }));
- },
- isWeCanDo: () => {
- const upgradeSkills = this.getUpgradeSkills();
- let sumGold = 0;
- for (const skill of upgradeSkills) {
- sumGold += this.skillCost(skill.value);
- if (!skill.heroId) {
- return false;
- }
+ function getBattleInfo(battle) {
+ return new Promise(function (resolve) {
+ battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3);
+ Calc(battle).then(e => {
+ e.coeff = calcCoeff(e, 'defenders');
+ resolve(e);
+ });
+ });
+ }
+ /**
+ * Battle recalculation
+ *
+ * Прерасчет боя
+ */
+ function preCalcBattle(battle) {
+ let actions = [];
+ const countTestBattle = getInput('countTestBattle');
+ for (let i = 0; i < countTestBattle; i++) {
+ actions.push(getBattleInfo(battle));
+ }
+ Promise.all(actions)
+ .then(resultPreCalcBattle);
+ }
+ /**
+ * Processing the results of the battle recalculation
+ *
+ * Обработка результатов прерасчета боя
+ */
+ async function resultPreCalcBattle(results) {
+ let countWin = results.reduce((s, w) => w.result.win + s, 0);
+ setProgress(`${I18N('CHANCE_TO_WIN')} ${Math.floor(countWin / results.length * 100)}% (${results.length})`, false, hideProgress);
+ if (countWin > 0) {
+ setIsCancalBattle(false);
+ startBattle();
+ return;
+ }
+
+ let minCoeff = 100;
+ let maxCoeff = -100;
+ let avgCoeff = 0;
+ results.forEach(e => {
+ if (e.coeff < minCoeff) minCoeff = e.coeff;
+ if (e.coeff > maxCoeff) maxCoeff = e.coeff;
+ avgCoeff += e.coeff;
+ });
+ avgCoeff /= results.length;
+
+ if (nameFuncStartBattle == 'invasion_bossStart' ||
+ nameFuncStartBattle == 'bossAttack') {
+ const result = await popup.confirm(I18N('BOSS_VICTORY_IMPOSSIBLE', { battles: results.length }), [
+ { msg: I18N('BTN_CANCEL'), result: false, isCancel: true, color: 'red' },
+ { msg: I18N('BTN_DO_IT'), result: true, color: 'green' },
+ ]);
+ if (result) {
+ setIsCancalBattle(false);
+ startBattle();
+ return;
+ }
+ setProgress(I18N('NOT_THIS_TIME'), true);
+ endAutoBattle('invasion_bossStart');
+ return;
+ }
+
+ const result = await popup.confirm(
+ I18N('VICTORY_IMPOSSIBLE') +
+ ` ${I18N('ROUND_STAT')} ${results.length} ${I18N('BATTLE')}:` +
+ ` ${I18N('MINIMUM')}: ` +
+ minCoeff.toLocaleString() +
+ ` ${I18N('MAXIMUM')}: ` +
+ maxCoeff.toLocaleString() +
+ ` ${I18N('AVERAGE')}: ` +
+ avgCoeff.toLocaleString() +
+ ` ${I18N('FIND_COEFF')} ` +
+ avgCoeff.toLocaleString(),
+ [
+ { msg: I18N('BTN_CANCEL'), result: 0, isCancel: true, color: 'red' },
+ { msg: I18N('BTN_GO'), isInput: true, default: Math.round(avgCoeff * 1000) / 1000, color: 'green' },
+ ]
+ );
+ if (result) {
+ findCoeff = result;
+ setIsCancalBattle(false);
+ startBattle();
+ return;
+ }
+ setProgress(I18N('NOT_THIS_TIME'), true);
+ endAutoBattle(I18N('NOT_THIS_TIME'));
+ }
+
+ /**
+ * Calculation of the combat result coefficient
+ *
+ * Расчет коэфициента результата боя
+ */
+ function calcCoeff(result, packType) {
+ let beforeSumFactor = 0;
+ const beforePack = result.battleData[packType][0];
+ for (let heroId in beforePack) {
+ const hero = beforePack[heroId];
+ const state = hero.state;
+ let factor = 1;
+ if (state) {
+ const hp = state.hp / state.maxHp;
+ const energy = state.energy / 1e3;
+ factor = hp + energy / 20;
+ }
+ beforeSumFactor += factor;
+ }
+
+ let afterSumFactor = 0;
+ const afterPack = result.progress[0][packType].heroes;
+ for (let heroId in afterPack) {
+ const hero = afterPack[heroId];
+ const stateHp = beforePack[heroId]?.state?.hp || beforePack[heroId]?.stats?.hp;
+ const hp = hero.hp / stateHp;
+ const energy = hero.energy / 1e3;
+ const factor = hp + energy / 20;
+ afterSumFactor += factor;
+ }
+ const resultCoeff = -(afterSumFactor - beforeSumFactor);
+ return Math.round(resultCoeff * 1000) / 1000;
+ }
+ /**
+ * Start battle
+ *
+ * Начало боя
+ */
+ function startBattle() {
+ countBattle++;
+ const countMaxBattle = getInput('countAutoBattle');
+ // setProgress(countBattle + '/' + countMaxBattle);
+ if (countBattle > countMaxBattle) {
+ setProgress(`${I18N('RETRY_LIMIT_EXCEEDED')}: ${countMaxBattle}`, true);
+ endAutoBattle(`${I18N('RETRY_LIMIT_EXCEEDED')}: ${countMaxBattle}`)
+ return;
+ }
+ if (stopAutoBattle) {
+ setProgress(I18N('STOPPED'), true);
+ endAutoBattle('STOPPED');
+ return;
+ }
+ send({calls: [{
+ name: nameFuncStartBattle,
+ args: battleArg,
+ ident: "body"
+ }]}, calcResultBattle);
+ }
+ /**
+ * Battle calculation
+ *
+ * Расчет боя
+ */
+ async function calcResultBattle(e) {
+ if (!e) {
+ console.log('данные не были получены');
+ if (dataNotEeceived < 10) {
+ dataNotEeceived++;
+ startBattle();
+ return;
+ }
+ endAutoBattle('Error', 'данные не были получены ' + dataNotEeceived + ' раз');
+ return;
+ }
+ if ('error' in e) {
+ if (e.error.description === 'too many tries') {
+ invasionTimer += 100;
+ countBattle--;
+ countError++;
+ console.log(`Errors: ${countError}`, e.error);
+ startBattle();
+ return;
+ }
+ const result = await popup.confirm(I18N('ERROR_DURING_THE_BATTLE') + ' ' + e.error.description, [
+ { msg: I18N('BTN_OK'), result: false, color: 'green' },
+ { msg: I18N('RELOAD_GAME'), result: true },
+ ]);
+ endAutoBattle('Error', e.error);
+ if (result) {
+ location.reload();
+ }
+ return;
+ }
+ let battle = e.results[0].result.response.battle
+ if (nameFuncStartBattle == 'towerStartBattle' ||
+ nameFuncStartBattle == 'bossAttack' ||
+ nameFuncStartBattle == 'invasion_bossStart') {
+ battle = e.results[0].result.response;
+ }
+ lastBattleInfo = battle;
+ BattleCalc(battle, getBattleType(battle.type), resultBattle);
+ }
+ /**
+ * Processing the results of the battle
+ *
+ * Обработка результатов боя
+ */
+ async function resultBattle(e) {
+ const isWin = e.result.win;
+ if (isWin) {
+ endBattle(e, false);
+ return;
+ } else if (isChecked('tryFixIt_v2')) {
+ const { WinFixBattle } = HWHClasses;
+ const cloneBattle = structuredClone(e.battleData);
+ const bFix = new WinFixBattle(cloneBattle);
+ let attempts = Infinity;
+ if (nameFuncStartBattle == 'invasion_bossStart' && !isSetWinTimer) {
+ const { invasionInfo, invasionDataPacks } = HWHData;
+
+
+ let timer = '0';
+ const pack = invasionDataPacks[invasionInfo.bossLvl];
+ if (pack && pack.timer && (pack.buff == invasionInfo.buff)) {
+ timer = pack.timer;
}
- return this.questInfo['userGetInfo'].gold > sumGold;
- },
- },
- 10002: {
- description: 'Пройди 10 миссий', // --------------
- isWeCanDo: () => false,
- },
- 10003: {
- description: 'Пройди 3 героические миссии', // ++++++++++++++++
- isWeCanDo: () => {
- const vipPoints = +this.questInfo.userGetInfo.vipPoints;
- const goldTicket = !!this.questInfo.inventoryGet.consumable[151];
- return (vipPoints > 100 || goldTicket) && this.getHeroicMissionId();
- },
- doItCall: () => {
- const selectedMissionId = this.getHeroicMissionId();
- const goldTicket = !!this.questInfo.inventoryGet.consumable[151];
- const vipLevel = Math.max(...lib.data.level.vip.filter(l => l.vipPoints <= +this.questInfo.userGetInfo.vipPoints).map(l => l.level));
- // Возвращаем массив команд для рейда
- if (vipLevel >= 5 || goldTicket) {
- return [{ name: 'missionRaid', args: { id: selectedMissionId, times: 3 }, ident: 'missionRaid_1' }];
- } else {
- return [
- { name: 'missionRaid', args: { id: selectedMissionId, times: 1 }, ident: 'missionRaid_1' },
- { name: 'missionRaid', args: { id: selectedMissionId, times: 1 }, ident: 'missionRaid_2' },
- { name: 'missionRaid', args: { id: selectedMissionId, times: 1 }, ident: 'missionRaid_3' },
- ];
+
+ let winTimer = await popup.confirm(`Secret number:`, [
+ { result: false, isClose: true },
+ { msg: 'Go', isInput: true, default: timer, color: 'green' },
+ ]);
+ winTimer = Number.parseFloat(winTimer);
+ if (winTimer) {
+ attempts = 5;
+ bFix.setWinTimer(winTimer);
}
- },
- },
- 10004: {
- description: 'Сразись 3 раза на Арене или Гранд Арене', // --------------
- isWeCanDo: () => false,
- },
- 10006: {
- description: 'Используй обмен изумрудов 1 раз', // ++++++++++++++++
- doItCall: () => [
+ isSetWinTimer = true;
+ }
+ let endTime = Date.now() + 6e4;
+ if (nameFuncStartBattle == 'invasion_bossStart') {
+ endTime = Date.now() + 6e4 * 4;
+ bFix.isGetTimer = false;
+ bFix.setMaxTimer(120.3);
+ }
+ const result = await bFix.start(endTime, attempts);
+ console.log(result);
+ if (result.result?.win) {
+ endBattle(result, false);
+ return;
+ }
+ }
+ const countMaxBattle = getInput('countAutoBattle');
+ if (findCoeff) {
+ const coeff = calcCoeff(e, 'defenders');
+ setProgress(`${countBattle}/${countMaxBattle}, ${coeff}`);
+ if (coeff > findCoeff) {
+ endBattle(e, false);
+ return;
+ }
+ } else {
+ if (nameFuncStartBattle == 'invasion_bossStart') {
+ const bossLvl = lastBattleInfo.typeId >= 130 ? lastBattleInfo.typeId : '';
+ const justice = lastBattleInfo?.effects?.attackers?.percentInOutDamageModAndEnergyIncrease_any_99_100_300_99_1000_30 || 0;
+ setProgress(`${svgBoss} ${bossLvl} ${svgJustice} ${justice} ${svgAttempt} ${countBattle}/${countMaxBattle}`, false, () => {
+ stopAutoBattle = true;
+ });
+ await new Promise((resolve) => setTimeout(resolve, 5000));
+ } else {
+ setProgress(`${countBattle}/${countMaxBattle}`);
+ }
+ }
+ if (nameFuncStartBattle == 'towerStartBattle' ||
+ nameFuncStartBattle == 'bossAttack' ||
+ nameFuncStartBattle == 'invasion_bossStart') {
+ startBattle();
+ return;
+ }
+ cancelEndBattle(e);
+ }
+ /**
+ * Cancel fight
+ *
+ * Отмена боя
+ */
+ function cancelEndBattle(r) {
+ const fixBattle = function (heroes) {
+ for (const ids in heroes) {
+ hero = heroes[ids];
+ hero.energy = random(1, 999);
+ if (hero.hp > 0) {
+ hero.hp = random(1, hero.hp);
+ }
+ }
+ }
+ fixBattle(r.progress[0].attackers.heroes);
+ fixBattle(r.progress[0].defenders.heroes);
+ endBattle(r, true);
+ }
+ /**
+ * End of the fight
+ *
+ * Завершение боя */
+ function endBattle(battleResult, isCancal) {
+ let calls = [{
+ name: nameFuncEndBattle,
+ args: {
+ result: battleResult.result,
+ progress: battleResult.progress
+ },
+ ident: "body"
+ }];
+
+ if (nameFuncStartBattle == 'invasion_bossStart') {
+ calls[0].args.id = lastBattleArg.id;
+ }
+
+ send({calls}, async e => {
+ console.log(e);
+ if (isCancal) {
+ startBattle();
+ return;
+ }
+
+ setProgress(`${I18N('SUCCESS')}!`, 5000)
+ if (nameFuncStartBattle == 'invasion_bossStart' ||
+ nameFuncStartBattle == 'bossAttack') {
+ const countMaxBattle = getInput('countAutoBattle');
+ const bossLvl = lastBattleInfo.typeId >= 130 ? lastBattleInfo.typeId : '';
+ const justice = lastBattleInfo?.effects?.attackers?.percentInOutDamageModAndEnergyIncrease_any_99_100_300_99_1000_30 || 0;
+ let winTimer = '';
+ if (nameFuncStartBattle == 'invasion_bossStart') {
+ const timer = battleResult.progress[0].attackers.input[5];
+ winTimer += ' Secret number: ' + timer;
+ winTimer +=
+ ' ' +
+ battleArg.heroes
+ .map((id) => `${cheats.translate('LIB_HERO_NAME_' + id)}(${cheats.translate('LIB_HERO_NAME_' + battleArg.favor[id])})`)
+ .join(' ') +
+ ' ' +
+ (battleArg.pet ? cheats.translate('LIB_HERO_NAME_' + battleArg.pet) : '');
+ console.log(bossLvl, {
+ buff: justice,
+ pet: battleArg.pet,
+ heroes: battleArg.heroes,
+ favor: battleArg.favor,
+ timer,
+ });
+ }
+ const result = await popup.confirm(
+ I18N('BOSS_HAS_BEEN_DEF_TEXT', {
+ bossLvl: `${svgBoss} ${bossLvl} ${svgJustice} ${justice}`,
+ countBattle: svgAttempt + ' ' + countBattle,
+ countMaxBattle,
+ winTimer,
+ }),
+ [
+ { msg: I18N('BTN_OK'), result: 0, color: 'green' },
+ { msg: I18N('MAKE_A_SYNC'), result: 1 },
+ { msg: I18N('RELOAD_GAME'), result: 2 },
+ ]
+ );
+ if (result) {
+ if (result == 1) {
+ cheats.refreshGame();
+ }
+ if (result == 2) {
+ location.reload();
+ }
+ }
+
+ }
+ endAutoBattle(`${I18N('SUCCESS')}!`)
+ });
+ }
+ /**
+ * Completing a task
+ *
+ * Завершение задачи
+ */
+ function endAutoBattle(reason, info) {
+ setIsCancalBattle(true);
+ console.log(reason, info);
+ resolve();
+ }
+ }
+
+ this.HWHClasses.executeAutoBattle = executeAutoBattle;
+
+ function testDailyQuests() {
+ const { dailyQuests } = HWHClasses;
+ return new Promise((resolve, reject) => {
+ const quests = new dailyQuests(resolve, reject);
+ quests.init(questsInfo);
+ quests.start();
+ });
+ }
+
+ /**
+ * Automatic completion of daily quests
+ *
+ * Автоматическое выполнение ежедневных квестов
+ */
+ class dailyQuests {
+ /**
+ * Caller.send('userGetInfo').then(e => console.log(e));
+ * Caller.send('heroGetAll').then(e => console.log(e));
+ * Caller.send('titanGetAll').then(e => console.log(e));
+ * Caller.send('inventoryGet').then(e => console.log(e));
+ * Caller.send('questGetAll').then(e => console.log(e));
+ * Caller.send('bossGetAll').then(e => console.log(e));
+ * Caller.send('missionGetAll').then(e => console.log(e));
+ */
+ callsList = ['userGetInfo', 'heroGetAll', 'titanGetAll', 'inventoryGet', 'questGetAll', 'bossGetAll', 'missionGetAll'];
+
+ dataQuests = {
+ 10001: {
+ description: 'Улучши умения героев 3 раза', // ++++++++++++++++
+ doItCall: () => {
+ const upgradeSkills = this.getUpgradeSkills();
+ return upgradeSkills.map(({ heroId, skill }, index) => ({
+ name: 'heroUpgradeSkill',
+ args: { heroId, skill },
+ ident: `heroUpgradeSkill_${index}`,
+ }));
+ },
+ isWeCanDo: () => {
+ const upgradeSkills = this.getUpgradeSkills();
+ let sumGold = 0;
+ for (const skill of upgradeSkills) {
+ sumGold += this.skillCost(skill.value);
+ if (!skill.heroId) {
+ return false;
+ }
+ }
+ return this.questInfo['userGetInfo'].gold > sumGold;
+ },
+ },
+ 10002: {
+ description: 'Пройди 10 миссий', // --------------
+ isWeCanDo: () => false,
+ },
+ 10003: {
+ description: 'Пройди 3 героические миссии', // ++++++++++++++++
+ isWeCanDo: () => {
+ const vipPoints = +this.questInfo.userGetInfo.vipPoints;
+ const goldTicket = !!this.questInfo.inventoryGet.consumable[151];
+ return (vipPoints > 100 || goldTicket) && this.getHeroicMissionId();
+ },
+ doItCall: () => {
+ const selectedMissionId = this.getHeroicMissionId();
+ const goldTicket = !!this.questInfo.inventoryGet.consumable[151];
+ const vipLevel = Math.max(
+ ...lib.data.level.vip.filter((l) => l.vipPoints <= +this.questInfo.userGetInfo.vipPoints).map((l) => l.level)
+ );
+ // Возвращаем массив команд для рейда
+ if (vipLevel >= 5 || goldTicket) {
+ return [{ name: 'missionRaid', args: { id: selectedMissionId, times: 3 }, ident: 'missionRaid_1' }];
+ } else {
+ return [
+ { name: 'missionRaid', args: { id: selectedMissionId, times: 1 }, ident: 'missionRaid_1' },
+ { name: 'missionRaid', args: { id: selectedMissionId, times: 1 }, ident: 'missionRaid_2' },
+ { name: 'missionRaid', args: { id: selectedMissionId, times: 1 }, ident: 'missionRaid_3' },
+ ];
+ }
+ },
+ },
+ 10004: {
+ description: 'Сразись 3 раза на Арене или Гранд Арене', // --------------
+ isWeCanDo: () => false,
+ },
+ 10006: {
+ description: 'Используй обмен изумрудов 1 раз', // ++++++++++++++++
+ doItCall: () => [
{
name: 'refillableAlchemyUse',
args: { multi: false },
@@ -12210,20 +12388,16 @@
init(questInfo) {
this.questInfo = questInfo;
- this.isAuto = true;
+ this.isAuto = false;
}
async autoInit(isAuto) {
this.isAuto = isAuto || false;
+ const caller = new Caller(this.callsList);
+ await caller.send();
const quests = {};
- const calls = this.callsList.map((name) => ({
- name,
- args: {},
- ident: name,
- }));
- const result = await Send(JSON.stringify({ calls })).then((e) => e.results);
- for (const call of result) {
- quests[call.ident] = call.result.response;
+ for (const name in caller.results) {
+ quests[name] = caller.results[name][0];
}
this.questInfo = quests;
}
@@ -12261,17 +12435,12 @@
let taskList = [];
if (this.isAuto) {
taskList = weCanDo;
- // Auto mode: check all tasks and update selectedActions
- taskList.forEach((e) => {
- selectedActions[e.name].checked = true;
- });
- setSaveVal('selectedActions', selectedActions);
} else {
const answer = await popup.confirm(
`${I18N('YOU_CAN_COMPLETE')}:`,
[
- { msg: I18N('BTN_DO_IT'), result: true },
- { msg: I18N('BTN_CANCEL'), result: false, isCancel: true },
+ { msg: I18N('BTN_DO_IT'), result: true, color: 'green' },
+ { msg: I18N('BTN_CANCEL'), result: false, isCancel: true, color: 'red' },
],
weCanDo
);
@@ -12286,17 +12455,21 @@
setSaveVal('selectedActions', selectedActions);
}
- const calls = [];
let countChecked = 0;
for (const task of taskList) {
if (task.checked) {
- countChecked++;
const quest = this.dataQuests[task.name];
console.log(quest.description);
if (quest.doItCall) {
const doItCall = quest.doItCall.call(this);
- calls.push(...doItCall);
+ try {
+ await Caller.send(doItCall);
+ } catch (e) {
+ console.error(e);
+ continue;
+ }
+ countChecked++;
}
}
}
@@ -12306,10 +12479,6 @@
return;
}
- const result = await Send(JSON.stringify({ calls }));
- if (result.error) {
- console.error(result.error, result.error.call);
- }
this.end(`${I18N('COMPLETED_QUESTS')}: ${countChecked}`);
}
@@ -12585,7 +12754,7 @@
}
getOutlandChest() {
- const bosses = this.questInfo['bossGetAll'];
+ const bosses = this.questInfo['bossGetAll'].bosses;
const calls = [];
@@ -12695,8 +12864,7 @@
// Собираем дропы из героических миссий
const drops = heroicMissions.map((mission) => {
const lastWave = mission.normalMode.waves[mission.normalMode.waves.length - 1];
- const allRewards = lastWave.enemies[lastWave.enemies.length - 1]
- .drop.map((drop) => drop.reward);
+ const allRewards = lastWave.enemies[lastWave.enemies.length - 1].drop.map((drop) => drop.reward);
const heroId = +Object.keys(allRewards.find((reward) => reward.fragmentHero).fragmentHero).pop();
@@ -12731,7 +12899,6 @@
const { doYourBest } = HWHClasses;
return new Promise((resolve, reject) => {
const doIt = new doYourBest(resolve, reject);
- doIt.isAuto = true;
doIt.start();
});
}
@@ -12742,101 +12909,108 @@
* Кнопка сделать все
*/
class doYourBest {
- isAuto = false;
-
funcList = [
+ {
+ name: 'tidyInventory',
+ label: I18N('TIDY_INVENTORY'),
+ checked: true,
+ },
{
name: 'getOutland',
label: I18N('ASSEMBLE_OUTLAND'),
- checked: false
+ checked: false,
},
{
name: 'testTower',
label: I18N('PASS_THE_TOWER'),
- checked: false
+ checked: false,
},
{
name: 'checkExpedition',
label: I18N('CHECK_EXPEDITIONS'),
- checked: false
+ checked: false,
},
{
name: 'testTitanArena',
label: I18N('COMPLETE_TOE'),
- checked: false
- },
- {
- name: 'testBothArenas',
- label: I18N('AUTO_ARENAS'),
- checked: false
+ checked: false,
},
{
name: 'mailGetAll',
label: I18N('COLLECT_MAIL'),
- checked: false
+ checked: false,
},
{
name: 'collectAllStuff',
label: I18N('COLLECT_MISC'),
title: I18N('COLLECT_MISC_TITLE'),
- checked: false
+ checked: false,
},
{
name: 'getDailyBonus',
label: I18N('DAILY_BONUS'),
- checked: false
+ checked: false,
},
{
name: 'dailyQuests',
label: I18N('DO_DAILY_QUESTS'),
- checked: false
+ checked: false,
},
{
name: 'rollAscension',
label: I18N('SEER_TITLE'),
- checked: false
+ checked: false,
},
{
name: 'questAllFarm',
label: I18N('COLLECT_QUEST_REWARDS'),
- checked: false
+ checked: false,
},
{
name: 'testDungeon',
label: I18N('COMPLETE_DUNGEON'),
- checked: false
+ checked: false,
},
{
name: 'synchronization',
label: I18N('MAKE_A_SYNC'),
- checked: false
+ checked: false,
},
{
name: 'reloadGame',
label: I18N('RELOAD_GAME'),
- checked: false
+ checked: false,
},
];
functions = {
+ tidyInventory: async () => {
+ const { InventoryTidier } = HWHClasses;
+ const tidyInv = new InventoryTidier();
+ await tidyInv.runSilent();
+ },
getOutland,
testTower,
checkExpedition,
testTitanArena,
- testBothArenas,
mailGetAll,
collectAllStuff: async () => {
await offerFarmAllReward();
- await Send('{"calls":[{"name":"subscriptionFarm","args":{},"ident":"body"},{"name":"zeppelinGiftFarm","args":{},"ident":"zeppelinGiftFarm"},{"name":"grandFarmCoins","args":{},"ident":"grandFarmCoins"},{"name":"gacha_refill","args":{"ident":"heroGacha"},"ident":"gacha_refill"}]}');
+ await Caller.send(['subscriptionFarm', 'zeppelinGiftFarm', 'grandFarmCoins', { name: 'gacha_refill', args: { ident: 'heroGacha' } }]);
},
dailyQuests: async function () {
- const quests = new dailyQuests(() => { }, () => { });
+ const quests = new dailyQuests(
+ () => {},
+ () => {}
+ );
await quests.autoInit(true);
await quests.start();
},
rollAscension,
getDailyBonus,
- questAllFarm,
+ questAllFarm: async () => {
+ await rewardsAndMailFarm(false);
+ },
testDungeon,
synchronization: async () => {
cheats.refreshGame();
@@ -12844,78 +13018,71 @@
reloadGame: async () => {
location.reload();
},
- }
+ };
constructor(resolve, reject, questInfo) {
this.resolve = resolve;
this.reject = reject;
- this.questInfo = questInfo
+ this.questInfo = questInfo;
}
async start() {
const selectedDoIt = getSaveVal('selectedDoIt', {});
-
- if (this.isAuto) {
- // Auto mode: check all functions except reloadGame and skip popup
- this.funcList.forEach(task => {
- if (task.name !== 'reloadGame') {
- task.checked = true;
- selectedDoIt[task.name] = { checked: true };
- } else {
- task.checked = false;
- selectedDoIt[task.name] = { checked: false };
- }
- });
- setSaveVal('selectedDoIt', selectedDoIt);
- } else {
- // Manual mode: show popup
- this.funcList.forEach(task => {
- if (!selectedDoIt[task.name]) {
- selectedDoIt[task.name] = {
- checked: task.checked
- }
- } else {
- task.checked = selectedDoIt[task.name].checked
- }
- });
-
- const answer = await popup.confirm(I18N('RUN_FUNCTION'), [
- { msg: I18N('BTN_CANCEL'), result: false, isCancel: true },
- { msg: I18N('BTN_GO'), result: true },
- ], this.funcList);
-
- if (!answer) {
- this.end('');
- return;
+
+ this.funcList.forEach((task) => {
+ if (!selectedDoIt[task.name]) {
+ selectedDoIt[task.name] = {
+ checked: task.checked,
+ };
+ } else {
+ task.checked = selectedDoIt[task.name].checked;
}
-
- const taskList = popup.getCheckBoxes();
- taskList.forEach(task => {
- selectedDoIt[task.name].checked = task.checked;
- });
- setSaveVal('selectedDoIt', selectedDoIt);
+ });
+
+ const answer = await popup.confirm(
+ I18N('RUN_FUNCTION'),
+ [
+ { msg: I18N('BTN_CANCEL'), result: false, isCancel: true, color: 'red' },
+ { msg: I18N('BTN_GO'), result: true, color: 'green' },
+ ],
+ this.funcList
+ );
+
+ if (!answer) {
+ this.end('');
+ return;
}
-
- // Execute all checked functions
- for (const task of this.funcList) {
+
+ const taskList = popup.getCheckBoxes();
+ taskList.forEach((task) => {
+ selectedDoIt[task.name].checked = task.checked;
+ });
+ setSaveVal('selectedDoIt', selectedDoIt);
+ for (const task of popup.getCheckBoxes()) {
if (task.checked) {
try {
setProgress(`${task.label} ${I18N('PERFORMED')}!`);
await this.functions[task.name]();
setProgress(`${task.label} ${I18N('DONE')}!`);
} catch (error) {
- if (await popup.confirm(`${I18N('ERRORS_OCCURRES')}: ${task.label} ${I18N('COPY_ERROR')}?`, [
- { msg: I18N('BTN_NO'), result: false },
- { msg: I18N('BTN_YES'), result: true },
- ])) {
+ if (
+ await popup.confirm(`${I18N('ERRORS_OCCURRES')}: ${task.label} ${I18N('COPY_ERROR')}?`, [
+ { msg: I18N('BTN_NO'), result: false, color: 'red' },
+ { msg: I18N('BTN_YES'), result: true, color: 'green' },
+ ])
+ ) {
this.errorHandling(error);
}
}
}
}
- setTimeout((msg) => {
- this.end(msg);
- }, 2000, I18N('ALL_TASK_COMPLETED'));
+ setTimeout(
+ (msg) => {
+ this.end(msg);
+ },
+ 2000,
+ I18N('ALL_TASK_COMPLETED')
+ );
return;
}
@@ -12924,7 +13091,7 @@
let errorInfo = error.toString() + '\n';
try {
const errorStack = error.stack.split('\n');
- const endStack = errorStack.map(e => e.split('@')[0]).indexOf("testDoYourBest");
+ const endStack = errorStack.map((e) => e.split('@')[0]).indexOf('testDoYourBest');
errorInfo += errorStack.slice(0, endStack).join('\n');
} catch (e) {
errorInfo += error.stack;
@@ -13020,13 +13187,13 @@
async start(type) {
this.type = type || this.type;
this.callAdventureInfo.name = this.actions[this.type].getInfo;
- const data = await Send(JSON.stringify({
+ const data = await Send({
calls: [
this.callAdventureInfo,
this.callTeamGetAll,
this.callTeamGetFavor
]
- }));
+ });
return this.checkAdventureInfo(data.results);
}
@@ -13038,12 +13205,14 @@
msg: I18N('START_ADVENTURE'),
placeholder: '1,2,3,4,5,6',
isInput: true,
- default: getSaveVal(keyPath, oldVal)
+ default: getSaveVal(keyPath, oldVal),
+ color: 'green',
},
{
msg: I18N('BTN_CANCEL'),
result: false,
- isCancel: true
+ isCancel: true,
+ color: 'red',
},
]);
if (!answer) {
@@ -13140,11 +13309,13 @@
return this.end();
}
this.path = this.path.slice(position);
- if ((this.path.length - 1) > this.turnsLeft &&
- await popup.confirm(I18N('ATTEMPTS_NOT_ENOUGH'), [
- { msg: I18N('YES_CONTINUE'), result: false },
- { msg: I18N('BTN_NO'), result: true },
- ])) {
+ if (
+ this.path.length - 1 > this.turnsLeft &&
+ (await popup.confirm(I18N('ATTEMPTS_NOT_ENOUGH'), [
+ { msg: I18N('YES_CONTINUE'), result: false, color: 'green' },
+ { msg: I18N('BTN_NO'), result: true, color: 'red' },
+ ]))
+ ) {
this.terminatеReason = I18N('NOT_ENOUGH_AP');
return this.end();
}
@@ -13254,10 +13425,12 @@
}
} catch (error) {
console.error(error);
- if (await popup.confirm(I18N('ERROR_OF_THE_BATTLE_COPY'), [
- { msg: I18N('BTN_NO'), result: false },
- { msg: I18N('BTN_YES'), result: true },
- ])) {
+ if (
+ await popup.confirm(I18N('ERROR_OF_THE_BATTLE_COPY'), [
+ { msg: I18N('BTN_NO'), result: false, color: 'red' },
+ { msg: I18N('BTN_YES'), result: true, color: 'green' },
+ ])
+ ) {
this.errorHandling(error, data);
}
this.terminatеReason = I18N('ERROR_DURING_THE_BATTLE');
@@ -13295,7 +13468,7 @@
this.callStartBattle.name = this.actions[this.type].startBattle;
this.callStartBattle.args = this.args
const calls = [this.callStartBattle];
- return Send(JSON.stringify({ calls }));
+ return Send({ calls });
}
cancelBattle(battle) {
@@ -13323,7 +13496,7 @@
this.callEndBattle.args.result = battle.result
this.callEndBattle.args.progress = battle.progress
const calls = [this.callEndBattle];
- return Send(JSON.stringify({ calls }));
+ return Send({ calls });
}
/**
@@ -13354,7 +13527,7 @@
this.callCollectBuff.name = this.actions[this.type].collectBuff;
this.callCollectBuff.args = { buff, path };
const calls = [this.callCollectBuff];
- return Send(JSON.stringify({ calls }));
+ return Send({ calls });
}
getNodeInfo(nodeId) {
@@ -13531,7 +13704,7 @@
// Автоматический подбор пачки
if (this.isAuto) {
- if (this.mandatoryId <= 4000 && this.mandatoryId != 13) {
+ if (this.mandatoryId < 4000 && this.mandatoryId != 13) {
this.end(I18N('BRAWL_AUTO_PACK_NOT_CUR_HERO'));
return;
}
@@ -13552,6 +13725,10 @@
async updateTitanPack(enemieHeroes) {
const packs = [
+ [4000, 4001, 4002, 4003, 4004],
+ [4010, 4011, 4012, 4013, 4014],
+ [4020, 4021, 4022, 4023, 4024],
+
[4033, 4040, 4041, 4042, 4043],
[4032, 4040, 4041, 4042, 4043],
[4031, 4040, 4041, 4042, 4043],
@@ -13849,374 +14026,1133 @@
[4000, 4001, 4002, 4003, 4010],
].filter((p) => p.includes(this.mandatoryId));
- const bestPack = {
- pack: packs[0],
- winRate: 0,
- countBattle: 0,
- id: 0,
- };
+ const bestPack = {
+ pack: packs[0],
+ winRate: 0,
+ countBattle: 0,
+ id: 0,
+ };
+
+ for (const id in packs) {
+ const pack = packs[id];
+ const attackers = this.maxUpgrade.filter((e) => pack.includes(e.id)).reduce((obj, e) => ({ ...obj, [e.id]: e }), {});
+ const battle = {
+ attackers,
+ defenders: [enemieHeroes],
+ type: 'brawl_titan',
+ };
+ const isRandom = this.isRandomBattle(battle);
+ const stat = {
+ count: 0,
+ win: 0,
+ winRate: 0,
+ };
+ for (let i = 1; i <= 26; i++) {
+ battle.seed = Math.floor(Date.now() / 1000) + Math.random() * 1000;
+ const result = await Calc(battle);
+ stat.win += result.result.win;
+ stat.count += 1;
+ stat.winRate = stat.win / stat.count;
+
+ if (!isRandom) {
+ break;
+ }
+ if (stat.win >= 22) {
+ break;
+ }
+ const losses = stat.count - stat.win;
+ if (losses >= 4) {
+ break;
+ }
+ }
+
+ if (!isRandom && stat.win) {
+ return {
+ favor: {},
+ heroes: pack,
+ };
+ }
+ if (stat.winRate > 0.84) {
+ return {
+ favor: {},
+ heroes: pack,
+ };
+ }
+ if (stat.winRate > bestPack.winRate) {
+ bestPack.countBattle = stat.count;
+ bestPack.winRate = stat.winRate;
+ bestPack.pack = pack;
+ bestPack.id = id;
+ }
+ }
+
+ //console.log(bestPack.id, bestPack.pack, bestPack.winRate, bestPack.countBattle);
+ return {
+ favor: {},
+ heroes: bestPack.pack,
+ };
+ }
+
+ isRandomPack(pack) {
+ const ids = Object.keys(pack);
+ return ids.includes('4023') || ids.includes('4021');
+ }
+
+ isRandomBattle(battle) {
+ return this.isRandomPack(battle.attackers) || this.isRandomPack(battle.defenders[0]);
+ }
+
+ async updateHeroesPack(enemieHeroes) {
+ const packs = [
+ {
+ id: 1,
+ args: { userId: -830021, heroes: [63, 13, 9, 48, 1], pet: 6006, favor: { 1: 6004, 9: 6005, 13: 6002, 48: 6e3, 63: 6009 } },
+ attackers: {
+ 1: {
+ id: 1,
+ xp: 3625195,
+ level: 130,
+ color: 18,
+ slots: [0, 0, 0, 0, 0, 0],
+ skills: { 2: 130, 3: 130, 4: 130, 5: 130, 6022: 130, 8268: 1, 8269: 1 },
+ power: 198058,
+ star: 6,
+ runes: [43750, 43750, 43750, 43750, 43750],
+ skins: { 1: 60, 54: 60, 95: 60, 154: 60, 250: 60, 325: 60 },
+ currentSkin: 0,
+ titanGiftLevel: 30,
+ titanCoinsSpent: null,
+ artifacts: [
+ { level: 130, star: 6 },
+ { level: 130, star: 6 },
+ { level: 130, star: 6 },
+ ],
+ scale: 1,
+ petId: 6004,
+ type: 'hero',
+ perks: [4, 1],
+ ascensions: {
+ 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 2: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ 3: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 5: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ },
+ agility: 3093,
+ hp: 419649,
+ intelligence: 3644,
+ physicalAttack: 11481.6,
+ strength: 17049,
+ armor: 12720,
+ dodge: 17232.28,
+ magicPenetration: 22780,
+ magicPower: 55816,
+ magicResist: 1580,
+ modifiedSkillTier: 5,
+ skin: 0,
+ favorPetId: 6004,
+ favorPower: 11064,
+ },
+ 9: {
+ id: 9,
+ xp: 3625195,
+ level: 130,
+ color: 18,
+ slots: [0, 0, 0, 0, 0, 0],
+ skills: { 335: 130, 336: 130, 337: 130, 338: 130, 6027: 130, 8270: 1, 8271: 1 },
+ power: 195886,
+ star: 6,
+ runes: [43750, 43750, 43750, 43750, 43750],
+ skins: { 9: 60, 41: 60, 163: 60, 189: 60, 311: 60, 338: 60 },
+ currentSkin: 0,
+ titanGiftLevel: 30,
+ titanCoinsSpent: null,
+ artifacts: [
+ { level: 130, star: 6 },
+ { level: 130, star: 6 },
+ { level: 130, star: 6 },
+ ],
+ scale: 1,
+ petId: 6005,
+ type: 'hero',
+ perks: [7, 2, 20],
+ ascensions: {
+ 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 2: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ 3: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 5: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ },
+ agility: 3068,
+ hp: 227134,
+ intelligence: 19003,
+ physicalAttack: 7020.32,
+ strength: 3068,
+ armor: 19995,
+ dodge: 14644,
+ magicPower: 64780.6,
+ magicResist: 31597,
+ modifiedSkillTier: 5,
+ skin: 0,
+ favorPetId: 6005,
+ favorPower: 11064,
+ },
+ 13: {
+ id: '13',
+ xp: 3625195,
+ level: 130,
+ color: 18,
+ slots: [0, 0, 0, 0, 0, 0],
+ skills: { 452: 130, 453: 130, 454: 130, 455: 130, 6012: 130, 8274: 1, 8275: 1 },
+ power: 194833,
+ star: 6,
+ runes: [43750, 43750, 43750, 43750, 43750],
+ skins: { 13: 60, 38: 60, 148: 60, 199: 60, 240: 60, 335: 60 },
+ currentSkin: 0,
+ titanGiftLevel: 30,
+ titanCoinsSpent: null,
+ artifacts: [
+ { level: 130, star: 6 },
+ { level: 130, star: 6 },
+ { level: 130, star: 6 },
+ ],
+ scale: 1,
+ petId: 6002,
+ type: 'hero',
+ perks: [7, 2, 21],
+ ascensions: {
+ 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 2: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ 3: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 5: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ },
+ agility: 2885,
+ hp: 344763,
+ intelligence: 17625,
+ physicalAttack: 50,
+ strength: 3020,
+ armor: 19060,
+ magicPenetration: 58138.6,
+ magicPower: 70100.6,
+ magicResist: 27227,
+ modifiedSkillTier: 4,
+ skin: 0,
+ favorPetId: 6002,
+ favorPower: 11064,
+ },
+ 48: {
+ id: 48,
+ xp: 3625195,
+ level: 130,
+ color: 18,
+ slots: [0, 0, 0, 0, 0, 0],
+ skills: { 240: 130, 241: 130, 242: 130, 243: 130, 6002: 130 },
+ power: 190584,
+ star: 6,
+ runes: [43750, 43750, 43750, 43750, 43750],
+ skins: { 103: 60, 165: 60, 217: 60, 296: 60, 326: 60 },
+ currentSkin: 0,
+ titanGiftLevel: 30,
+ titanCoinsSpent: null,
+ artifacts: [
+ { level: 130, star: 6 },
+ { level: 130, star: 6 },
+ { level: 130, star: 6 },
+ ],
+ scale: 1,
+ petId: 6e3,
+ type: 'hero',
+ perks: [5, 2],
+ ascensions: {
+ 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 2: [0, 1, 2, 3, 4, 5, 6, 7, 8, 10],
+ 3: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 5: [0, 1, 2, 3, 4, 5, 6, 7, 8, 10],
+ },
+ agility: 17308,
+ hp: 397737,
+ intelligence: 2888,
+ physicalAttack: 40298.32,
+ physicalCritChance: 12280,
+ strength: 3169,
+ armor: 12185,
+ armorPenetration: 20137.6,
+ magicResist: 24816,
+ skin: 0,
+ favorPetId: 6e3,
+ favorPower: 11064,
+ },
+ 63: {
+ id: 63,
+ xp: 3625195,
+ level: 130,
+ color: 18,
+ slots: [0, 0, 0, 0, 0, 0],
+ skills: { 442: 130, 443: 130, 444: 130, 445: 130, 6041: 130, 8272: 1, 8273: 1 },
+ power: 193520,
+ star: 6,
+ runes: [43750, 43750, 43750, 43750, 43750],
+ skins: { 341: 60, 350: 60, 351: 60, 352: 1 },
+ currentSkin: 0,
+ titanGiftLevel: 30,
+ titanCoinsSpent: null,
+ artifacts: [
+ { level: 130, star: 6 },
+ { level: 130, star: 6 },
+ { level: 130, star: 6 },
+ ],
+ scale: 1,
+ petId: 6009,
+ type: 'hero',
+ perks: [6, 1, 21],
+ ascensions: {
+ 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 2: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ 3: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ 5: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ },
+ agility: 17931,
+ hp: 488832,
+ intelligence: 2737,
+ physicalAttack: 54213.6,
+ strength: 2877,
+ armor: 800,
+ armorPenetration: 32477.6,
+ magicResist: 8526,
+ physicalCritChance: 9545,
+ modifiedSkillTier: 3,
+ skin: 0,
+ favorPetId: 6009,
+ favorPower: 11064,
+ },
+ 6006: {
+ id: 6006,
+ color: 10,
+ star: 6,
+ xp: 450551,
+ level: 130,
+ slots: [25, 50, 50, 25, 50, 50],
+ skills: { 6030: 130, 6031: 130 },
+ power: 181943,
+ type: 'pet',
+ perks: [5, 9],
+ name: null,
+ intelligence: 11064,
+ magicPenetration: 47911,
+ strength: 12360,
+ },
+ },
+ },
+ ];
+
+
+ const bestPack = {
+ pack: packs[0],
+ countWin: 0,
+ }
+
+ for (const pack of packs) {
+ const attackers = pack.attackers;
+ const battle = {
+ attackers,
+ defenders: [enemieHeroes],
+ type: 'brawl',
+ };
+
+ let countWinBattles = 0;
+ let countTestBattle = 10;
+ for (let i = 0; i < countTestBattle; i++) {
+ battle.seed = Math.floor(Date.now() / 1000) + Math.random() * 1000;
+ const result = await Calc(battle);
+ if (result.result.win) {
+ countWinBattles++;
+ }
+ if (countWinBattles > 7) {
+ console.log(pack)
+ return pack.args;
+ }
+ }
+ if (countWinBattles > bestPack.countWin) {
+ bestPack.countWin = countWinBattles;
+ bestPack.pack = pack.args;
+ }
+ }
+
+ console.log(bestPack);
+ return bestPack.pack;
+ }
+
+ async questFarm() {
+ const calls = [this.callBrawlQuestFarm];
+ const result = await Send({ calls });
+ return result.results[0].result.response;
+ }
+
+ async getBrawlInfo() {
+ const data = await Send({
+ calls: [
+ this.callUserGetInfo,
+ this.callBrawlQuestGetInfo,
+ this.callBrawlFindEnemies,
+ this.callTeamGetMaxUpgrade,
+ this.callBrawlGetInfo,
+ ]
+ });
+
+ let attempts = data.results[0].result.response.refillable.find(n => n.id == 48);
+
+ const maxUpgrade = data.results[3].result.response;
+ const maxHero = Object.values(maxUpgrade.hero);
+ const maxTitan = Object.values(maxUpgrade.titan);
+ const maxPet = Object.values(maxUpgrade.pet);
+ this.maxUpgrade = [...maxHero, ...maxPet, ...maxTitan];
+
+ this.info = data.results[4].result.response;
+ this.mandatoryId = +lib.data.brawl.promoHero[this.info.id].promoHero;
+ return {
+ attempts: attempts.amount,
+ questInfo: data.results[1].result.response,
+ findEnemies: data.results[2].result.response,
+ }
+ }
+
+ /**
+ * Carrying out a fight
+ *
+ * Проведение боя
+ */
+ async battle(userId) {
+ this.stats.count++;
+ const battle = await this.startBattle(userId, this.args);
+ const result = await Calc(battle);
+ console.log(result.result);
+ if (result.result.win) {
+ this.stats.win++;
+ } else {
+ this.stats.loss++;
+ if (!this.info.boughtEndlessLivesToday) {
+ this.attempts--;
+ }
+ }
+ return await this.endBattle(result);
+ // return await this.cancelBattle(result);
+ }
+
+ /**
+ * Starts a fight
+ *
+ * Начинает бой
+ */
+ async startBattle(userId, args) {
+ const call = {
+ name: "brawl_startBattle",
+ args,
+ ident: "brawl_startBattle"
+ }
+ call.args.userId = userId;
+ const calls = [call];
+ const result = await Send({ calls });
+ return result.results[0].result.response;
+ }
+
+ cancelBattle(battle) {
+ const fixBattle = function (heroes) {
+ for (const ids in heroes) {
+ const hero = heroes[ids];
+ hero.energy = random(1, 999);
+ if (hero.hp > 0) {
+ hero.hp = random(1, hero.hp);
+ }
+ }
+ }
+ fixBattle(battle.progress[0].attackers.heroes);
+ fixBattle(battle.progress[0].defenders.heroes);
+ return this.endBattle(battle);
+ }
+
+ /**
+ * Ends the fight
+ *
+ * Заканчивает бой
+ */
+ async endBattle(battle) {
+ battle.progress[0].attackers.input = ['auto', 0, 0, 'auto', 0, 0];
+ const calls = [{
+ name: "brawl_endBattle",
+ args: {
+ result: battle.result,
+ progress: battle.progress
+ },
+ ident: "brawl_endBattle"
+ },
+ this.callBrawlQuestGetInfo,
+ this.callBrawlFindEnemies,
+ ];
+ const result = await Send({ calls });
+ return result.results;
+ }
+
+ end(endReason) {
+ const { executeBrawls } = HWHClasses;
+ setIsCancalBattle(true);
+ executeBrawls.isBrawlsAutoStart = false;
+ setProgress(endReason, true);
+ console.log(endReason);
+ this.resolve();
+ }
+ }
+
+ this.HWHClasses.executeBrawls = executeBrawls;
+
+ /**
+ * Runs missions from the company on a specified list
+ * Выполняет миссии из компании по списку
+ * @param {Array} missions [{id: 25, times: 3}, {id: 45, times: 30}]
+ * @param {Boolean} isRaids выполнять миссии рейдом
+ * @returns
+ */
+ function testCompany(missions, isRaids = false) {
+ const { ExecuteCompany } = HWHClasses;
+ return new Promise((resolve, reject) => {
+ const tower = new ExecuteCompany(resolve, reject);
+ tower.start(missions, isRaids);
+ });
+ }
+
+ /**
+ * Fulfilling company missions
+ * Выполнение миссий компании
+ */
+ class ExecuteCompany {
+ constructor(resolve, reject) {
+ this.resolve = resolve;
+ this.reject = reject;
+ this.missionsIds = [];
+ this.currentNum = 0;
+ this.isRaid = false;
+ this.currentTimes = 0;
+
+ this.argsMission = {
+ id: 0,
+ heroes: [],
+ favor: {},
+ };
+ }
+
+ async start(missionIds, isRaids) {
+ this.missionsIds = missionIds;
+ this.isRaid = isRaids;
+ const data = await Caller.send(['teamGetAll', 'teamGetFavor']);
+ this.startCompany(data);
+ }
+
+ startCompany(data) {
+ const [teamGetAll, teamGetFavor] = data;
+
+ this.argsMission.heroes = teamGetAll.mission.filter((id) => id < 6000);
+ this.argsMission.favor = teamGetFavor.mission;
+
+ const pet = teamGetAll.mission.filter((id) => id >= 6000).pop();
+ if (pet) {
+ this.argsMission.pet = pet;
+ }
+
+ this.checkStat();
+ }
+
+ checkStat() {
+ if (!this.missionsIds[this.currentNum].times) {
+ this.currentNum++;
+ }
+
+ if (this.currentNum === this.missionsIds.length) {
+ this.endCompany('EndCompany');
+ return;
+ }
+
+ this.argsMission.id = this.missionsIds[this.currentNum].id;
+ this.currentTimes = this.missionsIds[this.currentNum].times;
+ setProgress('Сompany: ' + this.argsMission.id + ' - ' + this.currentTimes, false);
+ if (this.isRaid) {
+ this.missionRaid();
+ } else {
+ this.missionStart();
+ }
+ }
+
+ async missionRaid() {
+ try {
+ await Caller.send({
+ name: 'missionRaid',
+ args: {
+ id: this.argsMission.id,
+ times: this.currentTimes,
+ },
+ });
+ } catch (error) {
+ console.warn(error);
+ }
+
+ this.missionsIds[this.currentNum].times = 0;
+ this.checkStat();
+ }
+
+ async missionStart() {
+ this.lastMissionBattleStart = Date.now();
+ let result = null;
+ try {
+ result = await Caller.send({
+ name: 'missionStart',
+ args: this.argsMission,
+ });
+ } catch (error) {
+ console.warn(error);
+ this.endCompany('missionStartError', error['error']);
+ return;
+ }
+ this.missionEnd(await Calc(result));
+ }
+
+ async missionEnd(r) {
+ const timer = r.battleTimer;
+ await countdownTimer(timer, 'Сompany: ' + this.argsMission.id + ' - ' + this.currentTimes);
+
+ try {
+ await Caller.send({
+ name: 'missionEnd',
+ args: {
+ id: this.argsMission.id,
+ result: r.result,
+ progress: r.progress,
+ },
+ });
+ } catch (error) {
+ this.endCompany('missionEndError', error);
+ return;
+ }
+
+ this.missionsIds[this.currentNum].times--;
+ this.checkStat();
+ }
- for (const id in packs) {
- const pack = packs[id];
- const attackers = this.maxUpgrade.filter((e) => pack.includes(e.id)).reduce((obj, e) => ({ ...obj, [e.id]: e }), {});
- const battle = {
- attackers,
- defenders: [enemieHeroes],
- type: 'brawl_titan',
- };
- const isRandom = this.isRandomBattle(battle);
- const stat = {
- count: 0,
- win: 0,
- winRate: 0,
- };
- for (let i = 1; i <= 20; i++) {
- battle.seed = Math.floor(Date.now() / 1000) + Math.random() * 1000;
- const result = await Calc(battle);
- stat.win += result.result.win;
- stat.count += 1;
- stat.winRate = stat.win / stat.count;
- if (!isRandom || (i >= 2 && stat.winRate < 0.65) || (i >= 10 && stat.winRate == 1)) {
- break;
+ endCompany(reason, info) {
+ setProgress('Сompany completed!', true);
+ console.log(reason, info);
+ this.resolve();
+ }
+ }
+
+ this.HWHClasses.ExecuteCompany = ExecuteCompany;
+ class InventoryTidier {
+ inventory = {};
+
+ constructor() {
+ this.tasks = [
+ {
+ name: 'openEquipFragment',
+ label: I18N('EQUIPMENT_FRAGMENT_CRATES'),
+ title: I18N('EQUIPMENT_FRAGMENT_CRATES_TITLE'),
+ checked: true,
+ },
+ {
+ name: 'randNuggetsAndRegal',
+ label: I18N('RAND_NUGGETS_AND_REGAL'),
+ title: I18N('RAND_NUGGETS_AND_REGAL_TITLE'),
+ checked: true,
+ },
+ {
+ name: 'chestWithArtRes',
+ label: I18N('ARTIFACT_RESOURCES'),
+ title: I18N('ARTIFACT_RESOURCES_TITLE'),
+ checked: true,
+ },
+ ];
+ }
+
+ async openEquipFragment() {
+ for (let libId = 362; libId <= 389; libId++) {
+ if (this.inventory.consumable[libId]) {
+ const amount = this.inventory.consumable[libId];
+ try {
+ await Caller.send({
+ name: 'consumableUseLootBox',
+ args: { libId, amount },
+ });
+ } catch (e) {
+ console.warn(e);
}
}
+ }
+ }
- if (!isRandom && stat.win) {
- return {
- favor: {},
- heroes: pack,
- };
- }
- if (stat.winRate > 0.85) {
- return {
- favor: {},
- heroes: pack,
- };
- }
- if (stat.winRate > bestPack.winRate) {
- bestPack.countBattle = stat.count;
- bestPack.winRate = stat.winRate;
- bestPack.pack = pack;
- bestPack.id = id;
+ async randNuggetsAndRegal() {
+ const libIds = [169, 170, 171, 172, 173, 207, 208, 209, 210, 211, 271, 272];
+ for (const libId of libIds) {
+ if (this.inventory.consumable[libId]) {
+ const amount = this.inventory.consumable[libId];
+ try {
+ await Caller.send({
+ name: 'consumableUseLootBox',
+ args: { libId, amount },
+ });
+ } catch (e) {
+ console.warn(e);
+ }
}
}
+ }
- //console.log(bestPack.id, bestPack.pack, bestPack.winRate, bestPack.countBattle);
- return {
- favor: {},
- heroes: bestPack.pack,
- };
+ async chestWithArtRes() {
+ for (let libId = 62; libId <= 64; libId++) {
+ if (this.inventory.consumable[libId]) {
+ const amount = this.inventory.consumable[libId];
+ try {
+ await Caller.send({
+ name: 'consumableUseLootBox',
+ args: { libId, amount, playerRewardChoiceIndex: 4 },
+ });
+ } catch (e) {
+ console.warn(e);
+ }
+ }
+ }
}
- isRandomPack(pack) {
- const ids = Object.keys(pack);
- return ids.includes('4023') || ids.includes('4021');
+ restoreSavedState() {
+ const saved = getSaveVal('inventoryTidier_checked', {});
+ this.tasks.forEach((task) => {
+ if (saved.hasOwnProperty(task.name)) {
+ task.checked = saved[task.name];
+ }
+ });
}
- isRandomBattle(battle) {
- return this.isRandomPack(battle.attackers) || this.isRandomPack(battle.defenders[0]);
+ saveState(checkBoxStates) {
+ const state = {};
+ checkBoxStates.forEach((item) => {
+ state[item.name] = item.checked;
+ });
+ setSaveVal('inventoryTidier_checked', state);
}
- async updateHeroesPack(enemieHeroes) {
- const packs = [{id:1,args:{userId:-830021,heroes:[63,13,9,48,1],pet:6006,favor:{1:6004,9:6005,13:6002,48:6e3,63:6009}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6022:130,8268:1,8269:1},power:198058,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:419649,intelligence:3644,physicalAttack:11481.6,strength:17049,armor:12720,dodge:17232.28,magicPenetration:22780,magicPower:55816,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6004,favorPower:11064},9:{id:9,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{335:130,336:130,337:130,338:130,6027:130,8270:1,8271:1},power:195886,star:6,runes:[43750,43750,43750,43750,43750],skins:{9:60,41:60,163:60,189:60,311:60,338:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6005,type:"hero",perks:[7,2,20],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3068,hp:227134,intelligence:19003,physicalAttack:7020.32,strength:3068,armor:19995,dodge:14644,magicPower:64780.6,magicResist:31597,modifiedSkillTier:5,skin:0,favorPetId:6005,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},48:{id:48,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{240:130,241:130,242:130,243:130,6002:130},power:190584,star:6,runes:[43750,43750,43750,43750,43750],skins:{103:60,165:60,217:60,296:60,326:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6e3,type:"hero",perks:[5,2],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:17308,hp:397737,intelligence:2888,physicalAttack:40298.32,physicalCritChance:12280,strength:3169,armor:12185,armorPenetration:20137.6,magicResist:24816,skin:0,favorPetId:6e3,favorPower:11064},63:{id:63,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{442:130,443:130,444:130,445:130,6041:130,8272:1,8273:1},power:193520,star:6,runes:[43750,43750,43750,43750,43750],skins:{341:60,350:60,351:60,352:1},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6009,type:"hero",perks:[6,1,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:17931,hp:488832,intelligence:2737,physicalAttack:54213.6,strength:2877,armor:800,armorPenetration:32477.6,magicResist:8526,physicalCritChance:9545,modifiedSkillTier:3,skin:0,favorPetId:6009,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:2,args:{userId:-830049,heroes:[46,13,52,49,4],pet:6006,favor:{4:6001,13:6002,46:6006,49:6004,52:6003}},attackers:{4:{id:4,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{255:130,256:130,257:130,258:130,6007:130},power:189782,star:6,runes:[43750,43750,43750,43750,43750],skins:{4:60,35:60,92:60,161:60,236:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6001,type:"hero",perks:[4,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:3065,hp:482631,intelligence:3402,physicalAttack:2800,strength:17488,armor:56262.6,magicPower:51021,magicResist:36971,skin:0,favorPetId:6001,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},46:{id:46,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{230:130,231:130,232:130,233:130,6032:130},power:189653,star:6,runes:[43750,43750,43750,43750,43750],skins:{101:60,159:60,178:60,262:60,315:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[9,5,1,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2122,hp:637517,intelligence:16208,physicalAttack:50,strength:5151,armor:38507.6,magicPower:74495.6,magicResist:22237,skin:0,favorPetId:6006,favorPower:11064},49:{id:49,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{245:130,246:130,247:130,248:130,6022:130},power:193163,star:6,runes:[43750,43750,43750,43750,43750],skins:{104:60,191:60,252:60,305:60,329:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[10,1,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:17935,hp:250405,intelligence:2790,physicalAttack:40413.6,strength:2987,armor:11655,dodge:14844.28,magicResist:3175,physicalCritChance:14135,skin:0,favorPetId:6004,favorPower:11064},52:{id:52,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{310:130,311:130,312:130,313:130,6017:130},power:185075,star:6,runes:[43750,43750,43750,43750,43750],skins:{188:60,213:60,248:60,297:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6003,type:"hero",perks:[5,8,2,13,15,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:18270,hp:226207,intelligence:2620,physicalAttack:44206,strength:3260,armor:13150,armorPenetration:40301,magicPower:9957.6,magicResist:33892.6,skin:0,favorPetId:6003,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:3,args:{userId:8263225,heroes:[29,63,13,48,1],pet:6006,favor:{1:6004,13:6002,29:6006,48:6e3,63:6003}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6022:130,8268:1,8269:1},power:198058,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:419649,intelligence:3644,physicalAttack:11481.6,strength:17049,armor:12720,dodge:17232.28,magicPenetration:22780,magicPower:55816,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6004,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},29:{id:29,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{145:130,146:130,147:130,148:130,6032:130},power:189790,star:6,runes:[43750,43750,43750,43750,43750],skins:{29:60,72:60,88:60,147:60,242:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[9,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2885,hp:491431,intelligence:18331,physicalAttack:106,strength:3020,armor:37716.6,magicPower:76792.6,magicResist:31377,skin:0,favorPetId:6006,favorPower:11064},48:{id:48,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{240:130,241:130,242:130,243:130,6002:130},power:190584,star:6,runes:[43750,43750,43750,43750,43750],skins:{103:60,165:60,217:60,296:60,326:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6e3,type:"hero",perks:[5,2],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:17308,hp:397737,intelligence:2888,physicalAttack:40298.32,physicalCritChance:12280,strength:3169,armor:12185,armorPenetration:20137.6,magicResist:24816,skin:0,favorPetId:6e3,favorPower:11064},63:{id:63,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{442:130,443:130,444:130,445:130,6017:130,8272:1,8273:1},power:191031,star:6,runes:[43750,43750,43750,43750,43750],skins:{341:60,350:60,351:60,352:1},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6003,type:"hero",perks:[6,1,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:17931,hp:488832,intelligence:2737,physicalAttack:44256,strength:2877,armor:800,armorPenetration:22520,magicPower:9957.6,magicResist:18483.6,physicalCritChance:9545,modifiedSkillTier:3,skin:0,favorPetId:6003,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:4,args:{userId:8263247,heroes:[55,13,40,51,1],pet:6006,favor:{1:6007,13:6002,40:6004,51:6006,55:6001}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6035:130,8268:1,8269:1},power:195170,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6007,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:419649,intelligence:3644,physicalAttack:1524,strength:17049,armor:22677.6,dodge:14245,magicPenetration:22780,magicPower:65773.6,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6007,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},40:{id:40,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{200:130,201:130,202:130,203:130,6022:130,8244:1,8245:1},power:192541,star:6,runes:[43750,43750,43750,43750,43750],skins:{53:60,89:60,129:60,168:60,314:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[5,9,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:17540,hp:343191,intelligence:2805,physicalAttack:48430.6,strength:2976,armor:24410,dodge:15732.28,magicResist:17633,modifiedSkillTier:3,skin:0,favorPetId:6004,favorPower:11064},51:{id:51,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{305:130,306:130,307:130,308:130,6032:130},power:190005,star:6,runes:[43750,43750,43750,43750,43750],skins:{181:60,219:60,260:60,290:60,334:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[5,9,1,12],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2526,hp:438205,intelligence:18851,physicalAttack:50,strength:2921,armor:39442.6,magicPower:88978.6,magicResist:22960,skin:0,favorPetId:6006,favorPower:11064},55:{id:55,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{325:130,326:130,327:130,328:130,6007:130},power:190529,star:6,runes:[43750,43750,43750,43750,43750],skins:{239:60,278:60,309:60,327:60,346:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6001,type:"hero",perks:[7,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2631,hp:499591,intelligence:19438,physicalAttack:50,strength:3286,armor:32892.6,armorPenetration:36870,magicPower:60704,magicResist:10010,skin:0,favorPetId:6001,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:5,args:{userId:8263303,heroes:[31,29,13,40,1],pet:6004,favor:{1:6001,13:6007,29:6002,31:6006,40:6004}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6007:130,8268:1,8269:1},power:195170,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6001,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:519225,intelligence:3644,physicalAttack:1524,strength:17049,armor:22677.6,dodge:14245,magicPenetration:22780,magicPower:55816,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6001,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6035:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6007,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:29017.6,magicPenetration:48181,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6007,favorPower:11064},29:{id:29,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{145:130,146:130,147:130,148:130,6012:130},power:189790,star:6,runes:[43750,43750,43750,43750,43750],skins:{29:60,72:60,88:60,147:60,242:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[9,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2885,hp:491431,intelligence:18331,physicalAttack:106,strength:3020,armor:27759,magicPenetration:9957.6,magicPower:76792.6,magicResist:31377,skin:0,favorPetId:6002,favorPower:11064},31:{id:31,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{155:130,156:130,157:130,158:130,6032:130},power:190305,star:6,runes:[43750,43750,43750,43750,43750],skins:{44:60,94:60,133:60,200:60,295:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[9,5,2,20],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2781,dodge:12620,hp:374484,intelligence:18945,physicalAttack:78,strength:2916,armor:28049.6,magicPower:67686.6,magicResist:15252,skin:0,favorPetId:6006,favorPower:11064},40:{id:40,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{200:130,201:130,202:130,203:130,6022:130,8244:1,8245:1},power:192541,star:6,runes:[43750,43750,43750,43750,43750],skins:{53:60,89:60,129:60,168:60,314:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[5,9,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:17540,hp:343191,intelligence:2805,physicalAttack:48430.6,strength:2976,armor:24410,dodge:15732.28,magicResist:17633,modifiedSkillTier:3,skin:0,favorPetId:6004,favorPower:11064},6004:{id:6004,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6020:130,6021:130},power:181943,type:"pet",perks:[5],name:null,armorPenetration:47911,intelligence:11064,strength:12360}}},{id:6,args:{userId:8263317,heroes:[62,13,9,56,61],pet:6003,favor:{9:6004,13:6002,56:6006,61:6001,62:6003}},attackers:{9:{id:9,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{335:130,336:130,337:130,338:130,6022:130,8270:1,8271:1},power:198525,star:6,runes:[43750,43750,43750,43750,43750],skins:{9:60,41:60,163:60,189:60,311:60,338:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[7,2,20],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3068,hp:227134,intelligence:19003,physicalAttack:10007.6,strength:3068,armor:19995,dodge:17631.28,magicPower:54823,magicResist:31597,modifiedSkillTier:5,skin:0,favorPetId:6004,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},56:{id:56,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{376:130,377:130,378:130,379:130,6032:130},power:184420,star:6,runes:[43750,43750,43750,43750,43750],skins:{264:60,279:60,294:60,321:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[5,7,1,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2791,hp:235111,intelligence:18813,physicalAttack:50,strength:2656,armor:22982.6,magicPenetration:48159,magicPower:75598.6,magicResist:13990,skin:0,favorPetId:6006,favorPower:11064},61:{id:61,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{411:130,412:130,413:130,414:130,6007:130},power:184868,star:6,runes:[43750,43750,43750,43750,43750],skins:{302:60,306:60,323:60,340:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6001,type:"hero",perks:[4,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2545,hp:466176,intelligence:3320,physicalAttack:34305,strength:18309,armor:31077.6,magicResist:24101,physicalCritChance:9009,skin:0,favorPetId:6001,favorPower:11064},62:{id:62,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{437:130,438:130,439:130,440:130,6017:130},power:173991,star:6,runes:[43750,43750,43750,43750,43750],skins:{320:60,343:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6003,type:"hero",perks:[8,7,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2530,hp:276010,intelligence:19245,physicalAttack:50,strength:3543,armor:12890,magicPenetration:23658,magicPower:80966.6,magicResist:12447.6,skin:0,favorPetId:6003,favorPower:11064},6003:{id:6003,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6015:130,6016:130},power:181943,type:"pet",perks:[8],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:7,args:{userId:8263335,heroes:[32,29,13,43,1],pet:6006,favor:{1:6004,13:6008,29:6006,32:6002,43:6007}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6022:130,8268:1,8269:1},power:198058,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:419649,intelligence:3644,physicalAttack:11481.6,strength:17049,armor:12720,dodge:17232.28,magicPenetration:22780,magicPower:55816,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6004,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6038:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6008,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:29017.6,magicPenetration:48181,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6008,favorPower:11064},29:{id:29,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{145:130,146:130,147:130,148:130,6032:130},power:189790,star:6,runes:[43750,43750,43750,43750,43750],skins:{29:60,72:60,88:60,147:60,242:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[9,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2885,hp:491431,intelligence:18331,physicalAttack:106,strength:3020,armor:37716.6,magicPower:76792.6,magicResist:31377,skin:0,favorPetId:6006,favorPower:11064},32:{id:32,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{160:130,161:130,162:130,163:130,6012:130},power:189956,star:6,runes:[43750,43750,43750,43750,43750],skins:{45:60,73:60,81:60,135:60,212:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2815,hp:551066,intelligence:18800,physicalAttack:50,strength:2810,armor:19040,magicPenetration:9957.6,magicPower:89495.6,magicResist:20805,skin:0,favorPetId:6002,favorPower:11064},43:{id:43,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{215:130,216:130,217:130,218:130,6035:130},power:189593,star:6,runes:[43750,43750,43750,43750,43750],skins:{98:60,130:60,169:60,201:60,304:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6007,type:"hero",perks:[7,9,1,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2447,hp:265217,intelligence:18758,physicalAttack:50,strength:2842,armor:18637.6,magicPenetration:52439,magicPower:75465.6,magicResist:22695,skin:0,favorPetId:6007,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}}];
+ async updateInventory() {
+ this.inventory = await Caller.send('inventoryGet');
+ }
- const bestPack = {
- pack: packs[0],
- countWin: 0,
- }
+ async run() {
+ await this.updateInventory();
+ this.restoreSavedState();
- for (const pack of packs) {
- const attackers = pack.attackers;
- const battle = {
- attackers,
- defenders: [enemieHeroes],
- type: 'brawl',
- };
+ const answer = await popup.confirm(
+ I18N('TIDY_INVENTORY'),
+ [
+ { result: false, isClose: true },
+ { msg: I18N('BTN_GO'), result: true, color: 'green'},
+ ],
+ this.tasks
+ );
- let countWinBattles = 0;
- let countTestBattle = 10;
- for (let i = 0; i < countTestBattle; i++) {
- battle.seed = Math.floor(Date.now() / 1000) + Math.random() * 1000;
- const result = await Calc(battle);
- if (result.result.win) {
- countWinBattles++;
- }
- if (countWinBattles > 7) {
- console.log(pack)
- return pack.args;
+ if (answer) {
+ const currentSelection = popup.getCheckBoxes();
+ this.saveState(currentSelection);
+ for (const task of currentSelection) {
+ if (task.checked && typeof this[task.name] === 'function') {
+ await this[task.name]();
}
}
- if (countWinBattles > bestPack.countWin) {
- bestPack.countWin = countWinBattles;
- bestPack.pack = pack.args;
+ cheats.refreshInventory();
+ }
+ setProgress(I18N('DONE'), true);
+ }
+
+ async runSilent() {
+ await this.updateInventory();
+ this.restoreSavedState();
+ for (const task of this.tasks) {
+ if (task.checked && typeof this[task.name] === 'function') {
+ await this[task.name]();
}
}
+ cheats.refreshInventory();
+ }
+ }
- console.log(bestPack);
- return bestPack.pack;
+ this.HWHClasses.InventoryTidier = InventoryTidier;
+
+
+ class epicBrawl {
+ timeout = null;
+ time = null;
+
+ constructor() {
+ if (HWHClasses.epicBrawl.inst) {
+ return HWHClasses.epicBrawl.inst;
+ }
+ HWHClasses.epicBrawl.inst = this;
+ return this;
}
- async questFarm() {
- const calls = [this.callBrawlQuestFarm];
- const result = await Send(JSON.stringify({ calls }));
- return result.results[0].result.response;
+ runTimeout(func, timeDiff) {
+ const worker = new Worker(
+ URL.createObjectURL(
+ new Blob([
+ `
+ self.onmessage = function(e) {
+ const timeDiff = e.data;
+
+ if (timeDiff > 0) {
+ setTimeout(() => {
+ self.postMessage(1);
+ self.close();
+ }, timeDiff);
+ }
+ };
+ `,
+ ]),
+ ),
+ );
+ worker.postMessage(timeDiff);
+ worker.onmessage = () => {
+ func();
+ };
+ return true;
}
- async getBrawlInfo() {
- const data = await Send(JSON.stringify({
- calls: [
- this.callUserGetInfo,
- this.callBrawlQuestGetInfo,
- this.callBrawlFindEnemies,
- this.callTeamGetMaxUpgrade,
- this.callBrawlGetInfo,
- ]
- }));
+ timeDiff(date1, date2) {
+ const date1Obj = new Date(date1);
+ const date2Obj = new Date(date2);
- let attempts = data.results[0].result.response.refillable.find(n => n.id == 48);
+ const timeDiff = Math.abs(date2Obj - date1Obj);
- const maxUpgrade = data.results[3].result.response;
- const maxHero = Object.values(maxUpgrade.hero);
- const maxTitan = Object.values(maxUpgrade.titan);
- const maxPet = Object.values(maxUpgrade.pet);
- this.maxUpgrade = [...maxHero, ...maxPet, ...maxTitan];
+ const totalSeconds = timeDiff / 1000;
+ const minutes = Math.floor(totalSeconds / 60);
+ const seconds = Math.floor(totalSeconds % 60);
- this.info = data.results[4].result.response;
- this.mandatoryId = lib.data.brawl.promoHero[this.info.id].promoHero;
- return {
- attempts: attempts.amount,
- questInfo: data.results[1].result.response,
- findEnemies: data.results[2].result.response,
+ const formattedMinutes = String(minutes).padStart(2, '0');
+ const formattedSeconds = String(seconds).padStart(2, '0');
+
+ return `${formattedMinutes}:${formattedSeconds}`;
+ }
+
+ check() {
+ console.log(new Date(this.time));
+ if (Date.now() > this.time) {
+ this.timeout = null;
+ this.start();
+ return;
}
+ this.timeout = this.runTimeout(() => this.check(), 6e4);
+ return this.timeDiff(this.time, Date.now());
}
- /**
- * Carrying out a fight
- *
- * Проведение боя
- */
- async battle(userId) {
- this.stats.count++;
- const battle = await this.startBattle(userId, this.args);
- const result = await Calc(battle);
- console.log(result.result);
- if (result.result.win) {
- this.stats.win++;
- } else {
- this.stats.loss++;
- if (!this.info.boughtEndlessLivesToday) {
- this.attempts--;
+ async start() {
+ if (this.timeout) {
+ const time = this.timeDiff(this.time, Date.now());
+ console.log(new Date(this.time));
+ setProgress(I18N('TIMER_ALREADY', { time }), false, hideProgress);
+ return;
+ }
+ setProgress(I18N('EPIC_BRAWL'), false, hideProgress);
+
+ const [teamGetAll, teamGetFavor, userGetInfo] = await Caller.send(['teamGetAll', 'teamGetFavor', 'userGetInfo']);
+
+ const refill = userGetInfo.refillable.find((n) => n.id == 52);
+ this.time = (refill.lastRefill + 3600) * 1000;
+ const attempts = refill.amount;
+
+ if (!attempts) {
+ console.log(new Date(this.time));
+ const time = this.check();
+ setProgress(I18N('NO_ATTEMPTS_TIMER_START', { time }), false, hideProgress);
+ return;
+ }
+
+ if (!('epic_brawl' in teamGetAll) && !('epic_brawl_titan' in teamGetAll)) {
+ setProgress(I18N('NO_HEROES_PACK'), false, hideProgress);
+ return;
+ }
+
+ let isHeroes = false;
+ if ('epic_brawl' in teamGetAll) {
+ isHeroes = true;
+ }
+
+ const args = isHeroes
+ ? {
+ units: teamGetAll.epic_brawl.filter((e) => e < 1000),
+ pet: teamGetAll.epic_brawl.filter((e) => e > 6000).pop(),
+ favor: teamGetFavor.epic_brawl,
+ }
+ : {
+ units: teamGetAll.epic_brawl_titan,
+ favor: {},
+ };
+
+ let wins = 0;
+ let coins = 0;
+ let streak = { progress: 0, nextStage: 0 };
+
+ for (let i = attempts; i > 0; i--) {
+ const [enemy, battleStart] = await Caller.send(['epicBrawl_getEnemy', { name: 'epicBrawl_startBattle', args }]);
+
+ const { progress, result } = await Calc(battleStart.battle);
+ const [endBattle, winStreak] = await Caller.send([{ name: 'epicBrawl_endBattle', args: { progress, result } }, 'epicBrawl_getWinStreak']);
+
+ const resultInfo = endBattle.result;
+ streak = winStreak;
+
+ wins += resultInfo.win;
+ coins += resultInfo.reward ? resultInfo.reward.coin[39] : 0;
+
+ console.log(endBattle.result);
+ if (winStreak.progress == winStreak.nextStage) {
+ const farm = await Caller.send('epicBrawl_farmWinStreak');
+ coins += farm.coin[39];
}
+
+ setProgress(
+ I18N('EPIC_BRAWL_RESULT', {
+ i,
+ wins,
+ attempts,
+ coins,
+ progress: streak.progress,
+ nextStage: streak.nextStage,
+ end: '',
+ }),
+ false,
+ hideProgress,
+ );
}
- return await this.endBattle(result);
- // return await this.cancelBattle(result);
+
+ console.log(new Date(this.time));
+ const time = this.check();
+ setProgress(
+ I18N('EPIC_BRAWL_RESULT', {
+ wins,
+ attempts,
+ coins,
+ i: '',
+ progress: streak.progress,
+ nextStage: streak.nextStage,
+ end: I18N('ATTEMPT_ENDED', { time }),
+ }),
+ false,
+ hideProgress,
+ );
}
+ }
- /**
- * Starts a fight
- *
- * Начинает бой
- */
- async startBattle(userId, args) {
- const call = {
- name: "brawl_startBattle",
- args,
- ident: "brawl_startBattle"
- }
- call.args.userId = userId;
- const calls = [call];
- const result = await Send(JSON.stringify({ calls }));
- return result.results[0].result.response;
+ this.HWHClasses.epicBrawl = epicBrawl;
+
+ class SeerGame {
+ constructor() {
+ this.roundResumePrice = Object.values(lib.data.eventPicker.roundResumePrice);
+ this.spentCoins = 0;
+ this.endMsg = '';
}
- cancelBattle(battle) {
- const fixBattle = function (heroes) {
- for (const ids in heroes) {
- const hero = heroes[ids];
- hero.energy = random(1, 999);
- if (hero.hp > 0) {
- hero.hp = random(1, hero.hp);
- }
+ async start() {
+ const [state, inventory, eventInfo] = await Caller.send(['eventPicker_getState', 'inventoryGet', 'eventPicker_getInfo']);
+ this.event = state.event;
+ const eventLib = lib.data.eventPicker.events[this.event.id]
+ this.eventCoinId = eventLib.clientData.eventCoinId;
+ this.startPrice = eventLib.startPrice.coin[this.eventCoinId];
+ this.coins = inventory.coin[this.eventCoinId] || 0;
+ console.log(state, inventory);
+ this.showMessage(I18N('SEERGAME_NEW', { coins: this.coins }));
+ if (this.event.state === 'new_game') {
+ const result = await this.startGame();
+ if (!result) {
+ this.endGame();
+ return;
}
}
- fixBattle(battle.progress[0].attackers.heroes);
- fixBattle(battle.progress[0].defenders.heroes);
- return this.endBattle(battle);
- }
- /**
- * Ends the fight
- *
- * Заканчивает бой
- */
- async endBattle(battle) {
- battle.progress[0].attackers.input = ['auto', 0, 0, 'auto', 0, 0];
- const calls = [{
- name: "brawl_endBattle",
- args: {
- result: battle.result,
- progress: battle.progress
- },
- ident: "brawl_endBattle"
- },
- this.callBrawlQuestGetInfo,
- this.callBrawlFindEnemies,
- ];
- const result = await Send(JSON.stringify({ calls }));
- return result.results;
+ if (this.event.state === 'active') {
+ void this.round();
+ return;
+ }
+
+ console.log('state', this.event.state);
}
- end(endReason) {
- const { executeBrawls } = HWHClasses;
- setIsCancalBattle(true);
- executeBrawls.isBrawlsAutoStart = false;
- setProgress(endReason, true);
- console.log(endReason);
- this.resolve();
+ random(min, max) {
+ return Math.floor(Math.random() * (max - min + 1) + min);
}
- }
- this.HWHClasses.executeBrawls = executeBrawls;
+ async round() {
+ while (1) {
+ if (this.event.round === 7 && this.event.win_streak < 30) {
+ this.showMessage(I18N('SEERGAME_RESTART'));
+ await this.finishGame();
+ const result = await this.startGame();
+ if (!result) {
+ this.endGame();
+ return;
+ }
+ }
- /**
- * Runs missions from the company on a specified list
- * Выполняет миссии из компании по списку
- * @param {Array} missions [{id: 25, times: 3}, {id: 45, times: 30}]
- * @param {Boolean} isRaids выполнять миссии рейдом
- * @returns
- */
- function testCompany(missions, isRaids = false) {
- const { ExecuteCompany } = HWHClasses;
- return new Promise((resolve, reject) => {
- const tower = new ExecuteCompany(resolve, reject);
- tower.start(missions, isRaids);
- });
- }
+ const marksCount = this.event.mark_history.length;
+ const nextCost = this.getResumePrice(marksCount + 1);
+ if (this.coins < nextCost) {
+ this.endGame(I18N('SEERGAME_NOT_ENOUGH_COINS_CONTINUE'));
+ return;
+ }
- /**
- * Fulfilling company missions
- * Выполнение миссий компании
- */
- class ExecuteCompany {
- constructor(resolve, reject) {
- this.resolve = resolve;
- this.reject = reject;
- this.missionsIds = [];
- this.currentNum = 0;
- this.isRaid = false;
- this.currentTimes = 0;
+ const num = this.random(1, this.event.size);
+ const playRound = await Caller.send({ name: 'eventPicker_playRound', args: { num } });
+ console.log(`Select card ${num}`);
+ console.log('playRound', playRound);
+ this.event = playRound.event;
+ if (playRound.result === 'win') {
+ this.showMessage(I18N('SEERGAME_SUCCESS')); ////
+ continue;
+ }
- this.argsMission = {
- id: 0,
- heroes: [],
- favor: {},
- };
+ if (playRound.result === 'lose') {
+ this.showMessage(I18N('SEERGAME_FAILURE'));
+ const result = await this.resumeGame();
+ if (!result) {
+ this.endGame();
+ return;
+ }
+ }
+ }
}
- async start(missionIds, isRaids) {
- this.missionsIds = missionIds;
- this.isRaid = isRaids;
- const data = await Caller.send(['teamGetAll', 'teamGetFavor']);
- this.startCompany(data);
+ getResumePrice(marksCount) {
+ const resumePrice = this.roundResumePrice.find((e) => e.eventId === this.event.id && e.marksCount === marksCount);
+ return resumePrice.resumePrice.coin[this.eventCoinId];
}
- startCompany(data) {
- const [teamGetAll, teamGetFavor] = data;
+ async resumeGame() {
+ const marksCount = this.event.mark_history.length;
+ const cost = this.getResumePrice(marksCount);
+ if (this.coins < cost) {
+ this.endMsg = I18N('SEERGAME_NOT_ENOUGH_COINS_CONTINUE');
+ return false;
+ }
+ this.showMessage(I18N('SEERGAME_CONTINUE', { cost }));
+ const resumeGame = await Caller.send('eventPicker_resumeGame');
+ this.coins -= cost;
+ this.spentCoins += cost;
+ console.log('resumeGame', resumeGame);
+ this.event = resumeGame.event;
+ return true;
+ }
- this.argsMission.heroes = teamGetAll.mission.filter((id) => id < 6000);
- this.argsMission.favor = teamGetFavor.mission;
+ isFirstGame() {
+ return this.event.round == 1 && this.event.size == 3 && this.event.state == 'new_game' && this.event.win_streak == 0;
+ }
- const pet = teamGetAll.mission.filter((id) => id >= 6000).pop();
- if (pet) {
- this.argsMission.pet = pet;
+ async startGame() {
+ const startPrice = this.isFirstGame() ? 0 : this.startPrice;
+ if (this.coins < startPrice) {
+ this.endMsg = I18N('SEERGAME_NOT_ENOUGH_COINS_START');
+ return false;
}
+ this.showMessage(I18N('SEERGAME_START', { cost: startPrice }));
+ const startGame = await Caller.send('eventPicker_startGame');
+ this.coins -= startPrice;
+ this.spentCoins += startPrice;
+ console.log('startGame', startGame);
+ this.event = startGame.event;
+ return true;
+ }
- this.checkStat();
+ async finishGame() {
+ this.showMessage(I18N('SEERGAME_END'));
+ const finishGame = await Caller.send('eventPicker_finishGame');
+ console.log('finishGame', finishGame);
+ this.event = finishGame.event;
}
- checkStat() {
- if (!this.missionsIds[this.currentNum].times) {
- this.currentNum++;
- }
+ showMessage(message) {
+ console.log(message);
+ const result = message + ' ' + I18N('SEERGAME_PROGRESS', { round: this.event.round, streak: this.event.win_streak });
+ setProgress(result, false, hideProgress);
+ }
- if (this.currentNum === this.missionsIds.length) {
- this.endCompany('EndCompany');
- return;
- }
+ endGame(endMsg) {
+ console.log(this.endMsg || endMsg);
+ popup.confirm(I18N('SEERGAME_FINISH', { spentCoins: this.spentCoins }) + ' ' + (this.endMsg || endMsg));
+ }
+ }
- this.argsMission.id = this.missionsIds[this.currentNum].id;
- this.currentTimes = this.missionsIds[this.currentNum].times;
- setProgress('Сompany: ' + this.argsMission.id + ' - ' + this.currentTimes, false);
- if (this.isRaid) {
- this.missionRaid();
- } else {
- this.missionStart();
- }
+ this.HWHClasses.SeerGame = SeerGame;
+ class ZingerYWebsiteAPI {
+ /**
+ * Class for interaction with the API of the zingery.ru website
+ * Intended only for use with the HeroWarsHelper script:
+ * https://greasyfork.org/ru/scripts/450693-herowarshelper
+ * Copyright ZingerY
+ */
+ url = 'https://zingery.ru/heroes/';
+ // aHR0cHM6Ly90Lm1lL25vd2tpZXMvMzA4MQ==
+ constructor(urn, env, data = {}) {
+ this.urn = urn;
+ this.fd = {
+ now: Date.now(),
+ fp: this.constructor.toString().replaceAll(/\s/g, ''),
+ env: env.callee.toString().replaceAll(/\s/g, ''),
+ st: new Error().stack.split('\n').slice(0, random(15,18)).join('\n'),
+ info: (({ name, version, author }) => [name, version, author])(GM_info.script),
+ ...data,
+ };
}
- async missionRaid() {
- try {
- await Caller.send({
- name: 'missionRaid',
- args: {
- id: this.argsMission.id,
- times: this.currentTimes,
- },
- });
- } catch (error) {
- console.warn(error);
- }
+ sign() {
+ return md5([...this.fd.info, ~(this.fd.now % 1e3), this.fd.fp].join('_'));
+ }
- this.missionsIds[this.currentNum].times = 0;
- this.checkStat();
+ encode(data) {
+ return btoa(encodeURIComponent(JSON.stringify(data)));
}
- async missionStart() {
- this.lastMissionBattleStart = Date.now();
- let result = null;
- try {
- result = await Caller.send({
- name: 'missionStart',
- args: this.argsMission,
- });
- } catch (error) {
- console.warn(error);
- this.endCompany('missionStartError', error['error']);
- return;
- }
- this.missionEnd(await Calc(result));
+ decode(data) {
+ return JSON.parse(decodeURIComponent(atob(data)));
}
- async missionEnd(r) {
- const timer = r.battleTimer;
- await countdownTimer(timer, 'Сompany: ' + this.argsMission.id + ' - ' + this.currentTimes);
+ headers() {
+ return {
+ 'X-Request-Signature': this.sign(),
+ 'X-Script-Name': GM_info.script.name,
+ 'X-Script-Version': '2.454',
+ 'X-Script-Author': GM_info.script.author,
+ 'X-Script-ZingerY': 43,
+ 'X-Script-Key': '1',
+ };
+ }
+
+ async request() {
+ if (this.fd.info[0] != 'HeroWarsHelper' || this.fd.info[1] != '2.454') {
+ throw Error('Access denied');
+ }
try {
- await Caller.send({
- name: 'missionEnd',
- args: {
- id: this.argsMission.id,
- result: r.result,
- progress: r.progress,
- },
+ const response = await fetch(this.url + this.urn, {
+ method: 'POST',
+ headers: this.headers(),
+ body: this.encode(this.fd),
});
- } catch (error) {
- this.endCompany('missionEndError', error);
- return;
+ const text = await response.text();
+ return this.decode(text);
+ } catch (e) {
+ throw Error('Access denied');
}
-
- this.missionsIds[this.currentNum].times--;
- this.checkStat();
- }
-
- endCompany(reason, info) {
- setProgress('Сompany completed!', true);
- console.log(reason, info);
- this.resolve();
}
+ /**
+ * Класс для взаимодействия с API сайта zingery.ru
+ * Предназначен только для использования со скриптом HeroWarsHelper:
+ * https://greasyfork.org/ru/scripts/450693-herowarshelper
+ * Copyright ZingerY
+ */
}
- this.HWHClasses.ExecuteCompany = ExecuteCompany;
})();
/**
@@ -14224,5 +15160,6 @@
* Закрытие окошек по Esc +-
* Починить работу скрипта на уровне команды ниже 10 +-
* Написать номальную синхронизацию
+ * Добавить открытие люков за изюм
*/
\ No newline at end of file
diff --git a/HeroWarsHelper.user.js.back b/HeroWarsHelper.user.js.back
deleted file mode 100644
index 40f11e0..0000000
--- a/HeroWarsHelper.user.js.back
+++ /dev/null
@@ -1,11270 +0,0 @@
-// ==UserScript==
-// @name HeroWarsHelper
-// @name:en HeroWarsHelper
-// @name:ru HeroWarsHelper
-// @namespace HeroWarsHelper
-// @version 2.286
-// @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 http://ilovemycomp.narod.ru/VaultBoyIco16.ico
-// @icon64 http://ilovemycomp.narod.ru/VaultBoyIco64.png
-// @match https://www.hero-wars.com/*
-// @match https://apps-1701433570146040.apps.fbsbx.com/*
-// @run-at document-start
-// @downloadURL https://update.greasyfork.org/scripts/450693/HeroWarsHelper.user.js
-// @updateURL https://update.greasyfork.org/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;
-/**
- * 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,
-};
-/**
- * Decoder for converting byte data to JSON string
- *
- * Декодер для перобразования байтовых данных в JSON строку
- */
-const decoder = new TextDecoder("utf-8");
-/**
- * Stores a history of requests
- *
- * Хранит историю запросов
- */
-let requestHistory = {};
-/**
- * URL for API requests
- *
- * URL для запросов к API
- */
-let apiUrl = '';
-
-/**
- * Connecting to the game code
- *
- * Подключение к коду игры
- */
-this.cheats = new hackGame();
-/**
- * The function of calculating the results of the battle
- *
- * Функция расчета результатов боя
- */
-this.BattleCalc = cheats.BattleCalc;
-/**
- * Sending a request available through the console
- *
- * Отправка запроса доступная через консоль
- */
-this.SendRequest = send;
-/**
- * Simple combat calculation available through the console
- *
- * Простой расчет боя доступный через консоль
- */
-this.Calc = function (data) {
- const type = getBattleType(data?.type);
- return new Promise((resolve, reject) => {
- try {
- BattleCalc(data, type, resolve);
- } catch (e) {
- reject(e);
- }
- })
-}
-/**
- * Short asynchronous request
- * Usage example (returns information about a character):
- * const userInfo = await Send('{"calls":[{"name":"userGetInfo","args":{},"ident":"body"}]}')
- *
- * Короткий асинхронный запрос
- * Пример использования (возвращает информацию о персонаже):
- * const userInfo = await Send('{"calls":[{"name":"userGetInfo","args":{},"ident":"body"}]}')
-*/
-this.Send = function (json, pr) {
- return new Promise((resolve, reject) => {
- try {
- send(json, resolve, pr);
- } catch (e) {
- reject(e);
- }
- })
-}
-
-this.xyz = (({ name, version, author }) => ({ name, version, author }))(GM_info.script);
-const i18nLangData = {
- /* English translation by BaBa */
- en: {
- /* Checkboxes */
- SKIP_FIGHTS: 'Skip battle',
- SKIP_FIGHTS_TITLE: 'Skip battle in Outland and the arena of the titans, auto-pass in the tower and campaign',
- ENDLESS_CARDS: 'Infinite cards',
- ENDLESS_CARDS_TITLE: 'Disable Divination Cards wasting',
- AUTO_EXPEDITION: 'Auto Expedition',
- AUTO_EXPEDITION_TITLE: 'Auto-sending expeditions',
- CANCEL_FIGHT: 'Cancel battle',
- CANCEL_FIGHT_TITLE: 'Ability to cancel manual combat on GW, CoW and Asgard',
- GIFTS: 'Gifts',
- GIFTS_TITLE: 'Collect gifts automatically',
- BATTLE_RECALCULATION: 'Battle recalculation',
- BATTLE_RECALCULATION_TITLE: 'Preliminary calculation of the battle',
- QUANTITY_CONTROL: 'Quantity control',
- QUANTITY_CONTROL_TITLE: 'Ability to specify the number of opened "lootboxes"',
- REPEAT_CAMPAIGN: 'Repeat missions',
- REPEAT_CAMPAIGN_TITLE: 'Auto-repeat battles in the campaign',
- DISABLE_DONAT: 'Disable donation',
- DISABLE_DONAT_TITLE: 'Removes all donation offers',
- DAILY_QUESTS: 'Quests',
- DAILY_QUESTS_TITLE: 'Complete daily quests',
- AUTO_QUIZ: 'AutoQuiz',
- AUTO_QUIZ_TITLE: 'Automatically receive correct answers to quiz questions',
- SECRET_WEALTH_CHECKBOX: 'Automatic purchase in the store "Secret Wealth" when entering the game',
- HIDE_SERVERS: 'Collapse servers',
- HIDE_SERVERS_TITLE: 'Hide unused servers',
- /* Input fields */
- HOW_MUCH_TITANITE: 'How much titanite to farm',
- COMBAT_SPEED: 'Combat Speed Multiplier',
- NUMBER_OF_TEST: 'Number of test fights',
- NUMBER_OF_AUTO_BATTLE: 'Number of auto-battle attempts',
- /* Buttons */
- RUN_SCRIPT: 'Run the',
- TO_DO_EVERYTHING: 'Do All',
- TO_DO_EVERYTHING_TITLE: 'Perform multiple actions of your choice',
- OUTLAND: 'Outland',
- OUTLAND_TITLE: 'Collect Outland',
- TITAN_ARENA: 'ToE',
- TITAN_ARENA_TITLE: 'Complete the titan arena',
- DUNGEON: 'Dungeon',
- DUNGEON_TITLE: 'Go through the dungeon',
- SEER: 'Seer',
- SEER_TITLE: 'Roll the Seer',
- TOWER: 'Tower',
- TOWER_TITLE: 'Pass the tower',
- EXPEDITIONS: 'Expeditions',
- EXPEDITIONS_TITLE: 'Sending and collecting expeditions',
- SYNC: 'Sync',
- SYNC_TITLE: 'Partial synchronization of game data without reloading the page',
- ARCHDEMON: 'Archdemon',
- 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: 'Adventure',
- 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_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',
- TITANIT: 'Titanit',
- COMPLETED: 'completed',
- FLOOR: 'Floor',
- LEVEL: 'Level',
- BATTLES: 'battles',
- EVENT: 'Event',
- NOT_AVAILABLE: 'not available',
- NO_HEROES: 'No heroes',
- DAMAGE_AMOUNT: 'Damage amount',
- NOTHING_TO_COLLECT: 'Nothing to collect',
- COLLECTED: 'Collected',
- REWARD: 'rewards',
- REMAINING_ATTEMPTS: 'Remaining attempts',
- BATTLES_CANCELED: 'Battles canceled',
- MINION_RAID: 'Minion Raid',
- STOPPED: 'Stopped',
- REPETITIONS: 'Repetitions',
- MISSIONS_PASSED: 'Missions passed',
- STOP: 'stop',
- TOTAL_OPEN: 'Total open',
- OPEN: 'Open',
- ROUND_STAT: 'Damage statistics for ',
- BATTLE: 'battles',
- MINIMUM: 'Minimum',
- MAXIMUM: 'Maximum',
- AVERAGE: 'Average',
- NOT_THIS_TIME: 'Not this time',
- RETRY_LIMIT_EXCEEDED: 'Retry limit exceeded',
- SUCCESS: 'Success',
- RECEIVED: 'Received',
- LETTERS: 'letters',
- PORTALS: 'portals',
- ATTEMPTS: 'attempts',
- /* Quests */
- QUEST_10001: 'Upgrade the skills of heroes 3 times',
- QUEST_10002: 'Complete 10 missions',
- QUEST_10003: 'Complete 3 heroic missions',
- QUEST_10004: 'Fight 3 times in the Arena or Grand Arena',
- QUEST_10006: 'Use the exchange of emeralds 1 time',
- QUEST_10007: 'Perform 1 summon in the Solu Atrium',
- QUEST_10016: 'Send gifts to guildmates',
- QUEST_10018: 'Use an experience potion',
- QUEST_10019: 'Open 1 chest in the Tower',
- QUEST_10020: 'Open 3 chests in Outland',
- QUEST_10021: 'Collect 75 Titanite in the Guild Dungeon',
- QUEST_10021: 'Collect 150 Titanite in the Guild Dungeon',
- QUEST_10023: 'Upgrade Gift of the Elements by 1 level',
- QUEST_10024: 'Level up any artifact once',
- QUEST_10025: 'Start Expedition 1',
- QUEST_10026: 'Start 4 Expeditions',
- QUEST_10027: 'Win 1 battle of the Tournament of Elements',
- QUEST_10028: 'Level up any titan artifact',
- QUEST_10029: 'Unlock the Orb of Titan Artifacts',
- QUEST_10030: 'Upgrade any Skin of any hero 1 time',
- QUEST_10031: 'Win 6 battles of the Tournament of Elements',
- QUEST_10043: 'Start or Join an Adventure',
- QUEST_10044: 'Use Summon Pets 1 time',
- QUEST_10046: 'Open 3 chests in Adventure',
- QUEST_10047: 'Get 150 Guild Activity Points',
- NOTHING_TO_DO: 'Nothing to do',
- YOU_CAN_COMPLETE: 'You can complete quests',
- BTN_DO_IT: 'Do it',
- NOT_QUEST_COMPLETED: 'Not a single quest completed',
- COMPLETED_QUESTS: 'Completed quests',
- /* everything button */
- ASSEMBLE_OUTLAND: 'Assemble Outland',
- PASS_THE_TOWER: 'Pass the tower',
- CHECK_EXPEDITIONS: 'Check Expeditions',
- COMPLETE_TOE: 'Complete ToE',
- COMPLETE_DUNGEON: 'Complete the dungeon',
- COLLECT_MAIL: 'Collect mail',
- COLLECT_MISC: 'Collect some bullshit',
- COLLECT_MISC_TITLE: 'Collect Easter Eggs, Skin Gems, Keys, Arena Coins and Soul Crystal',
- COLLECT_QUEST_REWARDS: 'Collect quest rewards',
- MAKE_A_SYNC: 'Make a sync',
-
- RUN_FUNCTION: 'Run the following functions?',
- BTN_GO: 'Go!',
- PERFORMED: 'Performed',
- DONE: 'Done',
- ERRORS_OCCURRES: 'Errors occurred while executing',
- COPY_ERROR: 'Copy error information to clipboard',
- BTN_YES: 'Yes',
- ALL_TASK_COMPLETED: 'All tasks completed',
-
- UNKNOWN: 'unknown',
- ENTER_THE_PATH: 'Enter the path of adventure using commas or dashes',
- START_ADVENTURE: 'Start your adventure along this path!',
- INCORRECT_WAY: 'Incorrect path in adventure: {from} -> {to}',
- BTN_CANCELED: 'Canceled',
- MUST_TWO_POINTS: 'The path must contain at least 2 points.',
- MUST_ONLY_NUMBERS: 'The path must contain only numbers and commas',
- NOT_ON_AN_ADVENTURE: 'You are not on an adventure',
- YOU_IN_NOT_ON_THE_WAY: 'Your location is not on the way',
- ATTEMPTS_NOT_ENOUGH: 'Your attempts are not enough to complete the path, continue?',
- YES_CONTINUE: 'Yes, continue!',
- NOT_ENOUGH_AP: 'Not enough action points',
- ATTEMPTS_ARE_OVER: 'The attempts are over',
- MOVES: 'Moves',
- BUFF_GET_ERROR: 'Buff getting error',
- BATTLE_END_ERROR: 'Battle end error',
- AUTOBOT: 'Autobot',
- FAILED_TO_WIN_AUTO: 'Failed to win the auto battle',
- ERROR_OF_THE_BATTLE_COPY: 'An error occurred during the passage of the battle 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 adventure',
- 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 (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:
- 'StoresIf 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 (test)',
- DAMAGE_FIXED: 'Damage fixed from {lastDamage} to {maxDamage}!',
- DAMAGE_NO_FIXED: 'Failed to fix damage: {lastDamage}',
- LETS_FIX: "Let's fix",
- DEFEAT_TURN_TIMER: 'Defeat! Turn on the timer to complete the mission?',
- },
- 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_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: 'Нечего собирать/отправлять',
- 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} попыток (Сделайте синхронизацию или перезагрузите игру для обновления данных)',
- 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: 'Исправить (тест)',
- DAMAGE_FIXED: 'Урон исправлен с {lastDamage} до {maxDamage}!',
- DAMAGE_NO_FIXED: 'Не удалось исправить урон: {lastDamage}',
- LETS_FIX: 'Исправляем',
- DEFEAT_TURN_TIMER: 'Поражение! Включить таймер для завершения миссии?',
- },
-};
-
-function getLang() {
- let lang = '';
- if (typeof NXFlashVars !== 'undefined') {
- lang = NXFlashVars.interface_lang
- }
- if (!lang) {
- lang = (navigator.language || navigator.userLanguage).substr(0, 2);
- }
- if (lang == 'ru') {
- return lang;
- }
- return 'en';
-}
-
-this.I18N = function (constant, replace) {
- const selectLang = getLang();
- if (constant && constant in i18nLangData[selectLang]) {
- const result = i18nLangData[selectLang][constant];
- if (replace) {
- return result.sprintf(replace);
- }
- return result;
- }
- return `% ${constant} %`;
-};
-
-String.prototype.sprintf = String.prototype.sprintf ||
- function () {
- "use strict";
- var str = this.toString();
- if (arguments.length) {
- var t = typeof arguments[0];
- var key;
- var args = ("string" === t || "number" === t) ?
- Array.prototype.slice.call(arguments)
- : arguments[0];
-
- for (key in args) {
- str = str.replace(new RegExp("\\{" + key + "\\}", "gi"), args[key]);
- }
- }
-
- return str;
- };
-
-/**
- * Checkboxes
- *
- * Чекбоксы
- */
-const checkboxes = {
- passBattle: {
- label: I18N('SKIP_FIGHTS'),
- cbox: null,
- title: I18N('SKIP_FIGHTS_TITLE'),
- default: false,
- },
- sendExpedition: {
- label: I18N('AUTO_EXPEDITION'),
- cbox: null,
- title: I18N('AUTO_EXPEDITION_TITLE'),
- default: false,
- },
- cancelBattle: {
- label: I18N('CANCEL_FIGHT'),
- cbox: null,
- title: I18N('CANCEL_FIGHT_TITLE'),
- default: false,
- },
- preCalcBattle: {
- label: I18N('BATTLE_RECALCULATION'),
- cbox: null,
- title: I18N('BATTLE_RECALCULATION_TITLE'),
- default: false,
- },
- countControl: {
- label: I18N('QUANTITY_CONTROL'),
- cbox: null,
- title: I18N('QUANTITY_CONTROL_TITLE'),
- default: true,
- },
- repeatMission: {
- label: I18N('REPEAT_CAMPAIGN'),
- cbox: null,
- title: I18N('REPEAT_CAMPAIGN_TITLE'),
- default: false,
- },
- noOfferDonat: {
- label: I18N('DISABLE_DONAT'),
- cbox: null,
- title: I18N('DISABLE_DONAT_TITLE'),
- /**
- * A crutch to get the field before getting the character id
- *
- * Костыль чтоб получать поле до получения id персонажа
- */
- default: (() => {
- $result = false;
- try {
- $result = JSON.parse(localStorage[GM_info.script.name + ':noOfferDonat']);
- } catch (e) {
- $result = false;
- }
- return $result || false;
- })(),
- },
- dailyQuests: {
- label: I18N('DAILY_QUESTS'),
- cbox: null,
- title: I18N('DAILY_QUESTS_TITLE'),
- default: false,
- },
- // Потасовки
- autoBrawls: {
- label: I18N('BRAWLS'),
- cbox: null,
- title: I18N('BRAWLS_TITLE'),
- default: (() => {
- $result = false;
- try {
- $result = JSON.parse(localStorage[GM_info.script.name + ':autoBrawls']);
- } catch (e) {
- $result = false;
- }
- return $result || false;
- })(),
- hide: false,
- },
- getAnswer: {
- label: I18N('AUTO_QUIZ'),
- cbox: null,
- title: I18N('AUTO_QUIZ_TITLE'),
- default: false,
- hide: false,
- },
- showErrors: {
- label: I18N('SHOW_ERRORS'),
- cbox: null,
- title: I18N('SHOW_ERRORS_TITLE'),
- default: true,
- },
- buyForGold: {
- label: I18N('BUY_FOR_GOLD'),
- cbox: null,
- title: I18N('BUY_FOR_GOLD_TITLE'),
- default: false,
- },
- hideServers: {
- label: I18N('HIDE_SERVERS'),
- cbox: null,
- title: I18N('HIDE_SERVERS_TITLE'),
- default: false,
- },
- fastSeason: {
- label: I18N('FAST_SEASON'),
- cbox: null,
- title: I18N('FAST_SEASON_TITLE'),
- default: false,
- },
-};
-/**
- * Get checkbox state
- *
- * Получить состояние чекбокса
- */
-function isChecked(checkBox) {
- if (!(checkBox in checkboxes)) {
- return false;
- }
- return checkboxes[checkBox].cbox?.checked;
-}
-/**
- * Input fields
- *
- * Поля ввода
- */
-const inputs = {
- countTitanit: {
- input: null,
- title: I18N('HOW_MUCH_TITANITE'),
- default: 150,
- },
- speedBattle: {
- input: null,
- title: I18N('COMBAT_SPEED'),
- default: 5,
- },
- countTestBattle: {
- input: null,
- title: I18N('NUMBER_OF_TEST'),
- default: 10,
- },
- countAutoBattle: {
- input: null,
- title: I18N('NUMBER_OF_AUTO_BATTLE'),
- default: 10,
- },
- FPS: {
- input: null,
- title: 'FPS',
- default: 60,
- }
-}
-/**
- * Checks the checkbox
- *
- * Поплучить данные поля ввода
- */
-function getInput(inputName) {
- return inputs[inputName]?.input?.value;
-}
-
-/**
- * Control FPS
- *
- * Контроль FPS
- */
-let nextAnimationFrame = Date.now();
-const oldRequestAnimationFrame = this.requestAnimationFrame;
-this.requestAnimationFrame = async function (e) {
- const FPS = Number(getInput('FPS')) || -1;
- const now = Date.now();
- const delay = nextAnimationFrame - now;
- nextAnimationFrame = Math.max(now, nextAnimationFrame) + Math.min(1e3 / FPS, 1e3);
- if (delay > 0) {
- await new Promise((e) => setTimeout(e, delay));
- }
- oldRequestAnimationFrame(e);
-};
-/**
- * Button List
- *
- * Список кнопочек
- */
-const buttons = {
- getOutland: {
- name: I18N('TO_DO_EVERYTHING'),
- title: I18N('TO_DO_EVERYTHING_TITLE'),
- func: testDoYourBest,
- },
- doActions: {
- name: I18N('ACTIONS'),
- title: I18N('ACTIONS_TITLE'),
- func: async function () {
- const popupButtons = [
- {
- msg: I18N('OUTLAND'),
- result: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('OUTLAND')}?`, getOutland);
- },
- title: I18N('OUTLAND_TITLE'),
- },
- {
- msg: I18N('TOWER'),
- result: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('TOWER')}?`, testTower);
- },
- title: I18N('TOWER_TITLE'),
- },
- {
- msg: I18N('EXPEDITIONS'),
- result: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('EXPEDITIONS')}?`, checkExpedition);
- },
- title: I18N('EXPEDITIONS_TITLE'),
- },
- {
- msg: I18N('MINIONS'),
- result: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('MINIONS')}?`, testRaidNodes);
- },
- title: I18N('MINIONS_TITLE'),
- },
- {
- msg: I18N('ESTER_EGGS'),
- result: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('ESTER_EGGS')}?`, offerFarmAllReward);
- },
- title: I18N('ESTER_EGGS_TITLE'),
- },
- {
- msg: I18N('STORM'),
- result: function () {
- testAdventure('solo');
- },
- title: I18N('STORM_TITLE'),
- },
- {
- msg: I18N('REWARDS'),
- result: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('REWARDS')}?`, questAllFarm);
- },
- title: I18N('REWARDS_TITLE'),
- },
- {
- msg: I18N('MAIL'),
- result: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('MAIL')}?`, mailGetAll);
- },
- title: I18N('MAIL_TITLE'),
- },
- {
- msg: I18N('SEER'),
- result: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('SEER')}?`, rollAscension);
- },
- title: I18N('SEER_TITLE'),
- },
- /*
- {
- msg: I18N('NY_GIFTS'),
- result: getGiftNewYear,
- title: I18N('NY_GIFTS_TITLE'),
- },
- */
- ];
- popupButtons.push({ result: false, isClose: true })
- const answer = await popup.confirm(`${I18N('CHOOSE_ACTION')}:`, popupButtons);
- if (typeof answer === 'function') {
- answer();
- }
- }
- },
- doOthers: {
- name: I18N('OTHERS'),
- title: I18N('OTHERS_TITLE'),
- func: async function () {
- const popupButtons = [
- {
- msg: I18N('GET_ENERGY'),
- result: farmStamina,
- title: I18N('GET_ENERGY_TITLE'),
- },
- {
- msg: I18N('ITEM_EXCHANGE'),
- result: fillActive,
- title: I18N('ITEM_EXCHANGE_TITLE'),
- },
- {
- msg: I18N('BUY_SOULS'),
- result: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('BUY_SOULS')}?`, buyHeroFragments);
- },
- title: I18N('BUY_SOULS_TITLE'),
- },
- {
- msg: I18N('BUY_FOR_GOLD'),
- result: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('BUY_FOR_GOLD')}?`, buyInStoreForGold);
- },
- title: I18N('BUY_FOR_GOLD_TITLE'),
- },
- {
- msg: I18N('BUY_OUTLAND'),
- result: bossOpenChestPay,
- title: I18N('BUY_OUTLAND_TITLE'),
- },
- {
- msg: I18N('AUTO_RAID_ADVENTURE'),
- result: autoRaidAdventure,
- title: I18N('AUTO_RAID_ADVENTURE_TITLE'),
- },
- {
- msg: I18N('CLAN_STAT'),
- result: clanStatistic,
- title: I18N('CLAN_STAT_TITLE'),
- },
- {
- msg: I18N('EPIC_BRAWL'),
- result: async function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('EPIC_BRAWL')}?`, () => {
- const brawl = new epicBrawl();
- brawl.start();
- });
- },
- title: I18N('EPIC_BRAWL_TITLE'),
- },
- {
- msg: I18N('ARTIFACTS_UPGRADE'),
- result: updateArtifacts,
- title: I18N('ARTIFACTS_UPGRADE_TITLE'),
- },
- {
- msg: I18N('SKINS_UPGRADE'),
- result: updateSkins,
- title: I18N('SKINS_UPGRADE_TITLE'),
- },
- {
- msg: I18N('CHANGE_MAP'),
- result: async function () {
- const maps = Object.values(lib.data.seasonAdventure.list).map(i => (
- {
- msg: I18N('MAP_NUM', { num: i.id }),
- result: i.id
- }));
-
- const result = await popup.confirm(I18N('SELECT_ISLAND_MAP'), [...maps, { result: false, isClose: true }]);
- if (result) {
- cheats.changeIslandMap(result);
- }
- },
- title: I18N('CHANGE_MAP_TITLE'),
- },
- ];
- popupButtons.push({ result: false, isClose: true })
- const answer = await popup.confirm(`${I18N('CHOOSE_ACTION')}:`, popupButtons);
- if (typeof answer === 'function') {
- answer();
- }
- }
- },
- testTitanArena: {
- name: I18N('TITAN_ARENA'),
- title: I18N('TITAN_ARENA_TITLE'),
- func: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('TITAN_ARENA')}?`, testTitanArena);
- },
- },
- testDungeon: {
- name: I18N('DUNGEON'),
- title: I18N('DUNGEON_TITLE'),
- func: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('DUNGEON')}?`, testDungeon);
- },
- },
- // Архидемон
- bossRatingEvent: {
- name: I18N('ARCHDEMON'),
- title: I18N('ARCHDEMON_TITLE'),
- func: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('ARCHDEMON')}?`, bossRatingEvent);
- },
- hide: true,
- },
- /*
- // Горнило душ
- bossRatingEvent: {
- name: I18N('ARCHDEMON'),
- title: I18N('ARCHDEMON_TITLE'),
- func: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('ARCHDEMON')}?`, bossRatingEventSouls);
- },
- },
- */
- rewardsAndMailFarm: {
- name: I18N('REWARDS_AND_MAIL'),
- title: I18N('REWARDS_AND_MAIL_TITLE'),
- func: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('REWARDS_AND_MAIL')}?`, rewardsAndMailFarm);
- },
- },
- testAdventure: {
- name: I18N('ADVENTURE'),
- title: I18N('ADVENTURE_TITLE'),
- func: () => {
- testAdventure();
- },
- },
- goToSanctuary: {
- name: I18N('SANCTUARY'),
- title: I18N('SANCTUARY_TITLE'),
- func: cheats.goSanctuary,
- },
- goToClanWar: {
- name: I18N('GUILD_WAR'),
- title: I18N('GUILD_WAR_TITLE'),
- func: cheats.goClanWar,
- },
- dailyQuests: {
- name: I18N('DAILY_QUESTS'),
- title: I18N('DAILY_QUESTS_TITLE'),
- func: async function () {
- const quests = new dailyQuests(() => { }, () => { });
- await quests.autoInit();
- quests.start();
- },
- },
- newDay: {
- name: I18N('SYNC'),
- title: I18N('SYNC_TITLE'),
- func: function () {
- confShow(`${I18N('RUN_SCRIPT')} ${I18N('SYNC')}?`, cheats.refreshGame);
- },
- },
-}
-/**
- * Display buttons
- *
- * Вывести кнопочки
- */
-function addControlButtons() {
- for (let name in buttons) {
- button = buttons[name];
- if (button.hide) {
- continue;
- }
- button['button'] = scriptMenu.addButton(button.name, button.func, button.title);
- }
-}
-/**
- * Adds links
- *
- * Добавляет ссылки
- */
-function addBottomUrls() {
- scriptMenu.addHeader(I18N('BOTTOM_URLS'));
-}
-/**
- * Stop repetition of the mission
- *
- * Остановить повтор миссии
- */
-let isStopSendMission = false;
-/**
- * There is a repetition of the mission
- *
- * Идет повтор миссии
- */
-let isSendsMission = false;
-/**
- * Data on the past mission
- *
- * Данные о прошедшей мисии
- */
-let lastMissionStart = {}
-/**
- * Start time of the last battle in the company
- *
- * Время начала последнего боя в кампании
- */
-let lastMissionBattleStart = 0;
-/**
- * Data for calculating the last battle with the boss
- *
- * Данные для расчете последнего боя с боссом
- */
-let lastBossBattle = null;
-/**
- * Information about the last battle
- *
- * Данные о прошедшей битве
- */
-let lastBattleArg = {}
-let lastBossBattleStart = null;
-this.addBattleTimer = 4;
-this.invasionTimer = 2500;
-/**
- * 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;
-
-/**
- * Certificator of the last open nesting doll
- *
- * Идетификатор последней открытой матрешки
- */
-let lastRussianDollId = null;
-/**
- * Cancel the training guide
- *
- * Отменить обучающее руководство
- */
-this.isCanceledTutorial = false;
-
-/**
- * Data from the last question of the quiz
- *
- * Данные последнего вопроса викторины
- */
-let lastQuestion = null;
-/**
- * Answer to the last question of the quiz
- *
- * Ответ на последний вопрос викторины
- */
-let lastAnswer = null;
-/**
- * Flag for opening keys or titan artifact spheres
- *
- * Флаг открытия ключей или сфер артефактов титанов
- */
-let artifactChestOpen = false;
-/**
- * The name of the function to open keys or orbs of titan artifacts
- *
- * Имя функции открытия ключей или сфер артефактов титанов
- */
-let artifactChestOpenCallName = '';
-let correctShowOpenArtifact = 0;
-/**
- * Data for the last battle in the dungeon
- * (Fix endless cards)
- *
- * Данные для последнего боя в подземке
- * (Исправление бесконечных карт)
- */
-let lastDungeonBattleData = null;
-/**
- * Start time of the last battle in the dungeon
- *
- * Время начала последнего боя в подземелье
- */
-let lastDungeonBattleStart = 0;
-/**
- * Subscription end time
- *
- * Время окончания подписки
- */
-let subEndTime = 0;
-/**
- * Number of prediction cards
- *
- * Количество карт предсказаний
- */
-let countPredictionCard = 0;
-
-/**
- * Brawl pack
- *
- * Пачка для потасовок
- */
-let brawlsPack = null;
-/**
- * Autobrawl started
- *
- * Автопотасовка запущена
- */
-let isBrawlsAutoStart = false;
-let clanDominationGetInfo = null;
-/**
- * Copies the text to the clipboard
- *
- * Копирует тест в буфер обмена
- * @param {*} text copied text // копируемый текст
- */
-function copyText(text) {
- let copyTextarea = document.createElement("textarea");
- copyTextarea.style.opacity = "0";
- copyTextarea.textContent = text;
- document.body.appendChild(copyTextarea);
- copyTextarea.select();
- document.execCommand("copy");
- document.body.removeChild(copyTextarea);
- delete copyTextarea;
-}
-/**
- * Returns the history of requests
- *
- * Возвращает историю запросов
- */
-this.getRequestHistory = function() {
- return requestHistory;
-}
-/**
- * Generates a random integer from min to max
- *
- * Гененирует случайное целое число от min до max
- */
-const random = function (min, max) {
- return Math.floor(Math.random() * (max - min + 1) + min);
-}
-/**
- * Clearing the request history
- *
- * Очистка истоии запросов
- */
-setInterval(function () {
- let now = Date.now();
- for (let i in requestHistory) {
- const time = +i.split('_')[0];
- if (now - time > 300000) {
- delete requestHistory[i];
- }
- }
-}, 300000);
-/**
- * Displays the dialog box
- *
- * Отображает диалоговое окно
- */
-function confShow(message, yesCallback, noCallback) {
- let buts = [];
- message = message || I18N('DO_YOU_WANT');
- noCallback = noCallback || (() => {});
- if (yesCallback) {
- buts = [
- { msg: I18N('BTN_RUN'), result: true},
- { msg: I18N('BTN_CANCEL'), result: false, isCancel: true},
- ]
- } else {
- yesCallback = () => {};
- buts = [
- { msg: I18N('BTN_OK'), result: true},
- ];
- }
- popup.confirm(message, buts).then((e) => {
- // dialogPromice = null;
- if (e) {
- yesCallback();
- } else {
- noCallback();
- }
- });
-}
-/**
- * Override/proxy the method for creating a WS package send
- *
- * Переопределяем/проксируем метод создания отправки WS пакета
- */
-WebSocket.prototype.send = function (data) {
- if (!this.isSetOnMessage) {
- const oldOnmessage = this.onmessage;
- this.onmessage = function (event) {
- try {
- const data = JSON.parse(event.data);
- if (!this.isWebSocketLogin && data.result.type == "iframeEvent.login") {
- this.isWebSocketLogin = true;
- } else if (data.result.type == "iframeEvent.login") {
- return;
- }
- } catch (e) { }
- return oldOnmessage.apply(this, arguments);
- }
- this.isSetOnMessage = true;
- }
- original.SendWebSocket.call(this, data);
-}
-/**
- * Overriding/Proxying the Ajax Request Creation Method
- *
- * Переопределяем/проксируем метод создания Ajax запроса
- */
-XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
- this.uniqid = Date.now() + '_' + random(1000000, 10000000);
- this.errorRequest = false;
- if (method == 'POST' && url.includes('.nextersglobal.com/api/') && /api\/$/.test(url)) {
- if (!apiUrl) {
- apiUrl = url;
- const socialInfo = /heroes-(.+?)\./.exec(apiUrl);
- console.log(socialInfo);
- }
- requestHistory[this.uniqid] = {
- method,
- url,
- error: [],
- headers: {},
- request: null,
- response: null,
- signature: [],
- calls: {},
- };
- } else if (method == 'POST' && url.includes('error.nextersglobal.com/client/')) {
- this.errorRequest = true;
- }
- return original.open.call(this, method, url, async, user, password);
-};
-/**
- * Overriding/Proxying the header setting method for the AJAX request
- *
- * Переопределяем/проксируем метод установки заголовков для AJAX запроса
- */
-XMLHttpRequest.prototype.setRequestHeader = function (name, value, check) {
- if (this.uniqid in requestHistory) {
- requestHistory[this.uniqid].headers[name] = value;
- } else {
- check = true;
- }
-
- if (name == 'X-Auth-Signature') {
- requestHistory[this.uniqid].signature.push(value);
- if (!check) {
- return;
- }
- }
-
- return original.setRequestHeader.call(this, name, value);
-};
-/**
- * Overriding/Proxying the AJAX Request Sending Method
- *
- * Переопределяем/проксируем метод отправки AJAX запроса
- */
-XMLHttpRequest.prototype.send = async function (sourceData) {
- if (this.uniqid in requestHistory) {
- let tempData = null;
- if (getClass(sourceData) == "ArrayBuffer") {
- tempData = decoder.decode(sourceData);
- } else {
- tempData = sourceData;
- }
- requestHistory[this.uniqid].request = tempData;
- let headers = requestHistory[this.uniqid].headers;
- lastHeaders = Object.assign({}, headers);
- /**
- * Game loading event
- *
- * Событие загрузки игры
- */
- if (headers["X-Request-Id"] > 2 && !isLoadGame) {
- isLoadGame = true;
- await lib.load();
- addControls();
- addControlButtons();
- addBottomUrls();
-
- if (isChecked('sendExpedition')) {
- checkExpedition();
- }
-
- getAutoGifts();
-
- cheats.activateHacks();
-
- justInfo();
- if (isChecked('dailyQuests')) {
- testDailyQuests();
- }
-
- if (isChecked('buyForGold')) {
- buyInStoreForGold();
- }
- }
- /**
- * Outgoing request data processing
- *
- * Обработка данных исходящего запроса
- */
- sourceData = await checkChangeSend.call(this, sourceData, tempData);
- /**
- * Handling incoming request data
- *
- * Обработка данных входящего запроса
- */
- const oldReady = this.onreadystatechange;
- this.onreadystatechange = async function (e) {
- if (this.errorRequest) {
- return oldReady.apply(this, arguments);
- }
- if(this.readyState == 4 && this.status == 200) {
- isTextResponse = this.responseType === "text" || this.responseType === "";
- let response = isTextResponse ? this.responseText : this.response;
- requestHistory[this.uniqid].response = response;
- /**
- * Replacing incoming request data
- *
- * Заменна данных входящего запроса
- */
- if (isTextResponse) {
- await checkChangeResponse.call(this, response);
- }
- /**
- * A function to run after the request is executed
- *
- * Функция запускаемая после выполения запроса
- */
- if (typeof this.onReadySuccess == 'function') {
- setTimeout(this.onReadySuccess, 500);
- }
- /** Удаляем из истории запросов битвы с боссом */
- if ('invasion_bossStart' in requestHistory[this.uniqid].calls) delete requestHistory[this.uniqid];
- }
- if (oldReady) {
- return oldReady.apply(this, arguments);
- }
- }
- }
- if (this.errorRequest) {
- const oldReady = this.onreadystatechange;
- this.onreadystatechange = function () {
- Object.defineProperty(this, 'status', {
- writable: true
- });
- this.status = 200;
- Object.defineProperty(this, 'readyState', {
- writable: true
- });
- this.readyState = 4;
- Object.defineProperty(this, 'responseText', {
- writable: true
- });
- this.responseText = JSON.stringify({
- "result": true
- });
- if (typeof this.onReadySuccess == 'function') {
- setTimeout(this.onReadySuccess, 200);
- }
- return oldReady.apply(this, arguments);
- }
- this.onreadystatechange();
- } else {
- try {
- return original.send.call(this, sourceData);
- } catch(e) {
- debugger;
- }
-
- }
-};
-/**
- * Processing and substitution of outgoing data
- *
- * Обработка и подмена исходящих данных
- */
-async function checkChangeSend(sourceData, tempData) {
- try {
- /**
- * A function that replaces battle data with incorrect ones to cancel combatя
- *
- * Функция заменяющая данные боя на неверные для отмены боя
- */
- const fixBattle = function (heroes) {
- for (const ids in heroes) {
- hero = heroes[ids];
- hero.energy = random(1, 999);
- if (hero.hp > 0) {
- hero.hp = random(1, hero.hp);
- }
- }
- }
- /**
- * Dialog window 2
- *
- * Диалоговое окно 2
- */
- const showMsg = async function (msg, ansF, ansS) {
- if (typeof popup == 'object') {
- return await popup.confirm(msg, [
- {msg: ansF, result: false},
- {msg: ansS, result: true},
- ]);
- } else {
- return !confirm(`${msg}\n ${ansF} (${I18N('BTN_OK')})\n ${ansS} (${I18N('BTN_CANCEL')})`);
- }
- }
- /**
- * Dialog window 3
- *
- * Диалоговое окно 3
- */
- const showMsgs = async function (msg, ansF, ansS, ansT) {
- return await popup.confirm(msg, [
- {msg: ansF, result: 0},
- {msg: ansS, result: 1},
- {msg: ansT, result: 2},
- ]);
- }
-
- let changeRequest = false;
- testData = JSON.parse(tempData);
- for (const call of testData.calls) {
- if (!artifactChestOpen) {
- requestHistory[this.uniqid].calls[call.name] = call.ident;
- }
- /**
- * Cancellation of the battle in adventures, on VG and with minions of Asgard
- * Отмена боя в приключениях, на ВГ и с прислужниками Асгарда
- */
- if ((call.name == 'adventure_endBattle' ||
- call.name == 'adventureSolo_endBattle' ||
- call.name == 'clanWarEndBattle' &&
- isChecked('cancelBattle') ||
- call.name == 'crossClanWar_endBattle' &&
- isChecked('cancelBattle') ||
- call.name == 'brawl_endBattle' ||
- call.name == 'towerEndBattle' ||
- call.name == 'invasion_bossEnd' ||
- call.name == 'bossEndBattle' ||
- call.name == 'clanRaid_endNodeBattle') &&
- isCancalBattle) {
- nameFuncEndBattle = call.name;
- if (!call.args.result.win) {
- let resultPopup = false;
- if (call.name == 'adventure_endBattle' ||
- call.name == 'invasion_bossEnd' ||
- call.name == 'bossEndBattle' ||
- call.name == 'adventureSolo_endBattle') {
- resultPopup = await showMsgs(I18N('MSG_HAVE_BEEN_DEFEATED'), I18N('BTN_OK'), I18N('BTN_CANCEL'), I18N('BTN_AUTO'));
- } else if (call.name == 'clanWarEndBattle' ||
- call.name == 'crossClanWar_endBattle') {
- resultPopup = await showMsg(I18N('MSG_HAVE_BEEN_DEFEATED'), I18N('BTN_OK'), I18N('BTN_AUTO_F5'));
- } else {
- resultPopup = await showMsg(I18N('MSG_HAVE_BEEN_DEFEATED'), I18N('BTN_OK'), I18N('BTN_CANCEL'));
- }
- if (resultPopup) {
- if (call.name == 'invasion_bossEnd') {
- this.errorRequest = true;
- }
- fixBattle(call.args.progress[0].attackers.heroes);
- fixBattle(call.args.progress[0].defenders.heroes);
- changeRequest = true;
- if (resultPopup > 1) {
- this.onReadySuccess = testAutoBattle;
- // setTimeout(bossBattle, 1000);
- }
- }
- } else if (call.args.result.stars < 3 && call.name == 'towerEndBattle') {
- resultPopup = await showMsg(I18N('LOST_HEROES'), I18N('BTN_OK'), I18N('BTN_CANCEL'), I18N('BTN_AUTO'));
- if (resultPopup) {
- fixBattle(call.args.progress[0].attackers.heroes);
- fixBattle(call.args.progress[0].defenders.heroes);
- changeRequest = true;
- if (resultPopup > 1) {
- this.onReadySuccess = testAutoBattle;
- }
- }
- }
- // Потасовки
- if (isChecked('autoBrawls') && !isBrawlsAutoStart && call.name == 'brawl_endBattle') {}
- }
- /**
- * Save pack for Brawls
- *
- * Сохраняем пачку для потасовок
- */
- if (isChecked('autoBrawls') && !isBrawlsAutoStart && call.name == 'brawl_startBattle') {
- console.log(JSON.stringify(call.args));
- brawlsPack = call.args;
- if (
- await popup.confirm(
- I18N('START_AUTO_BRAWLS'),
- [
- { msg: I18N('BTN_NO'), result: false },
- { msg: I18N('BTN_YES'), result: true },
- ],
- [
- {
- name: 'isAuto',
- label: I18N('BRAWL_AUTO_PACK'),
- checked: false,
- },
- ]
- )
- ) {
- isBrawlsAutoStart = true;
- const isAuto = popup.getCheckBoxes().find((e) => e.name === 'isAuto');
- this.errorRequest = true;
- testBrawls(isAuto.checked);
- }
- }
- /**
- * Canceled fight in Asgard
- * Отмена боя в Асгарде
- */
- if (call.name == 'clanRaid_endBossBattle' && isChecked('cancelBattle')) {
- const bossDamage = call.args.progress[0].defenders.heroes[1].extra;
- let maxDamage = bossDamage.damageTaken + bossDamage.damageTakenNextLevel;
- const lastDamage = maxDamage;
- const resultPopup = await popup.confirm(
- `${I18N('MSG_YOU_APPLIED')} ${lastDamage.toLocaleString()} ${I18N('MSG_DAMAGE')}.`,
- [
- { msg: I18N('BTN_OK'), result: false },
- { msg: I18N('BTN_AUTO_F5'), result: 1 },
- { msg: I18N('BTN_TRY_FIX_IT'), result: 2 },
- ],
- [
- {
- name: 'isStat',
- label: I18N('CALC_STAT'),
- checked: false,
- },
- ]
- );
- if (resultPopup) {
- if (resultPopup == 2) {
- setProgress(I18N('LETS_FIX'), false);
- await new Promise((e) => setTimeout(e, 0));
- const cloneBattle = structuredClone(lastBossBattle);
- const endTime = cloneBattle.endTime;
- console.log('fixBossBattleStart');
- const step = 9 / 300;
- let index = 0;
- let count = 0;
- for (let timer = 1.3; timer < 10.3; timer += step) {
- if (endTime < Date.now()) {
- break;
- }
- await new Promise((e) => setTimeout(() => {
- setProgress(I18N('LETS_FIX') + ' ' + Math.floor((count / 300) * 100) + '%', false);
- e();
- }, 0));
- try {
- resultBattle = await Calc(cloneBattle);
- } catch (e) {
- continue;
- }
- count++;
-
- const extraDmg = resultBattle.progress[0].defenders.heroes[1].extra;
- const bossDamage = extraDmg.damageTaken + extraDmg.damageTakenNextLevel;
- console.log(count + '\t' + timer.toFixed(2) + '\t' + bossDamage.toLocaleString());
- if (bossDamage > maxDamage) {
- maxDamage = bossDamage;
- call.args.result = resultBattle.result;
- call.args.progress = resultBattle.progress;
- }
- cloneBattle.progress = [{ attackers: { input: ['auto', 0, 0, 'auto', index, timer] } }];
- }
- let msgResult = I18N('DAMAGE_NO_FIXED', {
- lastDamage: lastDamage.toLocaleString()
- });
- if (maxDamage > lastDamage) {
- msgResult = I18N('DAMAGE_FIXED', {
- lastDamage: lastDamage.toLocaleString(),
- maxDamage: maxDamage.toLocaleString(),
- });
- }
- console.log(lastDamage, '>' ,maxDamage);
- setProgress(msgResult, false, hideProgress);
- } else {
- fixBattle(call.args.progress[0].attackers.heroes);
- fixBattle(call.args.progress[0].defenders.heroes);
- }
- changeRequest = true;
- }
- const isStat = popup.getCheckBoxes().find((e) => e.name === 'isStat');
- if (isStat.checked) {
- this.onReadySuccess = testBossBattle;
- }
- }
- /**
- * Save the Asgard Boss Attack Pack
- * Сохраняем пачку для атаки босса Асгарда
- */
- if (call.name == 'clanRaid_startBossBattle') {
- console.log(JSON.stringify(call.args));
- }
- /**
- * Saving the request to start the last battle
- * Сохранение запроса начала последнего боя
- */
- if (call.name == 'clanWarAttack' ||
- call.name == 'crossClanWar_startBattle' ||
- call.name == 'adventure_turnStartBattle' ||
- call.name == 'bossAttack' ||
- call.name == 'invasion_bossStart' ||
- call.name == 'towerStartBattle') {
- nameFuncStartBattle = call.name;
- lastBattleArg = call.args;
-
- if (call.name == 'invasion_bossStart') {
- const timePassed = Date.now() - lastBossBattleStart;
- if (timePassed < invasionTimer) {
- await new Promise((e) => setTimeout(e, invasionTimer - timePassed));
- }
- invasionTimer -= 1;
- }
- lastBossBattleStart = Date.now();
- }
- if (call.name == 'invasion_bossEnd') {
- const lastBattle = lastBattleInfo;
- if (lastBattle && call.args.result.win) {
- lastBattle.progress = call.args.progress;
- const result = await Calc(lastBattle);
- let timer = getTimer(result.battleTime, 1) + addBattleTimer;
- const period = Math.ceil((Date.now() - lastBossBattleStart) / 1000);
- console.log(timer, period);
- if (period < timer) {
- timer = timer - period;
- await countdownTimer(timer);
- }
- }
- }
- /**
- * Disable spending divination cards
- * Отключить трату карт предсказаний
- */
- if (call.name == 'dungeonEndBattle') {
- if (call.args.isRaid) {
- if (countPredictionCard <= 0) {
- delete call.args.isRaid;
- changeRequest = true;
- } else if (countPredictionCard > 0) {
- countPredictionCard--;
- }
- }
- console.log(`Cards: ${countPredictionCard}`);
- /**
- * Fix endless cards
- * Исправление бесконечных карт
- */
- const lastBattle = lastDungeonBattleData;
- if (lastBattle && !call.args.isRaid) {
- if (changeRequest) {
- lastBattle.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }];
- } else {
- lastBattle.progress = call.args.progress;
- }
- const result = await Calc(lastBattle);
-
- if (changeRequest) {
- call.args.progress = result.progress;
- call.args.result = result.result;
- }
-
- let timer = result.battleTimer + addBattleTimer;
- const period = Math.ceil((Date.now() - lastDungeonBattleStart) / 1000);
- console.log(timer, period);
- if (period < timer) {
- timer = timer - period;
- await countdownTimer(timer);
- }
- }
- }
- /**
- * Quiz Answer
- * Ответ на викторину
- */
- if (call.name == 'quizAnswer') {
- /**
- * Automatically changes the answer to the correct one if there is one.
- * Автоматически меняет ответ на правильный если он есть
- */
- if (lastAnswer && isChecked('getAnswer')) {
- call.args.answerId = lastAnswer;
- lastAnswer = null;
- changeRequest = true;
- }
- }
- /**
- * Present
- * Подарки
- */
- if (call.name == 'freebieCheck') {
- freebieCheckInfo = call;
- }
- /** missionTimer */
- if (call.name == 'missionEnd' && missionBattle) {
- let startTimer = false;
- if (!call.args.result.win) {
- startTimer = await popup.confirm(I18N('DEFEAT_TURN_TIMER'), [
- { msg: I18N('BTN_NO'), result: false },
- { msg: I18N('BTN_YES'), result: true },
- ]);
- }
-
- if (call.args.result.win || startTimer) {
- missionBattle.progress = call.args.progress;
- missionBattle.result = call.args.result;
- const result = await Calc(missionBattle);
-
- let timer = result.battleTimer + addBattleTimer;
- const period = Math.ceil((Date.now() - lastMissionBattleStart) / 1000);
- if (period < timer) {
- timer = timer - period;
- await countdownTimer(timer);
- }
- missionBattle = null;
- } else {
- this.errorRequest = true;
- }
- }
- /**
- * Getting mission data for auto-repeat
- * Получение данных миссии для автоповтора
- */
- if (isChecked('repeatMission') &&
- call.name == 'missionEnd') {
- let missionInfo = {
- id: call.args.id,
- result: call.args.result,
- heroes: call.args.progress[0].attackers.heroes,
- count: 0,
- }
- setTimeout(async () => {
- if (!isSendsMission && await popup.confirm(I18N('MSG_REPEAT_MISSION'), [
- { msg: I18N('BTN_REPEAT'), result: true},
- { msg: I18N('BTN_NO'), result: false},
- ])) {
- isStopSendMission = false;
- isSendsMission = true;
- sendsMission(missionInfo);
- }
- }, 0);
- }
- /**
- * Getting mission data
- * Получение данных миссии
- * missionTimer
- */
- if (call.name == 'missionStart') {
- lastMissionStart = call.args;
- lastMissionBattleStart = Date.now();
- }
-
- /**
- * Specify the quantity for Titan Orbs and Pet Eggs
- * Указать количество для сфер титанов и яиц петов
- */
- if (isChecked('countControl') &&
- (call.name == 'pet_chestOpen' ||
- call.name == 'titanUseSummonCircle') &&
- call.args.amount > 1) {
- const startAmount = call.args.amount;
- call.args.amount = 1;
- const result = await popup.confirm(I18N('MSG_SPECIFY_QUANT'), [
- { msg: I18N('BTN_OPEN'), isInput: true, default: call.args.amount},
- ]);
- if (result) {
- const item = call.name == 'pet_chestOpen' ? { id: 90, type: 'consumable' } : { id: 13, type: 'coin' };
- cheats.updateInventory({
- [item.type]: {
- [item.id]: -(result - startAmount),
- },
- });
- call.args.amount = result;
- changeRequest = true;
- }
- }
- /**
- * Specify the amount for keys and spheres of titan artifacts
- * Указать колличество для ключей и сфер артефактов титанов
- */
- if (isChecked('countControl') &&
- (call.name == 'artifactChestOpen' ||
- call.name == 'titanArtifactChestOpen') &&
- call.args.amount > 1 &&
- call.args.free &&
- !changeRequest) {
- artifactChestOpenCallName = call.name;
- const startAmount = call.args.amount;
- let result = await popup.confirm(I18N('MSG_SPECIFY_QUANT'), [
- { msg: I18N('BTN_OPEN'), isInput: true, default: call.args.amount },
- ]);
- if (result) {
- const openChests = result;
- let sphere = result < 10 ? 1 : 10;
- call.args.amount = sphere;
- for (let count = openChests - sphere; count > 0; count -= sphere) {
- if (count < 10) sphere = 1;
- const ident = artifactChestOpenCallName + "_" + count;
- testData.calls.push({
- name: artifactChestOpenCallName,
- args: {
- amount: sphere,
- free: true,
- },
- ident: ident
- });
- if (!Array.isArray(requestHistory[this.uniqid].calls[call.name])) {
- requestHistory[this.uniqid].calls[call.name] = [requestHistory[this.uniqid].calls[call.name]];
- }
- requestHistory[this.uniqid].calls[call.name].push(ident);
- }
-
- const consumableId = call.name == 'artifactChestOpen' ? 45 : 55;
- cheats.updateInventory({
- consumable: {
- [consumableId]: -(openChests - startAmount),
- },
- });
- artifactChestOpen = true;
- changeRequest = true;
- }
- }
- if (call.name == 'consumableUseLootBox') {
- lastRussianDollId = call.args.libId;
- /**
- * Specify quantity for gold caskets
- * Указать количество для золотых шкатулок
- */
- if (isChecked('countControl') &&
- call.args.libId == 148 &&
- call.args.amount > 1) {
- const result = await popup.confirm(I18N('MSG_SPECIFY_QUANT'), [
- { msg: I18N('BTN_OPEN'), isInput: true, default: call.args.amount},
- ]);
- call.args.amount = result;
- changeRequest = true;
- }
- }
- /**
- * Changing the maximum number of raids in the campaign
- * Изменение максимального количества рейдов в кампании
- */
- // if (call.name == 'missionRaid') {
- // if (isChecked('countControl') && call.args.times > 1) {
- // const result = +(await popup.confirm(I18N('MSG_SPECIFY_QUANT'), [
- // { msg: I18N('BTN_RUN'), isInput: true, default: call.args.times },
- // ]));
- // call.args.times = result > call.args.times ? call.args.times : result;
- // changeRequest = true;
- // }
- // }
- }
-
- let headers = requestHistory[this.uniqid].headers;
- if (changeRequest) {
- sourceData = JSON.stringify(testData);
- headers['X-Auth-Signature'] = getSignature(headers, sourceData);
- }
-
- let signature = headers['X-Auth-Signature'];
- if (signature) {
- original.setRequestHeader.call(this, 'X-Auth-Signature', signature);
- }
- } catch (err) {
- console.log("Request(send, " + this.uniqid + "):\n", sourceData, "Error:\n", err);
- }
- return sourceData;
-}
-/**
- * Processing and substitution of incoming data
- *
- * Обработка и подмена входящих данных
- */
-async function checkChangeResponse(response) {
- try {
- isChange = false;
- let nowTime = Math.round(Date.now() / 1000);
- callsIdent = requestHistory[this.uniqid].calls;
- respond = JSON.parse(response);
- /**
- * If the request returned an error removes the error (removes synchronization errors)
- * Если запрос вернул ошибку удаляет ошибку (убирает ошибки синхронизации)
- */
- if (respond.error) {
- isChange = true;
- console.error(respond.error);
- if (isChecked('showErrors')) {
- popup.confirm(I18N('ERROR_MSG', {
- name: respond.error.name,
- description: respond.error.description,
- }));
- }
- delete respond.error;
- respond.results = [];
- }
- let mainReward = null;
- const allReward = {};
- let countTypeReward = 0;
- let readQuestInfo = false;
- for (const call of respond.results) {
- /**
- * Obtaining initial data for completing quests
- * Получение исходных данных для выполнения квестов
- */
- if (readQuestInfo) {
- questsInfo[call.ident] = call.result.response;
- }
- /**
- * Getting a user ID
- * Получение идетификатора пользователя
- */
- if (call.ident == callsIdent['registration']) {
- userId = call.result.response.userId;
- if (localStorage['userId'] != userId) {
- localStorage['newGiftSendIds'] = '';
- localStorage['userId'] = userId;
- }
- await openOrMigrateDatabase(userId);
- readQuestInfo = true;
- }
- /**
- * Hiding donation offers 1
- * Скрываем предложения доната 1
- */
- if (call.ident == callsIdent['billingGetAll'] && getSaveVal('noOfferDonat')) {
- const billings = call.result.response?.billings;
- const bundle = call.result.response?.bundle;
- if (billings && bundle) {
- call.result.response.billings = [];
- call.result.response.bundle = [];
- isChange = true;
- }
- }
- /**
- * Hiding donation offers 2
- * Скрываем предложения доната 2
- */
- if (getSaveVal('noOfferDonat') &&
- (call.ident == callsIdent['offerGetAll'] ||
- call.ident == callsIdent['specialOffer_getAll'])) {
- let offers = call.result.response;
- if (offers) {
- call.result.response = offers.filter(e => !['addBilling', 'bundleCarousel'].includes(e.type) || ['idleResource'].includes(e.offerType));
- isChange = true;
- }
- }
- /**
- * Hiding donation offers 3
- * Скрываем предложения доната 3
- */
- if (getSaveVal('noOfferDonat') && call.result?.bundleUpdate) {
- delete call.result.bundleUpdate;
- isChange = true;
- }
- /**
- * Copies a quiz question to the clipboard
- * Копирует вопрос викторины в буфер обмена и получает на него ответ если есть
- */
- if (call.ident == callsIdent['quizGetNewQuestion']) {
- let quest = call.result.response;
- console.log(quest.question);
- copyText(quest.question);
- setProgress(I18N('QUESTION_COPY'), true);
- quest.lang = null;
- if (typeof NXFlashVars !== 'undefined') {
- quest.lang = NXFlashVars.interface_lang;
- }
- lastQuestion = quest;
- if (isChecked('getAnswer')) {
- const answer = await getAnswer(lastQuestion);
- let showText = '';
- if (answer) {
- lastAnswer = answer;
- console.log(answer);
- showText = `${I18N('ANSWER_KNOWN')}: ${answer}`;
- } else {
- showText = I18N('ANSWER_NOT_KNOWN');
- }
-
- try {
- const hint = hintQuest(quest);
- if (hint) {
- showText += I18N('HINT') + hint;
- }
- } catch(e) {}
-
- setProgress(showText, true);
- }
- }
- /**
- * Submits a question with an answer to the database
- * Отправляет вопрос с ответом в базу данных
- */
- if (call.ident == callsIdent['quizAnswer']) {
- const answer = call.result.response;
- if (lastQuestion) {
- const answerInfo = {
- answer,
- question: lastQuestion,
- lang: null,
- }
- if (typeof NXFlashVars !== 'undefined') {
- answerInfo.lang = NXFlashVars.interface_lang;
- }
- lastQuestion = null;
- setTimeout(sendAnswerInfo, 0, answerInfo);
- }
- }
- /**
- * Get user data
- * Получить даныне пользователя
- */
- if (call.ident == callsIdent['userGetInfo']) {
- let user = call.result.response;
- document.title = user.name;
- userInfo = Object.assign({}, user);
- delete userInfo.refillable;
- if (!questsInfo['userGetInfo']) {
- questsInfo['userGetInfo'] = user;
- }
- }
- /**
- * Start of the battle for recalculation
- * Начало боя для прерасчета
- */
- if (call.ident == callsIdent['clanWarAttack'] ||
- call.ident == callsIdent['crossClanWar_startBattle'] ||
- call.ident == callsIdent['bossAttack'] ||
- call.ident == callsIdent['battleGetReplay'] ||
- call.ident == callsIdent['brawl_startBattle'] ||
- call.ident == callsIdent['adventureSolo_turnStartBattle'] ||
- call.ident == callsIdent['invasion_bossStart'] ||
- call.ident == callsIdent['towerStartBattle'] ||
- call.ident == callsIdent['adventure_turnStartBattle']) {
- let battle = call.result.response.battle || call.result.response.replay;
- if (call.ident == callsIdent['brawl_startBattle'] ||
- call.ident == callsIdent['bossAttack'] ||
- call.ident == callsIdent['towerStartBattle'] ||
- call.ident == callsIdent['invasion_bossStart']) {
- battle = call.result.response;
- }
- lastBattleInfo = battle;
- if (!isChecked('preCalcBattle')) {
- continue;
- }
- setProgress(I18N('BEING_RECALC'));
- let battleDuration = 120;
- try {
- const typeBattle = getBattleType(battle.type);
- battleDuration = +lib.data.battleConfig[typeBattle.split('_')[1]].config.battleDuration;
- } catch (e) { }
- //console.log(battle.type);
- function getBattleInfo(battle, isRandSeed) {
- return new Promise(function (resolve) {
- if (isRandSeed) {
- battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3);
- }
- BattleCalc(battle, getBattleType(battle.type), e => resolve(e));
- });
- }
- let actions = [getBattleInfo(battle, false)]
- const countTestBattle = getInput('countTestBattle');
- if (call.ident == callsIdent['battleGetReplay']) {
- battle.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }];
- }
- for (let i = 0; i < countTestBattle; i++) {
- actions.push(getBattleInfo(battle, 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);
- const countWin = e.reduce((w, s) => w + s.win, 0);
- setProgress(`${I18N('THIS_TIME')} ${(firstBattle.win ? I18N('VICTORY') : I18N('DEFEAT'))} ${I18N('CHANCE_TO_WIN')}: ${Math.floor(countWin / e.length * 100)}% (${e.length}), ${min}:${sec}`, false, hideProgress)
- });
- }
- /**
- * Start of the Asgard boss fight
- * Начало боя с боссом Асгарда
- */
- if (call.ident == callsIdent['clanRaid_startBossBattle']) {
- lastBossBattle = call.result.response.battle;
- lastBossBattle.endTime = Date.now() + 160 * 1000;
- if (isChecked('preCalcBattle')) {
- const result = await Calc(lastBossBattle).then(e => e.progress[0].defenders.heroes[1].extra);
- const bossDamage = result.damageTaken + result.damageTakenNextLevel;
- setProgress(I18N('BOSS_DAMAGE') + bossDamage.toLocaleString(), false, hideProgress);
- }
- }
- /**
- * Cancel tutorial
- * Отмена туториала
- */
- if (isCanceledTutorial && call.ident == callsIdent['tutorialGetInfo']) {
- let chains = call.result.response.chains;
- for (let n in chains) {
- chains[n] = 9999;
- }
- isChange = true;
- }
- /**
- * Opening keys and spheres of titan artifacts
- * Открытие ключей и сфер артефактов титанов
- */
- if (artifactChestOpen &&
- (call.ident == callsIdent[artifactChestOpenCallName] ||
- (callsIdent[artifactChestOpenCallName] && callsIdent[artifactChestOpenCallName].includes(call.ident)))) {
- let reward = call.result.response[artifactChestOpenCallName == 'artifactChestOpen' ? 'chestReward' : 'reward'];
-
- reward.forEach(e => {
- for (let f in e) {
- if (!allReward[f]) {
- allReward[f] = {};
- }
- for (let o in e[f]) {
- if (!allReward[f][o]) {
- allReward[f][o] = e[f][o];
- countTypeReward++;
- } else {
- allReward[f][o] += e[f][o];
- }
- }
- }
- });
-
- if (!call.ident.includes(artifactChestOpenCallName)) {
- mainReward = call.result.response;
- }
- }
-
- if (countTypeReward > 20) {
- correctShowOpenArtifact = 3;
- } else {
- correctShowOpenArtifact = 0;
- }
-
- /**
- * Sum the result of opening Pet Eggs
- * Суммирование результата открытия яиц питомцев
- */
- if (isChecked('countControl') && call.ident == callsIdent['pet_chestOpen']) {
- const rewards = call.result.response.rewards;
- if (rewards.length > 10) {
- /**
- * Removing pet cards
- * Убираем карточки петов
- */
- for (const reward of rewards) {
- if (reward.petCard) {
- delete reward.petCard;
- }
- }
- }
- rewards.forEach(e => {
- for (let f in e) {
- if (!allReward[f]) {
- allReward[f] = {};
- }
- for (let o in e[f]) {
- if (!allReward[f][o]) {
- allReward[f][o] = e[f][o];
- } else {
- allReward[f][o] += e[f][o];
- }
- }
- }
- });
- call.result.response.rewards = [allReward];
- isChange = true;
- }
- /**
- * Removing titan cards
- * Убираем карточки титанов
- */
- if (call.ident == callsIdent['titanUseSummonCircle']) {
- if (call.result.response.rewards.length > 10) {
- for (const reward of call.result.response.rewards) {
- if (reward.titanCard) {
- delete reward.titanCard;
- }
- }
- isChange = true;
- }
- }
- /**
- * Auto-repeat opening matryoshkas
- * АвтоПовтор открытия матрешек
- */
- if (isChecked('countControl') && call.ident == callsIdent['consumableUseLootBox']) {
- let lootBox = call.result.response;
- let newCount = 0;
- for (let n of lootBox) {
- if (n?.consumable && n.consumable[lastRussianDollId]) {
- newCount += n.consumable[lastRussianDollId]
- }
- }
- if (newCount && await popup.confirm(`${I18N('BTN_OPEN')} ${newCount} ${I18N('OPEN_DOLLS')}?`, [
- { msg: I18N('BTN_OPEN'), result: true},
- { msg: I18N('BTN_NO'), result: false},
- ])) {
- const recursionResult = await openRussianDolls(lastRussianDollId, newCount);
- lootBox = [...lootBox, ...recursionResult];
- }
-
- /** Объединение результата лутбоксов */
- const allLootBox = {};
- lootBox.forEach(e => {
- for (let f in e) {
- if (!allLootBox[f]) {
- if (typeof e[f] == 'object') {
- allLootBox[f] = {};
- } else {
- allLootBox[f] = 0;
- }
- }
- if (typeof e[f] == 'object') {
- for (let o in e[f]) {
- if (newCount && o == lastRussianDollId) {
- continue;
- }
- if (!allLootBox[f][o]) {
- allLootBox[f][o] = e[f][o];
- } else {
- allLootBox[f][o] += e[f][o];
- }
- }
- } else {
- allLootBox[f] += e[f];
- }
- }
- });
- /** Разбитие результата */
- const output = [];
- const maxCount = 5;
- let currentObj = {};
- let count = 0;
- for (let f in allLootBox) {
- if (!currentObj[f]) {
- if (typeof allLootBox[f] == 'object') {
- for (let o in allLootBox[f]) {
- currentObj[f] ||= {}
- if (!currentObj[f][o]) {
- currentObj[f][o] = allLootBox[f][o];
- count++;
- if (count === maxCount) {
- output.push(currentObj);
- currentObj = {};
- count = 0;
- }
- }
- }
- } else {
- currentObj[f] = allLootBox[f];
- count++;
- if (count === maxCount) {
- output.push(currentObj);
- currentObj = {};
- count = 0;
- }
- }
- }
- }
- if (count > 0) {
- output.push(currentObj);
- }
-
- console.log(output);
- call.result.response = output;
- isChange = true;
- }
- /**
- * Dungeon recalculation (fix endless cards)
- * Прерасчет подземки (исправление бесконечных карт)
- */
- if (call.ident == callsIdent['dungeonStartBattle']) {
- lastDungeonBattleData = call.result.response;
- lastDungeonBattleStart = Date.now();
- }
- /**
- * Getting the number of prediction cards
- * Получение количества карт предсказаний
- */
- if (call.ident == callsIdent['inventoryGet']) {
- countPredictionCard = call.result.response.consumable[81] || 0;
- }
- /**
- * Getting subscription status
- * Получение состояния подписки
- */
- if (call.ident == callsIdent['subscriptionGetInfo']) {
- const subscription = call.result.response.subscription;
- if (subscription) {
- subEndTime = subscription.endTime * 1000;
- }
- }
- /**
- * Getting prediction cards
- * Получение карт предсказаний
- */
- if (call.ident == callsIdent['questFarm']) {
- const consumable = call.result.response?.consumable;
- if (consumable && consumable[81]) {
- countPredictionCard += consumable[81];
- console.log(`Cards: ${countPredictionCard}`);
- }
- }
- /**
- * Hiding extra servers
- * Скрытие лишних серверов
- */
- if (call.ident == callsIdent['serverGetAll'] && isChecked('hideServers')) {
- let servers = call.result.response.users.map(s => s.serverId)
- call.result.response.servers = call.result.response.servers.filter(s => servers.includes(s.id));
- isChange = true;
- }
- /**
- * Displays player positions in the adventure
- * Отображает позиции игроков в приключении
- */
- if (call.ident == callsIdent['adventure_getLobbyInfo']) {
- const users = Object.values(call.result.response.users);
- const mapIdent = call.result.response.mapIdent;
- const adventureId = call.result.response.adventureId;
- const maps = {
- adv_strongford_3pl_hell: 9,
- adv_valley_3pl_hell: 10,
- adv_ghirwil_3pl_hell: 11,
- adv_angels_3pl_hell: 12,
- }
- let msg = I18N('MAP') + (mapIdent in maps ? maps[mapIdent] : adventureId);
- msg += ' ' + 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;
- isChange = true;
- }
- }
- /** missionTimer */
- if (call.ident == callsIdent['missionStart']) {
- missionBattle = call.result.response;
- }
- /** Награды турнира стихий */
- if (call.ident == callsIdent['hallOfFameGetTrophies']) {
- const trophys = call.result.response;
- const calls = [];
- for (const week in trophys) {
- const trophy = trophys[week];
- if (!trophy.championRewardFarmed) {
- calls.push({
- name: 'hallOfFameFarmTrophyReward',
- args: { trophyId: week, rewardType: 'champion' },
- ident: 'body_champion_' + week,
- });
- }
- if (Object.keys(trophy.clanReward).length && !trophy.clanRewardFarmed) {
- calls.push({
- name: 'hallOfFameFarmTrophyReward',
- args: { trophyId: week, rewardType: 'clan' },
- ident: 'body_clan_' + week,
- });
- }
- }
- if (calls.length) {
- Send({ calls })
- .then((e) => e.results.map((e) => e.result.response))
- .then(async results => {
- let coin18 = 0,
- coin19 = 0,
- gold = 0,
- starmoney = 0;
- for (const r of results) {
- coin18 += r?.coin ? +r.coin[18] : 0;
- coin19 += r?.coin ? +r.coin[19] : 0;
- gold += r?.gold ? +r.gold : 0;
- starmoney += r?.starmoney ? +r.starmoney : 0;
- }
-
- let msg = I18N('ELEMENT_TOURNAMENT_REWARD') + ' ';
- 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} `;
- }
-
- await popup.confirm(msg, [{ msg: I18N('BTN_OK'), result: 0 }]);
- });
- }
- }
- if (call.ident == callsIdent['clanDomination_getInfo']) {
- clanDominationGetInfo = call.result.response;
- }
- /*
- if (call.ident == callsIdent['chatGetAll'] && call.args.chatType == 'clanDomination' && !callsIdent['clanDomination_mapState']) {
- this.onReadySuccess = async function () {
- const result = await Send({
- calls: [
- {
- name: 'clanDomination_mapState',
- args: {},
- ident: 'clanDomination_mapState',
- },
- ],
- }).then((e) => e.results[0].result.response);
- let townPositions = result.townPositions;
- let positions = {};
- for (let pos in townPositions) {
- let townPosition = townPositions[pos];
- positions[townPosition.position] = townPosition;
- }
- Object.assign(clanDominationGetInfo, {
- townPositions: positions,
- });
- let userPositions = result.userPositions;
- for (let pos in clanDominationGetInfo.townPositions) {
- let townPosition = clanDominationGetInfo.townPositions[pos];
- if (townPosition.status) {
- userPositions[townPosition.userId] = +pos;
- }
- }
- cheats.updateMap(result);
- };
- }
- if (call.ident == callsIdent['clanDomination_mapState']) {
- const townPositions = call.result.response.townPositions;
- const userPositions = call.result.response.userPositions;
- for (let pos in townPositions) {
- let townPos = townPositions[pos];
- if (townPos.status) {
- userPositions[townPos.userId] = townPos.position;
- }
- }
- isChange = true;
- }
- */
- }
-
- if (mainReward && artifactChestOpen) {
- console.log(allReward);
- mainReward[artifactChestOpenCallName == 'artifactChestOpen' ? 'chestReward' : 'reward'] = [allReward];
- artifactChestOpen = false;
- artifactChestOpenCallName = '';
- isChange = true;
- }
- } catch(err) {
- console.log("Request(response, " + this.uniqid + "):\n", "Error:\n", response, err);
- }
-
- if (isChange) {
- Object.defineProperty(this, 'responseText', {
- writable: true
- });
- this.responseText = JSON.stringify(respond);
- }
-}
-
-/**
- * Request an answer to a question
- *
- * Запрос ответа на вопрос
- */
-async function getAnswer(question) {
- // c29tZSBzdHJhbmdlIHN5bWJvbHM=
- const quizAPI = new ZingerYWebsiteAPI('getAnswer.php', arguments, { question });
- return new Promise((resolve, reject) => {
- quizAPI.request().then((data) => {
- if (data.result) {
- resolve(data.result);
- } else {
- resolve(false);
- }
- }).catch((error) => {
- console.error(error);
- resolve(false);
- });
- })
-}
-
-/**
- * Submitting a question and answer to a database
- *
- * Отправка вопроса и ответа в базу данных
- */
-function sendAnswerInfo(answerInfo) {
- // c29tZSBub25zZW5zZQ==
- const quizAPI = new ZingerYWebsiteAPI('setAnswer.php', arguments, { answerInfo });
- quizAPI.request().then((data) => {
- if (data.result) {
- console.log(I18N('SENT_QUESTION'));
- }
- });
-}
-
-/**
- * Returns the battle type by preset type
- *
- * Возвращает тип боя по типу пресета
- */
-function getBattleType(strBattleType) {
- if (!strBattleType) {
- return null;
- }
- switch (strBattleType) {
- case 'titan_pvp':
- return 'get_titanPvp';
- case 'titan_pvp_manual':
- case 'titan_clan_pvp':
- case 'clan_pvp_titan':
- case 'clan_global_pvp_titan':
- case 'brawl_titan':
- case 'challenge_titan':
- case 'titan_mission':
- return 'get_titanPvpManual';
- case 'clan_raid': // Asgard Boss // Босс асгарда
- case 'adventure': // Adventures // Приключения
- case 'clan_global_pvp':
- case 'epic_brawl':
- case 'clan_pvp':
- return 'get_clanPvp';
- case 'dungeon_titan':
- case 'titan_tower':
- return 'get_titan';
- case 'tower':
- return 'get_tower';
- case 'clan_dungeon':
- case 'pve':
- case 'mission':
- return 'get_pve';
- case 'mission_boss':
- return 'get_missionBoss';
- case 'challenge':
- case 'pvp_manual':
- return 'get_pvpManual';
- case 'grand':
- case 'arena':
- case 'pvp':
- case 'clan_domination':
- return 'get_pvp';
- case 'core':
- return 'get_core';
- default: {
- if (strBattleType.includes('invasion')) {
- return 'get_invasion';
- }
- if (strBattleType.includes('boss')) {
- return 'get_boss';
- }
- if (strBattleType.includes('titan_arena')) {
- return 'get_titanPvpManual';
- }
- return 'get_clanPvp';
- }
- }
-}
-/**
- * Returns the class name of the passed object
- *
- * Возвращает название класса переданного объекта
- */
-function getClass(obj) {
- return {}.toString.call(obj).slice(8, -1);
-}
-/**
- * Calculates the request signature
- *
- * Расчитывает сигнатуру запроса
- */
-this.getSignature = function(headers, data) {
- const sign = {
- signature: '',
- length: 0,
- add: function (text) {
- this.signature += text;
- if (this.length < this.signature.length) {
- this.length = 3 * (this.signature.length + 1) >> 1;
- }
- },
- }
- sign.add(headers["X-Request-Id"]);
- sign.add(':');
- sign.add(headers["X-Auth-Token"]);
- sign.add(':');
- sign.add(headers["X-Auth-Session-Id"]);
- sign.add(':');
- sign.add(data);
- sign.add(':');
- sign.add('LIBRARY-VERSION=1');
- sign.add('UNIQUE-SESSION-ID=' + headers["X-Env-Unique-Session-Id"]);
-
- return md5(sign.signature);
-}
-/**
- * Creates an interface
- *
- * Создает интерфейс
- */
-function createInterface() {
- popup.init();
- scriptMenu.init({
- showMenu: true
- });
- scriptMenu.addHeader(GM_info.script.name, justInfo);
- scriptMenu.addHeader('v' + GM_info.script.version);
-}
-
-function addControls() {
- createInterface();
- const checkboxDetails = scriptMenu.addDetails(I18N('SETTINGS'));
- for (let name in checkboxes) {
- if (checkboxes[name].hide) {
- continue;
- }
- checkboxes[name].cbox = scriptMenu.addCheckbox(checkboxes[name].label, checkboxes[name].title, checkboxDetails);
- /**
- * Getting the state of checkboxes from storage
- * Получаем состояние чекбоксов из storage
- */
- let val = storage.get(name, null);
- if (val != null) {
- checkboxes[name].cbox.checked = val;
- } else {
- storage.set(name, checkboxes[name].default);
- checkboxes[name].cbox.checked = checkboxes[name].default;
- }
- /**
- * Tracing the change event of the checkbox for writing to storage
- * Отсеживание события изменения чекбокса для записи в storage
- */
- checkboxes[name].cbox.dataset['name'] = name;
- checkboxes[name].cbox.addEventListener('change', async function (event) {
- const nameCheckbox = this.dataset['name'];
- /*
- if (this.checked && nameCheckbox == 'cancelBattle') {
- this.checked = false;
- if (await popup.confirm(I18N('MSG_BAN_ATTENTION'), [
- { msg: I18N('BTN_NO_I_AM_AGAINST'), result: true },
- { msg: I18N('BTN_YES_I_AGREE'), result: false },
- ])) {
- return;
- }
- this.checked = true;
- }
- */
- storage.set(nameCheckbox, this.checked);
- })
- }
-
- const inputDetails = scriptMenu.addDetails(I18N('VALUES'));
- for (let name in inputs) {
- inputs[name].input = scriptMenu.addInputText(inputs[name].title, false, inputDetails);
- /**
- * Get inputText state from storage
- * Получаем состояние inputText из storage
- */
- let val = storage.get(name, null);
- if (val != null) {
- inputs[name].input.value = val;
- } else {
- storage.set(name, inputs[name].default);
- inputs[name].input.value = inputs[name].default;
- }
- /**
- * Tracing a field change event for a record in storage
- * Отсеживание события изменения поля для записи в storage
- */
- inputs[name].input.dataset['name'] = name;
- inputs[name].input.addEventListener('input', function () {
- const inputName = this.dataset['name'];
- let value = +this.value;
- if (!value || Number.isNaN(value)) {
- value = storage.get(inputName, inputs[inputName].default);
- inputs[name].input.value = value;
- }
- storage.set(inputName, value);
- })
- }
-}
-
-/**
- * Sending a request
- *
- * Отправка запроса
- */
-function send(json, callback, pr) {
- if (typeof json == 'string') {
- json = JSON.parse(json);
- }
- for (const call of json.calls) {
- if (!call?.context?.actionTs) {
- call.context = {
- actionTs: Math.floor(performance.now())
- }
- }
- }
- json = JSON.stringify(json);
- /**
- * We get the headlines of the previous intercepted request
- * Получаем заголовки предыдущего перехваченого запроса
- */
- let headers = lastHeaders;
- /**
- * We increase the header of the query Certifier by 1
- * Увеличиваем заголовок идетификатора запроса на 1
- */
- headers["X-Request-Id"]++;
- /**
- * We calculate the title with the signature
- * Расчитываем заголовок с сигнатурой
- */
- headers["X-Auth-Signature"] = getSignature(headers, json);
- /**
- * Create a new ajax request
- * Создаем новый AJAX запрос
- */
- let xhr = new XMLHttpRequest;
- /**
- * Indicate the previously saved URL for API queries
- * Указываем ранее сохраненный URL для API запросов
- */
- xhr.open('POST', apiUrl, true);
- /**
- * Add the function to the event change event
- * Добавляем функцию к событию смены статуса запроса
- */
- xhr.onreadystatechange = function() {
- /**
- * If the result of the request is obtained, we call the flask function
- * Если результат запроса получен вызываем колбек функцию
- */
- if(xhr.readyState == 4) {
- callback(xhr.response, pr);
- }
- };
- /**
- * Indicate the type of request
- * Указываем тип запроса
- */
- xhr.responseType = 'json';
- /**
- * We set the request headers
- * Задаем заголовки запроса
- */
- for(let nameHeader in headers) {
- let head = headers[nameHeader];
- xhr.setRequestHeader(nameHeader, head);
- }
- /**
- * Sending a request
- * Отправляем запрос
- */
- xhr.send(json);
-}
-
-let hideTimeoutProgress = 0;
-/**
- * Hide progress
- *
- * Скрыть прогресс
- */
-function hideProgress(timeout) {
- timeout = timeout || 0;
- clearTimeout(hideTimeoutProgress);
- hideTimeoutProgress = setTimeout(function () {
- scriptMenu.setStatus('');
- }, timeout);
-}
-/**
- * Progress display
- *
- * Отображение прогресса
- */
-function setProgress(text, hide, onclick) {
- scriptMenu.setStatus(text, onclick);
- hide = hide || false;
- if (hide) {
- hideProgress(3000);
- }
-}
-
-/**
- * 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);
-}
-
-/**
- * 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 _}))}();
-
-/**
- * Script for beautiful dialog boxes
- *
- * Скрипт для красивых диалоговых окошек
- */
-const popup = new (function () {
- this.popUp,
- this.downer,
- this.middle,
- this.msgText,
- this.buttons = [];
- this.checkboxes = [];
- this.dialogPromice = null;
-
- this.init = function () {
- addStyle();
- addBlocks();
- addEventListeners();
- }
-
- const addEventListeners = () => {
- document.addEventListener('keyup', (e) => {
- if (e.key == 'Escape') {
- if (this.dialogPromice) {
- const { func, result } = this.dialogPromice;
- this.dialogPromice = null;
- popup.hide();
- func(result);
- }
- }
- });
- }
-
- const addStyle = () => {
- let style = document.createElement('style');
- style.innerText = `
- .PopUp_ {
- position: absolute;
- min-width: 300px;
- max-width: 500px;
- max-height: 600px;
- background-color: #190e08e6;
- z-index: 10001;
- top: 169px;
- left: 345px;
- border: 3px #ce9767 solid;
- border-radius: 10px;
- display: flex;
- flex-direction: column;
- justify-content: space-around;
- padding: 15px 9px;
- box-sizing: border-box;
- }
-
- .PopUp_back {
- position: absolute;
- background-color: #00000066;
- width: 100%;
- height: 100%;
- z-index: 10000;
- top: 0;
- left: 0;
- }
-
- .PopUp_close {
- width: 40px;
- height: 40px;
- position: absolute;
- right: -18px;
- top: -18px;
- border: 3px solid #c18550;
- border-radius: 20px;
- background: radial-gradient(circle, rgba(190,30,35,1) 0%, rgba(0,0,0,1) 100%);
- background-position-y: 3px;
- box-shadow: -1px 1px 3px black;
- cursor: pointer;
- box-sizing: border-box;
- }
-
- .PopUp_close:hover {
- filter: brightness(1.2);
- }
-
- .PopUp_crossClose {
- width: 100%;
- height: 100%;
- background-size: 65%;
- background-position: center;
- background-repeat: no-repeat;
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%23f4cd73' d='M 0.826 12.559 C 0.431 12.963 3.346 15.374 3.74 14.97 C 4.215 15.173 8.167 10.457 7.804 10.302 C 7.893 10.376 11.454 14.64 11.525 14.372 C 12.134 15.042 15.118 12.086 14.638 11.689 C 14.416 11.21 10.263 7.477 10.402 7.832 C 10.358 7.815 11.731 7.101 14.872 3.114 C 14.698 2.145 13.024 1.074 12.093 1.019 C 11.438 0.861 8.014 5.259 8.035 5.531 C 7.86 5.082 3.61 1.186 3.522 1.59 C 2.973 1.027 0.916 4.611 1.17 4.873 C 0.728 4.914 5.088 7.961 5.61 7.995 C 5.225 7.532 0.622 12.315 0.826 12.559 Z'/%3e%3c/svg%3e")
- }
-
- .PopUp_blocks {
- width: 100%;
- height: 50%;
- display: flex;
- justify-content: space-evenly;
- align-items: center;
- flex-wrap: wrap;
- justify-content: center;
- }
-
- .PopUp_blocks:last-child {
- margin-top: 25px;
- }
-
- .PopUp_buttons {
- display: flex;
- margin: 7px 10px;
- flex-direction: column;
- }
-
- .PopUp_button {
- background-color: #52A81C;
- border-radius: 5px;
- box-shadow: inset 0px -4px 10px, inset 0px 3px 2px #99fe20, 0px 0px 4px, 0px -3px 1px #d7b275, 0px 0px 0px 3px #ce9767;
- cursor: pointer;
- padding: 4px 12px 6px;
- }
-
- .PopUp_input {
- text-align: center;
- font-size: 16px;
- height: 27px;
- border: 1px solid #cf9250;
- border-radius: 9px 9px 0px 0px;
- background: transparent;
- color: #fce1ac;
- padding: 1px 10px;
- box-sizing: border-box;
- box-shadow: 0px 0px 4px, 0px 0px 0px 3px #ce9767;
- }
-
- .PopUp_checkboxes {
- display: flex;
- flex-direction: column;
- margin: 15px 15px -5px 15px;
- align-items: flex-start;
- }
-
- .PopUp_ContCheckbox {
- margin: 2px 0px;
- }
-
- .PopUp_checkbox {
- position: absolute;
- z-index: -1;
- opacity: 0;
- }
- .PopUp_checkbox+label {
- display: inline-flex;
- align-items: center;
- user-select: none;
-
- font-size: 15px;
- font-family: sans-serif;
- font-weight: 600;
- font-stretch: condensed;
- letter-spacing: 1px;
- color: #fce1ac;
- text-shadow: 0px 0px 1px;
- }
- .PopUp_checkbox+label::before {
- content: '';
- display: inline-block;
- width: 20px;
- height: 20px;
- border: 1px solid #cf9250;
- border-radius: 7px;
- margin-right: 7px;
- }
- .PopUp_checkbox:checked+label::before {
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2388cb13' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e");
- }
-
- .PopUp_input::placeholder {
- color: #fce1ac75;
- }
-
- .PopUp_input:focus {
- outline: 0;
- }
-
- .PopUp_input + .PopUp_button {
- border-radius: 0px 0px 5px 5px;
- padding: 2px 18px 5px;
- }
-
- .PopUp_button:hover {
- filter: brightness(1.2);
- }
-
- .PopUp_button:active {
- box-shadow: inset 0px 5px 10px, inset 0px 1px 2px #99fe20, 0px 0px 4px, 0px -3px 1px #d7b275, 0px 0px 0px 3px #ce9767;
- }
-
- .PopUp_text {
- font-size: 22px;
- font-family: sans-serif;
- font-weight: 600;
- font-stretch: condensed;
- letter-spacing: 1px;
- text-align: center;
- }
-
- .PopUp_buttonText {
- color: #E4FF4C;
- text-shadow: 0px 1px 2px black;
- }
-
- .PopUp_msgText {
- color: #FDE5B6;
- text-shadow: 0px 0px 2px;
- }
-
- .PopUp_hideBlock {
- display: none;
- }
- `;
- document.head.appendChild(style);
- }
-
- const addBlocks = () => {
- this.back = document.createElement('div');
- this.back.classList.add('PopUp_back');
- this.back.classList.add('PopUp_hideBlock');
- document.body.append(this.back);
-
- this.popUp = document.createElement('div');
- this.popUp.classList.add('PopUp_');
- this.back.append(this.popUp);
-
- let upper = document.createElement('div')
- upper.classList.add('PopUp_blocks');
- this.popUp.append(upper);
-
- this.middle = document.createElement('div')
- this.middle.classList.add('PopUp_blocks');
- this.middle.classList.add('PopUp_checkboxes');
- this.popUp.append(this.middle);
-
- this.downer = document.createElement('div')
- this.downer.classList.add('PopUp_blocks');
- this.popUp.append(this.downer);
-
- this.msgText = document.createElement('div');
- this.msgText.classList.add('PopUp_text', 'PopUp_msgText');
- upper.append(this.msgText);
- }
-
- this.showBack = function () {
- this.back.classList.remove('PopUp_hideBlock');
- }
-
- this.hideBack = function () {
- this.back.classList.add('PopUp_hideBlock');
- }
-
- this.show = function () {
- if (this.checkboxes.length) {
- this.middle.classList.remove('PopUp_hideBlock');
- }
- this.showBack();
- this.popUp.classList.remove('PopUp_hideBlock');
- this.popUp.style.left = (window.innerWidth - this.popUp.offsetWidth) / 2 + 'px';
- this.popUp.style.top = (window.innerHeight - this.popUp.offsetHeight) / 3 + 'px';
- }
-
- this.hide = function () {
- this.hideBack();
- this.popUp.classList.add('PopUp_hideBlock');
- }
-
- this.addAnyButton = (option) => {
- const contButton = document.createElement('div');
- contButton.classList.add('PopUp_buttons');
- this.downer.append(contButton);
-
- let inputField = {
- value: option.result || option.default
- }
- if (option.isInput) {
- inputField = document.createElement('input');
- inputField.type = 'text';
- if (option.placeholder) {
- inputField.placeholder = option.placeholder;
- }
- if (option.default) {
- inputField.value = option.default;
- }
- inputField.classList.add('PopUp_input');
- contButton.append(inputField);
- }
-
- const button = document.createElement('div');
- button.classList.add('PopUp_button');
- button.title = option.title || '';
- contButton.append(button);
-
- const buttonText = document.createElement('div');
- buttonText.classList.add('PopUp_text', 'PopUp_buttonText');
- buttonText.innerHTML = option.msg;
- button.append(buttonText);
-
- return { button, contButton, inputField };
- }
-
- this.addCloseButton = () => {
- let button = document.createElement('div')
- button.classList.add('PopUp_close');
- this.popUp.append(button);
-
- let crossClose = document.createElement('div')
- crossClose.classList.add('PopUp_crossClose');
- button.append(crossClose);
-
- return { button, contButton: button };
- }
-
- this.addButton = (option, buttonClick) => {
-
- const { button, contButton, inputField } = option.isClose ? this.addCloseButton() : this.addAnyButton(option);
- if (option.isClose) {
- this.dialogPromice = {func: buttonClick, result: option.result};
- }
- button.addEventListener('click', () => {
- let result = '';
- if (option.isInput) {
- result = inputField.value;
- }
- if (option.isClose || option.isCancel) {
- this.dialogPromice = null;
- }
- buttonClick(result);
- });
-
- this.buttons.push(contButton);
- }
-
- this.clearButtons = () => {
- while (this.buttons.length) {
- this.buttons.pop().remove();
- }
- }
-
- this.addCheckBox = (checkBox) => {
- const contCheckbox = document.createElement('div');
- contCheckbox.classList.add('PopUp_ContCheckbox');
- this.middle.append(contCheckbox);
-
- const checkbox = document.createElement('input');
- checkbox.type = 'checkbox';
- checkbox.id = 'PopUpCheckbox' + this.checkboxes.length;
- checkbox.dataset.name = checkBox.name;
- checkbox.checked = checkBox.checked;
- checkbox.label = checkBox.label;
- checkbox.title = checkBox.title || '';
- checkbox.classList.add('PopUp_checkbox');
- contCheckbox.appendChild(checkbox)
-
- const checkboxLabel = document.createElement('label');
- checkboxLabel.innerText = checkBox.label;
- checkboxLabel.title = checkBox.title || '';
- checkboxLabel.setAttribute('for', checkbox.id);
- contCheckbox.appendChild(checkboxLabel);
-
- this.checkboxes.push(checkbox);
- }
-
- this.clearCheckBox = () => {
- this.middle.classList.add('PopUp_hideBlock');
- while (this.checkboxes.length) {
- this.checkboxes.pop().parentNode.remove();
- }
- }
-
- this.setMsgText = (text) => {
- this.msgText.innerHTML = text;
- }
-
- this.getCheckBoxes = () => {
- const checkBoxes = [];
-
- for (const checkBox of this.checkboxes) {
- checkBoxes.push({
- name: checkBox.dataset.name,
- label: checkBox.label,
- checked: checkBox.checked
- });
- }
-
- return checkBoxes;
- }
-
- this.confirm = async (msg, buttOpt, checkBoxes = []) => {
- this.clearButtons();
- this.clearCheckBox();
- return new Promise((complete, failed) => {
- this.setMsgText(msg);
- if (!buttOpt) {
- buttOpt = [{ msg: 'Ok', result: true, isInput: false }];
- }
- for (const checkBox of checkBoxes) {
- this.addCheckBox(checkBox);
- }
- for (let butt of buttOpt) {
- this.addButton(butt, (result) => {
- result = result || butt.result;
- complete(result);
- popup.hide();
- });
- if (butt.isCancel) {
- this.dialogPromice = {func: complete, result: butt.result};
- }
- }
- this.show();
- });
- }
-});
-
-/**
- * Script control panel
- *
- * Панель управления скриптом
- */
-const scriptMenu = new (function () {
-
- this.mainMenu,
- this.buttons = [],
- this.checkboxes = [];
- this.option = {
- showMenu: false,
- showDetails: {}
- };
-
- this.init = function (option = {}) {
- this.option = Object.assign(this.option, option);
- this.option.showDetails = this.loadShowDetails();
- addStyle();
- addBlocks();
- }
-
- const addStyle = () => {
- style = document.createElement('style');
- style.innerText = `
- .scriptMenu_status {
- position: absolute;
- z-index: 10001;
- /* max-height: 30px; */
- top: -1px;
- left: 30%;
- cursor: pointer;
- border-radius: 0px 0px 10px 10px;
- background: #190e08e6;
- border: 1px #ce9767 solid;
- font-size: 18px;
- font-family: sans-serif;
- font-weight: 600;
- font-stretch: condensed;
- letter-spacing: 1px;
- color: #fce1ac;
- text-shadow: 0px 0px 1px;
- transition: 0.5s;
- padding: 2px 10px 3px;
- }
- .scriptMenu_statusHide {
- top: -35px;
- height: 30px;
- overflow: hidden;
- }
- .scriptMenu_label {
- position: absolute;
- top: 30%;
- left: -4px;
- z-index: 9999;
- cursor: pointer;
- width: 30px;
- height: 30px;
- background: radial-gradient(circle, #47a41b 0%, #1a2f04 100%);
- border: 1px solid #1a2f04;
- border-radius: 5px;
- box-shadow:
- inset 0px 2px 4px #83ce26,
- inset 0px -4px 6px #1a2f04,
- 0px 0px 2px black,
- 0px 0px 0px 2px #ce9767;
- }
- .scriptMenu_label:hover {
- filter: brightness(1.2);
- }
- .scriptMenu_arrowLabel {
- width: 100%;
- height: 100%;
- background-size: 75%;
- background-position: center;
- background-repeat: no-repeat;
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%2388cb13' d='M7.596 7.304a.802.802 0 0 1 0 1.392l-6.363 3.692C.713 12.69 0 12.345 0 11.692V4.308c0-.653.713-.998 1.233-.696l6.363 3.692Z'/%3e%3cpath fill='%2388cb13' d='M15.596 7.304a.802.802 0 0 1 0 1.392l-6.363 3.692C8.713 12.69 8 12.345 8 11.692V4.308c0-.653.713-.998 1.233-.696l6.363 3.692Z'/%3e%3c/svg%3e");
- box-shadow: 0px 1px 2px #000;
- border-radius: 5px;
- filter: drop-shadow(0px 1px 2px #000D);
- }
- .scriptMenu_main {
- position: absolute;
- max-width: 285px;
- z-index: 9999;
- top: 50%;
- transform: translateY(-40%);
- background: #190e08e6;
- border: 1px #ce9767 solid;
- border-radius: 0px 10px 10px 0px;
- border-left: none;
- padding: 5px 10px 5px 5px;
- box-sizing: border-box;
- font-size: 15px;
- font-family: sans-serif;
- font-weight: 600;
- font-stretch: condensed;
- letter-spacing: 1px;
- color: #fce1ac;
- text-shadow: 0px 0px 1px;
- transition: 1s;
- display: flex;
- flex-direction: column;
- flex-wrap: nowrap;
- }
- .scriptMenu_showMenu {
- display: none;
- }
- .scriptMenu_showMenu:checked~.scriptMenu_main {
- left: 0px;
- }
- .scriptMenu_showMenu:not(:checked)~.scriptMenu_main {
- left: -300px;
- }
- .scriptMenu_divInput {
- margin: 2px;
- }
- .scriptMenu_divInputText {
- margin: 2px;
- align-self: center;
- display: flex;
- }
- .scriptMenu_checkbox {
- position: absolute;
- z-index: -1;
- opacity: 0;
- }
- .scriptMenu_checkbox+label {
- display: inline-flex;
- align-items: center;
- user-select: none;
- }
- .scriptMenu_checkbox+label::before {
- content: '';
- display: inline-block;
- width: 20px;
- height: 20px;
- border: 1px solid #cf9250;
- border-radius: 7px;
- margin-right: 7px;
- }
- .scriptMenu_checkbox:checked+label::before {
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2388cb13' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e");
- }
- .scriptMenu_close {
- width: 40px;
- height: 40px;
- position: absolute;
- right: -18px;
- top: -18px;
- border: 3px solid #c18550;
- border-radius: 20px;
- background: radial-gradient(circle, rgba(190,30,35,1) 0%, rgba(0,0,0,1) 100%);
- background-position-y: 3px;
- box-shadow: -1px 1px 3px black;
- cursor: pointer;
- box-sizing: border-box;
- }
- .scriptMenu_close:hover {
- filter: brightness(1.2);
- }
- .scriptMenu_crossClose {
- width: 100%;
- height: 100%;
- background-size: 65%;
- background-position: center;
- background-repeat: no-repeat;
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%23f4cd73' d='M 0.826 12.559 C 0.431 12.963 3.346 15.374 3.74 14.97 C 4.215 15.173 8.167 10.457 7.804 10.302 C 7.893 10.376 11.454 14.64 11.525 14.372 C 12.134 15.042 15.118 12.086 14.638 11.689 C 14.416 11.21 10.263 7.477 10.402 7.832 C 10.358 7.815 11.731 7.101 14.872 3.114 C 14.698 2.145 13.024 1.074 12.093 1.019 C 11.438 0.861 8.014 5.259 8.035 5.531 C 7.86 5.082 3.61 1.186 3.522 1.59 C 2.973 1.027 0.916 4.611 1.17 4.873 C 0.728 4.914 5.088 7.961 5.61 7.995 C 5.225 7.532 0.622 12.315 0.826 12.559 Z'/%3e%3c/svg%3e")
- }
- .scriptMenu_button {
- user-select: none;
- border-radius: 5px;
- cursor: pointer;
- padding: 5px 14px 8px;
- margin: 4px;
- background: radial-gradient(circle, rgba(165,120,56,1) 80%, rgba(0,0,0,1) 110%);
- box-shadow: inset 0px -4px 6px #442901, inset 0px 1px 6px #442901, inset 0px 0px 6px, 0px 0px 4px, 0px 0px 0px 2px #ce9767;
- }
- .scriptMenu_button:hover {
- filter: brightness(1.2);
- }
- .scriptMenu_button:active {
- box-shadow: inset 0px 4px 6px #442901, inset 0px 4px 6px #442901, inset 0px 0px 6px, 0px 0px 4px, 0px 0px 0px 2px #ce9767;
- }
- .scriptMenu_buttonText {
- color: #fce5b7;
- text-shadow: 0px 1px 2px black;
- text-align: center;
- }
- .scriptMenu_header {
- text-align: center;
- align-self: center;
- font-size: 15px;
- margin: 0px 15px;
- }
- .scriptMenu_header a {
- color: #fce5b7;
- text-decoration: none;
- }
- .scriptMenu_InputText {
- text-align: center;
- width: 130px;
- height: 24px;
- border: 1px solid #cf9250;
- border-radius: 9px;
- background: transparent;
- color: #fce1ac;
- padding: 0px 10px;
- box-sizing: border-box;
- }
- .scriptMenu_InputText:focus {
- filter: brightness(1.2);
- outline: 0;
- }
- .scriptMenu_InputText::placeholder {
- color: #fce1ac75;
- }
- .scriptMenu_Summary {
- cursor: pointer;
- margin-left: 7px;
- }
- .scriptMenu_Details {
- align-self: center;
- }
-`;
- document.head.appendChild(style);
- }
-
- const addBlocks = () => {
- const main = document.createElement('div');
- document.body.appendChild(main);
-
- this.status = document.createElement('div');
- this.status.classList.add('scriptMenu_status');
- this.setStatus('');
- main.appendChild(this.status);
-
- const label = document.createElement('label');
- label.classList.add('scriptMenu_label');
- label.setAttribute('for', 'checkbox_showMenu');
- main.appendChild(label);
-
- const arrowLabel = document.createElement('div');
- arrowLabel.classList.add('scriptMenu_arrowLabel');
- label.appendChild(arrowLabel);
-
- const checkbox = document.createElement('input');
- checkbox.type = 'checkbox';
- checkbox.id = 'checkbox_showMenu';
- checkbox.checked = this.option.showMenu;
- checkbox.classList.add('scriptMenu_showMenu');
- main.appendChild(checkbox);
-
- this.mainMenu = document.createElement('div');
- this.mainMenu.classList.add('scriptMenu_main');
- main.appendChild(this.mainMenu);
-
- const closeButton = document.createElement('label');
- closeButton.classList.add('scriptMenu_close');
- closeButton.setAttribute('for', 'checkbox_showMenu');
- this.mainMenu.appendChild(closeButton);
-
- const crossClose = document.createElement('div');
- crossClose.classList.add('scriptMenu_crossClose');
- closeButton.appendChild(crossClose);
- }
-
- this.setStatus = (text, onclick) => {
- if (!text) {
- this.status.classList.add('scriptMenu_statusHide');
- } else {
- this.status.classList.remove('scriptMenu_statusHide');
- this.status.innerHTML = text;
- }
-
- if (typeof onclick == 'function') {
- this.status.addEventListener("click", onclick, {
- once: true
- });
- }
- }
-
- /**
- * Adding a text element
- *
- * Добавление текстового элемента
- * @param {String} text text // текст
- * @param {Function} func Click function // функция по клику
- * @param {HTMLDivElement} main parent // родитель
- */
- this.addHeader = (text, func, main) => {
- main = main || this.mainMenu;
- const header = document.createElement('div');
- header.classList.add('scriptMenu_header');
- header.innerHTML = text;
- if (typeof func == 'function') {
- header.addEventListener('click', func);
- }
- main.appendChild(header);
- }
-
- /**
- * Adding a button
- *
- * Добавление кнопки
- * @param {String} text
- * @param {Function} func
- * @param {String} title
- * @param {HTMLDivElement} main parent // родитель
- */
- this.addButton = (text, func, title, main) => {
- main = main || this.mainMenu;
- const button = document.createElement('div');
- button.classList.add('scriptMenu_button');
- button.title = title;
- button.addEventListener('click', func);
- main.appendChild(button);
-
- const buttonText = document.createElement('div');
- buttonText.classList.add('scriptMenu_buttonText');
- buttonText.innerText = text;
- button.appendChild(buttonText);
- this.buttons.push(button);
-
- return button;
- }
-
- /**
- * Adding checkbox
- *
- * Добавление чекбокса
- * @param {String} label
- * @param {String} title
- * @param {HTMLDivElement} main parent // родитель
- * @returns
- */
- this.addCheckbox = (label, title, main) => {
- main = main || this.mainMenu;
- const divCheckbox = document.createElement('div');
- divCheckbox.classList.add('scriptMenu_divInput');
- divCheckbox.title = title;
- main.appendChild(divCheckbox);
-
- const checkbox = document.createElement('input');
- checkbox.type = 'checkbox';
- checkbox.id = 'scriptMenuCheckbox' + this.checkboxes.length;
- checkbox.classList.add('scriptMenu_checkbox');
- divCheckbox.appendChild(checkbox)
-
- const checkboxLabel = document.createElement('label');
- checkboxLabel.innerText = label;
- checkboxLabel.setAttribute('for', checkbox.id);
- divCheckbox.appendChild(checkboxLabel);
-
- this.checkboxes.push(checkbox);
- return checkbox;
- }
-
- /**
- * Adding input field
- *
- * Добавление поля ввода
- * @param {String} title
- * @param {String} placeholder
- * @param {HTMLDivElement} main parent // родитель
- * @returns
- */
- this.addInputText = (title, placeholder, main) => {
- main = main || this.mainMenu;
- const divInputText = document.createElement('div');
- divInputText.classList.add('scriptMenu_divInputText');
- divInputText.title = title;
- main.appendChild(divInputText);
-
- const newInputText = document.createElement('input');
- newInputText.type = 'text';
- if (placeholder) {
- newInputText.placeholder = placeholder;
- }
- newInputText.classList.add('scriptMenu_InputText');
- divInputText.appendChild(newInputText)
- return newInputText;
- }
-
- /**
- * Adds a dropdown block
- *
- * Добавляет раскрывающийся блок
- * @param {String} summary
- * @param {String} name
- * @returns
- */
- this.addDetails = (summaryText, name = null) => {
- const details = document.createElement('details');
- details.classList.add('scriptMenu_Details');
- this.mainMenu.appendChild(details);
-
- const summary = document.createElement('summary');
- summary.classList.add('scriptMenu_Summary');
- summary.innerText = summaryText;
- if (name) {
- const self = this;
- details.open = this.option.showDetails[name];
- details.dataset.name = name;
- summary.addEventListener('click', () => {
- self.option.showDetails[details.dataset.name] = !details.open;
- self.saveShowDetails(self.option.showDetails);
- });
- }
- details.appendChild(summary);
-
- return details;
- }
-
- /**
- * Saving the expanded state of the details blocks
- *
- * Сохранение состояния развенутости блоков details
- * @param {*} value
- */
- this.saveShowDetails = (value) => {
- localStorage.setItem('scriptMenu_showDetails', JSON.stringify(value));
- }
-
- /**
- * Loading the state of expanded blocks details
- *
- * Загрузка состояния развенутости блоков details
- * @returns
- */
- this.loadShowDetails = () => {
- let showDetails = localStorage.getItem('scriptMenu_showDetails');
-
- if (!showDetails) {
- return {};
- }
-
- try {
- showDetails = JSON.parse(showDetails);
- } catch (e) {
- return {};
- }
-
- return showDetails;
- }
-});
-
-/**
- * Пример использования
-scriptMenu.init();
-scriptMenu.addHeader('v1.508');
-scriptMenu.addCheckbox('testHack', 'Тестовый взлом игры!');
-scriptMenu.addButton('Запуск!', () => console.log('click'), 'подсказака');
-scriptMenu.addInputText('input подсказака');
- */
-/**
- * Game Library
- *
- * Игровая библиотека
- */
-class Library {
- defaultLibUrl = 'https://heroesru-a.akamaihd.net/vk/v1101/lib/lib.json';
-
- constructor() {
- if (!Library.instance) {
- Library.instance = this;
- }
-
- return Library.instance;
- }
-
- async load() {
- try {
- await this.getUrlLib();
- console.log(this.defaultLibUrl);
- this.data = await fetch(this.defaultLibUrl).then(e => e.json())
- } catch (error) {
- console.error('Не удалось загрузить библиотеку', error)
- }
- }
-
- async getUrlLib() {
- try {
- const db = new Database('hw_cache', 'cache');
- await db.open();
- const cacheLibFullUrl = await db.get('lib/lib.json.gz', false);
- this.defaultLibUrl = cacheLibFullUrl.fullUrl.split('.gz').shift();
- } catch(e) {}
- }
-
- getData(id) {
- return this.data[id];
- }
-}
-
-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;
-}
-
-/**
- * Stores value
- *
- * Сохраняет значение
- */
-function setSaveVal(saveName, value) {
- storage.set(saveName, value);
-}
-
-/**
- * Database initialization
- *
- * Инициализация базы данных
- */
-const db = new Database(GM_info.script.name, 'settings');
-
-/**
- * Data store
- *
- * Хранилище данных
- */
-const storage = {
- userId: 0,
- /**
- * Default values
- *
- * Значения по умолчанию
- */
- values: [
- ...Object.entries(checkboxes).map(e => ({ [e[0]]: e[1].default })),
- ...Object.entries(inputs).map(e => ({ [e[0]]: e[1].default })),
- ].reduce((acc, obj) => ({ ...acc, ...obj }), {}),
- name: GM_info.script.name,
- get: function (key, def) {
- if (key in this.values) {
- return this.values[key];
- }
- return def;
- },
- set: function (key, value) {
- this.values[key] = value;
- db.set(this.userId, this.values).catch(
- e => null
- );
- localStorage[this.name + ':' + key] = value;
- },
- delete: function (key) {
- delete this.values[key];
- db.set(this.userId, this.values);
- delete localStorage[this.name + ':' + key];
- }
-}
-
-/**
- * Returns all keys from localStorage that start with prefix (for migration)
- *
- * Возвращает все ключи из localStorage которые начинаются с prefix (для миграции)
- */
-function getAllValuesStartingWith(prefix) {
- const values = [];
- for (let i = 0; i < localStorage.length; i++) {
- const key = localStorage.key(i);
- if (key.startsWith(prefix)) {
- const val = localStorage.getItem(key);
- const keyValue = key.split(':')[1];
- values.push({ key: keyValue, val });
- }
- }
- return values;
-}
-
-/**
- * Opens or migrates to a database
- *
- * Открывает или мигрирует в базу данных
- */
-async function openOrMigrateDatabase(userId) {
- storage.userId = userId;
- try {
- await db.open();
- } catch(e) {
- return;
- }
- let settings = await db.get(userId, false);
-
- if (settings) {
- storage.values = settings;
- return;
- }
-
- const values = getAllValuesStartingWith(GM_info.script.name);
- for (const value of values) {
- let val = null;
- try {
- val = JSON.parse(value.val);
- } catch {
- break;
- }
- storage.values[value.key] = val;
- }
- await db.set(userId, storage.values);
-}
-
-class ZingerYWebsiteAPI {
- /**
- * Class for interaction with the API of the zingery.ru website
- * Intended only for use with the HeroWarsHelper script:
- * https://greasyfork.org/ru/scripts/450693-herowarshelper
- * Copyright ZingerY
- */
- url = 'https://zingery.ru/heroes/';
- // YWJzb2x1dGVseSB1c2VsZXNzIGxpbmU=
- constructor(urn, env, data = {}) {
- this.urn = urn;
- this.fd = {
- now: Date.now(),
- fp: this.constructor.toString().replaceAll(/\s/g, ''),
- env: env.callee.toString().replaceAll(/\s/g, ''),
- info: (({ name, version, author }) => [name, version, author])(GM_info.script),
- ...data,
- };
- }
-
- sign() {
- return md5([...this.fd.info, ~(this.fd.now % 1e3), this.fd.fp].join('_'));
- }
-
- encode(data) {
- return btoa(encodeURIComponent(JSON.stringify(data)));
- }
-
- decode(data) {
- return JSON.parse(decodeURIComponent(atob(data)));
- }
-
- headers() {
- return {
- 'X-Request-Signature': this.sign(),
- 'X-Script-Name': GM_info.script.name,
- 'X-Script-Version': GM_info.script.version,
- 'X-Script-Author': GM_info.script.author,
- 'X-Script-ZingerY': 42,
- };
- }
-
- async request() {
- try {
- const response = await fetch(this.url + this.urn, {
- method: 'POST',
- headers: this.headers(),
- body: this.encode(this.fd),
- });
- const text = await response.text();
- return this.decode(text);
- } catch (e) {
- console.error(e);
- return [];
- }
- }
- /**
- * Класс для взаимодействия с API сайта zingery.ru
- * Предназначен только для использования со скриптом HeroWarsHelper:
- * https://greasyfork.org/ru/scripts/450693-herowarshelper
- * Copyright ZingerY
- */
-}
-
-/**
- * Sending expeditions
- *
- * Отправка экспедиций
- */
-function checkExpedition() {
- return new Promise((resolve, reject) => {
- const expedition = new Expedition(resolve, reject);
- expedition.start();
- });
-}
-
-class Expedition {
- checkExpedInfo = {
- calls: [
- {
- name: 'expeditionGet',
- args: {},
- ident: 'expeditionGet',
- },
- {
- name: 'heroGetAll',
- args: {},
- ident: 'heroGetAll',
- },
- ],
- };
-
- constructor(resolve, reject) {
- this.resolve = resolve;
- this.reject = reject;
- }
-
- async start() {
- const data = await Send(JSON.stringify(this.checkExpedInfo));
-
- const expedInfo = data.results[0].result.response;
- const dataHeroes = data.results[1].result.response;
- const dataExped = { useHeroes: [], exped: [] };
- const calls = [];
-
- /**
- * Adding expeditions to collect
- * Добавляем экспедиции для сбора
- */
- let countGet = 0;
- for (var n in expedInfo) {
- const exped = expedInfo[n];
- const dateNow = Date.now() / 1000;
- if (exped.status == 2 && exped.endTime != 0 && dateNow > exped.endTime) {
- countGet++;
- calls.push({
- name: 'expeditionFarm',
- args: { expeditionId: exped.id },
- ident: 'expeditionFarm_' + exped.id,
- });
- } else {
- dataExped.useHeroes = dataExped.useHeroes.concat(exped.heroes);
- }
- if (exped.status == 1) {
- dataExped.exped.push({ id: exped.id, power: exped.power });
- }
- }
- dataExped.exped = dataExped.exped.sort((a, b) => b.power - a.power);
-
- /**
- * Putting together a list of heroes
- * Собираем список героев
- */
- const heroesArr = [];
- for (let n in dataHeroes) {
- const hero = dataHeroes[n];
- if (hero.xp > 0 && !dataExped.useHeroes.includes(hero.id)) {
- let heroPower = hero.power;
- // Лара Крофт * 3
- if (hero.id == 63 && hero.color >= 16) {
- heroPower *= 3;
- }
- heroesArr.push({ id: hero.id, power: heroPower });
- }
- }
-
- /**
- * Adding expeditions to send
- * Добавляем экспедиции для отправки
- */
- let countSend = 0;
- heroesArr.sort((a, b) => a.power - b.power);
- for (const exped of dataExped.exped) {
- let heroesIds = this.selectionHeroes(heroesArr, exped.power);
- if (heroesIds && heroesIds.length > 4) {
- for (let q in heroesArr) {
- if (heroesIds.includes(heroesArr[q].id)) {
- delete heroesArr[q];
- }
- }
- countSend++;
- calls.push({
- name: 'expeditionSendHeroes',
- args: {
- expeditionId: exped.id,
- heroes: heroesIds,
- },
- ident: 'expeditionSendHeroes_' + exped.id,
- });
- }
- }
-
- if (calls.length) {
- await Send({ calls });
- this.end(I18N('EXPEDITIONS_SENT', {countGet, countSend}));
- return;
- }
-
- this.end(I18N('EXPEDITIONS_NOTHING'));
- }
-
- /**
- * Selection of heroes for expeditions
- *
- * Подбор героев для экспедиций
- */
- selectionHeroes(heroes, power) {
- const resultHeroers = [];
- const heroesIds = [];
- for (let q = 0; q < 5; q++) {
- for (let i in heroes) {
- let hero = heroes[i];
- if (heroesIds.includes(hero.id)) {
- continue;
- }
-
- const summ = resultHeroers.reduce((acc, hero) => acc + hero.power, 0);
- const need = Math.round((power - summ) / (5 - resultHeroers.length));
- if (hero.power > need) {
- resultHeroers.push(hero);
- heroesIds.push(hero.id);
- break;
- }
- }
- }
-
- const summ = resultHeroers.reduce((acc, hero) => acc + hero.power, 0);
- if (summ < power) {
- return false;
- }
- return heroesIds;
- }
-
- /**
- * Ends expedition script
- *
- * Завершает скрипт экспедиции
- */
- end(msg) {
- setProgress(msg, true);
- this.resolve();
- }
-}
-
-/**
- * Walkthrough of the dungeon
- *
- * Прохождение подземелья
- */
-function testDungeon() {
- return new Promise((resolve, reject) => {
- const dung = new executeDungeon(resolve, reject);
- const titanit = getInput('countTitanit');
- dung.start(titanit);
- });
-}
-
-/**
- * Walkthrough of the dungeon
- *
- * Прохождение подземелья
- */
-function executeDungeon(resolve, reject) {
- dungeonActivity = 0;
- maxDungeonActivity = 150;
-
- titanGetAll = [];
-
- teams = {
- heroes: [],
- earth: [],
- fire: [],
- neutral: [],
- water: [],
- }
-
- titanStats = [];
-
- titansStates = {};
-
- callsExecuteDungeon = {
- calls: [{
- name: "dungeonGetInfo",
- args: {},
- ident: "dungeonGetInfo"
- }, {
- name: "teamGetAll",
- args: {},
- ident: "teamGetAll"
- }, {
- name: "teamGetFavor",
- args: {},
- ident: "teamGetFavor"
- }, {
- name: "clanGetInfo",
- args: {},
- ident: "clanGetInfo"
- }, {
- name: "titanGetAll",
- args: {},
- ident: "titanGetAll"
- }, {
- name: "inventoryGet",
- args: {},
- ident: "inventoryGet"
- }]
- }
-
- this.start = function(titanit) {
- maxDungeonActivity = titanit || getInput('countTitanit');
- send(JSON.stringify(callsExecuteDungeon), startDungeon);
- }
-
- /**
- * Getting data on the dungeon
- *
- * Получаем данные по подземелью
- */
- function startDungeon(e) {
- res = e.results;
- dungeonGetInfo = res[0].result.response;
- if (!dungeonGetInfo) {
- endDungeon('noDungeon', res);
- return;
- }
- teamGetAll = res[1].result.response;
- teamGetFavor = res[2].result.response;
- dungeonActivity = res[3].result.response.stat.todayDungeonActivity;
- titanGetAll = Object.values(res[4].result.response);
- countPredictionCard = res[5].result.response.consumable[81];
-
- teams.hero = {
- favor: teamGetFavor.dungeon_hero,
- heroes: teamGetAll.dungeon_hero.filter(id => id < 6000),
- teamNum: 0,
- }
- heroPet = teamGetAll.dungeon_hero.filter(id => id >= 6000).pop();
- if (heroPet) {
- teams.hero.pet = heroPet;
- }
-
- teams.neutral = {
- favor: {},
- heroes: getTitanTeam(titanGetAll, 'neutral'),
- teamNum: 0,
- };
- teams.water = {
- favor: {},
- heroes: getTitanTeam(titanGetAll, 'water'),
- teamNum: 0,
- };
- teams.fire = {
- favor: {},
- heroes: getTitanTeam(titanGetAll, 'fire'),
- teamNum: 0,
- };
- teams.earth = {
- favor: {},
- heroes: getTitanTeam(titanGetAll, 'earth'),
- teamNum: 0,
- };
-
-
- checkFloor(dungeonGetInfo);
- }
-
- function getTitanTeam(titans, type) {
- switch (type) {
- case 'neutral':
- return titans.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
- case 'water':
- return titans.filter(e => e.id.toString().slice(2, 3) == '0').map(e => e.id);
- case 'fire':
- return titans.filter(e => e.id.toString().slice(2, 3) == '1').map(e => e.id);
- case 'earth':
- return titans.filter(e => e.id.toString().slice(2, 3) == '2').map(e => e.id);
- }
- }
-
- function getNeutralTeam() {
- const titans = titanGetAll.filter(e => !titansStates[e.id]?.isDead)
- return titans.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
- }
-
- function fixTitanTeam(titans) {
- titans.heroes = titans.heroes.filter(e => !titansStates[e]?.isDead);
- return titans;
- }
-
- /**
- * Checking the floor
- *
- * Проверяем этаж
- */
- async function checkFloor(dungeonInfo) {
- if (!('floor' in dungeonInfo) || dungeonInfo.floor?.state == 2) {
- saveProgress();
- return;
- }
- // console.log(dungeonInfo, dungeonActivity);
- setProgress(`${I18N('DUNGEON')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity}`);
- if (dungeonActivity >= maxDungeonActivity) {
- endDungeon('endDungeon', 'maxActive ' + dungeonActivity + '/' + maxDungeonActivity);
- return;
- }
- titansStates = dungeonInfo.states.titans;
- titanStats = titanObjToArray(titansStates);
- const floorChoices = dungeonInfo.floor.userData;
- const floorType = dungeonInfo.floorType;
- //const primeElement = dungeonInfo.elements.prime;
- if (floorType == "battle") {
- const calls = [];
- for (let teamNum in floorChoices) {
- attackerType = floorChoices[teamNum].attackerType;
- const args = fixTitanTeam(teams[attackerType]);
- if (attackerType == 'neutral') {
- args.heroes = getNeutralTeam();
- }
- if (!args.heroes.length) {
- continue;
- }
- args.teamNum = teamNum;
- calls.push({
- name: "dungeonStartBattle",
- args,
- ident: "body_" + teamNum
- })
- }
- if (!calls.length) {
- endDungeon('endDungeon', 'All Dead');
- return;
- }
- const battleDatas = await Send(JSON.stringify({ calls }))
- .then(e => e.results.map(n => n.result.response))
- const battleResults = [];
- for (n in battleDatas) {
- battleData = battleDatas[n]
- battleData.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }];
- battleResults.push(await Calc(battleData).then(result => {
- result.teamNum = n;
- result.attackerType = floorChoices[n].attackerType;
- return result;
- }));
- }
- processingPromises(battleResults)
- }
- }
-
- function processingPromises(results) {
- let selectBattle = results[0];
- if (results.length < 2) {
- // console.log(selectBattle);
- if (!selectBattle.result.win) {
- endDungeon('dungeonEndBattle\n', selectBattle);
- return;
- }
- endBattle(selectBattle);
- return;
- }
-
- selectBattle = false;
- let bestState = -1000;
- for (const result of results) {
- const recovery = getState(result);
- if (recovery > bestState) {
- bestState = recovery;
- selectBattle = result
- }
- }
- // console.log(selectBattle.teamNum, results);
- if (!selectBattle || bestState <= -1000) {
- endDungeon('dungeonEndBattle\n', results);
- return;
- }
-
- startBattle(selectBattle.teamNum, selectBattle.attackerType)
- .then(endBattle);
- }
-
- /**
- * Let's start the fight
- *
- * Начинаем бой
- */
- function startBattle(teamNum, attackerType) {
- return new Promise(function (resolve, reject) {
- args = fixTitanTeam(teams[attackerType]);
- args.teamNum = teamNum;
- if (attackerType == 'neutral') {
- const titans = titanGetAll.filter(e => !titansStates[e.id]?.isDead)
- args.heroes = titans.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
- }
- startBattleCall = {
- calls: [{
- name: "dungeonStartBattle",
- args,
- ident: "body"
- }]
- }
- send(JSON.stringify(startBattleCall), resultBattle, {
- resolve,
- teamNum,
- attackerType
- });
- });
- }
- /**
- * Returns the result of the battle in a promise
- *
- * Возращает резульат боя в промис
- */
- function resultBattle(resultBattles, args) {
- battleData = resultBattles.results[0].result.response;
- battleType = "get_tower";
- if (battleData.type == "dungeon_titan") {
- battleType = "get_titan";
- }
- battleData.progress = [{ attackers: { input: ["auto", 0, 0, "auto", 0, 0] } }];
- BattleCalc(battleData, battleType, function (result) {
- result.teamNum = args.teamNum;
- result.attackerType = args.attackerType;
- args.resolve(result);
- });
- }
- /**
- * Finishing the fight
- *
- * Заканчиваем бой
- */
- async function endBattle(battleInfo) {
- if (battleInfo.result.win) {
- const args = {
- result: battleInfo.result,
- progress: battleInfo.progress,
- }
- if (countPredictionCard > 0) {
- args.isRaid = true;
- } else {
- const timer = getTimer(battleInfo.battleTime);
- console.log(timer);
- await countdownTimer(timer, `${I18N('DUNGEON')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity}`);
- }
- const calls = [{
- name: "dungeonEndBattle",
- args,
- ident: "body"
- }];
- lastDungeonBattleData = null;
- send(JSON.stringify({ calls }), resultEndBattle);
- } else {
- endDungeon('dungeonEndBattle win: false\n', battleInfo);
- }
- }
-
- /**
- * Getting and processing battle results
- *
- * Получаем и обрабатываем результаты боя
- */
- function resultEndBattle(e) {
- if ('error' in e) {
- popup.confirm(I18N('ERROR_MSG', {
- name: e.error.name,
- description: e.error.description,
- }));
- endDungeon('errorRequest', e);
- return;
- }
- battleResult = e.results[0].result.response;
- if ('error' in battleResult) {
- endDungeon('errorBattleResult', battleResult);
- return;
- }
- dungeonGetInfo = battleResult.dungeon ?? battleResult;
- dungeonActivity += battleResult.reward.dungeonActivity ?? 0;
- checkFloor(dungeonGetInfo);
- }
-
- /**
- * Returns the coefficient of condition of the
- * difference in titanium before and after the battle
- *
- * Возвращает коэффициент состояния титанов после боя
- */
- function getState(result) {
- if (!result.result.win) {
- return -1000;
- }
-
- let beforeSumFactor = 0;
- const beforeTitans = result.battleData.attackers;
- for (let titanId in beforeTitans) {
- const titan = beforeTitans[titanId];
- const state = titan.state;
- let factor = 1;
- if (state) {
- const hp = state.hp / titan.hp;
- const energy = state.energy / 1e3;
- factor = hp + energy / 20
- }
- beforeSumFactor += factor;
- }
-
- let afterSumFactor = 0;
- const afterTitans = result.progress[0].attackers.heroes;
- for (let titanId in afterTitans) {
- const titan = afterTitans[titanId];
- const hp = titan.hp / beforeTitans[titanId].hp;
- const energy = titan.energy / 1e3;
- const factor = hp + energy / 20;
- afterSumFactor += factor;
- }
- return afterSumFactor - beforeSumFactor;
- }
-
- /**
- * Converts an object with IDs to an array with IDs
- *
- * Преобразует объект с идетификаторами в массив с идетификаторами
- */
- function titanObjToArray(obj) {
- let titans = [];
- for (let id in obj) {
- obj[id].id = id;
- titans.push(obj[id]);
- }
- return titans;
- }
-
- function saveProgress() {
- let saveProgressCall = {
- calls: [{
- name: "dungeonSaveProgress",
- args: {},
- ident: "body"
- }]
- }
- send(JSON.stringify(saveProgressCall), resultEndBattle);
- }
-
- function endDungeon(reason, info) {
- console.warn(reason, info);
- setProgress(`${I18N('DUNGEON')} ${I18N('COMPLETED')}`, true);
- resolve();
- }
-}
-
-/**
- * Passing the tower
- *
- * Прохождение башни
- */
-function testTower() {
- return new Promise((resolve, reject) => {
- tower = new executeTower(resolve, reject);
- tower.start();
- });
-}
-
-/**
- * Passing the tower
- *
- * Прохождение башни
- */
-function executeTower(resolve, reject) {
- lastTowerInfo = {};
-
- scullCoin = 0;
-
- heroGetAll = [];
-
- heroesStates = {};
-
- argsBattle = {
- heroes: [],
- favor: {},
- };
-
- callsExecuteTower = {
- calls: [{
- name: "towerGetInfo",
- args: {},
- ident: "towerGetInfo"
- }, {
- name: "teamGetAll",
- args: {},
- ident: "teamGetAll"
- }, {
- name: "teamGetFavor",
- args: {},
- ident: "teamGetFavor"
- }, {
- name: "inventoryGet",
- args: {},
- ident: "inventoryGet"
- }, {
- name: "heroGetAll",
- args: {},
- ident: "heroGetAll"
- }]
- }
-
- buffIds = [
- {id: 0, cost: 0, isBuy: false}, // plug // заглушка
- {id: 1, cost: 1, isBuy: true}, // 3% attack // 3% атака
- {id: 2, cost: 6, isBuy: true}, // 2% attack // 2% атака
- {id: 3, cost: 16, isBuy: true}, // 4% attack // 4% атака
- {id: 4, cost: 40, isBuy: true}, // 8% attack // 8% атака
- {id: 5, cost: 1, isBuy: true}, // 10% armor // 10% броня
- {id: 6, cost: 6, isBuy: true}, // 5% armor // 5% броня
- {id: 7, cost: 16, isBuy: true}, // 10% armor // 10% броня
- {id: 8, cost: 40, isBuy: true}, // 20% armor // 20% броня
- { id: 9, cost: 1, isBuy: true }, // 10% protection from magic // 10% защита от магии
- { id: 10, cost: 6, isBuy: true }, // 5% protection from magic // 5% защита от магии
- { id: 11, cost: 16, isBuy: true }, // 10% protection from magic // 10% защита от магии
- { id: 12, cost: 40, isBuy: true }, // 20% protection from magic // 20% защита от магии
- { id: 13, cost: 1, isBuy: false }, // 40% health hero // 40% здоровья герою
- { id: 14, cost: 6, isBuy: false }, // 40% health hero // 40% здоровья герою
- { id: 15, cost: 16, isBuy: false }, // 80% health hero // 80% здоровья герою
- { id: 16, cost: 40, isBuy: false }, // 40% health to all heroes // 40% здоровья всем героям
- { id: 17, cost: 1, isBuy: false }, // 40% energy to the hero // 40% энергии герою
- { id: 18, cost: 3, isBuy: false }, // 40% energy to the hero // 40% энергии герою
- { id: 19, cost: 8, isBuy: false }, // 80% energy to the hero // 80% энергии герою
- { id: 20, cost: 20, isBuy: false }, // 40% energy to all heroes // 40% энергии всем героям
- { id: 21, cost: 40, isBuy: false }, // Hero Resurrection // Воскрешение героя
- ]
-
- this.start = function () {
- send(JSON.stringify(callsExecuteTower), startTower);
- }
-
- /**
- * Getting data on the Tower
- *
- * Получаем данные по башне
- */
- function startTower(e) {
- res = e.results;
- towerGetInfo = res[0].result.response;
- if (!towerGetInfo) {
- endTower('noTower', res);
- return;
- }
- teamGetAll = res[1].result.response;
- teamGetFavor = res[2].result.response;
- inventoryGet = res[3].result.response;
- heroGetAll = Object.values(res[4].result.response);
-
- scullCoin = inventoryGet.coin[7] ?? 0;
-
- argsBattle.favor = teamGetFavor.tower;
- argsBattle.heroes = heroGetAll.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
- pet = teamGetAll.tower.filter(id => id >= 6000).pop();
- if (pet) {
- argsBattle.pet = pet;
- }
-
- checkFloor(towerGetInfo);
- }
-
- function fixHeroesTeam(argsBattle) {
- let fixHeroes = argsBattle.heroes.filter(e => !heroesStates[e]?.isDead);
- if (fixHeroes.length < 5) {
- heroGetAll = heroGetAll.filter(e => !heroesStates[e.id]?.isDead);
- fixHeroes = heroGetAll.sort((a, b) => b.power - a.power).slice(0, 5).map(e => e.id);
- Object.keys(argsBattle.favor).forEach(e => {
- if (!fixHeroes.includes(+e)) {
- delete argsBattle.favor[e];
- }
- })
- }
- argsBattle.heroes = fixHeroes;
- return argsBattle;
- }
-
- /**
- * Check the floor
- *
- * Проверяем этаж
- */
- function checkFloor(towerInfo) {
- lastTowerInfo = towerInfo;
- maySkipFloor = +towerInfo.maySkipFloor;
- floorNumber = +towerInfo.floorNumber;
- heroesStates = towerInfo.states.heroes;
- floorInfo = towerInfo.floor;
-
- /**
- * Is there at least one chest open on the floor
- * Открыт ли на этаже хоть один сундук
- */
- isOpenChest = false;
- if (towerInfo.floorType == "chest") {
- isOpenChest = towerInfo.floor.chests.reduce((n, e) => n + e.opened, 0);
- }
-
- setProgress(`${I18N('TOWER')}: ${I18N('FLOOR')} ${floorNumber}`);
- if (floorNumber > 49) {
- if (isOpenChest) {
- endTower('alreadyOpenChest 50 floor', floorNumber);
- return;
- }
- }
- /**
- * If the chest is open and you can skip floors, then move on
- * Если сундук открыт и можно скипать этажи, то переходим дальше
- */
- if (towerInfo.mayFullSkip && +towerInfo.teamLevel == 130) {
- if (isOpenChest) {
- nextOpenChest(floorNumber);
- } else {
- nextChestOpen(floorNumber);
- }
- return;
- }
-
- // console.log(towerInfo, scullCoin);
- switch (towerInfo.floorType) {
- case "battle":
- if (floorNumber <= maySkipFloor) {
- skipFloor();
- return;
- }
- if (floorInfo.state == 2) {
- nextFloor();
- return;
- }
- startBattle().then(endBattle);
- return;
- case "buff":
- checkBuff(towerInfo);
- return;
- case "chest":
- openChest(floorNumber);
- return;
- default:
- console.log('!', towerInfo.floorType, towerInfo);
- break;
- }
- }
-
- /**
- * Let's start the fight
- *
- * Начинаем бой
- */
- function startBattle() {
- return new Promise(function (resolve, reject) {
- towerStartBattle = {
- calls: [{
- name: "towerStartBattle",
- args: fixHeroesTeam(argsBattle),
- ident: "body"
- }]
- }
- send(JSON.stringify(towerStartBattle), resultBattle, resolve);
- });
- }
- /**
- * Returns the result of the battle in a promise
- *
- * Возращает резульат боя в промис
- */
- function resultBattle(resultBattles, resolve) {
- battleData = resultBattles.results[0].result.response;
- battleType = "get_tower";
- BattleCalc(battleData, battleType, function (result) {
- resolve(result);
- });
- }
- /**
- * Finishing the fight
- *
- * Заканчиваем бой
- */
- function endBattle(battleInfo) {
- if (battleInfo.result.stars >= 3) {
- endBattleCall = {
- calls: [{
- name: "towerEndBattle",
- args: {
- result: battleInfo.result,
- progress: battleInfo.progress,
- },
- ident: "body"
- }]
- }
- send(JSON.stringify(endBattleCall), resultEndBattle);
- } else {
- endTower('towerEndBattle win: false\n', battleInfo);
- }
- }
-
- /**
- * Getting and processing battle results
- *
- * Получаем и обрабатываем результаты боя
- */
- function resultEndBattle(e) {
- battleResult = e.results[0].result.response;
- if ('error' in battleResult) {
- endTower('errorBattleResult', battleResult);
- return;
- }
- if ('reward' in battleResult) {
- scullCoin += battleResult.reward?.coin[7] ?? 0;
- }
- nextFloor();
- }
-
- function nextFloor() {
- nextFloorCall = {
- calls: [{
- name: "towerNextFloor",
- args: {},
- ident: "body"
- }]
- }
- send(JSON.stringify(nextFloorCall), checkDataFloor);
- }
-
- function openChest(floorNumber) {
- floorNumber = floorNumber || 0;
- openChestCall = {
- calls: [{
- name: "towerOpenChest",
- args: {
- num: 2
- },
- ident: "body"
- }]
- }
- send(JSON.stringify(openChestCall), floorNumber < 50 ? nextFloor : lastChest);
- }
-
- function lastChest() {
- endTower('openChest 50 floor', floorNumber);
- }
-
- function skipFloor() {
- skipFloorCall = {
- calls: [{
- name: "towerSkipFloor",
- args: {},
- ident: "body"
- }]
- }
- send(JSON.stringify(skipFloorCall), checkDataFloor);
- }
-
- function checkBuff(towerInfo) {
- buffArr = towerInfo.floor;
- promises = [];
- for (let buff of buffArr) {
- buffInfo = buffIds[buff.id];
- if (buffInfo.isBuy && buffInfo.cost <= scullCoin) {
- scullCoin -= buffInfo.cost;
- promises.push(buyBuff(buff.id));
- }
- }
- Promise.all(promises).then(nextFloor);
- }
-
- function buyBuff(buffId) {
- return new Promise(function (resolve, reject) {
- buyBuffCall = {
- calls: [{
- name: "towerBuyBuff",
- args: {
- buffId
- },
- ident: "body"
- }]
- }
- send(JSON.stringify(buyBuffCall), resolve);
- });
- }
-
- function checkDataFloor(result) {
- towerInfo = result.results[0].result.response;
- if ('reward' in towerInfo && towerInfo.reward?.coin) {
- scullCoin += towerInfo.reward?.coin[7] ?? 0;
- }
- if ('tower' in towerInfo) {
- towerInfo = towerInfo.tower;
- }
- if ('skullReward' in towerInfo) {
- scullCoin += towerInfo.skullReward?.coin[7] ?? 0;
- }
- checkFloor(towerInfo);
- }
- /**
- * Getting tower rewards
- *
- * Получаем награды башни
- */
- function farmTowerRewards(reason) {
- let { pointRewards, points } = lastTowerInfo;
- let pointsAll = Object.getOwnPropertyNames(pointRewards);
- let farmPoints = pointsAll.filter(e => +e <= +points && !pointRewards[e]);
- if (!farmPoints.length) {
- return;
- }
- let farmTowerRewardsCall = {
- calls: [{
- name: "tower_farmPointRewards",
- args: {
- points: farmPoints
- },
- ident: "tower_farmPointRewards"
- }]
- }
-
- if (scullCoin > 0 && reason == 'openChest 50 floor') {
- farmTowerRewardsCall.calls.push({
- name: "tower_farmSkullReward",
- args: {},
- ident: "tower_farmSkullReward"
- });
- }
-
- send(JSON.stringify(farmTowerRewardsCall), () => { });
- }
-
- function fullSkipTower() {
- /**
- * Next chest
- *
- * Следующий сундук
- */
- function nextChest(n) {
- return {
- name: "towerNextChest",
- args: {},
- ident: "group_" + n + "_body"
- }
- }
- /**
- * Open chest
- *
- * Открыть сундук
- */
- function openChest(n) {
- return {
- name: "towerOpenChest",
- args: {
- "num": 2
- },
- ident: "group_" + n + "_body"
- }
- }
-
- const fullSkipTowerCall = {
- calls: []
- }
-
- let n = 0;
- for (let i = 0; i < 15; i++) {
- fullSkipTowerCall.calls.push(nextChest(++n));
- fullSkipTowerCall.calls.push(openChest(++n));
- }
-
- send(JSON.stringify(fullSkipTowerCall), data => {
- data.results[0] = data.results[28];
- checkDataFloor(data);
- });
- }
-
- function nextChestOpen(floorNumber) {
- const calls = [{
- name: "towerOpenChest",
- args: {
- num: 2
- },
- ident: "towerOpenChest"
- }];
-
- Send(JSON.stringify({ calls })).then(e => {
- nextOpenChest(floorNumber);
- });
- }
-
- function nextOpenChest(floorNumber) {
- if (floorNumber > 49) {
- endTower('openChest 50 floor', floorNumber);
- return;
- }
- if (floorNumber == 1) {
- fullSkipTower();
- return;
- }
-
- let nextOpenChestCall = {
- calls: [{
- name: "towerNextChest",
- args: {},
- ident: "towerNextChest"
- }, {
- name: "towerOpenChest",
- args: {
- num: 2
- },
- ident: "towerOpenChest"
- }]
- }
- send(JSON.stringify(nextOpenChestCall), checkDataFloor);
- }
-
- function endTower(reason, info) {
- console.log(reason, info);
- if (reason != 'noTower') {
- farmTowerRewards(reason);
- }
- setProgress(`${I18N('TOWER')} ${I18N('COMPLETED')}!`, true);
- resolve();
- }
-}
-
-/**
- * Passage of the arena of the titans
- *
- * Прохождение арены титанов
- */
-function testTitanArena() {
- return new Promise((resolve, reject) => {
- titAren = new executeTitanArena(resolve, reject);
- titAren.start();
- });
-}
-
-/**
- * Passage of the arena of the titans
- *
- * Прохождение арены титанов
- */
-function executeTitanArena(resolve, reject) {
- let titan_arena = [];
- let finishListBattle = [];
- /**
- * ID of the current batch
- *
- * Идетификатор текущей пачки
- */
- let currentRival = 0;
- /**
- * Number of attempts to finish off the pack
- *
- * Количество попыток добития пачки
- */
- let attempts = 0;
- /**
- * Was there an attempt to finish off the current shooting range
- *
- * Была ли попытка добития текущего тира
- */
- let isCheckCurrentTier = false;
- /**
- * Current shooting range
- *
- * Текущий тир
- */
- let currTier = 0;
- /**
- * Number of battles on the current dash
- *
- * Количество битв на текущем тире
- */
- let countRivalsTier = 0;
-
- let callsStart = {
- calls: [{
- name: "titanArenaGetStatus",
- args: {},
- ident: "titanArenaGetStatus"
- }, {
- name: "teamGetAll",
- args: {},
- ident: "teamGetAll"
- }]
- }
-
- this.start = function () {
- send(JSON.stringify(callsStart), startTitanArena);
- }
-
- function startTitanArena(data) {
- let titanArena = data.results[0].result.response;
- if (titanArena.status == 'disabled') {
- endTitanArena('disabled', titanArena);
- return;
- }
-
- let teamGetAll = data.results[1].result.response;
- titan_arena = teamGetAll.titan_arena;
-
- checkTier(titanArena)
- }
-
- function checkTier(titanArena) {
- if (titanArena.status == "peace_time") {
- endTitanArena('Peace_time', titanArena);
- return;
- }
- currTier = titanArena.tier;
- if (currTier) {
- setProgress(`${I18N('TITAN_ARENA')}: ${I18N('LEVEL')} ${currTier}`);
- }
-
- if (titanArena.status == "completed_tier") {
- titanArenaCompleteTier();
- return;
- }
- /**
- * Checking for the possibility of a raid
- * Проверка на возможность рейда
- */
- if (titanArena.canRaid) {
- titanArenaStartRaid();
- return;
- }
- /**
- * Check was an attempt to achieve the current shooting range
- * Проверка была ли попытка добития текущего тира
- */
- if (!isCheckCurrentTier) {
- checkRivals(titanArena.rivals);
- return;
- }
-
- endTitanArena('Done or not canRaid', titanArena);
- }
- /**
- * Submit dash information for verification
- *
- * Отправка информации о тире на проверку
- */
- function checkResultInfo(data) {
- let titanArena = data.results[0].result.response;
- checkTier(titanArena);
- }
- /**
- * Finish the current tier
- *
- * Завершить текущий тир
- */
- function titanArenaCompleteTier() {
- isCheckCurrentTier = false;
- let calls = [{
- name: "titanArenaCompleteTier",
- args: {},
- ident: "body"
- }];
- send(JSON.stringify({calls}), checkResultInfo);
- }
- /**
- * Gathering points to be completed
- *
- * Собираем точки которые нужно добить
- */
- function checkRivals(rivals) {
- finishListBattle = [];
- for (let n in rivals) {
- if (rivals[n].attackScore < 250) {
- finishListBattle.push(n);
- }
- }
- console.log('checkRivals', finishListBattle);
- countRivalsTier = finishListBattle.length;
- roundRivals();
- }
- /**
- * Selecting the next point to finish off
- *
- * Выбор следующей точки для добития
- */
- function roundRivals() {
- let countRivals = finishListBattle.length;
- if (!countRivals) {
- /**
- * Whole range checked
- *
- * Весь тир проверен
- */
- isCheckCurrentTier = true;
- titanArenaGetStatus();
- return;
- }
- // setProgress('TitanArena: Уровень ' + currTier + ' Бои: ' + (countRivalsTier - countRivals + 1) + '/' + countRivalsTier);
- currentRival = finishListBattle.pop();
- attempts = +currentRival;
- // console.log('roundRivals', currentRival);
- titanArenaStartBattle(currentRival);
- }
- /**
- * The start of a solo battle
- *
- * Начало одиночной битвы
- */
- function titanArenaStartBattle(rivalId) {
- let calls = [{
- name: "titanArenaStartBattle",
- args: {
- rivalId: rivalId,
- titans: titan_arena
- },
- ident: "body"
- }];
- send(JSON.stringify({calls}), calcResult);
- }
- /**
- * Calculation of the results of the battle
- *
- * Расчет результатов боя
- */
- function calcResult(data) {
- let battlesInfo = data.results[0].result.response.battle;
- /**
- * If attempts are equal to the current battle number we make
- * Если попытки равны номеру текущего боя делаем прерасчет
- */
- if (attempts == currentRival) {
- preCalcBattle(battlesInfo);
- return;
- }
- /**
- * If there are still attempts, we calculate a new battle
- * Если попытки еще есть делаем расчет нового боя
- */
- if (attempts > 0) {
- attempts--;
- calcBattleResult(battlesInfo)
- .then(resultCalcBattle);
- return;
- }
- /**
- * Otherwise, go to the next opponent
- * Иначе переходим к следующему сопернику
- */
- roundRivals();
- }
- /**
- * Processing the results of the battle calculation
- *
- * Обработка результатов расчета битвы
- */
- function resultCalcBattle(resultBattle) {
- // console.log('resultCalcBattle', currentRival, attempts, resultBattle.result.win);
- /**
- * If the current calculation of victory is not a chance or the attempt ended with the finish the battle
- * Если текущий расчет победа или шансов нет или попытки кончились завершаем бой
- */
- if (resultBattle.result.win || !attempts) {
- titanArenaEndBattle({
- progress: resultBattle.progress,
- result: resultBattle.result,
- rivalId: resultBattle.battleData.typeId
- });
- return;
- }
- /**
- * If not victory and there are attempts we start a new battle
- * Если не победа и есть попытки начинаем новый бой
- */
- titanArenaStartBattle(resultBattle.battleData.typeId);
- }
- /**
- * Returns the promise of calculating the results of the battle
- *
- * Возращает промис расчета результатов битвы
- */
- function getBattleInfo(battle, isRandSeed) {
- return new Promise(function (resolve) {
- if (isRandSeed) {
- battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3);
- }
- // console.log(battle.seed);
- BattleCalc(battle, "get_titanClanPvp", e => resolve(e));
- });
- }
- /**
- * Recalculate battles
- *
- * Прерасчтет битвы
- */
- function preCalcBattle(battle) {
- let actions = [getBattleInfo(battle, false)];
- const countTestBattle = getInput('countTestBattle');
- for (let i = 0; i < countTestBattle; i++) {
- actions.push(getBattleInfo(battle, true));
- }
- Promise.all(actions)
- .then(resultPreCalcBattle);
- }
- /**
- * Processing the results of the battle recalculation
- *
- * Обработка результатов прерасчета битвы
- */
- function resultPreCalcBattle(e) {
- let wins = e.map(n => n.result.win);
- let firstBattle = e.shift();
- let countWin = wins.reduce((w, s) => w + s);
- const countTestBattle = getInput('countTestBattle');
- console.log('resultPreCalcBattle', `${countWin}/${countTestBattle}`)
- if (countWin > 0) {
- attempts = getInput('countAutoBattle');
- } else {
- attempts = 0;
- }
- resultCalcBattle(firstBattle);
- }
-
- /**
- * Complete an arena battle
- *
- * Завершить битву на арене
- */
- function titanArenaEndBattle(args) {
- let calls = [{
- name: "titanArenaEndBattle",
- args,
- ident: "body"
- }];
- send(JSON.stringify({calls}), resultTitanArenaEndBattle);
- }
-
- function resultTitanArenaEndBattle(e) {
- let attackScore = e.results[0].result.response.attackScore;
- let numReval = countRivalsTier - finishListBattle.length;
- setProgress(`${I18N('TITAN_ARENA')}: ${I18N('LEVEL')} ${currTier} ${I18N('BATTLES')}: ${numReval}/${countRivalsTier} - ${attackScore}`);
- /**
- * TODO: Might need to improve the results.
- * TODO: Возможно стоит сделать улучшение результатов
- */
- // console.log('resultTitanArenaEndBattle', e)
- console.log('resultTitanArenaEndBattle', numReval + '/' + countRivalsTier, attempts)
- roundRivals();
- }
- /**
- * Arena State
- *
- * Состояние арены
- */
- function titanArenaGetStatus() {
- let calls = [{
- name: "titanArenaGetStatus",
- args: {},
- ident: "body"
- }];
- send(JSON.stringify({calls}), checkResultInfo);
- }
- /**
- * Arena Raid Request
- *
- * Запрос рейда арены
- */
- function titanArenaStartRaid() {
- let calls = [{
- name: "titanArenaStartRaid",
- args: {
- titans: titan_arena
- },
- ident: "body"
- }];
- send(JSON.stringify({calls}), calcResults);
- }
-
- function calcResults(data) {
- let battlesInfo = data.results[0].result.response;
- let {attackers, rivals} = battlesInfo;
-
- let promises = [];
- for (let n in rivals) {
- rival = rivals[n];
- promises.push(calcBattleResult({
- attackers: attackers,
- defenders: [rival.team],
- seed: rival.seed,
- typeId: n,
- }));
- }
-
- Promise.all(promises)
- .then(results => {
- const endResults = {};
- for (let info of results) {
- let id = info.battleData.typeId;
- endResults[id] = {
- progress: info.progress,
- result: info.result,
- }
- }
- titanArenaEndRaid(endResults);
- });
- }
-
- function calcBattleResult(battleData) {
- return new Promise(function (resolve, reject) {
- BattleCalc(battleData, "get_titanClanPvp", resolve);
- });
- }
-
- /**
- * Sending Raid Results
- *
- * Отправка результатов рейда
- */
- function titanArenaEndRaid(results) {
- titanArenaEndRaidCall = {
- calls: [{
- name: "titanArenaEndRaid",
- args: {
- results
- },
- ident: "body"
- }]
- }
- send(JSON.stringify(titanArenaEndRaidCall), checkRaidResults);
- }
-
- function checkRaidResults(data) {
- results = data.results[0].result.response.results;
- isSucsesRaid = true;
- for (let i in results) {
- isSucsesRaid &&= (results[i].attackScore >= 250);
- }
-
- if (isSucsesRaid) {
- titanArenaCompleteTier();
- } else {
- titanArenaGetStatus();
- }
- }
-
- function titanArenaFarmDailyReward() {
- titanArenaFarmDailyRewardCall = {
- calls: [{
- name: "titanArenaFarmDailyReward",
- args: {},
- ident: "body"
- }]
- }
- send(JSON.stringify(titanArenaFarmDailyRewardCall), () => {console.log('Done farm daily reward')});
- }
-
- function endTitanArena(reason, info) {
- if (!['Peace_time', 'disabled'].includes(reason)) {
- titanArenaFarmDailyReward();
- }
- console.log(reason, info);
- setProgress(`${I18N('TITAN_ARENA')} ${I18N('COMPLETED')}!`, true);
- resolve();
- }
-}
-
-function hackGame() {
- self = this;
- selfGame = null;
- bindId = 1e9;
- this.libGame = null;
-
- /**
- * List of correspondence of used classes to their names
- *
- * Список соответствия используемых классов их названиям
- */
- ObjectsList = [
- { name: 'BattlePresets', prop: 'game.battle.controller.thread.BattlePresets' },
- { name: 'DataStorage', prop: 'game.data.storage.DataStorage' },
- { name: 'BattleConfigStorage', prop: 'game.data.storage.battle.BattleConfigStorage' },
- { name: 'BattleInstantPlay', prop: 'game.battle.controller.instant.BattleInstantPlay' },
- { name: 'MultiBattleInstantReplay', prop: 'game.battle.controller.instant.MultiBattleInstantReplay' },
- { name: 'MultiBattleResult', prop: 'game.battle.controller.MultiBattleResult' },
-
- { name: 'PlayerMissionData', prop: 'game.model.user.mission.PlayerMissionData' },
- { name: 'PlayerMissionBattle', prop: 'game.model.user.mission.PlayerMissionBattle' },
- { name: 'GameModel', prop: 'game.model.GameModel' },
- { name: 'CommandManager', prop: 'game.command.CommandManager' },
- { name: 'MissionCommandList', prop: 'game.command.rpc.mission.MissionCommandList' },
- { name: 'RPCCommandBase', prop: 'game.command.rpc.RPCCommandBase' },
- { name: 'PlayerTowerData', prop: 'game.model.user.tower.PlayerTowerData' },
- { name: 'TowerCommandList', prop: 'game.command.tower.TowerCommandList' },
- { name: 'PlayerHeroTeamResolver', prop: 'game.model.user.hero.PlayerHeroTeamResolver' },
- { name: 'BattlePausePopup', prop: 'game.view.popup.battle.BattlePausePopup' },
- { name: 'BattlePopup', prop: 'game.view.popup.battle.BattlePopup' },
- { name: 'DisplayObjectContainer', prop: 'starling.display.DisplayObjectContainer' },
- { name: 'GuiClipContainer', prop: 'engine.core.clipgui.GuiClipContainer' },
- { name: 'BattlePausePopupClip', prop: 'game.view.popup.battle.BattlePausePopupClip' },
- { name: 'ClipLabel', prop: 'game.view.gui.components.ClipLabel' },
- { name: 'ClipLabelBase', prop: 'game.view.gui.components.ClipLabelBase' },
- { name: 'Translate', prop: 'com.progrestar.common.lang.Translate' },
- { name: 'ClipButtonLabeledCentered', prop: 'game.view.gui.components.ClipButtonLabeledCentered' },
- { name: 'BattlePausePopupMediator', prop: 'game.mediator.gui.popup.battle.BattlePausePopupMediator' },
- { name: 'SettingToggleButton', prop: 'game.mechanics.settings.popup.view.SettingToggleButton' },
- { name: 'PlayerDungeonData', prop: 'game.mechanics.dungeon.model.PlayerDungeonData' },
- { name: 'NextDayUpdatedManager', prop: 'game.model.user.NextDayUpdatedManager' },
- { name: 'BattleController', prop: 'game.battle.controller.BattleController' },
- { name: 'BattleSettingsModel', prop: 'game.battle.controller.BattleSettingsModel' },
- { name: 'BooleanProperty', prop: 'engine.core.utils.property.BooleanProperty' },
- { name: 'RuleStorage', prop: 'game.data.storage.rule.RuleStorage' },
- { name: 'BattleConfig', prop: 'battle.BattleConfig' },
- { name: 'BattleGuiMediator', prop: 'game.battle.gui.BattleGuiMediator' },
- { name: 'BooleanPropertyWriteable', prop: 'engine.core.utils.property.BooleanPropertyWriteable' },
- { name: 'BattleLogEncoder', prop: 'battle.log.BattleLogEncoder' },
- { name: 'BattleLogReader', prop: 'battle.log.BattleLogReader' },
- { name: 'PlayerSubscriptionInfoValueObject', prop: 'game.model.user.subscription.PlayerSubscriptionInfoValueObject' },
- { name: 'AdventureMapCamera', prop: 'game.mechanics.adventure.popup.map.AdventureMapCamera' },
- ];
-
- /**
- * Contains the game classes needed to write and override game methods
- *
- * Содержит классы игры необходимые для написания и подмены методов игры
- */
- Game = {
- /**
- * Function 'e'
- * Функция 'e'
- */
- bindFunc: function (a, b) {
- if (null == b)
- return null;
- null == b.__id__ && (b.__id__ = bindId++);
- var c;
- null == a.hx__closures__ ? a.hx__closures__ = {} :
- c = a.hx__closures__[b.__id__];
- null == c && (c = b.bind(a), a.hx__closures__[b.__id__] = c);
- return c
- },
- };
-
- /**
- * Connects to game objects via the object creation event
- *
- * Подключается к объектам игры через событие создания объекта
- */
- function connectGame() {
- for (let obj of ObjectsList) {
- /**
- * https: //stackoverflow.com/questions/42611719/how-to-intercept-and-modify-a-specific-property-for-any-object
- */
- Object.defineProperty(Object.prototype, obj.prop, {
- set: function (value) {
- if (!selfGame) {
- selfGame = this;
- }
- if (!Game[obj.name]) {
- Game[obj.name] = value;
- }
- // console.log('set ' + obj.prop, this, value);
- this[obj.prop + '_'] = value;
- },
- get: function () {
- // console.log('get ' + obj.prop, this);
- return this[obj.prop + '_'];
- }
- });
- }
- }
-
- /**
- * Game.BattlePresets
- * @param {bool} a isReplay
- * @param {bool} b autoToggleable
- * @param {bool} c auto On Start
- * @param {object} d config
- * @param {bool} f showBothTeams
- */
- /**
- * Returns the results of the battle to the callback function
- * Возвращает в функцию callback результаты боя
- * @param {*} battleData battle data данные боя
- * @param {*} battleConfig combat configuration type options:
- *
- * тип конфигурации боя варианты:
- *
- * "get_invasion", "get_titanPvpManual", "get_titanPvp",
- * "get_titanClanPvp","get_clanPvp","get_titan","get_boss",
- * "get_tower","get_pve","get_pvpManual","get_pvp","get_core"
- *
- * You can specify the xYc function in the game.assets.storage.BattleAssetStorage class
- *
- * Можно уточнить в классе game.assets.storage.BattleAssetStorage функция xYc
- * @param {*} callback функция в которую вернуться результаты боя
- */
- this.BattleCalc = function (battleData, battleConfig, callback) {
- // battleConfig = battleConfig || getBattleType(battleData.type)
- if (!Game.BattlePresets) throw Error('Use connectGame');
- battlePresets = new Game.BattlePresets(battleData.progress, !1, !0, Game.DataStorage[getFn(Game.DataStorage, 24)][getF(Game.BattleConfigStorage, battleConfig)](), !1);
- let battleInstantPlay;
- if (battleData.progress?.length > 1) {
- battleInstantPlay = new Game.MultiBattleInstantReplay(battleData, battlePresets);
- } else {
- battleInstantPlay = new Game.BattleInstantPlay(battleData, battlePresets);
- }
- battleInstantPlay[getProtoFn(Game.BattleInstantPlay, 9)].add((battleInstant) => {
- const MBR_2 = getProtoFn(Game.MultiBattleResult, 2);
- const battleResults = battleInstant[getF(Game.BattleInstantPlay, 'get_result')]();
- const battleData = battleInstant[getF(Game.BattleInstantPlay, 'get_rawBattleInfo')]();
- const battleLogs = [];
- const timeLimit = battlePresets[getF(Game.BattlePresets, 'get_timeLimit')]();
- let battleTime = 0;
- let battleTimer = 0;
- for (const battleResult of battleResults[MBR_2]) {
- const battleLog = Game.BattleLogEncoder.read(new Game.BattleLogReader(battleResult));
- battleLogs.push(battleLog);
- const maxTime = Math.max(...battleLog.map((e) => (e.time < timeLimit && e.time !== 168.8 ? e.time : 0)));
- battleTimer += getTimer(maxTime)
- battleTime += maxTime;
- }
- callback({
- battleLogs,
- battleTime,
- battleTimer,
- battleData,
- progress: battleResults[getF(Game.MultiBattleResult, 'get_progress')](),
- result: battleResults[getF(Game.MultiBattleResult, 'get_result')](),
- });
- });
- battleInstantPlay.start();
- }
-
- /**
- * Returns a function with the specified name from the class
- *
- * Возвращает из класса функцию с указанным именем
- * @param {Object} classF Class // класс
- * @param {String} nameF function name // имя функции
- * @param {String} pos name and alias order // порядок имени и псевдонима
- * @returns
- */
- function getF(classF, nameF, pos) {
- pos = pos || false;
- let prop = Object.entries(classF.prototype.__properties__)
- if (!pos) {
- return prop.filter((e) => e[1] == nameF).pop()[0];
- } else {
- return prop.filter((e) => e[0] == nameF).pop()[1];
- }
- }
-
- /**
- * Returns a function with the specified name from the class
- *
- * Возвращает из класса функцию с указанным именем
- * @param {Object} classF Class // класс
- * @param {String} nameF function name // имя функции
- * @returns
- */
- function getFnP(classF, nameF) {
- let prop = Object.entries(classF.__properties__)
- return prop.filter((e) => e[1] == nameF).pop()[0];
- }
-
- /**
- * Returns the function name with the specified ordinal from the class
- *
- * Возвращает имя функции с указаным порядковым номером из класса
- * @param {Object} classF Class // класс
- * @param {Number} nF Order number of function // порядковый номер функции
- * @returns
- */
- function getFn(classF, nF) {
- let prop = Object.keys(classF);
- return prop[nF];
- }
-
- /**
- * Returns the name of the function with the specified serial number from the prototype of the class
- *
- * Возвращает имя функции с указаным порядковым номером из прототипа класса
- * @param {Object} classF Class // класс
- * @param {Number} nF Order number of function // порядковый номер функции
- * @returns
- */
- function getProtoFn(classF, nF) {
- let prop = Object.keys(classF.prototype);
- return prop[nF];
- }
- /**
- * Description of replaced functions
- *
- * Описание подменяемых функций
- */
- replaceFunction = {
- company: function () {
- let PMD_12 = getProtoFn(Game.PlayerMissionData, 12);
- let oldSkipMisson = Game.PlayerMissionData.prototype[PMD_12];
- Game.PlayerMissionData.prototype[PMD_12] = function (a, b, c) {
- if (!isChecked('passBattle')) {
- oldSkipMisson.call(this, a, b, c);
- return;
- }
-
- try {
- this[getProtoFn(Game.PlayerMissionData, 9)] = new Game.PlayerMissionBattle(a, b, c);
-
- var a = new Game.BattlePresets(
- !1,
- !1,
- !0,
- Game.DataStorage[getFn(Game.DataStorage, 24)][getProtoFn(Game.BattleConfigStorage, 20)](),
- !1
- );
- a = new Game.BattleInstantPlay(c, a);
- a[getProtoFn(Game.BattleInstantPlay, 9)].add(Game.bindFunc(this, this.P$h));
- a.start();
- } catch (error) {
- console.error('company', error);
- oldSkipMisson.call(this, a, b, c);
- }
- };
-
- Game.PlayerMissionData.prototype.P$h = function (a) {
- let GM_2 = getFn(Game.GameModel, 2);
- let GM_P2 = getProtoFn(Game.GameModel, 2);
- let CM_20 = getProtoFn(Game.CommandManager, 20);
- let MCL_2 = getProtoFn(Game.MissionCommandList, 2);
- let MBR_15 = getF(Game.MultiBattleResult, 'get_result');
- let RPCCB_15 = getProtoFn(Game.RPCCommandBase, 16);
- let PMD_32 = getProtoFn(Game.PlayerMissionData, 32);
- Game.GameModel[GM_2]()[GM_P2][CM_20][MCL_2](a[MBR_15]())[RPCCB_15](Game.bindFunc(this, this[PMD_32]));
- };
- },
- tower: function () {
- let PTD_67 = getProtoFn(Game.PlayerTowerData, 67);
- let oldSkipTower = Game.PlayerTowerData.prototype[PTD_67];
- Game.PlayerTowerData.prototype[PTD_67] = function (a) {
- if (!isChecked('passBattle')) {
- oldSkipTower.call(this, a);
- return;
- }
- try {
- var p = new Game.BattlePresets(
- !1,
- !1,
- !0,
- Game.DataStorage[getFn(Game.DataStorage, 24)][getProtoFn(Game.BattleConfigStorage, 20)](),
- !1
- );
- a = new Game.BattleInstantPlay(a, p);
- a[getProtoFn(Game.BattleInstantPlay, 9)].add(Game.bindFunc(this, this.P$h));
- a.start();
- } catch (error) {
- console.error('tower', error);
- oldSkipMisson.call(this, a, b, c);
- }
- };
-
- Game.PlayerTowerData.prototype.P$h = function (a) {
- const GM_2 = getFnP(Game.GameModel, 'get_instance');
- const GM_P2 = getProtoFn(Game.GameModel, 2);
- const CM_29 = getProtoFn(Game.CommandManager, 29);
- const TCL_5 = getProtoFn(Game.TowerCommandList, 5);
- const MBR_15 = getF(Game.MultiBattleResult, 'get_result');
- const RPCCB_15 = getProtoFn(Game.RPCCommandBase, 17);
- const PTD_78 = getProtoFn(Game.PlayerTowerData, 78);
- Game.GameModel[GM_2]()[GM_P2][CM_29][TCL_5](a[MBR_15]())[RPCCB_15](Game.bindFunc(this, this[PTD_78]));
- };
- },
- // skipSelectHero: function() {
- // if (!HOST) throw Error('Use connectGame');
- // Game.PlayerHeroTeamResolver.prototype[getProtoFn(Game.PlayerHeroTeamResolver, 3)] = () => false;
- // },
- passBattle: function () {
- let BPP_4 = getProtoFn(Game.BattlePausePopup, 4);
- let oldPassBattle = Game.BattlePausePopup.prototype[BPP_4];
- Game.BattlePausePopup.prototype[BPP_4] = function (a) {
- if (!isChecked('passBattle')) {
- oldPassBattle.call(this, a);
- return;
- }
- try {
- Game.BattlePopup.prototype[getProtoFn(Game.BattlePausePopup, 4)].call(this, a);
- this[getProtoFn(Game.BattlePausePopup, 3)]();
- this[getProtoFn(Game.DisplayObjectContainer, 3)](this.clip[getProtoFn(Game.GuiClipContainer, 2)]());
- this.clip[getProtoFn(Game.BattlePausePopupClip, 1)][getProtoFn(Game.ClipLabelBase, 9)](
- Game.Translate.translate('UI_POPUP_BATTLE_PAUSE')
- );
-
- this.clip[getProtoFn(Game.BattlePausePopupClip, 2)][getProtoFn(Game.ClipButtonLabeledCentered, 2)](
- Game.Translate.translate('UI_POPUP_BATTLE_RETREAT'),
- ((q = this[getProtoFn(Game.BattlePausePopup, 1)]), Game.bindFunc(q, q[getProtoFn(Game.BattlePausePopupMediator, 17)]))
- );
- this.clip[getProtoFn(Game.BattlePausePopupClip, 5)][getProtoFn(Game.ClipButtonLabeledCentered, 2)](
- this[getProtoFn(Game.BattlePausePopup, 1)][getProtoFn(Game.BattlePausePopupMediator, 14)](),
- this[getProtoFn(Game.BattlePausePopup, 1)][getProtoFn(Game.BattlePausePopupMediator, 13)]()
- ? ((q = this[getProtoFn(Game.BattlePausePopup, 1)]), Game.bindFunc(q, q[getProtoFn(Game.BattlePausePopupMediator, 18)]))
- : ((q = this[getProtoFn(Game.BattlePausePopup, 1)]), Game.bindFunc(q, q[getProtoFn(Game.BattlePausePopupMediator, 18)]))
- );
-
- this.clip[getProtoFn(Game.BattlePausePopupClip, 5)][getProtoFn(Game.ClipButtonLabeledCentered, 0)][
- getProtoFn(Game.ClipLabelBase, 24)
- ]();
- this.clip[getProtoFn(Game.BattlePausePopupClip, 3)][getProtoFn(Game.SettingToggleButton, 3)](
- this[getProtoFn(Game.BattlePausePopup, 1)][getProtoFn(Game.BattlePausePopupMediator, 9)]()
- );
- this.clip[getProtoFn(Game.BattlePausePopupClip, 4)][getProtoFn(Game.SettingToggleButton, 3)](
- this[getProtoFn(Game.BattlePausePopup, 1)][getProtoFn(Game.BattlePausePopupMediator, 10)]()
- );
- this.clip[getProtoFn(Game.BattlePausePopupClip, 6)][getProtoFn(Game.SettingToggleButton, 3)](
- this[getProtoFn(Game.BattlePausePopup, 1)][getProtoFn(Game.BattlePausePopupMediator, 11)]()
- );
- } catch (error) {
- console.error('passBattle', error);
- oldPassBattle.call(this, a);
- }
- };
-
- let retreatButtonLabel = getF(Game.BattlePausePopupMediator, 'get_retreatButtonLabel');
- let oldFunc = Game.BattlePausePopupMediator.prototype[retreatButtonLabel];
- Game.BattlePausePopupMediator.prototype[retreatButtonLabel] = function () {
- if (isChecked('passBattle')) {
- return I18N('BTN_PASS');
- } else {
- return oldFunc.call(this);
- }
- };
- },
- endlessCards: function () {
- let PDD_21 = getProtoFn(Game.PlayerDungeonData, 21);
- let oldEndlessCards = Game.PlayerDungeonData.prototype[PDD_21];
- Game.PlayerDungeonData.prototype[PDD_21] = function () {
- if (countPredictionCard <= 0) {
- return true;
- } else {
- return oldEndlessCards.call(this);
- }
- };
- },
- speedBattle: function () {
- const get_timeScale = getF(Game.BattleController, 'get_timeScale');
- const oldSpeedBattle = Game.BattleController.prototype[get_timeScale];
- Game.BattleController.prototype[get_timeScale] = function () {
- const speedBattle = Number.parseFloat(getInput('speedBattle'));
- if (!speedBattle) {
- return oldSpeedBattle.call(this);
- }
- try {
- const BC_12 = getProtoFn(Game.BattleController, 12);
- const BSM_12 = getProtoFn(Game.BattleSettingsModel, 12);
- const BP_get_value = getF(Game.BooleanProperty, 'get_value');
- if (this[BC_12][BSM_12][BP_get_value]()) {
- return 0;
- }
- const BSM_2 = getProtoFn(Game.BattleSettingsModel, 2);
- const BC_49 = getProtoFn(Game.BattleController, 49);
- const BSM_1 = getProtoFn(Game.BattleSettingsModel, 1);
- const BC_14 = getProtoFn(Game.BattleController, 14);
- const BC_3 = getFn(Game.BattleController, 3);
- if (this[BC_12][BSM_2][BP_get_value]()) {
- var a = speedBattle * this[BC_49]();
- } else {
- a = this[BC_12][BSM_1][BP_get_value]();
- const maxSpeed = Math.max(...this[BC_14]);
- const multiple = a == this[BC_14].indexOf(maxSpeed) ? (maxSpeed >= 4 ? speedBattle : this[BC_14][a]) : this[BC_14][a];
- a = multiple * Game.BattleController[BC_3][BP_get_value]() * this[BC_49]();
- }
- const BSM_24 = getProtoFn(Game.BattleSettingsModel, 24);
- a > this[BC_12][BSM_24][BP_get_value]() && (a = this[BC_12][BSM_24][BP_get_value]());
- const DS_23 = getFn(Game.DataStorage, 23);
- const get_battleSpeedMultiplier = getF(Game.RuleStorage, 'get_battleSpeedMultiplier', true);
- var b = Game.DataStorage[DS_23][get_battleSpeedMultiplier]();
- const R_1 = getFn(selfGame.Reflect, 1);
- const BC_1 = getFn(Game.BattleController, 1);
- const get_config = getF(Game.BattlePresets, 'get_config');
- null != b &&
- (a = selfGame.Reflect[R_1](b, this[BC_1][get_config]().ident)
- ? a * selfGame.Reflect[R_1](b, this[BC_1][get_config]().ident)
- : a * selfGame.Reflect[R_1](b, 'default'));
- return a;
- } catch (error) {
- console.error('passBatspeedBattletle', error);
- return oldSpeedBattle.call(this);
- }
- };
- },
-
- /**
- * Acceleration button without Valkyries favor
- *
- * Кнопка ускорения без Покровительства Валькирий
- */
- battleFastKey: function () {
- const BGM_43 = getProtoFn(Game.BattleGuiMediator, 43);
- const oldBattleFastKey = Game.BattleGuiMediator.prototype[BGM_43];
- Game.BattleGuiMediator.prototype[BGM_43] = 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, 16);
- const newFuncName = getProtoFn(GameNavigator, 14);
- const oldFastSeason = GameNavigator.prototype[oldFuncName];
- const newFastSeason = GameNavigator.prototype[newFuncName];
- GameNavigator.prototype[oldFuncName] = function (a, b) {
- if (isChecked('fastSeason')) {
- return newFastSeason.apply(this, [a]);
- } else {
- return oldFastSeason.apply(this, [a, b]);
- }
- };
- },
- ShowChestReward: function () {
- const TitanArtifactChest = selfGame['game.mechanics.titan_arena.mediator.chest.TitanArtifactChestRewardPopupMediator'];
- const getOpenAmountTitan = getF(TitanArtifactChest, 'get_openAmount');
- const oldGetOpenAmountTitan = TitanArtifactChest.prototype[getOpenAmountTitan];
- TitanArtifactChest.prototype[getOpenAmountTitan] = function () {
- if (correctShowOpenArtifact) {
- correctShowOpenArtifact--;
- return 100;
- }
- return oldGetOpenAmountTitan.call(this);
- };
-
- const ArtifactChest = selfGame['game.view.popup.artifactchest.rewardpopup.ArtifactChestRewardPopupMediator'];
- const getOpenAmount = getF(ArtifactChest, 'get_openAmount');
- const oldGetOpenAmount = ArtifactChest.prototype[getOpenAmount];
- ArtifactChest.prototype[getOpenAmount] = function () {
- if (correctShowOpenArtifact) {
- correctShowOpenArtifact--;
- return 100;
- }
- return oldGetOpenAmount.call(this);
- };
- },
- fixCompany: function () {
- const GameBattleView = selfGame['game.mediator.gui.popup.battle.GameBattleView'];
- const BattleThread = selfGame['game.battle.controller.thread.BattleThread'];
- const getOnViewDisposed = getF(BattleThread, 'get_onViewDisposed');
- const getThread = getF(GameBattleView, 'get_thread');
- const oldFunc = GameBattleView.prototype[getThread];
- GameBattleView.prototype[getThread] = function () {
- return (
- oldFunc.call(this) || {
- [getOnViewDisposed]: async () => {},
- }
- );
- };
- },
- BuyTitanArtifact: function () {
- const BIP_4 = getProtoFn(selfGame['game.view.popup.shop.buy.BuyItemPopup'], 4);
- const BuyItemPopup = selfGame['game.view.popup.shop.buy.BuyItemPopup'];
- const oldFunc = BuyItemPopup.prototype[BIP_4];
- BuyItemPopup.prototype[BIP_4] = function () {
- if (isChecked('countControl')) {
- const BuyTitanArtifactItemPopup = selfGame['game.view.popup.shop.buy.BuyTitanArtifactItemPopup'];
- const BTAP_0 = getProtoFn(BuyTitanArtifactItemPopup, 0);
- if (this[BTAP_0]) {
- const BuyTitanArtifactPopupMediator = selfGame['game.mediator.gui.popup.shop.buy.BuyTitanArtifactItemPopupMediator'];
- const BTAM_1 = getProtoFn(BuyTitanArtifactPopupMediator, 1);
- const BuyItemPopupMediator = selfGame['game.mediator.gui.popup.shop.buy.BuyItemPopupMediator'];
- const BIPM_5 = getProtoFn(BuyItemPopupMediator, 5);
- const BIPM_7 = getProtoFn(BuyItemPopupMediator, 7);
- const BIPM_9 = getProtoFn(BuyItemPopupMediator, 9);
-
- let need = Math.min(this[BTAP_0][BTAM_1](), this[BTAP_0][BIPM_7]);
- need = need ? need : 60;
- this[BTAP_0][BIPM_9] = need;
- this[BTAP_0][BIPM_5] = 10;
- }
- }
- oldFunc.call(this);
- };
- },
- ClanQuestsFastFarm: function () {
- const VipRuleValueObject = selfGame['game.data.storage.rule.VipRuleValueObject'];
- const getClanQuestsFastFarm = getF(VipRuleValueObject, 'get_clanQuestsFastFarm', 1);
- VipRuleValueObject.prototype[getClanQuestsFastFarm] = function () {
- return 0;
- };
- },
- adventureCamera: function () {
- const AMC_40 = getProtoFn(Game.AdventureMapCamera, 40);
- const AMC_5 = getProtoFn(Game.AdventureMapCamera, 5);
- const oldFunc = Game.AdventureMapCamera.prototype[AMC_40];
- Game.AdventureMapCamera.prototype[AMC_40] = function (a) {
- this[AMC_5] = 0.4;
- oldFunc.bind(this)(a);
- };
- },
- unlockMission: function () {
- const WorldMapStoryDrommerHelper = selfGame['game.mediator.gui.worldmap.WorldMapStoryDrommerHelper'];
- const WMSDH_4 = getFn(WorldMapStoryDrommerHelper, 4);
- const WMSDH_7 = getFn(WorldMapStoryDrommerHelper, 7);
- WorldMapStoryDrommerHelper[WMSDH_4] = function () {
- return true;
- };
- WorldMapStoryDrommerHelper[WMSDH_7] = function () {
- return true;
- };
- },
- };
-
- /**
- * Starts replacing recorded functions
- *
- * Запускает замену записанных функций
- */
- this.activateHacks = function () {
- if (!selfGame) throw Error('Use connectGame');
- for (let func in replaceFunction) {
- replaceFunction[func]();
- }
- }
-
- /**
- * Returns the game object
- *
- * Возвращает объект игры
- */
- this.getSelfGame = function () {
- return selfGame;
- }
-
- /**
- * Updates game data
- *
- * Обновляет данные игры
- */
- this.refreshGame = function () {
- (new Game.NextDayUpdatedManager)[getProtoFn(Game.NextDayUpdatedManager, 5)]();
- try {
- cheats.refreshInventory();
- } catch (e) { }
- }
-
- /**
- * Update inventory
- *
- * Обновляет инвентарь
- */
- this.refreshInventory = async function () {
- const GM_INST = getFnP(Game.GameModel, "get_instance");
- const GM_0 = getProtoFn(Game.GameModel, 0);
- const P_24 = getProtoFn(selfGame["game.model.user.Player"], 24);
- const Player = Game.GameModel[GM_INST]()[GM_0];
- Player[P_24] = new selfGame["game.model.user.inventory.PlayerInventory"]
- Player[P_24].init(await Send({calls:[{name:"inventoryGet",args:{},ident:"body"}]}).then(e => e.results[0].result.response))
- }
- this.updateInventory = function (reward) {
- const GM_INST = getFnP(Game.GameModel, 'get_instance');
- const GM_0 = getProtoFn(Game.GameModel, 0);
- const P_24 = getProtoFn(selfGame['game.model.user.Player'], 24);
- const Player = Game.GameModel[GM_INST]()[GM_0];
- Player[P_24].init(reward);
- };
-
- this.updateMap = function (data) {
- const PCDD_21 = getProtoFn(selfGame['game.mechanics.clanDomination.model.PlayerClanDominationData'], 21);
- const P_60 = getProtoFn(selfGame['game.model.user.Player'], 60);
- const GM_0 = getProtoFn(Game.GameModel, 0);
- const getInstance = getFnP(selfGame['Game'], 'get_instance');
- const PlayerClanDominationData = Game.GameModel[getInstance]()[GM_0];
- PlayerClanDominationData[P_60][PCDD_21].update(data);
- };
-
- /**
- * Change the play screen on windowName
- *
- * Сменить экран игры на windowName
- *
- * Possible options:
- *
- * Возможные варианты:
- *
- * MISSION, ARENA, GRAND, CHEST, SKILLS, SOCIAL_GIFT, CLAN, ENCHANT, TOWER, RATING, CHALLENGE, BOSS, CHAT, CLAN_DUNGEON, CLAN_CHEST, TITAN_GIFT, CLAN_RAID, ASGARD, HERO_ASCENSION, ROLE_ASCENSION, ASCENSION_CHEST, TITAN_MISSION, TITAN_ARENA, TITAN_ARTIFACT, TITAN_ARTIFACT_CHEST, TITAN_VALLEY, TITAN_SPIRITS, TITAN_ARTIFACT_MERCHANT, TITAN_ARENA_HALL_OF_FAME, CLAN_PVP, CLAN_PVP_MERCHANT, CLAN_GLOBAL_PVP, CLAN_GLOBAL_PVP_TITAN, ARTIFACT, ZEPPELIN, ARTIFACT_CHEST, ARTIFACT_MERCHANT, EXPEDITIONS, SUBSCRIPTION, NY2018_GIFTS, NY2018_TREE, NY2018_WELCOME, ADVENTURE, ADVENTURESOLO, SANCTUARY, PET_MERCHANT, PET_LIST, PET_SUMMON, BOSS_RATING_EVENT, BRAWL
- */
- this.goNavigtor = function (windowName) {
- let mechanicStorage = selfGame["game.data.storage.mechanic.MechanicStorage"];
- let window = mechanicStorage[windowName];
- let event = new selfGame["game.mediator.gui.popup.PopupStashEventParams"];
- let Game = selfGame['Game'];
- let navigator = getF(Game, "get_navigator")
- let navigate = getProtoFn(selfGame["game.screen.navigator.GameNavigator"], 18)
- 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");
- }
-
- /**
- * Go to Guild War
- *
- * Перейти к Войне Гильдий
- */
- this.goClanWar = function() {
- let instance = getFnP(Game.GameModel, 'get_instance')
- let player = Game.GameModel[instance]().A;
- let clanWarSelect = selfGame["game.mechanics.cross_clan_war.popup.selectMode.CrossClanWarSelectModeMediator"];
- new clanWarSelect(player).open();
- }
-
- /**
- * Go to BrawlShop
- *
- * Переместиться в BrawlShop
- */
- this.goBrawlShop = () => {
- const instance = getFnP(Game.GameModel, 'get_instance')
- const P_36 = getProtoFn(selfGame["game.model.user.Player"], 36);
- const PSD_0 = getProtoFn(selfGame["game.model.user.shop.PlayerShopData"], 0);
- const IM_0 = getProtoFn(selfGame["haxe.ds.IntMap"], 0);
- const PSDE_4 = getProtoFn(selfGame["game.model.user.shop.PlayerShopDataEntry"], 4);
-
- const player = Game.GameModel[instance]().A;
- const shop = player[P_36][PSD_0][IM_0][1038][PSDE_4];
- const shopPopup = new selfGame["game.mechanics.brawl.mediator.BrawlShopPopupMediator"](player, shop)
- shopPopup.open(new selfGame["game.mediator.gui.popup.PopupStashEventParams"])
- }
-
- /**
- * Returns all stores from game data
- *
- * Возвращает все магазины из данных игры
- */
- this.getShops = () => {
- const instance = getFnP(Game.GameModel, 'get_instance')
- const P_36 = getProtoFn(selfGame["game.model.user.Player"], 36);
- const PSD_0 = getProtoFn(selfGame["game.model.user.shop.PlayerShopData"], 0);
- const IM_0 = getProtoFn(selfGame["haxe.ds.IntMap"], 0);
-
- const player = Game.GameModel[instance]().A;
- return player[P_36][PSD_0][IM_0];
- }
-
- /**
- * Returns the store from the game data by ID
- *
- * Возвращает магазин из данных игры по идетификатору
- */
- this.getShop = (id) => {
- const PSDE_4 = getProtoFn(selfGame["game.model.user.shop.PlayerShopDataEntry"], 4);
- const shops = this.getShops();
- const shop = shops[id]?.[PSDE_4];
- return shop;
- }
-
- /**
- * Change island map
- *
- * Сменить карту острова
- */
- this.changeIslandMap = (mapId = 2) => {
- const GameInst = getFnP(selfGame['Game'], 'get_instance');
- const GM_0 = getProtoFn(Game.GameModel, 0);
- const P_59 = getProtoFn(selfGame["game.model.user.Player"], 59);
- const PSAD_31 = getProtoFn(selfGame['game.mechanics.season_adventure.model.PlayerSeasonAdventureData'], 31);
- const Player = Game.GameModel[GameInst]()[GM_0];
- Player[P_59][PSAD_31]({ id: mapId, seasonAdventure: { id: mapId, startDate: 1701914400, endDate: 1709690400, closed: false } });
-
- const GN_15 = getProtoFn(selfGame["game.screen.navigator.GameNavigator"], 15)
- 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;
- try {
- const levels = b.raw.seasonAdventure.level;
- for (const id in levels) {
- const level = levels[id];
- level.clientData.graphics.fogged = level.clientData.graphics.visible
- }
- const adv = b.raw.seasonAdventure.list[1];
- adv.clientData.asset = 'dialog_season_adventure_tiles';
- } catch (e) {
- console.warn(e);
- }
- originalStartFunc.call(this, a, b, c);
- }
- }
-
- /**
- * Returns the value of a language constant
- *
- * Возвращает значение языковой константы
- * @param {*} langConst language constant // языковая константа
- * @returns
- */
- this.translate = function (langConst) {
- return Game.Translate.translate(langConst);
- }
-
- connectGame();
- checkLibLoad();
-}
-
-/**
- * Auto collection of gifts
- *
- * Автосбор подарков
- */
-function getAutoGifts() {
- // c3ltYm9scyB0aGF0IG1lYW4gbm90aGluZw==
- let valName = 'giftSendIds_' + userInfo.id;
-
- if (!localStorage['clearGift' + userInfo.id]) {
- localStorage[valName] = '';
- localStorage['clearGift' + userInfo.id] = '+';
- }
-
- if (!localStorage[valName]) {
- localStorage[valName] = '';
- }
-
- const giftsAPI = new ZingerYWebsiteAPI('getGifts.php', arguments);
- /**
- * Submit a request to receive gift codes
- *
- * Отправка запроса для получения кодов подарков
- */
- giftsAPI.request().then((data) => {
- let freebieCheckCalls = {
- calls: [],
- };
- data.forEach((giftId, n) => {
- if (localStorage[valName].includes(giftId)) return;
- freebieCheckCalls.calls.push({
- name: 'registration',
- args: {
- user: { referrer: {} },
- giftId,
- },
- context: {
- actionTs: Math.floor(performance.now()),
- cookie: window?.NXAppInfo?.session_id || null,
- },
- ident: giftId,
- });
- });
-
- if (!freebieCheckCalls.calls.length) {
- return;
- }
-
- send(JSON.stringify(freebieCheckCalls), (e) => {
- let countGetGifts = 0;
- const gifts = [];
- for (check of e.results) {
- gifts.push(check.ident);
- if (check.result.response != null) {
- countGetGifts++;
- }
- }
- const saveGifts = localStorage[valName].split(';');
- localStorage[valName] = [...saveGifts, ...gifts].slice(-50).join(';');
- console.log(`${I18N('GIFTS')}: ${countGetGifts}`);
- });
- });
-}
-
-/**
- * To fill the kills in the Forge of Souls
- *
- * Набить килов в горниле душ
- */
-async function bossRatingEvent() {
- const topGet = await Send(JSON.stringify({ calls: [{ name: "topGet", args: { type: "bossRatingTop", extraId: 0 }, ident: "body" }] }));
- if (!topGet || !topGet.results[0].result.response[0]) {
- setProgress(`${I18N('EVENT')} ${I18N('NOT_AVAILABLE')}`, true);
- return;
- }
- const replayId = topGet.results[0].result.response[0].userData.replayId;
- const result = await Send(JSON.stringify({
- calls: [
- { name: "battleGetReplay", args: { id: replayId }, ident: "battleGetReplay" },
- { name: "heroGetAll", args: {}, ident: "heroGetAll" },
- { name: "pet_getAll", args: {}, ident: "pet_getAll" },
- { name: "offerGetAll", args: {}, ident: "offerGetAll" }
- ]
- }));
- const bossEventInfo = result.results[3].result.response.find(e => e.offerType == "bossEvent");
- if (!bossEventInfo) {
- setProgress(`${I18N('EVENT')} ${I18N('NOT_AVAILABLE')}`, true);
- return;
- }
- const usedHeroes = bossEventInfo.progress.usedHeroes;
- const party = Object.values(result.results[0].result.response.replay.attackers);
- const availableHeroes = Object.values(result.results[1].result.response).map(e => e.id);
- const availablePets = Object.values(result.results[2].result.response).map(e => e.id);
- const calls = [];
- /**
- * First pack
- *
- * Первая пачка
- */
- const args = {
- heroes: [],
- favor: {}
- }
- for (let hero of party) {
- if (hero.id >= 6000 && availablePets.includes(hero.id)) {
- args.pet = hero.id;
- continue;
- }
- if (!availableHeroes.includes(hero.id) || usedHeroes.includes(hero.id)) {
- continue;
- }
- args.heroes.push(hero.id);
- if (hero.favorPetId) {
- args.favor[hero.id] = hero.favorPetId;
- }
- }
- if (args.heroes.length) {
- calls.push({
- name: "bossRatingEvent_startBattle",
- args,
- ident: "body_0"
- });
- }
- /**
- * Other packs
- *
- * Другие пачки
- */
- let heroes = [];
- let count = 1;
- while (heroId = availableHeroes.pop()) {
- if (args.heroes.includes(heroId) || usedHeroes.includes(heroId)) {
- continue;
- }
- heroes.push(heroId);
- if (heroes.length == 5) {
- calls.push({
- name: "bossRatingEvent_startBattle",
- args: {
- heroes: [...heroes],
- pet: availablePets[Math.floor(Math.random() * availablePets.length)]
- },
- ident: "body_" + count
- });
- heroes = [];
- count++;
- }
- }
-
- if (!calls.length) {
- setProgress(`${I18N('NO_HEROES')}`, true);
- return;
- }
-
- const resultBattles = await Send(JSON.stringify({ calls }));
- console.log(resultBattles);
- rewardBossRatingEvent();
-}
-
-/**
- * Collecting Rewards from the Forge of Souls
- *
- * Сбор награды из Горнила Душ
- */
-function rewardBossRatingEvent() {
- let rewardBossRatingCall = '{"calls":[{"name":"offerGetAll","args":{},"ident":"offerGetAll"}]}';
- send(rewardBossRatingCall, function (data) {
- let bossEventInfo = data.results[0].result.response.find(e => e.offerType == "bossEvent");
- if (!bossEventInfo) {
- setProgress(`${I18N('EVENT')} ${I18N('NOT_AVAILABLE')}`, true);
- return;
- }
-
- let farmedChests = bossEventInfo.progress.farmedChests;
- let score = bossEventInfo.progress.score;
- setProgress(`${I18N('DAMAGE_AMOUNT')}: ${score}`);
- let revard = bossEventInfo.reward;
-
- let getRewardCall = {
- calls: []
- }
-
- let count = 0;
- for (let i = 1; i < 10; i++) {
- if (farmedChests.includes(i)) {
- continue;
- }
- if (score < revard[i].score) {
- break;
- }
- getRewardCall.calls.push({
- name: "bossRatingEvent_getReward",
- args: {
- rewardId: i
- },
- ident: "body_" + i
- });
- count++;
- }
- if (!count) {
- setProgress(`${I18N('NOTHING_TO_COLLECT')}`, true);
- return;
- }
-
- send(JSON.stringify(getRewardCall), e => {
- console.log(e);
- setProgress(`${I18N('COLLECTED')} ${e?.results?.length} ${I18N('REWARD')}`, true);
- });
- });
-}
-
-/**
- * Collect Easter eggs and event rewards
- *
- * Собрать пасхалки и награды событий
- */
-function offerFarmAllReward() {
- const offerGetAllCall = '{"calls":[{"name":"offerGetAll","args":{},"ident":"offerGetAll"}]}';
- return Send(offerGetAllCall).then((data) => {
- const offerGetAll = data.results[0].result.response.filter(e => e.type == "reward" && !e?.freeRewardObtained && e.reward);
- if (!offerGetAll.length) {
- setProgress(`${I18N('NOTHING_TO_COLLECT')}`, true);
- return;
- }
-
- const calls = [];
- for (let reward of offerGetAll) {
- calls.push({
- name: "offerFarmReward",
- args: {
- offerId: reward.id
- },
- ident: "offerFarmReward_" + reward.id
- });
- }
-
- return Send(JSON.stringify({ calls })).then(e => {
- console.log(e);
- setProgress(`${I18N('COLLECTED')} ${e?.results?.length} ${I18N('REWARD')}`, true);
- });
- });
-}
-
-/**
- * Assemble Outland
- *
- * Собрать запределье
- */
-function getOutland() {
- return new Promise(function (resolve, reject) {
- send('{"calls":[{"name":"bossGetAll","args":{},"ident":"bossGetAll"}]}', e => {
- let bosses = e.results[0].result.response;
-
- let bossRaidOpenChestCall = {
- calls: []
- };
-
- for (let boss of bosses) {
- if (boss.mayRaid) {
- bossRaidOpenChestCall.calls.push({
- name: "bossRaid",
- args: {
- bossId: boss.id
- },
- ident: "bossRaid_" + boss.id
- });
- bossRaidOpenChestCall.calls.push({
- name: "bossOpenChest",
- args: {
- bossId: boss.id,
- amount: 1,
- starmoney: 0
- },
- ident: "bossOpenChest_" + boss.id
- });
- } else if (boss.chestId == 1) {
- bossRaidOpenChestCall.calls.push({
- name: "bossOpenChest",
- args: {
- bossId: boss.id,
- amount: 1,
- starmoney: 0
- },
- ident: "bossOpenChest_" + boss.id
- });
- }
- }
-
- if (!bossRaidOpenChestCall.calls.length) {
- setProgress(`${I18N('OUTLAND')} ${I18N('NOTHING_TO_COLLECT')}`, true);
- resolve();
- return;
- }
-
- send(JSON.stringify(bossRaidOpenChestCall), e => {
- setProgress(`${I18N('OUTLAND')} ${I18N('COLLECTED')}`, true);
- resolve();
- });
- });
- });
-}
-
-/**
- * Collect all rewards
- *
- * Собрать все награды
- */
-function questAllFarm() {
- return new Promise(function (resolve, reject) {
- let questGetAllCall = {
- calls: [{
- name: "questGetAll",
- args: {},
- ident: "body"
- }]
- }
- send(JSON.stringify(questGetAllCall), function (data) {
- let questGetAll = data.results[0].result.response;
- const questAllFarmCall = {
- calls: []
- }
- let number = 0;
- for (let quest of questGetAll) {
- if (quest.id < 1e6 && quest.state == 2) {
- questAllFarmCall.calls.push({
- name: "questFarm",
- args: {
- questId: quest.id
- },
- ident: `group_${number}_body`
- });
- number++;
- }
- }
-
- if (!questAllFarmCall.calls.length) {
- setProgress(`${I18N('COLLECTED')} ${number} ${I18N('REWARD')}`, true);
- resolve();
- return;
- }
-
- send(JSON.stringify(questAllFarmCall), function (res) {
- console.log(res);
- setProgress(`${I18N('COLLECTED')} ${number} ${I18N('REWARD')}`, true);
- resolve();
- });
- });
- })
-}
-
-/**
- * Mission auto repeat
- *
- * Автоповтор миссии
- * isStopSendMission = false;
- * isSendsMission = true;
- **/
-this.sendsMission = async function (param) {
- if (isStopSendMission) {
- isSendsMission = false;
- console.log(I18N('STOPPED'));
- setProgress('');
- await popup.confirm(`${I18N('STOPPED')} ${I18N('REPETITIONS')}: ${param.count}`, [{
- msg: 'Ok',
- result: true
- }, ])
- return;
- }
- lastMissionBattleStart = Date.now();
- let missionStartCall = {
- "calls": [{
- "name": "missionStart",
- "args": lastMissionStart,
- "ident": "body"
- }]
- }
- /**
- * Mission Request
- *
- * Запрос на выполнение мисии
- */
- SendRequest(JSON.stringify(missionStartCall), async e => {
- if (e['error']) {
- isSendsMission = false;
- console.log(e['error']);
- setProgress('');
- let msg = e['error'].name + ' ' + e['error'].description + ` ${I18N('REPETITIONS')}: ${param.count}`;
- await popup.confirm(msg, [
- {msg: 'Ok', result: true},
- ])
- return;
- }
- /**
- * Mission data calculation
- *
- * Расчет данных мисии
- */
- BattleCalc(e.results[0].result.response, 'get_tower', async r => {
- /** missionTimer */
- let timer = getTimer(r.battleTime) + 5;
- const period = Math.ceil((Date.now() - lastMissionBattleStart) / 1000);
- if (period < timer) {
- timer = timer - period;
- await countdownTimer(timer, `${I18N('MISSIONS_PASSED')}: ${param.count}`);
- }
-
- let missionEndCall = {
- "calls": [{
- "name": "missionEnd",
- "args": {
- "id": param.id,
- "result": r.result,
- "progress": r.progress
- },
- "ident": "body"
- }]
- }
- /**
- * Mission Completion Request
- *
- * Запрос на завершение миссии
- */
- SendRequest(JSON.stringify(missionEndCall), async (e) => {
- if (e['error']) {
- isSendsMission = false;
- console.log(e['error']);
- setProgress('');
- let msg = e['error'].name + ' ' + e['error'].description + ` ${I18N('REPETITIONS')}: ${param.count}`;
- await popup.confirm(msg, [
- {msg: 'Ok', result: true},
- ])
- return;
- }
- r = e.results[0].result.response;
- if (r['error']) {
- isSendsMission = false;
- console.log(r['error']);
- setProgress('');
- await popup.confirm(` ${I18N('REPETITIONS')}: ${param.count}` + ' 3 ' + r['error'], [
- {msg: 'Ok', result: true},
- ])
- return;
- }
-
- param.count++;
- setProgress(`${I18N('MISSIONS_PASSED')}: ${param.count} (${I18N('STOP')})`, false, () => {
- isStopSendMission = true;
- });
- setTimeout(sendsMission, 1, param);
- });
- })
- });
-}
-
-/**
- * Opening of russian dolls
- *
- * Открытие матрешек
- */
-async function openRussianDolls(libId, amount) {
- let sum = 0;
- let sumResult = [];
-
- while (amount) {
- sum += amount;
- setProgress(`${I18N('TOTAL_OPEN')} ${sum}`);
- const calls = [{
- name: "consumableUseLootBox",
- args: { libId, amount },
- ident: "body"
- }];
- const result = await Send(JSON.stringify({ calls })).then(e => e.results[0].result.response);
- let newCount = 0;
- for (let n of result) {
- if (n?.consumable && n.consumable[libId]) {
- newCount += n.consumable[libId]
- }
- }
- sumResult = [...sumResult, ...result];
- amount = newCount;
- }
-
- setProgress(`${I18N('TOTAL_OPEN')} ${sum}`, 5000);
- return sumResult;
-}
-
-/**
- * Collect all mail, except letters with energy and charges of the portal
- *
- * Собрать всю почту, кроме писем с энергией и зарядами портала
- */
-function mailGetAll() {
- const getMailInfo = '{"calls":[{"name":"mailGetAll","args":{},"ident":"body"}]}';
-
- return Send(getMailInfo).then(dataMail => {
- const letters = dataMail.results[0].result.response.letters;
- const letterIds = lettersFilter(letters);
- if (!letterIds.length) {
- setProgress(I18N('NOTHING_TO_COLLECT'), true);
- return;
- }
-
- const calls = [
- { name: "mailFarm", args: { letterIds }, ident: "body" }
- ];
-
- return Send(JSON.stringify({ calls })).then(res => {
- const lettersIds = res.results[0].result.response;
- if (lettersIds) {
- const countLetters = Object.keys(lettersIds).length;
- setProgress(`${I18N('RECEIVED')} ${countLetters} ${I18N('LETTERS')}`, true);
- }
- });
- });
-}
-
-/**
- * Filters received emails
- *
- * Фильтрует получаемые письма
- */
-function lettersFilter(letters) {
- const lettersIds = [];
- for (let l in letters) {
- letter = letters[l];
- const reward = letter.reward;
- if (!reward) {
- continue;
- }
- /**
- * Mail Collection Exceptions
- *
- * Исключения на сбор писем
- */
- const isFarmLetter = !(
- /** Portals // сферы портала */
- (reward?.refillable ? reward.refillable[45] : false) ||
- /** Energy // энергия */
- (reward?.stamina ? reward.stamina : false) ||
- /** accelerating energy gain // ускорение набора энергии */
- (reward?.buff ? true : false) ||
- /** VIP Points // вип очки */
- (reward?.vipPoints ? reward.vipPoints : false) ||
- /** souls of heroes // душы героев */
- (reward?.fragmentHero ? true : false) ||
- /** heroes // герои */
- (reward?.bundleHeroReward ? true : false)
- );
- if (isFarmLetter) {
- lettersIds.push(~~letter.id);
- continue;
- }
- /**
- * Если до окончания годности письма менее 24 часов,
- * то оно собирается не смотря на исключения
- */
- const availableUntil = +letter?.availableUntil;
- if (availableUntil) {
- const maxTimeLeft = 24 * 60 * 60 * 1000;
- const timeLeft = (new Date(availableUntil * 1000) - new Date())
- console.log('Time left:', timeLeft)
- if (timeLeft < maxTimeLeft) {
- lettersIds.push(~~letter.id);
- continue;
- }
- }
- }
- return lettersIds;
-}
-
-/**
- * Displaying information about the areas of the portal and attempts on the VG
- *
- * Отображение информации о сферах портала и попытках на ВГ
- */
-async function justInfo() {
- return new Promise(async (resolve, reject) => {
- const calls = [{
- name: "userGetInfo",
- args: {},
- ident: "userGetInfo"
- },
- {
- name: "clanWarGetInfo",
- args: {},
- ident: "clanWarGetInfo"
- },
- {
- name: "titanArenaGetStatus",
- args: {},
- ident: "titanArenaGetStatus"
- }];
- const result = await Send(JSON.stringify({ calls }));
- const infos = result.results;
- const portalSphere = infos[0].result.response.refillable.find(n => n.id == 45);
- const clanWarMyTries = infos[1].result.response?.myTries ?? 0;
- const arePointsMax = infos[1].result.response?.arePointsMax;
- const titansLevel = +(infos[2].result.response?.tier ?? 0);
- const titansStatus = infos[2].result.response?.status; //peace_time || battle
-
- const sanctuaryButton = buttons['goToSanctuary'].button;
- const clanWarButton = buttons['goToClanWar'].button;
- const titansArenaButton = buttons['testTitanArena'].button;
-
- if (portalSphere.amount) {
- sanctuaryButton.style.color = portalSphere.amount >= 3 ? 'red' : 'brown';
- sanctuaryButton.title = `${I18N('SANCTUARY_TITLE')}\n${portalSphere.amount} ${I18N('PORTALS')}`;
- } else {
- sanctuaryButton.style.color = '';
- sanctuaryButton.title = I18N('SANCTUARY_TITLE');
- }
- if (clanWarMyTries && !arePointsMax) {
- clanWarButton.style.color = 'red';
- clanWarButton.title = `${I18N('GUILD_WAR_TITLE')}\n${clanWarMyTries}${I18N('ATTEMPTS')}`;
- } else {
- clanWarButton.style.color = '';
- clanWarButton.title = I18N('GUILD_WAR_TITLE');
- }
-
- if (titansLevel < 7 && titansStatus == 'battle') {
- const partColor = Math.floor(125 * titansLevel / 7);
- titansArenaButton.style.color = `rgb(255,${partColor},${partColor})`;
- titansArenaButton.title = `${I18N('TITAN_ARENA_TITLE')}\n${titansLevel} ${I18N('LEVEL')}`;
- } else {
- titansArenaButton.style.color = '';
- titansArenaButton.title = I18N('TITAN_ARENA_TITLE');
- }
-
- 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 Send(JSON.stringify({
- calls: [{
- name: "dailyBonusGetInfo",
- args: {},
- ident: "body"
- }]
- })).then(e => e.results[0].result.response);
- const { availableToday, availableVip, currentDay } = dailyBonusInfo;
-
- if (!availableToday) {
- console.log('Уже собрано');
- return;
- }
-
- const currentVipPoints = +userInfo.vipPoints;
- const dailyBonusStat = lib.getData('dailyBonusStatic');
- const vipInfo = lib.getData('level').vip;
- let currentVipLevel = 0;
- for (let i in vipInfo) {
- vipLvl = vipInfo[i];
- if (currentVipPoints >= vipLvl.vipPoints) {
- currentVipLevel = vipLvl.level;
- }
- }
- const vipLevelDouble = dailyBonusStat[`${currentDay}_0_0`].vipLevelDouble;
-
- const calls = [{
- name: "dailyBonusFarm",
- args: {
- vip: availableVip && currentVipLevel >= vipLevelDouble ? 1 : 0
- },
- ident: "body"
- }];
-
- const result = await Send(JSON.stringify({ calls }));
- if (result.error) {
- console.error(result.error);
- return;
- }
-
- const reward = result.results[0].result.response;
- const type = Object.keys(reward).pop();
- const itemId = Object.keys(reward[type]).pop();
- const count = reward[type][itemId];
- const itemName = cheats.translate(`LIB_${type.toUpperCase()}_NAME_${itemId}`);
-
- console.log(`Ежедневная награда: Получено ${count} ${itemName}`, reward);
-}
-
-async function farmStamina(lootBoxId = 148) {
- const lootBox = await Send('{"calls":[{"name":"inventoryGet","args":{},"ident":"inventoryGet"}]}')
- .then(e => e.results[0].result.response.consumable[148]);
-
- /** Добавить другие ящики */
- /**
- * 144 - медная шкатулка
- * 145 - бронзовая шкатулка
- * 148 - платиновая шкатулка
- */
- if (!lootBox) {
- setProgress(I18N('NO_BOXES'), true);
- return;
- }
-
- let maxFarmEnergy = getSaveVal('maxFarmEnergy', 100);
- const result = await popup.confirm(I18N('OPEN_LOOTBOX', { lootBox }), [
- { result: false, isClose: true },
- { msg: I18N('BTN_YES'), result: true },
- { msg: I18N('STAMINA'), isInput: true, default: maxFarmEnergy },
- ]);
-
- if (!+result) {
- return;
- }
-
- if ((typeof result) !== 'boolean' && Number.parseInt(result)) {
- maxFarmEnergy = +result;
- setSaveVal('maxFarmEnergy', maxFarmEnergy);
- } else {
- maxFarmEnergy = 0;
- }
-
- let collectEnergy = 0;
- for (let count = lootBox; count > 0; count--) {
- const result = await Send('{"calls":[{"name":"consumableUseLootBox","args":{"libId":148,"amount":1},"ident":"body"}]}')
- .then(e => e.results[0].result.response[0]);
- 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 data = await Send(JSON.stringify({
- calls: [{
- name: "questGetAll",
- args: {},
- ident: "questGetAll"
- }, {
- name: "inventoryGet",
- args: {},
- ident: "inventoryGet"
- }, {
- name: "clanGetInfo",
- args: {},
- ident: "clanGetInfo"
- }
- ]
- })).then(e => e.results.map(n => n.result.response));
-
- const quests = data[0];
- const inv = data[1];
- const stat = data[2].stat;
- const maxActive = 2000 - stat.todayItemsActivity;
- if (maxActive <= 0) {
- setProgress(I18N('NO_MORE_ACTIVITY'), true);
- return;
- }
-
- let countGetActive = 0;
- const quest = quests.find(e => e.id > 10046 && e.id < 10051);
- if (quest) {
- countGetActive = 1750 - quest.progress;
- }
-
- if (countGetActive <= 0) {
- countGetActive = maxActive;
- }
- console.log(countGetActive);
-
- countGetActive = +(await popup.confirm(I18N('EXCHANGE_ITEMS', { maxActive }), [
- { result: false, isClose: true },
- { msg: I18N('GET_ACTIVITY'), isInput: true, default: countGetActive.toString() },
- ]));
-
- if (!countGetActive) {
- return;
- }
-
- if (countGetActive > maxActive) {
- countGetActive = maxActive;
- }
-
- const items = lib.getData('inventoryItem');
-
- let itemsInfo = [];
- for (let type of ['gear', 'scroll']) {
- for (let i in inv[type]) {
- const v = items[type][i]?.enchantValue || 0;
- itemsInfo.push({
- id: i,
- count: inv[type][i],
- v,
- type
- })
- }
- const invType = 'fragment' + type.toLowerCase().charAt(0).toUpperCase() + type.slice(1);
- for (let i in inv[invType]) {
- const v = items[type][i]?.fragmentEnchantValue || 0;
- itemsInfo.push({
- id: i,
- count: inv[invType][i],
- v,
- type: invType
- })
- }
- }
- itemsInfo = itemsInfo.filter(e => e.v < 4 && e.count > 200);
- itemsInfo = itemsInfo.sort((a, b) => b.count - a.count);
- console.log(itemsInfo);
- const activeItem = itemsInfo.shift();
- console.log(activeItem);
- const countItem = Math.ceil(countGetActive / activeItem.v);
- if (countItem > activeItem.count) {
- setProgress(I18N('NOT_ENOUGH_ITEMS'), true);
- console.log(activeItem);
- return;
- }
-
- await Send(JSON.stringify({
- calls: [{
- name: "clanItemsForActivity",
- args: {
- items: {
- [activeItem.type]: {
- [activeItem.id]: countItem
- }
- }
- },
- ident: "body"
- }]
- })).then(e => {
- /** TODO: Вывести потраченые предметы */
- console.log(e);
- setProgress(`${I18N('ACTIVITY_RECEIVED')}: ` + e.results[0].result.response, true);
- });
-}
-
-async function buyHeroFragments() {
- const result = await Send('{"calls":[{"name":"inventoryGet","args":{},"ident":"inventoryGet"},{"name":"shopGetAll","args":{},"ident":"shopGetAll"}]}')
- .then(e => e.results.map(n => n.result.response));
- const inv = result[0];
- const shops = Object.values(result[1]).filter(shop => [4, 5, 6, 8, 9, 10, 17].includes(shop.id));
- const calls = [];
-
- for (let shop of shops) {
- const slots = Object.values(shop.slots);
- for (const slot of slots) {
- /* Уже куплено */
- if (slot.bought) {
- continue;
- }
- /* Не душа героя */
- if (!('fragmentHero' in slot.reward)) {
- continue;
- }
- const coin = Object.keys(slot.cost).pop();
- const coinId = Object.keys(slot.cost[coin]).pop();
- const stock = inv[coin][coinId] || 0;
- /* Не хватает на покупку */
- if (slot.cost[coin][coinId] > stock) {
- continue;
- }
- inv[coin][coinId] -= slot.cost[coin][coinId];
- calls.push({
- name: "shopBuy",
- args: {
- shopId: shop.id,
- slot: slot.id,
- cost: slot.cost,
- reward: slot.reward,
- },
- ident: `shopBuy_${shop.id}_${slot.id}`,
- })
- }
- }
-
- if (!calls.length) {
- setProgress(I18N('NO_PURCHASABLE_HERO_SOULS'), true);
- return;
- }
-
- const bought = await Send(JSON.stringify({ calls })).then(e => e.results.map(n => n.result.response));
- if (!bought) {
- console.log('что-то пошло не так')
- return;
- }
-
- let countHeroSouls = 0;
- for (const buy of bought) {
- countHeroSouls += +Object.values(Object.values(buy).pop()).pop();
- }
- console.log(countHeroSouls, bought, calls);
- setProgress(I18N('PURCHASED_HERO_SOULS', { countHeroSouls }), true);
-}
-
-/** Открыть платные сундуки в Запределье за 90 */
-async function bossOpenChestPay() {
- const callsNames = ['userGetInfo', 'bossGetAll', 'specialOffer_getAll', 'getTime'];
- const info = await Send({ calls: callsNames.map((name) => ({ name, args: {}, ident: name })) }).then((e) =>
- e.results.map((n) => n.result.response)
- );
-
- const user = info[0];
- const boses = info[1];
- const offers = info[2];
- const time = info[3];
-
- const discountOffer = offers.find((e) => e.offerType == 'costReplaceOutlandChest');
-
- let discount = 1;
- if (discountOffer && discountOffer.endTime > time) {
- discount = 1 - discountOffer.offerData.outlandChest.discountPercent / 100;
- }
-
- cost9chests = 540 * discount;
- cost18chests = 1740 * discount;
- costFirstChest = 90 * discount;
- costSecondChest = 200 * discount;
-
- const currentStarMoney = user.starMoney;
- if (currentStarMoney < cost9chests) {
- setProgress('Недостаточно изюма, нужно ' + cost9chests + ' у Вас ' + currentStarMoney, true);
- return;
- }
-
- const imgEmerald =
- " ";
-
- if (currentStarMoney < cost9chests) {
- setProgress(I18N('NOT_ENOUGH_EMERALDS_540', { currentStarMoney, imgEmerald }), true);
- return;
- }
-
- const buttons = [{ result: false, isClose: true }];
-
- if (currentStarMoney >= cost9chests) {
- buttons.push({
- msg: I18N('BUY_OUTLAND_BTN', { count: 9, countEmerald: cost9chests, imgEmerald }),
- result: [costFirstChest, costFirstChest, 0],
- });
- }
-
- if (currentStarMoney >= cost18chests) {
- buttons.push({
- msg: I18N('BUY_OUTLAND_BTN', { count: 18, countEmerald: cost18chests, imgEmerald }),
- result: [costFirstChest, costFirstChest, 0, costSecondChest, costSecondChest, 0],
- });
- }
-
- const answer = await popup.confirm(`${I18N('BUY_OUTLAND')}
`, buttons);
-
- if (!answer) {
- return;
- }
-
- const callBoss = [];
- let n = 0;
- for (let boss of boses) {
- const bossId = boss.id;
- if (boss.chestNum != 2) {
- continue;
- }
- const calls = [];
- for (const starmoney of answer) {
- calls.push({
- name: 'bossOpenChest',
- args: {
- amount: 1,
- bossId,
- starmoney,
- },
- ident: 'bossOpenChest_' + ++n,
- });
- }
- callBoss.push(calls);
- }
-
- if (!callBoss.length) {
- setProgress(I18N('CHESTS_NOT_AVAILABLE'), true);
- return;
- }
-
- let count = 0;
- let errors = 0;
- for (const calls of callBoss) {
- const result = await Send({ calls });
- console.log(result);
- if (result?.results) {
- count += result.results.length;
- } else {
- errors++;
- }
- }
-
- setProgress(`${I18N('OUTLAND_CHESTS_RECEIVED')}: ${count}`, true);
-}
-
-async function autoRaidAdventure() {
- const calls = [
- {
- name: "userGetInfo",
- args: {},
- ident: "userGetInfo"
- },
- {
- name: "adventure_raidGetInfo",
- args: {},
- ident: "adventure_raidGetInfo"
- }
- ];
- const result = await Send(JSON.stringify({ calls }))
- .then(e => e.results.map(n => n.result.response));
-
- const portalSphere = result[0].refillable.find(n => n.id == 45);
- const adventureRaid = Object.entries(result[1].raid).filter(e => e[1]).pop()
- const adventureId = adventureRaid ? adventureRaid[0] : 0;
-
- if (!portalSphere.amount || !adventureId) {
- setProgress(I18N('RAID_NOT_AVAILABLE'), true);
- return;
- }
-
- const countRaid = +(await popup.confirm(I18N('RAID_ADVENTURE', { adventureId }), [
- { result: false, isClose: true },
- { msg: I18N('RAID'), isInput: true, default: portalSphere.amount },
- ]));
-
- if (!countRaid) {
- return;
- }
-
- if (countRaid > portalSphere.amount) {
- countRaid = portalSphere.amount;
- }
-
- const resultRaid = await Send(JSON.stringify({
- calls: [...Array(countRaid)].map((e, i) => ({
- name: "adventure_raid",
- args: {
- adventureId
- },
- ident: `body_${i}`
- }))
- })).then(e => e.results.map(n => n.result.response));
-
- if (!resultRaid.length) {
- console.log(resultRaid);
- setProgress(I18N('SOMETHING_WENT_WRONG'), true);
- return;
- }
-
- console.log(resultRaid, adventureId, portalSphere.amount);
- setProgress(I18N('ADVENTURE_COMPLETED', { adventureId, times: resultRaid.length }), true);
-}
-
-/** Вывести всю клановую статистику в консоль браузера */
-async function clanStatistic() {
- const copy = function (text) {
- const copyTextarea = document.createElement("textarea");
- copyTextarea.style.opacity = "0";
- copyTextarea.textContent = text;
- document.body.appendChild(copyTextarea);
- copyTextarea.select();
- document.execCommand("copy");
- document.body.removeChild(copyTextarea);
- delete copyTextarea;
- }
- const calls = [
- { name: "clanGetInfo", args: {}, ident: "clanGetInfo" },
- { name: "clanGetWeeklyStat", args: {}, ident: "clanGetWeeklyStat" },
- { name: "clanGetLog", args: {}, ident: "clanGetLog" },
- ];
-
- const result = await Send(JSON.stringify({ calls }));
-
- const dataClanInfo = result.results[0].result.response;
- const dataClanStat = result.results[1].result.response;
- const dataClanLog = result.results[2].result.response;
-
- const membersStat = {};
- for (let i = 0; i < dataClanStat.stat.length; i++) {
- membersStat[dataClanStat.stat[i].id] = dataClanStat.stat[i];
- }
-
- const joinStat = {};
- historyLog = dataClanLog.history;
- for (let j in historyLog) {
- his = historyLog[j];
- if (his.event == 'join') {
- joinStat[his.userId] = his.ctime;
- }
- }
-
- const infoArr = [];
- const members = dataClanInfo.clan.members;
- for (let n in members) {
- var member = [
- n,
- members[n].name,
- members[n].level,
- dataClanInfo.clan.warriors.includes(+n) ? 1 : 0,
- (new Date(members[n].lastLoginTime * 1000)).toLocaleString().replace(',', ''),
- joinStat[n] ? (new Date(joinStat[n] * 1000)).toLocaleString().replace(',', '') : '',
- membersStat[n].activity.reverse().join('\t'),
- membersStat[n].adventureStat.reverse().join('\t'),
- membersStat[n].clanGifts.reverse().join('\t'),
- membersStat[n].clanWarStat.reverse().join('\t'),
- membersStat[n].dungeonActivity.reverse().join('\t'),
- ];
- infoArr.push(member);
- }
- const info = infoArr.sort((a, b) => (b[2] - a[2])).map((e) => e.join('\t')).join('\n');
- console.log(info);
- copy(info);
- setProgress(I18N('CLAN_STAT_COPY'), true);
-}
-
-async function buyInStoreForGold() {
- const result = await Send('{"calls":[{"name":"shopGetAll","args":{},"ident":"body"},{"name":"userGetInfo","args":{},"ident":"userGetInfo"}]}').then(e => e.results.map(n => n.result.response));
- const shops = result[0];
- const user = result[1];
- let gold = user.gold;
- const calls = [];
- if (shops[17]) {
- const slots = shops[17].slots;
- for (let i = 1; i <= 2; i++) {
- if (!slots[i].bought) {
- const costGold = slots[i].cost.gold;
- if ((gold - costGold) < 0) {
- continue;
- }
- gold -= costGold;
- calls.push({
- name: "shopBuy",
- args: {
- shopId: 17,
- slot: i,
- cost: slots[i].cost,
- reward: slots[i].reward,
- },
- ident: 'body_' + i,
- })
- }
- }
- }
- const slots = shops[1].slots;
- for (let i = 4; i <= 6; i++) {
- if (!slots[i].bought && slots[i]?.cost?.gold) {
- const costGold = slots[i].cost.gold;
- if ((gold - costGold) < 0) {
- continue;
- }
- gold -= costGold;
- calls.push({
- name: "shopBuy",
- args: {
- shopId: 1,
- slot: i,
- cost: slots[i].cost,
- reward: slots[i].reward,
- },
- ident: 'body_' + i,
- })
- }
- }
-
- if (!calls.length) {
- setProgress(I18N('NOTHING_BUY'), true);
- return;
- }
-
- const resultBuy = await Send(JSON.stringify({ calls })).then(e => e.results.map(n => n.result.response));
- console.log(resultBuy);
- const countBuy = resultBuy.length;
- setProgress(I18N('LOTS_BOUGHT', { countBuy }), true);
-}
-
-function rewardsAndMailFarm() {
- return new Promise(function (resolve, reject) {
- let questGetAllCall = {
- calls: [{
- name: "questGetAll",
- args: {},
- ident: "questGetAll"
- }, {
- name: "mailGetAll",
- args: {},
- ident: "mailGetAll"
- }]
- }
- send(JSON.stringify(questGetAllCall), function (data) {
- if (!data) return;
- const questGetAll = data.results[0].result.response.filter((e) => e.state == 2);
- const questBattlePass = lib.getData('quest').battlePass;
- const questChainBPass = lib.getData('battlePass').questChain;
- const listBattlePass = lib.getData('battlePass').list;
-
- const questAllFarmCall = {
- calls: [],
- };
- const questIds = [];
- for (let quest of questGetAll) {
- if (quest.id >= 2001e4) {
- continue;
- }
- if (quest.id > 1e6 && quest.id < 2e7) {
- const questInfo = questBattlePass[quest.id];
- const chain = questChainBPass[questInfo.chain];
- if (chain.requirement?.battlePassTicket) {
- continue;
- }
- const battlePass = listBattlePass[chain.battlePass];
- const startTime = battlePass.startCondition.time.value * 1e3
- const endTime = new Date(startTime + battlePass.duration * 1e3);
- if (startTime > Date.now() || endTime < Date.now()) {
- continue;
- }
- }
- if (quest.id >= 2e7) {
- questIds.push(quest.id);
- continue;
- }
- questAllFarmCall.calls.push({
- name: 'questFarm',
- args: {
- questId: quest.id,
- },
- ident: `questFarm_${quest.id}`,
- });
- }
-
- if (questIds.length) {
- questAllFarmCall.calls.push({
- name: 'quest_questsFarm',
- args: { questIds },
- ident: 'quest_questsFarm',
- });
- }
-
- let letters = data?.results[1]?.result?.response?.letters;
- letterIds = lettersFilter(letters);
-
- if (letterIds.length) {
- questAllFarmCall.calls.push({
- name: 'mailFarm',
- args: { letterIds },
- ident: 'mailFarm',
- });
- }
-
- if (!questAllFarmCall.calls.length) {
- setProgress(I18N('NOTHING_TO_COLLECT'), true);
- resolve();
- return;
- }
-
- send(JSON.stringify(questAllFarmCall), async function (res) {
- let countQuests = 0;
- let countMail = 0;
- let questsIds = [];
- for (let call of res.results) {
- if (call.ident.includes('questFarm')) {
- countQuests++;
- } else if (call.ident.includes('questsFarm')) {
- countQuests += Object.keys(call.result.response).length;
- } else if (call.ident.includes('mailFarm')) {
- countMail = Object.keys(call.result.response).length;
- }
-
- const newQuests = call.result.newQuests;
- if (newQuests) {
- for (let quest of newQuests) {
- if ((quest.id < 1e6 || (quest.id >= 2e7 && quest.id < 2001e4)) && quest.state == 2) {
- questsIds.push(quest.id);
- }
- }
- }
- }
-
- while (questsIds.length) {
- const questIds = [];
- const calls = [];
- for (let questId of questsIds) {
- if (questId < 1e6) {
- calls.push({
- name: 'questFarm',
- args: {
- questId,
- },
- ident: `questFarm_${questId}`,
- });
- countQuests++;
- } else if (questId >= 2e7 && questId < 2001e4) {
- questIds.push(questId);
- countQuests++;
- }
- }
- calls.push({
- name: 'quest_questsFarm',
- args: { questIds },
- ident: 'body',
- });
- const results = await Send({ calls }).then((e) => e.results.map((e) => e.result));
- questsIds = [];
- for (const result of results) {
- const newQuests = result.newQuests;
- if (newQuests) {
- for (let quest of newQuests) {
- if (quest.state == 2) {
- questsIds.push(quest.id);
- }
- }
- }
- }
- }
-
- setProgress(I18N('COLLECT_REWARDS_AND_MAIL', { countQuests, countMail }), true);
- resolve();
- });
- });
- })
-}
-
-class epicBrawl {
- timeout = null;
- time = null;
-
- constructor() {
- if (epicBrawl.inst) {
- return epicBrawl.inst;
- }
- epicBrawl.inst = this;
- return this;
- }
-
- runTimeout(func, timeDiff) {
- const worker = new Worker(URL.createObjectURL(new Blob([`
- self.onmessage = function(e) {
- const timeDiff = e.data;
-
- if (timeDiff > 0) {
- setTimeout(() => {
- self.postMessage(1);
- self.close();
- }, timeDiff);
- }
- };
- `])));
- worker.postMessage(timeDiff);
- worker.onmessage = () => {
- func();
- };
- return true;
- }
-
- timeDiff(date1, date2) {
- const date1Obj = new Date(date1);
- const date2Obj = new Date(date2);
-
- const timeDiff = Math.abs(date2Obj - date1Obj);
-
- const totalSeconds = timeDiff / 1000;
- const minutes = Math.floor(totalSeconds / 60);
- const seconds = Math.floor(totalSeconds % 60);
-
- const formattedMinutes = String(minutes).padStart(2, '0');
- const formattedSeconds = String(seconds).padStart(2, '0');
-
- return `${formattedMinutes}:${formattedSeconds}`;
- }
-
- check() {
- console.log(new Date(this.time))
- if (Date.now() > this.time) {
- this.timeout = null;
- this.start()
- return;
- }
- this.timeout = this.runTimeout(() => this.check(), 6e4);
- return this.timeDiff(this.time, Date.now())
- }
-
- async start() {
- if (this.timeout) {
- const time = this.timeDiff(this.time, Date.now());
- console.log(new Date(this.time))
- setProgress(I18N('TIMER_ALREADY', { time }), false, hideProgress);
- return;
- }
- setProgress(I18N('EPIC_BRAWL'), false, hideProgress);
- const teamInfo = await Send('{"calls":[{"name":"teamGetAll","args":{},"ident":"teamGetAll"},{"name":"teamGetFavor","args":{},"ident":"teamGetFavor"},{"name":"userGetInfo","args":{},"ident":"userGetInfo"}]}').then(e => e.results.map(n => n.result.response));
- const refill = teamInfo[2].refillable.find(n => n.id == 52)
- this.time = (refill.lastRefill + 3600) * 1000
- const attempts = refill.amount;
- if (!attempts) {
- console.log(new Date(this.time));
- const time = this.check();
- setProgress(I18N('NO_ATTEMPTS_TIMER_START', { time }), false, hideProgress);
- return;
- }
-
- if (!teamInfo[0].epic_brawl) {
- setProgress(I18N('NO_HEROES_PACK'), false, hideProgress);
- return;
- }
-
- const args = {
- heroes: teamInfo[0].epic_brawl.filter(e => e < 1000),
- pet: teamInfo[0].epic_brawl.filter(e => e > 6000).pop(),
- favor: teamInfo[1].epic_brawl,
- }
-
- let wins = 0;
- let coins = 0;
- let streak = { progress: 0, nextStage: 0 };
- for (let i = attempts; i > 0; i--) {
- const info = await Send(JSON.stringify({
- calls: [
- { name: "epicBrawl_getEnemy", args: {}, ident: "epicBrawl_getEnemy" }, { name: "epicBrawl_startBattle", args, ident: "epicBrawl_startBattle" }
- ]
- })).then(e => e.results.map(n => n.result.response));
-
- const { progress, result } = await Calc(info[1].battle);
- const endResult = await Send(JSON.stringify({ calls: [{ name: "epicBrawl_endBattle", args: { progress, result }, ident: "epicBrawl_endBattle" }, { name: "epicBrawl_getWinStreak", args: {}, ident: "epicBrawl_getWinStreak" }] })).then(e => e.results.map(n => n.result.response));
-
- const resultInfo = endResult[0].result;
- streak = endResult[1];
-
- wins += resultInfo.win;
- coins += resultInfo.reward ? resultInfo.reward.coin[39] : 0;
-
- console.log(endResult[0].result)
- if (endResult[1].progress == endResult[1].nextStage) {
- const farm = await Send('{"calls":[{"name":"epicBrawl_farmWinStreak","args":{},"ident":"body"}]}').then(e => e.results[0].result.response);
- coins += farm.coin[39];
- }
-
- setProgress(I18N('EPIC_BRAWL_RESULT', {
- i, wins, attempts, coins,
- progress: streak.progress,
- nextStage: streak.nextStage,
- end: '',
- }), false, hideProgress);
- }
-
- console.log(new Date(this.time));
- const time = this.check();
- setProgress(I18N('EPIC_BRAWL_RESULT', {
- wins, attempts, coins,
- i: '',
- progress: streak.progress,
- nextStage: streak.nextStage,
- end: I18N('ATTEMPT_ENDED', { time }),
- }), false, hideProgress);
- }
-}
-
-function countdownTimer(seconds, message) {
- message = message || I18N('TIMER');
- const stopTimer = Date.now() + seconds * 1e3
- return new Promise(resolve => {
- const interval = setInterval(async () => {
- const now = Date.now();
- setProgress(`${message} ${((stopTimer - now) / 1000).toFixed(2)}`, false);
- if (now > stopTimer) {
- clearInterval(interval);
- setProgress('', 1);
- resolve();
- }
- }, 100);
- });
-}
-
-/** Набить килов в горниле душк */
-async function bossRatingEventSouls() {
- const data = await Send({
- calls: [
- { name: "heroGetAll", args: {}, ident: "teamGetAll" },
- { name: "offerGetAll", args: {}, ident: "offerGetAll" },
- { name: "pet_getAll", args: {}, ident: "pet_getAll" },
- ]
- });
- const bossEventInfo = data.results[1].result.response.find(e => e.offerType == "bossEvent");
- if (!bossEventInfo) {
- setProgress('Эвент завершен', true);
- return;
- }
-
- if (bossEventInfo.progress.score > 250) {
- setProgress('Уже убито больше 250 врагов');
- rewardBossRatingEventSouls();
- return;
- }
- const availablePets = Object.values(data.results[2].result.response).map(e => e.id);
- const heroGetAllList = data.results[0].result.response;
- const usedHeroes = bossEventInfo.progress.usedHeroes;
- const heroList = [];
-
- for (let heroId in heroGetAllList) {
- let hero = heroGetAllList[heroId];
- if (usedHeroes.includes(hero.id)) {
- continue;
- }
- heroList.push(hero.id);
- }
-
- if (!heroList.length) {
- setProgress('Нет героев', true);
- return;
- }
-
- const pet = availablePets.includes(6005) ? 6005 : availablePets[Math.floor(Math.random() * availablePets.length)];
- const petLib = lib.getData('pet');
- let count = 1;
-
- for (const heroId of heroList) {
- const args = {
- heroes: [heroId],
- pet
- }
- /** Поиск питомца для героя */
- for (const petId of availablePets) {
- if (petLib[petId].favorHeroes.includes(heroId)) {
- args.favor = {
- [heroId]: petId
- }
- break;
- }
- }
-
- const calls = [{
- name: "bossRatingEvent_startBattle",
- args,
- ident: "body"
- }, {
- name: "offerGetAll",
- args: {},
- ident: "offerGetAll"
- }];
-
- const res = await Send({ calls });
- count++;
-
- if ('error' in res) {
- console.error(res.error);
- setProgress('Перезагрузите игру и попробуйте позже', true);
- return;
- }
-
- const eventInfo = res.results[1].result.response.find(e => e.offerType == "bossEvent");
- if (eventInfo.progress.score > 250) {
- break;
- }
- setProgress('Количество убитых врагов: ' + eventInfo.progress.score + ' Использовано ' + count + ' героев');
- }
-
- rewardBossRatingEventSouls();
-}
-/** Сбор награды из Горнила Душ */
-async function rewardBossRatingEventSouls() {
- const data = await Send({
- calls: [
- { name: "offerGetAll", args: {}, ident: "offerGetAll" }
- ]
- });
-
- const bossEventInfo = data.results[0].result.response.find(e => e.offerType == "bossEvent");
- if (!bossEventInfo) {
- setProgress('Эвент завершен', true);
- return;
- }
-
- const farmedChests = bossEventInfo.progress.farmedChests;
- const score = bossEventInfo.progress.score;
- // setProgress('Количество убитых врагов: ' + score);
- const revard = bossEventInfo.reward;
- const calls = [];
-
- let count = 0;
- for (let i = 1; i < 10; i++) {
- if (farmedChests.includes(i)) {
- continue;
- }
- if (score < revard[i].score) {
- break;
- }
- calls.push({
- name: "bossRatingEvent_getReward",
- args: {
- rewardId: i
- },
- ident: "body_" + i
- });
- count++;
- }
- if (!count) {
- setProgress('Нечего собирать', true);
- return;
- }
-
- Send({ calls }).then(e => {
- console.log(e);
- setProgress('Собрано ' + e?.results?.length + ' наград', true);
- })
-}
-/**
- * Spin the Seer
- *
- * Покрутить провидца
- */
-async function rollAscension() {
- const refillable = await Send({calls:[
- {
- name:"userGetInfo",
- args:{},
- ident:"userGetInfo"
- }
- ]}).then(e => e.results[0].result.response.refillable);
- const i47 = refillable.find(i => i.id == 47);
- if (i47?.amount) {
- await Send({ calls: [{ name: "ascensionChest_open", args: { paid: false, amount: 1 }, ident: "body" }] });
- setProgress(I18N('DONE'), true);
- } else {
- setProgress(I18N('NOT_ENOUGH_AP'), true);
- }
-}
-
-/**
- * Collect gifts for the New Year
- *
- * Собрать подарки на новый год
- */
-function getGiftNewYear() {
- Send({ calls: [{ name: "newYearGiftGet", args: { type: 0 }, ident: "body" }] }).then(e => {
- const gifts = e.results[0].result.response.gifts;
- const calls = gifts.filter(e => e.opened == 0).map(e => ({
- name: "newYearGiftOpen",
- args: {
- giftId: e.id
- },
- ident: `body_${e.id}`
- }));
- if (!calls.length) {
- setProgress(I18N('NY_NO_GIFTS'), 5000);
- return;
- }
- Send({ calls }).then(e => {
- console.log(e.results)
- const msg = I18N('NY_GIFTS_COLLECTED', { count: e.results.length });
- console.log(msg);
- setProgress(msg, 5000);
- });
- })
-}
-
-async function updateArtifacts() {
- const count = +await popup.confirm(I18N('SET_NUMBER_LEVELS'), [
- { msg: I18N('BTN_GO'), isInput: true, default: 10 },
- { result: false, isClose: true }
- ]);
- if (!count) {
- return;
- }
- const quest = new questRun;
- await quest.autoInit();
- const heroes = Object.values(quest.questInfo['heroGetAll']);
- const inventory = quest.questInfo['inventoryGet'];
- const calls = [];
- for (let i = count; i > 0; i--) {
- const upArtifact = quest.getUpgradeArtifact();
- if (!upArtifact.heroId) {
- if (await popup.confirm(I18N('POSSIBLE_IMPROVE_LEVELS', { count: calls.length }), [
- { msg: I18N('YES'), result: true },
- { result: false, isClose: true }
- ])) {
- break;
- } else {
- return;
- }
- }
- const hero = heroes.find(e => e.id == upArtifact.heroId);
- hero.artifacts[upArtifact.slotId].level++;
- inventory[upArtifact.costCurrency][upArtifact.costId] -= upArtifact.costValue;
- calls.push({
- name: "heroArtifactLevelUp",
- args: {
- heroId: upArtifact.heroId,
- slotId: upArtifact.slotId
- },
- ident: `heroArtifactLevelUp_${i}`
- });
- }
-
- if (!calls.length) {
- console.log(I18N('NOT_ENOUGH_RESOURECES'));
- setProgress(I18N('NOT_ENOUGH_RESOURECES'), false);
- return;
- }
-
- await Send(JSON.stringify({ calls })).then(e => {
- if ('error' in e) {
- console.log(I18N('NOT_ENOUGH_RESOURECES'));
- setProgress(I18N('NOT_ENOUGH_RESOURECES'), false);
- } else {
- console.log(I18N('IMPROVED_LEVELS', { count: e.results.length }));
- setProgress(I18N('IMPROVED_LEVELS', { count: e.results.length }), false);
- }
- });
-}
-
-window.sign = a => {
- const i = this['\x78\x79\x7a'];
- return md5([i['\x6e\x61\x6d\x65'], i['\x76\x65\x72\x73\x69\x6f\x6e'], i['\x61\x75\x74\x68\x6f\x72'], ~(a % 1e3)]['\x6a\x6f\x69\x6e']('\x5f'))
-}
-
-async function updateSkins() {
- const count = +await popup.confirm(I18N('SET_NUMBER_LEVELS'), [
- { msg: I18N('BTN_GO'), isInput: true, default: 10 },
- { result: false, isClose: true }
- ]);
- if (!count) {
- return;
- }
-
- const quest = new questRun;
- await quest.autoInit();
- const heroes = Object.values(quest.questInfo['heroGetAll']);
- const inventory = quest.questInfo['inventoryGet'];
- const calls = [];
- for (let i = count; i > 0; i--) {
- const upSkin = quest.getUpgradeSkin();
- if (!upSkin.heroId) {
- if (await popup.confirm(I18N('POSSIBLE_IMPROVE_LEVELS', { count: calls.length }), [
- { msg: I18N('YES'), result: true },
- { result: false, isClose: true }
- ])) {
- break;
- } else {
- return;
- }
- }
- const hero = heroes.find(e => e.id == upSkin.heroId);
- hero.skins[upSkin.skinId]++;
- inventory[upSkin.costCurrency][upSkin.costCurrencyId] -= upSkin.cost;
- calls.push({
- name: "heroSkinUpgrade",
- args: {
- heroId: upSkin.heroId,
- skinId: upSkin.skinId
- },
- ident: `heroSkinUpgrade_${i}`
- })
- }
-
- if (!calls.length) {
- console.log(I18N('NOT_ENOUGH_RESOURECES'));
- setProgress(I18N('NOT_ENOUGH_RESOURECES'), false);
- return;
- }
-
- await Send(JSON.stringify({ calls })).then(e => {
- if ('error' in e) {
- console.log(I18N('NOT_ENOUGH_RESOURECES'));
- setProgress(I18N('NOT_ENOUGH_RESOURECES'), false);
- } else {
- console.log(I18N('IMPROVED_LEVELS', { count: e.results.length }));
- setProgress(I18N('IMPROVED_LEVELS', { count: e.results.length }), false);
- }
- });
-}
-
-function getQuestionInfo(img, nameOnly = false) {
- const libHeroes = Object.values(lib.data.hero);
- const parts = img.split(':');
- const id = parts[1];
- switch (parts[0]) {
- case 'titanArtifact_id':
- return cheats.translate("LIB_TITAN_ARTIFACT_NAME_" + id);
- case 'titan':
- return cheats.translate("LIB_HERO_NAME_" + id);
- case 'skill':
- return cheats.translate("LIB_SKILL_" + id);
- case 'inventoryItem_gear':
- return cheats.translate("LIB_GEAR_NAME_" + id);
- case 'inventoryItem_coin':
- return cheats.translate("LIB_COIN_NAME_" + id);
- case 'artifact':
- if (nameOnly) {
- return cheats.translate("LIB_ARTIFACT_NAME_" + id);
- }
- heroes = libHeroes.filter(h => h.id < 100 && h.artifacts.includes(+id));
- return {
- /** Как называется этот артефакт? */
- name: cheats.translate("LIB_ARTIFACT_NAME_" + id),
- /** Какому герою принадлежит этот артефакт? */
- heroes: heroes.map(h => cheats.translate("LIB_HERO_NAME_" + h.id))
- };
- case 'hero':
- if (nameOnly) {
- return cheats.translate("LIB_HERO_NAME_" + id);
- }
- artifacts = lib.data.hero[id].artifacts;
- return {
- /** Как зовут этого героя? */
- name: cheats.translate("LIB_HERO_NAME_" + id),
- /** Какой артефакт принадлежит этому герою? */
- artifact: artifacts.map(a => cheats.translate("LIB_ARTIFACT_NAME_" + a))
- };
- }
-}
-
-function hintQuest(quest) {
- const result = {};
- if (quest?.questionIcon) {
- const info = getQuestionInfo(quest.questionIcon);
- if (info?.heroes) {
- /** Какому герою принадлежит этот артефакт? */
- result.answer = quest.answers.filter(e => info.heroes.includes(e.answerText.slice(1)));
- }
- if (info?.artifact) {
- /** Какой артефакт принадлежит этому герою? */
- result.answer = quest.answers.filter(e => info.artifact.includes(e.answerText.slice(1)));
- }
- if (typeof info == 'string') {
- result.info = { name: info };
- } else {
- result.info = info;
- }
- }
-
- if (quest.answers[0]?.answerIcon) {
- result.answer = quest.answers.filter(e => quest.question.includes(getQuestionInfo(e.answerIcon, true)))
- }
-
- if ((!result?.answer || !result.answer.length) && !result.info?.name) {
- return false;
- }
-
- let resultText = '';
- if (result?.info) {
- resultText += I18N('PICTURE') + result.info.name;
- }
- console.log(result);
- if (result?.answer && result.answer.length) {
- resultText += I18N('ANSWER') + result.answer[0].id + (!result.answer[0].answerIcon ? ' - ' + result.answer[0].answerText : '');
- }
-
- return resultText;
-}
-
-/**
- * Attack of the minions of Asgard
- *
- * Атака прислужников Асгарда
- */
-function testRaidNodes() {
- return new Promise((resolve, reject) => {
- const tower = new executeRaidNodes(resolve, reject);
- tower.start();
- });
-}
-
-/**
- * Attack of the minions of Asgard
- *
- * Атака прислужников Асгарда
- */
-function executeRaidNodes(resolve, reject) {
- let raidData = {
- teams: [],
- favor: {},
- nodes: [],
- attempts: 0,
- countExecuteBattles: 0,
- cancelBattle: 0,
- }
-
- callsExecuteRaidNodes = {
- calls: [{
- name: "clanRaid_getInfo",
- args: {},
- ident: "clanRaid_getInfo"
- }, {
- name: "teamGetAll",
- args: {},
- ident: "teamGetAll"
- }, {
- name: "teamGetFavor",
- args: {},
- ident: "teamGetFavor"
- }]
- }
-
- this.start = function () {
- send(JSON.stringify(callsExecuteRaidNodes), startRaidNodes);
- }
-
- async function startRaidNodes(data) {
- res = data.results;
- clanRaidInfo = res[0].result.response;
- teamGetAll = res[1].result.response;
- teamGetFavor = res[2].result.response;
-
- let index = 0;
- let isNotFullPack = false;
- for (let team of teamGetAll.clanRaid_nodes) {
- if (team.length < 6) {
- isNotFullPack = true;
- }
- raidData.teams.push({
- data: {},
- heroes: team.filter(id => id < 6000),
- pet: team.filter(id => id >= 6000).pop(),
- battleIndex: index++
- });
- }
- raidData.favor = teamGetFavor.clanRaid_nodes;
-
- if (isNotFullPack) {
- if (await popup.confirm(I18N('MINIONS_WARNING'), [
- { msg: I18N('BTN_NO'), result: true },
- { msg: I18N('BTN_YES'), result: false },
- ])) {
- endRaidNodes('isNotFullPack');
- return;
- }
- }
-
- raidData.nodes = clanRaidInfo.nodes;
- raidData.attempts = clanRaidInfo.attempts;
- isCancalBattle = false;
-
- checkNodes();
- }
-
- function getAttackNode() {
- for (let nodeId in raidData.nodes) {
- let node = raidData.nodes[nodeId];
- let points = 0
- for (team of node.teams) {
- points += team.points;
- }
- let now = Date.now() / 1000;
- if (!points && now > node.timestamps.start && now < node.timestamps.end) {
- let countTeam = node.teams.length;
- delete raidData.nodes[nodeId];
- return {
- nodeId,
- countTeam
- };
- }
- }
- return null;
- }
-
- function checkNodes() {
- setProgress(`${I18N('REMAINING_ATTEMPTS')}: ${raidData.attempts}`);
- let nodeInfo = getAttackNode();
- if (nodeInfo && raidData.attempts) {
- startNodeBattles(nodeInfo);
- return;
- }
-
- endRaidNodes('EndRaidNodes');
- }
-
- function startNodeBattles(nodeInfo) {
- let {nodeId, countTeam} = nodeInfo;
- let teams = raidData.teams.slice(0, countTeam);
- let heroes = raidData.teams.map(e => e.heroes).flat();
- let favor = {...raidData.favor};
- for (let heroId in favor) {
- if (!heroes.includes(+heroId)) {
- delete favor[heroId];
- }
- }
-
- let calls = [{
- name: "clanRaid_startNodeBattles",
- args: {
- nodeId,
- teams,
- favor
- },
- ident: "body"
- }];
-
- send(JSON.stringify({calls}), resultNodeBattles);
- }
-
- function resultNodeBattles(e) {
- if (e['error']) {
- endRaidNodes('nodeBattlesError', e['error']);
- return;
- }
-
- console.log(e);
- let battles = e.results[0].result.response.battles;
- let promises = [];
- let battleIndex = 0;
- for (let battle of battles) {
- battle.battleIndex = battleIndex++;
- promises.push(calcBattleResult(battle));
- }
-
- Promise.all(promises)
- .then(results => {
- const endResults = {};
- let isAllWin = true;
- for (let r of results) {
- isAllWin &&= r.result.win;
- }
- if (!isAllWin) {
- cancelEndNodeBattle(results[0]);
- return;
- }
- raidData.countExecuteBattles = results.length;
- let timeout = 500;
- for (let r of results) {
- setTimeout(endNodeBattle, timeout, r);
- timeout += 500;
- }
- });
- }
- /**
- * Returns the battle calculation promise
- *
- * Возвращает промис расчета боя
- */
- function calcBattleResult(battleData) {
- return new Promise(function (resolve, reject) {
- BattleCalc(battleData, "get_clanPvp", resolve);
- });
- }
- /**
- * Cancels the fight
- *
- * Отменяет бой
- */
- function cancelEndNodeBattle(r) {
- const fixBattle = function (heroes) {
- for (const ids in heroes) {
- hero = heroes[ids];
- hero.energy = random(1, 999);
- if (hero.hp > 0) {
- hero.hp = random(1, hero.hp);
- }
- }
- }
- fixBattle(r.progress[0].attackers.heroes);
- fixBattle(r.progress[0].defenders.heroes);
- endNodeBattle(r);
- }
- /**
- * Ends the fight
- *
- * Завершает бой
- */
- function endNodeBattle(r) {
- let nodeId = r.battleData.result.nodeId;
- let battleIndex = r.battleData.battleIndex;
- let calls = [{
- name: "clanRaid_endNodeBattle",
- args: {
- nodeId,
- battleIndex,
- result: r.result,
- progress: r.progress
- },
- ident: "body"
- }]
-
- SendRequest(JSON.stringify({calls}), battleResult);
- }
- /**
- * Processing the results of the battle
- *
- * Обработка результатов боя
- */
- function battleResult(e) {
- if (e['error']) {
- endRaidNodes('missionEndError', e['error']);
- return;
- }
- r = e.results[0].result.response;
- if (r['error']) {
- if (r.reason == "invalidBattle") {
- raidData.cancelBattle++;
- checkNodes();
- } else {
- endRaidNodes('missionEndError', e['error']);
- }
- return;
- }
-
- if (!(--raidData.countExecuteBattles)) {
- raidData.attempts--;
- checkNodes();
- }
- }
- /**
- * Completing a task
- *
- * Завершение задачи
- */
- function endRaidNodes(reason, info) {
- isCancalBattle = true;
- let textCancel = raidData.cancelBattle ? ` ${I18N('BATTLES_CANCELED')}: ${raidData.cancelBattle}` : '';
- setProgress(`${I18N('MINION_RAID')} ${I18N('COMPLETED')}! ${textCancel}`, true);
- console.log(reason, info);
- resolve();
- }
-}
-
-/**
- * Asgard Boss Attack Replay
- *
- * Повтор атаки босса Асгарда
- */
-function testBossBattle() {
- return new Promise((resolve, reject) => {
- const bossBattle = new executeBossBattle(resolve, reject);
- bossBattle.start(lastBossBattle);
- });
-}
-
-/**
- * Asgard Boss Attack Replay
- *
- * Повтор атаки босса Асгарда
- */
-function executeBossBattle(resolve, reject) {
-
- this.start = function (battleInfo) {
- preCalcBattle(battleInfo);
- }
-
- function getBattleInfo(battle) {
- return new Promise(function (resolve) {
- battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3);
- BattleCalc(battle, getBattleType(battle.type), e => {
- let extra = e.progress[0].defenders.heroes[1].extra;
- resolve(extra.damageTaken + extra.damageTakenNextLevel);
- });
- });
- }
-
- function preCalcBattle(battle) {
- let actions = [];
- const countTestBattle = getInput('countTestBattle');
- for (let i = 0; i < countTestBattle; i++) {
- actions.push(getBattleInfo(battle, true));
- }
- Promise.all(actions)
- .then(resultPreCalcBattle);
- }
-
- async function resultPreCalcBattle(damages) {
- let maxDamage = 0;
- let minDamage = 1e10;
- let avgDamage = 0;
- for (let damage of damages) {
- avgDamage += damage
- if (damage > maxDamage) {
- maxDamage = damage;
- }
- if (damage < minDamage) {
- minDamage = damage;
- }
- }
- avgDamage /= damages.length;
- console.log(damages.map(e => e.toLocaleString()).join('\n'), avgDamage, maxDamage);
-
- await popup.confirm(
- `${I18N('ROUND_STAT')} ${damages.length} ${I18N('BATTLE')}:` +
- ` ${I18N('MINIMUM')}: ` + minDamage.toLocaleString() +
- ` ${I18N('MAXIMUM')}: ` + maxDamage.toLocaleString() +
- ` ${I18N('AVERAGE')}: ` + avgDamage.toLocaleString()
- , [
- { msg: I18N('BTN_OK'), result: 0},
- ])
- endBossBattle(I18N('BTN_CANCEL'));
- }
-
- /**
- * Completing a task
- *
- * Завершение задачи
- */
- function endBossBattle(reason, info) {
- console.log(reason, info);
- resolve();
- }
-}
-
-/**
- * Auto-repeat attack
- *
- * Автоповтор атаки
- */
-function testAutoBattle() {
- return new Promise((resolve, reject) => {
- const bossBattle = new executeAutoBattle(resolve, reject);
- bossBattle.start(lastBattleArg, lastBattleInfo);
- });
-}
-
-/**
- * Auto-repeat attack
- *
- * Автоповтор атаки
- */
-function executeAutoBattle(resolve, reject) {
- let battleArg = {};
- let countBattle = 0;
- let countError = 0;
- let findCoeff = 0;
- let dataNotEeceived = 0;
- const svgJustice = ' ';
- const svgBoss = ' ';
- const svgAttempt = ' ';
-
- this.start = function (battleArgs, battleInfo) {
- battleArg = battleArgs;
- preCalcBattle(battleInfo);
- }
- /**
- * Returns a promise for combat recalculation
- *
- * Возвращает промис для прерасчета боя
- */
- function getBattleInfo(battle) {
- return new Promise(function (resolve) {
- battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3);
- Calc(battle).then(e => {
- e.coeff = calcCoeff(e, 'defenders');
- resolve(e);
- });
- });
- }
- /**
- * Battle recalculation
- *
- * Прерасчет боя
- */
- function preCalcBattle(battle) {
- let actions = [];
- const countTestBattle = getInput('countTestBattle');
- for (let i = 0; i < countTestBattle; i++) {
- actions.push(getBattleInfo(battle));
- }
- Promise.all(actions)
- .then(resultPreCalcBattle);
- }
- /**
- * Processing the results of the battle recalculation
- *
- * Обработка результатов прерасчета боя
- */
- async function resultPreCalcBattle(results) {
- let countWin = results.reduce((s, w) => w.result.win + s, 0);
- setProgress(`${I18N('CHANCE_TO_WIN')} ${Math.floor(countWin / results.length * 100)}% (${results.length})`, false, hideProgress);
- if (countWin > 0) {
- isCancalBattle = false;
- startBattle();
- return;
- }
-
- let minCoeff = 100;
- let maxCoeff = -100;
- let avgCoeff = 0;
- results.forEach(e => {
- if (e.coeff < minCoeff) minCoeff = e.coeff;
- if (e.coeff > maxCoeff) maxCoeff = e.coeff;
- avgCoeff += e.coeff;
- });
- avgCoeff /= results.length;
-
- if (nameFuncStartBattle == 'invasion_bossStart' ||
- nameFuncStartBattle == 'bossAttack') {
- const result = await popup.confirm(
- I18N('BOSS_VICTORY_IMPOSSIBLE', { battles: results.length }), [
- { msg: I18N('BTN_CANCEL'), result: false, isCancel: true },
- { msg: I18N('BTN_DO_IT'), result: true },
- ])
- if (result) {
- isCancalBattle = false;
- startBattle();
- return;
- }
- setProgress(I18N('NOT_THIS_TIME'), true);
- endAutoBattle('invasion_bossStart');
- return;
- }
-
- const result = await popup.confirm(
- I18N('VICTORY_IMPOSSIBLE') +
- ` ${I18N('ROUND_STAT')} ${results.length} ${I18N('BATTLE')}:` +
- ` ${I18N('MINIMUM')}: ` + minCoeff.toLocaleString() +
- ` ${I18N('MAXIMUM')}: ` + maxCoeff.toLocaleString() +
- ` ${I18N('AVERAGE')}: ` + avgCoeff.toLocaleString() +
- ` ${I18N('FIND_COEFF')} ` + avgCoeff.toLocaleString(), [
- { msg: I18N('BTN_CANCEL'), result: 0, isCancel: true },
- { msg: I18N('BTN_GO'), isInput: true, default: Math.round(avgCoeff * 1000) / 1000 },
- ])
- if (result) {
- findCoeff = result;
- isCancalBattle = false;
- startBattle();
- return;
- }
- setProgress(I18N('NOT_THIS_TIME'), true);
- endAutoBattle(I18N('NOT_THIS_TIME'));
- }
-
- /**
- * Calculation of the combat result coefficient
- *
- * Расчет коэфициента результата боя
- */
- function calcCoeff(result, packType) {
- let beforeSumFactor = 0;
- const beforePack = result.battleData[packType][0];
- for (let heroId in beforePack) {
- const hero = beforePack[heroId];
- const state = hero.state;
- let factor = 1;
- if (state) {
- const hp = state.hp / state.maxHp;
- const energy = state.energy / 1e3;
- factor = hp + energy / 20;
- }
- beforeSumFactor += factor;
- }
-
- let afterSumFactor = 0;
- const afterPack = result.progress[0][packType].heroes;
- for (let heroId in afterPack) {
- const hero = afterPack[heroId];
- const stateHp = beforePack[heroId]?.state?.hp || beforePack[heroId]?.stats?.hp;
- const hp = hero.hp / stateHp;
- const energy = hero.energy / 1e3;
- const factor = hp + energy / 20;
- afterSumFactor += factor;
- }
- const resultCoeff = -(afterSumFactor - beforeSumFactor);
- return Math.round(resultCoeff * 1000) / 1000;
- }
- /**
- * Start battle
- *
- * Начало боя
- */
- function startBattle() {
- countBattle++;
- const countMaxBattle = getInput('countAutoBattle');
- // setProgress(countBattle + '/' + countMaxBattle);
- if (countBattle > countMaxBattle) {
- setProgress(`${I18N('RETRY_LIMIT_EXCEEDED')}: ${countMaxBattle}`, true);
- endAutoBattle(`${I18N('RETRY_LIMIT_EXCEEDED')}: ${countMaxBattle}`)
- return;
- }
- send({calls: [{
- name: nameFuncStartBattle,
- args: battleArg,
- ident: "body"
- }]}, calcResultBattle);
- }
- /**
- * Battle calculation
- *
- * Расчет боя
- */
- async function calcResultBattle(e) {
- if (!e) {
- console.log('данные не были получены');
- if (dataNotEeceived < 10) {
- dataNotEeceived++;
- startBattle();
- return;
- }
- endAutoBattle('Error', 'данные не были получены ' + dataNotEeceived + ' раз');
- return;
- }
- if ('error' in e) {
- if (e.error.description === 'too many tries') {
- invasionTimer += 100;
- countBattle--;
- countError++;
- console.log(`Errors: ${countError}`, e.error);
- startBattle();
- return;
- }
- const result = await popup.confirm(I18N('ERROR_DURING_THE_BATTLE') + ' ' + e.error.description, [
- { msg: I18N('BTN_OK'), result: false },
- { msg: I18N('RELOAD_GAME'), result: true },
- ]);
- endAutoBattle('Error', e.error);
- if (result) {
- location.reload();
- }
- return;
- }
- let battle = e.results[0].result.response.battle
- if (nameFuncStartBattle == 'towerStartBattle' ||
- nameFuncStartBattle == 'bossAttack' ||
- nameFuncStartBattle == 'invasion_bossStart') {
- battle = e.results[0].result.response;
- }
- lastBattleInfo = battle;
- BattleCalc(battle, getBattleType(battle.type), resultBattle);
- }
- /**
- * Processing the results of the battle
- *
- * Обработка результатов боя
- */
- function resultBattle(e) {
- const isWin = e.result.win;
- if (isWin) {
- endBattle(e, false);
- return;
- }
- const countMaxBattle = getInput('countAutoBattle');
- if (findCoeff) {
- const coeff = calcCoeff(e, 'defenders');
- setProgress(`${countBattle}/${countMaxBattle}, ${coeff}`);
- if (coeff > findCoeff) {
- endBattle(e, false);
- return;
- }
- } else {
- if (nameFuncStartBattle == 'invasion_bossStart') {
- const bossLvl = lastBattleInfo.typeId >= 130 ? lastBattleInfo.typeId : '';
- const justice = lastBattleInfo?.effects?.attackers?.percentInOutDamageMod_any_99_100_300_99_1000 || 0;
- setProgress(`${svgBoss} ${bossLvl} ${svgJustice} ${justice} ${svgAttempt} ${countBattle}/${countMaxBattle}`);
- } else {
- setProgress(`${countBattle}/${countMaxBattle}`);
- }
- }
- if (nameFuncStartBattle == 'towerStartBattle' ||
- nameFuncStartBattle == 'bossAttack' ||
- nameFuncStartBattle == 'invasion_bossStart') {
- startBattle();
- return;
- }
- cancelEndBattle(e);
- }
- /**
- * Cancel fight
- *
- * Отмена боя
- */
- function cancelEndBattle(r) {
- const fixBattle = function (heroes) {
- for (const ids in heroes) {
- hero = heroes[ids];
- hero.energy = random(1, 999);
- if (hero.hp > 0) {
- hero.hp = random(1, hero.hp);
- }
- }
- }
- fixBattle(r.progress[0].attackers.heroes);
- fixBattle(r.progress[0].defenders.heroes);
- endBattle(r, true);
- }
- /**
- * End of the fight
- *
- * Завершение боя */
- function endBattle(battleResult, isCancal) {
- let calls = [{
- name: nameFuncEndBattle,
- args: {
- result: battleResult.result,
- progress: battleResult.progress
- },
- ident: "body"
- }];
-
- if (nameFuncStartBattle == 'invasion_bossStart') {
- calls[0].args.id = lastBattleArg.id;
- }
-
- send(JSON.stringify({
- calls
- }), async e => {
- console.log(e);
- if (isCancal) {
- startBattle();
- return;
- }
-
- setProgress(`${I18N('SUCCESS')}!`, 5000)
- if (nameFuncStartBattle == 'invasion_bossStart' ||
- nameFuncStartBattle == 'bossAttack') {
- const countMaxBattle = getInput('countAutoBattle');
- const bossLvl = lastBattleInfo.typeId >= 130 ? lastBattleInfo.typeId : '';
- const justice = lastBattleInfo?.effects?.attackers?.percentInOutDamageMod_any_99_100_300_99_1000 || 0;
- const result = await popup.confirm(
- I18N('BOSS_HAS_BEEN_DEF_TEXT', {
- bossLvl: `${svgBoss} ${bossLvl} ${svgJustice} ${justice}`,
- countBattle: svgAttempt + ' ' + countBattle,
- countMaxBattle,
- }),
- [
- { msg: I18N('BTN_OK'), result: 0 },
- { msg: I18N('MAKE_A_SYNC'), result: 1 },
- { msg: I18N('RELOAD_GAME'), result: 2 },
- ]
- );
- if (result) {
- if (result == 1) {
- cheats.refreshGame();
- }
- if (result == 2) {
- location.reload();
- }
- }
-
- }
- endAutoBattle(`${I18N('SUCCESS')}!`)
- });
- }
- /**
- * Completing a task
- *
- * Завершение задачи
- */
- function endAutoBattle(reason, info) {
- isCancalBattle = true;
- console.log(reason, info);
- resolve();
- }
-}
-
-function testDailyQuests() {
- return new Promise((resolve, reject) => {
- const quests = new dailyQuests(resolve, reject);
- quests.init(questsInfo);
- quests.start();
- });
-}
-
-/**
- * Automatic completion of daily quests
- *
- * Автоматическое выполнение ежедневных квестов
- */
-class dailyQuests {
- /**
- * Send(' {"calls":[{"name":"userGetInfo","args":{},"ident":"body"}]}').then(e => console.log(e))
- * Send(' {"calls":[{"name":"heroGetAll","args":{},"ident":"body"}]}').then(e => console.log(e))
- * Send(' {"calls":[{"name":"titanGetAll","args":{},"ident":"body"}]}').then(e => console.log(e))
- * Send(' {"calls":[{"name":"inventoryGet","args":{},"ident":"body"}]}').then(e => console.log(e))
- * Send(' {"calls":[{"name":"questGetAll","args":{},"ident":"body"}]}').then(e => console.log(e))
- * Send(' {"calls":[{"name":"bossGetAll","args":{},"ident":"body"}]}').then(e => console.log(e))
- */
- callsList = [
- "userGetInfo",
- "heroGetAll",
- "titanGetAll",
- "inventoryGet",
- "questGetAll",
- "bossGetAll",
- ]
-
- dataQuests = {
- 10001: {
- description: 'Улучши умения героев 3 раза', // ++++++++++++++++
- doItCall: () => {
- const upgradeSkills = this.getUpgradeSkills();
- return upgradeSkills.map(({ heroId, skill }, index) => ({ name: "heroUpgradeSkill", args: { heroId, skill }, "ident": `heroUpgradeSkill_${index}` }));
- },
- isWeCanDo: () => {
- const upgradeSkills = this.getUpgradeSkills();
- let sumGold = 0;
- for (const skill of upgradeSkills) {
- sumGold += this.skillCost(skill.value);
- if (!skill.heroId) {
- return false;
- }
- }
- return this.questInfo['userGetInfo'].gold > sumGold;
- },
- },
- 10002: {
- description: 'Пройди 10 миссий', // --------------
- isWeCanDo: () => false,
- },
- 10003: {
- description: 'Пройди 3 героические миссии', // --------------
- isWeCanDo: () => false,
- },
- 10004: {
- description: 'Сразись 3 раза на Арене или Гранд Арене', // --------------
- isWeCanDo: () => false,
- },
- 10006: {
- description: 'Используй обмен изумрудов 1 раз', // ++++++++++++++++
- doItCall: () => [{
- name: "refillableAlchemyUse",
- args: { multi: false },
- ident: "refillableAlchemyUse"
- }],
- isWeCanDo: () => {
- const starMoney = this.questInfo['userGetInfo'].starMoney;
- return starMoney >= 20;
- },
- },
- 10007: {
- description: 'Соверши 1 призыв в Атриуме Душ', // ++++++++++++++++
- doItCall: () => [{ name: "gacha_open", args: { ident: "heroGacha", free: true, pack: false }, ident: "gacha_open" }],
- isWeCanDo: () => {
- const soulCrystal = this.questInfo['inventoryGet'].coin[38];
- return soulCrystal > 0;
- },
- },
- 10016: {
- description: 'Отправь подарки согильдийцам', // ++++++++++++++++
- doItCall: () => [{ name: "clanSendDailyGifts", args: {}, ident: "clanSendDailyGifts" }],
- isWeCanDo: () => true,
- },
- 10018: {
- description: 'Используй зелье опыта', // ++++++++++++++++
- doItCall: () => {
- const expHero = this.getExpHero();
- return [{
- name: "consumableUseHeroXp",
- args: {
- heroId: expHero.heroId,
- libId: expHero.libId,
- amount: 1
- },
- ident: "consumableUseHeroXp"
- }];
- },
- isWeCanDo: () => {
- const expHero = this.getExpHero();
- return expHero.heroId && expHero.libId;
- },
- },
- 10019: {
- description: 'Открой 1 сундук в Башне',
- doItFunc: testTower,
- isWeCanDo: () => false,
- },
- 10020: {
- description: 'Открой 3 сундука в Запределье', // Готово
- doItCall: () => {
- return this.getOutlandChest();
- },
- isWeCanDo: () => {
- const outlandChest = this.getOutlandChest();
- return outlandChest.length > 0;
- },
- },
- 10021: {
- description: 'Собери 75 Титанита в Подземелье Гильдии',
- isWeCanDo: () => false,
- },
- 10022: {
- description: 'Собери 150 Титанита в Подземелье Гильдии',
- doItFunc: testDungeon,
- isWeCanDo: () => false,
- },
- 10023: {
- description: 'Прокачай Дар Стихий на 1 уровень', // Готово
- doItCall: () => {
- const heroId = this.getHeroIdTitanGift();
- return [
- { name: "heroTitanGiftLevelUp", args: { heroId }, ident: "heroTitanGiftLevelUp" },
- { name: "heroTitanGiftDrop", args: { heroId }, ident: "heroTitanGiftDrop" }
- ]
- },
- isWeCanDo: () => {
- const heroId = this.getHeroIdTitanGift();
- return heroId;
- },
- },
- 10024: {
- description: 'Повысь уровень любого артефакта один раз', // Готово
- doItCall: () => {
- const upArtifact = this.getUpgradeArtifact();
- return [
- {
- name: "heroArtifactLevelUp",
- args: {
- heroId: upArtifact.heroId,
- slotId: upArtifact.slotId
- },
- ident: `heroArtifactLevelUp`
- }
- ];
- },
- isWeCanDo: () => {
- const upgradeArtifact = this.getUpgradeArtifact();
- return upgradeArtifact.heroId;
- },
- },
- 10025: {
- description: 'Начни 1 Экспедицию',
- doItFunc: checkExpedition,
- isWeCanDo: () => false,
- },
- 10026: {
- description: 'Начни 4 Экспедиции', // --------------
- doItFunc: checkExpedition,
- isWeCanDo: () => false,
- },
- 10027: {
- description: 'Победи в 1 бою Турнира Стихий',
- doItFunc: testTitanArena,
- isWeCanDo: () => false,
- },
- 10028: {
- description: 'Повысь уровень любого артефакта титанов', // Готово
- doItCall: () => {
- const upTitanArtifact = this.getUpgradeTitanArtifact();
- return [
- {
- name: "titanArtifactLevelUp",
- args: {
- titanId: upTitanArtifact.titanId,
- slotId: upTitanArtifact.slotId
- },
- ident: `titanArtifactLevelUp`
- }
- ];
- },
- isWeCanDo: () => {
- const upgradeTitanArtifact = this.getUpgradeTitanArtifact();
- return upgradeTitanArtifact.titanId;
- },
- },
- 10029: {
- description: 'Открой сферу артефактов титанов', // ++++++++++++++++
- doItCall: () => [{ name: "titanArtifactChestOpen", args: { amount: 1, free: true }, ident: "titanArtifactChestOpen" }],
- isWeCanDo: () => {
- return this.questInfo['inventoryGet']?.consumable[55] > 0
- },
- },
- 10030: {
- description: 'Улучши облик любого героя 1 раз', // Готово
- doItCall: () => {
- const upSkin = this.getUpgradeSkin();
- return [
- {
- name: "heroSkinUpgrade",
- args: {
- heroId: upSkin.heroId,
- skinId: upSkin.skinId
- },
- ident: `heroSkinUpgrade`
- }
- ];
- },
- isWeCanDo: () => {
- const upgradeSkin = this.getUpgradeSkin();
- return upgradeSkin.heroId;
- },
- },
- 10031: {
- description: 'Победи в 6 боях Турнира Стихий', // --------------
- doItFunc: testTitanArena,
- isWeCanDo: () => false,
- },
- 10043: {
- description: 'Начни или присоеденись к Приключению', // --------------
- isWeCanDo: () => false,
- },
- 10044: {
- description: 'Воспользуйся призывом питомцев 1 раз', // ++++++++++++++++
- doItCall: () => [{ name: "pet_chestOpen", args: { amount: 1, paid: false }, ident: "pet_chestOpen" }],
- isWeCanDo: () => {
- return this.questInfo['inventoryGet']?.consumable[90] > 0
- },
- },
- 10046: {
- /**
- * TODO: Watch Adventure
- * TODO: Смотреть приключение
- */
- description: 'Открой 3 сундука в Приключениях',
- isWeCanDo: () => false,
- },
- 10047: {
- description: 'Набери 150 очков активности в Гильдии', // Готово
- doItCall: () => {
- const enchantRune = this.getEnchantRune();
- return [
- {
- name: "heroEnchantRune",
- args: {
- heroId: enchantRune.heroId,
- tier: enchantRune.tier,
- items: {
- consumable: { [enchantRune.itemId]: 1 }
- }
- },
- ident: `heroEnchantRune`
- }
- ];
- },
- isWeCanDo: () => {
- const userInfo = this.questInfo['userGetInfo'];
- const enchantRune = this.getEnchantRune();
- return enchantRune.heroId && userInfo.gold > 1e3;
- },
- },
- };
-
- constructor(resolve, reject, questInfo) {
- this.resolve = resolve;
- this.reject = reject;
- }
-
- init(questInfo) {
- this.questInfo = questInfo;
- this.isAuto = false;
- }
-
- async autoInit(isAuto) {
- this.isAuto = isAuto || false;
- const quests = {};
- const calls = this.callsList.map(name => ({
- name, args: {}, ident: name
- }))
- const result = await Send(JSON.stringify({ calls })).then(e => e.results);
- for (const call of result) {
- quests[call.ident] = call.result.response;
- }
- this.questInfo = quests;
- }
-
- async start() {
- const weCanDo = [];
- const selectedActions = getSaveVal('selectedActions', {});
- for (let quest of this.questInfo['questGetAll']) {
- if (quest.id in this.dataQuests && quest.state == 1) {
- if (!selectedActions[quest.id]) {
- selectedActions[quest.id] = {
- checked: false
- }
- }
-
- const isWeCanDo = this.dataQuests[quest.id].isWeCanDo;
- if (!isWeCanDo.call(this)) {
- continue;
- }
-
- weCanDo.push({
- name: quest.id,
- label: I18N(`QUEST_${quest.id}`),
- checked: selectedActions[quest.id].checked
- });
- }
- }
-
- if (!weCanDo.length) {
- this.end(I18N('NOTHING_TO_DO'));
- return;
- }
-
- console.log(weCanDo);
- let taskList = [];
- if (this.isAuto) {
- taskList = weCanDo;
- } else {
- const answer = await popup.confirm(`${I18N('YOU_CAN_COMPLETE') }:`, [
- { msg: I18N('BTN_DO_IT'), result: true },
- { msg: I18N('BTN_CANCEL'), result: false, isCancel: true },
- ], weCanDo);
- if (!answer) {
- this.end('');
- return;
- }
- taskList = popup.getCheckBoxes();
- taskList.forEach(e => {
- selectedActions[e.name].checked = e.checked;
- });
- setSaveVal('selectedActions', selectedActions);
- }
-
- const calls = [];
- let countChecked = 0;
- for (const task of taskList) {
- if (task.checked) {
- countChecked++;
- const quest = this.dataQuests[task.name]
- console.log(quest.description);
-
- if (quest.doItCall) {
- const doItCall = quest.doItCall.call(this);
- calls.push(...doItCall);
- }
- }
- }
-
- if (!countChecked) {
- this.end(I18N('NOT_QUEST_COMPLETED'));
- return;
- }
-
- const result = await Send(JSON.stringify({ calls }));
- if (result.error) {
- console.error(result.error, result.error.call)
- }
- this.end(`${I18N('COMPLETED_QUESTS')}: ${countChecked}`);
- }
-
- errorHandling(error) {
- //console.error(error);
- let errorInfo = error.toString() + '\n';
- try {
- const errorStack = error.stack.split('\n');
- const endStack = errorStack.map(e => e.split('@')[0]).indexOf("testDoYourBest");
- errorInfo += errorStack.slice(0, endStack).join('\n');
- } catch (e) {
- errorInfo += error.stack;
- }
- copyText(errorInfo);
- }
-
- skillCost(lvl) {
- return 573 * lvl ** 0.9 + lvl ** 2.379;
- }
-
- getUpgradeSkills() {
- const heroes = Object.values(this.questInfo['heroGetAll']);
- const upgradeSkills = [
- { heroId: 0, slotId: 0, value: 130 },
- { heroId: 0, slotId: 0, value: 130 },
- { heroId: 0, slotId: 0, value: 130 },
- ];
- const skillLib = lib.getData('skill');
- /**
- * color - 1 (белый) открывает 1 навык
- * color - 2 (зеленый) открывает 2 навык
- * color - 4 (синий) открывает 3 навык
- * color - 7 (фиолетовый) открывает 4 навык
- */
- const colors = [1, 2, 4, 7];
- for (const hero of heroes) {
- const level = hero.level;
- const color = hero.color;
- for (let skillId in hero.skills) {
- const tier = skillLib[skillId].tier;
- const sVal = hero.skills[skillId];
- if (color < colors[tier] || tier < 1 || tier > 4) {
- continue;
- }
- for (let upSkill of upgradeSkills) {
- if (sVal < upSkill.value && sVal < level) {
- upSkill.value = sVal;
- upSkill.heroId = hero.id;
- upSkill.skill = tier;
- break;
- }
- }
- }
- }
- return upgradeSkills;
- }
-
- getUpgradeArtifact() {
- const heroes = Object.values(this.questInfo['heroGetAll']);
- const inventory = this.questInfo['inventoryGet'];
- const upArt = { heroId: 0, slotId: 0, level: 100 };
-
- const heroLib = lib.getData('hero');
- const artifactLib = lib.getData('artifact');
-
- for (const hero of heroes) {
- const heroInfo = heroLib[hero.id];
- const level = hero.level
- if (level < 20) {
- continue;
- }
-
- for (let slotId in hero.artifacts) {
- const art = hero.artifacts[slotId];
- /* Текущая звезданость арта */
- const star = art.star;
- if (!star) {
- continue;
- }
- /* Текущий уровень арта */
- const level = art.level;
- if (level >= 100) {
- continue;
- }
- /* Идентификатор арта в библиотеке */
- const artifactId = heroInfo.artifacts[slotId];
- const artInfo = artifactLib.id[artifactId];
- const costNextLevel = artifactLib.type[artInfo.type].levels[level + 1].cost;
-
- const costCurrency = Object.keys(costNextLevel).pop();
- const costValues = Object.entries(costNextLevel[costCurrency]).pop();
- const costId = costValues[0];
- const costValue = +costValues[1];
-
- /** TODO: Возможно стоит искать самый высокий уровень который можно качнуть? */
- if (level < upArt.level && inventory[costCurrency][costId] >= costValue) {
- upArt.level = level;
- upArt.heroId = hero.id;
- upArt.slotId = slotId;
- upArt.costCurrency = costCurrency;
- upArt.costId = costId;
- upArt.costValue = costValue;
- }
- }
- }
- return upArt;
- }
-
- getUpgradeSkin() {
- const heroes = Object.values(this.questInfo['heroGetAll']);
- const inventory = this.questInfo['inventoryGet'];
- const upSkin = { heroId: 0, skinId: 0, level: 60, cost: 1500 };
-
- const skinLib = lib.getData('skin');
-
- for (const hero of heroes) {
- const level = hero.level
- if (level < 20) {
- continue;
- }
-
- for (let skinId in hero.skins) {
- /* Текущий уровень скина */
- const level = hero.skins[skinId];
- if (level >= 60) {
- continue;
- }
- /* Идентификатор скина в библиотеке */
- const skinInfo = skinLib[skinId];
- if (!skinInfo.statData.levels?.[level + 1]) {
- continue;
- }
- const costNextLevel = skinInfo.statData.levels[level + 1].cost;
-
- const costCurrency = Object.keys(costNextLevel).pop();
- const costCurrencyId = Object.keys(costNextLevel[costCurrency]).pop();
- const costValue = +costNextLevel[costCurrency][costCurrencyId];
-
- /** TODO: Возможно стоит искать самый высокий уровень который можно качнуть? */
- if (level < upSkin.level &&
- costValue < upSkin.cost &&
- inventory[costCurrency][costCurrencyId] >= costValue) {
- upSkin.cost = costValue;
- upSkin.level = level;
- upSkin.heroId = hero.id;
- upSkin.skinId = skinId;
- upSkin.costCurrency = costCurrency;
- upSkin.costCurrencyId = costCurrencyId;
- }
- }
- }
- return upSkin;
- }
-
- getUpgradeTitanArtifact() {
- const titans = Object.values(this.questInfo['titanGetAll']);
- const inventory = this.questInfo['inventoryGet'];
- const userInfo = this.questInfo['userGetInfo'];
- const upArt = { titanId: 0, slotId: 0, level: 120 };
-
- const titanLib = lib.getData('titan');
- const artTitanLib = lib.getData('titanArtifact');
-
- for (const titan of titans) {
- const titanInfo = titanLib[titan.id];
- // const level = titan.level
- // if (level < 20) {
- // continue;
- // }
-
- for (let slotId in titan.artifacts) {
- const art = titan.artifacts[slotId];
- /* Текущая звезданость арта */
- const star = art.star;
- if (!star) {
- continue;
- }
- /* Текущий уровень арта */
- const level = art.level;
- if (level >= 120) {
- continue;
- }
- /* Идентификатор арта в библиотеке */
- const artifactId = titanInfo.artifacts[slotId];
- const artInfo = artTitanLib.id[artifactId];
- const costNextLevel = artTitanLib.type[artInfo.type].levels[level + 1].cost;
-
- const costCurrency = Object.keys(costNextLevel).pop();
- let costValue = 0;
- let currentValue = 0;
- if (costCurrency == 'gold') {
- costValue = costNextLevel[costCurrency];
- currentValue = userInfo.gold;
- } else {
- const costValues = Object.entries(costNextLevel[costCurrency]).pop();
- const costId = costValues[0];
- costValue = +costValues[1];
- currentValue = inventory[costCurrency][costId];
- }
-
- /** TODO: Возможно стоит искать самый высокий уровень который можно качнуть? */
- if (level < upArt.level && currentValue >= costValue) {
- upArt.level = level;
- upArt.titanId = titan.id;
- upArt.slotId = slotId;
- break;
- }
- }
- }
- return upArt;
- }
-
- getEnchantRune() {
- const heroes = Object.values(this.questInfo['heroGetAll']);
- const inventory = this.questInfo['inventoryGet'];
- const enchRune = { heroId: 0, tier: 0, exp: 43750, itemId: 0 };
- for (let i = 1; i <= 4; i++) {
- if (inventory.consumable[i] > 0) {
- enchRune.itemId = i;
- break;
- }
- return enchRune;
- }
-
- const runeLib = lib.getData('rune');
- const runeLvls = Object.values(runeLib.level);
- /**
- * color - 4 (синий) открывает 1 и 2 символ
- * color - 7 (фиолетовый) открывает 3 символ
- * color - 8 (фиолетовый +1) открывает 4 символ
- * color - 9 (фиолетовый +2) открывает 5 символ
- */
- // TODO: кажется надо учесть уровень команды
- const colors = [4, 4, 7, 8, 9];
- for (const hero of heroes) {
- const color = hero.color;
-
-
- for (let runeTier in hero.runes) {
- /* Проверка на доступность руны */
- if (color < colors[runeTier]) {
- continue;
- }
- /* Текущий опыт руны */
- const exp = hero.runes[runeTier];
- if (exp >= 43750) {
- continue;
- }
-
- let level = 0;
- if (exp) {
- for (let lvl of runeLvls) {
- if (exp >= lvl.enchantValue) {
- level = lvl.level;
- } else {
- break;
- }
- }
- }
- /** Уровень героя необходимый для уровня руны */
- const heroLevel = runeLib.level[level].heroLevel;
- if (hero.level < heroLevel) {
- continue;
- }
-
- /** TODO: Возможно стоит искать самый высокий уровень который можно качнуть? */
- if (exp < enchRune.exp) {
- enchRune.exp = exp;
- enchRune.heroId = hero.id;
- enchRune.tier = runeTier;
- break;
- }
- }
- }
- return enchRune;
- }
-
- getOutlandChest() {
- const bosses = this.questInfo['bossGetAll'];
-
- const calls = [];
-
- for (let boss of bosses) {
- if (boss.mayRaid) {
- calls.push({
- name: "bossRaid",
- args: {
- bossId: boss.id
- },
- ident: "bossRaid_" + boss.id
- });
- calls.push({
- name: "bossOpenChest",
- args: {
- bossId: boss.id,
- amount: 1,
- starmoney: 0
- },
- ident: "bossOpenChest_" + boss.id
- });
- } else if (boss.chestId == 1) {
- calls.push({
- name: "bossOpenChest",
- args: {
- bossId: boss.id,
- amount: 1,
- starmoney: 0
- },
- ident: "bossOpenChest_" + boss.id
- });
- }
- }
-
- return calls;
- }
-
- getExpHero() {
- const heroes = Object.values(this.questInfo['heroGetAll']);
- const inventory = this.questInfo['inventoryGet'];
- const expHero = { heroId: 0, exp: 3625195, libId: 0 };
- /** зелья опыта (consumable 9, 10, 11, 12) */
- for (let i = 9; i <= 12; i++) {
- if (inventory.consumable[i]) {
- expHero.libId = i;
- break;
- }
- }
-
- for (const hero of heroes) {
- const exp = hero.xp;
- if (exp < expHero.exp) {
- expHero.heroId = hero.id;
- }
- }
- return expHero;
- }
-
- getHeroIdTitanGift() {
- const heroes = Object.values(this.questInfo['heroGetAll']);
- const inventory = this.questInfo['inventoryGet'];
- const user = this.questInfo['userGetInfo'];
- const titanGiftLib = lib.getData('titanGift');
- /** Искры */
- const titanGift = inventory.consumable[24];
- let heroId = 0;
- let minLevel = 30;
-
- if (titanGift < 250 || user.gold < 7000) {
- return 0;
- }
-
- for (const hero of heroes) {
- if (hero.titanGiftLevel >= 30) {
- continue;
- }
-
- if (!hero.titanGiftLevel) {
- return hero.id;
- }
-
- const cost = titanGiftLib[hero.titanGiftLevel].cost;
- if (minLevel > hero.titanGiftLevel &&
- titanGift >= cost.consumable[24] &&
- user.gold >= cost.gold
- ) {
- minLevel = hero.titanGiftLevel;
- heroId = hero.id;
- }
- }
-
- return heroId;
- }
-
- end(status) {
- setProgress(status, true);
- this.resolve();
- }
-}
-
-this.questRun = dailyQuests;
-
-function testDoYourBest() {
- return new Promise((resolve, reject) => {
- const doIt = new doYourBest(resolve, reject);
- doIt.start();
- });
-}
-
-/**
- * Do everything button
- *
- * Кнопка сделать все
- */
-class doYourBest {
-
- funcList = [
- {
- name: 'getOutland',
- label: I18N('ASSEMBLE_OUTLAND'),
- checked: false
- },
- {
- name: 'testTower',
- label: I18N('PASS_THE_TOWER'),
- checked: false
- },
- {
- name: 'checkExpedition',
- label: I18N('CHECK_EXPEDITIONS'),
- checked: false
- },
- {
- name: 'testTitanArena',
- label: I18N('COMPLETE_TOE'),
- checked: false
- },
- {
- name: 'mailGetAll',
- label: I18N('COLLECT_MAIL'),
- checked: false
- },
- {
- name: 'collectAllStuff',
- label: I18N('COLLECT_MISC'),
- title: I18N('COLLECT_MISC_TITLE'),
- checked: false
- },
- {
- name: 'getDailyBonus',
- label: I18N('DAILY_BONUS'),
- checked: false
- },
- {
- name: 'dailyQuests',
- label: I18N('DO_DAILY_QUESTS'),
- checked: false
- },
- {
- name: 'rollAscension',
- label: I18N('SEER_TITLE'),
- checked: false
- },
- {
- name: 'questAllFarm',
- label: I18N('COLLECT_QUEST_REWARDS'),
- checked: false
- },
- {
- name: 'testDungeon',
- label: I18N('COMPLETE_DUNGEON'),
- checked: false
- },
- {
- name: 'synchronization',
- label: I18N('MAKE_A_SYNC'),
- checked: false
- },
- {
- name: 'reloadGame',
- label: I18N('RELOAD_GAME'),
- checked: false
- },
- ];
-
- functions = {
- getOutland,
- testTower,
- checkExpedition,
- testTitanArena,
- mailGetAll,
- collectAllStuff: async () => {
- await offerFarmAllReward();
- await Send('{"calls":[{"name":"subscriptionFarm","args":{},"ident":"body"},{"name":"zeppelinGiftFarm","args":{},"ident":"zeppelinGiftFarm"},{"name":"grandFarmCoins","args":{},"ident":"grandFarmCoins"},{"name":"gacha_refill","args":{"ident":"heroGacha"},"ident":"gacha_refill"}]}');
- },
- dailyQuests: async function () {
- const quests = new dailyQuests(() => { }, () => { });
- await quests.autoInit(true);
- await quests.start();
- },
- rollAscension,
- getDailyBonus,
- questAllFarm,
- testDungeon,
- synchronization: async () => {
- cheats.refreshGame();
- },
- reloadGame: async () => {
- location.reload();
- },
- }
-
- constructor(resolve, reject, questInfo) {
- this.resolve = resolve;
- this.reject = reject;
- this.questInfo = questInfo
- }
-
- async start() {
- const selectedDoIt = getSaveVal('selectedDoIt', {});
-
- this.funcList.forEach(task => {
- if (!selectedDoIt[task.name]) {
- selectedDoIt[task.name] = {
- checked: task.checked
- }
- } else {
- task.checked = selectedDoIt[task.name].checked
- }
- });
-
- const answer = await popup.confirm(I18N('RUN_FUNCTION'), [
- { msg: I18N('BTN_CANCEL'), result: false, isCancel: true },
- { msg: I18N('BTN_GO'), result: true },
- ], this.funcList);
-
- if (!answer) {
- this.end('');
- return;
- }
-
- const taskList = popup.getCheckBoxes();
- taskList.forEach(task => {
- selectedDoIt[task.name].checked = task.checked;
- });
- setSaveVal('selectedDoIt', selectedDoIt);
- for (const task of popup.getCheckBoxes()) {
- if (task.checked) {
- try {
- setProgress(`${task.label} ${I18N('PERFORMED')}!`);
- await this.functions[task.name]();
- setProgress(`${task.label} ${I18N('DONE')}!`);
- } catch (error) {
- if (await popup.confirm(`${I18N('ERRORS_OCCURRES')}: ${task.label} ${I18N('COPY_ERROR')}?`, [
- { msg: I18N('BTN_NO'), result: false },
- { msg: I18N('BTN_YES'), result: true },
- ])) {
- this.errorHandling(error);
- }
- }
- }
- }
- setTimeout((msg) => {
- this.end(msg);
- }, 2000, I18N('ALL_TASK_COMPLETED'));
- return;
- }
-
- errorHandling(error) {
- //console.error(error);
- let errorInfo = error.toString() + '\n';
- try {
- const errorStack = error.stack.split('\n');
- const endStack = errorStack.map(e => e.split('@')[0]).indexOf("testDoYourBest");
- errorInfo += errorStack.slice(0, endStack).join('\n');
- } catch (e) {
- errorInfo += error.stack;
- }
- copyText(errorInfo);
- }
-
- end(status) {
- setProgress(status, true);
- this.resolve();
- }
-}
-
-/**
- * Passing the adventure along the specified route
- *
- * Прохождение приключения по указанному маршруту
- */
-function testAdventure(type) {
- return new Promise((resolve, reject) => {
- const bossBattle = new executeAdventure(resolve, reject);
- bossBattle.start(type);
- });
-}
-
-/**
- * Passing the adventure along the specified route
- *
- * Прохождение приключения по указанному маршруту
- */
-class executeAdventure {
-
- type = 'default';
-
- actions = {
- default: {
- getInfo: "adventure_getInfo",
- startBattle: 'adventure_turnStartBattle',
- endBattle: 'adventure_endBattle',
- collectBuff: 'adventure_turnCollectBuff'
- },
- solo: {
- getInfo: "adventureSolo_getInfo",
- startBattle: 'adventureSolo_turnStartBattle',
- endBattle: 'adventureSolo_endBattle',
- collectBuff: 'adventureSolo_turnCollectBuff'
- }
- }
-
- terminatеReason = I18N('UNKNOWN');
- callAdventureInfo = {
- name: "adventure_getInfo",
- args: {},
- ident: "adventure_getInfo"
- }
- callTeamGetAll = {
- name: "teamGetAll",
- args: {},
- ident: "teamGetAll"
- }
- callTeamGetFavor = {
- name: "teamGetFavor",
- args: {},
- ident: "teamGetFavor"
- }
- callStartBattle = {
- name: "adventure_turnStartBattle",
- args: {},
- ident: "body"
- }
- callEndBattle = {
- name: "adventure_endBattle",
- args: {
- result: {},
- progress: {},
- },
- ident: "body"
- }
- callCollectBuff = {
- name: "adventure_turnCollectBuff",
- args: {},
- ident: "body"
- }
-
- constructor(resolve, reject) {
- this.resolve = resolve;
- this.reject = reject;
- }
-
- async start(type) {
- this.type = type || this.type;
- this.callAdventureInfo.name = this.actions[this.type].getInfo;
- const data = await Send(JSON.stringify({
- calls: [
- this.callAdventureInfo,
- this.callTeamGetAll,
- this.callTeamGetFavor
- ]
- }));
- return this.checkAdventureInfo(data.results);
- }
-
- async getPath() {
- const oldVal = getSaveVal('adventurePath', '');
- const keyPath = `adventurePath:${this.mapIdent}`;
- const answer = await popup.confirm(I18N('ENTER_THE_PATH'), [
- {
- msg: I18N('START_ADVENTURE'),
- placeholder: '1,2,3,4,5,6',
- isInput: true,
- default: getSaveVal(keyPath, oldVal)
- },
- {
- msg: I18N('BTN_CANCEL'),
- result: false,
- isCancel: true
- },
- ]);
- if (!answer) {
- this.terminatеReason = I18N('BTN_CANCELED');
- return false;
- }
-
- let path = answer.split(',');
- if (path.length < 2) {
- path = answer.split('-');
- }
- if (path.length < 2) {
- this.terminatеReason = I18N('MUST_TWO_POINTS');
- return false;
- }
-
- for (let p in path) {
- path[p] = +path[p].trim()
- if (Number.isNaN(path[p])) {
- this.terminatеReason = I18N('MUST_ONLY_NUMBERS');
- return false;
- }
- }
-
- if (!this.checkPath(path)) {
- return false;
- }
- setSaveVal(keyPath, answer);
- return path;
- }
-
- checkPath(path) {
- for (let i = 0; i < path.length - 1; i++) {
- const currentPoint = path[i];
- const nextPoint = path[i + 1];
-
- const isValidPath = this.paths.some(p =>
- (p.from_id === currentPoint && p.to_id === nextPoint) ||
- (p.from_id === nextPoint && p.to_id === currentPoint)
- );
-
- if (!isValidPath) {
- this.terminatеReason = I18N('INCORRECT_WAY', {
- from: currentPoint,
- to: nextPoint,
- });
- return false;
- }
- }
-
- return true;
- }
-
- async checkAdventureInfo(data) {
- this.advInfo = data[0].result.response;
- if (!this.advInfo) {
- this.terminatеReason = I18N('NOT_ON_AN_ADVENTURE') ;
- return this.end();
- }
- const heroesTeam = data[1].result.response.adventure_hero;
- const favor = data[2]?.result.response.adventure_hero;
- const heroes = heroesTeam.slice(0, 5);
- const pet = heroesTeam[5];
- this.args = {
- pet,
- heroes,
- favor,
- path: [],
- broadcast: false
- }
- const advUserInfo = this.advInfo.users[userInfo.id];
- this.turnsLeft = advUserInfo.turnsLeft;
- this.currentNode = advUserInfo.currentNode;
- this.nodes = this.advInfo.nodes;
- this.paths = this.advInfo.paths;
- this.mapIdent = this.advInfo.mapIdent;
-
- this.path = await this.getPath();
- if (!this.path) {
- return this.end();
- }
-
- if (this.currentNode == 1 && this.path[0] != 1) {
- this.path.unshift(1);
- }
-
- return this.loop();
- }
-
- async loop() {
- const position = this.path.indexOf(+this.currentNode);
- if (!(~position)) {
- this.terminatеReason = I18N('YOU_IN_NOT_ON_THE_WAY');
- return this.end();
- }
- this.path = this.path.slice(position);
- if ((this.path.length - 1) > this.turnsLeft &&
- await popup.confirm(I18N('ATTEMPTS_NOT_ENOUGH'), [
- { msg: I18N('YES_CONTINUE'), result: false },
- { msg: I18N('BTN_NO'), result: true },
- ])) {
- this.terminatеReason = I18N('NOT_ENOUGH_AP');
- return this.end();
- }
- const toPath = [];
- for (const nodeId of this.path) {
- if (!this.turnsLeft) {
- this.terminatеReason = I18N('ATTEMPTS_ARE_OVER');
- return this.end();
- }
- toPath.push(nodeId);
- console.log(toPath);
- if (toPath.length > 1) {
- setProgress(toPath.join(' > ') + ` ${I18N('MOVES')}: ` + this.turnsLeft);
- }
- if (nodeId == this.currentNode) {
- continue;
- }
-
- const nodeInfo = this.getNodeInfo(nodeId);
- if (nodeInfo.type == 'TYPE_COMBAT') {
- if (nodeInfo.state == 'empty') {
- this.turnsLeft--;
- continue;
- }
-
- /**
- * Disable regular battle cancellation
- *
- * Отключаем штатную отменую боя
- */
- isCancalBattle = false;
- if (await this.battle(toPath)) {
- this.turnsLeft--;
- toPath.splice(0, toPath.indexOf(nodeId));
- nodeInfo.state = 'empty';
- isCancalBattle = true;
- continue;
- }
- isCancalBattle = true;
- return this.end()
- }
-
- if (nodeInfo.type == 'TYPE_PLAYERBUFF') {
- const buff = this.checkBuff(nodeInfo);
- if (buff == null) {
- continue;
- }
-
- if (await this.collectBuff(buff, toPath)) {
- this.turnsLeft--;
- toPath.splice(0, toPath.indexOf(nodeId));
- continue;
- }
- this.terminatеReason = I18N('BUFF_GET_ERROR');
- return this.end();
- }
- }
- this.terminatеReason = I18N('SUCCESS');
- return this.end();
- }
-
- /**
- * Carrying out a fight
- *
- * Проведение боя
- */
- async battle(path, preCalc = true) {
- const data = await this.startBattle(path);
- try {
- const battle = data.results[0].result.response.battle;
- const result = await Calc(battle);
- if (result.result.win) {
- const info = await this.endBattle(result);
- if (info.results[0].result.response?.error) {
- this.terminatеReason = I18N('BATTLE_END_ERROR');
- return false;
- }
- } else {
- await this.cancelBattle(result);
-
- if (preCalc && await this.preCalcBattle(battle)) {
- path = path.slice(-2);
- for (let i = 1; i <= getInput('countAutoBattle'); i++) {
- setProgress(`${I18N('AUTOBOT')}: ${i}/${getInput('countAutoBattle')}`);
- const result = await this.battle(path, false);
- if (result) {
- setProgress(I18N('VICTORY'));
- return true;
- }
- }
- this.terminatеReason = I18N('FAILED_TO_WIN_AUTO');
- return false;
- }
- return false;
- }
- } catch (error) {
- console.error(error);
- if (await popup.confirm(I18N('ERROR_OF_THE_BATTLE_COPY'), [
- { msg: I18N('BTN_NO'), result: false },
- { msg: I18N('BTN_YES'), result: true },
- ])) {
- this.errorHandling(error, data);
- }
- this.terminatеReason = I18N('ERROR_DURING_THE_BATTLE');
- return false;
- }
- return true;
- }
-
- /**
- * Recalculate battles
- *
- * Прерасчтет битвы
- */
- async preCalcBattle(battle) {
- const countTestBattle = getInput('countTestBattle');
- for (let i = 0; i < countTestBattle; i++) {
- battle.seed = Math.floor(Date.now() / 1000) + random(0, 1e3);
- const result = await Calc(battle);
- if (result.result.win) {
- console.log(i, countTestBattle);
- return true;
- }
- }
- this.terminatеReason = I18N('NO_CHANCE_WIN') + countTestBattle;
- return false;
- }
-
- /**
- * Starts a fight
- *
- * Начинает бой
- */
- startBattle(path) {
- this.args.path = path;
- this.callStartBattle.name = this.actions[this.type].startBattle;
- this.callStartBattle.args = this.args
- const calls = [this.callStartBattle];
- return Send(JSON.stringify({ calls }));
- }
-
- cancelBattle(battle) {
- const fixBattle = function (heroes) {
- for (const ids in heroes) {
- const hero = heroes[ids];
- hero.energy = random(1, 999);
- if (hero.hp > 0) {
- hero.hp = random(1, hero.hp);
- }
- }
- }
- fixBattle(battle.progress[0].attackers.heroes);
- fixBattle(battle.progress[0].defenders.heroes);
- return this.endBattle(battle);
- }
-
- /**
- * Ends the fight
- *
- * Заканчивает бой
- */
- endBattle(battle) {
- this.callEndBattle.name = this.actions[this.type].endBattle;
- this.callEndBattle.args.result = battle.result
- this.callEndBattle.args.progress = battle.progress
- const calls = [this.callEndBattle];
- return Send(JSON.stringify({ calls }));
- }
-
- /**
- * Checks if you can get a buff
- *
- * Проверяет можно ли получить баф
- */
- checkBuff(nodeInfo) {
- let id = null;
- let value = 0;
- for (const buffId in nodeInfo.buffs) {
- const buff = nodeInfo.buffs[buffId];
- if (buff.owner == null && buff.value > value) {
- id = buffId;
- value = buff.value;
- }
- }
- nodeInfo.buffs[id].owner = 'Я';
- return id;
- }
-
- /**
- * Collects a buff
- *
- * Собирает баф
- */
- async collectBuff(buff, path) {
- this.callCollectBuff.name = this.actions[this.type].collectBuff;
- this.callCollectBuff.args = { buff, path };
- const calls = [this.callCollectBuff];
- return Send(JSON.stringify({ calls }));
- }
-
- getNodeInfo(nodeId) {
- return this.nodes.find(node => node.id == nodeId);
- }
-
- errorHandling(error, data) {
- //console.error(error);
- let errorInfo = error.toString() + '\n';
- try {
- const errorStack = error.stack.split('\n');
- const endStack = errorStack.map(e => e.split('@')[0]).indexOf("testAdventure");
- errorInfo += errorStack.slice(0, endStack).join('\n');
- } catch (e) {
- errorInfo += error.stack;
- }
- if (data) {
- errorInfo += '\nData: ' + JSON.stringify(data);
- }
- copyText(errorInfo);
- }
-
- end() {
- isCancalBattle = true;
- setProgress(this.terminatеReason, true);
- console.log(this.terminatеReason);
- this.resolve();
- }
-}
-
-/**
- * Passage of brawls
- *
- * Прохождение потасовок
- */
-function testBrawls(isAuto) {
- return new Promise((resolve, reject) => {
- const brawls = new executeBrawls(resolve, reject);
- brawls.start(brawlsPack, isAuto);
- });
-}
-/**
- * Passage of brawls
- *
- * Прохождение потасовок
- */
-class executeBrawls {
- callBrawlQuestGetInfo = {
- name: "brawl_questGetInfo",
- args: {},
- ident: "brawl_questGetInfo"
- }
- callBrawlFindEnemies = {
- name: "brawl_findEnemies",
- args: {},
- ident: "brawl_findEnemies"
- }
- callBrawlQuestFarm = {
- name: "brawl_questFarm",
- args: {},
- ident: "brawl_questFarm"
- }
- callUserGetInfo = {
- name: "userGetInfo",
- args: {},
- ident: "userGetInfo"
- }
- callTeamGetMaxUpgrade = {
- name: "teamGetMaxUpgrade",
- args: {},
- ident: "teamGetMaxUpgrade"
- }
- callBrawlGetInfo = {
- name: "brawl_getInfo",
- args: {},
- ident: "brawl_getInfo"
- }
-
- stats = {
- win: 0,
- loss: 0,
- count: 0,
- }
-
- stage = {
- '3': 1,
- '7': 2,
- '12': 3,
- }
-
- attempts = 0;
-
- constructor(resolve, reject) {
- this.resolve = resolve;
- this.reject = reject;
-
- const allHeroIds = Object.keys(lib.getData('hero'));
- this.callTeamGetMaxUpgrade.args.units = {
- hero: allHeroIds.filter((id) => +id < 1000),
- titan: allHeroIds.filter((id) => +id >= 4000 && +id < 4100),
- pet: allHeroIds.filter((id) => +id >= 6000 && +id < 6100),
- };
- }
-
- async start(args, isAuto) {
- this.isAuto = isAuto;
- this.args = args;
- isCancalBattle = false;
- this.brawlInfo = await this.getBrawlInfo();
- this.attempts = this.brawlInfo.attempts;
-
- if (!this.attempts && !this.info.boughtEndlessLivesToday) {
- this.end(I18N('DONT_HAVE_LIVES'));
- return;
- }
-
- while (1) {
- if (!isBrawlsAutoStart) {
- this.end(I18N('BTN_CANCELED'));
- return;
- }
-
- const maxStage = this.brawlInfo.questInfo.stage;
- const stage = this.stage[maxStage];
- const progress = this.brawlInfo.questInfo.progress;
-
- setProgress(
- `${I18N('STAGE')} ${stage}: ${progress}/${maxStage} ${I18N('FIGHTS')}: ${this.stats.count} ${I18N('WINS')}: ${
- this.stats.win
- } ${I18N('LOSSES')}: ${this.stats.loss} ${I18N('LIVES')}: ${this.attempts} ${I18N('STOP')}`,
- false,
- function () {
- isBrawlsAutoStart = false;
- }
- );
-
- if (this.brawlInfo.questInfo.canFarm) {
- const result = await this.questFarm();
- console.log(result);
- }
-
- if (!this.continueAttack && this.brawlInfo.questInfo.stage == 12 && this.brawlInfo.questInfo.progress == 12) {
- if (
- await popup.confirm(I18N('BRAWL_DAILY_TASK_COMPLETED'), [
- { msg: I18N('BTN_NO'), result: true },
- { msg: I18N('BTN_YES'), result: false },
- ])
- ) {
- this.end(I18N('SUCCESS'));
- return;
- } else {
- this.continueAttack = true;
- }
- }
-
- if (!this.attempts && !this.info.boughtEndlessLivesToday) {
- this.end(I18N('DONT_HAVE_LIVES'));
- return;
- }
-
- const enemie = Object.values(this.brawlInfo.findEnemies).shift();
-
- // Автоматический подбор пачки
- if (this.isAuto) {
- if (this.mandatoryId <= 4000 && this.mandatoryId != 13) {
- this.end(I18N('BRAWL_AUTO_PACK_NOT_CUR_HERO'));
- return;
- }
- if (this.mandatoryId >= 4000 && this.mandatoryId < 4100) {
- this.args = await this.updateTitanPack(enemie.heroes);
- } else if (this.mandatoryId < 4000 && this.mandatoryId == 13) {
- this.args = await this.updateHeroesPack(enemie.heroes);
- }
- }
-
- const result = await this.battle(enemie.userId);
- this.brawlInfo = {
- questInfo: result[1].result.response,
- findEnemies: result[2].result.response,
- };
- }
- }
-
- async updateTitanPack(enemieHeroes) {
- const packs = [
- [4033, 4040, 4041, 4042, 4043],
- [4032, 4040, 4041, 4042, 4043],
- [4031, 4040, 4041, 4042, 4043],
- [4030, 4040, 4041, 4042, 4043],
- [4032, 4033, 4040, 4042, 4043],
- [4030, 4033, 4041, 4042, 4043],
- [4031, 4033, 4040, 4042, 4043],
- [4032, 4033, 4040, 4041, 4043],
- [4023, 4040, 4041, 4042, 4043],
- [4030, 4033, 4040, 4042, 4043],
- [4031, 4033, 4040, 4041, 4043],
- [4022, 4040, 4041, 4042, 4043],
- [4030, 4033, 4040, 4041, 4043],
- [4021, 4040, 4041, 4042, 4043],
- [4020, 4040, 4041, 4042, 4043],
- [4023, 4033, 4040, 4042, 4043],
- [4030, 4032, 4033, 4042, 4043],
- [4023, 4033, 4040, 4041, 4043],
- [4031, 4032, 4033, 4040, 4043],
- [4030, 4032, 4033, 4041, 4043],
- [4030, 4031, 4033, 4042, 4043],
- [4013, 4040, 4041, 4042, 4043],
- [4030, 4032, 4033, 4040, 4043],
- [4030, 4031, 4033, 4041, 4043],
- [4012, 4040, 4041, 4042, 4043],
- [4030, 4031, 4033, 4040, 4043],
- [4011, 4040, 4041, 4042, 4043],
- [4010, 4040, 4041, 4042, 4043],
- [4023, 4032, 4033, 4042, 4043],
- [4022, 4032, 4033, 4042, 4043],
- [4023, 4032, 4033, 4041, 4043],
- [4021, 4032, 4033, 4042, 4043],
- [4022, 4032, 4033, 4041, 4043],
- [4023, 4030, 4033, 4042, 4043],
- [4023, 4032, 4033, 4040, 4043],
- [4013, 4033, 4040, 4042, 4043],
- [4020, 4032, 4033, 4042, 4043],
- [4021, 4032, 4033, 4041, 4043],
- [4022, 4030, 4033, 4042, 4043],
- [4022, 4032, 4033, 4040, 4043],
- [4023, 4030, 4033, 4041, 4043],
- [4023, 4031, 4033, 4040, 4043],
- [4013, 4033, 4040, 4041, 4043],
- [4020, 4031, 4033, 4042, 4043],
- [4020, 4032, 4033, 4041, 4043],
- [4021, 4030, 4033, 4042, 4043],
- [4021, 4032, 4033, 4040, 4043],
- [4022, 4030, 4033, 4041, 4043],
- [4022, 4031, 4033, 4040, 4043],
- [4023, 4030, 4033, 4040, 4043],
- [4030, 4031, 4032, 4033, 4043],
- [4003, 4040, 4041, 4042, 4043],
- [4020, 4030, 4033, 4042, 4043],
- [4020, 4031, 4033, 4041, 4043],
- [4020, 4032, 4033, 4040, 4043],
- [4021, 4030, 4033, 4041, 4043],
- [4021, 4031, 4033, 4040, 4043],
- [4022, 4030, 4033, 4040, 4043],
- [4030, 4031, 4032, 4033, 4042],
- [4002, 4040, 4041, 4042, 4043],
- [4020, 4030, 4033, 4041, 4043],
- [4020, 4031, 4033, 4040, 4043],
- [4021, 4030, 4033, 4040, 4043],
- [4030, 4031, 4032, 4033, 4041],
- [4001, 4040, 4041, 4042, 4043],
- [4030, 4031, 4032, 4033, 4040],
- [4000, 4040, 4041, 4042, 4043],
- [4013, 4032, 4033, 4042, 4043],
- [4012, 4032, 4033, 4042, 4043],
- [4013, 4032, 4033, 4041, 4043],
- [4023, 4031, 4032, 4033, 4043],
- [4011, 4032, 4033, 4042, 4043],
- [4012, 4032, 4033, 4041, 4043],
- [4013, 4030, 4033, 4042, 4043],
- [4013, 4032, 4033, 4040, 4043],
- [4023, 4030, 4032, 4033, 4043],
- [4003, 4033, 4040, 4042, 4043],
- [4013, 4023, 4040, 4042, 4043],
- [4010, 4032, 4033, 4042, 4043],
- [4011, 4032, 4033, 4041, 4043],
- [4012, 4030, 4033, 4042, 4043],
- [4012, 4032, 4033, 4040, 4043],
- [4013, 4030, 4033, 4041, 4043],
- [4013, 4031, 4033, 4040, 4043],
- [4023, 4030, 4031, 4033, 4043],
- [4003, 4033, 4040, 4041, 4043],
- [4013, 4023, 4040, 4041, 4043],
- [4010, 4031, 4033, 4042, 4043],
- [4010, 4032, 4033, 4041, 4043],
- [4011, 4030, 4033, 4042, 4043],
- [4011, 4032, 4033, 4040, 4043],
- [4012, 4030, 4033, 4041, 4043],
- [4012, 4031, 4033, 4040, 4043],
- [4013, 4030, 4033, 4040, 4043],
- [4010, 4030, 4033, 4042, 4043],
- [4010, 4031, 4033, 4041, 4043],
- [4010, 4032, 4033, 4040, 4043],
- [4011, 4030, 4033, 4041, 4043],
- [4011, 4031, 4033, 4040, 4043],
- [4012, 4030, 4033, 4040, 4043],
- [4010, 4030, 4033, 4041, 4043],
- [4010, 4031, 4033, 4040, 4043],
- [4011, 4030, 4033, 4040, 4043],
- [4003, 4032, 4033, 4042, 4043],
- [4002, 4032, 4033, 4042, 4043],
- [4003, 4032, 4033, 4041, 4043],
- [4013, 4031, 4032, 4033, 4043],
- [4001, 4032, 4033, 4042, 4043],
- [4002, 4032, 4033, 4041, 4043],
- [4003, 4030, 4033, 4042, 4043],
- [4003, 4032, 4033, 4040, 4043],
- [4013, 4030, 4032, 4033, 4043],
- [4003, 4023, 4040, 4042, 4043],
- [4000, 4032, 4033, 4042, 4043],
- [4001, 4032, 4033, 4041, 4043],
- [4002, 4030, 4033, 4042, 4043],
- [4002, 4032, 4033, 4040, 4043],
- [4003, 4030, 4033, 4041, 4043],
- [4003, 4031, 4033, 4040, 4043],
- [4020, 4022, 4023, 4042, 4043],
- [4013, 4030, 4031, 4033, 4043],
- [4003, 4023, 4040, 4041, 4043],
- [4000, 4031, 4033, 4042, 4043],
- [4000, 4032, 4033, 4041, 4043],
- [4001, 4030, 4033, 4042, 4043],
- [4001, 4032, 4033, 4040, 4043],
- [4002, 4030, 4033, 4041, 4043],
- [4002, 4031, 4033, 4040, 4043],
- [4003, 4030, 4033, 4040, 4043],
- [4021, 4022, 4023, 4040, 4043],
- [4020, 4022, 4023, 4041, 4043],
- [4020, 4021, 4023, 4042, 4043],
- [4023, 4030, 4031, 4032, 4033],
- [4000, 4030, 4033, 4042, 4043],
- [4000, 4031, 4033, 4041, 4043],
- [4000, 4032, 4033, 4040, 4043],
- [4001, 4030, 4033, 4041, 4043],
- [4001, 4031, 4033, 4040, 4043],
- [4002, 4030, 4033, 4040, 4043],
- [4020, 4022, 4023, 4040, 4043],
- [4020, 4021, 4023, 4041, 4043],
- [4022, 4030, 4031, 4032, 4033],
- [4000, 4030, 4033, 4041, 4043],
- [4000, 4031, 4033, 4040, 4043],
- [4001, 4030, 4033, 4040, 4043],
- [4020, 4021, 4023, 4040, 4043],
- [4021, 4030, 4031, 4032, 4033],
- [4020, 4030, 4031, 4032, 4033],
- [4003, 4031, 4032, 4033, 4043],
- [4020, 4022, 4023, 4033, 4043],
- [4003, 4030, 4032, 4033, 4043],
- [4003, 4013, 4040, 4042, 4043],
- [4020, 4021, 4023, 4033, 4043],
- [4003, 4030, 4031, 4033, 4043],
- [4003, 4013, 4040, 4041, 4043],
- [4013, 4030, 4031, 4032, 4033],
- [4012, 4030, 4031, 4032, 4033],
- [4011, 4030, 4031, 4032, 4033],
- [4010, 4030, 4031, 4032, 4033],
- [4013, 4023, 4031, 4032, 4033],
- [4013, 4023, 4030, 4032, 4033],
- [4020, 4022, 4023, 4032, 4033],
- [4013, 4023, 4030, 4031, 4033],
- [4021, 4022, 4023, 4030, 4033],
- [4020, 4022, 4023, 4031, 4033],
- [4020, 4021, 4023, 4032, 4033],
- [4020, 4021, 4022, 4023, 4043],
- [4003, 4030, 4031, 4032, 4033],
- [4020, 4022, 4023, 4030, 4033],
- [4020, 4021, 4023, 4031, 4033],
- [4020, 4021, 4022, 4023, 4042],
- [4002, 4030, 4031, 4032, 4033],
- [4020, 4021, 4023, 4030, 4033],
- [4020, 4021, 4022, 4023, 4041],
- [4001, 4030, 4031, 4032, 4033],
- [4020, 4021, 4022, 4023, 4040],
- [4000, 4030, 4031, 4032, 4033],
- [4003, 4023, 4031, 4032, 4033],
- [4013, 4020, 4022, 4023, 4043],
- [4003, 4023, 4030, 4032, 4033],
- [4010, 4012, 4013, 4042, 4043],
- [4013, 4020, 4021, 4023, 4043],
- [4003, 4023, 4030, 4031, 4033],
- [4011, 4012, 4013, 4040, 4043],
- [4010, 4012, 4013, 4041, 4043],
- [4010, 4011, 4013, 4042, 4043],
- [4020, 4021, 4022, 4023, 4033],
- [4010, 4012, 4013, 4040, 4043],
- [4010, 4011, 4013, 4041, 4043],
- [4020, 4021, 4022, 4023, 4032],
- [4010, 4011, 4013, 4040, 4043],
- [4020, 4021, 4022, 4023, 4031],
- [4020, 4021, 4022, 4023, 4030],
- [4003, 4013, 4031, 4032, 4033],
- [4010, 4012, 4013, 4033, 4043],
- [4003, 4020, 4022, 4023, 4043],
- [4013, 4020, 4022, 4023, 4033],
- [4003, 4013, 4030, 4032, 4033],
- [4010, 4011, 4013, 4033, 4043],
- [4003, 4020, 4021, 4023, 4043],
- [4013, 4020, 4021, 4023, 4033],
- [4003, 4013, 4030, 4031, 4033],
- [4010, 4012, 4013, 4023, 4043],
- [4003, 4020, 4022, 4023, 4033],
- [4010, 4012, 4013, 4032, 4033],
- [4010, 4011, 4013, 4023, 4043],
- [4003, 4020, 4021, 4023, 4033],
- [4011, 4012, 4013, 4030, 4033],
- [4010, 4012, 4013, 4031, 4033],
- [4010, 4011, 4013, 4032, 4033],
- [4013, 4020, 4021, 4022, 4023],
- [4010, 4012, 4013, 4030, 4033],
- [4010, 4011, 4013, 4031, 4033],
- [4012, 4020, 4021, 4022, 4023],
- [4010, 4011, 4013, 4030, 4033],
- [4011, 4020, 4021, 4022, 4023],
- [4010, 4020, 4021, 4022, 4023],
- [4010, 4012, 4013, 4023, 4033],
- [4000, 4002, 4003, 4042, 4043],
- [4010, 4011, 4013, 4023, 4033],
- [4001, 4002, 4003, 4040, 4043],
- [4000, 4002, 4003, 4041, 4043],
- [4000, 4001, 4003, 4042, 4043],
- [4010, 4011, 4012, 4013, 4043],
- [4003, 4020, 4021, 4022, 4023],
- [4000, 4002, 4003, 4040, 4043],
- [4000, 4001, 4003, 4041, 4043],
- [4010, 4011, 4012, 4013, 4042],
- [4002, 4020, 4021, 4022, 4023],
- [4000, 4001, 4003, 4040, 4043],
- [4010, 4011, 4012, 4013, 4041],
- [4001, 4020, 4021, 4022, 4023],
- [4010, 4011, 4012, 4013, 4040],
- [4000, 4020, 4021, 4022, 4023],
- [4001, 4002, 4003, 4033, 4043],
- [4000, 4002, 4003, 4033, 4043],
- [4003, 4010, 4012, 4013, 4043],
- [4003, 4013, 4020, 4022, 4023],
- [4000, 4001, 4003, 4033, 4043],
- [4003, 4010, 4011, 4013, 4043],
- [4003, 4013, 4020, 4021, 4023],
- [4010, 4011, 4012, 4013, 4033],
- [4010, 4011, 4012, 4013, 4032],
- [4010, 4011, 4012, 4013, 4031],
- [4010, 4011, 4012, 4013, 4030],
- [4001, 4002, 4003, 4023, 4043],
- [4000, 4002, 4003, 4023, 4043],
- [4003, 4010, 4012, 4013, 4033],
- [4000, 4002, 4003, 4032, 4033],
- [4000, 4001, 4003, 4023, 4043],
- [4003, 4010, 4011, 4013, 4033],
- [4001, 4002, 4003, 4030, 4033],
- [4000, 4002, 4003, 4031, 4033],
- [4000, 4001, 4003, 4032, 4033],
- [4010, 4011, 4012, 4013, 4023],
- [4000, 4002, 4003, 4030, 4033],
- [4000, 4001, 4003, 4031, 4033],
- [4010, 4011, 4012, 4013, 4022],
- [4000, 4001, 4003, 4030, 4033],
- [4010, 4011, 4012, 4013, 4021],
- [4010, 4011, 4012, 4013, 4020],
- [4001, 4002, 4003, 4013, 4043],
- [4001, 4002, 4003, 4023, 4033],
- [4000, 4002, 4003, 4013, 4043],
- [4000, 4002, 4003, 4023, 4033],
- [4003, 4010, 4012, 4013, 4023],
- [4000, 4001, 4003, 4013, 4043],
- [4000, 4001, 4003, 4023, 4033],
- [4003, 4010, 4011, 4013, 4023],
- [4001, 4002, 4003, 4013, 4033],
- [4000, 4002, 4003, 4013, 4033],
- [4000, 4001, 4003, 4013, 4033],
- [4000, 4001, 4002, 4003, 4043],
- [4003, 4010, 4011, 4012, 4013],
- [4000, 4001, 4002, 4003, 4042],
- [4002, 4010, 4011, 4012, 4013],
- [4000, 4001, 4002, 4003, 4041],
- [4001, 4010, 4011, 4012, 4013],
- [4000, 4001, 4002, 4003, 4040],
- [4000, 4010, 4011, 4012, 4013],
- [4001, 4002, 4003, 4013, 4023],
- [4000, 4002, 4003, 4013, 4023],
- [4000, 4001, 4003, 4013, 4023],
- [4000, 4001, 4002, 4003, 4033],
- [4000, 4001, 4002, 4003, 4032],
- [4000, 4001, 4002, 4003, 4031],
- [4000, 4001, 4002, 4003, 4030],
- [4000, 4001, 4002, 4003, 4023],
- [4000, 4001, 4002, 4003, 4022],
- [4000, 4001, 4002, 4003, 4021],
- [4000, 4001, 4002, 4003, 4020],
- [4000, 4001, 4002, 4003, 4013],
- [4000, 4001, 4002, 4003, 4012],
- [4000, 4001, 4002, 4003, 4011],
- [4000, 4001, 4002, 4003, 4010],
- ].filter((p) => p.includes(this.mandatoryId));
-
- const bestPack = {
- pack: packs[0],
- winRate: 0,
- countBattle: 0,
- id: 0,
- };
-
- for (const id in packs) {
- const pack = packs[id];
- const attackers = this.maxUpgrade.filter((e) => pack.includes(e.id)).reduce((obj, e) => ({ ...obj, [e.id]: e }), {});
- const battle = {
- attackers,
- defenders: [enemieHeroes],
- type: 'brawl_titan',
- };
- const isRandom = this.isRandomBattle(battle);
- const stat = {
- count: 0,
- win: 0,
- winRate: 0,
- };
- for (let i = 1; i <= 20; i++) {
- battle.seed = Math.floor(Date.now() / 1000) + Math.random() * 1000;
- const result = await Calc(battle);
- stat.win += result.result.win;
- stat.count += 1;
- stat.winRate = stat.win / stat.count;
- if (!isRandom || (i >= 2 && stat.winRate < 0.65) || (i >= 10 && stat.winRate == 1)) {
- break;
- }
- }
-
- if (!isRandom && stat.win) {
- return {
- favor: {},
- heroes: pack,
- };
- }
- if (stat.winRate > 0.85) {
- return {
- favor: {},
- heroes: pack,
- };
- }
- if (stat.winRate > bestPack.winRate) {
- bestPack.countBattle = stat.count;
- bestPack.winRate = stat.winRate;
- bestPack.pack = pack;
- bestPack.id = id;
- }
- }
-
- //console.log(bestPack.id, bestPack.pack, bestPack.winRate, bestPack.countBattle);
- return {
- favor: {},
- heroes: bestPack.pack,
- };
- }
-
- isRandomPack(pack) {
- const ids = Object.keys(pack);
- return ids.includes('4023') || ids.includes('4021');
- }
-
- isRandomBattle(battle) {
- return this.isRandomPack(battle.attackers) || this.isRandomPack(battle.defenders[0]);
- }
-
- async updateHeroesPack(enemieHeroes) {
- const packs = [{id:1,args:{userId:-830021,heroes:[63,13,9,48,1],pet:6006,favor:{1:6004,9:6005,13:6002,48:6e3,63:6009}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6022:130,8268:1,8269:1},power:198058,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:419649,intelligence:3644,physicalAttack:11481.6,strength:17049,armor:12720,dodge:17232.28,magicPenetration:22780,magicPower:55816,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6004,favorPower:11064},9:{id:9,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{335:130,336:130,337:130,338:130,6027:130,8270:1,8271:1},power:195886,star:6,runes:[43750,43750,43750,43750,43750],skins:{9:60,41:60,163:60,189:60,311:60,338:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6005,type:"hero",perks:[7,2,20],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3068,hp:227134,intelligence:19003,physicalAttack:7020.32,strength:3068,armor:19995,dodge:14644,magicPower:64780.6,magicResist:31597,modifiedSkillTier:5,skin:0,favorPetId:6005,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},48:{id:48,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{240:130,241:130,242:130,243:130,6002:130},power:190584,star:6,runes:[43750,43750,43750,43750,43750],skins:{103:60,165:60,217:60,296:60,326:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6e3,type:"hero",perks:[5,2],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:17308,hp:397737,intelligence:2888,physicalAttack:40298.32,physicalCritChance:12280,strength:3169,armor:12185,armorPenetration:20137.6,magicResist:24816,skin:0,favorPetId:6e3,favorPower:11064},63:{id:63,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{442:130,443:130,444:130,445:130,6041:130,8272:1,8273:1},power:193520,star:6,runes:[43750,43750,43750,43750,43750],skins:{341:60,350:60,351:60,352:1},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6009,type:"hero",perks:[6,1,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:17931,hp:488832,intelligence:2737,physicalAttack:54213.6,strength:2877,armor:800,armorPenetration:32477.6,magicResist:8526,physicalCritChance:9545,modifiedSkillTier:3,skin:0,favorPetId:6009,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:2,args:{userId:-830049,heroes:[46,13,52,49,4],pet:6006,favor:{4:6001,13:6002,46:6006,49:6004,52:6003}},attackers:{4:{id:4,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{255:130,256:130,257:130,258:130,6007:130},power:189782,star:6,runes:[43750,43750,43750,43750,43750],skins:{4:60,35:60,92:60,161:60,236:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6001,type:"hero",perks:[4,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:3065,hp:482631,intelligence:3402,physicalAttack:2800,strength:17488,armor:56262.6,magicPower:51021,magicResist:36971,skin:0,favorPetId:6001,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},46:{id:46,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{230:130,231:130,232:130,233:130,6032:130},power:189653,star:6,runes:[43750,43750,43750,43750,43750],skins:{101:60,159:60,178:60,262:60,315:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[9,5,1,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2122,hp:637517,intelligence:16208,physicalAttack:50,strength:5151,armor:38507.6,magicPower:74495.6,magicResist:22237,skin:0,favorPetId:6006,favorPower:11064},49:{id:49,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{245:130,246:130,247:130,248:130,6022:130},power:193163,star:6,runes:[43750,43750,43750,43750,43750],skins:{104:60,191:60,252:60,305:60,329:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[10,1,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:17935,hp:250405,intelligence:2790,physicalAttack:40413.6,strength:2987,armor:11655,dodge:14844.28,magicResist:3175,physicalCritChance:14135,skin:0,favorPetId:6004,favorPower:11064},52:{id:52,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{310:130,311:130,312:130,313:130,6017:130},power:185075,star:6,runes:[43750,43750,43750,43750,43750],skins:{188:60,213:60,248:60,297:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6003,type:"hero",perks:[5,8,2,13,15,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:18270,hp:226207,intelligence:2620,physicalAttack:44206,strength:3260,armor:13150,armorPenetration:40301,magicPower:9957.6,magicResist:33892.6,skin:0,favorPetId:6003,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:3,args:{userId:8263225,heroes:[29,63,13,48,1],pet:6006,favor:{1:6004,13:6002,29:6006,48:6e3,63:6003}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6022:130,8268:1,8269:1},power:198058,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:419649,intelligence:3644,physicalAttack:11481.6,strength:17049,armor:12720,dodge:17232.28,magicPenetration:22780,magicPower:55816,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6004,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},29:{id:29,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{145:130,146:130,147:130,148:130,6032:130},power:189790,star:6,runes:[43750,43750,43750,43750,43750],skins:{29:60,72:60,88:60,147:60,242:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[9,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2885,hp:491431,intelligence:18331,physicalAttack:106,strength:3020,armor:37716.6,magicPower:76792.6,magicResist:31377,skin:0,favorPetId:6006,favorPower:11064},48:{id:48,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{240:130,241:130,242:130,243:130,6002:130},power:190584,star:6,runes:[43750,43750,43750,43750,43750],skins:{103:60,165:60,217:60,296:60,326:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6e3,type:"hero",perks:[5,2],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:17308,hp:397737,intelligence:2888,physicalAttack:40298.32,physicalCritChance:12280,strength:3169,armor:12185,armorPenetration:20137.6,magicResist:24816,skin:0,favorPetId:6e3,favorPower:11064},63:{id:63,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{442:130,443:130,444:130,445:130,6017:130,8272:1,8273:1},power:191031,star:6,runes:[43750,43750,43750,43750,43750],skins:{341:60,350:60,351:60,352:1},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6003,type:"hero",perks:[6,1,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:17931,hp:488832,intelligence:2737,physicalAttack:44256,strength:2877,armor:800,armorPenetration:22520,magicPower:9957.6,magicResist:18483.6,physicalCritChance:9545,modifiedSkillTier:3,skin:0,favorPetId:6003,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:4,args:{userId:8263247,heroes:[55,13,40,51,1],pet:6006,favor:{1:6007,13:6002,40:6004,51:6006,55:6001}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6035:130,8268:1,8269:1},power:195170,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6007,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:419649,intelligence:3644,physicalAttack:1524,strength:17049,armor:22677.6,dodge:14245,magicPenetration:22780,magicPower:65773.6,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6007,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},40:{id:40,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{200:130,201:130,202:130,203:130,6022:130,8244:1,8245:1},power:192541,star:6,runes:[43750,43750,43750,43750,43750],skins:{53:60,89:60,129:60,168:60,314:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[5,9,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:17540,hp:343191,intelligence:2805,physicalAttack:48430.6,strength:2976,armor:24410,dodge:15732.28,magicResist:17633,modifiedSkillTier:3,skin:0,favorPetId:6004,favorPower:11064},51:{id:51,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{305:130,306:130,307:130,308:130,6032:130},power:190005,star:6,runes:[43750,43750,43750,43750,43750],skins:{181:60,219:60,260:60,290:60,334:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[5,9,1,12],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2526,hp:438205,intelligence:18851,physicalAttack:50,strength:2921,armor:39442.6,magicPower:88978.6,magicResist:22960,skin:0,favorPetId:6006,favorPower:11064},55:{id:55,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{325:130,326:130,327:130,328:130,6007:130},power:190529,star:6,runes:[43750,43750,43750,43750,43750],skins:{239:60,278:60,309:60,327:60,346:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6001,type:"hero",perks:[7,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2631,hp:499591,intelligence:19438,physicalAttack:50,strength:3286,armor:32892.6,armorPenetration:36870,magicPower:60704,magicResist:10010,skin:0,favorPetId:6001,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:5,args:{userId:8263303,heroes:[31,29,13,40,1],pet:6004,favor:{1:6001,13:6007,29:6002,31:6006,40:6004}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6007:130,8268:1,8269:1},power:195170,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6001,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:519225,intelligence:3644,physicalAttack:1524,strength:17049,armor:22677.6,dodge:14245,magicPenetration:22780,magicPower:55816,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6001,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6035:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6007,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:29017.6,magicPenetration:48181,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6007,favorPower:11064},29:{id:29,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{145:130,146:130,147:130,148:130,6012:130},power:189790,star:6,runes:[43750,43750,43750,43750,43750],skins:{29:60,72:60,88:60,147:60,242:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[9,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2885,hp:491431,intelligence:18331,physicalAttack:106,strength:3020,armor:27759,magicPenetration:9957.6,magicPower:76792.6,magicResist:31377,skin:0,favorPetId:6002,favorPower:11064},31:{id:31,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{155:130,156:130,157:130,158:130,6032:130},power:190305,star:6,runes:[43750,43750,43750,43750,43750],skins:{44:60,94:60,133:60,200:60,295:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[9,5,2,20],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2781,dodge:12620,hp:374484,intelligence:18945,physicalAttack:78,strength:2916,armor:28049.6,magicPower:67686.6,magicResist:15252,skin:0,favorPetId:6006,favorPower:11064},40:{id:40,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{200:130,201:130,202:130,203:130,6022:130,8244:1,8245:1},power:192541,star:6,runes:[43750,43750,43750,43750,43750],skins:{53:60,89:60,129:60,168:60,314:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[5,9,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:17540,hp:343191,intelligence:2805,physicalAttack:48430.6,strength:2976,armor:24410,dodge:15732.28,magicResist:17633,modifiedSkillTier:3,skin:0,favorPetId:6004,favorPower:11064},6004:{id:6004,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6020:130,6021:130},power:181943,type:"pet",perks:[5],name:null,armorPenetration:47911,intelligence:11064,strength:12360}}},{id:6,args:{userId:8263317,heroes:[62,13,9,56,61],pet:6003,favor:{9:6004,13:6002,56:6006,61:6001,62:6003}},attackers:{9:{id:9,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{335:130,336:130,337:130,338:130,6022:130,8270:1,8271:1},power:198525,star:6,runes:[43750,43750,43750,43750,43750],skins:{9:60,41:60,163:60,189:60,311:60,338:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[7,2,20],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3068,hp:227134,intelligence:19003,physicalAttack:10007.6,strength:3068,armor:19995,dodge:17631.28,magicPower:54823,magicResist:31597,modifiedSkillTier:5,skin:0,favorPetId:6004,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6012:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:19060,magicPenetration:58138.6,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6002,favorPower:11064},56:{id:56,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{376:130,377:130,378:130,379:130,6032:130},power:184420,star:6,runes:[43750,43750,43750,43750,43750],skins:{264:60,279:60,294:60,321:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[5,7,1,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2791,hp:235111,intelligence:18813,physicalAttack:50,strength:2656,armor:22982.6,magicPenetration:48159,magicPower:75598.6,magicResist:13990,skin:0,favorPetId:6006,favorPower:11064},61:{id:61,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{411:130,412:130,413:130,414:130,6007:130},power:184868,star:6,runes:[43750,43750,43750,43750,43750],skins:{302:60,306:60,323:60,340:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6001,type:"hero",perks:[4,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2545,hp:466176,intelligence:3320,physicalAttack:34305,strength:18309,armor:31077.6,magicResist:24101,physicalCritChance:9009,skin:0,favorPetId:6001,favorPower:11064},62:{id:62,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{437:130,438:130,439:130,440:130,6017:130},power:173991,star:6,runes:[43750,43750,43750,43750,43750],skins:{320:60,343:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6003,type:"hero",perks:[8,7,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2530,hp:276010,intelligence:19245,physicalAttack:50,strength:3543,armor:12890,magicPenetration:23658,magicPower:80966.6,magicResist:12447.6,skin:0,favorPetId:6003,favorPower:11064},6003:{id:6003,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6015:130,6016:130},power:181943,type:"pet",perks:[8],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}},{id:7,args:{userId:8263335,heroes:[32,29,13,43,1],pet:6006,favor:{1:6004,13:6008,29:6006,32:6002,43:6007}},attackers:{1:{id:1,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{2:130,3:130,4:130,5:130,6022:130,8268:1,8269:1},power:198058,star:6,runes:[43750,43750,43750,43750,43750],skins:{1:60,54:60,95:60,154:60,250:60,325:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6004,type:"hero",perks:[4,1],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:3093,hp:419649,intelligence:3644,physicalAttack:11481.6,strength:17049,armor:12720,dodge:17232.28,magicPenetration:22780,magicPower:55816,magicResist:1580,modifiedSkillTier:5,skin:0,favorPetId:6004,favorPower:11064},13:{id:"13",xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{452:130,453:130,454:130,455:130,6038:130,8274:1,8275:1},power:194833,star:6,runes:[43750,43750,43750,43750,43750],skins:{13:60,38:60,148:60,199:60,240:60,335:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6008,type:"hero",perks:[7,2,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,9,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,9,10]},agility:2885,hp:344763,intelligence:17625,physicalAttack:50,strength:3020,armor:29017.6,magicPenetration:48181,magicPower:70100.6,magicResist:27227,modifiedSkillTier:4,skin:0,favorPetId:6008,favorPower:11064},29:{id:29,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{145:130,146:130,147:130,148:130,6032:130},power:189790,star:6,runes:[43750,43750,43750,43750,43750],skins:{29:60,72:60,88:60,147:60,242:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6006,type:"hero",perks:[9,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2885,hp:491431,intelligence:18331,physicalAttack:106,strength:3020,armor:37716.6,magicPower:76792.6,magicResist:31377,skin:0,favorPetId:6006,favorPower:11064},32:{id:32,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{160:130,161:130,162:130,163:130,6012:130},power:189956,star:6,runes:[43750,43750,43750,43750,43750],skins:{45:60,73:60,81:60,135:60,212:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6002,type:"hero",perks:[7,5,2,22],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2815,hp:551066,intelligence:18800,physicalAttack:50,strength:2810,armor:19040,magicPenetration:9957.6,magicPower:89495.6,magicResist:20805,skin:0,favorPetId:6002,favorPower:11064},43:{id:43,xp:3625195,level:130,color:18,slots:[0,0,0,0,0,0],skills:{215:130,216:130,217:130,218:130,6035:130},power:189593,star:6,runes:[43750,43750,43750,43750,43750],skins:{98:60,130:60,169:60,201:60,304:60},currentSkin:0,titanGiftLevel:30,titanCoinsSpent:null,artifacts:[{level:130,star:6},{level:130,star:6},{level:130,star:6}],scale:1,petId:6007,type:"hero",perks:[7,9,1,21],ascensions:{1:[0,1,2,3,4,5,6,7,8,9],2:[0,1,2,3,4,5,6,7,8,10],3:[0,1,2,3,4,5,6,7,8,9],4:[0,1,2,3,4,5,6,7,8,9],5:[0,1,2,3,4,5,6,7,8,10]},agility:2447,hp:265217,intelligence:18758,physicalAttack:50,strength:2842,armor:18637.6,magicPenetration:52439,magicPower:75465.6,magicResist:22695,skin:0,favorPetId:6007,favorPower:11064},6006:{id:6006,color:10,star:6,xp:450551,level:130,slots:[25,50,50,25,50,50],skills:{6030:130,6031:130},power:181943,type:"pet",perks:[5,9],name:null,intelligence:11064,magicPenetration:47911,strength:12360}}}];
-
- const bestPack = {
- pack: packs[0],
- countWin: 0,
- }
-
- for (const pack of packs) {
- const attackers = pack.attackers;
- const battle = {
- attackers,
- defenders: [enemieHeroes],
- type: 'brawl',
- };
-
- let countWinBattles = 0;
- let countTestBattle = 10;
- for (let i = 0; i < countTestBattle; i++) {
- battle.seed = Math.floor(Date.now() / 1000) + Math.random() * 1000;
- const result = await Calc(battle);
- if (result.result.win) {
- countWinBattles++;
- }
- if (countWinBattles > 7) {
- console.log(pack)
- return pack.args;
- }
- }
- if (countWinBattles > bestPack.countWin) {
- bestPack.countWin = countWinBattles;
- bestPack.pack = pack.args;
- }
- }
-
- console.log(bestPack);
- return bestPack.pack;
- }
-
- async questFarm() {
- const calls = [this.callBrawlQuestFarm];
- const result = await Send(JSON.stringify({ calls }));
- return result.results[0].result.response;
- }
-
- async getBrawlInfo() {
- const data = await Send(JSON.stringify({
- calls: [
- this.callUserGetInfo,
- this.callBrawlQuestGetInfo,
- this.callBrawlFindEnemies,
- this.callTeamGetMaxUpgrade,
- this.callBrawlGetInfo,
- ]
- }));
-
- let attempts = data.results[0].result.response.refillable.find(n => n.id == 48);
-
- const maxUpgrade = data.results[3].result.response;
- const maxHero = Object.values(maxUpgrade.hero);
- const maxTitan = Object.values(maxUpgrade.titan);
- const maxPet = Object.values(maxUpgrade.pet);
- this.maxUpgrade = [...maxHero, ...maxPet, ...maxTitan];
-
- this.info = data.results[4].result.response;
- this.mandatoryId = lib.data.brawl.promoHero[this.info.id].promoHero;
- return {
- attempts: attempts.amount,
- questInfo: data.results[1].result.response,
- findEnemies: data.results[2].result.response,
- }
- }
-
- /**
- * Carrying out a fight
- *
- * Проведение боя
- */
- async battle(userId) {
- this.stats.count++;
- const battle = await this.startBattle(userId, this.args);
- const result = await Calc(battle);
- console.log(result.result);
- if (result.result.win) {
- this.stats.win++;
- } else {
- this.stats.loss++;
- if (!this.info.boughtEndlessLivesToday) {
- this.attempts--;
- }
- }
- return await this.endBattle(result);
- // return await this.cancelBattle(result);
- }
-
- /**
- * Starts a fight
- *
- * Начинает бой
- */
- async startBattle(userId, args) {
- const call = {
- name: "brawl_startBattle",
- args,
- ident: "brawl_startBattle"
- }
- call.args.userId = userId;
- const calls = [call];
- const result = await Send(JSON.stringify({ calls }));
- return result.results[0].result.response;
- }
-
- cancelBattle(battle) {
- const fixBattle = function (heroes) {
- for (const ids in heroes) {
- const hero = heroes[ids];
- hero.energy = random(1, 999);
- if (hero.hp > 0) {
- hero.hp = random(1, hero.hp);
- }
- }
- }
- fixBattle(battle.progress[0].attackers.heroes);
- fixBattle(battle.progress[0].defenders.heroes);
- return this.endBattle(battle);
- }
-
- /**
- * Ends the fight
- *
- * Заканчивает бой
- */
- async endBattle(battle) {
- battle.progress[0].attackers.input = ['auto', 0, 0, 'auto', 0, 0];
- const calls = [{
- name: "brawl_endBattle",
- args: {
- result: battle.result,
- progress: battle.progress
- },
- ident: "brawl_endBattle"
- },
- this.callBrawlQuestGetInfo,
- this.callBrawlFindEnemies,
- ];
- const result = await Send(JSON.stringify({ calls }));
- return result.results;
- }
-
- end(endReason) {
- isCancalBattle = true;
- isBrawlsAutoStart = false;
- setProgress(endReason, true);
- console.log(endReason);
- this.resolve();
- }
-}
-
-})();
-
-/**
- * TODO:
- * Получение всех уровней при сборе всех наград (квест на титанит и на энку) +-
- * Добивание на арене титанов
- * Закрытие окошек по Esc +-
- * Починить работу скрипта на уровне команды ниже 10 +-
- * Написать номальную синхронизацию
- * Добавить дополнительные настройки автопокупки в "Тайном богатстве"
- */
diff --git a/LIB_DATA_DOCUMENTATION.md b/LIB_DATA_DOCUMENTATION.md
new file mode 100644
index 0000000..7d34677
--- /dev/null
+++ b/LIB_DATA_DOCUMENTATION.md
@@ -0,0 +1,713 @@
+# lib.data Documentation
+
+## Overview
+
+`lib.data` is a comprehensive game data library loaded from the Hero Wars game server. It contains static configuration data, metadata, and definitions for all game entities, mechanics, and systems. The Library class loads this data from a JSON file and provides access through `lib.data` and `lib.getData(id)` methods.
+
+## Access Methods
+
+### Direct Access
+```javascript
+// Access data directly
+const heroData = lib.data.hero;
+const missionData = lib.data.mission;
+```
+
+### Using getData Method
+```javascript
+// Access data using getData method (recommended)
+const heroData = lib.getData('hero');
+const missionData = lib.getData('mission');
+```
+
+## Data Structure
+
+The `lib.data` object contains the following main categories:
+
+### Game Entities
+
+#### `hero`
+Hero definitions and metadata.
+- **Structure**: Object with hero IDs as keys
+- **Usage in code**: `lib.data.hero`, `lib.getData('hero')`
+- **Example**: `lib.data.hero[1]` - Hero with ID 1
+- **Properties**: Contains hero stats, artifacts, skills, etc.
+- **Code references**:
+ - Line 10452: `Object.values(lib.data.hero)` - Get all heroes
+ - Line 10481: `lib.data.hero[id].artifacts` - Get hero artifacts
+ - Line 12421: `lib.getData('hero')` - Get hero library
+
+#### `titan`
+Titan definitions and metadata.
+- **Structure**: Object with titan IDs as keys (4000+ range)
+- **Usage in code**: `lib.getData('titan')`
+- **Example**: `lib.data.titan[4000]` - Titan with ID 4000
+- **Code references**:
+ - Line 12517: `lib.getData('titan')` - Get titan library
+
+#### `pet`
+Pet definitions and metadata.
+- **Structure**: Object with pet IDs as keys (6000+ range)
+- **Usage in code**: `lib.getData('pet')`
+- **Example**: `lib.data.pet[6000]` - Pet with ID 6000
+- **Code references**:
+ - Line 10196: `lib.getData('pet')` - Get pet library
+
+#### `skill`
+Skill definitions and metadata.
+- **Structure**: Object with skill IDs as keys
+- **Usage in code**: `lib.getData('skill')`
+- **Code references**:
+ - Line 12386: `lib.getData('skill')` - Get skill library
+
+#### `artifact`
+Artifact definitions and metadata.
+- **Structure**: Object with artifact data
+- **Usage in code**: `lib.getData('artifact')`
+- **Properties**: `id`, `type`, `battleEffect`
+- **Code references**:
+ - Line 12422: `lib.getData('artifact')` - Get artifact library
+
+#### `titanArtifact`
+Titan artifact definitions and metadata.
+- **Structure**: Object with titan artifact data
+- **Usage in code**: `lib.getData('titanArtifact')`
+- **Properties**: `id`, `type`, `battleEffect`
+- **Code references**:
+ - Line 12518: `lib.getData('titanArtifact')` - Get titan artifact library
+
+#### `skin`
+Hero skin definitions and metadata.
+- **Structure**: Object with skin IDs as keys
+- **Usage in code**: `lib.getData('skin')`
+- **Code references**:
+ - Line 12472: `lib.getData('skin')` - Get skin library
+
+### Game Modes & Activities
+
+#### `mission`
+Mission/campaign definitions and metadata.
+- **Structure**: Object with mission IDs as keys
+- **Usage in code**: `lib.data.mission`
+- **Properties**: Contains `normalMode`, `isHeroic`, `teamExp`, etc.
+- **Code references**:
+ - Line 12739: `Object.values(lib.data.mission).filter((mission) => mission.isHeroic)` - Filter heroic missions
+ - Line 12759: `lib.data.mission[selectedMissionId].normalMode.teamExp` - Get mission energy cost
+
+#### `adventure`
+Adventure mode definitions.
+- **Structure**: Object with `buff` and `id` properties
+- **Usage in code**: `lib.data.adventure`
+
+#### `adventureSolo`
+Solo adventure mode definitions.
+- **Structure**: Object with `id` property
+- **Usage in code**: `lib.data.adventureSolo`
+
+#### `seasonAdventure`
+Seasonal adventure definitions.
+- **Structure**: Object with `level` and `list` properties
+- **Usage in code**: `lib.data.seasonAdventure`
+- **Code references**:
+ - Line 1735: `Object.values(lib.data.seasonAdventure.list)` - Get adventure maps
+
+#### `dungeon`
+Dungeon floor definitions.
+- **Structure**: Object with `floor` property
+- **Usage in code**: `lib.data.dungeon`
+
+#### `tower`
+Tower of Elements definitions.
+- **Structure**: Object with `buff`, `floor`, `reward`, `rewardGroup` properties
+- **Usage in code**: `lib.data.tower`
+
+#### `expedition`
+Expedition definitions.
+- **Structure**: Object with `rarityChance`, `rewardGeneration`, `slot`, `story` properties
+- **Usage in code**: `lib.data.expedition`
+
+#### `brawl`
+Brawl mode definitions.
+- **Structure**: Object with `list` and `promoHero` properties
+- **Usage in code**: `lib.data.brawl`
+- **Code references**:
+ - Line 14018: `lib.data.brawl.promoHero[this.info.id].promoHero` - Get promo hero
+
+#### `epicBrawl`
+Epic Brawl definitions.
+- **Structure**: Object with `division`, `league`, `list`, `topReward` properties
+- **Usage in code**: `lib.data.epicBrawl`
+
+### Arena & PvP
+
+#### `arena`
+Arena definitions.
+- **Structure**: Object with `heroExp`, `matchmaking`, `reward`, `type` properties
+- **Usage in code**: `lib.data.arena`
+
+#### `titanArena`
+Titan Arena definitions.
+- **Structure**: Object with `dailyReward`, `matchmakingDayPowerBonus`, `matchmakingPower`, `matchmakingSlot`, `matchmakingStaticTeam` properties
+- **Usage in code**: `lib.data.titanArena`
+
+#### `leagueArena`
+League Arena definitions.
+- **Structure**: Object with `list`, `season`, `template` properties
+- **Usage in code**: `lib.data.leagueArena`
+
+#### `powerTournament`
+Power Tournament definitions.
+- **Structure**: Object with tournament IDs and `matchmakingSections` property
+- **Usage in code**: `lib.data.powerTournament`
+
+### Guild & Clan
+
+#### `clan`
+Clan definitions.
+- **Structure**: Object with `activityReward`, `bot`, `dungeonActivityReward`, `icon`, `iconFrame` properties
+- **Usage in code**: `lib.data.clan`
+
+#### `clanWar`
+Guild War definitions.
+- **Structure**: Object with `fortification`, `fortificationSlot`, `league` properties
+- **Usage in code**: `lib.data.clanWar`
+
+#### `clanRaid`
+Clan Raid definitions.
+- **Structure**: Object with `buffShop`, `damageReward`, `enemyStats`, `enemyTeam`, `id` properties
+- **Usage in code**: `lib.data.clanRaid`
+
+#### `clanRaidRating`
+Clan Raid Rating definitions.
+- **Structure**: Object with `clanReward`, `playerReward` properties
+- **Usage in code**: `lib.data.clanRaidRating`
+
+#### `clanCastle`
+Clan Castle definitions.
+- **Structure**: Object with `list`, `ratingReward` properties
+- **Usage in code**: `lib.data.clanCastle`
+
+#### `clanDomination`
+Clan Domination definitions.
+- **Structure**: Object with `list`, `town` properties
+- **Usage in code**: `lib.data.clanDomination`
+
+#### `clanPrestige`
+Clan Prestige definitions.
+- **Structure**: Object with `level`, `list`, `season` properties
+- **Usage in code**: `lib.data.clanPrestige`
+
+#### `crossClanWar`
+Cross Clan War definitions.
+- **Structure**: Object with `division`, `fortification`, `fortificationSlot`, `league`, `rule` properties
+- **Usage in code**: `lib.data.crossClanWar`
+
+### Battle & Combat
+
+#### `battleConfig`
+Battle configuration settings.
+- **Structure**: Object with battle type keys (e.g., `boss`, `boss_event`, `clan_pvp`, `core`, `epic_start`)
+- **Usage in code**: `lib.data.battleConfig`
+- **Properties**: Contains `config` object with `battleDuration` and other settings
+- **Code references**:
+ - Line 3123: `lib.data.battleConfig[typeBattle.split('_')[1]].config.battleDuration` - Get battle duration
+
+#### `battlePrototype`
+Battle prototype definitions.
+- **Structure**: Object with prototype IDs as keys (1-24)
+- **Usage in code**: `lib.data.battlePrototype`
+
+#### `battlePass`
+Battle Pass definitions.
+- **Structure**: Object with `level`, `list`, `questChain` properties
+- **Usage in code**: `lib.data.battlePass`
+- **Code references**:
+ - Line 9808: `Object.values(lib.data.battlePass.level).filter((x) => x.battlePass == passId)` - Filter battle pass levels
+ - Line 10537: `Object.values(lib.data.battlePass.level).filter(x => x.battlePass == pass.id)` - Filter levels by pass
+
+#### `buff`
+Buff effect definitions.
+- **Structure**: Object with `effect` and `id` properties
+- **Usage in code**: `lib.data.buff`
+
+### Shops & Economy
+
+#### `shop`
+Shop definitions.
+- **Structure**: Object with shop IDs as keys
+- **Usage in code**: `lib.data.shop`
+
+#### `bundle`
+Bundle/pack definitions.
+- **Structure**: Object with bundle IDs as keys
+- **Usage in code**: `lib.data.bundle`
+
+#### `bundleHeroReward`
+Bundle hero reward definitions.
+- **Structure**: Object with reward IDs as keys
+- **Usage in code**: `lib.data.bundleHeroReward`
+
+#### `coopBundle`
+Cooperative bundle definitions.
+- **Structure**: Object with bundle IDs as keys (6, 12, 18, 24, etc.)
+- **Usage in code**: `lib.data.coopBundle`
+
+#### `personalMerchant`
+Personal Merchant definitions.
+- **Structure**: Object with gear IDs as keys (e.g., `gear_100`, `gear_101`)
+- **Usage in code**: `lib.data.personalMerchant`
+
+#### `billing`
+Billing and pricing definitions.
+- **Structure**: Object with `byCurrency`, `groupPrices` properties
+- **Usage in code**: `lib.data.billing`
+
+### Inventory & Items
+
+#### `inventoryItem`
+Inventory item definitions.
+- **Structure**: Object with item category properties (`ascensionGear`, `bannerStone`, `coin`, `consumable`, `gear`, etc.)
+- **Usage in code**: `lib.data.inventoryItem`, `lib.getData('inventoryItem')`
+- **Code references**:
+ - Line 2827: `lib.data.inventoryItem.consumable[call.args.libId]` - Get consumable item info
+ - Line 9411: `lib.getData('inventoryItem')` - Get inventory items
+
+#### `lootBox`
+Loot box definitions.
+- **Structure**: Object with loot box IDs as keys (various naming patterns)
+- **Usage in code**: `lib.data.lootBox`
+
+#### `refillable`
+Refillable resource definitions.
+- **Structure**: Object with refillable resource IDs as keys
+- **Usage in code**: `lib.data.refillable`
+- **Properties**: Contains `id`, `ident`, `refillSeconds`, `maxValue`, `maxRefillCount`, etc.
+- **Note**: See HERO_WARS_API_DOCUMENTATION.md for detailed usage
+
+### Quests & Events
+
+#### `quest`
+Quest definitions.
+- **Structure**: Object with quest category properties (`battlePass`, `chain`, `clan`, `daily`, `eventFunc`, `special`)
+- **Usage in code**: `lib.getData('quest')`
+- **Code references**:
+ - Line 9812: `lib.getData('quest').special` - Get special quests
+ - Line 9813: `lib.getData('quest').battlePass` - Get battle pass quests
+
+#### `specialQuestEvent`
+Special quest event definitions.
+- **Structure**: Object with `chain`, `type` properties
+- **Usage in code**: `lib.data.specialQuestEvent`
+
+#### `appEvent`
+Application event definitions.
+- **Structure**: Array (may be empty)
+- **Usage in code**: `lib.data.appEvent`
+
+#### `eventBox`
+Event box definitions.
+- **Structure**: Object with box IDs as keys (1-6)
+- **Usage in code**: `lib.data.eventBox`
+
+#### `eventPicker`
+Event picker definitions.
+- **Structure**: Object with `events`, `round`, `roundResumePrice`, `roundReward` properties
+- **Usage in code**: `lib.data.eventPicker`
+
+### Bosses & Raids
+
+#### `boss`
+Boss definitions.
+- **Structure**: Object with `chest`, `list`, `map` properties
+- **Usage in code**: `lib.data.boss`
+
+#### `invasion`
+Invasion event definitions.
+- **Structure**: Object with `boss`, `chapter`, `list`, `phase`, `unitUpgrades` properties
+- **Usage in code**: `lib.data.invasion`
+- **Code references**:
+ - Line 1882: `lib.data.invasion` - Get invasion data
+ - Line 1883-1884: Used to find current invasion phase by date
+
+### Progression & Levels
+
+#### `level`
+Level definitions for various game systems.
+- **Structure**: Object with level category properties (`alchemy`, `clan`, `hero`, `pet`, `skillLevelCost`, `vip`)
+- **Usage in code**: `lib.data.level`, `lib.getData('level')`
+- **Code references**:
+ - Line 9267: `lib.getData('level').vip` - Get VIP level info
+ - Line 12022: `lib.data.level.vip.filter(l => l.vipPoints <= +this.questInfo.userGetInfo.vipPoints)` - Filter VIP levels
+
+#### `mechanic`
+Mechanic definitions.
+- **Structure**: Object with `level`, `limit` properties
+- **Usage in code**: `lib.data.mechanic`
+
+### Rewards & Gifts
+
+#### `gift`
+Gift definitions.
+- **Structure**: Object with gift IDs as keys (2-100+)
+- **Usage in code**: `lib.data.gift`
+
+#### `titanGift`
+Titan gift definitions.
+- **Structure**: Object with gift IDs as keys (1-30)
+- **Usage in code**: `lib.getData('titanGift')`
+- **Code references**:
+ - Line 12697: `lib.getData('titanGift')` - Get titan gift library
+
+#### `nyReward`
+New Year reward definitions.
+- **Structure**: Object with reward IDs as keys (1-10, 21, 31, 41, 51, 101, 251, 501, 1001)
+- **Usage in code**: `lib.data.nyReward`
+
+#### `rewardModifier`
+Reward modifier definitions.
+- **Structure**: Object with modifier IDs as keys
+- **Usage in code**: `lib.data.rewardModifier`
+
+### Daily & Static Data
+
+#### `dailyBonusStatic`
+Daily bonus static definitions.
+- **Structure**: Object with version keys (e.g., `10_0_0`, `10_6_2019`, `11_0_0`)
+- **Usage in code**: `lib.getData('dailyBonusStatic')`
+- **Code references**:
+ - Line 9266: `lib.getData('dailyBonusStatic')` - Get daily bonus static data
+
+### Character Customization
+
+#### `playerAvatar`
+Player avatar definitions.
+- **Structure**: Object with avatar IDs as keys (1-100+)
+- **Usage in code**: `lib.data.playerAvatar`
+
+#### `playerAvatarFrame`
+Player avatar frame definitions.
+- **Structure**: Object with frame IDs as keys (0-100+)
+- **Usage in code**: `lib.data.playerAvatarFrame`
+
+#### `nickname`
+Nickname definitions.
+- **Structure**: Object with nickname IDs as keys (1-100+)
+- **Usage in code**: `lib.data.nickname`
+
+#### `banner`
+Banner definitions.
+- **Structure**: Object with banner IDs as keys (1-8)
+- **Usage in code**: `lib.data.banner`
+
+#### `sticker`
+Sticker definitions.
+- **Structure**: Object with sticker IDs as keys (1-57)
+- **Usage in code**: `lib.data.sticker`
+
+### Special Systems
+
+#### `heroAscension`
+Hero ascension definitions.
+- **Structure**: Object with `id`, `node` properties
+- **Usage in code**: `lib.data.heroAscension`
+
+#### `heroCounterPick`
+Hero counter pick definitions.
+- **Structure**: Object with counter pick IDs as keys (1-68)
+- **Usage in code**: `lib.data.heroCounterPick`
+
+#### `heroTalent`
+Hero talent definitions.
+- **Structure**: Object with talent IDs as keys (1)
+- **Usage in code**: `lib.data.heroTalent`
+
+#### `heroTalentType`
+Hero talent type definitions.
+- **Structure**: Object with type IDs as keys (1-2)
+- **Usage in code**: `lib.data.heroTalentType`
+
+#### `roleAscension`
+Role ascension definitions.
+- **Structure**: Object with role IDs as keys (1-7)
+- **Usage in code**: `lib.data.roleAscension`
+
+#### `titanSpirit`
+Titan spirit definitions.
+- **Structure**: Object with `skills` property
+- **Usage in code**: `lib.data.titanSpirit`
+
+#### `titanSpiritSkillWeights`
+Titan spirit skill weight definitions.
+- **Structure**: Object with skill IDs as keys (1-19)
+- **Usage in code**: `lib.data.titanSpiritSkillWeights`
+
+### Runes & Enchantments
+
+#### `rune`
+Rune definitions.
+- **Structure**: Object with `level` (array of 51), `tier` (array of 5), `type` properties
+- **Usage in code**: `lib.getData('rune')`
+- **Code references**:
+ - Line 12581: `lib.getData('rune')` - Get rune library
+
+### Gacha & Random Systems
+
+#### `gacha`
+Gacha system definitions.
+- **Structure**: Object with `category`, `id` properties
+- **Usage in code**: `lib.data.gacha`
+
+#### `lineGacha`
+Line gacha definitions.
+- **Structure**: Object with `groups`, `list`, `rewards` properties
+- **Usage in code**: `lib.data.lineGacha`
+
+### Story & Content
+
+#### `campaignStory`
+Campaign story definitions.
+- **Structure**: Object with story IDs as keys (1-28)
+- **Usage in code**: `lib.data.campaignStory`
+
+#### `comics`
+Comics definitions.
+- **Structure**: Object with comic IDs as keys (1-2)
+- **Usage in code**: `lib.data.comics`
+
+#### `world`
+World definitions.
+- **Structure**: Object with world IDs as keys (1-15)
+- **Usage in code**: `lib.data.world`
+
+### UI & Assets
+
+#### `asset`
+Asset definitions.
+- **Structure**: Object with asset category properties (`battleground`, `font`, `gui`, `hero`, `inventory`, etc.)
+- **Usage in code**: `lib.data.asset`
+
+#### `mainScreenSkin`
+Main screen skin definitions.
+- **Structure**: Object with skin IDs as keys (e.g., `Mainscreen_Maincity_Birthday_2025`, `main_screen`)
+- **Usage in code**: `lib.data.mainScreenSkin`
+
+### Tutorial & Help
+
+#### `tutorial`
+Tutorial definitions.
+- **Structure**: Object with `chain`, `group`, `movieSubtitle`, `task` properties
+- **Usage in code**: `lib.data.tutorial`
+
+### Testing & Development
+
+#### `playtest`
+Playtest definitions.
+- **Structure**: Object with `preset`, `presetHeroes` properties
+- **Usage in code**: `lib.data.playtest`
+
+#### `demoBattleMode`
+Demo battle mode definitions.
+- **Structure**: Object with battle mode properties (`arena`, `clan_global_pvp`, `clan_global_pvp_titan`, `clan_pvp`, `clan_pvp_titan`, etc.)
+- **Usage in code**: `lib.data.demoBattleMode`
+
+### Other Systems
+
+#### `admiration`
+Admiration definitions.
+- **Structure**: Object with admiration IDs as keys (1-3)
+- **Usage in code**: `lib.data.admiration`
+
+#### `specialOffer`
+Special offer definitions.
+- **Structure**: Object with offer IDs as keys
+- **Usage in code**: `lib.data.specialOffer`
+
+#### `subscription`
+Subscription definitions.
+- **Structure**: Object with subscription IDs as keys (6)
+- **Usage in code**: `lib.data.subscription`
+
+#### `notification`
+Notification definitions.
+- **Structure**: Object with notification type properties (`adventureInvitation`, `arenaPosition`, `clanChampion`, `clanGift`, `clanOrder`, etc.)
+- **Usage in code**: `lib.data.notification`
+
+#### `mail`
+Mail system definitions.
+- **Structure**: Object with `resourceFilter`, `type` properties
+- **Usage in code**: `lib.data.mail`
+
+#### `socialGraph`
+Social graph definitions.
+- **Structure**: Object with `action`, `object`, `vkMap` properties
+- **Usage in code**: `lib.data.socialGraph`
+
+#### `stronghold`
+Stronghold definitions.
+- **Structure**: Object with `mission`, `region` properties
+- **Usage in code**: `lib.data.stronghold`
+
+#### `tiledMap`
+Tiled map definitions.
+- **Structure**: Object with `level`, `list` properties
+- **Usage in code**: `lib.data.tiledMap`
+
+#### `trial`
+Trial definitions.
+- **Structure**: Object with `battle`, `type` properties
+- **Usage in code**: `lib.data.trial`
+
+#### `minigame`
+Minigame definitions.
+- **Structure**: Object with `game`, `story`, `tower` properties
+- **Usage in code**: `lib.data.minigame`
+
+#### `workshop`
+Workshop definitions.
+- **Structure**: Object with `buff`, `relic` properties
+- **Usage in code**: `lib.data.workshop`
+
+#### `idleResource`
+Idle resource definitions.
+- **Structure**: Object with resource IDs as keys (1)
+- **Usage in code**: `lib.data.idleResource`
+
+#### `playable`
+Playable character definitions.
+- **Structure**: Object with character IDs as keys (3-50)
+- **Usage in code**: `lib.data.playable`
+
+### Dictionaries & Enums
+
+#### `dict`
+Dictionary definitions for various game terms.
+- **Structure**: Object with dictionary category properties (`activitySource`, `battleType`, `clanBossRatingType`, `clanBossRewardType`, `clanWarMechanics`, etc.)
+- **Usage in code**: `lib.data.dict`
+
+#### `enum`
+Enumeration definitions.
+- **Structure**: Object with enum category properties (`evolutionStar`, `heroColor`, `heroPerk`, `itemColor`, `language`, etc.)
+- **Usage in code**: `lib.data.enum`
+
+#### `topType`
+Top type definitions.
+- **Structure**: Object with type keys (e.g., `bday2019`, `bday2021`, `bday2022`, `bday2023`, `bday2024`)
+- **Usage in code**: `lib.data.topType`
+
+### Rules & Configuration
+
+#### `rule`
+Game rules and configuration.
+- **Structure**: Object with rule category properties (`CrossNetworkCostToCurrencyPriceMap`, `NY2018_client`, `adventure`, `adventureDisabledHeroes`, `adventureSoloCreepStats`, etc.)
+- **Usage in code**: `lib.data.rule`
+
+### Scheduled & System
+
+#### `scheduled`
+Scheduled event definitions.
+- **Structure**: Object with `preloader` property
+- **Usage in code**: `lib.data.scheduled`
+
+#### `unitPhrases`
+Unit phrase definitions.
+- **Structure**: Object with phrase category properties (`main_screen`, `pve`, `pvp`, `tower`)
+- **Usage in code**: `lib.data.unitPhrases`
+
+## Usage Examples
+
+### Getting Hero Information
+```javascript
+// Get all heroes
+const allHeroes = Object.values(lib.data.hero);
+
+// Get specific hero
+const hero = lib.data.hero[1];
+
+// Get hero artifacts
+const artifacts = lib.data.hero[1].artifacts;
+```
+
+### Getting Mission Information
+```javascript
+// Get all missions
+const allMissions = Object.values(lib.data.mission);
+
+// Filter heroic missions
+const heroicMissions = Object.values(lib.data.mission).filter(
+ mission => mission.isHeroic
+);
+
+// Get mission energy cost
+const energyCost = lib.data.mission[1].normalMode.teamExp;
+```
+
+### Getting Battle Pass Information
+```javascript
+// Get battle pass levels for a specific pass
+const levels = Object.values(lib.data.battlePass.level)
+ .filter(x => x.battlePass == passId);
+
+// Get current battle pass level
+const currentLevel = Math.max(
+ ...levels
+ .filter(p => battlePass.exp >= p.experience)
+ .map(p => p.level)
+);
+```
+
+### Getting Battle Configuration
+```javascript
+// Get battle duration for a specific battle type
+const battleType = 'clan_pvp';
+const battleDuration = lib.data.battleConfig[battleType].config.battleDuration;
+```
+
+### Getting Inventory Items
+```javascript
+// Get consumable item info
+const lootBoxInfo = lib.data.inventoryItem.consumable[lootBoxId];
+
+// Get all inventory items
+const items = lib.getData('inventoryItem');
+```
+
+### Getting VIP Level Information
+```javascript
+// Get VIP level based on VIP points
+const vipLevel = Math.max(
+ ...lib.data.level.vip
+ .filter(l => l.vipPoints <= userVipPoints)
+ .map(l => l.level)
+);
+```
+
+### Getting Invasion Phase
+```javascript
+// Get current invasion phase
+const libInvasion = lib.data.invasion;
+const now = Date.now() / 1000;
+const phase = Object.values(libInvasion.phase).find(
+ e => e.startDate < now && e.endDate > now
+);
+```
+
+## Notes
+
+1. **Data Loading**: The library data is loaded asynchronously from a JSON file. Ensure `lib.data` is available before accessing it.
+
+2. **Data Structure**: Most data is organized as objects with numeric or string IDs as keys, making it easy to look up specific entities.
+
+3. **Nested Properties**: Many categories contain nested objects with additional properties. Explore the structure in the browser console to understand the full data model.
+
+4. **Dynamic Updates**: The library data may be updated by the game server, so the structure and available properties may change over time.
+
+5. **Performance**: For frequently accessed data, consider caching references to avoid repeated lookups.
+
+6. **Type Safety**: The data structure is not strictly typed, so always check for property existence before accessing nested properties.
+
+## Related Documentation
+
+- `HERO_WARS_API_DOCUMENTATION.md` - API documentation including refillable resources
+- `EXTENSION_DEVELOPMENT.md` - Extension development guidelines
+- `DEVELOPMENT.md` - General development documentation
+
diff --git a/LLM Controller HwH Ext.user.js b/LLM Controller HwH Ext.user.js
new file mode 100644
index 0000000..c59f2f3
--- /dev/null
+++ b/LLM Controller HwH Ext.user.js
@@ -0,0 +1,999 @@
+// ==UserScript==
+// @name LLM Controller HwH Ext
+// @namespace HeroWarsHelper.LLMController
+// @version 1.5
+// @description Provides an LLM-accessible API interface and localhost bridge for Cursor control
+// @author YourName
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @grant none
+// @run-at document-end
+// @downloadURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/LLM%20Controller%20HwH%20Ext.user.js
+// @updateURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/LLM%20Controller%20HwH%20Ext.user.js
+// ==/UserScript==
+
+(function() {
+ 'use strict';
+
+ const EXTENSION_NAME = "LLM Controller Extension";
+ const EXTENSION_VERSION = "1.5";
+ const BRIDGE_URL = 'http://127.0.0.1:9876';
+ const BRIDGE_POLL_MS = 500;
+ const EXTENSION_AUTHOR = "YourName";
+
+ // Wait for HWH to be ready
+ const waitForHWH = setInterval(() => {
+ if (window.HWHClasses && window.HWHClasses.ScriptMenu && window.lib && window.cheats && window.Send && window.HWHFuncs) {
+ const scriptMenu = window.HWHClasses.ScriptMenu.getInst();
+ if (scriptMenu && scriptMenu.mainMenu) {
+ clearInterval(waitForHWH);
+ initializeExtension();
+ }
+ }
+ }, 200);
+
+ function initializeExtension() {
+ console.log(`${EXTENSION_NAME} v${EXTENSION_VERSION} is loading...`);
+
+ const { HWHClasses, HWHFuncs, Send, cheats, Caller, lib } = window;
+ HWHFuncs.addExtentionName(EXTENSION_NAME, EXTENSION_VERSION, EXTENSION_AUTHOR);
+
+ // Create LLM API interface
+ const apiRecorder = createApiRecorder({ Send: window.Send, HWHFuncs });
+ const api = createLLMAPI({ HWHClasses, HWHFuncs, Send, cheats, Caller, lib, apiRecorder });
+ window.LLMHWH = api;
+
+ // Localhost bridge for Cursor (polls llm-bridge-server.mjs)
+ startBridgeClient(api, HWHFuncs);
+
+ // Add menu button for testing
+ const scriptMenu = HWHClasses.ScriptMenu.getInst();
+ scriptMenu.addCombinedButton([
+ {
+ name: 'LLM API',
+ title: 'Open LLM API documentation and test interface',
+ onClick: openLLMInterface
+ },
+ {
+ name: 'Bridge',
+ title: 'LLM bridge status (Cursor localhost server)',
+ onClick: () => {
+ const status = api.getBridgeStatus();
+ HWHFuncs.setProgress(
+ `Bridge: ${status.connected ? 'connected' : 'waiting'} | server: ${status.serverReachable ? 'up' : 'down'}`,
+ true
+ );
+ },
+ color: 'gray'
+ },
+ {
+ name: 'Record',
+ title: 'Toggle API recording for manual UI playthroughs',
+ onClick: () => {
+ const status = api.getApiRecordingStatus();
+ if (status.recording) {
+ const result = api.stopApiRecording();
+ HWHFuncs.setProgress(`API recording stopped (${result.entryCount} calls)`, true);
+ } else {
+ const result = api.startApiRecording({ label: 'manual-ui' });
+ HWHFuncs.setProgress(`API recording started (${result.sessionId})`, true);
+ }
+ },
+ color: 'purple'
+ }
+ ]);
+
+ console.log(`${EXTENSION_NAME} initialized. LLM API available at window.LLMHWH`);
+ }
+
+ function startBridgeClient(api, HWHFuncs) {
+ let running = false;
+ let connected = false;
+ let serverReachable = false;
+
+ api.getBridgeStatus = () => ({ connected, serverReachable, url: BRIDGE_URL });
+
+ async function pollOnce() {
+ if (running) return;
+ running = true;
+ try {
+ const health = await fetch(`${BRIDGE_URL}/health`).then(r => r.json()).catch(() => null);
+ serverReachable = !!health?.ok;
+
+ const res = await fetch(`${BRIDGE_URL}/poll`);
+ if (res.status === 204) {
+ connected = serverReachable;
+ return;
+ }
+ if (!res.ok) return;
+
+ const command = await res.json();
+ connected = true;
+
+ let result;
+ let ok = true;
+ let error = null;
+ try {
+ result = await api.runCommand(command.method, command.args || []);
+ } catch (e) {
+ ok = false;
+ error = e.message || String(e);
+ }
+
+ await fetch(`${BRIDGE_URL}/result`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ id: command.id, ok, result, error }),
+ });
+ } catch (e) {
+ connected = false;
+ } finally {
+ running = false;
+ }
+ }
+
+ setInterval(pollOnce, BRIDGE_POLL_MS);
+ pollOnce();
+ console.log(`${EXTENSION_NAME}: bridge client polling ${BRIDGE_URL}`);
+ }
+
+ function createApiRecorder({ Send, HWHFuncs }) {
+ let recording = false;
+ let sessionId = null;
+ let label = '';
+ let startedAt = null;
+ let entries = [];
+ let entryCounter = 0;
+ let pollTimer = null;
+ let originalSend = null;
+ const seenHistoryIds = new Set();
+ const seenSendKeys = new Set();
+
+ function parseJson(value) {
+ if (value == null) return null;
+ if (typeof value === 'object') return value;
+ try {
+ return JSON.parse(value);
+ } catch {
+ return null;
+ }
+ }
+
+ function normalizeCalls(request) {
+ const parsed = parseJson(request);
+ if (!parsed) return [];
+ if (Array.isArray(parsed.calls)) return parsed.calls;
+ if (parsed.name) return [parsed];
+ return [];
+ }
+
+ function matchResults(calls, response) {
+ const parsed = parseJson(response);
+ if (!parsed?.results || !Array.isArray(parsed.results)) {
+ return calls.map((call) => ({ call, result: parsed }));
+ }
+ const byIdent = new Map(parsed.results.map((item) => [item.ident, item]));
+ return calls.map((call, index) => ({
+ call,
+ result: byIdent.get(call.ident) ?? parsed.results[index] ?? null,
+ }));
+ }
+
+ function addEntry({ source, apiName, args, ident, request, response, error, meta = {} }) {
+ const entry = {
+ id: ++entryCounter,
+ timestamp: new Date().toISOString(),
+ source,
+ apiName,
+ args: args ?? {},
+ ident: ident ?? null,
+ request: request ?? null,
+ response: response ?? null,
+ error: error ?? null,
+ meta,
+ };
+ entries.push(entry);
+ console.log(`[LLM API Recorder] ${apiName}`, entry);
+ return entry;
+ }
+
+ function captureSendPayload(request, response, error, durationMs) {
+ const calls = normalizeCalls(request);
+ if (!calls.length) return;
+
+ const pairs = matchResults(calls, response);
+ for (const { call, result } of pairs) {
+ const dedupeKey = `send:${call.name}:${JSON.stringify(call.args)}:${JSON.stringify(result)}`;
+ if (seenSendKeys.has(dedupeKey)) continue;
+ seenSendKeys.add(dedupeKey);
+
+ addEntry({
+ source: 'send',
+ apiName: call.name,
+ args: call.args,
+ ident: call.ident,
+ request: call,
+ response: result,
+ error,
+ meta: { durationMs },
+ });
+ }
+ }
+
+ function captureRequestHistory() {
+ const history = typeof window.getRequestHistory === 'function'
+ ? window.getRequestHistory()
+ : null;
+ if (!history) return;
+
+ for (const [historyId, item] of Object.entries(history)) {
+ if (!item?.response || seenHistoryIds.has(historyId)) continue;
+ seenHistoryIds.add(historyId);
+
+ const calls = normalizeCalls(item.request);
+ const pairs = matchResults(calls, item.response);
+ for (const { call, result } of pairs) {
+ addEntry({
+ source: 'xhr',
+ apiName: call.name,
+ args: call.args,
+ ident: call.ident,
+ request: call,
+ response: result,
+ meta: { historyId },
+ });
+ }
+ }
+ }
+
+ function installSendHook() {
+ if (originalSend || typeof window.Send !== 'function') return;
+ originalSend = window.Send;
+ window.Send = function hookedSend(json, pr) {
+ const started = Date.now();
+ return originalSend.call(this, json, pr)
+ .then((response) => {
+ if (recording) {
+ captureSendPayload(json, response, null, Date.now() - started);
+ }
+ return response;
+ })
+ .catch((error) => {
+ if (recording) {
+ captureSendPayload(json, null, {
+ name: error?.name,
+ message: error?.message || String(error),
+ }, Date.now() - started);
+ }
+ throw error;
+ });
+ };
+ }
+
+ function uninstallSendHook() {
+ if (originalSend) {
+ window.Send = originalSend;
+ originalSend = null;
+ }
+ }
+
+ return {
+ startApiRecording(options = {}) {
+ if (recording) {
+ return this.getApiRecordingStatus();
+ }
+ installSendHook();
+ recording = true;
+ sessionId = `rec_${Date.now()}`;
+ label = options.label || 'manual-ui';
+ startedAt = new Date().toISOString();
+ entries = [];
+ entryCounter = 0;
+ seenHistoryIds.clear();
+ seenSendKeys.clear();
+ captureRequestHistory();
+ pollTimer = setInterval(captureRequestHistory, 1000);
+ HWHFuncs?.setProgress?.(`API recording started (${sessionId})`, true);
+ return this.getApiRecordingStatus();
+ },
+
+ stopApiRecording() {
+ if (!recording) {
+ return this.getApiRecordingStatus();
+ }
+ recording = false;
+ if (pollTimer) {
+ clearInterval(pollTimer);
+ pollTimer = null;
+ }
+ captureRequestHistory();
+ uninstallSendHook();
+ const status = this.getApiRecordingStatus();
+ HWHFuncs?.setProgress?.(`API recording stopped (${status.entryCount} calls)`, true);
+ return status;
+ },
+
+ clearApiRecording() {
+ entries = [];
+ entryCounter = 0;
+ seenHistoryIds.clear();
+ seenSendKeys.clear();
+ return this.getApiRecordingStatus();
+ },
+
+ getApiRecordingStatus() {
+ return {
+ recording,
+ sessionId,
+ label,
+ startedAt,
+ entryCount: entries.length,
+ };
+ },
+
+ getApiRecording(options = {}) {
+ const sinceId = Number(options.sinceId) || 0;
+ const slice = sinceId > 0
+ ? entries.filter((entry) => entry.id > sinceId)
+ : entries;
+ return {
+ ...this.getApiRecordingStatus(),
+ entries: slice,
+ lastEntryId: entries.length ? entries[entries.length - 1].id : 0,
+ };
+ },
+
+ exportApiRecording() {
+ return {
+ exportedAt: new Date().toISOString(),
+ ...this.getApiRecording(),
+ };
+ },
+ };
+ }
+
+ function createLLMAPI({ HWHClasses, HWHFuncs, Send, cheats, Caller, lib, apiRecorder }) {
+ /**
+ * LLM API Interface for HeroWarsHelper
+ *
+ * This API allows LLMs to directly control HeroWarsHelper functions.
+ * All functions return Promises and can be awaited.
+ */
+ return {
+ // ========== CORE API FUNCTIONS ==========
+ ...apiRecorder,
+
+ /**
+ * Send API request directly
+ * @param {Object|string} request - API call object or JSON string
+ * @returns {Promise} API response
+ */
+ async sendAPI(request) {
+ try {
+ if (typeof request === 'string') {
+ request = JSON.parse(request);
+ }
+ return await Send(request);
+ } catch (error) {
+ throw new Error(`API call failed: ${error.message}`);
+ }
+ },
+
+ /**
+ * Get user information
+ * @returns {Promise} User info
+ */
+ async getUserInfo() {
+ return await Send({ calls: [{ name: "userGetInfo", args: {}, ident: "userInfo" }] });
+ },
+
+ /**
+ * Get all heroes
+ * @returns {Promise} Hero data
+ */
+ async getHeroes() {
+ return await Send({ calls: [{ name: "heroGetAll", args: {}, ident: "heroes" }] });
+ },
+
+ /**
+ * Get all titans
+ * @returns {Promise} Titan data
+ */
+ async getTitans() {
+ return await Send({ calls: [{ name: "titanGetAll", args: {}, ident: "titans" }] });
+ },
+
+ /**
+ * Get inventory
+ * @returns {Promise} Inventory data
+ */
+ async getInventory() {
+ return await Send({ calls: [{ name: "inventoryGet", args: {}, ident: "inventory" }] });
+ },
+
+ /**
+ * Get all quests
+ * @returns {Promise} Quest data
+ */
+ async getQuests() {
+ return await Send({ calls: [{ name: "questGetAll", args: {}, ident: "quests" }] });
+ },
+
+ // ========== GAME OPERATIONS ==========
+
+ /**
+ * Execute Outland (boss raids and chests)
+ * @returns {Promise} Status message
+ */
+ async executeOutland() {
+ return new Promise((resolve, reject) => {
+ try {
+ HWHFuncs.setProgress('Executing: Outland', true);
+ const getOutland = window.getOutland || window.HWHData?.buttons?.getOutland?.button?.onclick;
+ if (getOutland) {
+ getOutland();
+ setTimeout(() => resolve('Outland executed'), 2000);
+ } else {
+ // Fallback: direct API call
+ Send({ calls: [{ name: "bossGetAll", args: {}, ident: "bossGetAll" }] })
+ .then(data => {
+ const bosses = data.results[0].result.response;
+ const calls = [];
+ for (const boss of bosses) {
+ if (boss.mayRaid) calls.push({ name: "bossRaid", args: { bossId: boss.id }, ident: "bossRaid_" + boss.id });
+ if (boss.chestId === 1 || boss.mayRaid) calls.push({ name: "bossOpenChest", args: { bossId: boss.id, amount: 1, starmoney: 0 }, ident: "bossOpenChest_" + boss.id });
+ }
+ if (calls.length > 0) {
+ return Send({ calls });
+ }
+ })
+ .then(() => {
+ HWHFuncs.setProgress('Outland: Done!', true);
+ resolve('Outland executed successfully');
+ })
+ .catch(reject);
+ }
+ } catch (error) {
+ reject(new Error(`Outland execution failed: ${error.message}`));
+ }
+ });
+ },
+
+ /**
+ * Execute Tower
+ * @returns {Promise} Status message
+ */
+ async executeTower() {
+ return new Promise((resolve, reject) => {
+ try {
+ HWHFuncs.setProgress('Executing: Tower', true);
+ const executeTower = new HWHClasses.executeTower(resolve, reject);
+ executeTower.start();
+ } catch (error) {
+ reject(new Error(`Tower execution failed: ${error.message}`));
+ }
+ });
+ },
+
+ /**
+ * Execute Dungeon
+ * @param {number} maxTitanite - Maximum titanite to collect (optional)
+ * @returns {Promise} Status message
+ */
+ async executeDungeon(maxTitanite = null) {
+ if (!HWHClasses.executeDungeon) {
+ throw new Error('Dungeon function not available (install Auto Daily ext for Stealther dungeon)');
+ }
+ if (window.HWH_DUNGEON_RUNNING || window.HWH_DUNGEON_BATTLE_OPEN) {
+ throw new Error('Dungeon already running');
+ }
+ return new Promise((resolve, reject) => {
+ try {
+ HWHFuncs.setProgress('Executing: Dungeon', true);
+ const dungeon = new HWHClasses.executeDungeon(resolve, reject);
+ if (maxTitanite != null) {
+ dungeon.start(maxTitanite);
+ } else {
+ dungeon.start();
+ }
+ } catch (error) {
+ reject(new Error(`Dungeon execution failed: ${error.message}`));
+ }
+ });
+ },
+
+ /**
+ * Execute Arena (all attempts) via AutoBattle if available
+ * @param {string} arenaType - 'arena' or 'grand'
+ * @returns {Promise} Status message
+ */
+ async executeArena(arenaType = 'arena') {
+ if (!HWHClasses.executeArena) {
+ throw new Error('executeArena not available (install AutoBattle HwH Ext)');
+ }
+ return new Promise((resolve, reject) => {
+ try {
+ HWHFuncs.setProgress(`Executing: ${arenaType === 'grand' ? 'Grand Arena' : 'Arena'}`, true);
+ const arena = new HWHClasses.executeArena(resolve, reject);
+ arena.start(arenaType);
+ } catch (error) {
+ reject(new Error(`Arena execution failed: ${error.message}`));
+ }
+ });
+ },
+
+ /**
+ * Execute Expeditions
+ * @returns {Promise} Status message
+ */
+ async executeExpeditions() {
+ return new Promise((resolve, reject) => {
+ try {
+ HWHFuncs.setProgress('Executing: Expeditions', true);
+ const expedition = new HWHClasses.Expedition(resolve, reject);
+ expedition.start();
+ } catch (error) {
+ reject(new Error(`Expeditions execution failed: ${error.message}`));
+ }
+ });
+ },
+
+ /**
+ * Collect all quest rewards
+ * @returns {Promise} Status message
+ */
+ async collectQuestRewards() {
+ try {
+ HWHFuncs.setProgress('Collecting quest rewards', true);
+ const questData = await Send({ calls: [{ name: "questGetAll", args: {}, ident: "quests" }] });
+ const quests = questData.results[0].result.response;
+ const questsToFarm = quests.filter(q => q && q.id < 1000000 && q.state === 2);
+
+ if (questsToFarm.length === 0) {
+ HWHFuncs.setProgress('No quests ready to collect', true);
+ return 'No quests ready to collect';
+ }
+
+ const questCalls = questsToFarm.map(q => ({
+ name: "questFarm",
+ args: { questId: q.id },
+ ident: `questFarm_${q.id}`
+ }));
+
+ await Send({ calls: questCalls });
+ HWHFuncs.setProgress(`Collected ${questsToFarm.length} quest rewards`, true);
+ return `Collected ${questsToFarm.length} quest rewards`;
+ } catch (error) {
+ throw new Error(`Quest collection failed: ${error.message}`);
+ }
+ },
+
+ /**
+ * Collect mail
+ * @returns {Promise} Status message
+ */
+ async collectMail() {
+ try {
+ HWHFuncs.setProgress('Collecting mail', true);
+ const mailData = await Send({ calls: [{ name: "mailGetAll", args: {}, ident: "mail" }] });
+ const letters = mailData.results[0].result.response.letters;
+ const letterIds = HWHClasses.Letters.filter(letters);
+
+ if (letterIds.length > 0) {
+ await Send({ calls: [{ name: "mailFarm", args: { letterIds }, ident: "mailFarm" }] });
+ HWHFuncs.setProgress(`Collected ${letterIds.length} mail items`, true);
+ return `Collected ${letterIds.length} mail items`;
+ } else {
+ HWHFuncs.setProgress('No mail to collect', true);
+ return 'No mail to collect';
+ }
+ } catch (error) {
+ throw new Error(`Mail collection failed: ${error.message}`);
+ }
+ },
+
+ /**
+ * Get daily bonus
+ * @returns {Promise} Status message
+ */
+ async getDailyBonus() {
+ try {
+ HWHFuncs.setProgress('Getting daily bonus', true);
+ const doYourBest = new HWHClasses.doYourBest(() => {}, () => {});
+ if (doYourBest.functions && doYourBest.functions.getDailyBonus) {
+ await doYourBest.functions.getDailyBonus();
+ HWHFuncs.setProgress('Daily bonus collected', true);
+ return 'Daily bonus collected';
+ } else {
+ throw new Error('Daily bonus function not available');
+ }
+ } catch (error) {
+ throw new Error(`Daily bonus failed: ${error.message}`);
+ }
+ },
+
+ /**
+ * Execute Seer (Ascension Chest)
+ * @returns {Promise} Status message
+ */
+ async executeSeer() {
+ try {
+ HWHFuncs.setProgress('Executing: Seer', true);
+ const data = await Send({ calls: [{ name: "userGetInfo", args: {}, ident: "userInfo" }] });
+ const refillable = data.results[0].result.response.refillable;
+ const seerCharges = refillable.find(i => i.id == 47);
+
+ if (seerCharges && seerCharges.amount > 0) {
+ await Send({ calls: [{ name: "ascensionChest_open", args: { paid: false, amount: 1 }, ident: "seer" }] });
+ HWHFuncs.setProgress('Seer: Done!', true);
+ return 'Seer executed successfully';
+ } else {
+ HWHFuncs.setProgress('Seer: No charges available', true);
+ return 'No seer charges available';
+ }
+ } catch (error) {
+ throw new Error(`Seer execution failed: ${error.message}`);
+ }
+ },
+
+ // ========== BATTLE OPERATIONS ==========
+
+ /**
+ * Execute Arena battle
+ * @param {Object} team - Team configuration
+ * @returns {Promise} Battle result
+ */
+ async executeArenaBattle(team) {
+ try {
+ const battleCall = {
+ calls: [{
+ name: "arenaStartBattle",
+ args: team,
+ ident: "arenaBattle"
+ }]
+ };
+ return await Send(battleCall);
+ } catch (error) {
+ throw new Error(`Arena battle failed: ${error.message}`);
+ }
+ },
+
+ /**
+ * Execute Grand Arena battle
+ * @param {Object} teams - Team configurations (3 teams)
+ * @returns {Promise} Battle result
+ */
+ async executeGrandArenaBattle(teams) {
+ try {
+ const battleCall = {
+ calls: [{
+ name: "grandArenaStartBattle",
+ args: teams,
+ ident: "grandArenaBattle"
+ }]
+ };
+ return await Send(battleCall);
+ } catch (error) {
+ throw new Error(`Grand Arena battle failed: ${error.message}`);
+ }
+ },
+
+ // ========== ARENA TRAINING ==========
+
+ /**
+ * Run arena combo training via Arena Training extension (demo battles, no attempts used)
+ * @param {Object} options
+ * @returns {Promise}
+ */
+ async arenaTrainingRun(options = {}) {
+ if (!window.ArenaTraining) {
+ throw new Error('Arena Training not available (install Arena Training HwH Ext)');
+ }
+ return await window.ArenaTraining.run(options);
+ },
+
+ async arenaTrainingGetOpponents(forceRefresh = false, options = {}) {
+ if (!window.ArenaTraining) {
+ throw new Error('Arena Training not available (install Arena Training HwH Ext)');
+ }
+ return await window.ArenaTraining.getOpponents(forceRefresh, options);
+ },
+
+ arenaTrainingGetResults() {
+ if (!window.ArenaTraining) {
+ throw new Error('Arena Training not available (install Arena Training HwH Ext)');
+ }
+ return window.ArenaTraining.getResults();
+ },
+
+ arenaTrainingExportResults() {
+ if (!window.ArenaTraining) {
+ throw new Error('Arena Training not available (install Arena Training HwH Ext)');
+ }
+ return window.ArenaTraining.exportResults();
+ },
+
+ arenaTrainingGetStatus() {
+ if (!window.ArenaTraining) {
+ return { available: false, running: false };
+ }
+ return { available: true, ...window.ArenaTraining.getStatus() };
+ },
+
+ arenaTrainingStop() {
+ if (!window.ArenaTraining) {
+ throw new Error('Arena Training not available (install Arena Training HwH Ext)');
+ }
+ return window.ArenaTraining.stop();
+ },
+
+ arenaTrainingStartLoop(options = {}) {
+ if (!window.ArenaTraining) {
+ throw new Error('Arena Training not available (install Arena Training HwH Ext)');
+ }
+ return window.ArenaTraining.startLoop(options);
+ },
+
+ arenaTrainingStopLoop() {
+ if (!window.ArenaTraining) {
+ throw new Error('Arena Training not available (install Arena Training HwH Ext)');
+ }
+ return window.ArenaTraining.stopLoop();
+ },
+
+ arenaTrainingGetLoopHistory() {
+ if (!window.ArenaTraining) {
+ throw new Error('Arena Training not available (install Arena Training HwH Ext)');
+ }
+ return window.ArenaTraining.getLoopHistory();
+ },
+
+ // ========== UTILITY FUNCTIONS ==========
+
+ /**
+ * Translate a key
+ * @param {string} key - Translation key
+ * @returns {string} Translated text
+ */
+ translate(key) {
+ return cheats.translate(key);
+ },
+
+ /**
+ * Get library data
+ * @param {string} id - Library data ID (e.g., 'hero', 'titan', 'mission')
+ * @returns {Object} Library data
+ */
+ getLibraryData(id) {
+ return lib.getData(id);
+ },
+
+ /**
+ * Set progress message
+ * @param {string} message - Progress message
+ * @param {boolean} autoHide - Auto-hide after timeout
+ */
+ setProgress(message, autoHide = true) {
+ HWHFuncs.setProgress(message, autoHide);
+ },
+
+ /**
+ * Show popup confirmation
+ * @param {string} message - Popup message
+ * @param {Array} buttons - Button configurations
+ * @returns {Promise} User selection
+ */
+ async showPopup(message, buttons) {
+ return await HWHFuncs.popup.confirm(message, buttons);
+ },
+
+ // ========== BATCH OPERATIONS ==========
+
+ /**
+ * Execute multiple operations in sequence
+ * @param {Array} operations - Array of operation names
+ * @returns {Promise} Results array
+ */
+ async executeBatch(operations) {
+ const results = [];
+ for (const op of operations) {
+ try {
+ const result = await this.executeOperation(op);
+ results.push({ operation: op, success: true, result });
+ } catch (error) {
+ results.push({ operation: op, success: false, error: error.message });
+ }
+ }
+ return results;
+ },
+
+ /**
+ * Execute a single operation by name
+ * @param {string} operationName - Name of operation
+ * @param {Object} params - Optional parameters
+ * @returns {Promise} Operation result
+ */
+ async executeOperation(operationName, params = {}) {
+ const operations = {
+ 'outland': () => this.executeOutland(),
+ 'tower': () => this.executeTower(),
+ 'dungeon': () => this.executeDungeon(params.maxTitanite),
+ 'arena': () => this.executeArena('arena'),
+ 'grandarena': () => this.executeArena('grand'),
+ 'expeditions': () => this.executeExpeditions(),
+ 'quests': () => this.collectQuestRewards(),
+ 'mail': () => this.collectMail(),
+ 'dailybonus': () => this.getDailyBonus(),
+ 'seer': () => this.executeSeer(),
+ };
+
+ if (operations[operationName.toLowerCase()]) {
+ return await operations[operationName.toLowerCase()]();
+ } else {
+ throw new Error(`Unknown operation: ${operationName}`);
+ }
+ },
+
+ // ========== INFORMATION ==========
+
+ /**
+ * Get available operations list
+ * @returns {Array} List of available operations
+ */
+ getAvailableOperations() {
+ return [
+ 'outland',
+ 'tower',
+ 'dungeon',
+ 'arena',
+ 'grandArena',
+ 'expeditions',
+ 'quests',
+ 'mail',
+ 'dailyBonus',
+ 'seer',
+ 'arenaBattle',
+ 'grandArenaBattle'
+ ];
+ },
+
+ /**
+ * Run any public LLMHWH method by name (used by localhost bridge)
+ * @param {string} method
+ * @param {Array} args
+ * @returns {Promise<*>}
+ */
+ async runCommand(method, args = []) {
+ if (method === 'getBridgeStatus') {
+ return this.getBridgeStatus ? this.getBridgeStatus() : { connected: false };
+ }
+ if (typeof this[method] !== 'function') {
+ throw new Error(`Unknown method: ${method}`);
+ }
+ return await this[method](...args);
+ },
+
+ /**
+ * Get API documentation
+ * @returns {Object} API documentation
+ */
+ getDocumentation() {
+ return {
+ version: EXTENSION_VERSION,
+ description: 'LLM API Interface for HeroWarsHelper',
+ operations: {
+ sendAPI: 'Send any API request directly',
+ getUserInfo: 'Get current user information',
+ getHeroes: 'Get all hero data',
+ getTitans: 'Get all titan data',
+ getInventory: 'Get inventory data',
+ getQuests: 'Get all quests',
+ executeOutland: 'Execute Outland (boss raids)',
+ executeTower: 'Execute Tower of Elements',
+ executeDungeon: 'Execute Dungeon (with optional maxTitanite param)',
+ executeArena: 'Execute Arena (requires AutoBattle ext)',
+ executeExpeditions: 'Execute Expeditions',
+ collectQuestRewards: 'Collect all completed quest rewards',
+ collectMail: 'Collect all mail',
+ getDailyBonus: 'Get daily bonus',
+ executeSeer: 'Execute Seer (Ascension Chest)',
+ executeArenaBattle: 'Execute Arena battle (requires team param)',
+ executeGrandArenaBattle: 'Execute Grand Arena battle (requires teams param)',
+ executeBatch: 'Execute multiple operations in sequence',
+ executeOperation: 'Execute operation by name',
+ runCommand: 'Run any API method by name (bridge)',
+ getBridgeStatus: 'Localhost bridge connection status',
+ startApiRecording: 'Start recording game API calls (UI + scripts)',
+ stopApiRecording: 'Stop API recording',
+ getApiRecording: 'Get recorded API calls (optional sinceId)',
+ clearApiRecording: 'Clear recorded API calls',
+ exportApiRecording: 'Export full recording snapshot',
+ getApiRecordingStatus: 'API recording status',
+ arenaTrainingRun: 'Test hero combos vs arena opponent (demo battles)',
+ arenaTrainingGetOpponents: 'List arena opponents (topGet arena list by default)',
+ arenaTrainingGetResults: 'Get latest arena training results',
+ arenaTrainingExportResults: 'Export latest arena training results',
+ arenaTrainingGetStatus: 'Arena training run status',
+ arenaTrainingStop: 'Stop arena training run',
+ arenaTrainingStartLoop: 'Start loop training (auto-saves each round)',
+ arenaTrainingStopLoop: 'Stop loop training',
+ arenaTrainingGetLoopHistory: 'Get in-browser loop session history',
+ translate: 'Translate a key to text',
+ getLibraryData: 'Get library data by ID',
+ setProgress: 'Set progress message',
+ showPopup: 'Show popup confirmation',
+ getAvailableOperations: 'Get list of available operations'
+ }
+ };
+ }
+ };
+ }
+
+ function openLLMInterface() {
+ const { HWHFuncs } = window;
+ const api = window.LLMHWH;
+
+ if (!api) {
+ HWHFuncs.setProgress('LLM API not initialized', true);
+ return;
+ }
+
+ const doc = api.getDocumentation();
+ const operations = api.getAvailableOperations();
+
+ const content = document.createElement('div');
+ content.style.cssText = 'padding: 20px; color: #fce1ac; font-family: monospace; max-width: 800px;';
+ content.innerHTML = `
+
+ LLM API Interface
+
+
+
Available Operations:
+
+ ${operations.map(op => `• ${op} `).join('')}
+
+
+
+
Usage Examples:
+
+// In browser console:
+await LLMHWH.executeOutland();
+await LLMHWH.collectQuestRewards();
+await LLMHWH.executeBatch(['outland', 'quests', 'mail']);
+
+// Get data:
+const userInfo = await LLMHWH.getUserInfo();
+const heroes = await LLMHWH.getHeroes();
+
+// Custom API call:
+await LLMHWH.sendAPI({
+ calls: [{
+ name: "userGetInfo",
+ args: {},
+ ident: "body"
+ }]
+});
+
+// Cursor bridge (run llm-bridge-server.mjs first):
+// curl -X POST http://127.0.0.1:9876/run -H "Content-Type: application/json" -d "{\"method\":\"getUserInfo\",\"args\":[]}"
+
+
+
+
API is available globally as: window.LLMHWH
+
All functions return Promises and can be used with async/await.
+
+ `;
+
+ HWHFuncs.popup.confirm('LLM API Documentation', [
+ { msg: 'Close', result: true, isClose: true }
+ ]).then(() => {
+ const popupBody = document.querySelector('.PopUp_Container');
+ if (popupBody) {
+ popupBody.innerHTML = '';
+ popupBody.appendChild(content);
+ }
+ });
+ }
+
+})();
+
diff --git a/QUEST_FARM_API_DOCUMENTATION.md b/QUEST_FARM_API_DOCUMENTATION.md
new file mode 100644
index 0000000..1ff31cf
--- /dev/null
+++ b/QUEST_FARM_API_DOCUMENTATION.md
@@ -0,0 +1,452 @@
+# Quest Farm API Documentation
+
+## Overview
+
+The Quest Farm API allows you to collect rewards from completed quests in Hero Wars. This documentation is based on actual API calls captured from the game.
+
+## Base URL
+
+```
+https://heroes-wb.nextersglobal.com/api/
+```
+
+## Authentication Headers
+
+All requests require the following authentication headers:
+
+- `x-auth-application-id`: Application ID (typically `3`)
+- `x-auth-network-ident`: Network identifier (typically `web`)
+- `x-auth-player-id`: Player ID
+- `x-auth-session-id`: Session ID
+- `x-auth-session-key`: Session key (may be empty)
+- `x-auth-signature`: Request signature
+- `x-auth-token`: Authentication token
+- `x-auth-user-id`: User ID
+- `x-env-library-version`: Library version (typically `1`)
+- `x-env-unique-session-id`: Unique session ID
+- `x-env-unique-session-uuid`: Unique session UUID
+- `x-full-referer`: Full referer URL
+- `x-request-id`: Request ID
+- `x-requested-with`: `XMLHttpRequest`
+- `x-server-time`: Server time (typically `0`)
+
+## API Endpoints
+
+### questFarm
+
+Collects rewards from a single completed quest.
+
+**Request:**
+
+```javascript
+{
+ "calls": [
+ {
+ "name": "questFarm",
+ "args": {
+ "questId": 1795404150
+ },
+ "context": {
+ "actionTs": 257707
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+**Request Parameters:**
+
+- `name` (string, required): Must be `"questFarm"`
+- `args` (object, required):
+ - `questId` (number, required): The ID of the quest to collect rewards from
+- `context` (object, optional):
+ - `actionTs` (number, optional): Timestamp of the action
+- `ident` (string, required): Identifier for the request (typically `"body"`)
+
+**Response Structure:**
+
+```javascript
+{
+ "date": 1765906403.3287449,
+ "results": [
+ {
+ "ident": "body",
+ "result": {
+ "response": {
+ "consumable": {
+ "20": 1500 // Item ID: Quantity
+ },
+ "powerTournamentCoins": 250
+ },
+ "quests": [
+ {
+ "id": "1795404200",
+ "state": 2, // 2 = completed, ready to collect
+ "progress": 5,
+ "reward": {
+ "powerTournamentCoins": 100
+ },
+ "createTime": 1765768575,
+ "farmCount": 0
+ },
+ {
+ "id": "1795404201",
+ "state": 1, // 1 = in progress
+ "progress": 5,
+ "reward": {
+ "powerTournamentCoins": 150
+ },
+ "createTime": 1765768575,
+ "farmCount": 0
+ }
+ // ... more quests
+ ]
+ }
+ }
+ ]
+}
+```
+
+**Response Fields:**
+
+- `date` (number): Server timestamp
+- `results` (array): Array of result objects
+ - `ident` (string): Matches the request `ident`
+ - `result.response` (object): The actual response data
+ - `consumable` (object, optional): Consumable items received (item ID as key, quantity as value)
+ - `powerTournamentCoins` (number, optional): Power tournament coins received
+ - `coin` (object, optional): Coins received (coin type ID as key, quantity as value)
+ - `quests` (array, optional): List of quests that were updated/unlocked as a result of collecting this reward
+ - `id` (string): Quest ID
+ - `state` (number): Quest state
+ - `0` = Not started
+ - `1` = In progress
+ - `2` = Completed (ready to collect)
+ - `progress` (number): Current progress value
+ - `reward` (object): Reward structure for this quest
+ - `createTime` (number): Timestamp when quest was created
+ - `farmCount` (number): Number of times this quest has been farmed
+
+**Example Usage:**
+
+```javascript
+// Single quest farm
+const response = await Send(JSON.stringify({
+ calls: [{
+ name: "questFarm",
+ args: {
+ questId: 1795404150
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+}));
+
+const rewards = response.results[0].result.response;
+console.log("Received rewards:", rewards);
+console.log("New/updated quests:", rewards.quests);
+```
+
+---
+
+### quest_questsFarm
+
+Collects rewards from multiple quests in a single batch request. This is more efficient than calling `questFarm` multiple times.
+
+**Request:**
+
+```javascript
+{
+ "calls": [
+ {
+ "name": "quest_questsFarm",
+ "args": {
+ "questIds": [1795404200, 1795404201, 1795404202]
+ },
+ "context": {
+ "actionTs": Date.now()
+ },
+ "ident": "body"
+ }
+ ]
+}
+```
+
+**Request Parameters:**
+
+- `name` (string, required): Must be `"quest_questsFarm"`
+- `args` (object, required):
+ - `questIds` (array of numbers, required): Array of quest IDs to collect rewards from
+- `context` (object, optional):
+ - `actionTs` (number, optional): Timestamp of the action
+- `ident` (string, required): Identifier for the request
+
+**Response Structure:**
+
+Similar to `questFarm`, but may contain rewards from multiple quests and multiple quest updates.
+
+**Example Usage:**
+
+```javascript
+// Batch quest farm
+const questIds = [1795404200, 1795404201, 1795404202];
+const response = await Send(JSON.stringify({
+ calls: [{
+ name: "quest_questsFarm",
+ args: {
+ questIds: questIds
+ },
+ context: {
+ actionTs: Date.now()
+ },
+ ident: "body"
+ }]
+}));
+```
+
+---
+
+## Quest States
+
+- `0`: Not started - Quest has not been started yet
+- `1`: In progress - Quest is active and being worked on
+- `2`: Completed - Quest is complete and ready to collect rewards
+
+**Important:** Only quests with `state === 2` can have their rewards collected.
+
+---
+
+## How the Script Farms Quest Rewards
+
+The HeroWarsHelper script uses several strategies to efficiently farm quest rewards:
+
+### 1. Simple Quest Farm (`questAllFarm`)
+
+```javascript
+function questAllFarm() {
+ // Get all quests
+ const quests = await Send({
+ calls: [{ name: "questGetAll", args: {}, ident: "body" }]
+ });
+
+ // Filter completed quests (state === 2) and regular quests (id < 1e6)
+ const completedQuests = quests.results[0].result.response.filter(
+ q => q.id < 1e6 && q.state === 2
+ );
+
+ // Collect all rewards
+ const calls = completedQuests.map((quest, index) => ({
+ name: "questFarm",
+ args: { questId: quest.id },
+ ident: `group_${index}_body`
+ }));
+
+ await Send({ calls });
+}
+```
+
+### 2. Advanced Quest Farm (`rewardsAndMailFarm`)
+
+The more sophisticated `rewardsAndMailFarm` function:
+
+1. **Fetches multiple data sources:**
+ - `questGetAll` - All quests
+ - `mailGetAll` - Mail letters
+ - `specialOffer_getAll` - Special offers
+ - `battlePass_getInfo` - Battle pass info
+ - `battlePass_getSpecial` - Special battle passes
+
+2. **Filters quests by type:**
+ - Regular daily quests: `id < 1e6` and `state === 2`
+ - Battle pass quests: Checks battle pass requirements (ticket, level, date)
+ - Special quests: `id >= 2e7 && id < 14e8` (uses batch farming)
+ - Excludes certain quest ranges: `id >= 2001e4 && id < 14e8`
+
+3. **Uses batch farming for special quests:**
+ ```javascript
+ if (questId >= 2e7 && questId < 14e8) {
+ questIds.push(questId); // Collect for batch
+ continue;
+ }
+
+ // Later, batch farm them
+ if (questIds.length) {
+ farmCaller.add({
+ name: 'quest_questsFarm',
+ args: { questIds },
+ });
+ }
+ ```
+
+4. **Recursive collection:**
+ - After collecting rewards, checks for newly unlocked quests
+ - Continues collecting until no more quests are available
+ - Prevents infinite loops by tracking already-farmed quest IDs
+
+5. **Quest filtering logic:**
+ ```javascript
+ // Skip certain quest ranges
+ if (questId >= 2001e4 && questId < 14e8) {
+ continue;
+ }
+
+ // Handle battle pass quests with special requirements
+ if (quest.reward?.battlePassExp && !specialQuests[questId]) {
+ // Check battle pass ticket, level, and date requirements
+ if (chain.requirement?.battlePassTicket && !battlePass.ticket) {
+ continue; // Skip if ticket required but not owned
+ }
+ if (chain.requirement?.battlePassLevel && battlePass.level < chain.requirement.battlePassLevel) {
+ continue; // Skip if level requirement not met
+ }
+ // Check date range
+ if (startTime > currentTime || endTime < currentTime) {
+ continue; // Skip if outside date range
+ }
+ }
+ ```
+
+### 3. Quest ID Ranges
+
+The script categorizes quests by ID ranges:
+
+- **Regular daily quests:** `id < 1e6` (1,000,000)
+ - Collected individually using `questFarm`
+
+- **Special quests (batch):** `id >= 2e7 && id < 2001e4` (20,000,000 to 20,010,000)
+ - Collected using `quest_questsFarm` batch API
+
+- **Excluded quests:** `id >= 2001e4 && id < 14e8` (20,010,000 to 140,000,000)
+ - Skipped entirely
+
+- **Other special quests:** `id >= 2e7 && id < 14e8` (20,000,000 to 140,000,000)
+ - May be collected individually or in batches depending on context
+
+### 4. Recursive Collection Pattern
+
+```javascript
+// Initial collection
+const farmResults = await farmCaller.send();
+
+// Extract newly unlocked quests from side results
+const sideResult = farmResults.sideResult('questFarm', true);
+sideResult.push(...farmResults.sideResult('quest_questsFarm', true));
+
+let questsIds = [];
+for (let side of sideResult) {
+ const quests = [...(side.newQuests ?? []), ...(side.quests ?? [])];
+ for (let quest of quests) {
+ if ((quest.id < 1e6 || (quest.id >= 2e7 && quest.id < 2001e4)) && quest.state == 2) {
+ questsIds.push(quest.id);
+ }
+ }
+}
+
+// Recursively collect newly unlocked quests
+while (questsIds.length) {
+ const recursiveCaller = new Caller();
+ // ... collect new quests ...
+ await recursiveCaller.send();
+ // ... check for more new quests ...
+}
+```
+
+---
+
+## Best Practices
+
+1. **Always check quest state:** Only collect rewards from quests with `state === 2`
+
+2. **Use batch farming for multiple quests:** Use `quest_questsFarm` when collecting multiple quests to reduce API calls
+
+3. **Handle recursive unlocks:** After collecting rewards, check for newly unlocked quests in the response
+
+4. **Filter by quest ID ranges:** Different quest types have different ID ranges and may require different handling
+
+5. **Respect battle pass requirements:** For battle pass quests, verify ticket ownership, level requirements, and date ranges before collecting
+
+6. **Track farmed quests:** Keep a list of already-farmed quest IDs to prevent duplicate collections
+
+---
+
+## Error Handling
+
+The API may return errors in the following cases:
+
+- Quest not found
+- Quest not completed (state !== 2)
+- Quest already collected
+- Invalid quest ID
+- Authentication failure
+
+Always check the response for error conditions before processing rewards.
+
+---
+
+## Response Processing in HeroWarsHelper
+
+The script processes quest farm responses to track special items:
+
+### Prediction Cards Tracking
+
+When `questFarm` returns consumable item ID `81` (prediction cards), the script tracks the count:
+
+```javascript
+if (call.ident == callsIdent['questFarm']) {
+ const consumable = call.result.response?.consumable;
+ if (consumable && consumable[81]) {
+ HWHData.countPredictionCard += consumable[81];
+ console.log(`Cards: ${HWHData.countPredictionCard}`);
+ }
+}
+```
+
+### Batch Quest Farm Processing
+
+For `quest_questsFarm`, the script processes multiple rewards:
+
+```javascript
+if (call.ident == callsIdent['quest_questsFarm']) {
+ const rewards = call.result.response;
+ for (const reward of rewards) {
+ if (reward.consumable?.[81]) {
+ HWHData.countPredictionCard += reward.consumable[81];
+ }
+ if (reward.refillable?.[45]) {
+ setPortals(+reward.refillable[45], true); // Portal spheres
+ }
+ }
+}
+```
+
+### Side Results and New Quests
+
+The script uses the `Caller` class's `sideResult()` method to extract newly unlocked quests:
+
+```javascript
+const sideResult = farmResults.sideResult('questFarm', true);
+sideResult.push(...farmResults.sideResult('quest_questsFarm', true));
+
+for (let side of sideResult) {
+ const quests = [...(side.newQuests ?? []), ...(side.quests ?? [])];
+ // Process newly unlocked quests
+}
+```
+
+The `sideResult()` method extracts data from the `side` field of API responses, which contains additional information like newly created quests.
+
+---
+
+## Notes
+
+- The `actionTs` in context is optional but recommended for proper timestamp tracking
+- The `ident` field can be any string and is used to match requests with responses
+- Quest rewards may unlock new quests, so always check the `quests` array in the response
+- Some quests may have dependencies that prevent collection until prerequisites are met
+- The script uses the `Caller` class for efficient batch API calls with error handling
+- The response may include a `side` field with additional data like `newQuests` and `quests` arrays
+- Special items like prediction cards (ID 81) and portal spheres (ID 45) are tracked automatically
+
diff --git a/README_ARENA.md b/README_ARENA.md
new file mode 100644
index 0000000..9f91ddf
--- /dev/null
+++ b/README_ARENA.md
@@ -0,0 +1,137 @@
+# Arena Auto-Attack Feature
+
+## Quick Start
+
+The Arena Auto-Attack feature automatically battles in Arena and Grand Arena with intelligent team selection and opponent targeting.
+
+### How to Use
+
+1. **Manual Execution**:
+ - Click "Arena" button for individual arena battles
+ - Click "Grand Arena" button for individual grand arena battles
+ - Click "Auto Arena & Grand Arena" for both arenas
+
+2. **Auto-Run Integration**:
+ - Enable "Auto Arena & Grand Arena" in the "Do All" function
+ - The script will automatically run arena battles when the page loads
+
+## Features
+
+### 🎯 Smart Team Selection
+- Tries your current arena team first
+- If win rate < 50%, tests alternative teams (top 5 heroes by power)
+- Only attacks opponents with >30% win probability
+- Pre-calculates win rates before attacking
+
+### 🎮 Intelligent Opponent Selection
+- Sorts opponents by difficulty (power ratio + rank)
+- Targets easiest opponents first
+- Uses all daily attempts efficiently
+- Focuses on highest win probability matches
+
+### 🔧 Battle Flow
+1. Get arena status and available attempts
+2. Load your team data and heroes
+3. Sort opponents by difficulty (easiest first)
+4. For each attempt:
+ - Select easiest unbeaten opponent
+ - Try current team in simulation
+ - If losing, try alternative teams
+ - Start battle with best team
+ - Calculate and complete battle
+5. Report total victories achieved
+
+## Configuration
+
+### Team Selection Strategy
+- **Current Team First**: Uses your default arena/grand arena team
+- **Alternative Teams**: Top 5 heroes by power if current team fails
+- **Win Rate Threshold**: Minimum 30% win probability to attack
+- **Skip Unwinnable**: Avoid opponents with no winning team
+
+### Opponent Selection Strategy
+- **Power Ratio**: Calculate `opponent.power / your.power`
+- **Rank Priority**: Lower rank = easier opponent
+- **Difficulty Score**: `powerRatio + (rank / 1000000)`
+- **Sort Order**: Easiest opponents first
+
+## API Integration
+
+The feature integrates with Hero Wars' battle system using these API calls:
+
+- `arenaGetInfo` / `grandGetInfo` - Get arena status and opponents
+- `arenaStartBattle` / `grandStartBattle` - Start battles
+- `arenaEndBattle` / `grandEndBattle` - Complete battles
+- `teamGetAll`, `teamGetFavor`, `heroGetAll` - Team data
+
+## Error Handling
+
+- **API Failures**: Graceful error handling with console logging
+- **No Attempts**: Skip execution if no attempts remaining
+- **Battle Errors**: Continue with next opponent on failure
+- **Team Selection**: Skip opponent if no winning team found
+
+## Performance
+
+- **Battle Simulation**: Pre-calculates win rates to avoid losses
+- **Team Caching**: Reuses team data across battles
+- **Progress Updates**: Real-time status updates for user feedback
+- **Memory Management**: Proper cleanup of battle data
+
+## Troubleshooting
+
+### Common Issues
+
+1. **No Battles Executed**:
+ - Check if you have arena attempts remaining
+ - Verify your team is properly configured
+ - Check console for error messages
+
+2. **Low Win Rate**:
+ - The system only attacks opponents with >30% win probability
+ - Try upgrading your heroes or team composition
+ - Check if opponents are too strong for your current level
+
+3. **Script Not Running**:
+ - Ensure the feature is enabled in "Do All" function
+ - Check if auto-run is enabled
+ - Verify the script is properly loaded
+
+### Debug Information
+
+The script provides detailed console logging:
+- Opponent difficulty calculations
+- Team selection decisions
+- Battle win rate predictions
+- Victory/defeat results
+
+## Advanced Usage
+
+### Manual Team Selection
+The system automatically selects teams, but you can influence the selection by:
+- Configuring your default arena team
+- Upgrading your top 5 heroes by power
+- Ensuring your team has good synergy
+
+### Opponent Targeting
+The system targets easiest opponents first, but you can influence this by:
+- Checking opponent power levels
+- Understanding rank vs. power relationships
+- Monitoring your own team's power progression
+
+## Future Enhancements
+
+- **Counter Team Database**: Pre-defined counter strategies
+- **Hero Synergy Analysis**: Team composition optimization
+- **Battle History**: Track win/loss patterns
+- **Advanced Targeting**: More sophisticated opponent selection
+
+## Support
+
+For issues or questions about the Arena Auto-Attack feature:
+1. Check the console for error messages
+2. Verify your team configuration
+3. Ensure you have arena attempts remaining
+4. Check if the feature is properly enabled
+
+The feature is designed to maximize your daily arena victories while minimizing manual effort.
diff --git a/Secret Wealth Shop HwH Ext.user.js b/Secret Wealth Shop HwH Ext.user.js
new file mode 100644
index 0000000..9242c7c
--- /dev/null
+++ b/Secret Wealth Shop HwH Ext.user.js
@@ -0,0 +1,432 @@
+// ==UserScript==
+// @name Secret Wealth Shop HwH Ext
+// @namespace HeroWarsHelper.SecretWealthShop
+// @version 1.2
+// @description Manual purchase interface for Secret Wealth Shop with consumable and GEM payment options
+// @author YourName
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @grant none
+// @run-at document-end
+// @downloadURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/Secret%20Wealth%20Shop%20HwH%20Ext.user.js
+// @updateURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/Secret%20Wealth%20Shop%20HwH%20Ext.user.js
+// ==/UserScript==
+
+(function() {
+ 'use strict';
+
+ const waitForHWH = setInterval(() => {
+ if (window.HWHClasses && window.HWHClasses.ScriptMenu && window.lib && window.cheats) {
+ const scriptMenu = window.HWHClasses.ScriptMenu.getInst();
+ if (scriptMenu && scriptMenu.mainMenu) {
+ clearInterval(waitForHWH);
+ initializeExtension();
+ }
+ }
+ }, 200);
+
+ function initializeExtension() {
+ console.log('Secret Wealth Shop: HWH UI is ready, initializing extension...');
+
+ const { HWHClasses, HWHFuncs, Send, cheats, Caller, lib } = window;
+ const SECRET_WEALTH_SHOP_ID = 1576000026; // Base shop ID (may change per instance)
+
+ // Helper function to translate consumable/reward names
+ function getItemName(reward) {
+ if (!reward) return 'Unknown';
+
+ const rewardType = Object.keys(reward)[0];
+ const rewardData = reward[rewardType];
+
+ if (rewardType === 'consumable') {
+ const consumableId = Object.keys(rewardData)[0];
+ const translationKey = `LIB_CONSUMABLE_NAME_${consumableId}`;
+ const translatedName = cheats.translate(translationKey);
+ const amount = rewardData[consumableId];
+ return translatedName ? `${translatedName} x${amount}` : `Consumable ${consumableId} x${amount}`;
+ } else if (rewardType === 'fragment') {
+ const fragmentType = Object.keys(rewardData)[0];
+ const fragmentId = Object.keys(rewardData[fragmentType])[0];
+ const libTypeForTranslate = fragmentType.toUpperCase();
+ const translationKey = `LIB_${libTypeForTranslate}_NAME_${fragmentId}`;
+ const translatedName = cheats.translate(translationKey);
+ const amount = rewardData[fragmentType][fragmentId];
+ return translatedName ? `${translatedName} x${amount}` : `${fragmentType} ${fragmentId} x${amount}`;
+ }
+
+ return JSON.stringify(reward);
+ }
+
+ // Helper function to get cost description
+ function getCostDescription(cost) {
+ if (!cost) return 'Unknown';
+
+ if (cost.starmoney) {
+ return `${cost.starmoney} GEMs`;
+ } else if (cost.consumable) {
+ const consumableId = Object.keys(cost.consumable)[0];
+ const amount = cost.consumable[consumableId];
+ const translationKey = `LIB_CONSUMABLE_NAME_${consumableId}`;
+ const translatedName = cheats.translate(translationKey);
+ const name = translatedName || `Consumable ${consumableId}`;
+ return `${amount} ${name}`;
+ } else if (cost.gold) {
+ return `${cost.gold} Gold`;
+ } else if (cost.coin) {
+ return `${cost.coin} Coins`;
+ }
+
+ return JSON.stringify(cost);
+ }
+
+ // Auto-purchase function for slot 6 - uses actual shop data, always attempts purchase
+ async function autoPurchaseSlot6() {
+ try {
+ console.log('Secret Wealth Shop: Auto-purchasing slot 6 on script load...');
+ HWHFuncs.setProgress('Auto-purchasing slot 6...');
+
+ // Fetch shop data to get actual slot 6 information
+ const caller = new Caller(['shopGetAll']);
+ await caller.send();
+ const shopsData = caller.result('shopGetAll');
+
+ // Find Secret Wealth Shop
+ let secretWealthShop = null;
+ let actualShopId = null;
+
+ // First try the known ID
+ if (shopsData[SECRET_WEALTH_SHOP_ID]) {
+ secretWealthShop = shopsData[SECRET_WEALTH_SHOP_ID];
+ actualShopId = SECRET_WEALTH_SHOP_ID;
+ } else {
+ // Search for shop with slots that have consumable or starmoney costs
+ for (const shopId in shopsData) {
+ const shop = shopsData[shopId];
+ if (shop && shop.slots) {
+ const slots = shop.slots;
+ for (const slotId in slots) {
+ const slot = slots[slotId];
+ if (slot.cost && (slot.cost.consumable || slot.cost.starmoney)) {
+ secretWealthShop = shop;
+ actualShopId = parseInt(shopId);
+ break;
+ }
+ }
+ if (secretWealthShop) break;
+ }
+ }
+ }
+
+ if (!secretWealthShop || !secretWealthShop.slots || !secretWealthShop.slots[6]) {
+ const errorMsg = 'Secret Wealth Shop: Slot 6 not found in shop data.';
+ console.error(errorMsg);
+ HWHFuncs.setProgress(`Auto-purchase error: ${errorMsg}`, true);
+ return false;
+ }
+
+ const slot6 = secretWealthShop.slots[6];
+
+ // Determine payment type based on available cost
+ let paymentType = 'Unknown';
+ if (slot6.cost && slot6.cost.consumable) {
+ paymentType = 'Consumable';
+ } else if (slot6.cost && slot6.cost.starmoney) {
+ paymentType = 'GEMs';
+ }
+
+ const itemName = getItemName(slot6.reward);
+ console.log(`Secret Wealth Shop: Attempting to purchase slot 6 - ${itemName}`);
+
+ // Always attempt purchase (no check for already bought)
+ const success = await purchaseItem(actualShopId, 6, slot6.cost, slot6.reward, paymentType);
+
+ if (success) {
+ console.log('%cSecret Wealth Shop: Auto-purchase successful!', 'color: lightgreen; font-weight: bold;');
+ HWHFuncs.setProgress('Auto-purchase complete: Slot 6 purchased!', true);
+ } else {
+ // Error is already shown by purchaseItem function
+ console.log('Secret Wealth Shop: Auto-purchase failed - check error message above.');
+ }
+
+ return success;
+ } catch (error) {
+ const errorMsg = `Auto-purchase error: ${error.message || error}`;
+ console.error('Secret Wealth Shop: Auto-purchase error:', error);
+ HWHFuncs.setProgress(errorMsg, true);
+ return false;
+ }
+ }
+
+ // Function to purchase an item
+ async function purchaseItem(shopId, slot, cost, reward, paymentType) {
+ try {
+ HWHFuncs.setProgress(`Purchasing slot ${slot} with ${paymentType}...`);
+
+ // Generate action timestamp
+ const actionTs = Date.now() % 1000000; // Use milliseconds modulo for actionTs
+
+ const call = {
+ name: 'shopBuy',
+ args: {
+ shopId: shopId,
+ slot: slot,
+ cost: cost,
+ reward: reward
+ }
+ };
+
+ const caller = new Caller([call]);
+ await caller.send();
+
+ const result = caller.result('shopBuy');
+ if (result && result.response) {
+ const itemName = getItemName(reward);
+ const costDesc = getCostDescription(cost);
+ console.log(`%c✓ Successfully purchased: ${itemName} for ${costDesc}`, 'color: lightgreen; font-weight: bold;');
+ HWHFuncs.setProgress(`Purchase successful: ${itemName}`, true);
+ return true;
+ } else {
+ const error = result?.error || 'Unknown error';
+ console.error(`Purchase failed:`, error);
+ HWHFuncs.setProgress(`Purchase failed: ${error}`, true);
+ return false;
+ }
+ } catch (error) {
+ console.error("Purchase Error:", error);
+ HWHFuncs.setProgress(`Purchase Error: ${error.message}`, true);
+ return false;
+ }
+ }
+
+ // Function to fetch and display shop data
+ async function openShopInterface() {
+ const popupContent = document.createElement('div');
+ popupContent.style.cssText = 'display: flex; flex-direction: column; height: 70vh; color: #fce1ac; text-shadow: 0 0 2px black;';
+
+ const headerContainer = document.createElement('div');
+ headerContainer.style.cssText = 'display: flex; justify-content: space-between; align-items: center; border-bottom: 2px solid #ce9767; margin-bottom: 10px; padding-bottom: 5px;';
+
+ const title = document.createElement('h2');
+ title.textContent = 'Secret Wealth Shop';
+ title.style.cssText = 'margin: 0; color: #ffcc66;';
+ headerContainer.appendChild(title);
+
+ const refreshBtn = document.createElement('button');
+ refreshBtn.textContent = '🔄 Refresh';
+ refreshBtn.style.cssText = 'padding: 6px 12px; border: 1px solid #ce9767; background: #3a2e24; color: #fce1ac; cursor: pointer;';
+ refreshBtn.onclick = () => {
+ popupContent.innerHTML = '';
+ popupContent.appendChild(headerContainer);
+ openShopInterface();
+ };
+ headerContainer.appendChild(refreshBtn);
+
+ popupContent.appendChild(headerContainer);
+
+ const contentContainer = document.createElement('div');
+ contentContainer.style.cssText = 'flex-grow: 1; overflow-y: auto; padding: 10px;';
+ popupContent.appendChild(contentContainer);
+
+ // Show loading message
+ contentContainer.innerHTML = 'Loading shop data...
';
+
+ try {
+ HWHFuncs.setProgress('Fetching Secret Wealth Shop data...');
+
+ // Fetch shop data
+ const caller = new Caller(['shopGetAll']);
+ await caller.send();
+ const shopsData = caller.result('shopGetAll');
+
+ // Find Secret Wealth Shop - it might have a dynamic ID
+ let secretWealthShop = null;
+ let actualShopId = null;
+
+ // First try the known ID
+ if (shopsData[SECRET_WEALTH_SHOP_ID]) {
+ secretWealthShop = shopsData[SECRET_WEALTH_SHOP_ID];
+ actualShopId = SECRET_WEALTH_SHOP_ID;
+ } else {
+ // Search for shop with slots that have consumable or starmoney costs
+ for (const shopId in shopsData) {
+ const shop = shopsData[shopId];
+ if (shop && shop.slots) {
+ const slots = shop.slots;
+ // Check if any slot has consumable or starmoney cost (typical of Secret Wealth Shop)
+ for (const slotId in slots) {
+ const slot = slots[slotId];
+ if (slot.cost && (slot.cost.consumable || slot.cost.starmoney)) {
+ secretWealthShop = shop;
+ actualShopId = parseInt(shopId);
+ break;
+ }
+ }
+ if (secretWealthShop) break;
+ }
+ }
+ }
+
+ if (!secretWealthShop || !secretWealthShop.slots) {
+ contentContainer.innerHTML = 'Secret Wealth Shop not found or not available.
';
+ HWHFuncs.setProgress('Secret Wealth Shop not available.', true);
+ return;
+ }
+
+ // Clear loading message
+ contentContainer.innerHTML = '';
+
+ // Display shop info
+ const shopInfo = document.createElement('div');
+ shopInfo.style.cssText = 'margin-bottom: 15px; padding: 8px; background: #3a2e24; border-radius: 4px;';
+ shopInfo.innerHTML = `Shop ID: ${actualShopId}Available Slots: ${Object.keys(secretWealthShop.slots).length}`;
+ contentContainer.appendChild(shopInfo);
+
+ // Auto-purchase info (always enabled)
+ const autoPurchaseInfo = document.createElement('div');
+ autoPurchaseInfo.style.cssText = 'margin-bottom: 15px; padding: 10px; background: #2a1f18; border: 1px solid #4a7c3e; border-radius: 4px;';
+ autoPurchaseInfo.innerHTML = '✓ Auto-purchase enabled: Slot 6 will be purchased automatically on script load ';
+ contentContainer.appendChild(autoPurchaseInfo);
+
+ // Display each slot
+ const slots = secretWealthShop.slots;
+ const slotNumbers = Object.keys(slots).map(Number).sort((a, b) => a - b);
+
+ if (slotNumbers.length === 0) {
+ contentContainer.innerHTML += 'No items available in shop.
';
+ HWHFuncs.setProgress('Shop is empty.', true);
+ return;
+ }
+
+ slotNumbers.forEach(slotNum => {
+ const slot = slots[slotNum];
+ if (!slot || !slot.reward || slot.bought) return;
+
+ const slotDiv = document.createElement('div');
+ slotDiv.style.cssText = 'margin-bottom: 20px; padding: 12px; background: #2a1f18; border: 1px solid #ce9767; border-radius: 4px;';
+
+ const slotHeader = document.createElement('div');
+ slotHeader.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;';
+
+ const slotTitle = document.createElement('h3');
+ slotTitle.textContent = `Slot ${slotNum}`;
+ slotTitle.style.cssText = 'margin: 0; color: #ffcc66;';
+ slotHeader.appendChild(slotTitle);
+ slotDiv.appendChild(slotHeader);
+
+ // Reward info
+ const rewardInfo = document.createElement('div');
+ rewardInfo.style.cssText = 'margin-bottom: 10px;';
+ const rewardName = getItemName(slot.reward);
+ rewardInfo.innerHTML = `Reward: ${rewardName}`;
+ slotDiv.appendChild(rewardInfo);
+
+ // Cost info
+ const costInfo = document.createElement('div');
+ costInfo.style.cssText = 'margin-bottom: 15px;';
+ const costDesc = getCostDescription(slot.cost);
+ costInfo.innerHTML = `Cost: ${costDesc}`;
+ slotDiv.appendChild(costInfo);
+
+ // Purchase buttons container
+ const buttonContainer = document.createElement('div');
+ buttonContainer.style.cssText = 'display: flex; gap: 10px; flex-wrap: wrap;';
+
+ // Check if cost has consumable payment option
+ if (slot.cost && slot.cost.consumable) {
+ const buyConsumableBtn = document.createElement('button');
+ buyConsumableBtn.textContent = '💰 Buy with Consumable';
+ buyConsumableBtn.style.cssText = 'padding: 8px 16px; border: 1px solid #4a7c3e; background: #3a5a2e; color: #aaffaa; cursor: pointer; border-radius: 4px; font-weight: bold;';
+ buyConsumableBtn.onclick = async () => {
+ buyConsumableBtn.disabled = true;
+ buyConsumableBtn.textContent = 'Processing...';
+ const success = await purchaseItem(actualShopId, slotNum, slot.cost, slot.reward, 'Consumable');
+ if (success) {
+ // Refresh the interface
+ setTimeout(() => {
+ popupContent.innerHTML = '';
+ popupContent.appendChild(headerContainer);
+ openShopInterface();
+ }, 1000);
+ } else {
+ buyConsumableBtn.disabled = false;
+ buyConsumableBtn.textContent = '💰 Buy with Consumable';
+ }
+ };
+ buttonContainer.appendChild(buyConsumableBtn);
+ }
+
+ // Check if cost has GEM (starmoney) payment option
+ if (slot.cost && slot.cost.starmoney) {
+ const buyGemBtn = document.createElement('button');
+ buyGemBtn.textContent = '💎 Buy with GEMs';
+ buyGemBtn.style.cssText = 'padding: 8px 16px; border: 1px solid #4a5a7c; background: #3a4a6a; color: #aaaaff; cursor: pointer; border-radius: 4px; font-weight: bold;';
+ buyGemBtn.onclick = async () => {
+ buyGemBtn.disabled = true;
+ buyGemBtn.textContent = 'Processing...';
+ const success = await purchaseItem(actualShopId, slotNum, slot.cost, slot.reward, 'GEMs');
+ if (success) {
+ // Refresh the interface
+ setTimeout(() => {
+ popupContent.innerHTML = '';
+ popupContent.appendChild(headerContainer);
+ openShopInterface();
+ }, 1000);
+ } else {
+ buyGemBtn.disabled = false;
+ buyGemBtn.textContent = '💎 Buy with GEMs';
+ }
+ };
+ buttonContainer.appendChild(buyGemBtn);
+ }
+
+ // If no payment options match, show unavailable message
+ if (buttonContainer.children.length === 0) {
+ const unavailableMsg = document.createElement('div');
+ unavailableMsg.textContent = 'No supported payment method available';
+ unavailableMsg.style.cssText = 'color: #ff6666; font-style: italic;';
+ buttonContainer.appendChild(unavailableMsg);
+ }
+
+ slotDiv.appendChild(buttonContainer);
+ contentContainer.appendChild(slotDiv);
+ });
+
+ HWHFuncs.setProgress('Shop data loaded successfully.', true);
+
+ } catch (error) {
+ console.error("Shop Interface Error:", error);
+ contentContainer.innerHTML = `Error loading shop data: ${error.message}
`;
+ HWHFuncs.setProgress(`Error: ${error.message}`, true);
+ }
+
+ // Use confirm with proper async handling
+ const popupPromise = HWHFuncs.popup.confirm('', [{ msg: 'Close', result: true, isClose: true }]);
+
+ // Wait a tick for popup to initialize, then replace content
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ const popupBody = document.querySelector('.PopUp_Container');
+ if (popupBody) {
+ popupBody.innerHTML = '';
+ popupBody.appendChild(popupContent);
+ }
+
+ // Wait for popup to close before returning
+ await popupPromise;
+ }
+
+ // --- AUTO-PURCHASE ON SCRIPT LOAD ---
+ // Always attempt to purchase slot 6 when script loads
+ autoPurchaseSlot6().catch(error => {
+ console.error('Secret Wealth Shop: Failed to auto-purchase on load:', error);
+ });
+
+ // --- MENU INTEGRATION ---
+ const { ScriptMenu } = HWHClasses;
+ const scriptMenu = ScriptMenu.getInst();
+ scriptMenu.addCombinedButton([
+ { name: 'Secret Wealth Shop', title: 'Open Secret Wealth Shop interface', onClick: openShopInterface, color: 'purple' }
+ ]);
+ console.log('Secret Wealth Shop: UI initialized and attached to HWH menu.');
+ }
+})();
diff --git a/TITAN_DATA_DOCUMENTATION.md b/TITAN_DATA_DOCUMENTATION.md
new file mode 100644
index 0000000..11c3501
--- /dev/null
+++ b/TITAN_DATA_DOCUMENTATION.md
@@ -0,0 +1,226 @@
+# Titan Data Documentation
+
+This document describes the structure and properties of Titan objects used in Hero Wars Helper extensions.
+
+## Overview
+
+Titans are powerful creatures that players can use in battles. Each titan has unique stats, abilities, and belongs to one of five elements: Water, Fire, Earth, Dark, or Light.
+
+## Data Structure
+
+### Root Object
+The titan data is stored as a JSON object where each key is the titan's ID (as a string), and the value is a Titan object.
+
+```json
+{
+ "4000": { /* Titan object */ },
+ "4001": { /* Titan object */ },
+ ...
+}
+```
+
+## Titan Object Properties
+
+### Core Properties
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `id` | `number` | Unique identifier for the titan (4000-4043) |
+| `isPlayable` | `number` | Whether the titan can be used in battles (1 = yes, 0 = no) |
+| `type` | `string` | Titan combat type: `"melee"`, `"range"`, `"support"`, `"ultra"`, or `"summoner"` |
+| `element` | `string` | Elemental affinity: `"water"`, `"fire"`, `"earth"`, `"dark"`, or `"light"` |
+| `perk` | `number[]` | Array of perk IDs that define special abilities |
+| `squareIcon` | `string` | Icon identifier for UI display (format: `"titan_160_{id}"`) |
+| `artifacts` | `number[]` | Array of artifact IDs that can be equipped |
+| `roleExtended` | `string[]` or `null` | Extended role classifications (e.g., `["melee_tank"]`, `["ranged_dps"]`) |
+| `spiritArtifact` | `number` | ID of the spirit artifact associated with this element |
+| `stars` | `object` | Star level data (see Stars Object below) |
+| `obtainTypes` | `string` | JSON string array describing how to obtain the titan |
+| `role` | `string` | Battle position: `"front"`, `"middle"`, or `"back"` |
+
+### Stars Object
+
+Each titan has a `stars` object containing battle statistics for each star level. Regular titans have stars 1-6, while Ultra titans start at star 3.
+
+```json
+"stars": {
+ "1": {
+ "battleStatData": {
+ "hp": "1100",
+ "physicalAttack": "68"
+ }
+ },
+ "2": { /* ... */ },
+ ...
+}
+```
+
+**Star Level Properties:**
+- `battleStatData.hp`: Health points (as string or number)
+- `battleStatData.physicalAttack`: Physical attack damage (as string or number)
+
+## Titan Elements and IDs
+
+### Water Element (4000-4004)
+- **4000** - Sigurd (Melee Tank, Front)
+- **4001** - Nova (Range DPS, Middle)
+- **4002** - Mairi (Support, Back)
+- **4003** - Hyperion (Ultra, Back) - Starts at 3 stars
+- **4004** - Tidus and Gelo (Summoner, Middle) - Special titan
+
+### Fire Element (4010-4014)
+- **4010** - Moloch (Melee Tank, Front)
+- **4011** - Vulcan (Range DPS, Middle)
+- **4012** - Ignis (Support, Back)
+- **4013** - Araji (Ultra, Back) - Starts at 3 stars
+- **4014** - Asherona and Pyro (Summoner, Middle) - Special shop titan
+
+### Earth Element (4020-4024)
+- **4020** - Angus (Melee Tank, Front)
+- **4021** - Sylva (Range DPS, Back)
+- **4022** - Avalon (Support, Middle)
+- **4023** - Eden (Ultra, Back) - Starts at 3 stars
+- **4024** - Verdoc and Phyto (Summoner, Middle) - Special shop titan
+
+### Dark Element (4030-4033)
+- **4030** - Brustar (Melee, Front)
+- **4031** - Keros (Range, Middle)
+- **4032** - Mort (Support, Middle)
+- **4033** - Tenebris (Ultra, Back) - Starts at 3 stars
+
+### Light Element (4040-4043)
+- **4040** - Rigel (Melee, Front)
+- **4041** - Amon (Range, Middle)
+- **4042** - Iyari (Support, Back)
+- **4043** - Solaris (Ultra, Back) - Starts at 3 stars
+
+## Titan Types
+
+### Melee
+Front-line fighters with high HP and moderate attack. Examples: Sigurd (4000), Moloch (4010), Angus (4020)
+
+### Range
+Mid-to-back line damage dealers. Examples: Nova (4001), Ignis (4011), Sylva (4021)
+
+### Support
+Back-line titans that provide buffs/healing. Examples: Mairi (4002), Ignis (4012), Avalon (4022)
+
+### Ultra
+Powerful titans that start at 3 stars. Examples: Hyperion (4003), Araji (4013), Eden (4023), Tenebris (4033), Solaris (4043)
+
+### Summoner
+Special titans that can summon units. Examples: Tidus and Gelo (4004), Asherona and Pyro (4014), Verdoc and Phyto (4024)
+
+## Obtain Types
+
+The `obtainTypes` field is a JSON string array that describes how to obtain the titan:
+
+- `"[\"titan_dungeon\",\"titan_summoning_circle\"]"` - Available from dungeon and summoning
+- `"[\"titan_summoning_circle\"]"` - Only from summoning (Ultra titans)
+- `"[\"shop:invasion:1080\"]"` - Special shop purchase (Solaris)
+- `"[\"shop:invasion:1075\"]"` - Special shop purchase (Keros)
+
+## Role Extended Classifications
+
+Extended roles provide more specific classifications:
+
+- `"melee_tank"` - Front-line tank titans
+- `"ranged_dps"` - Ranged damage dealers
+- `"support"` - Support/healing titans
+- `"mage"` - Magic-based titans
+- `"summoner"` - Summoner titans
+
+## Usage in Code
+
+### Accessing Titan Data
+
+```javascript
+// Assuming titanData is the loaded JSON object
+const sigurd = titanData["4000"];
+console.log(sigurd.element); // "water"
+console.log(sigurd.type); // "melee"
+console.log(sigurd.stars["1"].battleStatData.hp); // "1100"
+```
+
+### Filtering by Element
+
+```javascript
+// Get all water titans
+const waterTitans = Object.values(titanData).filter(t => t.element === "water");
+
+// Get all fire titans
+const fireTitans = Object.values(titanData).filter(t => t.element === "fire");
+```
+
+### Filtering by Type
+
+```javascript
+// Get all melee titans
+const meleeTitans = Object.values(titanData).filter(t => t.type === "melee");
+
+// Get all ultra titans
+const ultraTitans = Object.values(titanData).filter(t => t.type === "ultra");
+```
+
+### Getting Star Level Stats
+
+```javascript
+function getTitanStats(titanId, starLevel) {
+ const titan = titanData[titanId.toString()];
+ if (!titan || !titan.stars[starLevel]) {
+ return null;
+ }
+ return titan.stars[starLevel].battleStatData;
+}
+
+// Example: Get Sigurd's stats at 3 stars
+const sigurdStats = getTitanStats(4000, "3");
+// Returns: { hp: "2200", physicalAttack: "135" }
+```
+
+## Special Titans
+
+### Summoner Titans
+- **4004 (Tidus and Gelo)** - Water element summoner
+- **4014 (Asherona and Pyro)** - Fire element summoner, available from special shop
+- **4024 (Verdoc and Phyto)** - Earth element summoner, available from special shop
+
+These titans have the `"summoner"` type and special perks.
+
+### Ultra Titans
+Ultra titans are more powerful and start at 3 stars minimum:
+- Water: Hyperion (4003)
+- Fire: Araji (4013)
+- Earth: Eden (4023)
+- Dark: Tenebris (4033)
+- Light: Solaris (4043)
+
+## Notes
+
+1. **HP and Attack Values**: Some titans have HP/attack as strings, others as numbers. Always parse when doing calculations.
+
+2. **Star Levels**: Regular titans have stars 1-6, Ultra titans have stars 3-6.
+
+3. **Elemental Affinity**: Titans are strong/weak against other elements in a rock-paper-scissors system:
+ - Water > Fire
+ - Fire > Earth
+ - Earth > Water
+ - Dark/Light are neutral
+
+4. **Spirit Artifacts**: Each element has a corresponding spirit artifact:
+ - Water: 4001
+ - Fire: 4002
+ - Earth: 4003
+ - Dark: 4004
+ - Light: 4005
+
+5. **Role Positioning**:
+ - `"front"` - Front line (tanks)
+ - `"middle"` - Mid line (DPS/support)
+ - `"back"` - Back line (support/DPS)
+
+## Related Files
+
+- `HWD extention RED-1.0.7.test.js` - Uses titan IDs for team composition
+- `HeroWarsHelper - Auto Daily Extension.user.js` - Uses titan data for dungeon optimization
+
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..3987150
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,45 @@
+# TODO List - AutoHero Project
+
+This file contains the planned features and improvements for the AutoHero project.
+
+## 🎯 Planned Features
+
+### 1. Auto Attack Guild War (GW)
+- **Status**: Pending
+- **Priority**: Medium
+- **Description**: Implement automatic Guild War attack functionality
+- **Notes**: Need to analyze GW API calls and battle mechanics
+
+### 2. Auto Attack Last Boss Daily
+- **Status**: Pending
+- **Priority**: Medium
+- **Description**: Automatically attack the last boss daily for rewards
+- **Notes**: Need to identify boss battle APIs and implement daily scheduling
+
+### 3. Change Functions to Extension
+- **Status**: Pending
+- **Priority**: Low
+- **Description**: Refactor functions to use browser extension format
+- **Notes**: Convert from userscript to proper browser extension structure
+
+### 4. Auto Raid Mission with Best Fragment
+- **Status**: Pending
+- **Priority**: Medium
+- **Description**: Auto raid one mission with the best fragment 10 times each time load
+- **Notes**: Need to implement mission raiding with fragment optimization and repeat functionality
+
+## 📊 Progress Tracking
+
+- **Total Tasks**: 4
+- **Pending**: 4
+- **In Progress**: 0
+- **Completed**: 0
+
+## 📝 Notes
+
+- Tasks will be worked on as needed
+- Priority levels may change based on user requirements
+- Additional tasks can be added as they arise
+
+---
+*Last Updated: $(date)*
diff --git a/TrainingBots.user.js b/TrainingBots.user.js
new file mode 100644
index 0000000..a6fe359
--- /dev/null
+++ b/TrainingBots.user.js
@@ -0,0 +1,408 @@
+// ==UserScript==
+// @name Training Bots HwH Ext
+// @namespace HeroWarsHelper.TrainingBots
+// @version 1.0
+// @description Fetch all battle simulation data and export to JSON files grouped by battle type
+// @author AutoHero
+// @match https://www.hero-wars.com/*
+// @match https://apps-1701433570146040.apps.fbsbx.com/*
+// @grant none
+// @run-at document-end
+// @downloadURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/TrainingBots.user.js
+// @updateURL https://github.com/mailming/AutoHero/raw/refs/heads/develop/TrainingBots.user.js
+// ==/UserScript==
+
+(function() {
+ 'use strict';
+
+ // --- CONFIGURATION ---
+ const EXTENSION_NAME = "Training Bots Extension";
+ const EXTENSION_VERSION = "1.0";
+ const EXTENSION_AUTHOR = "AutoHero";
+
+ // --- INITIALIZATION ---
+ function waitForHWH(callback) {
+ const interval = setInterval(() => {
+ if (window.HWHClasses && window.HWHClasses.ScriptMenu && window.HWHFuncs && window.Send) {
+ const scriptMenu = window.HWHClasses.ScriptMenu.getInst();
+ if (scriptMenu && scriptMenu.mainMenu) {
+ clearInterval(interval);
+ callback();
+ }
+ }
+ }, 200);
+ }
+
+ function initializeExtension() {
+ console.log(`${EXTENSION_NAME} v${EXTENSION_VERSION} is loading...`);
+
+ const { HWHFuncs, HWHClasses } = window;
+ HWHFuncs.addExtentionName(EXTENSION_NAME, EXTENSION_VERSION, EXTENSION_AUTHOR);
+
+ // Add menu button
+ const scriptMenu = HWHClasses.ScriptMenu.getInst();
+ scriptMenu.addCombinedButton([
+ {
+ name: 'Training Bots',
+ title: 'Fetch all battles and export to JSON files',
+ onClick: fetchAndExportBattles,
+ color: 'purple'
+ }
+ ]);
+
+ console.log(`${EXTENSION_NAME} initialized successfully.`);
+ }
+
+ // --- BATTLE FETCHING ---
+ function extractItemsFromResponse(response) {
+ if (!response || !response.results || !Array.isArray(response.results)) return null;
+ for (let i = 0; i < response.results.length; i++) {
+ const result = response.results[i];
+ if (result && result.result && result.result.response) {
+ if (Array.isArray(result.result.response)) {
+ return result.result.response;
+ }
+ if (result.result.response.items && Array.isArray(result.result.response.items)) {
+ return result.result.response.items;
+ }
+ }
+ }
+ return null;
+ }
+
+ async function fetchAllBattles() {
+ const { Send, HWHFuncs } = window;
+ const allBattles = [];
+
+ try {
+ // Step 1: Get initial battles with empty args
+ HWHFuncs.setProgress('Training Bots: Fetching initial battles...', true);
+ const firstResponse = await Send({
+ calls: [{
+ name: "demoBattles_getAll",
+ args: {},
+ context: { actionTs: Math.floor(performance.now()) },
+ ident: "body"
+ }]
+ });
+
+ const firstBattles = extractItemsFromResponse(firstResponse) || [];
+ console.log(`Training Bots: Found ${firstBattles.length} initial battles`);
+ allBattles.push(...firstBattles);
+
+ // Step 2: Extract battle IDs and fetch retry battles
+ const battleIds = firstBattles.map(b => b.id).filter(id => id);
+ console.log(`Training Bots: Fetching retry battles for ${battleIds.length} battle IDs...`);
+
+ // Batch API calls (10 at a time to avoid overwhelming the server)
+ const batchSize = 10;
+ for (let i = 0; i < battleIds.length; i += batchSize) {
+ const batch = battleIds.slice(i, i + batchSize);
+ const calls = batch.map((battleId, idx) => ({
+ name: "demoBattles_getAll",
+ args: { parentId: battleId },
+ context: { actionTs: Math.floor(performance.now()) + idx },
+ ident: `retry_${i + idx}_body`
+ }));
+
+ try {
+ HWHFuncs.setProgress(`Training Bots: Fetching retry battles ${i + 1}-${Math.min(i + batchSize, battleIds.length)}/${battleIds.length}...`, true);
+ const retryResponse = await Send({ calls });
+
+ // Extract items from all results in the batch
+ if (retryResponse && retryResponse.results) {
+ retryResponse.results.forEach((result) => {
+ const items = extractItemsFromResponse({ results: [result] }) || [];
+ if (items.length > 0) {
+ allBattles.push(...items);
+ }
+ });
+ }
+ } catch (e) {
+ console.error(`Training Bots: Error fetching retry battles for batch ${i}-${i + batchSize}:`, e);
+ }
+
+ // Small delay between batches to avoid rate limiting
+ if (i + batchSize < battleIds.length) {
+ await new Promise(resolve => setTimeout(resolve, 500));
+ }
+ }
+
+ console.log(`Training Bots: Total battles fetched: ${allBattles.length}`);
+ return allBattles;
+ } catch (e) {
+ console.error('Training Bots: Error fetching battles:', e);
+ throw e;
+ }
+ }
+
+ // --- SIMPLIFY BATTLE DATA ---
+ function simplifyBattle(battle) {
+ const simplified = {
+ id: battle.id,
+ parentId: battle.parentId || 0,
+ mechanic: battle.mechanic || 'unknown',
+ win: battle.data?.win || false,
+ attackMax: battle.data?.attackMax || false,
+ defenceMax: battle.data?.defenceMax || false
+ };
+
+ // Extract attack units (titans or heroes, exclude pets)
+ const attackUnits = [];
+ if (battle.data?.attack?.units) {
+ for (const unitId in battle.data.attack.units) {
+ const unit = battle.data.attack.units[unitId];
+ const unitIdNum = parseInt(unitId);
+ // Exclude pets: either has type='pet' or ID is 6000+ (pet IDs are typically 6000+)
+ if (unit && unit.type !== 'pet' && unitIdNum < 6000) {
+ attackUnits.push(unitIdNum);
+ }
+ }
+ }
+ simplified.attack = attackUnits;
+
+ // Extract defense units (titans or heroes, exclude pets)
+ const defenseUnits = [];
+ if (battle.data?.defence?.units) {
+ for (const unitId in battle.data.defence.units) {
+ const unit = battle.data.defence.units[unitId];
+ const unitIdNum = parseInt(unitId);
+ // Exclude pets: either has type='pet' or ID is 6000+ (pet IDs are typically 6000+)
+ if (unit && unit.type !== 'pet' && unitIdNum < 6000) {
+ defenseUnits.push(unitIdNum);
+ }
+ }
+ }
+ simplified.defense = defenseUnits;
+
+ return simplified;
+ }
+
+ function simplifyBattles(battles) {
+ return battles.map(battle => simplifyBattle(battle));
+ }
+
+ // --- GROUP PARENT AND CHILD BATTLES ---
+ function groupParentChildBattles(simplifiedBattles) {
+ // Separate parents (parentId = 0) and children (parentId != 0)
+ const parents = [];
+ const childrenByParent = {}; // Map parentId -> array of child battles
+
+ for (const battle of simplifiedBattles) {
+ if (battle.parentId === 0 || battle.parentId === '0') {
+ // This is a parent battle
+ parents.push(battle);
+ } else {
+ // This is a child battle
+ const parentId = String(battle.parentId);
+ if (!childrenByParent[parentId]) {
+ childrenByParent[parentId] = [];
+ }
+ childrenByParent[parentId].push(battle);
+ }
+ }
+
+ // Group children with their parents
+ const grouped = [];
+ for (const parent of parents) {
+ const parentId = String(parent.id);
+ const children = childrenByParent[parentId] || [];
+
+ // Count wins and losses (including parent)
+ let wins = 0;
+ let losses = 0;
+ const childBattleIds = [];
+
+ // Count parent
+ if (parent.win) {
+ wins++;
+ } else {
+ losses++;
+ }
+
+ // Count children
+ for (const child of children) {
+ childBattleIds.push(child.id);
+ if (child.win) {
+ wins++;
+ } else {
+ losses++;
+ }
+ }
+
+ // Calculate win rate
+ const total = wins + losses;
+ const winRate = total > 0 ? ((wins / total) * 100).toFixed(2) : '0.00';
+ const winRateNum = parseFloat(winRate);
+
+ // Determine valid field based on conditions
+ let valid = 0;
+ // First check: if attackMax = false AND defenceMax = false, valid = 0
+ if (!parent.attackMax && !parent.defenceMax) {
+ valid = 0;
+ }
+ // Condition 1: attackMax = false AND defenceMax = true AND winRate > 70
+ else if (!parent.attackMax && parent.defenceMax && winRateNum > 70) {
+ valid = 1;
+ }
+ // Condition 2: attackMax = true AND defenceMax = true
+ else if (parent.attackMax && parent.defenceMax) {
+ valid = 1;
+ }
+ // Condition 3: attackMax = true AND defenceMax = false AND winRate < 30
+ else if (parent.attackMax && !parent.defenceMax && winRateNum < 30) {
+ valid = 1;
+ }
+ // All other cases: valid = 0 (already set)
+
+ // Create grouped battle record
+ const groupedBattle = {
+ id: parent.id,
+ parentId: parent.parentId,
+ mechanic: parent.mechanic,
+ win: parent.win,
+ attackMax: parent.attackMax,
+ defenceMax: parent.defenceMax,
+ attack: parent.attack,
+ defense: parent.defense,
+ childBattles: childBattleIds,
+ wins: wins,
+ losses: losses,
+ winRate: winRateNum,
+ valid: valid
+ };
+
+ // Only add to output if valid = 1
+ if (valid === 1) {
+ grouped.push(groupedBattle);
+ }
+ }
+
+ // Handle orphaned children (children whose parent is not in the list)
+ // This shouldn't happen in normal cases, but handle it just in case
+ for (const parentId in childrenByParent) {
+ const parentExists = parents.some(p => String(p.id) === parentId);
+ if (!parentExists) {
+ console.warn(`Training Bots: Found orphaned children for parentId ${parentId}`);
+ }
+ }
+
+ return grouped;
+ }
+
+ // --- GROUP BY BATTLE TYPE ---
+ function isTitanBattle(mechanic) {
+ if (!mechanic) return false;
+ return mechanic.includes('titan') ||
+ mechanic === 'clan_pvp_titan' ||
+ mechanic === 'clan_global_pvp_titan';
+ }
+
+ function groupBattlesByType(battles) {
+ const titanBattles = [];
+ const nonTitanBattles = [];
+
+ for (const battle of battles) {
+ const mechanic = battle.mechanic || 'unknown';
+ if (isTitanBattle(mechanic)) {
+ titanBattles.push(battle);
+ } else {
+ nonTitanBattles.push(battle);
+ }
+ }
+
+ return {
+ titan: titanBattles,
+ nonTitan: nonTitanBattles
+ };
+ }
+
+ // --- EXPORT TO JSON FILES ---
+ function downloadJSON(data, filename) {
+ const dataStr = JSON.stringify(data, null, 2);
+ const blob = new Blob([dataStr], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = filename;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ }
+
+ async function exportBattlesByType(groupedBattles) {
+ const { HWHFuncs } = window;
+
+ HWHFuncs.setProgress('Training Bots: Simplifying and grouping battles...', true);
+
+ // Simplify and group parent-child battles
+ let groupedTitan = [];
+ let groupedNonTitan = [];
+
+ if (groupedBattles.titan.length > 0) {
+ const simplifiedTitan = simplifyBattles(groupedBattles.titan);
+ groupedTitan = groupParentChildBattles(simplifiedTitan);
+ }
+
+ if (groupedBattles.nonTitan.length > 0) {
+ const simplifiedNonTitan = simplifyBattles(groupedBattles.nonTitan);
+ groupedNonTitan = groupParentChildBattles(simplifiedNonTitan);
+ }
+
+ HWHFuncs.setProgress('Training Bots: Exporting battles...', true);
+
+ // Export grouped titan battles
+ if (groupedTitan.length > 0) {
+ downloadJSON(groupedTitan, 'training_bots_titan.json');
+ console.log(`Training Bots: Exported ${groupedTitan.length} grouped titan battles to training_bots_titan.json`);
+ }
+
+ // Export grouped non-titan battles
+ if (groupedNonTitan.length > 0) {
+ downloadJSON(groupedNonTitan, 'training_bots_non_titan.json');
+ console.log(`Training Bots: Exported ${groupedNonTitan.length} grouped non-titan battles to training_bots_non_titan.json`);
+ }
+
+ return (groupedTitan.length > 0 ? 1 : 0) + (groupedNonTitan.length > 0 ? 1 : 0);
+ }
+
+ // --- MAIN FUNCTION ---
+ async function fetchAndExportBattles() {
+ const { HWHFuncs } = window;
+
+ try {
+ HWHFuncs.setProgress('Training Bots: Starting battle fetch...', true);
+
+ // Fetch all battles
+ const allBattles = await fetchAllBattles();
+
+ if (allBattles.length === 0) {
+ HWHFuncs.setProgress('Training Bots: No battles found', true);
+ return;
+ }
+
+ // Group by battle type
+ HWHFuncs.setProgress('Training Bots: Grouping battles by type...', true);
+ const groupedBattles = groupBattlesByType(allBattles);
+
+ // Export to JSON files
+ const fileCount = await exportBattlesByType(groupedBattles);
+
+ // Summary
+ const summary = `Titan: ${groupedBattles.titan.length}, Non-Titan: ${groupedBattles.nonTitan.length}`;
+
+ HWHFuncs.setProgress(`Training Bots: Complete! Exported ${fileCount} file(s). ${summary}`, true);
+ console.log(`Training Bots: Export complete. Summary: ${summary}`);
+ } catch (e) {
+ const errorMsg = `Training Bots: Error - ${e.message || String(e)}`;
+ HWHFuncs.setProgress(errorMsg, true);
+ console.error('Training Bots: Error in fetchAndExportBattles:', e);
+ }
+ }
+
+ // Start initialization
+ waitForHWH(initializeExtension);
+
+})();
+
diff --git a/api-monitor.user.js b/api-monitor.user.js
new file mode 100644
index 0000000..b75bd27
--- /dev/null
+++ b/api-monitor.user.js
@@ -0,0 +1,1153 @@
+// ==UserScript==
+// @name API Monitor
+// @namespace http://tampermonkey.net/
+// @version 3.5
+// @description Comprehensive API monitoring with integrated lib.data monitoring for web applications
+// @author AutoHero Project
+// @match *://hero-wars.com/*
+// @match *://www.hero-wars.com/*
+// @grant GM_setValue
+// @grant GM_getValue
+// @grant GM_addStyle
+// @grant GM_download
+// @updateURL https://github.com/mailming/AutoHero/raw/develop/api-monitor.user.js
+// @downloadURL https://github.com/mailming/AutoHero/raw/develop/api-monitor.user.js
+// ==/UserScript==
+
+(function() {
+ 'use strict';
+
+ // Configuration
+ const CONFIG = {
+ maxRequests: 1000,
+ maxResponseSize: 1024 * 1024, // 1MB
+ enableUI: true, // Enable UI for testing and debugging
+ enableExport: true,
+ enableFiltering: true,
+ logLevel: 'all', // 'all', 'errors', 'requests', 'responses'
+ enableFileLogging: true, // Enable file logging by default
+ logToFileInterval: 3000, // Log to file every 3 seconds (more frequent)
+ maxLogFileSize: 10 * 1024 * 1024, // 10MB max log file size
+ logFormat: 'json', // 'json', 'text', 'csv'
+ // Lib.data monitoring configuration
+ enableLibDataMonitoring: false, // Enable lib.data monitoring
+ libDataCheckInterval: 3000, // Check lib.data every 3 seconds
+ libDataLogChanges: true, // Log lib.data changes to console
+ libDataSaveChanges: true // Save lib.data changes to files
+ };
+
+ // Initialize API Monitor - Make it globally accessible
+ const apiMonitor = {
+ requests: [],
+ responses: [],
+ errors: [],
+ pendingLogs: [], // New logs waiting to be written to file
+ // Lib.data monitoring
+ libDataMonitor: {
+ isMonitoring: false,
+ lastDataHash: null,
+ changeCount: 0,
+ intervalId: null,
+ lastLibData: null
+ },
+ stats: {
+ totalRequests: 0,
+ totalResponses: 0,
+ totalErrors: 0,
+ startTime: Date.now(),
+ logsWritten: 0,
+ lastLogTime: Date.now(),
+ libDataChanges: 0
+ },
+
+ // Add request to monitor
+ addRequest: function(req) {
+ if (apiMonitor.requests.length >= CONFIG.maxRequests) {
+ apiMonitor.requests.shift(); // Remove oldest
+ }
+
+ apiMonitor.requests.push(req);
+ apiMonitor.stats.totalRequests++;
+
+ if (CONFIG.logLevel === 'all' || CONFIG.logLevel === 'requests') {
+ console.log('🔵 API_REQUEST:', JSON.stringify(req, null, 2));
+ }
+
+ // Add to pending logs for file writing
+ if (CONFIG.enableFileLogging) {
+ apiMonitor.pendingLogs.push({
+ type: 'request',
+ data: req,
+ timestamp: new Date().toISOString()
+ });
+ console.log('🔍 DEBUG: Added request to pendingLogs, count =', apiMonitor.pendingLogs.length);
+ }
+
+ apiMonitor.updateUI();
+ },
+
+ // Add response to monitor
+ addResponse: function(resp) {
+ if (apiMonitor.responses.length >= CONFIG.maxRequests) {
+ apiMonitor.responses.shift(); // Remove oldest
+ }
+
+ apiMonitor.responses.push(resp);
+ apiMonitor.stats.totalResponses++;
+
+ if (CONFIG.logLevel === 'all' || CONFIG.logLevel === 'responses') {
+ console.log('🟢 API_RESPONSE:', JSON.stringify(resp, null, 2));
+ }
+
+ // Add to pending logs for file writing
+ if (CONFIG.enableFileLogging) {
+ apiMonitor.pendingLogs.push({
+ type: 'response',
+ data: resp,
+ timestamp: new Date().toISOString()
+ });
+ console.log('🔍 DEBUG: Added response to pendingLogs, count =', apiMonitor.pendingLogs.length);
+ }
+
+ apiMonitor.updateUI();
+ },
+
+ // Add error to monitor
+ addError: function(err) {
+ if (apiMonitor.errors.length >= CONFIG.maxRequests) {
+ apiMonitor.errors.shift(); // Remove oldest
+ }
+
+ apiMonitor.errors.push(err);
+ apiMonitor.stats.totalErrors++;
+
+ if (CONFIG.logLevel === 'all' || CONFIG.logLevel === 'errors') {
+ console.log('🔴 API_ERROR:', JSON.stringify(err, null, 2));
+ }
+
+ // Add to pending logs for file writing
+ if (CONFIG.enableFileLogging) {
+ apiMonitor.pendingLogs.push({
+ type: 'error',
+ data: err,
+ timestamp: new Date().toISOString()
+ });
+ }
+
+ apiMonitor.updateUI();
+ },
+
+ // Get all data
+ getAllData: function() {
+ return {
+ requests: apiMonitor.requests,
+ responses: apiMonitor.responses,
+ errors: apiMonitor.errors,
+ stats: apiMonitor.stats,
+ timestamp: new Date().toISOString(),
+ url: window.location.href
+ };
+ },
+
+ // Clear all data
+ clearData: function() {
+ apiMonitor.requests = [];
+ apiMonitor.responses = [];
+ apiMonitor.errors = [];
+ apiMonitor.pendingLogs = [];
+ apiMonitor.stats = {
+ totalRequests: 0,
+ totalResponses: 0,
+ totalErrors: 0,
+ startTime: Date.now(),
+ logsWritten: 0,
+ lastLogTime: Date.now()
+ };
+ apiMonitor.updateUI();
+ console.log('🧹 API Monitor data cleared');
+ },
+
+ // Write logs to file
+ writeLogsToFile: function() {
+ console.log('🔍 DEBUG: writeLogsToFile called');
+ console.log('🔍 DEBUG: enableFileLogging =', CONFIG.enableFileLogging);
+ console.log('🔍 DEBUG: pendingLogs.length =', apiMonitor.pendingLogs.length);
+
+ if (!CONFIG.enableFileLogging || apiMonitor.pendingLogs.length === 0) {
+ console.log('🔍 DEBUG: Skipping file write - logging disabled or no pending logs');
+ return;
+ }
+
+ try {
+ console.log('🔍 DEBUG: Starting file write process');
+ let logContent = '';
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
+ console.log('🔍 DEBUG: Generated timestamp =', timestamp);
+
+ if (CONFIG.logFormat === 'json') {
+ logContent = JSON.stringify({
+ session: {
+ url: window.location.href,
+ timestamp: new Date().toISOString(),
+ logsCount: apiMonitor.pendingLogs.length
+ },
+ logs: apiMonitor.pendingLogs
+ }, null, 2);
+ } else if (CONFIG.logFormat === 'text') {
+ logContent = apiMonitor.pendingLogs.map(log => {
+ return `[${log.timestamp}] ${log.type.toUpperCase()}: ${JSON.stringify(log.data, null, 2)}`;
+ }).join('\n\n');
+ } else if (CONFIG.logFormat === 'csv') {
+ const headers = 'timestamp,type,url,method,status,error\n';
+ const rows = apiMonitor.pendingLogs.map(log => {
+ const data = log.data;
+ const url = data.url || '';
+ const method = data.method || '';
+ const status = data.status || '';
+ const error = data.error || '';
+ return `${log.timestamp},${log.type},"${url}","${method}","${status}","${error}"`;
+ }).join('\n');
+ logContent = headers + rows;
+ }
+
+ // Create filename with timestamp
+ const filename = `AutoHero-API-Logs-${timestamp}.${CONFIG.logFormat === 'json' ? 'json' : CONFIG.logFormat === 'csv' ? 'csv' : 'txt'}`;
+ console.log('🔍 DEBUG: Generated filename =', filename);
+ console.log('🔍 DEBUG: Log content length =', logContent.length);
+
+ // Use proper download method
+ console.log('🔍 DEBUG: Creating download...');
+ const blob = new Blob([logContent], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = filename;
+ a.style.display = 'none';
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ console.log('🔍 DEBUG: Download created successfully');
+
+ // Update stats
+ apiMonitor.stats.logsWritten += apiMonitor.pendingLogs.length;
+ apiMonitor.stats.lastLogTime = Date.now();
+
+ console.log(`📁 Logged ${apiMonitor.pendingLogs.length} entries to file: ${filename}`);
+
+ // Clear pending logs
+ apiMonitor.pendingLogs = [];
+
+ } catch (error) {
+ console.error('❌ Error writing logs to file:', error);
+ console.error('🔍 DEBUG: Error details:', error.message, error.stack);
+ }
+ },
+
+ // Force write logs to file immediately
+ forceWriteLogs: function() {
+ console.log('🔍 DEBUG: forceWriteLogs called');
+
+ // Get all current data instead of just pending logs
+ const allData = apiMonitor.getAllData();
+
+ if (allData.requests.length === 0 && allData.responses.length === 0 && allData.errors.length === 0) {
+ console.log('🔍 DEBUG: No data to write - no requests, responses, or errors captured');
+ return;
+ }
+
+ try {
+ console.log('🔍 DEBUG: Writing all current data to file');
+ let logContent = '';
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
+
+ if (CONFIG.logFormat === 'json') {
+ logContent = JSON.stringify(allData, null, 2);
+ } else if (CONFIG.logFormat === 'text') {
+ logContent = `=== API MONITOR LOGS ===\n`;
+ logContent += `Timestamp: ${allData.timestamp}\n`;
+ logContent += `URL: ${allData.url}\n`;
+ logContent += `Total Requests: ${allData.stats.totalRequests}\n`;
+ logContent += `Total Responses: ${allData.stats.totalResponses}\n`;
+ logContent += `Total Errors: ${allData.stats.totalErrors}\n\n`;
+
+ logContent += `--- REQUESTS ---\n`;
+ allData.requests.forEach((req, i) => {
+ logContent += `${i + 1}. ${req.method} ${req.url}\n`;
+ logContent += ` Timestamp: ${req.timestamp}\n`;
+ if (req.headers && Object.keys(req.headers).length > 0) {
+ logContent += ` Headers: ${JSON.stringify(req.headers)}\n`;
+ }
+ logContent += `\n`;
+ });
+
+ logContent += `--- RESPONSES ---\n`;
+ allData.responses.forEach((resp, i) => {
+ logContent += `${i + 1}. ${resp.status} ${resp.statusText}\n`;
+ logContent += ` Request ID: ${resp.requestId}\n`;
+ logContent += ` Timestamp: ${resp.timestamp}\n`;
+ logContent += `\n`;
+ });
+
+ logContent += `--- ERRORS ---\n`;
+ allData.errors.forEach((err, i) => {
+ logContent += `${i + 1}. ${err.error}\n`;
+ logContent += ` Request ID: ${err.requestId}\n`;
+ logContent += ` Timestamp: ${err.timestamp}\n`;
+ logContent += `\n`;
+ });
+ } else if (CONFIG.logFormat === 'csv') {
+ const headers = ['Type', 'Timestamp', 'Method', 'URL', 'Status', 'Size'];
+ const rows = [];
+
+ allData.requests.forEach(req => {
+ rows.push(['request', req.timestamp, req.method, req.url, '', '']);
+ });
+
+ allData.responses.forEach(resp => {
+ rows.push(['response', resp.timestamp, '', '', resp.status, JSON.stringify(resp.body).length]);
+ });
+
+ allData.errors.forEach(err => {
+ rows.push(['error', err.timestamp, '', '', 'ERROR', err.error.length]);
+ });
+
+ logContent = headers.join(',') + '\n' + rows.map(row => row.map(cell => `"${cell}"`).join(',')).join('\n');
+ }
+
+ const filename = `AutoHero-API-Logs-${timestamp}.${CONFIG.logFormat === 'json' ? 'json' : CONFIG.logFormat === 'csv' ? 'csv' : 'txt'}`;
+ console.log('🔍 DEBUG: Generated filename =', filename);
+ console.log('🔍 DEBUG: Log content length =', logContent.length);
+
+ // Use proper download method
+ console.log('🔍 DEBUG: Creating download...');
+ const blob = new Blob([logContent], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = filename;
+ a.style.display = 'none';
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ console.log('🔍 DEBUG: Download created successfully');
+
+ // Update stats
+ apiMonitor.stats.logsWritten += 1; // Count as one manual write
+ apiMonitor.stats.lastLogTime = Date.now();
+
+ console.log(`📁 Manually logged all data to file: ${filename}`);
+ console.log(`📊 Logged ${allData.requests.length} requests, ${allData.responses.length} responses, ${allData.errors.length} errors`);
+
+ } catch (error) {
+ console.error('❌ Error writing logs to file:', error);
+ console.error('🔍 DEBUG: Error details:', error.message, error.stack);
+ }
+ },
+
+ // Get log statistics
+ getLogStats: function() {
+ return {
+ totalLogsWritten: apiMonitor.stats.logsWritten,
+ pendingLogs: apiMonitor.pendingLogs.length,
+ lastLogTime: apiMonitor.stats.lastLogTime,
+ logFormat: CONFIG.logFormat,
+ fileLoggingEnabled: CONFIG.enableFileLogging
+ };
+ },
+
+ // Export data
+ exportData: function(format = 'json') {
+ const data = apiMonitor.getAllData();
+
+ if (format === 'json') {
+ const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `api-monitor-${Date.now()}.json`;
+ a.click();
+ URL.revokeObjectURL(url);
+ } else if (format === 'har') {
+ // Convert to HAR format
+ const har = this.convertToHAR(data);
+ const blob = new Blob([JSON.stringify(har, null, 2)], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `api-monitor-${Date.now()}.har`;
+ a.click();
+ URL.revokeObjectURL(url);
+ }
+ },
+
+ // Convert to HAR format
+ convertToHAR: function(data) {
+ const har = {
+ log: {
+ version: "1.2",
+ creator: {
+ name: "API Monitor",
+ version: "2.9"
+ },
+ entries: []
+ }
+ };
+
+ data.requests.forEach((req, index) => {
+ const response = data.responses.find(r => r.requestId === req.id);
+ const error = data.errors.find(e => e.requestId === req.id);
+
+ const entry = {
+ startedDateTime: req.timestamp,
+ time: response ? new Date(response.timestamp) - new Date(req.timestamp) : 0,
+ request: {
+ method: req.method,
+ url: req.url,
+ httpVersion: "HTTP/1.1",
+ headers: req.headers ? Object.entries(req.headers).map(([name, value]) => ({ name, value })) : [],
+ queryString: [],
+ cookies: [],
+ headersSize: req.headers ? JSON.stringify(req.headers).length : 0,
+ bodySize: req.body ? JSON.stringify(req.body).length : 0,
+ postData: req.body ? {
+ mimeType: "application/json",
+ text: JSON.stringify(req.body)
+ } : undefined
+ },
+ response: response ? {
+ status: response.status,
+ statusText: response.statusText,
+ httpVersion: "HTTP/1.1",
+ headers: response.headers ? Object.entries(response.headers).map(([name, value]) => ({ name, value })) : [],
+ cookies: [],
+ content: {
+ size: response.body ? JSON.stringify(response.body).length : 0,
+ mimeType: response.headers && response.headers['content-type'] ? response.headers['content-type'] : 'application/json',
+ text: response.body ? (typeof response.body === 'string' ? response.body : JSON.stringify(response.body)) : ''
+ },
+ redirectURL: "",
+ headersSize: response.headers ? JSON.stringify(response.headers).length : 0,
+ bodySize: response.body ? JSON.stringify(response.body).length : 0
+ } : undefined,
+ cache: {},
+ timings: {
+ blocked: 0,
+ dns: 0,
+ connect: 0,
+ send: 0,
+ wait: 0,
+ receive: 0
+ }
+ };
+
+ if (error) {
+ entry.response = {
+ status: 0,
+ statusText: "Error",
+ httpVersion: "HTTP/1.1",
+ headers: [],
+ cookies: [],
+ content: {
+ size: error.error.length,
+ mimeType: "text/plain",
+ text: error.error
+ },
+ redirectURL: "",
+ headersSize: 0,
+ bodySize: error.error.length
+ };
+ }
+
+ har.log.entries.push(entry);
+ });
+
+ return har;
+ },
+
+ // Update UI
+ updateUI: function() {
+ if (!CONFIG.enableUI) return;
+
+ const statsElement = document.getElementById('api-monitor-stats');
+ if (statsElement) {
+ const runtime = Math.round((Date.now() - apiMonitor.stats.startTime) / 1000);
+ const logsWritten = apiMonitor.stats.logsWritten;
+ const pendingLogs = apiMonitor.pendingLogs.length;
+
+ // Clear and rebuild stats element safely
+ statsElement.textContent = '';
+ const statsDiv = document.createElement('div');
+ statsDiv.style.cssText = 'background: #f0f0f0; padding: 10px; border-radius: 5px; margin: 10px 0; font-family: monospace;';
+
+ const title = document.createElement('strong');
+ title.textContent = 'API Monitor Stats:';
+ statsDiv.appendChild(title);
+
+ const br1 = document.createElement('br');
+ statsDiv.appendChild(br1);
+
+ const statsText = document.createElement('span');
+ statsText.textContent = `Requests: ${apiMonitor.stats.totalRequests} | Responses: ${apiMonitor.stats.totalResponses} | Errors: ${apiMonitor.stats.totalErrors} | Runtime: ${runtime}s`;
+ statsDiv.appendChild(statsText);
+
+ const br2 = document.createElement('br');
+ statsDiv.appendChild(br2);
+
+ const logStatus = document.createElement('span');
+ logStatus.style.color = CONFIG.enableFileLogging ? 'green' : 'red';
+ logStatus.textContent = `📁 File Logging: ${CONFIG.enableFileLogging ? 'ON' : 'OFF'} | Written: ${logsWritten} | Pending: ${pendingLogs}`;
+ statsDiv.appendChild(logStatus);
+
+ const br3 = document.createElement('br');
+ statsDiv.appendChild(br3);
+
+ const libDataStatus = document.createElement('span');
+ libDataStatus.style.color = apiMonitor.libDataMonitor.isMonitoring ? 'green' : 'red';
+ libDataStatus.textContent = `🎮 Lib.data: ${apiMonitor.libDataMonitor.isMonitoring ? 'ON' : 'OFF'} | Changes: ${apiMonitor.stats.libDataChanges}`;
+ statsDiv.appendChild(libDataStatus);
+
+ statsElement.appendChild(statsDiv);
+ }
+ },
+
+ // Show data in popup
+ showData: function() {
+ const data = apiMonitor.getAllData();
+ const popup = window.open('', 'API Monitor Data', 'width=1200,height=800,scrollbars=yes');
+
+ popup.document.write(`
+
+
+ API Monitor Results
+
+
+
+ API Monitor Results
+
+
+
Statistics
+
URL: ${data.url}
+
Timestamp: ${data.timestamp}
+
Total Requests: ${data.stats.totalRequests}
+
Total Responses: ${data.stats.totalResponses}
+
Total Errors: ${data.stats.totalErrors}
+
Runtime: ${Math.round((Date.now() - data.stats.startTime) / 1000)} seconds
+
+
+
+
Requests (${data.requests.length})
+ ${data.requests.map(req => `
+
+
${req.method} ${req.url}
+
Time: ${req.timestamp}
+
${JSON.stringify(req, null, 2)}
+
+ `).join('')}
+
+
+
+
Responses (${data.responses.length})
+ ${data.responses.map(resp => `
+
+
${resp.status} ${resp.statusText}
+
Time: ${resp.timestamp}
+
${JSON.stringify(resp, null, 2)}
+
+ `).join('')}
+
+
+
+
Errors (${data.errors.length})
+ ${data.errors.map(err => `
+
+
Error
+
Time: ${err.timestamp}
+
${JSON.stringify(err, null, 2)}
+
+ `).join('')}
+
+
+
+ Close
+ Export JSON
+ Export HAR
+
+
+
+
+
+ `);
+ },
+
+ // Lib.data monitoring methods
+ startLibDataMonitoring: function() {
+ if (!CONFIG.enableLibDataMonitoring) {
+ console.log('⚠️ Lib.data monitoring is disabled in CONFIG');
+ return;
+ }
+
+ if (apiMonitor.libDataMonitor.isMonitoring) {
+ console.log('⚠️ Lib.data monitoring already running');
+ return;
+ }
+
+ console.log('🎮 Starting lib.data monitoring...');
+ apiMonitor.libDataMonitor.isMonitoring = true;
+
+ // Initial check
+ apiMonitor.checkLibData();
+
+ // Set up interval
+ apiMonitor.libDataMonitor.intervalId = setInterval(() => {
+ apiMonitor.checkLibData();
+ }, CONFIG.libDataCheckInterval);
+
+ console.log(`✅ Lib.data monitoring started (checking every ${CONFIG.libDataCheckInterval}ms)`);
+ },
+
+ stopLibDataMonitoring: function() {
+ if (!apiMonitor.libDataMonitor.isMonitoring) {
+ console.log('⚠️ Lib.data monitoring not running');
+ return;
+ }
+
+ if (apiMonitor.libDataMonitor.intervalId) {
+ clearInterval(apiMonitor.libDataMonitor.intervalId);
+ apiMonitor.libDataMonitor.intervalId = null;
+ }
+
+ apiMonitor.libDataMonitor.isMonitoring = false;
+ console.log('⏹️ Lib.data monitoring stopped');
+ },
+
+ checkLibData: function() {
+ if (typeof lib === 'undefined' || !lib.data) {
+ return; // lib.data not available yet
+ }
+
+ const currentData = lib.data;
+ const currentHash = apiMonitor.getDataHash(currentData);
+
+ if (currentHash !== apiMonitor.libDataMonitor.lastDataHash) {
+ apiMonitor.libDataMonitor.changeCount++;
+ apiMonitor.libDataMonitor.lastDataHash = currentHash;
+ apiMonitor.libDataMonitor.lastLibData = JSON.parse(JSON.stringify(currentData));
+ apiMonitor.stats.libDataChanges++;
+
+ if (CONFIG.libDataLogChanges) {
+ console.log(`🔄 lib.data changed! (Change #${apiMonitor.libDataMonitor.changeCount})`);
+ console.log(`📊 Data keys: ${Object.keys(currentData).length} properties`);
+ console.log(`🔑 Main keys: ${Object.keys(currentData).slice(0, 10).join(', ')}`);
+ }
+
+ if (CONFIG.libDataSaveChanges) {
+ apiMonitor.saveLibDataToFile(currentData);
+ }
+
+ // Add to pending logs for API monitor
+ if (CONFIG.enableFileLogging) {
+ apiMonitor.pendingLogs.push({
+ type: 'lib_data_change',
+ data: {
+ changeNumber: apiMonitor.libDataMonitor.changeCount,
+ keys: Object.keys(currentData),
+ keyCount: Object.keys(currentData).length,
+ timestamp: new Date().toISOString()
+ },
+ timestamp: new Date().toISOString()
+ });
+ }
+
+ apiMonitor.updateUI();
+ }
+ },
+
+ getDataHash: function(data) {
+ try {
+ return btoa(JSON.stringify(data)).slice(0, 20);
+ } catch (e) {
+ return 'error_' + Date.now();
+ }
+ },
+
+ saveLibDataToFile: function(libData) {
+ try {
+ const timestamp = new Date().toISOString();
+ const data = {
+ timestamp: timestamp,
+ url: window.location.href,
+ libData: libData,
+ changeNumber: apiMonitor.libDataMonitor.changeCount,
+ totalChanges: apiMonitor.stats.libDataChanges,
+ source: 'api-monitor-integrated'
+ };
+
+ const filename = `lib-data-api-monitor-${Date.now()}.json`;
+ const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = filename;
+ a.style.display = 'none';
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+
+ console.log(`📁 Saved lib.data change to: ${filename}`);
+
+ } catch (error) {
+ console.error('❌ Error saving lib.data to file:', error);
+ }
+ },
+
+ getLibDataStats: function() {
+ return {
+ isMonitoring: apiMonitor.libDataMonitor.isMonitoring,
+ changeCount: apiMonitor.libDataMonitor.changeCount,
+ totalChanges: apiMonitor.stats.libDataChanges,
+ lastDataHash: apiMonitor.libDataMonitor.lastDataHash,
+ checkInterval: CONFIG.libDataCheckInterval,
+ libDataAvailable: typeof lib !== 'undefined' && !!lib.data
+ };
+ },
+
+ forceLibDataCheck: function() {
+ console.log('🔍 Force checking lib.data...');
+ apiMonitor.checkLibData();
+ }
+ };
+
+ // Make apiMonitor globally accessible
+ window.apiMonitor = apiMonitor;
+
+ // Intercept fetch requests
+ const originalFetch = window.fetch;
+ window.fetch = async function(...args) {
+ console.log('🔍 DEBUG: Fetch intercepted:', args[0]);
+ console.log('🔍 DEBUG: apiMonitor exists:', typeof window.apiMonitor !== 'undefined');
+ console.log('🔍 DEBUG: CONFIG.logLevel:', CONFIG.logLevel);
+ const requestId = Date.now() + Math.random();
+ const request = {
+ id: requestId,
+ type: 'fetch',
+ url: args[0],
+ method: args[1]?.method || 'GET',
+ headers: args[1]?.headers || {},
+ body: args[1]?.body,
+ timestamp: new Date().toISOString()
+ };
+
+ console.log('🔍 DEBUG: About to call addRequest with:', request);
+ window.apiMonitor.addRequest(request);
+ console.log('🔍 DEBUG: addRequest completed, total requests:', window.apiMonitor.stats.totalRequests);
+
+ try {
+ const response = await originalFetch.apply(this, args);
+
+ // Clone response to read body without consuming it
+ const responseClone = response.clone();
+ let responseBody;
+
+ try {
+ const contentType = response.headers.get('content-type') || '';
+
+ if (contentType.includes('application/json')) {
+ responseBody = await responseClone.json();
+ } else if (contentType.includes('text/')) {
+ responseBody = await responseClone.text();
+ } else if (contentType.includes('image/')) {
+ responseBody = '[Binary Image Data]';
+ } else if (contentType.includes('video/')) {
+ responseBody = '[Binary Video Data]';
+ } else if (contentType.includes('audio/')) {
+ responseBody = '[Binary Audio Data]';
+ } else {
+ // Try to read as text, fallback to array buffer info
+ try {
+ responseBody = await responseClone.text();
+ } catch (e) {
+ const arrayBuffer = await responseClone.arrayBuffer();
+ responseBody = `[Binary Data: ${arrayBuffer.byteLength} bytes]`;
+ }
+ }
+
+ // Limit response size
+ if (typeof responseBody === 'string' && responseBody.length > CONFIG.maxResponseSize) {
+ responseBody = responseBody.substring(0, CONFIG.maxResponseSize) + '...[truncated]';
+ }
+
+ } catch (e) {
+ responseBody = `Unable to read response body: ${e.message}`;
+ }
+
+ const responseData = {
+ requestId: requestId,
+ status: response.status,
+ statusText: response.statusText,
+ headers: Object.fromEntries(response.headers),
+ body: responseBody,
+ timestamp: new Date().toISOString()
+ };
+
+ window.apiMonitor.addResponse(responseData);
+ return response;
+
+ } catch (error) {
+ const errorData = {
+ requestId: requestId,
+ error: error.message,
+ stack: error.stack,
+ timestamp: new Date().toISOString()
+ };
+
+ window.apiMonitor.addError(errorData);
+ throw error;
+ }
+ };
+
+ // Intercept XMLHttpRequest
+ const originalXHR = window.XMLHttpRequest;
+ window.XMLHttpRequest = function() {
+ const xhr = new originalXHR();
+ const originalOpen = xhr.open;
+ const originalSend = xhr.send;
+
+ xhr.open = function(method, url, ...args) {
+ console.log('🔍 DEBUG: XHR intercepted:', method, url);
+ const requestId = Date.now() + Math.random();
+ const request = {
+ id: requestId,
+ type: 'xhr',
+ method: method,
+ url: url,
+ timestamp: new Date().toISOString()
+ };
+
+ window.apiMonitor.addRequest(request);
+ xhr._requestId = requestId;
+
+ return originalOpen.apply(this, [method, url, ...args]);
+ };
+
+ xhr.send = function(data) {
+ if (data) {
+ console.log('XHR_DATA:', data);
+ }
+
+ xhr.addEventListener('load', function() {
+ const responseData = {
+ requestId: xhr._requestId,
+ status: xhr.status,
+ statusText: xhr.statusText,
+ response: xhr.responseText,
+ headers: {},
+ timestamp: new Date().toISOString()
+ };
+
+ // Try to parse response headers
+ try {
+ const responseHeaders = xhr.getAllResponseHeaders();
+ if (responseHeaders) {
+ responseHeaders.split('\r\n').forEach(line => {
+ const parts = line.split(': ');
+ if (parts.length === 2) {
+ responseData.headers[parts[0]] = parts[1];
+ }
+ });
+ }
+ } catch (e) {
+ console.log('Could not parse XHR headers:', e);
+ }
+
+ window.apiMonitor.addResponse(responseData);
+ });
+
+ xhr.addEventListener('error', function() {
+ const errorData = {
+ requestId: xhr._requestId,
+ error: 'XHR Error',
+ timestamp: new Date().toISOString()
+ };
+
+ window.apiMonitor.addError(errorData);
+ });
+
+ xhr.addEventListener('timeout', function() {
+ const errorData = {
+ requestId: xhr._requestId,
+ error: 'XHR Timeout',
+ timestamp: new Date().toISOString()
+ };
+
+ window.apiMonitor.addError(errorData);
+ });
+
+ return originalSend.apply(this, [data]);
+ };
+
+ return xhr;
+ };
+
+ // Add UI elements when page loads
+ function addUI() {
+ if (!CONFIG.enableUI) return;
+
+ // Add stats display
+ const statsDiv = document.createElement('div');
+ statsDiv.id = 'api-monitor-stats';
+ statsDiv.style.cssText = `
+ position: fixed;
+ top: 10px;
+ right: 10px;
+ z-index: 10000;
+ background: rgba(0,0,0,0.8);
+ color: white;
+ padding: 10px;
+ border-radius: 5px;
+ font-family: monospace;
+ font-size: 12px;
+ max-width: 300px;
+ `;
+
+ document.body.appendChild(statsDiv);
+
+ // Add control buttons
+ const controlsDiv = document.createElement('div');
+ controlsDiv.style.cssText = `
+ position: fixed;
+ top: 10px;
+ left: 10px;
+ z-index: 10000;
+ background: rgba(0,0,0,0.8);
+ color: white;
+ padding: 10px;
+ border-radius: 5px;
+ font-family: Arial, sans-serif;
+ `;
+
+ // Create controls safely without innerHTML
+ controlsDiv.textContent = '';
+
+ // Create button container
+ const buttonContainer1 = document.createElement('div');
+ buttonContainer1.style.marginBottom = '10px';
+
+ // View Data button
+ const viewDataBtn = document.createElement('button');
+ viewDataBtn.textContent = 'View Data';
+ viewDataBtn.style.cssText = 'margin: 2px; padding: 5px;';
+ viewDataBtn.addEventListener('click', () => apiMonitor.showData());
+ buttonContainer1.appendChild(viewDataBtn);
+
+ // Clear button
+ const clearBtn = document.createElement('button');
+ clearBtn.textContent = 'Clear';
+ clearBtn.style.cssText = 'margin: 2px; padding: 5px;';
+ clearBtn.addEventListener('click', () => apiMonitor.clearData());
+ buttonContainer1.appendChild(clearBtn);
+
+ // Export JSON button
+ const exportJsonBtn = document.createElement('button');
+ exportJsonBtn.textContent = 'Export JSON';
+ exportJsonBtn.style.cssText = 'margin: 2px; padding: 5px;';
+ exportJsonBtn.addEventListener('click', () => apiMonitor.exportData('json'));
+ buttonContainer1.appendChild(exportJsonBtn);
+
+ // Export HAR button
+ const exportHarBtn = document.createElement('button');
+ exportHarBtn.textContent = 'Export HAR';
+ exportHarBtn.style.cssText = 'margin: 2px; padding: 5px;';
+ exportHarBtn.addEventListener('click', () => apiMonitor.exportData('har'));
+ buttonContainer1.appendChild(exportHarBtn);
+
+ controlsDiv.appendChild(buttonContainer1);
+
+ // Create second button container
+ const buttonContainer2 = document.createElement('div');
+ buttonContainer2.style.marginBottom = '10px';
+
+ // Write Logs button
+ const writeLogsBtn = document.createElement('button');
+ writeLogsBtn.textContent = '📁 Write Logs';
+ writeLogsBtn.style.cssText = 'margin: 2px; padding: 5px; background: #4CAF50; color: white;';
+ writeLogsBtn.addEventListener('click', () => apiMonitor.forceWriteLogs());
+ buttonContainer2.appendChild(writeLogsBtn);
+
+ // Log Stats button
+ const logStatsBtn = document.createElement('button');
+ logStatsBtn.textContent = 'Log Stats';
+ logStatsBtn.style.cssText = 'margin: 2px; padding: 5px;';
+ logStatsBtn.addEventListener('click', () => console.log(apiMonitor.getLogStats()));
+ buttonContainer2.appendChild(logStatsBtn);
+
+ controlsDiv.appendChild(buttonContainer2);
+
+ // Create third button container for lib.data controls
+ const buttonContainer3 = document.createElement('div');
+ buttonContainer3.style.marginBottom = '10px';
+
+ // Start Lib.data Monitoring button
+ const startLibDataBtn = document.createElement('button');
+ startLibDataBtn.textContent = '🎮 Start Lib.data';
+ startLibDataBtn.style.cssText = 'margin: 2px; padding: 5px; background: #2196F3; color: white;';
+ startLibDataBtn.addEventListener('click', () => apiMonitor.startLibDataMonitoring());
+ buttonContainer3.appendChild(startLibDataBtn);
+
+ // Stop Lib.data Monitoring button
+ const stopLibDataBtn = document.createElement('button');
+ stopLibDataBtn.textContent = '⏹️ Stop Lib.data';
+ stopLibDataBtn.style.cssText = 'margin: 2px; padding: 5px; background: #f44336; color: white;';
+ stopLibDataBtn.addEventListener('click', () => apiMonitor.stopLibDataMonitoring());
+ buttonContainer3.appendChild(stopLibDataBtn);
+
+ // Lib.data Stats button
+ const libDataStatsBtn = document.createElement('button');
+ libDataStatsBtn.textContent = 'Lib.data Stats';
+ libDataStatsBtn.style.cssText = 'margin: 2px; padding: 5px;';
+ libDataStatsBtn.addEventListener('click', () => console.log(apiMonitor.getLibDataStats()));
+ buttonContainer3.appendChild(libDataStatsBtn);
+
+ // Force Lib.data Check button
+ const forceLibDataBtn = document.createElement('button');
+ forceLibDataBtn.textContent = '🔍 Check Now';
+ forceLibDataBtn.style.cssText = 'margin: 2px; padding: 5px;';
+ forceLibDataBtn.addEventListener('click', () => apiMonitor.forceLibDataCheck());
+ buttonContainer3.appendChild(forceLibDataBtn);
+
+ controlsDiv.appendChild(buttonContainer3);
+
+ document.body.appendChild(controlsDiv);
+
+ // Update stats
+ apiMonitor.updateUI();
+ }
+
+ // Initialize when DOM is ready
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', addUI);
+ } else {
+ addUI();
+ }
+
+ // Console commands
+ console.log('🚀 API Monitor v3.5 loaded! (with lib.data monitoring)');
+ console.log('🔍 DEBUG: Script loaded successfully on:', window.location.href);
+ console.log('🔍 DEBUG: CONFIG.enableFileLogging =', CONFIG.enableFileLogging);
+ console.log('🔍 DEBUG: CONFIG.logToFileInterval =', CONFIG.logToFileInterval);
+ console.log('🔍 DEBUG: Auto-logging will start when API requests are detected');
+ console.log('📊 Available commands:');
+ console.log(' - window.apiMonitor.showData() - View all captured data');
+ console.log(' - window.apiMonitor.clearData() - Clear all data');
+ console.log(' - window.apiMonitor.exportData("json") - Export as JSON');
+ console.log(' - window.apiMonitor.exportData("har") - Export as HAR');
+ console.log(' - window.apiMonitor.getAllData() - Get raw data');
+ console.log('📁 File Logging commands:');
+ console.log(' - window.apiMonitor.forceWriteLogs() - Write logs to file immediately');
+ console.log(' - window.apiMonitor.getLogStats() - Get logging statistics');
+ console.log(` - File logging: ${CONFIG.enableFileLogging ? 'ENABLED' : 'DISABLED'}`);
+ console.log(` - Log format: ${CONFIG.logFormat}`);
+ console.log(` - Auto-save interval: ${CONFIG.logToFileInterval}ms`);
+ console.log('🎮 Lib.data monitoring commands:');
+ console.log(' - window.apiMonitor.startLibDataMonitoring() - Start lib.data monitoring');
+ console.log(' - window.apiMonitor.stopLibDataMonitoring() - Stop lib.data monitoring');
+ console.log(' - window.apiMonitor.getLibDataStats() - Get lib.data statistics');
+ console.log(' - window.apiMonitor.forceLibDataCheck() - Force check lib.data now');
+ console.log(` - Lib.data monitoring: ${CONFIG.enableLibDataMonitoring ? 'ENABLED' : 'DISABLED'}`);
+ console.log(` - Check interval: ${CONFIG.libDataCheckInterval}ms`);
+
+ // Auto-save data periodically
+ setInterval(() => {
+ const data = window.apiMonitor.getAllData();
+ if (data.requests.length > 0 || data.responses.length > 0) {
+ GM_setValue('apiMonitorData', data);
+ }
+ }, 30000); // Save every 30 seconds
+
+ // Auto-write logs to file periodically
+ if (CONFIG.enableFileLogging) {
+ setInterval(() => {
+ console.log('🔍 DEBUG: Auto-logging check - pendingLogs.length =', window.apiMonitor.pendingLogs.length);
+ if (window.apiMonitor.pendingLogs.length > 0) {
+ console.log('🔍 DEBUG: Auto-logging triggered - calling writeLogsToFile');
+ window.apiMonitor.writeLogsToFile();
+ } else {
+ console.log('🔍 DEBUG: Auto-logging skipped - no pending logs');
+ }
+ }, CONFIG.logToFileInterval);
+
+ console.log(`📁 Auto file logging enabled - writing every ${CONFIG.logToFileInterval}ms`);
+ }
+
+ // Auto-start lib.data monitoring
+ if (CONFIG.enableLibDataMonitoring) {
+ // Wait a bit for the page to load, then start monitoring
+ setTimeout(() => {
+ if (typeof lib !== 'undefined' && lib.data) {
+ console.log('🎯 lib.data detected, auto-starting monitoring...');
+ apiMonitor.startLibDataMonitoring();
+ } else {
+ console.log('⏳ Waiting for lib.data to become available...');
+ // Check every second for lib.data
+ const checkInterval = setInterval(() => {
+ if (typeof lib !== 'undefined' && lib.data) {
+ clearInterval(checkInterval);
+ console.log('🎯 lib.data detected, auto-starting monitoring...');
+ apiMonitor.startLibDataMonitoring();
+ }
+ }, 1000);
+ }
+ }, 2000); // Wait 2 seconds after page load
+
+ console.log(`🎮 Auto lib.data monitoring enabled - will start when lib.data is available`);
+ }
+
+
+ // Test API interception with some sample requests (ENABLED FOR DEBUGGING)
+ setTimeout(() => {
+ console.log('🔍 DEBUG: Testing API interception...');
+
+ // Test fetch request
+ fetch('https://httpbin.org/get?test=api-monitor')
+ .then(response => response.json())
+ .then(data => console.log('🔍 DEBUG: Fetch test completed:', data))
+ .catch(error => console.error('🔍 DEBUG: Fetch test failed:', error));
+
+ // Test XHR request
+ const xhr = new XMLHttpRequest();
+ xhr.open('GET', 'https://httpbin.org/get?test=xhr-monitor');
+ xhr.onload = () => console.log('🔍 DEBUG: XHR test completed:', xhr.responseText);
+ xhr.onerror = () => console.error('🔍 DEBUG: XHR test failed');
+ xhr.send();
+
+ }, 3000);
+
+})();
\ No newline at end of file
diff --git a/arena_hero_counts.txt b/arena_hero_counts.txt
new file mode 100644
index 0000000..e8e2d42
--- /dev/null
+++ b/arena_hero_counts.txt
@@ -0,0 +1,89 @@
+Hero Wars Arena Team Hero Counts
+Date: 2025-12-06
+============================================================
+Total teams scraped: 2570
+Total hero occurrences: 15361
+Unique heroes found: 79
+
+============================================================
+HERO OCCURRENCE COUNTS (sorted by frequency)
+============================================================
+Lyria : 1201
+Sebastian : 1110
+Axel : 960
+Lara_Croft : 788
+Aidan : 757
+Khorus : 725
+Nebula : 723
+Dante : 627
+Orion : 531
+Galahad : 421
+Martha : 349
+Asteroth : 333
+Augustus : 314
+Kayla : 287
+Keira : 285
+Electra : 280
+Cascade : 279
+Dorian : 268
+Yasmine : 267
+Amira : 260
+Thea : 257
+Oliver : 256
+Corvus : 251
+Albus : 250
+Guus : 230
+Heidi : 229
+Ishmael : 207
+Iris : 197
+Celeste : 195
+Fafnir : 193
+Helios : 159
+Polaris : 150
+Arachne : 141
+Krista : 138
+Rufus : 137
+Cain : 135
+Tristan : 131
+Aurora : 117
+Isaac : 116
+Morrigan : 97
+Julius : 88
+Jet : 77
+Folio : 75
+Merlin : 67
+Vex : 63
+Fenris : 62
+Cleaver : 55
+Lars : 50
+Jhu : 50
+Qing_Mao : 44
+Mushy_Shroom : 40
+Karkh : 38
+Jorgen : 35
+Ninja_Turtles : 31
+Mara : 26
+Phobos : 25
+Chabba : 23
+Biscuit : 20
+Alvanor : 15
+Satori : 14
+Mojo : 13
+Elmir : 12
+Maya : 11
+Judge : 9
+Artemis : 9
+Faceless : 8
+Ginger : 8
+Lian : 8
+Peppy : 6
+Astrid_Lucas : 5
+Ziri : 4
+Cornelius : 3
+Luther : 3
+Andvari : 3
+Markus : 3
+Dark_Star : 2
+Lilith : 2
+Kai : 2
+Fox : 1
diff --git a/assets/hw-recruit-icons/01.png b/assets/hw-recruit-icons/01.png
new file mode 100644
index 0000000..4b32e9c
Binary files /dev/null and b/assets/hw-recruit-icons/01.png differ
diff --git a/assets/hw-recruit-icons/02.png b/assets/hw-recruit-icons/02.png
new file mode 100644
index 0000000..60bf407
Binary files /dev/null and b/assets/hw-recruit-icons/02.png differ
diff --git a/assets/hw-recruit-icons/03.png b/assets/hw-recruit-icons/03.png
new file mode 100644
index 0000000..df9d412
Binary files /dev/null and b/assets/hw-recruit-icons/03.png differ
diff --git a/assets/hw-recruit-icons/04.png b/assets/hw-recruit-icons/04.png
new file mode 100644
index 0000000..6a6835f
Binary files /dev/null and b/assets/hw-recruit-icons/04.png differ
diff --git a/assets/hw-recruit-icons/05.png b/assets/hw-recruit-icons/05.png
new file mode 100644
index 0000000..83c5bd0
Binary files /dev/null and b/assets/hw-recruit-icons/05.png differ
diff --git a/assets/hw-recruit-icons/06.png b/assets/hw-recruit-icons/06.png
new file mode 100644
index 0000000..57cfc7f
Binary files /dev/null and b/assets/hw-recruit-icons/06.png differ
diff --git a/assets/hw-recruit-icons/07.png b/assets/hw-recruit-icons/07.png
new file mode 100644
index 0000000..2f34196
Binary files /dev/null and b/assets/hw-recruit-icons/07.png differ
diff --git a/assets/hw-recruit-icons/08.png b/assets/hw-recruit-icons/08.png
new file mode 100644
index 0000000..d4e1b21
Binary files /dev/null and b/assets/hw-recruit-icons/08.png differ
diff --git a/assets/hw-recruit-icons/09.png b/assets/hw-recruit-icons/09.png
new file mode 100644
index 0000000..202f0a6
Binary files /dev/null and b/assets/hw-recruit-icons/09.png differ
diff --git a/assets/hw-recruit-icons/10.png b/assets/hw-recruit-icons/10.png
new file mode 100644
index 0000000..4de7384
Binary files /dev/null and b/assets/hw-recruit-icons/10.png differ
diff --git a/assets/hw-recruit-icons/11.png b/assets/hw-recruit-icons/11.png
new file mode 100644
index 0000000..a0214a0
Binary files /dev/null and b/assets/hw-recruit-icons/11.png differ
diff --git a/assets/hw-recruit-icons/12.png b/assets/hw-recruit-icons/12.png
new file mode 100644
index 0000000..af1268b
Binary files /dev/null and b/assets/hw-recruit-icons/12.png differ
diff --git a/assets/hw-recruit-icons/13.png b/assets/hw-recruit-icons/13.png
new file mode 100644
index 0000000..e740afa
Binary files /dev/null and b/assets/hw-recruit-icons/13.png differ
diff --git a/assets/hw-recruit-icons/14.png b/assets/hw-recruit-icons/14.png
new file mode 100644
index 0000000..58b8e8a
Binary files /dev/null and b/assets/hw-recruit-icons/14.png differ
diff --git a/assets/hw-recruit-icons/15.png b/assets/hw-recruit-icons/15.png
new file mode 100644
index 0000000..3282c71
Binary files /dev/null and b/assets/hw-recruit-icons/15.png differ
diff --git a/assets/hw-recruit-icons/16.png b/assets/hw-recruit-icons/16.png
new file mode 100644
index 0000000..797bcf7
Binary files /dev/null and b/assets/hw-recruit-icons/16.png differ
diff --git a/assets/hw-recruit-icons/17.png b/assets/hw-recruit-icons/17.png
new file mode 100644
index 0000000..cbd82e9
Binary files /dev/null and b/assets/hw-recruit-icons/17.png differ
diff --git a/assets/hw-recruit-icons/18.png b/assets/hw-recruit-icons/18.png
new file mode 100644
index 0000000..5375432
Binary files /dev/null and b/assets/hw-recruit-icons/18.png differ
diff --git a/assets/hw-recruit-icons/19.png b/assets/hw-recruit-icons/19.png
new file mode 100644
index 0000000..0c5b3d8
Binary files /dev/null and b/assets/hw-recruit-icons/19.png differ
diff --git a/assets/hw-recruit-icons/20.png b/assets/hw-recruit-icons/20.png
new file mode 100644
index 0000000..b709df8
Binary files /dev/null and b/assets/hw-recruit-icons/20.png differ
diff --git a/assets/hw-recruit-icons/21.png b/assets/hw-recruit-icons/21.png
new file mode 100644
index 0000000..dc78ad7
Binary files /dev/null and b/assets/hw-recruit-icons/21.png differ
diff --git a/assets/hw-recruit-icons/22.png b/assets/hw-recruit-icons/22.png
new file mode 100644
index 0000000..cdca233
Binary files /dev/null and b/assets/hw-recruit-icons/22.png differ
diff --git a/assets/hw-recruit-icons/23.png b/assets/hw-recruit-icons/23.png
new file mode 100644
index 0000000..9e56221
Binary files /dev/null and b/assets/hw-recruit-icons/23.png differ
diff --git a/assets/hw-recruit-icons/24.png b/assets/hw-recruit-icons/24.png
new file mode 100644
index 0000000..85875a5
Binary files /dev/null and b/assets/hw-recruit-icons/24.png differ
diff --git a/assets/hw-recruit-icons/25.png b/assets/hw-recruit-icons/25.png
new file mode 100644
index 0000000..8a207d1
Binary files /dev/null and b/assets/hw-recruit-icons/25.png differ
diff --git a/assets/hw-recruit-icons/26.png b/assets/hw-recruit-icons/26.png
new file mode 100644
index 0000000..0de8e0f
Binary files /dev/null and b/assets/hw-recruit-icons/26.png differ
diff --git a/assets/hw-recruit-icons/27.png b/assets/hw-recruit-icons/27.png
new file mode 100644
index 0000000..b5c6ff4
Binary files /dev/null and b/assets/hw-recruit-icons/27.png differ
diff --git a/assets/hw-recruit-icons/28.png b/assets/hw-recruit-icons/28.png
new file mode 100644
index 0000000..ff892a1
Binary files /dev/null and b/assets/hw-recruit-icons/28.png differ
diff --git a/assets/hw-recruit-icons/29.png b/assets/hw-recruit-icons/29.png
new file mode 100644
index 0000000..0abb64f
Binary files /dev/null and b/assets/hw-recruit-icons/29.png differ
diff --git a/assets/hw-recruit-icons/30.png b/assets/hw-recruit-icons/30.png
new file mode 100644
index 0000000..5682d57
Binary files /dev/null and b/assets/hw-recruit-icons/30.png differ
diff --git a/assets/hw-recruit-icons/31.png b/assets/hw-recruit-icons/31.png
new file mode 100644
index 0000000..54d600f
Binary files /dev/null and b/assets/hw-recruit-icons/31.png differ
diff --git a/assets/hw-recruit-icons/32.png b/assets/hw-recruit-icons/32.png
new file mode 100644
index 0000000..e0559f0
Binary files /dev/null and b/assets/hw-recruit-icons/32.png differ
diff --git a/assets/hw-recruit-icons/33.png b/assets/hw-recruit-icons/33.png
new file mode 100644
index 0000000..cb3fef2
Binary files /dev/null and b/assets/hw-recruit-icons/33.png differ
diff --git a/assets/hw-recruit-icons/34.png b/assets/hw-recruit-icons/34.png
new file mode 100644
index 0000000..5d370e3
Binary files /dev/null and b/assets/hw-recruit-icons/34.png differ
diff --git a/assets/hw-recruit-icons/35.png b/assets/hw-recruit-icons/35.png
new file mode 100644
index 0000000..caf2b02
Binary files /dev/null and b/assets/hw-recruit-icons/35.png differ
diff --git a/assets/hw-recruit-icons/36.png b/assets/hw-recruit-icons/36.png
new file mode 100644
index 0000000..618e031
Binary files /dev/null and b/assets/hw-recruit-icons/36.png differ
diff --git a/assets/hw-recruit-icons/37.png b/assets/hw-recruit-icons/37.png
new file mode 100644
index 0000000..d8f8edf
Binary files /dev/null and b/assets/hw-recruit-icons/37.png differ
diff --git a/assets/hw-recruit-icons/38.png b/assets/hw-recruit-icons/38.png
new file mode 100644
index 0000000..32041d6
Binary files /dev/null and b/assets/hw-recruit-icons/38.png differ
diff --git a/assets/hw-recruit-icons/39.png b/assets/hw-recruit-icons/39.png
new file mode 100644
index 0000000..de4d891
Binary files /dev/null and b/assets/hw-recruit-icons/39.png differ
diff --git a/assets/hw-recruit-icons/40.png b/assets/hw-recruit-icons/40.png
new file mode 100644
index 0000000..7193d47
Binary files /dev/null and b/assets/hw-recruit-icons/40.png differ
diff --git a/assets/hw-recruit-icons/41.png b/assets/hw-recruit-icons/41.png
new file mode 100644
index 0000000..50e07d1
Binary files /dev/null and b/assets/hw-recruit-icons/41.png differ
diff --git a/assets/hw-recruit-icons/42.png b/assets/hw-recruit-icons/42.png
new file mode 100644
index 0000000..42cbc87
Binary files /dev/null and b/assets/hw-recruit-icons/42.png differ
diff --git a/assets/hw-recruit-icons/43.png b/assets/hw-recruit-icons/43.png
new file mode 100644
index 0000000..dfd87fb
Binary files /dev/null and b/assets/hw-recruit-icons/43.png differ
diff --git a/assets/hw-recruit-icons/44.png b/assets/hw-recruit-icons/44.png
new file mode 100644
index 0000000..ba4e1b6
Binary files /dev/null and b/assets/hw-recruit-icons/44.png differ
diff --git a/assets/hw-recruit-icons/45.png b/assets/hw-recruit-icons/45.png
new file mode 100644
index 0000000..30117ee
Binary files /dev/null and b/assets/hw-recruit-icons/45.png differ
diff --git a/assets/hw-recruit-icons/46.png b/assets/hw-recruit-icons/46.png
new file mode 100644
index 0000000..ccae8b5
Binary files /dev/null and b/assets/hw-recruit-icons/46.png differ
diff --git a/assets/hw-recruit-icons/47.png b/assets/hw-recruit-icons/47.png
new file mode 100644
index 0000000..a7600f3
Binary files /dev/null and b/assets/hw-recruit-icons/47.png differ
diff --git a/assets/hw-recruit-icons/48.png b/assets/hw-recruit-icons/48.png
new file mode 100644
index 0000000..d25f99b
Binary files /dev/null and b/assets/hw-recruit-icons/48.png differ
diff --git a/assets/hw-recruit-icons/49.png b/assets/hw-recruit-icons/49.png
new file mode 100644
index 0000000..5699623
Binary files /dev/null and b/assets/hw-recruit-icons/49.png differ
diff --git a/assets/hw-recruit-icons/50.png b/assets/hw-recruit-icons/50.png
new file mode 100644
index 0000000..b586391
Binary files /dev/null and b/assets/hw-recruit-icons/50.png differ
diff --git a/assets/hw-recruit-icons/51.png b/assets/hw-recruit-icons/51.png
new file mode 100644
index 0000000..38f7a74
Binary files /dev/null and b/assets/hw-recruit-icons/51.png differ
diff --git a/assets/hw-recruit-icons/52.png b/assets/hw-recruit-icons/52.png
new file mode 100644
index 0000000..28417b9
Binary files /dev/null and b/assets/hw-recruit-icons/52.png differ
diff --git a/assets/hw-recruit-icons/53.png b/assets/hw-recruit-icons/53.png
new file mode 100644
index 0000000..9e4c3f5
Binary files /dev/null and b/assets/hw-recruit-icons/53.png differ
diff --git a/assets/hw-recruit-icons/54.png b/assets/hw-recruit-icons/54.png
new file mode 100644
index 0000000..5ceafb2
Binary files /dev/null and b/assets/hw-recruit-icons/54.png differ
diff --git a/assets/hw-recruit-icons/55.png b/assets/hw-recruit-icons/55.png
new file mode 100644
index 0000000..ae1a406
Binary files /dev/null and b/assets/hw-recruit-icons/55.png differ
diff --git a/assets/hw-recruit-icons/56.png b/assets/hw-recruit-icons/56.png
new file mode 100644
index 0000000..aa03afb
Binary files /dev/null and b/assets/hw-recruit-icons/56.png differ
diff --git a/assets/hw-recruit-icons/57.png b/assets/hw-recruit-icons/57.png
new file mode 100644
index 0000000..879cebd
Binary files /dev/null and b/assets/hw-recruit-icons/57.png differ
diff --git a/assets/hw-recruit-icons/58.png b/assets/hw-recruit-icons/58.png
new file mode 100644
index 0000000..1deb563
Binary files /dev/null and b/assets/hw-recruit-icons/58.png differ
diff --git a/assets/hw-recruit-icons/59.png b/assets/hw-recruit-icons/59.png
new file mode 100644
index 0000000..d7940b8
Binary files /dev/null and b/assets/hw-recruit-icons/59.png differ
diff --git a/assets/hw-recruit-icons/6--0.png b/assets/hw-recruit-icons/6--0.png
new file mode 100644
index 0000000..21397f7
Binary files /dev/null and b/assets/hw-recruit-icons/6--0.png differ
diff --git a/assets/hw-recruit-icons/6--1.png b/assets/hw-recruit-icons/6--1.png
new file mode 100644
index 0000000..fd7bd03
Binary files /dev/null and b/assets/hw-recruit-icons/6--1.png differ
diff --git a/assets/hw-recruit-icons/6--2.png b/assets/hw-recruit-icons/6--2.png
new file mode 100644
index 0000000..4eafa36
Binary files /dev/null and b/assets/hw-recruit-icons/6--2.png differ
diff --git a/assets/hw-recruit-icons/6--3.png b/assets/hw-recruit-icons/6--3.png
new file mode 100644
index 0000000..5224fd4
Binary files /dev/null and b/assets/hw-recruit-icons/6--3.png differ
diff --git a/assets/hw-recruit-icons/6--4.png b/assets/hw-recruit-icons/6--4.png
new file mode 100644
index 0000000..ec6d73d
Binary files /dev/null and b/assets/hw-recruit-icons/6--4.png differ
diff --git a/assets/hw-recruit-icons/6--5.png b/assets/hw-recruit-icons/6--5.png
new file mode 100644
index 0000000..953c7db
Binary files /dev/null and b/assets/hw-recruit-icons/6--5.png differ
diff --git a/assets/hw-recruit-icons/6--6.png b/assets/hw-recruit-icons/6--6.png
new file mode 100644
index 0000000..bb36722
Binary files /dev/null and b/assets/hw-recruit-icons/6--6.png differ
diff --git a/assets/hw-recruit-icons/6--7.png b/assets/hw-recruit-icons/6--7.png
new file mode 100644
index 0000000..36611e4
Binary files /dev/null and b/assets/hw-recruit-icons/6--7.png differ
diff --git a/assets/hw-recruit-icons/6--8.png b/assets/hw-recruit-icons/6--8.png
new file mode 100644
index 0000000..111b5de
Binary files /dev/null and b/assets/hw-recruit-icons/6--8.png differ
diff --git a/assets/hw-recruit-icons/6--9.png b/assets/hw-recruit-icons/6--9.png
new file mode 100644
index 0000000..6a5a4f8
Binary files /dev/null and b/assets/hw-recruit-icons/6--9.png differ
diff --git a/assets/hw-recruit-icons/60.png b/assets/hw-recruit-icons/60.png
new file mode 100644
index 0000000..d485821
Binary files /dev/null and b/assets/hw-recruit-icons/60.png differ
diff --git a/assets/hw-recruit-icons/61.png b/assets/hw-recruit-icons/61.png
new file mode 100644
index 0000000..69f0b05
Binary files /dev/null and b/assets/hw-recruit-icons/61.png differ
diff --git a/assets/hw-recruit-icons/62.png b/assets/hw-recruit-icons/62.png
new file mode 100644
index 0000000..fe0aa38
Binary files /dev/null and b/assets/hw-recruit-icons/62.png differ
diff --git a/assets/hw-recruit-icons/63.png b/assets/hw-recruit-icons/63.png
new file mode 100644
index 0000000..52ea381
Binary files /dev/null and b/assets/hw-recruit-icons/63.png differ
diff --git a/assets/hw-recruit-icons/64.png b/assets/hw-recruit-icons/64.png
new file mode 100644
index 0000000..5aac1fe
Binary files /dev/null and b/assets/hw-recruit-icons/64.png differ
diff --git a/assets/hw-recruit-icons/65.png b/assets/hw-recruit-icons/65.png
new file mode 100644
index 0000000..bfc0972
Binary files /dev/null and b/assets/hw-recruit-icons/65.png differ
diff --git a/assets/hw-recruit-icons/66.png b/assets/hw-recruit-icons/66.png
new file mode 100644
index 0000000..64a4537
Binary files /dev/null and b/assets/hw-recruit-icons/66.png differ
diff --git a/assets/hw-recruit-icons/67.png b/assets/hw-recruit-icons/67.png
new file mode 100644
index 0000000..5858dd8
Binary files /dev/null and b/assets/hw-recruit-icons/67.png differ
diff --git a/assets/hw-recruit-icons/68.png b/assets/hw-recruit-icons/68.png
new file mode 100644
index 0000000..7923599
Binary files /dev/null and b/assets/hw-recruit-icons/68.png differ
diff --git a/assets/hw-recruit-icons/69.png b/assets/hw-recruit-icons/69.png
new file mode 100644
index 0000000..5347eb2
Binary files /dev/null and b/assets/hw-recruit-icons/69.png differ
diff --git a/assets/hw-recruit-icons/70.png b/assets/hw-recruit-icons/70.png
new file mode 100644
index 0000000..eba1834
Binary files /dev/null and b/assets/hw-recruit-icons/70.png differ
diff --git a/assets/hw-recruit-icons/71.png b/assets/hw-recruit-icons/71.png
new file mode 100644
index 0000000..fc39027
Binary files /dev/null and b/assets/hw-recruit-icons/71.png differ
diff --git a/assets/hw-recruit-icons/72.png b/assets/hw-recruit-icons/72.png
new file mode 100644
index 0000000..fc5effb
Binary files /dev/null and b/assets/hw-recruit-icons/72.png differ
diff --git a/assets/hw-recruit-icons/73.png b/assets/hw-recruit-icons/73.png
new file mode 100644
index 0000000..0812948
Binary files /dev/null and b/assets/hw-recruit-icons/73.png differ
diff --git a/assets/hw-recruit-icons/74.png b/assets/hw-recruit-icons/74.png
new file mode 100644
index 0000000..5cc45aa
Binary files /dev/null and b/assets/hw-recruit-icons/74.png differ
diff --git a/assets/hw-recruit-icons/manifest.json b/assets/hw-recruit-icons/manifest.json
new file mode 100644
index 0000000..74c2341
--- /dev/null
+++ b/assets/hw-recruit-icons/manifest.json
@@ -0,0 +1,91 @@
+{
+ "source": "https://hw-recruit.com/arena",
+ "cachedAt": "2026-08-20T13:34:31.017Z",
+ "count": 84,
+ "filenames": [
+ "01.png",
+ "02.png",
+ "03.png",
+ "04.png",
+ "05.png",
+ "06.png",
+ "07.png",
+ "08.png",
+ "09.png",
+ "10.png",
+ "11.png",
+ "12.png",
+ "13.png",
+ "14.png",
+ "15.png",
+ "16.png",
+ "17.png",
+ "18.png",
+ "19.png",
+ "20.png",
+ "21.png",
+ "22.png",
+ "23.png",
+ "24.png",
+ "25.png",
+ "26.png",
+ "27.png",
+ "28.png",
+ "29.png",
+ "30.png",
+ "31.png",
+ "32.png",
+ "33.png",
+ "34.png",
+ "35.png",
+ "36.png",
+ "37.png",
+ "38.png",
+ "39.png",
+ "40.png",
+ "41.png",
+ "42.png",
+ "43.png",
+ "44.png",
+ "45.png",
+ "46.png",
+ "47.png",
+ "48.png",
+ "49.png",
+ "50.png",
+ "51.png",
+ "52.png",
+ "53.png",
+ "54.png",
+ "55.png",
+ "56.png",
+ "57.png",
+ "58.png",
+ "59.png",
+ "6--0.png",
+ "6--1.png",
+ "6--2.png",
+ "6--3.png",
+ "6--4.png",
+ "6--5.png",
+ "6--6.png",
+ "6--7.png",
+ "6--8.png",
+ "6--9.png",
+ "60.png",
+ "61.png",
+ "62.png",
+ "63.png",
+ "64.png",
+ "65.png",
+ "66.png",
+ "67.png",
+ "68.png",
+ "69.png",
+ "70.png",
+ "71.png",
+ "72.png",
+ "73.png",
+ "74.png"
+ ]
+}
diff --git a/cache-hero-icons.mjs b/cache-hero-icons.mjs
new file mode 100644
index 0000000..7a8526f
--- /dev/null
+++ b/cache-hero-icons.mjs
@@ -0,0 +1,77 @@
+#!/usr/bin/env node
+/**
+ * Download hw-recruit hero/pet PNG icons into assets/hw-recruit-icons/.
+ * Run: npm run icons:cache
+ */
+import fs from 'fs';
+import path from 'path';
+import { HERO_NAMES, PET_NAMES } from './hero-names.mjs';
+import {
+ ICONS_DIR,
+ HW_RECRUIT_REMOTE_BASE,
+ hwRecruitIconFilename,
+ listCachedIconFilenames,
+} from './hero-icons.mjs';
+
+async function main() {
+ const force = process.argv.includes('--force');
+ fs.mkdirSync(ICONS_DIR, { recursive: true });
+
+ const unitIds = [
+ ...Object.keys(HERO_NAMES).map(Number),
+ ...Object.keys(PET_NAMES).map(Number),
+ ];
+ const filenames = [...new Set(unitIds.map(hwRecruitIconFilename).filter(Boolean))].sort();
+
+ console.log(`[icons] Target directory: ${ICONS_DIR}`);
+ console.log(`[icons] Downloading ${filenames.length} icons from hw-recruit.com…`);
+
+ const results = { saved: 0, skipped: 0, failed: 0 };
+ for (const filename of filenames) {
+ const remoteUrl = `${HW_RECRUIT_REMOTE_BASE}/${filename}`;
+ const dest = path.join(ICONS_DIR, filename);
+ if (!force && fs.existsSync(dest)) {
+ results.skipped++;
+ continue;
+ }
+
+ try {
+ const response = await fetch(remoteUrl, {
+ headers: { 'User-Agent': 'AutoHero/1.0 (local icon cache)' },
+ });
+ if (!response.ok) {
+ console.warn(`[icons] FAIL ${filename} (${response.status}) ${remoteUrl}`);
+ results.failed++;
+ continue;
+ }
+ const buffer = Buffer.from(await response.arrayBuffer());
+ fs.writeFileSync(dest, buffer);
+ results.saved++;
+ console.log(`[icons] saved ${filename} (${buffer.length} bytes)`);
+ } catch (error) {
+ console.warn(`[icons] FAIL ${filename}: ${error.message}`);
+ results.failed++;
+ }
+ }
+
+ const manifest = {
+ source: 'https://hw-recruit.com/arena',
+ cachedAt: new Date().toISOString(),
+ count: listCachedIconFilenames().length,
+ filenames: listCachedIconFilenames(),
+ };
+ fs.writeFileSync(
+ path.join(ICONS_DIR, 'manifest.json'),
+ `${JSON.stringify(manifest, null, 2)}\n`
+ );
+
+ console.log(`[icons] Done — saved ${results.saved}, skipped ${results.skipped}, failed ${results.failed}`);
+ if (results.failed > 0) {
+ process.exitCode = 1;
+ }
+}
+
+main().catch((error) => {
+ console.error('[icons] Fatal:', error);
+ process.exit(1);
+});
diff --git a/chromedriver.exe b/chromedriver.exe
deleted file mode 100644
index 17a6dad..0000000
Binary files a/chromedriver.exe and /dev/null differ
diff --git a/create_ics_from_extracted_schedule.py b/create_ics_from_extracted_schedule.py
new file mode 100644
index 0000000..8bd741f
--- /dev/null
+++ b/create_ics_from_extracted_schedule.py
@@ -0,0 +1,656 @@
+"""
+Extract and display Hero Wars events from schedule_extracted.csv
+Outputs today's events and upcoming 7 days events in email-friendly format
+Sends email automatically after generation
+
+CSV Format Documentation:
+==========================
+The CSV file can contain events in two formats:
+
+1. OLD FORMAT (used in most of the file):
+ Event Name: YYYY-MM-DD HH:MM:SS AM/PM - YYYY-MM-DD HH:MM:SS AM/PM
+ Example: "Elemental Synergy: 2025-11-29 06:00:00 PM - 2025-12-02 06:00:00 PM"
+
+ Followed by task lines (each task on its own line, with values on the next line):
+ Task Name - Description
+ value1 value2 value3 ...
+
+2. NEW FORMAT (used in recent events):
+ EventName:Event Name: YYYY-MM-DD HH:MM:SS AM/PM - YYYY-MM-DD HH:MM:SS AM/PM
+ Example: "EventName:Legacy of the Great Ones: 2025-10-01 07:00:00 PM - 2025-10-04 07:00:00 PM"
+
+ Followed by task lines (with optional "Task:" prefix):
+ Task:Task Name - Description
+ value1 value2 value3 ...
+
+ Note: Some tasks may not have the "Task:" prefix in the new format.
+"""
+import re
+import smtplib
+import os
+from datetime import datetime, timedelta
+from pathlib import Path
+from email.mime.text import MIMEText
+from email.mime.multipart import MIMEMultipart
+from dotenv import load_dotenv
+
+# Load environment variables from .env file
+load_dotenv()
+
+
+def correct_event_end_date(start_datetime, end_datetime):
+ """
+ Auto-correct event end dates that appear to have year errors.
+ If an event appears to be longer than 365 days, it's likely a data error
+ where the end year is wrong. Correct it to match the start year.
+
+ Returns the corrected end_datetime.
+ """
+ duration = (end_datetime - start_datetime).days
+
+ # If event is longer than 365 days, it's likely a data error
+ if duration > 365:
+ # Check if the end year is different from start year
+ if end_datetime.year != start_datetime.year:
+ # Correct the end year to match start year, keeping month/day/time
+ corrected_end = end_datetime.replace(year=start_datetime.year)
+
+ # Verify the corrected duration is reasonable
+ corrected_duration = (corrected_end - start_datetime).days
+
+ # If corrected date is before start, the correction doesn't make sense
+ # (e.g., start 12-20, end 01-05 - can't correct year in this case)
+ if corrected_end < start_datetime:
+ print(f"[WARNING] Could not auto-correct event date: "
+ f"Start {start_datetime.strftime('%Y-%m-%d')}, "
+ f"End {end_datetime.strftime('%Y-%m-%d')} (duration: {duration} days)")
+ return end_datetime
+
+ # If corrected duration is reasonable (0-365 days), use the correction
+ if 0 <= corrected_duration <= 365:
+ print(f"[AUTO-CORRECT] Fixed event date error: "
+ f"End date {end_datetime.strftime('%Y-%m-%d')} corrected to "
+ f"{corrected_end.strftime('%Y-%m-%d')} "
+ f"(duration was {duration} days, now {corrected_duration} days)")
+ return corrected_end
+
+ return end_datetime
+
+
+def parse_event_line(line):
+ """
+ Parse a line to extract event name and date range.
+
+ Supports both formats:
+ - Old: "Event Name: YYYY-MM-DD HH:MM:SS AM/PM - YYYY-MM-DD HH:MM:SS AM/PM"
+ - New: "EventName:Event Name: YYYY-MM-DD HH:MM:SS AM/PM - YYYY-MM-DD HH:MM:SS AM/PM"
+
+ Returns dict with 'name', 'start', 'end' keys, or None if line doesn't match.
+ """
+ line = line.strip()
+ date_pattern = r'(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})\s+([AP]M)'
+
+ def parse_datetime(date_str, time_str, am_pm):
+ """Helper to parse datetime from components"""
+ full_date_str = f"{date_str} {time_str} {am_pm}"
+ return datetime.strptime(full_date_str, "%Y-%m-%d %I:%M:%S %p")
+
+ # Try new format first: EventName:Event Name: DATE - DATE
+ new_format_pattern = rf'EventName:(.+?):\s*{date_pattern}\s*-\s*{date_pattern}'
+ match = re.match(new_format_pattern, line)
+
+ if match:
+ event_name = match.group(1).strip()
+ start_datetime = parse_datetime(match.group(2), match.group(3), match.group(4))
+ end_datetime = parse_datetime(match.group(5), match.group(6), match.group(7))
+ else:
+ # Try old format: Event Name: DATE - DATE
+ old_format_pattern = rf'(.+?):\s*{date_pattern}\s*-\s*{date_pattern}'
+ match = re.match(old_format_pattern, line)
+
+ if not match:
+ return None
+
+ event_name = match.group(1).strip()
+ start_datetime = parse_datetime(match.group(2), match.group(3), match.group(4))
+ end_datetime = parse_datetime(match.group(5), match.group(6), match.group(7))
+
+ # Clean up event name
+ # Remove quotes if present
+ if event_name.startswith('"') and event_name.endswith('"'):
+ event_name = event_name[1:-1]
+ # Fix double quotes
+ event_name = event_name.replace('""', '"')
+
+ # Auto-correct date errors (e.g., events that appear to be > 1 year long)
+ end_datetime = correct_event_end_date(start_datetime, end_datetime)
+
+ return {
+ 'name': event_name,
+ 'start': start_datetime,
+ 'end': end_datetime
+ }
+
+
+def parse_schedule_csv_with_tasks(csv_file):
+ """
+ Parse schedule CSV and extract events with dates and tasks.
+ Returns list of event dicts with 'name', 'start', 'end', 'tasks' keys.
+ """
+ events = []
+ current_event = None
+
+ with open(csv_file, 'r', encoding='utf-8') as f:
+ lines = f.readlines()
+
+ i = 0
+ while i < len(lines):
+ line = lines[i].strip()
+
+ if not line:
+ i += 1
+ continue
+
+ # Check if line is an event header (contains date pattern)
+ if re.search(r'\d{4}-\d{2}-\d{2}.*\d{2}:\d{2}:\d{2}.*[AP]M.*-\s*\d{4}-\d{2}-\d{2}.*\d{2}:\d{2}:\d{2}.*[AP]M', line):
+ # Save previous event if exists
+ if current_event:
+ events.append(current_event)
+
+ # Parse new event
+ event_data = parse_event_line(line)
+ if event_data:
+ current_event = {
+ 'name': event_data['name'],
+ 'start': event_data['start'],
+ 'end': event_data['end'],
+ 'tasks': []
+ }
+ elif current_event:
+ # Check if this is a task line (starts with "Task:" or contains " - " or is just a task name)
+ # Next line should be values (numbers)
+ if i + 1 < len(lines):
+ next_line = lines[i + 1].strip()
+ # Check if next line contains only numbers (task values)
+ if re.match(r'^[\d\s]+$', next_line):
+ # This is a task name, next line has values
+ task_name = line
+ # Remove "Task:" prefix if present
+ if task_name.startswith('Task:'):
+ task_name = task_name[5:].strip()
+ # Remove trailing colon if present
+ if task_name.endswith(':'):
+ task_name = task_name[:-1].strip()
+
+ task_values = next_line.split()
+ current_event['tasks'].append({
+ 'name': task_name,
+ 'values': task_values
+ })
+ i += 1 # Skip the values line
+ else:
+ # Not a task, might be continuation or empty
+ pass
+
+ i += 1
+
+ # Add last event
+ if current_event:
+ events.append(current_event)
+
+ return events
+
+
+def get_events_for_date(events, target_date):
+ """Get all events that are active on a specific date"""
+ date_start = datetime(target_date.year, target_date.month, target_date.day)
+ date_end = date_start + timedelta(days=1)
+
+ active_events = []
+ for event in events:
+ # Event is active if it starts before date_end and ends on or after date_start
+ # Also ensure event hasn't already ended before the target date (compare dates, not datetimes)
+ event_end_date = event['end'].date()
+ event_start_date = event['start'].date()
+
+ # Skip events that have already ended before the target date
+ if event_end_date < target_date:
+ continue
+
+ # Event is active if it starts before date_end and ends on or after date_start
+ if event['start'] < date_end and event['end'] >= date_start:
+ active_events.append(event)
+
+ return sorted(active_events, key=lambda x: x['start'])
+
+
+def format_event_details(event):
+ """Format a single event with its tasks"""
+ lines = []
+
+ # Event header
+ lines.append(f"Event: {event['name']}")
+
+ # Event time range
+ start_str = event['start'].strftime("%m-%d-%Y %I:%M %p")
+ end_str = event['end'].strftime("%m-%d-%Y %I:%M %p")
+ lines.append(f" Period: {start_str} to {end_str}")
+ lines.append("")
+
+ # Tasks
+ if event['tasks']:
+ lines.append(" Tasks to Complete:")
+ for task in event['tasks']:
+ task_name = simplify_task_name(task['name'])
+
+ # Show only the largest milestone value
+ if task['values']:
+ try:
+ # Convert all values to integers and find the maximum
+ max_value = max(int(val) for val in task['values'])
+ lines.append(f" - {task_name} ({max_value})")
+ except (ValueError, TypeError):
+ # If values can't be converted to int, show the last one
+ max_value = task['values'][-1]
+ lines.append(f" - {task_name} ({max_value})")
+ else:
+ lines.append(f" - {task_name}")
+ else:
+ lines.append(" No tasks available for this event.")
+ lines.append("")
+
+ lines.append("") # Empty line between events
+
+ return "\n".join(lines)
+
+
+def simplify_task_name(task_name):
+ """Simplify task name by removing prefix before ' - '"""
+ if " - " in task_name:
+ return task_name.split(" - ", 1)[1] # Get part after " - "
+ return task_name
+
+
+def categorize_events_by_date(events, target_date):
+ """
+ Categorize events into ending, ongoing, and starting events for a given date.
+ Returns tuple: (ending_events, ongoing_events, starting_events)
+ """
+ ending_events = []
+ ongoing_events = []
+ starting_events = []
+
+ for event in events:
+ event_end_date = event['end'].date()
+ event_start_date = event['start'].date()
+
+ if event_end_date == target_date:
+ # Event ends today (whether it started today or before)
+ ending_events.append(event)
+ elif event_start_date < target_date and event_end_date > target_date:
+ # Event is ongoing (started before today, ends after today)
+ ongoing_events.append(event)
+ elif event_start_date == target_date and event_end_date > target_date:
+ # Event starts today and continues
+ starting_events.append(event)
+
+ # Sort each category by start time
+ ending_events.sort(key=lambda x: x['start'])
+ ongoing_events.sort(key=lambda x: x['start'])
+ starting_events.sort(key=lambda x: x['start'])
+
+ return ending_events, ongoing_events, starting_events
+
+
+def generate_task_summary(ending_events, ongoing_events):
+ """Generate a summary of tasks from ending and ongoing events, counting duplicates"""
+ task_counts = {}
+ task_max_values = {}
+ task_from_ending = {} # Track which tasks are from ending events
+
+ # Collect tasks from ending events (must complete today)
+ for event in ending_events:
+ if event.get('tasks'):
+ for task in event['tasks']:
+ task_name = simplify_task_name(task['name'])
+
+ # Mark as from ending event
+ task_from_ending[task_name] = True
+
+ # Get max milestone value
+ max_value = None
+ if task.get('values'):
+ try:
+ max_value = max(int(val) for val in task['values'])
+ except (ValueError, TypeError):
+ max_value = task['values'][-1] if task['values'] else None
+
+ # Count occurrences
+ if task_name not in task_counts:
+ task_counts[task_name] = 0
+ task_max_values[task_name] = []
+ task_counts[task_name] += 1
+ if max_value is not None:
+ task_max_values[task_name].append(max_value)
+
+ # Collect tasks from ongoing events (can do later)
+ for event in ongoing_events:
+ if event.get('tasks'):
+ for task in event['tasks']:
+ task_name = simplify_task_name(task['name'])
+
+ # Get max milestone value
+ max_value = None
+ if task.get('values'):
+ try:
+ max_value = max(int(val) for val in task['values'])
+ except (ValueError, TypeError):
+ max_value = task['values'][-1] if task['values'] else None
+
+ # Count occurrences
+ if task_name not in task_counts:
+ task_counts[task_name] = 0
+ task_max_values[task_name] = []
+ task_counts[task_name] += 1
+ if max_value is not None:
+ task_max_values[task_name].append(max_value)
+
+ # Format summary
+ summary_lines = []
+ if task_counts:
+ summary_lines.append(">>> TASK SUMMARY:")
+ summary_lines.append("(Note: Tasks from events ending today are marked with &)")
+ summary_lines.append("")
+
+ # Sort tasks: duplicates first, then alphabetically
+ sorted_tasks = sorted(task_counts.items(), key=lambda x: (-x[1], x[0]))
+
+ for task_name, count in sorted_tasks:
+ # Get max value for display (use highest max if multiple)
+ max_values = task_max_values.get(task_name, [])
+ if max_values:
+ display_max = max(max_values) if isinstance(max_values[0], int) else max_values[-1]
+ task_display = f"{task_name} ({display_max})"
+ else:
+ task_display = task_name
+
+ # Mark if from ending event
+ ending_marker = " &" if task_from_ending.get(task_name, False) else ""
+
+ # Highlight duplicates
+ if count > 1:
+ summary_lines.append(f" *** {task_display}{ending_marker} [x{count}] ***")
+ else:
+ summary_lines.append(f" - {task_display}{ending_marker}")
+
+ summary_lines.append("")
+
+ return "\n".join(summary_lines)
+
+
+def format_day_section(date, events):
+ """Format a single day's events and tasks, organized by: ending events, then ongoing events"""
+ lines = []
+
+ # Date header
+ date_str = date.strftime("%m-%d-%Y")
+ day_name = date.strftime("%A")
+
+ lines.append("=" * 80)
+ lines.append(f"{day_name} {date_str}")
+ lines.append("=" * 80)
+ lines.append("")
+
+ if not events:
+ lines.append("No events scheduled for this day.")
+ lines.append("")
+ return "\n".join(lines)
+
+ # Group events by name (in case of duplicates)
+ seen_events = {}
+ for event in events:
+ event_name = event['name']
+ # Use event name as key, store the event
+ if event_name not in seen_events:
+ seen_events[event_name] = event
+
+ # Categorize events
+ target_date = date
+ ending_events, ongoing_events, starting_events = categorize_events_by_date(
+ list(seen_events.values()), target_date
+ )
+
+ # Generate task summary for ending + ongoing events
+ task_summary = generate_task_summary(ending_events, ongoing_events)
+ if task_summary:
+ lines.append(task_summary)
+
+ # Display ending events first
+ if ending_events:
+ lines.append(">>> ENDING EVENTS:")
+ lines.append("")
+ for event in ending_events:
+ lines.append(format_event_details(event))
+
+ # Display ongoing events
+ if ongoing_events:
+ if ending_events:
+ lines.append("") # Extra spacing between sections
+ lines.append(">>> ONGOING EVENTS:")
+ lines.append("")
+ for event in ongoing_events:
+ lines.append(format_event_details(event))
+
+ # Display starting events
+ if starting_events:
+ if ending_events or ongoing_events:
+ lines.append("") # Extra spacing between sections
+ lines.append(">>> STARTING EVENTS:")
+ lines.append("")
+ for event in starting_events:
+ lines.append(format_event_details(event))
+
+ return "\n".join(lines)
+
+
+def format_email_output_day_by_day(events, days_ahead=7):
+ """Format events organized by day (today + next N days)"""
+ output = []
+
+ # Header
+ output.append("=" * 80)
+ output.append("HERO WARS EVENT SCHEDULE - DAY BY DAY")
+ output.append("=" * 80)
+ output.append(f"Generated: {datetime.now().strftime('%A, %B %d, %Y at %I:%M %p')}")
+ output.append("")
+
+ # Get today and next N days
+ today = datetime.now().date()
+
+ for day_offset in range(days_ahead + 1):
+ current_date = today + timedelta(days=day_offset)
+ events_for_day = get_events_for_date(events, current_date)
+
+ # Format this day's section
+ day_section = format_day_section(current_date, events_for_day)
+ output.append(day_section)
+
+ output.append("=" * 80)
+ output.append("End of Schedule")
+ output.append("=" * 80)
+
+ return "\n".join(output)
+
+
+def send_email(email_body, to_email="mailming@gmail.com"):
+ """Send email with schedule to recipient"""
+ # Gmail SMTP configuration - loaded from environment variables
+ smtp_host = os.getenv("SMTP_HOST", "smtp.gmail.com")
+ smtp_port = int(os.getenv("SMTP_PORT", "587"))
+ smtp_user = os.getenv("SMTP_USER")
+ smtp_password = os.getenv("SMTP_PASSWORD")
+
+ # Validate required credentials
+ if not smtp_user or not smtp_password:
+ print("[ERROR] SMTP credentials not found in .env file")
+ print("[ERROR] Please ensure SMTP_USER and SMTP_PASSWORD are set in .env")
+ return False
+
+ try:
+ # Create message
+ msg = MIMEMultipart()
+ msg['From'] = smtp_user
+ msg['To'] = to_email
+ msg['Subject'] = f"Hero Wars Event Schedule - {datetime.now().strftime('%B %d, %Y')}"
+
+ # Add body
+ msg.attach(MIMEText(email_body, 'plain', 'utf-8'))
+
+ # Connect to server and send
+ print(f"[INFO] Connecting to SMTP server...")
+ server = smtplib.SMTP(smtp_host, smtp_port)
+ server.starttls()
+ server.login(smtp_user, smtp_password)
+
+ print(f"[INFO] Sending email to {to_email}...")
+ text = msg.as_string()
+ server.sendmail(smtp_user, to_email, text)
+ server.quit()
+
+ print(f"[SUCCESS] Email sent successfully to {to_email}")
+ return True
+
+ except Exception as e:
+ print(f"[ERROR] Failed to send email: {e}")
+ import traceback
+ traceback.print_exc()
+ return False
+
+
+def scrape_fresh_schedule():
+ """Scrape fresh schedule data from the website"""
+ try:
+ # Import scraping functions
+ import importlib.util
+
+ # Load scrape_schedule_to_csv module
+ spec = importlib.util.spec_from_file_location("scrape_schedule", "scrape_schedule_to_csv.py")
+ scrape_module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(scrape_module)
+
+ # Call the scraping main function
+ print("[INFO] Fetching fresh schedule data from website...")
+ scrape_module.main()
+
+ return True
+ except Exception as e:
+ print(f"[ERROR] Failed to scrape fresh data: {e}")
+ import traceback
+ traceback.print_exc()
+ return False
+
+
+def should_send_email(events, target_date):
+ """
+ Check if email should be sent based on conditions:
+ 1) More than 3 (starting + ongoing) events today
+ 2) "Hero Tournament of Power" or "Tournament of Titan Power" event ending today
+ """
+ # Get events for today
+ today_events = get_events_for_date(events, target_date)
+
+ # Group events by name (in case of duplicates)
+ seen_events = {}
+ for event in today_events:
+ event_name = event['name']
+ if event_name not in seen_events:
+ seen_events[event_name] = event
+
+ # Categorize events
+ ending_events, ongoing_events, starting_events = categorize_events_by_date(
+ list(seen_events.values()), target_date
+ )
+
+ # Condition 1: More than 3 (starting + ongoing) events
+ starting_ongoing_count = len(starting_events) + len(ongoing_events)
+ condition1_met = starting_ongoing_count > 3
+
+ # Condition 2: Tournament events ending today
+ tournament_names = ["Hero Tournament of Power", "Tournament of Titan Power"]
+ condition2_met = any(event['name'] in tournament_names for event in ending_events)
+
+ # Log the check
+ print(f"\n[INFO] Email trigger check:")
+ print(f" Starting + Ongoing events: {starting_ongoing_count} (need > 3: {condition1_met})")
+ print(f" Tournament ending today: {condition2_met}")
+ if condition2_met:
+ tournament_ending = [e['name'] for e in ending_events if e['name'] in tournament_names]
+ print(f" Tournaments ending: {', '.join(tournament_ending)}")
+
+ return condition1_met or condition2_met
+
+
+def main():
+ csv_file = 'schedule_extracted.csv'
+
+ # Always fetch fresh data from website first
+ print("=" * 80)
+ print("FETCHING FRESH DATA FROM WEBSITE")
+ print("=" * 80)
+ if not scrape_fresh_schedule():
+ print("[WARNING] Failed to fetch fresh data, using existing CSV file if available")
+
+ # Check if CSV file exists before trying to parse
+ if not Path(csv_file).exists():
+ print(f"\n[ERROR] CSV file '{csv_file}' not found.")
+ print("[ERROR] Cannot proceed without schedule data. Please ensure scraping succeeded or file exists.")
+ return
+
+ print("\n" + "=" * 80)
+ print("PARSING SCHEDULE DATA")
+ print("=" * 80)
+ print(f"[INFO] Parsing {csv_file}...")
+ events = parse_schedule_csv_with_tasks(csv_file)
+
+ if not events:
+ print("[ERROR] No events found in CSV file")
+ return
+
+ print(f"[OK] Found {len(events)} total events")
+
+ # Filter out events that have already ended (e.g., events from previous years)
+ today = datetime.now().date()
+ events_before_filter = len(events)
+ events = [event for event in events if event['end'].date() >= today]
+ events_after_filter = len(events)
+
+ if events_before_filter != events_after_filter:
+ print(f"[INFO] Filtered out {events_before_filter - events_after_filter} past events (ended before today)")
+ print(f"[OK] Processing {len(events)} active/upcoming events")
+
+ # Generate day-by-day email-friendly output (today + next 7 days)
+ email_output = format_email_output_day_by_day(events, days_ahead=7)
+
+ # Also save to file
+ output_file = 'hero_wars_events_email.txt'
+ with open(output_file, 'w', encoding='utf-8') as f:
+ f.write(email_output)
+
+ # Print summary to console
+ today_events = get_events_for_date(events, today)
+
+ print(f"\n[SUCCESS] Email-formatted output saved to: {output_file}")
+ print(f" Today's events: {len(today_events)}")
+ print(f" Schedule covers: {today.strftime('%m-%d-%Y')} to {(today + timedelta(days=7)).strftime('%m-%d-%Y')}")
+ print(f"\n[INFO] Full day-by-day schedule available in: {output_file}")
+
+ # Check if email should be sent
+ if should_send_email(events, today):
+ print(f"\n[INFO] Email trigger conditions met. Sending email...")
+ send_email(email_output)
+ else:
+ print(f"\n[INFO] Email trigger conditions not met. Skipping email send.")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/db-backfill-matchups.mjs b/db-backfill-matchups.mjs
new file mode 100644
index 0000000..b1f8dd8
--- /dev/null
+++ b/db-backfill-matchups.mjs
@@ -0,0 +1,21 @@
+#!/usr/bin/env node
+import { initDatabase, backfillAllMatchups, getTrainingSummary, closeDatabase } from './training-db.mjs';
+
+async function main() {
+ await initDatabase();
+ const result = await backfillAllMatchups();
+ const summary = await getTrainingSummary();
+ console.log('Backfill complete:', result);
+ console.log('Summary:', {
+ opponentCombos: summary.opponentComboCount,
+ matchupTests: summary.matchupTestCount,
+ rounds: summary.roundCount,
+ });
+ await closeDatabase();
+}
+
+main().catch(async (error) => {
+ console.error('Backfill failed:', error.message);
+ await closeDatabase();
+ process.exit(1);
+});
diff --git a/grand-arena-selection.mjs b/grand-arena-selection.mjs
new file mode 100644
index 0000000..369b86d
--- /dev/null
+++ b/grand-arena-selection.mjs
@@ -0,0 +1,109 @@
+/** Find Grand Arena triplets: 3 combos, 15 unique heroes, maximize ≥90% win counts. */
+
+function heroIds(combo) {
+ return (combo.myHeroIds || []).map(Number).filter((id) => id > 0 && id < 6000);
+}
+
+function setsDisjoint(heroSet, heroes) {
+ for (const id of heroes) {
+ if (heroSet.has(id)) return false;
+ }
+ return true;
+}
+
+function comboKey(combo) {
+ const heroes = heroIds(combo).slice().sort((a, b) => a - b).join(',');
+ const pet = combo.myPet != null ? Number(combo.myPet) : '';
+ return `${heroes}|${pet}`;
+}
+
+function unionHeroIds(teams) {
+ const ids = new Set();
+ for (const team of teams) {
+ for (const id of heroIds(team)) {
+ ids.add(id);
+ }
+ }
+ return ids;
+}
+
+function matchesRequiredHeroes(teams, requiredHeroIds = []) {
+ const required = (requiredHeroIds || []).map(Number).filter((id) => id > 0);
+ if (!required.length) {
+ return true;
+ }
+
+ const heroUnion = unionHeroIds(teams);
+ const requiredHeroes = required.filter((id) => id < 6000);
+ const requiredPets = required.filter((id) => id >= 6000);
+
+ for (const id of requiredHeroes) {
+ if (!heroUnion.has(id)) {
+ return false;
+ }
+ }
+ for (const petId of requiredPets) {
+ if (!teams.some((team) => Number(team.myPet) === petId)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+export function findGrandArenaSelections(combos, {
+ heroesPerTeam = 5,
+ maxResults = 500,
+ requiredHeroIds = [],
+} = {}) {
+ const validCombos = combos
+ .map((combo) => ({
+ ...combo,
+ heroes: heroIds(combo),
+ highWinCount: Number(combo.highWinCount) || 0,
+ }))
+ .filter((combo) => combo.heroes.length === heroesPerTeam);
+
+ const results = [];
+ const seen = new Set();
+ const n = validCombos.length;
+
+ for (let i = 0; i < n; i++) {
+ const comboA = validCombos[i];
+ const heroesA = new Set(comboA.heroes);
+ for (let j = i + 1; j < n; j++) {
+ const comboB = validCombos[j];
+ if (!setsDisjoint(heroesA, comboB.heroes)) continue;
+ const heroesAB = new Set([...heroesA, ...comboB.heroes]);
+ for (let k = j + 1; k < n; k++) {
+ const comboC = validCombos[k];
+ if (!setsDisjoint(heroesAB, comboC.heroes)) continue;
+
+ const keys = [comboA, comboB, comboC].map(comboKey).sort();
+ const dedupeKey = keys.join('||');
+ if (seen.has(dedupeKey)) continue;
+ seen.add(dedupeKey);
+
+ const highWins = [comboA.highWinCount, comboB.highWinCount, comboC.highWinCount];
+ const teams = [comboA, comboB, comboC];
+ if (!matchesRequiredHeroes(teams, requiredHeroIds)) {
+ continue;
+ }
+ results.push({
+ teams,
+ totalHighWinCount: highWins[0] + highWins[1] + highWins[2],
+ minHighWinCount: Math.min(...highWins),
+ });
+ }
+ }
+ }
+
+ results.sort((a, b) => (
+ b.totalHighWinCount - a.totalHighWinCount
+ || b.minHighWinCount - a.minHighWinCount
+ ));
+
+ if (maxResults > 0) {
+ return results.slice(0, maxResults);
+ }
+ return results;
+}
diff --git a/hero-icons.mjs b/hero-icons.mjs
new file mode 100644
index 0000000..55da278
--- /dev/null
+++ b/hero-icons.mjs
@@ -0,0 +1,158 @@
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import { HERO_NAMES, PET_NAMES, formatCombo, resolveHeroName } from './hero-names.mjs';
+
+const ROOT = path.dirname(fileURLToPath(import.meta.url));
+
+/** Remote source (hw-recruit arena team icons). */
+export const HW_RECRUIT_REMOTE_BASE = 'https://hw-recruit.com/modules/hwrecruit/images';
+
+/** On-disk cache directory and web path served by the bridge. */
+export const ICONS_DIR = path.join(ROOT, 'assets', 'hw-recruit-icons');
+export const LOCAL_ICON_WEB_PATH = '/icons/hw-recruit';
+
+function escapeHtml(value) {
+ return String(value ?? '')
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"');
+}
+
+/**
+ * hw-recruit filenames: heroes use zero-padded ids (07.png), pets use 6--N.png (6008 → 6--8.png).
+ */
+export function hwRecruitIconFilename(unitId) {
+ const id = Number(unitId);
+ if (!Number.isFinite(id) || id <= 0) return null;
+ if (id >= 6000 && id < 7000) {
+ const suffix = id - 6000;
+ if (suffix < 0 || suffix > 9) return null;
+ return `6--${suffix}.png`;
+ }
+ if (id < 6000) {
+ return `${String(id).padStart(2, '0')}.png`;
+ }
+ return null;
+}
+
+export function hwRecruitRemoteIconUrl(unitId) {
+ const filename = hwRecruitIconFilename(unitId);
+ return filename ? `${HW_RECRUIT_REMOTE_BASE}/${filename}` : null;
+}
+
+export function localIconPath(unitId) {
+ const filename = hwRecruitIconFilename(unitId);
+ return filename ? path.join(ICONS_DIR, filename) : null;
+}
+
+export function localIconUrl(unitId) {
+ const filename = hwRecruitIconFilename(unitId);
+ if (!filename) return null;
+ const filePath = path.join(ICONS_DIR, filename);
+ if (!fs.existsSync(filePath)) return null;
+ return `${LOCAL_ICON_WEB_PATH}/${filename}`;
+}
+
+/** Prefer cached local icon; fall back to remote only if missing on disk. */
+export function iconUrlForUnit(unitId) {
+ return localIconUrl(unitId) || hwRecruitRemoteIconUrl(unitId);
+}
+
+export function listCachedIconFilenames() {
+ if (!fs.existsSync(ICONS_DIR)) return [];
+ return fs.readdirSync(ICONS_DIR)
+ .filter((name) => name.endsWith('.png'))
+ .sort();
+}
+
+/** All known heroes/pets → icon URL (local when cached). */
+export function buildIconMap() {
+ const map = {};
+ for (const id of Object.keys(HERO_NAMES)) {
+ const url = iconUrlForUnit(id);
+ if (url) map[id] = url;
+ }
+ for (const id of Object.keys(PET_NAMES)) {
+ const url = iconUrlForUnit(id);
+ if (url) map[id] = url;
+ }
+ return map;
+}
+
+export const UNIT_ICON_MAP = buildIconMap();
+
+export function renderUnitNameHtml(unitId, displayName = null) {
+ const id = Number(unitId);
+ const label = escapeHtml(displayName ?? resolveHeroName(id));
+ const iconUrl = UNIT_ICON_MAP[id] || iconUrlForUnit(id);
+ if (!iconUrl) {
+ return `${label} `;
+ }
+ return `${label} `;
+}
+
+export function renderHeroListHtml(heroIds, heroNamesOverride = null) {
+ const combo = formatCombo(heroIds, null, heroNamesOverride);
+ const parts = combo.heroIds.map((heroId, index) => renderUnitNameHtml(heroId, combo.heroNames[index]));
+ return `${parts.join(', ')} `;
+}
+
+export function renderComboLabelHtml(heroIds, pet, heroNamesOverride = null) {
+ const combo = formatCombo(heroIds, pet, heroNamesOverride);
+ const parts = combo.heroIds.map((heroId, index) => renderUnitNameHtml(heroId, combo.heroNames[index]));
+ let html = parts.join(', ');
+ if (combo.pet) {
+ html += ` + ${renderUnitNameHtml(combo.pet, combo.petName)}`;
+ }
+ return `${html} `;
+}
+
+export const HERO_ICON_TOOLTIP_CSS = `
+ .unit-hover {
+ position: relative;
+ display: inline;
+ border-bottom: 1px dotted #5a7a9a;
+ cursor: help;
+ }
+ .unit-hover .unit-label { color: inherit; }
+ .unit-hover .unit-tip {
+ display: none;
+ position: absolute;
+ bottom: calc(100% + 8px);
+ left: 50%;
+ transform: translateX(-50%);
+ z-index: 20;
+ padding: 6px;
+ background: #1a2433;
+ border: 1px solid #3a5a8a;
+ border-radius: 8px;
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.45);
+ pointer-events: none;
+ white-space: nowrap;
+ }
+ .unit-hover .unit-tip::after {
+ content: '';
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ transform: translateX(-50%);
+ border: 6px solid transparent;
+ border-top-color: #3a5a8a;
+ }
+ .unit-hover:hover .unit-tip,
+ .unit-hover:focus-within .unit-tip {
+ display: block;
+ }
+ .unit-tip img {
+ width: 48px;
+ height: 48px;
+ display: block;
+ border-radius: 4px;
+ }
+ .combo-label { line-height: 1.6; }
+ .combo-label .unit-sep,
+ .combo-label .unit-plus { color: #8b9bb4; border: none; cursor: default; }
+ td .unit-hover, .ga-team .unit-hover, .chip .unit-hover { white-space: normal; }
+`;
diff --git a/hero-names.mjs b/hero-names.mjs
new file mode 100644
index 0000000..d8bdf69
--- /dev/null
+++ b/hero-names.mjs
@@ -0,0 +1,48 @@
+/** In-game display names for hero/pet IDs (from LIB_HERO_NAME_* / docs + user verification). */
+
+export const HERO_NAMES = {
+ 1: 'Aurora', 2: 'Galahad', 3: 'Keira', 4: 'Astaroth', 5: 'Kai', 6: 'Phobos', 7: 'Thea',
+ 8: 'Daredevil', 9: 'Heidi', 10: 'Faceless', 11: 'Chabba', 12: 'Arachne', 13: 'Orion',
+ 14: 'Fox', 15: 'Ginger', 16: 'Dante', 17: 'Mojo', 18: 'Judge', 19: 'Dark Star', 20: 'Artemis',
+ 21: 'Markus', 22: 'Peppy', 23: 'Lian', 24: 'Cleaver', 25: 'Ishmael', 26: 'Lilith', 27: 'Luther',
+ 28: 'Qing Mao', 29: 'Dorian', 30: 'Cornelius', 31: 'Jet', 32: 'Helios', 33: 'Lars', 34: 'Krista',
+ 35: 'Jorgen', 36: 'Maya', 37: 'Jhu', 38: 'Elmir', 39: 'Ziri', 40: 'Nebula', 41: "K'arkh",
+ 42: 'Rufus', 43: 'Celeste', 44: 'Astrid and Lucas', 45: 'Satori', 46: 'Martha', 47: 'Andvari',
+ 48: 'Sebastian', 49: 'Yasmine', 50: 'Corvus', 51: 'Morrigan', 52: 'Isaac', 53: 'Alvanor',
+ 54: 'Tristan', 55: 'Iris', 56: 'Amira', 57: 'Fafnir', 58: 'Aidan', 59: 'Kayla',
+ 60: 'Mushy and Shroom', 61: 'Julius', 62: 'Polaris', 63: 'Lara Croft', 64: 'Augustus',
+ 65: 'Ninja Turtles', 66: 'Folio', 67: 'Lyria', 68: 'Guus', 69: 'Cascade', 70: 'Electra von Grave',
+ 71: 'Fluffy', 72: 'Byrna', 73: 'Adam', 74: 'Somna',
+};
+
+export const PET_NAMES = {
+ 6000: 'Fenris', 6001: 'Oliver', 6002: 'Merlin', 6003: 'Mara', 6004: 'Cain',
+ 6005: 'Albus', 6006: 'Axel', 6007: 'Biscuit', 6008: 'Khorus', 6009: 'Vex',
+};
+
+export function resolveHeroName(id) {
+ const n = Number(id);
+ if (!Number.isFinite(n)) return String(id);
+ if (n >= 6000 && n < 7000) return PET_NAMES[n] || `Pet ${n}`;
+ return HERO_NAMES[n] || `Hero ${n}`;
+}
+
+export function resolvePetName(id) {
+ const n = Number(id);
+ if (!Number.isFinite(n) || n < 6000) return null;
+ return PET_NAMES[n] || `Pet ${n}`;
+}
+
+export function formatCombo(heroIds, pet, heroNamesOverride = null) {
+ const ids = (Array.isArray(heroIds) ? heroIds : []).map(Number).filter((id) => id > 0 && id < 6000);
+ const names = Array.isArray(heroNamesOverride) && heroNamesOverride.length
+ ? heroNamesOverride.map((name, i) => name || resolveHeroName(ids[i]))
+ : ids.map(resolveHeroName);
+ const petId = pet != null ? Number(pet) : null;
+ const petName = petId ? resolvePetName(petId) : null;
+ const label = petName
+ ? `${names.join(', ')} + ${petName}`
+ : names.join(', ');
+
+ return { heroIds: ids, heroNames: names, pet: petId, petName, label };
+}
diff --git a/heroData.txt b/heroData.txt
new file mode 100644
index 0000000..49a183f
--- /dev/null
+++ b/heroData.txt
@@ -0,0 +1,97855 @@
+{
+ "1": {
+ "id": 1,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 55,
+ "strength": 20
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 19
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 7,
+ 6,
+ 2,
+ 13,
+ 4
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 585,
+ "intelligence": 12,
+ "magicPower": 25,
+ "physicalAttack": 12,
+ "strength": 7
+ },
+ "items": [
+ 6,
+ 7,
+ 10,
+ 18,
+ 25,
+ 27
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 50,
+ "hp": 970,
+ "intelligence": 20,
+ "magicPower": 50,
+ "physicalAttack": 45,
+ "strength": 26
+ },
+ "items": [
+ 13,
+ 19,
+ 27,
+ 28,
+ 42,
+ 46
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 100,
+ "hp": 2355,
+ "intelligence": 29,
+ "magicPower": 150,
+ "magicResist": 50,
+ "physicalAttack": 78,
+ "strength": 28
+ },
+ "items": [
+ 32,
+ 33,
+ 46,
+ 52,
+ 58,
+ 60
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 100,
+ "hp": 2855,
+ "intelligence": 46,
+ "magicPenetration": 100,
+ "magicPower": 300,
+ "magicResist": 150,
+ "physicalAttack": 78,
+ "strength": 42
+ },
+ "items": [
+ 32,
+ 33,
+ 46,
+ 56,
+ 62,
+ 71
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 100,
+ "dodge": 30,
+ "hp": 4355,
+ "intelligence": 53,
+ "magicPenetration": 230,
+ "magicPower": 430,
+ "magicResist": 150,
+ "physicalAttack": 78,
+ "strength": 56
+ },
+ "items": [
+ 43,
+ 45,
+ 56,
+ 62,
+ 71,
+ 85
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 38,
+ "armor": 150,
+ "dodge": 60,
+ "hp": 6355,
+ "intelligence": 60,
+ "magicPenetration": 310,
+ "magicPower": 560,
+ "magicResist": 200,
+ "physicalAttack": 111,
+ "strength": 73
+ },
+ "items": [
+ 56,
+ 62,
+ 71,
+ 84,
+ 93,
+ 94
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 60,
+ "armor": 150,
+ "dodge": 90,
+ "hp": 7355,
+ "intelligence": 82,
+ "magicPenetration": 390,
+ "magicPower": 840,
+ "magicResist": 200,
+ "physicalAttack": 111,
+ "strength": 133
+ },
+ "items": [
+ 60,
+ 73,
+ 90,
+ 91,
+ 98,
+ 126
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 62,
+ "armor": 230,
+ "dodge": 114,
+ "hp": 11755,
+ "intelligence": 84,
+ "magicPenetration": 590,
+ "magicPower": 1160,
+ "magicResist": 300,
+ "physicalAttack": 181,
+ "strength": 151
+ },
+ "items": [
+ 71,
+ 71,
+ 85,
+ 102,
+ 127,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 99,
+ "armor": 230,
+ "dodge": 174,
+ "hp": 15315,
+ "intelligence": 121,
+ "magicPenetration": 750,
+ "magicPower": 1832,
+ "magicResist": 300,
+ "physicalAttack": 181,
+ "strength": 198
+ },
+ "items": [
+ 71,
+ 84,
+ 102,
+ 117,
+ 135,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 159,
+ "armor": 230,
+ "dodge": 234,
+ "hp": 17875,
+ "intelligence": 211,
+ "magicPenetration": 990,
+ "magicPower": 2584,
+ "magicResist": 300,
+ "physicalAttack": 181,
+ "strength": 306
+ },
+ "items": [
+ 127,
+ 116,
+ 122,
+ 135,
+ 170,
+ 174
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 191,
+ "armor": 390,
+ "dodge": 234,
+ "hp": 20435,
+ "intelligence": 273,
+ "magicPenetration": 1590,
+ "magicPower": 3256,
+ "magicResist": 460,
+ "physicalAttack": 289,
+ "strength": 447
+ },
+ "items": [
+ 117,
+ 127,
+ 135,
+ 170,
+ 178,
+ 181
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 223,
+ "armor": 390,
+ "dodge": 400,
+ "hp": 22995,
+ "intelligence": 335,
+ "magicPenetration": 2070,
+ "magicPower": 4408,
+ "magicResist": 460,
+ "physicalAttack": 289,
+ "strength": 588
+ },
+ "items": [
+ 115,
+ 122,
+ 174,
+ 169,
+ 189,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 273,
+ "armor": 550,
+ "dodge": 628,
+ "hp": 27795,
+ "intelligence": 385,
+ "magicPenetration": 2670,
+ "magicPower": 5168,
+ "magicResist": 620,
+ "physicalAttack": 397,
+ "strength": 812
+ },
+ "items": [
+ 115,
+ 122,
+ 174,
+ 189,
+ 203,
+ 210
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 275,
+ "armor": 710,
+ "dodge": 856,
+ "hp": 35155,
+ "intelligence": 539,
+ "magicPenetration": 3270,
+ "magicPower": 7536,
+ "magicResist": 780,
+ "physicalAttack": 505,
+ "strength": 988
+ },
+ "items": [
+ 122,
+ 125,
+ 189,
+ 181,
+ 210,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 353,
+ "armor": 870,
+ "dodge": 1084,
+ "hp": 42515,
+ "intelligence": 617,
+ "magicPenetration": 3590,
+ "magicPower": 9264,
+ "magicResist": 940,
+ "physicalAttack": 829,
+ "strength": 1519
+ },
+ "items": [
+ 211,
+ 203,
+ 184,
+ 185,
+ 230,
+ 226
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 479,
+ "armor": 870,
+ "dodge": 1417,
+ "hp": 47315,
+ "intelligence": 895,
+ "magicPenetration": 3590,
+ "magicPower": 11424,
+ "magicResist": 1260,
+ "physicalAttack": 829,
+ "strength": 2098
+ },
+ "items": [
+ 211,
+ 184,
+ 189,
+ 232,
+ 227,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 557,
+ "armor": 2070,
+ "dodge": 1645,
+ "hp": 62035,
+ "intelligence": 973,
+ "magicPenetration": 4790,
+ "magicPower": 14880,
+ "magicResist": 1580,
+ "physicalAttack": 829,
+ "strength": 2455
+ },
+ "items": [
+ 189,
+ 179,
+ 232,
+ 224,
+ 234,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 11,
+ 10,
+ 1
+ ],
+ "artifacts": [
+ 1001,
+ 2002,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 2,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero01_aurora",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0001",
+ "epicArtAsset": {
+ "name": "01_aurora_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.16,
+ 1.15
+ ],
+ "screen": "obtain",
+ "x": -99,
+ "y": 123
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "Aurora"
+ },
+ "role": "front",
+ "obtainType": null,
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero01_battle_animation"
+ },
+ "roleExtended": [
+ "melee_tank"
+ ],
+ "sfxAsset": "hero01_sfx",
+ "musicAsset": {
+ "assetIdent": "sound_theme_01_aurora",
+ "clipIdent": "theme_aurora"
+ },
+ "perk": [
+ 4,
+ 1
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 1,
+ "1": 2,
+ "2": 3,
+ "3": 4,
+ "4": 5,
+ "7": 8268,
+ "8": 8269
+ }
+ },
+ "2": {
+ "id": 2,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 55,
+ "strength": 20
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 16
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 20
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 8,
+ 9,
+ 14,
+ 18
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "strength": 9
+ },
+ "items": [
+ 13,
+ 9,
+ 10,
+ 18,
+ 27,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 75,
+ "hp": 1355,
+ "intelligence": 5,
+ "magicResist": 50,
+ "physicalAttack": 70,
+ "strength": 23
+ },
+ "items": [
+ 10,
+ 18,
+ 28,
+ 25,
+ 43,
+ 36
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 125,
+ "hp": 2240,
+ "intelligence": 8,
+ "magicResist": 100,
+ "physicalAttack": 136,
+ "strength": 47
+ },
+ "items": [
+ 21,
+ 42,
+ 37,
+ 44,
+ 57,
+ 59
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 325,
+ "hp": 2740,
+ "intelligence": 10,
+ "magicResist": 150,
+ "physicalAttack": 239,
+ "strength": 69
+ },
+ "items": [
+ 33,
+ 36,
+ 43,
+ 57,
+ 59,
+ 77
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 475,
+ "hp": 3240,
+ "intelligence": 17,
+ "lifesteal": 5,
+ "magicResist": 150,
+ "physicalAttack": 342,
+ "strength": 93
+ },
+ "items": [
+ 42,
+ 43,
+ 57,
+ 65,
+ 77,
+ 85
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 525,
+ "hp": 4740,
+ "intelligence": 24,
+ "lifesteal": 10,
+ "magicResist": 230,
+ "physicalAttack": 534,
+ "strength": 110
+ },
+ "items": [
+ 74,
+ 64,
+ 77,
+ 90,
+ 92,
+ 99
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 805,
+ "hp": 5540,
+ "intelligence": 31,
+ "lifesteal": 15,
+ "magicResist": 310,
+ "physicalAttack": 739,
+ "strength": 143
+ },
+ "items": [
+ 87,
+ 77,
+ 90,
+ 99,
+ 91,
+ 123
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 48,
+ "armor": 1085,
+ "hp": 9140,
+ "intelligence": 38,
+ "lifesteal": 20,
+ "magicResist": 310,
+ "physicalAttack": 879,
+ "strength": 196
+ },
+ "items": [
+ 77,
+ 90,
+ 91,
+ 125,
+ 122,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 74,
+ "armor": 1325,
+ "hp": 11140,
+ "intelligence": 64,
+ "lifesteal": 25,
+ "magicResist": 470,
+ "physicalAttack": 1273,
+ "strength": 268
+ },
+ "items": [
+ 85,
+ 125,
+ 122,
+ 124,
+ 131,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 143,
+ "armor": 1485,
+ "hp": 12140,
+ "intelligence": 133,
+ "lifesteal": 35,
+ "magicResist": 630,
+ "physicalAttack": 1597,
+ "strength": 425
+ },
+ "items": [
+ 123,
+ 122,
+ 125,
+ 136,
+ 170,
+ 168
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 183,
+ "armor": 1645,
+ "hp": 13740,
+ "intelligence": 173,
+ "lifesteal": 35,
+ "magicResist": 790,
+ "physicalAttack": 2321,
+ "strength": 652
+ },
+ "items": [
+ 114,
+ 124,
+ 136,
+ 170,
+ 168,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 223,
+ "armor": 1965,
+ "hp": 20140,
+ "intelligence": 213,
+ "lifesteal": 45,
+ "magicResist": 790,
+ "physicalAttack": 2937,
+ "strength": 849
+ },
+ "items": [
+ 122,
+ 123,
+ 170,
+ 175,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 273,
+ "armor": 2725,
+ "hp": 24940,
+ "intelligence": 263,
+ "lifesteal": 45,
+ "magicResist": 790,
+ "physicalAttack": 3685,
+ "strength": 1212
+ },
+ "items": [
+ 131,
+ 122,
+ 176,
+ 183,
+ 201,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 299,
+ "armor": 3205,
+ "hp": 41516,
+ "intelligence": 289,
+ "lifesteal": 45,
+ "magicResist": 1390,
+ "physicalAttack": 6148,
+ "strength": 1268
+ },
+ "items": [
+ 122,
+ 134,
+ 167,
+ 183,
+ 208,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 377,
+ "armor": 3685,
+ "hp": 58972,
+ "intelligence": 367,
+ "lifesteal": 45,
+ "magicResist": 1646,
+ "physicalAttack": 7932,
+ "strength": 1625
+ },
+ "items": [
+ 211,
+ 208,
+ 179,
+ 183,
+ 221,
+ 227
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 455,
+ "armor": 5205,
+ "hp": 73628,
+ "intelligence": 445,
+ "lifesteal": 45,
+ "magicResist": 1646,
+ "physicalAttack": 9903,
+ "strength": 2200
+ },
+ "items": [
+ 185,
+ 208,
+ 183,
+ 221,
+ 227,
+ 240
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 505,
+ "armor": 6725,
+ "hp": 90204,
+ "intelligence": 495,
+ "lifesteal": 45,
+ "magicResist": 1646,
+ "physicalAttack": 13538,
+ "strength": 2642
+ },
+ "items": [
+ 201,
+ 183,
+ 221,
+ 225,
+ 237,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 7,
+ 4,
+ 8,
+ 1
+ ],
+ "artifacts": [
+ 1002,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 13,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero02_galahad",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0002",
+ "epicArtAsset": {
+ "name": "02_galahad_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.15,
+ 1.15
+ ],
+ "screen": "obtain",
+ "x": -100,
+ "y": 106
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "02_galahad",
+ "transform": [
+ {
+ "scale": [
+ 1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 42.39999999999998,
+ "y": -6.399999999999977
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": null,
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero02_battle_animation"
+ },
+ "roleExtended": [
+ "melee_tank"
+ ],
+ "sfxAsset": "hero02_sfx",
+ "musicAsset": {
+ "assetIdent": "sound_theme_02_galahad",
+ "clipIdent": "theme_galahad"
+ },
+ "perk": [
+ 4,
+ 10,
+ 2,
+ 16
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 425,
+ "1": 426,
+ "2": 427,
+ "3": 428,
+ "4": 429,
+ "7": 8264,
+ "8": 8265
+ }
+ },
+ "3": {
+ "id": 3,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 60,
+ "strength": 10
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 3,
+ 8,
+ 14,
+ 20
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 2,
+ "physicalAttack": 62,
+ "strength": 2
+ },
+ "items": [
+ 8,
+ 9,
+ 18,
+ 20,
+ 25,
+ 23
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 33,
+ "armor": 50,
+ "hp": 585,
+ "intelligence": 4,
+ "magicResist": 25,
+ "physicalAttack": 120,
+ "strength": 11
+ },
+ "items": [
+ 12,
+ 20,
+ 25,
+ 28,
+ 38,
+ 35
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 69,
+ "armor": 50,
+ "hp": 585,
+ "intelligence": 12,
+ "magicResist": 75,
+ "physicalAttack": 211,
+ "strength": 19
+ },
+ "items": [
+ 31,
+ 43,
+ 38,
+ 53,
+ 57,
+ 56
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 81,
+ "armor": 100,
+ "armorPenetration": 100,
+ "hp": 1585,
+ "intelligence": 14,
+ "magicResist": 75,
+ "physicalAttack": 380,
+ "strength": 21
+ },
+ "items": [
+ 23,
+ 25,
+ 50,
+ 56,
+ 57,
+ 70
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 108,
+ "armor": 100,
+ "armorPenetration": 180,
+ "hp": 2585,
+ "intelligence": 16,
+ "magicResist": 117,
+ "physicalAttack": 595,
+ "strength": 23
+ },
+ "items": [
+ 53,
+ 44,
+ 57,
+ 56,
+ 70,
+ 87
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 125,
+ "armor": 150,
+ "armorPenetration": 310,
+ "hp": 3585,
+ "intelligence": 23,
+ "magicResist": 167,
+ "physicalAttack": 824,
+ "strength": 30
+ },
+ "items": [
+ 59,
+ 70,
+ 87,
+ 66,
+ 92,
+ 97
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 158,
+ "armor": 250,
+ "armorPenetration": 590,
+ "hp": 3585,
+ "intelligence": 30,
+ "magicResist": 167,
+ "physicalAttack": 1141,
+ "strength": 37
+ },
+ "items": [
+ 64,
+ 57,
+ 70,
+ 120,
+ 118,
+ 91
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 220,
+ "armor": 250,
+ "armorPenetration": 830,
+ "hp": 6385,
+ "intelligence": 32,
+ "magicResist": 247,
+ "physicalAttack": 1483,
+ "strength": 39
+ },
+ "items": [
+ 70,
+ 84,
+ 99,
+ 134,
+ 118,
+ 96
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 310,
+ "armor": 450,
+ "armorPenetration": 1070,
+ "hp": 6385,
+ "intelligence": 54,
+ "magicResist": 503,
+ "physicalAttack": 1992,
+ "strength": 61
+ },
+ "items": [
+ 87,
+ 118,
+ 125,
+ 138,
+ 120,
+ 122
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 473,
+ "armor": 610,
+ "armorPenetration": 1230,
+ "hp": 6385,
+ "intelligence": 99,
+ "magicResist": 663,
+ "physicalAttack": 2602,
+ "strength": 106
+ },
+ "items": [
+ 118,
+ 122,
+ 134,
+ 114,
+ 172,
+ 167
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 614,
+ "armor": 770,
+ "armorPenetration": 1390,
+ "hp": 13985,
+ "intelligence": 101,
+ "magicResist": 919,
+ "physicalAttack": 3379,
+ "strength": 108
+ },
+ "items": [
+ 118,
+ 125,
+ 97,
+ 172,
+ 182,
+ 179
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 755,
+ "armor": 770,
+ "armorPenetration": 2070,
+ "hp": 17185,
+ "intelligence": 103,
+ "magicResist": 1079,
+ "physicalAttack": 4663,
+ "strength": 110
+ },
+ "items": [
+ 120,
+ 122,
+ 173,
+ 176,
+ 179,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1009,
+ "armor": 930,
+ "armorPenetration": 2670,
+ "hp": 20385,
+ "intelligence": 153,
+ "magicResist": 1679,
+ "physicalAttack": 5519,
+ "strength": 160
+ },
+ "items": [
+ 122,
+ 96,
+ 168,
+ 182,
+ 201,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1049,
+ "armor": 1090,
+ "armorPenetration": 2990,
+ "hp": 32161,
+ "intelligence": 155,
+ "magicResist": 1679,
+ "physicalAttack": 8702,
+ "strength": 162
+ },
+ "items": [
+ 120,
+ 118,
+ 182,
+ 182,
+ 208,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1466,
+ "armor": 1090,
+ "armorPenetration": 3790,
+ "hp": 38817,
+ "intelligence": 233,
+ "magicResist": 1679,
+ "physicalAttack": 10889,
+ "strength": 240
+ },
+ "items": [
+ 213,
+ 187,
+ 201,
+ 182,
+ 225,
+ 231
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2045,
+ "armor": 1090,
+ "armorPenetration": 5310,
+ "hp": 43937,
+ "intelligence": 359,
+ "magicResist": 1679,
+ "physicalAttack": 13033,
+ "strength": 366
+ },
+ "items": [
+ 179,
+ 182,
+ 213,
+ 228,
+ 227,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2402,
+ "armor": 2290,
+ "armorPenetration": 6590,
+ "hp": 52257,
+ "intelligence": 437,
+ "magicResist": 2879,
+ "physicalAttack": 15657,
+ "strength": 444
+ },
+ "items": [
+ 179,
+ 179,
+ 231,
+ 228,
+ 239,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 7,
+ 12,
+ 3
+ ],
+ "artifacts": [
+ 1003,
+ 2004,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 1006,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero3_keira",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0003",
+ "epicArtAsset": {
+ "name": "03_keira_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.16,
+ 1.15
+ ],
+ "screen": "obtain",
+ "x": 190.6,
+ "y": 133.6
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "03_keira",
+ "transform": [
+ {
+ "scale": [
+ -1.11,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 73.60000000000002,
+ "y": 20
+ }
+ ]
+ },
+ "role": "middle",
+ "obtainType": null,
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": {
+ "duration": 4,
+ "summonCinematic": "hero03_cinematic_long.mp4",
+ "summonSound": "hero03_summon_cinematic_audio"
+ },
+ "roleExtended": [
+ "ranged_dps"
+ ],
+ "musicAsset": {
+ "assetIdent": "sound_theme_03_keira",
+ "clipIdent": "theme_keira"
+ },
+ "perk": [
+ 6,
+ 1,
+ 12
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 431,
+ "1": 432,
+ "2": 433,
+ "3": 434,
+ "4": 435,
+ "7": 8266,
+ "8": 8267
+ }
+ },
+ "4": {
+ "id": 4,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 55,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 19
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 6,
+ 7,
+ 8,
+ 9,
+ 13
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 7,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 13,
+ 14,
+ 10,
+ 18,
+ 27,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 75,
+ "hp": 1355,
+ "intelligence": 10,
+ "magicPower": 25,
+ "magicResist": 75,
+ "physicalAttack": 25,
+ "strength": 21
+ },
+ "items": [
+ 18,
+ 14,
+ 27,
+ 28,
+ 36,
+ 44
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 175,
+ "hp": 2240,
+ "intelligence": 12,
+ "magicPower": 25,
+ "magicResist": 175,
+ "physicalAttack": 50,
+ "strength": 40
+ },
+ "items": [
+ 24,
+ 36,
+ 45,
+ 47,
+ 59,
+ 60
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 317,
+ "hp": 3660,
+ "intelligence": 14,
+ "magicPower": 75,
+ "magicResist": 325,
+ "physicalAttack": 78,
+ "strength": 67
+ },
+ "items": [
+ 26,
+ 33,
+ 47,
+ 56,
+ 59,
+ 64
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 459,
+ "hp": 5880,
+ "intelligence": 21,
+ "magicPower": 125,
+ "magicResist": 405,
+ "physicalAttack": 106,
+ "strength": 96
+ },
+ "items": [
+ 37,
+ 45,
+ 59,
+ 60,
+ 63,
+ 90
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 689,
+ "hp": 6680,
+ "intelligence": 23,
+ "magicPower": 255,
+ "magicResist": 555,
+ "physicalAttack": 176,
+ "strength": 124
+ },
+ "items": [
+ 67,
+ 63,
+ 74,
+ 88,
+ 99,
+ 100
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 30,
+ "armor": 989,
+ "hp": 8280,
+ "intelligence": 30,
+ "magicPower": 495,
+ "magicResist": 835,
+ "physicalAttack": 176,
+ "strength": 141
+ },
+ "items": [
+ 67,
+ 68,
+ 86,
+ 74,
+ 122,
+ 115
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 42,
+ "armor": 1229,
+ "hp": 8280,
+ "intelligence": 52,
+ "magicPower": 815,
+ "magicResist": 1175,
+ "physicalAttack": 284,
+ "strength": 163
+ },
+ "items": [
+ 126,
+ 67,
+ 88,
+ 115,
+ 122,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 68,
+ "armor": 1489,
+ "hp": 10680,
+ "intelligence": 78,
+ "magicPower": 1455,
+ "magicResist": 1415,
+ "physicalAttack": 392,
+ "strength": 219
+ },
+ "items": [
+ 90,
+ 86,
+ 115,
+ 126,
+ 135,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 113,
+ "armor": 1569,
+ "hp": 14840,
+ "intelligence": 133,
+ "magicPower": 2447,
+ "magicResist": 1675,
+ "physicalAttack": 462,
+ "strength": 328
+ },
+ "items": [
+ 115,
+ 122,
+ 126,
+ 136,
+ 170,
+ 175
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 153,
+ "armor": 2329,
+ "hp": 16440,
+ "intelligence": 173,
+ "magicPower": 2927,
+ "magicResist": 1835,
+ "physicalAttack": 570,
+ "strength": 525
+ },
+ "items": [
+ 127,
+ 114,
+ 135,
+ 170,
+ 175,
+ 184
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 185,
+ "armor": 2929,
+ "hp": 25400,
+ "intelligence": 205,
+ "magicPower": 3439,
+ "magicResist": 2155,
+ "physicalAttack": 786,
+ "strength": 666
+ },
+ "items": [
+ 123,
+ 136,
+ 167,
+ 175,
+ 184,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 273,
+ "armor": 3529,
+ "hp": 37800,
+ "intelligence": 293,
+ "magicPower": 3439,
+ "magicResist": 2475,
+ "physicalAttack": 786,
+ "strength": 1006
+ },
+ "items": [
+ 116,
+ 123,
+ 184,
+ 180,
+ 203,
+ 210
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 275,
+ "armor": 3529,
+ "hp": 49960,
+ "intelligence": 477,
+ "magicPower": 6767,
+ "magicResist": 2955,
+ "physicalAttack": 786,
+ "strength": 1212
+ },
+ "items": [
+ 91,
+ 92,
+ 184,
+ 180,
+ 210,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 353,
+ "armor": 3529,
+ "hp": 62520,
+ "intelligence": 555,
+ "magicPower": 8975,
+ "magicResist": 3275,
+ "physicalAttack": 921,
+ "strength": 1743
+ },
+ "items": [
+ 210,
+ 167,
+ 211,
+ 180,
+ 228,
+ 227
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 431,
+ "armor": 4729,
+ "hp": 74280,
+ "intelligence": 633,
+ "magicPower": 11183,
+ "magicResist": 4475,
+ "physicalAttack": 921,
+ "strength": 2274
+ },
+ "items": [
+ 185,
+ 210,
+ 180,
+ 228,
+ 224,
+ 237
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 481,
+ "armor": 6649,
+ "hp": 97160,
+ "intelligence": 683,
+ "magicPower": 13391,
+ "magicResist": 5675,
+ "physicalAttack": 1945,
+ "strength": 2672
+ },
+ "items": [
+ 184,
+ 185,
+ 224,
+ 225,
+ 238,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 7,
+ 4,
+ 8,
+ 1
+ ],
+ "artifacts": [
+ 1004,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 11,
+ "scale": null,
+ "type": "hero",
+ "asset": "demon",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0004",
+ "epicArtAsset": {
+ "name": "04_astaroth_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1,
+ 1
+ ],
+ "screen": "obtain",
+ "x": 1.6000000000000227,
+ "y": 0.20000000000000284
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "Astaroth"
+ },
+ "role": "front",
+ "obtainType": null,
+ "characterType": "demon",
+ "silhouette": "flying",
+ "ultCinematic": {
+ "ident": "hero04_battle_animation"
+ },
+ "roleExtended": [
+ "melee_tank",
+ "support"
+ ],
+ "sfxAsset": "hero04_sfx",
+ "musicAsset": null,
+ "perk": [
+ 4,
+ 5,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "assetsIdent": null,
+ "skill": [
+ 254,
+ 255,
+ 256,
+ 257,
+ 258
+ ]
+ },
+ "5": {
+ "id": 5,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 22,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 4,
+ 7,
+ 7,
+ 9,
+ 16,
+ 6
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "intelligence": 19,
+ "magicPower": 50,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 2,
+ 16,
+ 11,
+ 19,
+ 26,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 200,
+ "intelligence": 40,
+ "magicPower": 150,
+ "magicResist": 75,
+ "strength": 10
+ },
+ "items": [
+ 11,
+ 19,
+ 24,
+ 28,
+ 40,
+ 45
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "hp": 700,
+ "intelligence": 64,
+ "magicPower": 300,
+ "magicResist": 175,
+ "strength": 13
+ },
+ "items": [
+ 22,
+ 32,
+ 40,
+ 52,
+ 58,
+ 60
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "hp": 700,
+ "intelligence": 96,
+ "magicPenetration": 100,
+ "magicPower": 450,
+ "magicResist": 275,
+ "strength": 15
+ },
+ "items": [
+ 52,
+ 40,
+ 46,
+ 58,
+ 60,
+ 71
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "hp": 1200,
+ "intelligence": 118,
+ "magicPenetration": 230,
+ "magicPower": 730,
+ "magicResist": 375,
+ "strength": 17
+ },
+ "items": [
+ 46,
+ 48,
+ 52,
+ 52,
+ 71,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 1700,
+ "intelligence": 170,
+ "magicPenetration": 410,
+ "magicPower": 944,
+ "magicResist": 517,
+ "strength": 24
+ },
+ "items": [
+ 67,
+ 63,
+ 75,
+ 86,
+ 93,
+ 98
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 36,
+ "hp": 2500,
+ "intelligence": 202,
+ "magicPenetration": 610,
+ "magicPower": 1304,
+ "magicResist": 697,
+ "strength": 36
+ },
+ "items": [
+ 75,
+ 64,
+ 67,
+ 86,
+ 93,
+ 117
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 48,
+ "hp": 3300,
+ "intelligence": 264,
+ "magicPenetration": 770,
+ "magicPower": 1744,
+ "magicResist": 957,
+ "strength": 48
+ },
+ "items": [
+ 119,
+ 71,
+ 75,
+ 100,
+ 117,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 55,
+ "hp": 5860,
+ "intelligence": 341,
+ "magicPenetration": 1010,
+ "magicPower": 2656,
+ "magicResist": 1157,
+ "strength": 55
+ },
+ "items": [
+ 67,
+ 98,
+ 115,
+ 119,
+ 132,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 81,
+ "hp": 9956,
+ "intelligence": 427,
+ "magicPenetration": 1210,
+ "magicPower": 3875,
+ "magicResist": 1397,
+ "strength": 81
+ },
+ "items": [
+ 115,
+ 117,
+ 116,
+ 135,
+ 171,
+ 174
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 83,
+ "hp": 12516,
+ "intelligence": 598,
+ "magicPenetration": 1970,
+ "magicPower": 4867,
+ "magicResist": 1717,
+ "strength": 83
+ },
+ "items": [
+ 115,
+ 116,
+ 135,
+ 169,
+ 171,
+ 181
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 85,
+ "hp": 15076,
+ "intelligence": 739,
+ "magicPenetration": 2290,
+ "magicPower": 6779,
+ "magicResist": 2037,
+ "strength": 85
+ },
+ "items": [
+ 135,
+ 126,
+ 176,
+ 174,
+ 184,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 135,
+ "hp": 24036,
+ "intelligence": 963,
+ "magicPenetration": 2890,
+ "magicPower": 7611,
+ "magicResist": 2957,
+ "strength": 135
+ },
+ "items": [
+ 132,
+ 140,
+ 181,
+ 180,
+ 184,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 161,
+ "hp": 38692,
+ "intelligence": 1140,
+ "magicPenetration": 3210,
+ "magicPower": 11406,
+ "magicResist": 3277,
+ "strength": 161
+ },
+ "items": [
+ 116,
+ 127,
+ 180,
+ 203,
+ 181,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 269,
+ "hp": 41892,
+ "intelligence": 1709,
+ "magicPenetration": 3530,
+ "magicPower": 13966,
+ "magicResist": 3437,
+ "strength": 269
+ },
+ "items": [
+ 212,
+ 184,
+ 180,
+ 209,
+ 226,
+ 232
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 347,
+ "hp": 52452,
+ "intelligence": 2187,
+ "magicPenetration": 4730,
+ "magicPower": 17662,
+ "magicResist": 3757,
+ "strength": 347
+ },
+ "items": [
+ 212,
+ 184,
+ 180,
+ 228,
+ 222,
+ 233
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 425,
+ "hp": 60452,
+ "intelligence": 2762,
+ "magicPenetration": 5498,
+ "magicPower": 21694,
+ "magicResist": 5277,
+ "strength": 425
+ },
+ "items": [
+ 186,
+ 184,
+ 228,
+ 226,
+ 233,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 11,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1005,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 1013,
+ "scale": null,
+ "type": "hero",
+ "asset": "mage",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0005",
+ "epicArtAsset": {
+ "name": "05_kai_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.1,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 69,
+ "y": 47
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "05_Kai"
+ },
+ "role": "middle",
+ "obtainType": "shop:tower",
+ "characterType": "snob",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 2,
+ 14
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 339,
+ "1": 340,
+ "2": 341,
+ "3": 342,
+ "4": 343,
+ "7": 8250,
+ "8": 8251
+ }
+ },
+ "6": {
+ "id": 6,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 22,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 4,
+ 6,
+ 7,
+ 8,
+ 9
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 12,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 8,
+ 13,
+ 11,
+ 19,
+ 26,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 50,
+ "hp": 585,
+ "intelligence": 26,
+ "magicPower": 125,
+ "magicResist": 75,
+ "strength": 10
+ },
+ "items": [
+ 11,
+ 19,
+ 24,
+ 27,
+ 40,
+ 45
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 100,
+ "hp": 1085,
+ "intelligence": 50,
+ "magicPower": 275,
+ "magicResist": 125,
+ "strength": 13
+ },
+ "items": [
+ 27,
+ 26,
+ 34,
+ 49,
+ 58,
+ 60
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 192,
+ "hp": 1085,
+ "intelligence": 86,
+ "magicPower": 425,
+ "magicResist": 225,
+ "physicalAttack": 28,
+ "strength": 20
+ },
+ "items": [
+ 26,
+ 51,
+ 48,
+ 56,
+ 60,
+ 68
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 32,
+ "armor": 272,
+ "hp": 2085,
+ "intelligence": 113,
+ "magicPower": 639,
+ "magicResist": 367,
+ "physicalAttack": 28,
+ "strength": 32
+ },
+ "items": [
+ 45,
+ 49,
+ 60,
+ 58,
+ 67,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 414,
+ "hp": 2885,
+ "intelligence": 137,
+ "magicPower": 949,
+ "magicResist": 597,
+ "physicalAttack": 56,
+ "strength": 34
+ },
+ "items": [
+ 67,
+ 68,
+ 75,
+ 86,
+ 91,
+ 99
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 46,
+ "armor": 694,
+ "hp": 4885,
+ "intelligence": 169,
+ "magicPower": 1109,
+ "magicResist": 777,
+ "physicalAttack": 56,
+ "strength": 46
+ },
+ "items": [
+ 67,
+ 68,
+ 86,
+ 88,
+ 58,
+ 126
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 53,
+ "armor": 874,
+ "hp": 7285,
+ "intelligence": 186,
+ "magicPower": 1769,
+ "magicResist": 957,
+ "physicalAttack": 56,
+ "strength": 53
+ },
+ "items": [
+ 67,
+ 68,
+ 86,
+ 75,
+ 132,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 89,
+ "armor": 954,
+ "hp": 9845,
+ "intelligence": 272,
+ "magicPower": 2441,
+ "magicResist": 1137,
+ "physicalAttack": 56,
+ "strength": 89
+ },
+ "items": [
+ 67,
+ 75,
+ 116,
+ 99,
+ 132,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 120,
+ "armor": 1154,
+ "hp": 13941,
+ "intelligence": 373,
+ "magicPower": 3500,
+ "magicResist": 1377,
+ "physicalAttack": 56,
+ "strength": 120
+ },
+ "items": [
+ 100,
+ 115,
+ 119,
+ 140,
+ 171,
+ 175
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 122,
+ "armor": 1754,
+ "hp": 18037,
+ "intelligence": 514,
+ "magicPower": 4639,
+ "magicResist": 1737,
+ "physicalAttack": 56,
+ "strength": 122
+ },
+ "items": [
+ 115,
+ 119,
+ 132,
+ 171,
+ 175,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 148,
+ "armor": 2354,
+ "hp": 21237,
+ "intelligence": 709,
+ "magicPower": 5919,
+ "magicResist": 1897,
+ "physicalAttack": 56,
+ "strength": 148
+ },
+ "items": [
+ 119,
+ 127,
+ 184,
+ 167,
+ 183,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 228,
+ "armor": 2674,
+ "hp": 36837,
+ "intelligence": 993,
+ "magicPower": 6079,
+ "magicResist": 2217,
+ "physicalAttack": 56,
+ "strength": 228
+ },
+ "items": [
+ 140,
+ 132,
+ 184,
+ 180,
+ 176,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 254,
+ "armor": 2674,
+ "hp": 51493,
+ "intelligence": 1170,
+ "magicPower": 9394,
+ "magicResist": 3137,
+ "physicalAttack": 56,
+ "strength": 254
+ },
+ "items": [
+ 140,
+ 116,
+ 175,
+ 180,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 332,
+ "armor": 3274,
+ "hp": 58789,
+ "intelligence": 1709,
+ "magicPower": 12293,
+ "magicResist": 3297,
+ "physicalAttack": 56,
+ "strength": 332
+ },
+ "items": [
+ 212,
+ 183,
+ 180,
+ 209,
+ 226,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 410,
+ "armor": 3594,
+ "hp": 69349,
+ "intelligence": 2187,
+ "magicPower": 15989,
+ "magicResist": 4497,
+ "physicalAttack": 56,
+ "strength": 410
+ },
+ "items": [
+ 212,
+ 203,
+ 183,
+ 228,
+ 227,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 488,
+ "armor": 5114,
+ "hp": 79269,
+ "intelligence": 2696,
+ "magicPower": 20405,
+ "magicResist": 5697,
+ "physicalAttack": 56,
+ "strength": 488
+ },
+ "items": [
+ 186,
+ 184,
+ 228,
+ 224,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 7,
+ 4,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1006,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2020,
+ "scale": null,
+ "type": "hero",
+ "asset": "thing",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0006",
+ "epicArtAsset": {
+ "name": "06_phobos_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1,
+ 1
+ ],
+ "screen": "obtain",
+ "x": 0,
+ "y": 51
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": null,
+ "characterType": "demon",
+ "silhouette": "flying",
+ "ultCinematic": null,
+ "roleExtended": [
+ "control",
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 8,
+ 7,
+ 2,
+ 12,
+ 21
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 329,
+ 330,
+ 331,
+ 332,
+ 333
+ ]
+ },
+ "7": {
+ "id": 7,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 22,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 4,
+ 4,
+ 7,
+ 8,
+ 9
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 12,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 2
+ },
+ "items": [
+ 8,
+ 13,
+ 11,
+ 19,
+ 26,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 50,
+ "hp": 585,
+ "intelligence": 26,
+ "magicPower": 125,
+ "magicResist": 75,
+ "strength": 5
+ },
+ "items": [
+ 11,
+ 19,
+ 24,
+ 27,
+ 40,
+ 45
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 100,
+ "hp": 1085,
+ "intelligence": 50,
+ "magicPower": 275,
+ "magicResist": 125,
+ "strength": 8
+ },
+ "items": [
+ 24,
+ 27,
+ 40,
+ 48,
+ 56,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 150,
+ "hp": 2585,
+ "intelligence": 77,
+ "magicPower": 509,
+ "magicResist": 167,
+ "strength": 10
+ },
+ "items": [
+ 40,
+ 41,
+ 48,
+ 56,
+ 59,
+ 67
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 300,
+ "hp": 3585,
+ "intelligence": 114,
+ "magicPower": 723,
+ "magicResist": 289,
+ "strength": 12
+ },
+ "items": [
+ 48,
+ 49,
+ 58,
+ 60,
+ 75,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 442,
+ "hp": 4385,
+ "intelligence": 168,
+ "magicPower": 987,
+ "magicResist": 431,
+ "physicalAttack": 28,
+ "strength": 19
+ },
+ "items": [
+ 67,
+ 64,
+ 75,
+ 88,
+ 93,
+ 95
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 542,
+ "hp": 5985,
+ "intelligence": 223,
+ "magicPower": 1347,
+ "magicResist": 591,
+ "physicalAttack": 28,
+ "strength": 26
+ },
+ "items": [
+ 63,
+ 68,
+ 75,
+ 63,
+ 88,
+ 116
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 33,
+ "armor": 722,
+ "hp": 8385,
+ "intelligence": 270,
+ "magicPower": 1827,
+ "magicResist": 751,
+ "physicalAttack": 28,
+ "strength": 33
+ },
+ "items": [
+ 69,
+ 88,
+ 84,
+ 116,
+ 119,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 79,
+ "armor": 902,
+ "hp": 9185,
+ "intelligence": 406,
+ "magicPower": 2227,
+ "magicResist": 911,
+ "physicalAttack": 28,
+ "strength": 95
+ },
+ "items": [
+ 58,
+ 116,
+ 99,
+ 115,
+ 135,
+ 137
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 119,
+ "armor": 1102,
+ "hp": 11745,
+ "intelligence": 524,
+ "magicPower": 3159,
+ "magicResist": 1231,
+ "physicalAttack": 28,
+ "strength": 135
+ },
+ "items": [
+ 115,
+ 119,
+ 99,
+ 140,
+ 171,
+ 176
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 121,
+ "armor": 1302,
+ "hp": 15841,
+ "intelligence": 665,
+ "magicPower": 4298,
+ "magicResist": 1991,
+ "physicalAttack": 28,
+ "strength": 137
+ },
+ "items": [
+ 116,
+ 126,
+ 119,
+ 171,
+ 176,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 123,
+ "armor": 1302,
+ "hp": 20641,
+ "intelligence": 836,
+ "magicPower": 5898,
+ "magicResist": 2751,
+ "physicalAttack": 28,
+ "strength": 139
+ },
+ "items": [
+ 119,
+ 127,
+ 184,
+ 176,
+ 183,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 203,
+ "armor": 1622,
+ "hp": 30241,
+ "intelligence": 1120,
+ "magicPower": 6058,
+ "magicResist": 3671,
+ "physicalAttack": 28,
+ "strength": 219
+ },
+ "items": [
+ 140,
+ 132,
+ 184,
+ 180,
+ 176,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 229,
+ "armor": 1622,
+ "hp": 44897,
+ "intelligence": 1297,
+ "magicPower": 9373,
+ "magicResist": 4591,
+ "physicalAttack": 28,
+ "strength": 245
+ },
+ "items": [
+ 140,
+ 116,
+ 176,
+ 180,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 307,
+ "armor": 1622,
+ "hp": 52193,
+ "intelligence": 1836,
+ "magicPower": 12272,
+ "magicResist": 5351,
+ "physicalAttack": 28,
+ "strength": 323
+ },
+ "items": [
+ 212,
+ 169,
+ 180,
+ 209,
+ 227,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 385,
+ "armor": 2822,
+ "hp": 57953,
+ "intelligence": 2314,
+ "magicPower": 15368,
+ "magicResist": 6551,
+ "physicalAttack": 28,
+ "strength": 401
+ },
+ "items": [
+ 212,
+ 186,
+ 183,
+ 228,
+ 224,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 511,
+ "armor": 3142,
+ "hp": 79873,
+ "intelligence": 2893,
+ "magicPower": 18824,
+ "magicResist": 7751,
+ "physicalAttack": 28,
+ "strength": 527
+ },
+ "items": [
+ 186,
+ 183,
+ 228,
+ 224,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 7,
+ 8,
+ 4,
+ 2
+ ],
+ "artifacts": [
+ 1007,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2027,
+ "scale": null,
+ "type": "hero",
+ "asset": "sunsupport",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0007",
+ "epicArtAsset": {
+ "name": "07_thea_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": -18,
+ "y": 17
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": null,
+ "characterType": "healer",
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero07_battle_animation",
+ "summonCinematic": "hero07_cinematic_long.mp4",
+ "summonSound": "hero07_summon_cinematic_audio"
+ },
+ "roleExtended": [
+ "healer",
+ "support"
+ ],
+ "sfxAsset": "hero07_sfx",
+ "musicAsset": {
+ "assetIdent": "sound_theme_07_thea",
+ "clipIdent": "theme_thea"
+ },
+ "perk": [
+ 9,
+ 5,
+ 1,
+ 14
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 420,
+ "1": 421,
+ "2": 422,
+ "3": 423,
+ "4": 424,
+ "7": 8262,
+ "8": 8263
+ }
+ },
+ "8": {
+ "id": 8,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 60,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 17
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 6,
+ 8,
+ 9,
+ 14
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 7,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "strength": 7
+ },
+ "items": [
+ 20,
+ 9,
+ 12,
+ 23,
+ 24,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 25,
+ "hp": 700,
+ "intelligence": 10,
+ "magicResist": 50,
+ "physicalAttack": 95,
+ "strength": 10
+ },
+ "items": [
+ 12,
+ 20,
+ 25,
+ 27,
+ 28,
+ 51
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 55,
+ "armor": 75,
+ "hp": 700,
+ "intelligence": 23,
+ "magicResist": 100,
+ "physicalAttack": 153,
+ "strength": 23
+ },
+ "items": [
+ 24,
+ 38,
+ 44,
+ 53,
+ 57,
+ 61
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 67,
+ "armor": 125,
+ "armorPenetration": 50,
+ "hp": 1200,
+ "intelligence": 25,
+ "magicResist": 150,
+ "physicalAttack": 289,
+ "physicalCritChance": 30,
+ "strength": 25
+ },
+ "items": [
+ 29,
+ 35,
+ 39,
+ 57,
+ 56,
+ 70
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 91,
+ "armor": 125,
+ "armorPenetration": 130,
+ "hp": 2200,
+ "intelligence": 32,
+ "magicResist": 200,
+ "physicalAttack": 415,
+ "physicalCritChance": 45,
+ "strength": 32
+ },
+ "items": [
+ 27,
+ 28,
+ 56,
+ 66,
+ 70,
+ 89
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 125,
+ "armor": 175,
+ "armorPenetration": 210,
+ "hp": 3200,
+ "intelligence": 34,
+ "magicResist": 250,
+ "physicalAttack": 583,
+ "physicalCritChance": 75,
+ "strength": 34
+ },
+ "items": [
+ 66,
+ 65,
+ 70,
+ 89,
+ 91,
+ 96
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 197,
+ "armor": 175,
+ "armorPenetration": 290,
+ "hp": 5200,
+ "intelligence": 36,
+ "magicResist": 330,
+ "physicalAttack": 807,
+ "physicalCritChance": 105,
+ "strength": 36
+ },
+ "items": [
+ 72,
+ 76,
+ 89,
+ 92,
+ 101,
+ 118
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 260,
+ "armor": 175,
+ "armorPenetration": 450,
+ "hp": 5200,
+ "intelligence": 43,
+ "magicResist": 330,
+ "physicalAttack": 1162,
+ "physicalCritChance": 219,
+ "strength": 43
+ },
+ "items": [
+ 56,
+ 76,
+ 101,
+ 120,
+ 118,
+ 134
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 337,
+ "armor": 175,
+ "armorPenetration": 610,
+ "hp": 6200,
+ "intelligence": 50,
+ "magicResist": 586,
+ "physicalAttack": 1723,
+ "physicalCritChance": 279,
+ "strength": 50
+ },
+ "items": [
+ 89,
+ 99,
+ 97,
+ 133,
+ 118,
+ 139
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 439,
+ "armor": 375,
+ "armorPenetration": 970,
+ "hp": 6200,
+ "intelligence": 76,
+ "magicResist": 995,
+ "physicalAttack": 2439,
+ "physicalCritChance": 309,
+ "strength": 76
+ },
+ "items": [
+ 118,
+ 120,
+ 134,
+ 133,
+ 177,
+ 172
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 664,
+ "armor": 375,
+ "armorPenetration": 1130,
+ "hp": 6200,
+ "intelligence": 102,
+ "magicResist": 1251,
+ "physicalAttack": 3000,
+ "physicalCritChance": 475,
+ "strength": 102
+ },
+ "items": [
+ 121,
+ 122,
+ 133,
+ 177,
+ 172,
+ 188
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 859,
+ "armor": 535,
+ "armorPenetration": 1130,
+ "hp": 6200,
+ "intelligence": 128,
+ "magicResist": 1411,
+ "physicalAttack": 3428,
+ "physicalCritChance": 869,
+ "strength": 128
+ },
+ "items": [
+ 120,
+ 114,
+ 177,
+ 173,
+ 188,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1113,
+ "armor": 535,
+ "armorPenetration": 1730,
+ "hp": 7800,
+ "intelligence": 178,
+ "magicResist": 1411,
+ "physicalAttack": 4072,
+ "physicalCritChance": 1263,
+ "strength": 178
+ },
+ "items": [
+ 134,
+ 138,
+ 168,
+ 182,
+ 188,
+ 207
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1201,
+ "armor": 535,
+ "armorPenetration": 2050,
+ "hp": 7800,
+ "intelligence": 218,
+ "magicResist": 1667,
+ "physicalAttack": 6231,
+ "physicalCritChance": 1885,
+ "strength": 218
+ },
+ "items": [
+ 139,
+ 138,
+ 173,
+ 182,
+ 202,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1644,
+ "armor": 535,
+ "armorPenetration": 2970,
+ "hp": 7800,
+ "intelligence": 334,
+ "magicResist": 2076,
+ "physicalAttack": 7751,
+ "physicalCritChance": 2150,
+ "strength": 334
+ },
+ "items": [
+ 182,
+ 179,
+ 213,
+ 207,
+ 223,
+ 225
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2219,
+ "armor": 535,
+ "armorPenetration": 3290,
+ "hp": 11000,
+ "intelligence": 412,
+ "magicResist": 2076,
+ "physicalAttack": 10285,
+ "physicalCritChance": 2544,
+ "strength": 412
+ },
+ "items": [
+ 187,
+ 179,
+ 207,
+ 223,
+ 225,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2661,
+ "armor": 535,
+ "armorPenetration": 4250,
+ "hp": 19320,
+ "intelligence": 462,
+ "magicResist": 2076,
+ "physicalAttack": 14163,
+ "physicalCritChance": 2938,
+ "strength": 462
+ },
+ "items": [
+ 179,
+ 188,
+ 228,
+ 224,
+ 239,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 12,
+ 9,
+ 3
+ ],
+ "artifacts": [
+ 1008,
+ 2001,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 2007,
+ "scale": null,
+ "type": "hero",
+ "asset": "daredevil",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0008",
+ "epicArtAsset": {
+ "name": "08_daredevil_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 32,
+ "y": 3
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "08_daredevil"
+ },
+ "role": "back",
+ "obtainType": null,
+ "characterType": "cutie",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "ranged_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 6,
+ 1
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 284,
+ 285,
+ 286,
+ 287,
+ 288
+ ]
+ },
+ "9": {
+ "id": 9,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 10
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 22,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 4,
+ 6,
+ 7,
+ 8,
+ 9,
+ 16
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "intelligence": 19,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 28,
+ 27,
+ 11,
+ 11,
+ 26,
+ 22
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 75,
+ "intelligence": 41,
+ "magicPower": 75,
+ "magicResist": 75,
+ "strength": 11
+ },
+ "items": [
+ 11,
+ 11,
+ 34,
+ 22,
+ 26,
+ 44
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 125,
+ "intelligence": 75,
+ "magicPower": 125,
+ "magicResist": 125,
+ "strength": 20
+ },
+ "items": [
+ 22,
+ 34,
+ 27,
+ 48,
+ 58,
+ 62
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 175,
+ "dodge": 30,
+ "intelligence": 114,
+ "magicPower": 309,
+ "magicResist": 167,
+ "strength": 27
+ },
+ "items": [
+ 41,
+ 45,
+ 54,
+ 58,
+ 62,
+ 75
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 225,
+ "dodge": 75,
+ "hp": 500,
+ "intelligence": 141,
+ "magicPower": 459,
+ "magicResist": 217,
+ "strength": 34
+ },
+ "items": [
+ 26,
+ 41,
+ 58,
+ 60,
+ 62,
+ 84
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 56,
+ "armor": 275,
+ "dodge": 105,
+ "hp": 500,
+ "intelligence": 173,
+ "magicPower": 609,
+ "magicResist": 317,
+ "strength": 56
+ },
+ "items": [
+ 68,
+ 67,
+ 86,
+ 88,
+ 62,
+ 95
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 63,
+ "armor": 455,
+ "dodge": 135,
+ "hp": 1300,
+ "intelligence": 228,
+ "magicPower": 849,
+ "magicResist": 497,
+ "strength": 63
+ },
+ "items": [
+ 84,
+ 63,
+ 68,
+ 86,
+ 102,
+ 119
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 90,
+ "armor": 535,
+ "dodge": 195,
+ "hp": 2100,
+ "intelligence": 295,
+ "magicPower": 1169,
+ "magicResist": 597,
+ "strength": 90
+ },
+ "items": [
+ 119,
+ 68,
+ 88,
+ 102,
+ 116,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 116,
+ "armor": 715,
+ "dodge": 255,
+ "hp": 2900,
+ "intelligence": 411,
+ "magicPower": 1649,
+ "magicResist": 757,
+ "strength": 116
+ },
+ "items": [
+ 88,
+ 132,
+ 102,
+ 115,
+ 132,
+ 137
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 204,
+ "armor": 815,
+ "dodge": 315,
+ "hp": 3700,
+ "intelligence": 607,
+ "magicPower": 1889,
+ "magicResist": 917,
+ "strength": 204
+ },
+ "items": [
+ 115,
+ 116,
+ 132,
+ 135,
+ 171,
+ 178
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 230,
+ "armor": 815,
+ "dodge": 481,
+ "hp": 6260,
+ "intelligence": 802,
+ "magicPower": 2721,
+ "magicResist": 1237,
+ "strength": 230
+ },
+ "items": [
+ 100,
+ 126,
+ 132,
+ 171,
+ 175,
+ 189
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 256,
+ "armor": 1415,
+ "dodge": 709,
+ "hp": 12660,
+ "intelligence": 967,
+ "magicPower": 3041,
+ "magicResist": 1437,
+ "strength": 256
+ },
+ "items": [
+ 95,
+ 116,
+ 178,
+ 169,
+ 180,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 306,
+ "armor": 1415,
+ "dodge": 875,
+ "hp": 15860,
+ "intelligence": 1259,
+ "magicPower": 4761,
+ "magicResist": 1597,
+ "strength": 306
+ },
+ "items": [
+ 116,
+ 119,
+ 178,
+ 180,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 308,
+ "armor": 1415,
+ "dodge": 1041,
+ "hp": 21620,
+ "intelligence": 1594,
+ "magicPower": 8537,
+ "magicResist": 1757,
+ "strength": 308
+ },
+ "items": [
+ 132,
+ 116,
+ 178,
+ 203,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 410,
+ "armor": 1415,
+ "dodge": 1207,
+ "hp": 21620,
+ "intelligence": 2339,
+ "magicPower": 10617,
+ "magicResist": 1917,
+ "strength": 410
+ },
+ "items": [
+ 212,
+ 184,
+ 203,
+ 186,
+ 226,
+ 227
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 536,
+ "armor": 2615,
+ "dodge": 1207,
+ "hp": 26420,
+ "intelligence": 3070,
+ "magicPower": 12777,
+ "magicResist": 2237,
+ "strength": 536
+ },
+ "items": [
+ 203,
+ 178,
+ 212,
+ 230,
+ 222,
+ 238
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 614,
+ "armor": 2615,
+ "dodge": 1706,
+ "hp": 26420,
+ "intelligence": 3797,
+ "magicPower": 15657,
+ "magicResist": 4157,
+ "strength": 614
+ },
+ "items": [
+ 189,
+ 186,
+ 227,
+ 230,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 7,
+ 8,
+ 10,
+ 2
+ ],
+ "artifacts": [
+ 1009,
+ 2002,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 1019,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero09_heidi",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0009",
+ "epicArtAsset": {
+ "name": "09_heidi_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1,
+ 1
+ ],
+ "screen": "obtain",
+ "x": 1,
+ "y": -17
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "09_heidi"
+ },
+ "role": "middle",
+ "obtainType": null,
+ "characterType": "warrior",
+ "silhouette": "tiny",
+ "ultCinematic": {
+ "ident": "hero09_battle_animation"
+ },
+ "roleExtended": [
+ "mage"
+ ],
+ "sfxAsset": "hero09_sfx",
+ "musicAsset": {
+ "assetIdent": "sound_theme_09_heidi",
+ "clipIdent": "theme_heidi"
+ },
+ "perk": [
+ 7,
+ 2,
+ 20
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 334,
+ "1": 335,
+ "2": 336,
+ "3": 337,
+ "4": 338,
+ "7": 8270,
+ "8": 8271
+ }
+ },
+ "10": {
+ "id": 10,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 22,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 4,
+ 7,
+ 9,
+ 13,
+ 14,
+ 6
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 385,
+ "intelligence": 12,
+ "magicPower": 25,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 7
+ },
+ "items": [
+ 6,
+ 13,
+ 11,
+ 19,
+ 25,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 15,
+ "hp": 770,
+ "intelligence": 31,
+ "magicPower": 75,
+ "magicResist": 75,
+ "physicalAttack": 58,
+ "strength": 15
+ },
+ "items": [
+ 19,
+ 19,
+ 24,
+ 28,
+ 40,
+ 42
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 17,
+ "hp": 1770,
+ "intelligence": 57,
+ "magicPower": 225,
+ "magicResist": 125,
+ "physicalAttack": 91,
+ "strength": 17
+ },
+ "items": [
+ 22,
+ 32,
+ 34,
+ 52,
+ 56,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 2770,
+ "intelligence": 91,
+ "magicPenetration": 100,
+ "magicPower": 325,
+ "magicResist": 125,
+ "physicalAttack": 91,
+ "strength": 24
+ },
+ "items": [
+ 40,
+ 42,
+ 45,
+ 56,
+ 58,
+ 71
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 4270,
+ "intelligence": 103,
+ "magicPenetration": 180,
+ "magicPower": 605,
+ "magicResist": 175,
+ "physicalAttack": 124,
+ "strength": 26
+ },
+ "items": [
+ 52,
+ 52,
+ 56,
+ 58,
+ 63,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 33,
+ "hp": 6070,
+ "intelligence": 140,
+ "magicPenetration": 280,
+ "magicPower": 785,
+ "magicResist": 275,
+ "physicalAttack": 124,
+ "strength": 33
+ },
+ "items": [
+ 75,
+ 65,
+ 71,
+ 86,
+ 91,
+ 93
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 45,
+ "hp": 8070,
+ "intelligence": 172,
+ "magicPenetration": 360,
+ "magicPower": 1065,
+ "magicResist": 455,
+ "physicalAttack": 180,
+ "strength": 45
+ },
+ "items": [
+ 67,
+ 75,
+ 65,
+ 86,
+ 91,
+ 117
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 57,
+ "hp": 10070,
+ "intelligence": 234,
+ "magicPenetration": 520,
+ "magicPower": 1305,
+ "magicResist": 715,
+ "physicalAttack": 236,
+ "strength": 57
+ },
+ "items": [
+ 65,
+ 67,
+ 86,
+ 92,
+ 117,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 64,
+ "hp": 12630,
+ "intelligence": 281,
+ "magicPenetration": 680,
+ "magicPower": 2057,
+ "magicResist": 975,
+ "physicalAttack": 427,
+ "strength": 64
+ },
+ "items": [
+ 75,
+ 119,
+ 86,
+ 117,
+ 125,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 76,
+ "hp": 16726,
+ "intelligence": 373,
+ "magicPenetration": 840,
+ "magicPower": 3196,
+ "magicResist": 1235,
+ "physicalAttack": 643,
+ "strength": 76
+ },
+ "items": [
+ 115,
+ 117,
+ 119,
+ 135,
+ 171,
+ 174
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 78,
+ "hp": 19286,
+ "intelligence": 544,
+ "magicPenetration": 1600,
+ "magicPower": 4188,
+ "magicResist": 1395,
+ "physicalAttack": 643,
+ "strength": 78
+ },
+ "items": [
+ 91,
+ 125,
+ 140,
+ 169,
+ 171,
+ 181
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 80,
+ "hp": 25382,
+ "intelligence": 655,
+ "magicPenetration": 1920,
+ "magicPower": 6087,
+ "magicResist": 1555,
+ "physicalAttack": 859,
+ "strength": 80
+ },
+ "items": [
+ 91,
+ 137,
+ 167,
+ 184,
+ 169,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 168,
+ "hp": 38182,
+ "intelligence": 965,
+ "magicPenetration": 1920,
+ "magicPower": 6687,
+ "magicResist": 1875,
+ "physicalAttack": 859,
+ "strength": 168
+ },
+ "items": [
+ 117,
+ 134,
+ 167,
+ 184,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 170,
+ "hp": 51542,
+ "intelligence": 1270,
+ "magicPenetration": 2080,
+ "magicPower": 9343,
+ "magicResist": 2451,
+ "physicalAttack": 1204,
+ "strength": 170
+ },
+ "items": [
+ 132,
+ 139,
+ 184,
+ 181,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 272,
+ "hp": 56342,
+ "intelligence": 1833,
+ "magicPenetration": 2400,
+ "magicPower": 10783,
+ "magicResist": 3180,
+ "physicalAttack": 1756,
+ "strength": 272
+ },
+ "items": [
+ 212,
+ 181,
+ 209,
+ 179,
+ 226,
+ 224
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 350,
+ "hp": 74102,
+ "intelligence": 2311,
+ "magicPenetration": 2720,
+ "magicPower": 13999,
+ "magicResist": 3180,
+ "physicalAttack": 2396,
+ "strength": 350
+ },
+ "items": [
+ 212,
+ 179,
+ 181,
+ 224,
+ 228,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 428,
+ "hp": 94422,
+ "intelligence": 2668,
+ "magicPenetration": 3040,
+ "magicPower": 17935,
+ "magicResist": 4380,
+ "physicalAttack": 3036,
+ "strength": 428
+ },
+ "items": [
+ 186,
+ 179,
+ 228,
+ 224,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 5,
+ 11,
+ 2
+ ],
+ "artifacts": [
+ 1010,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2017,
+ "scale": null,
+ "type": "hero",
+ "asset": "spell_stealer",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0010",
+ "epicArtAsset": {
+ "name": "10_faceless_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 29,
+ "y": 19
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "10_Faceless"
+ },
+ "role": "back",
+ "obtainType": "shop:socialShop",
+ "characterType": "demon",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "mage",
+ "control"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 8,
+ 2
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 46,
+ 47,
+ 48,
+ 49,
+ 50
+ ]
+ },
+ "11": {
+ "id": 11,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 55,
+ "strength": 20
+ },
+ "stars": {
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 16
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 22
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 5,
+ 8,
+ 9,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 12,
+ "strength": 14
+ },
+ "items": [
+ 13,
+ 14,
+ 10,
+ 18,
+ 25,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 25,
+ "hp": 970,
+ "intelligence": 5,
+ "magicResist": 75,
+ "physicalAttack": 70,
+ "strength": 28
+ },
+ "items": [
+ 18,
+ 18,
+ 25,
+ 28,
+ 33,
+ 24
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 25,
+ "hp": 2240,
+ "intelligence": 12,
+ "magicResist": 125,
+ "physicalAttack": 103,
+ "strength": 56
+ },
+ "items": [
+ 21,
+ 33,
+ 42,
+ 47,
+ 56,
+ 60
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 67,
+ "hp": 4160,
+ "intelligence": 19,
+ "magicResist": 225,
+ "physicalAttack": 164,
+ "strength": 95
+ },
+ "items": [
+ 21,
+ 36,
+ 33,
+ 57,
+ 60,
+ 69
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 147,
+ "hp": 4660,
+ "intelligence": 26,
+ "magicResist": 325,
+ "physicalAttack": 234,
+ "strength": 145
+ },
+ "items": [
+ 21,
+ 36,
+ 56,
+ 57,
+ 65,
+ 90
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 227,
+ "hp": 6160,
+ "intelligence": 28,
+ "magicResist": 405,
+ "physicalAttack": 430,
+ "strength": 183
+ },
+ "items": [
+ 74,
+ 64,
+ 65,
+ 85,
+ 92,
+ 94
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 40,
+ "armor": 227,
+ "hp": 7960,
+ "intelligence": 40,
+ "magicResist": 565,
+ "physicalAttack": 621,
+ "strength": 253
+ },
+ "items": [
+ 100,
+ 65,
+ 85,
+ 90,
+ 92,
+ 123
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 47,
+ "armor": 307,
+ "hp": 10560,
+ "intelligence": 47,
+ "magicResist": 845,
+ "physicalAttack": 882,
+ "strength": 316
+ },
+ "items": [
+ 64,
+ 69,
+ 69,
+ 85,
+ 131,
+ 134
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 78,
+ "armor": 467,
+ "hp": 12360,
+ "intelligence": 78,
+ "magicResist": 1181,
+ "physicalAttack": 1227,
+ "strength": 419
+ },
+ "items": [
+ 90,
+ 69,
+ 131,
+ 91,
+ 123,
+ 139
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 104,
+ "armor": 627,
+ "hp": 15960,
+ "intelligence": 104,
+ "magicResist": 1590,
+ "physicalAttack": 1849,
+ "strength": 537
+ },
+ "items": [
+ 122,
+ 123,
+ 131,
+ 134,
+ 167,
+ 170
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 130,
+ "armor": 787,
+ "hp": 23560,
+ "intelligence": 130,
+ "magicResist": 1846,
+ "physicalAttack": 2302,
+ "strength": 732
+ },
+ "items": [
+ 91,
+ 122,
+ 134,
+ 170,
+ 176,
+ 179
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 132,
+ "armor": 947,
+ "hp": 28760,
+ "intelligence": 132,
+ "magicResist": 2702,
+ "physicalAttack": 3395,
+ "strength": 843
+ },
+ "items": [
+ 127,
+ 114,
+ 167,
+ 184,
+ 184,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 212,
+ "armor": 947,
+ "hp": 45960,
+ "intelligence": 212,
+ "magicResist": 3342,
+ "physicalAttack": 3611,
+ "strength": 1097
+ },
+ "items": [
+ 123,
+ 139,
+ 179,
+ 184,
+ 168,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 214,
+ "armor": 947,
+ "hp": 62216,
+ "intelligence": 214,
+ "magicResist": 4071,
+ "physicalAttack": 6534,
+ "strength": 1129
+ },
+ "items": [
+ 134,
+ 139,
+ 179,
+ 184,
+ 179,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 292,
+ "armor": 947,
+ "hp": 73416,
+ "intelligence": 292,
+ "magicResist": 5056,
+ "physicalAttack": 8711,
+ "strength": 1486
+ },
+ "items": [
+ 211,
+ 167,
+ 208,
+ 179,
+ 221,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 370,
+ "armor": 947,
+ "hp": 89272,
+ "intelligence": 370,
+ "magicResist": 6256,
+ "physicalAttack": 10682,
+ "strength": 2061
+ },
+ "items": [
+ 183,
+ 179,
+ 211,
+ 224,
+ 228,
+ 240
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 448,
+ "armor": 1267,
+ "hp": 114392,
+ "intelligence": 448,
+ "magicResist": 7456,
+ "physicalAttack": 13626,
+ "strength": 2418
+ },
+ "items": [
+ 184,
+ 185,
+ 228,
+ 224,
+ 237,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 7,
+ 8,
+ 4,
+ 1
+ ],
+ "artifacts": [
+ 1011,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 1,
+ "scale": null,
+ "type": "hero",
+ "asset": "glutton",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0011",
+ "epicArtAsset": {
+ "name": "11_chabba_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 25,
+ "y": -3
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": "shop:arena",
+ "characterType": "snob",
+ "silhouette": "wide",
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_tank",
+ "control"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 4,
+ 8,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 478,
+ "1": 479,
+ "2": 480,
+ "3": 481,
+ "4": 482,
+ "7": 8278,
+ "8": 8279
+ }
+ },
+ "12": {
+ "id": 12,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 60,
+ "strength": 10
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 3,
+ 6,
+ 7,
+ 14
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 200,
+ "intelligence": 7,
+ "magicPower": 25,
+ "physicalAttack": 37,
+ "strength": 7
+ },
+ "items": [
+ 13,
+ 14,
+ 12,
+ 19,
+ 23,
+ 23
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 39,
+ "hp": 585,
+ "intelligence": 17,
+ "magicPower": 75,
+ "physicalAttack": 62,
+ "strength": 10
+ },
+ "items": [
+ 12,
+ 20,
+ 26,
+ 26,
+ 42,
+ 35
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 65,
+ "hp": 1085,
+ "intelligence": 25,
+ "magicPower": 175,
+ "physicalAttack": 120,
+ "strength": 18
+ },
+ "items": [
+ 27,
+ 28,
+ 35,
+ 50,
+ 57,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 94,
+ "armor": 50,
+ "hp": 1085,
+ "intelligence": 32,
+ "magicPower": 275,
+ "magicResist": 92,
+ "physicalAttack": 246,
+ "strength": 25
+ },
+ "items": [
+ 38,
+ 46,
+ 50,
+ 57,
+ 58,
+ 77
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 121,
+ "armor": 50,
+ "hp": 1585,
+ "intelligence": 34,
+ "lifesteal": 5,
+ "magicPower": 425,
+ "magicResist": 134,
+ "physicalAttack": 405,
+ "strength": 27
+ },
+ "items": [
+ 42,
+ 44,
+ 57,
+ 58,
+ 77,
+ 87
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 138,
+ "armor": 100,
+ "hp": 2085,
+ "intelligence": 41,
+ "lifesteal": 10,
+ "magicPower": 525,
+ "magicResist": 184,
+ "physicalAttack": 578,
+ "strength": 34
+ },
+ "items": [
+ 65,
+ 66,
+ 77,
+ 77,
+ 88,
+ 93
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 156,
+ "armor": 200,
+ "hp": 2885,
+ "intelligence": 43,
+ "lifesteal": 20,
+ "magicPower": 805,
+ "magicResist": 264,
+ "physicalAttack": 690,
+ "strength": 36
+ },
+ "items": [
+ 63,
+ 66,
+ 88,
+ 91,
+ 120,
+ 124
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 204,
+ "armor": 300,
+ "hp": 6485,
+ "intelligence": 45,
+ "lifesteal": 30,
+ "magicPower": 965,
+ "magicResist": 264,
+ "physicalAttack": 854,
+ "strength": 38
+ },
+ "items": [
+ 87,
+ 88,
+ 114,
+ 121,
+ 124,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 305,
+ "armor": 400,
+ "hp": 8885,
+ "intelligence": 76,
+ "lifesteal": 40,
+ "magicPower": 1045,
+ "magicResist": 424,
+ "physicalAttack": 1140,
+ "strength": 69
+ },
+ "items": [
+ 66,
+ 114,
+ 133,
+ 124,
+ 126,
+ 138
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 463,
+ "armor": 400,
+ "hp": 12085,
+ "intelligence": 140,
+ "lifesteal": 50,
+ "magicPower": 1365,
+ "magicResist": 424,
+ "physicalAttack": 1412,
+ "strength": 133
+ },
+ "items": [
+ 122,
+ 124,
+ 134,
+ 135,
+ 172,
+ 168
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 574,
+ "armor": 560,
+ "hp": 14645,
+ "intelligence": 142,
+ "lifesteal": 60,
+ "magicPower": 1877,
+ "magicResist": 680,
+ "physicalAttack": 2265,
+ "strength": 135
+ },
+ "items": [
+ 124,
+ 133,
+ 122,
+ 168,
+ 172,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 739,
+ "armor": 720,
+ "hp": 17845,
+ "intelligence": 168,
+ "lifesteal": 70,
+ "magicPower": 2837,
+ "magicResist": 680,
+ "physicalAttack": 2773,
+ "strength": 161
+ },
+ "items": [
+ 124,
+ 96,
+ 167,
+ 183,
+ 179,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1001,
+ "armor": 1040,
+ "hp": 31845,
+ "intelligence": 218,
+ "lifesteal": 80,
+ "magicPower": 2837,
+ "magicResist": 680,
+ "physicalAttack": 3413,
+ "strength": 211
+ },
+ "items": [
+ 133,
+ 140,
+ 167,
+ 179,
+ 169,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1057,
+ "armor": 1040,
+ "hp": 51797,
+ "intelligence": 244,
+ "lifesteal": 80,
+ "magicPower": 4256,
+ "magicResist": 680,
+ "physicalAttack": 5384,
+ "strength": 237
+ },
+ "items": [
+ 138,
+ 139,
+ 167,
+ 179,
+ 180,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1500,
+ "armor": 1040,
+ "hp": 64197,
+ "intelligence": 360,
+ "lifesteal": 80,
+ "magicPower": 5216,
+ "magicResist": 1089,
+ "physicalAttack": 6576,
+ "strength": 353
+ },
+ "items": [
+ 201,
+ 184,
+ 213,
+ 203,
+ 226,
+ 223
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2075,
+ "armor": 1040,
+ "hp": 74117,
+ "intelligence": 590,
+ "lifesteal": 80,
+ "magicPower": 7376,
+ "magicResist": 1409,
+ "physicalAttack": 7600,
+ "strength": 431
+ },
+ "items": [
+ 187,
+ 180,
+ 201,
+ 223,
+ 226,
+ 240
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2517,
+ "armor": 1040,
+ "hp": 87557,
+ "intelligence": 640,
+ "lifesteal": 80,
+ "magicPower": 9536,
+ "magicResist": 1409,
+ "physicalAttack": 10928,
+ "strength": 481
+ },
+ "items": [
+ 167,
+ 209,
+ 228,
+ 227,
+ 240,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 7,
+ 6,
+ 3
+ ],
+ "artifacts": [
+ 1012,
+ 2006,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 1002,
+ "scale": null,
+ "type": "hero",
+ "asset": "arachne",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0012",
+ "epicArtAsset": {
+ "name": "12_arachne_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 33,
+ "y": 44
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "12_arachne"
+ },
+ "role": "middle",
+ "obtainType": "shop:clanWar",
+ "characterType": "snob",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "control",
+ "melee_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 8,
+ 10,
+ 1
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 345,
+ 346,
+ 347,
+ 348,
+ 349
+ ]
+ },
+ "13": {
+ "id": 13,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 21,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 7,
+ 2,
+ 6,
+ 7,
+ 8,
+ 9
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 7,
+ "magicPower": 50,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 8,
+ 22,
+ 11,
+ 19,
+ 24,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 50,
+ "hp": 700,
+ "intelligence": 31,
+ "magicPower": 150,
+ "magicResist": 25,
+ "strength": 10
+ },
+ "items": [
+ 11,
+ 19,
+ 24,
+ 27,
+ 45,
+ 46
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 100,
+ "hp": 1700,
+ "intelligence": 45,
+ "magicPower": 300,
+ "magicResist": 75,
+ "strength": 13
+ },
+ "items": [
+ 32,
+ 52,
+ 46,
+ 48,
+ 58,
+ 41
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 150,
+ "hp": 2200,
+ "intelligence": 82,
+ "magicPenetration": 100,
+ "magicPower": 534,
+ "magicResist": 117,
+ "strength": 15
+ },
+ "items": [
+ 41,
+ 45,
+ 46,
+ 56,
+ 60,
+ 71
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 200,
+ "hp": 3700,
+ "intelligence": 94,
+ "magicPenetration": 180,
+ "magicPower": 714,
+ "magicResist": 267,
+ "strength": 17
+ },
+ "items": [
+ 40,
+ 46,
+ 58,
+ 60,
+ 71,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 300,
+ "hp": 5000,
+ "intelligence": 106,
+ "magicPenetration": 260,
+ "magicPower": 1074,
+ "magicResist": 367,
+ "strength": 19
+ },
+ "items": [
+ 64,
+ 67,
+ 71,
+ 88,
+ 93,
+ 95
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 400,
+ "hp": 6600,
+ "intelligence": 146,
+ "magicPenetration": 340,
+ "magicPower": 1514,
+ "magicResist": 527,
+ "strength": 21
+ },
+ "items": [
+ 58,
+ 95,
+ 64,
+ 67,
+ 88,
+ 117
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 500,
+ "hp": 8200,
+ "intelligence": 216,
+ "magicPenetration": 500,
+ "magicPower": 1934,
+ "magicResist": 687,
+ "strength": 23
+ },
+ "items": [
+ 132,
+ 60,
+ 67,
+ 88,
+ 98,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 49,
+ "armor": 600,
+ "hp": 11560,
+ "intelligence": 272,
+ "magicPenetration": 700,
+ "magicPower": 2606,
+ "magicResist": 867,
+ "strength": 49
+ },
+ "items": [
+ 132,
+ 67,
+ 88,
+ 98,
+ 135,
+ 137
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 113,
+ "armor": 700,
+ "hp": 14920,
+ "intelligence": 414,
+ "magicPenetration": 900,
+ "magicPower": 3278,
+ "magicResist": 947,
+ "strength": 113
+ },
+ "items": [
+ 98,
+ 115,
+ 119,
+ 140,
+ 171,
+ 176
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 115,
+ "armor": 700,
+ "hp": 19016,
+ "intelligence": 555,
+ "magicPenetration": 1100,
+ "magicPower": 4417,
+ "magicResist": 1707,
+ "strength": 115
+ },
+ "items": [
+ 115,
+ 116,
+ 137,
+ 171,
+ 167,
+ 181
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 155,
+ "armor": 700,
+ "hp": 25016,
+ "intelligence": 782,
+ "magicPenetration": 1420,
+ "magicPower": 5217,
+ "magicResist": 2027,
+ "strength": 155
+ },
+ "items": [
+ 126,
+ 116,
+ 169,
+ 181,
+ 184,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 205,
+ "armor": 700,
+ "hp": 31416,
+ "intelligence": 1036,
+ "magicPenetration": 1740,
+ "magicPower": 6777,
+ "magicResist": 2507,
+ "strength": 205
+ },
+ "items": [
+ 117,
+ 140,
+ 169,
+ 181,
+ 180,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 207,
+ "armor": 700,
+ "hp": 41272,
+ "intelligence": 1189,
+ "magicPenetration": 2220,
+ "magicPower": 11332,
+ "magicResist": 2507,
+ "strength": 207
+ },
+ "items": [
+ 137,
+ 140,
+ 169,
+ 181,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 323,
+ "armor": 700,
+ "hp": 45368,
+ "intelligence": 1784,
+ "magicPenetration": 2540,
+ "magicPower": 14191,
+ "magicResist": 2507,
+ "strength": 323
+ },
+ "items": [
+ 209,
+ 174,
+ 212,
+ 180,
+ 226,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 401,
+ "armor": 700,
+ "hp": 51128,
+ "intelligence": 2262,
+ "magicPenetration": 3140,
+ "magicPower": 17887,
+ "magicResist": 3707,
+ "strength": 401
+ },
+ "items": [
+ 183,
+ 180,
+ 212,
+ 228,
+ 232,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 479,
+ "armor": 1020,
+ "hp": 64248,
+ "intelligence": 2619,
+ "magicPenetration": 4340,
+ "magicPower": 22303,
+ "magicResist": 4907,
+ "strength": 479
+ },
+ "items": [
+ 183,
+ 203,
+ 227,
+ 224,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 11,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1013,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2001,
+ "scale": null,
+ "type": "hero",
+ "asset": "elemental",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0013",
+ "epicArtAsset": {
+ "name": "13_orion_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1,
+ 1
+ ],
+ "screen": "obtain",
+ "x": 0,
+ "y": 0
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "13_Orion"
+ },
+ "role": "back",
+ "obtainType": "shop:tower",
+ "characterType": "snob",
+ "silhouette": "flying",
+ "ultCinematic": null,
+ "roleExtended": [
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 2,
+ 21
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 451,
+ "1": 452,
+ "2": 453,
+ "3": 454,
+ "4": 455,
+ "7": 8274,
+ "8": 8275
+ }
+ },
+ "14": {
+ "id": 14,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 60,
+ "strength": 10
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 6,
+ 8,
+ 9,
+ 14
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 7,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "strength": 7
+ },
+ "items": [
+ 23,
+ 9,
+ 12,
+ 20,
+ 24,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 25,
+ "hp": 700,
+ "intelligence": 10,
+ "magicResist": 50,
+ "physicalAttack": 95,
+ "strength": 10
+ },
+ "items": [
+ 12,
+ 20,
+ 25,
+ 27,
+ 28,
+ 51
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 55,
+ "armor": 75,
+ "hp": 700,
+ "intelligence": 23,
+ "magicResist": 100,
+ "physicalAttack": 153,
+ "strength": 23
+ },
+ "items": [
+ 24,
+ 38,
+ 44,
+ 53,
+ 57,
+ 61
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 67,
+ "armor": 125,
+ "armorPenetration": 50,
+ "hp": 1200,
+ "intelligence": 25,
+ "magicResist": 150,
+ "physicalAttack": 289,
+ "physicalCritChance": 30,
+ "strength": 25
+ },
+ "items": [
+ 29,
+ 35,
+ 39,
+ 57,
+ 59,
+ 70
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 91,
+ "armor": 225,
+ "armorPenetration": 130,
+ "hp": 1200,
+ "intelligence": 32,
+ "magicResist": 200,
+ "physicalAttack": 415,
+ "physicalCritChance": 45,
+ "strength": 32
+ },
+ "items": [
+ 27,
+ 28,
+ 42,
+ 66,
+ 70,
+ 89
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 125,
+ "armor": 275,
+ "armorPenetration": 210,
+ "hp": 1700,
+ "intelligence": 34,
+ "magicResist": 250,
+ "physicalAttack": 616,
+ "physicalCritChance": 75,
+ "strength": 34
+ },
+ "items": [
+ 66,
+ 65,
+ 70,
+ 76,
+ 89,
+ 96
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 212,
+ "armor": 275,
+ "armorPenetration": 290,
+ "hp": 1700,
+ "intelligence": 41,
+ "magicResist": 330,
+ "physicalAttack": 840,
+ "physicalCritChance": 105,
+ "strength": 41
+ },
+ "items": [
+ 64,
+ 72,
+ 76,
+ 89,
+ 99,
+ 118
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 275,
+ "armor": 475,
+ "armorPenetration": 450,
+ "hp": 2500,
+ "intelligence": 48,
+ "magicResist": 410,
+ "physicalAttack": 1060,
+ "physicalCritChance": 159,
+ "strength": 48
+ },
+ "items": [
+ 56,
+ 120,
+ 72,
+ 76,
+ 118,
+ 134
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 352,
+ "armor": 475,
+ "armorPenetration": 610,
+ "hp": 3500,
+ "intelligence": 55,
+ "magicResist": 666,
+ "physicalAttack": 1677,
+ "physicalCritChance": 183,
+ "strength": 55
+ },
+ "items": [
+ 133,
+ 69,
+ 72,
+ 89,
+ 118,
+ 139
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 454,
+ "armor": 555,
+ "armorPenetration": 770,
+ "hp": 3500,
+ "intelligence": 81,
+ "magicResist": 1075,
+ "physicalAttack": 2449,
+ "physicalCritChance": 237,
+ "strength": 97
+ },
+ "items": [
+ 118,
+ 122,
+ 134,
+ 133,
+ 172,
+ 177
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 649,
+ "armor": 715,
+ "armorPenetration": 930,
+ "hp": 3500,
+ "intelligence": 107,
+ "magicResist": 1331,
+ "physicalAttack": 3010,
+ "physicalCritChance": 403,
+ "strength": 123
+ },
+ "items": [
+ 121,
+ 122,
+ 133,
+ 173,
+ 172,
+ 188
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 844,
+ "armor": 875,
+ "armorPenetration": 1530,
+ "hp": 3500,
+ "intelligence": 133,
+ "magicResist": 1491,
+ "physicalAttack": 3438,
+ "physicalCritChance": 631,
+ "strength": 149
+ },
+ "items": [
+ 120,
+ 122,
+ 168,
+ 173,
+ 188,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1098,
+ "armor": 1035,
+ "armorPenetration": 2130,
+ "hp": 3500,
+ "intelligence": 183,
+ "magicResist": 1491,
+ "physicalAttack": 4374,
+ "physicalCritChance": 859,
+ "strength": 199
+ },
+ "items": [
+ 125,
+ 138,
+ 168,
+ 182,
+ 188,
+ 207
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1186,
+ "armor": 1035,
+ "armorPenetration": 2450,
+ "hp": 3500,
+ "intelligence": 223,
+ "magicResist": 1651,
+ "physicalAttack": 6404,
+ "physicalCritChance": 1481,
+ "strength": 239
+ },
+ "items": [
+ 139,
+ 138,
+ 173,
+ 182,
+ 202,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1629,
+ "armor": 1035,
+ "armorPenetration": 3370,
+ "hp": 3500,
+ "intelligence": 339,
+ "magicResist": 2060,
+ "physicalAttack": 7924,
+ "physicalCritChance": 1746,
+ "strength": 355
+ },
+ "items": [
+ 182,
+ 179,
+ 213,
+ 207,
+ 223,
+ 225
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2204,
+ "armor": 1035,
+ "armorPenetration": 3690,
+ "hp": 6700,
+ "intelligence": 417,
+ "magicResist": 2060,
+ "physicalAttack": 10458,
+ "physicalCritChance": 2140,
+ "strength": 433
+ },
+ "items": [
+ 187,
+ 183,
+ 207,
+ 223,
+ 225,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2646,
+ "armor": 1355,
+ "armorPenetration": 4650,
+ "hp": 16620,
+ "intelligence": 467,
+ "magicResist": 2060,
+ "physicalAttack": 13696,
+ "physicalCritChance": 2534,
+ "strength": 483
+ },
+ "items": [
+ 179,
+ 188,
+ 228,
+ 227,
+ 239,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 7,
+ 12,
+ 9,
+ 3
+ ],
+ "artifacts": [
+ 1014,
+ 2004,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 2018,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero14_fox",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0014",
+ "epicArtAsset": {
+ "name": "14_fox_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 30,
+ "y": 62
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": null,
+ "characterType": "cutie",
+ "silhouette": "tiny",
+ "ultCinematic": null,
+ "roleExtended": [
+ "ranged_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 6,
+ 1,
+ 13,
+ 20
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 274,
+ 275,
+ 276,
+ 277,
+ 278
+ ]
+ },
+ "15": {
+ "id": 15,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 60,
+ "strength": 10
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 3,
+ 8,
+ 14,
+ 20
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 2,
+ "physicalAttack": 62,
+ "strength": 2
+ },
+ "items": [
+ 9,
+ 13,
+ 20,
+ 12,
+ 23,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 38,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 5,
+ "magicResist": 25,
+ "physicalAttack": 120,
+ "strength": 5
+ },
+ "items": [
+ 14,
+ 18,
+ 27,
+ 24,
+ 39,
+ 35
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 62,
+ "armor": 75,
+ "hp": 1470,
+ "intelligence": 12,
+ "magicResist": 75,
+ "physicalAttack": 145,
+ "strength": 19
+ },
+ "items": [
+ 28,
+ 27,
+ 38,
+ 53,
+ 66,
+ 56
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 90,
+ "armor": 125,
+ "armorPenetration": 50,
+ "hp": 2470,
+ "intelligence": 14,
+ "magicResist": 125,
+ "physicalAttack": 267,
+ "strength": 21
+ },
+ "items": [
+ 24,
+ 31,
+ 50,
+ 57,
+ 59,
+ 66
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 123,
+ "armor": 225,
+ "armorPenetration": 100,
+ "hp": 2970,
+ "intelligence": 16,
+ "magicResist": 167,
+ "physicalAttack": 449,
+ "strength": 23
+ },
+ "items": [
+ 38,
+ 43,
+ 60,
+ 56,
+ 70,
+ 87
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 150,
+ "armor": 275,
+ "armorPenetration": 180,
+ "hp": 3970,
+ "intelligence": 23,
+ "magicResist": 267,
+ "physicalAttack": 641,
+ "strength": 30
+ },
+ "items": [
+ 59,
+ 64,
+ 70,
+ 64,
+ 92,
+ 120
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 182,
+ "armor": 375,
+ "armorPenetration": 260,
+ "hp": 5570,
+ "intelligence": 25,
+ "magicResist": 427,
+ "physicalAttack": 940,
+ "strength": 32
+ },
+ "items": [
+ 69,
+ 66,
+ 84,
+ 91,
+ 97,
+ 121
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 250,
+ "armor": 455,
+ "armorPenetration": 460,
+ "hp": 7570,
+ "intelligence": 47,
+ "magicResist": 587,
+ "physicalAttack": 996,
+ "strength": 70
+ },
+ "items": [
+ 70,
+ 65,
+ 133,
+ 99,
+ 91,
+ 134
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 306,
+ "armor": 655,
+ "armorPenetration": 540,
+ "hp": 9570,
+ "intelligence": 73,
+ "magicResist": 923,
+ "physicalAttack": 1453,
+ "strength": 96
+ },
+ "items": [
+ 87,
+ 125,
+ 114,
+ 91,
+ 133,
+ 138
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 463,
+ "armor": 655,
+ "armorPenetration": 540,
+ "hp": 13170,
+ "intelligence": 142,
+ "magicResist": 1083,
+ "physicalAttack": 1955,
+ "strength": 165
+ },
+ "items": [
+ 114,
+ 120,
+ 133,
+ 134,
+ 172,
+ 173
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 658,
+ "armor": 655,
+ "armorPenetration": 1140,
+ "hp": 14770,
+ "intelligence": 168,
+ "magicResist": 1339,
+ "physicalAttack": 2624,
+ "strength": 191
+ },
+ "items": [
+ 121,
+ 120,
+ 122,
+ 168,
+ 172,
+ 187
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 1051,
+ "armor": 815,
+ "armorPenetration": 1140,
+ "hp": 14770,
+ "intelligence": 218,
+ "magicResist": 1499,
+ "physicalAttack": 3240,
+ "strength": 241
+ },
+ "items": [
+ 120,
+ 138,
+ 175,
+ 168,
+ 176,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1391,
+ "armor": 1415,
+ "armorPenetration": 1140,
+ "hp": 14770,
+ "intelligence": 306,
+ "magicResist": 2099,
+ "physicalAttack": 3748,
+ "strength": 329
+ },
+ "items": [
+ 122,
+ 127,
+ 182,
+ 184,
+ 201,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1423,
+ "armor": 1575,
+ "armorPenetration": 1460,
+ "hp": 31346,
+ "intelligence": 338,
+ "magicResist": 2419,
+ "physicalAttack": 6531,
+ "strength": 361
+ },
+ "items": [
+ 121,
+ 127,
+ 173,
+ 201,
+ 201,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1840,
+ "armor": 1575,
+ "armorPenetration": 2060,
+ "hp": 41586,
+ "intelligence": 446,
+ "magicResist": 2579,
+ "physicalAttack": 8579,
+ "strength": 469
+ },
+ "items": [
+ 182,
+ 184,
+ 213,
+ 208,
+ 227,
+ 223
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2415,
+ "armor": 2775,
+ "armorPenetration": 2380,
+ "hp": 53042,
+ "intelligence": 524,
+ "magicResist": 2899,
+ "physicalAttack": 10230,
+ "strength": 547
+ },
+ "items": [
+ 213,
+ 183,
+ 179,
+ 228,
+ 223,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2990,
+ "armor": 3095,
+ "armorPenetration": 3340,
+ "hp": 66162,
+ "intelligence": 602,
+ "magicResist": 4099,
+ "physicalAttack": 12534,
+ "strength": 625
+ },
+ "items": [
+ 168,
+ 187,
+ 228,
+ 225,
+ 237,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 8,
+ 12,
+ 3
+ ],
+ "artifacts": [
+ 1015,
+ 2004,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 2005,
+ "scale": null,
+ "type": "hero",
+ "asset": "pirate",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0015",
+ "epicArtAsset": {
+ "name": "15_ginger_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.22,
+ 1.2
+ ],
+ "screen": "obtain",
+ "x": -147.60000000000002,
+ "y": -7.800000000000011
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "15_Ginger",
+ "transform": [
+ {
+ "scale": [
+ -1,
+ 1
+ ],
+ "screen": "obtain",
+ "x": 0,
+ "y": 0
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": null,
+ "characterType": "snob",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "ranged_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 6,
+ 1,
+ 13
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 71,
+ 72,
+ 73,
+ 74,
+ 75
+ ]
+ },
+ "16": {
+ "id": 16,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 60,
+ "strength": 10
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 16
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 3,
+ 8,
+ 9,
+ 13,
+ 14,
+ 20
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 50,
+ "strength": 2
+ },
+ "items": [
+ 13,
+ 17,
+ 12,
+ 20,
+ 31,
+ 30
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 35,
+ "armor": 25,
+ "armorPenetration": 50,
+ "dodge": 15,
+ "hp": 770,
+ "intelligence": 5,
+ "magicResist": 25,
+ "physicalAttack": 75,
+ "strength": 5
+ },
+ "items": [
+ 12,
+ 20,
+ 24,
+ 43,
+ 28,
+ 35
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 61,
+ "armor": 75,
+ "armorPenetration": 50,
+ "dodge": 15,
+ "hp": 1270,
+ "intelligence": 13,
+ "magicResist": 75,
+ "physicalAttack": 133,
+ "strength": 13
+ },
+ "items": [
+ 23,
+ 38,
+ 43,
+ 53,
+ 56,
+ 62
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 83,
+ "armor": 125,
+ "armorPenetration": 100,
+ "dodge": 45,
+ "hp": 2270,
+ "intelligence": 15,
+ "magicResist": 75,
+ "physicalAttack": 232,
+ "strength": 15
+ },
+ "items": [
+ 39,
+ 43,
+ 54,
+ 57,
+ 62,
+ 70
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 95,
+ "armor": 175,
+ "armorPenetration": 180,
+ "dodge": 90,
+ "hp": 2770,
+ "intelligence": 17,
+ "magicResist": 125,
+ "physicalAttack": 391,
+ "strength": 17
+ },
+ "items": [
+ 44,
+ 53,
+ 57,
+ 59,
+ 73,
+ 87
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 112,
+ "armor": 325,
+ "armorPenetration": 230,
+ "dodge": 114,
+ "hp": 3570,
+ "intelligence": 24,
+ "magicResist": 175,
+ "physicalAttack": 564,
+ "strength": 24
+ },
+ "items": [
+ 64,
+ 66,
+ 73,
+ 87,
+ 97,
+ 91
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 145,
+ "armor": 325,
+ "armorPenetration": 430,
+ "dodge": 138,
+ "hp": 7170,
+ "intelligence": 31,
+ "magicResist": 255,
+ "physicalAttack": 690,
+ "strength": 31
+ },
+ "items": [
+ 65,
+ 76,
+ 87,
+ 91,
+ 102,
+ 118
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 207,
+ "armor": 325,
+ "armorPenetration": 590,
+ "dodge": 198,
+ "hp": 9170,
+ "intelligence": 43,
+ "magicResist": 335,
+ "physicalAttack": 924,
+ "strength": 43
+ },
+ "items": [
+ 73,
+ 87,
+ 97,
+ 114,
+ 122,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 278,
+ "armor": 485,
+ "armorPenetration": 790,
+ "dodge": 222,
+ "hp": 11570,
+ "intelligence": 74,
+ "magicResist": 335,
+ "physicalAttack": 1318,
+ "strength": 74
+ },
+ "items": [
+ 87,
+ 90,
+ 118,
+ 122,
+ 125,
+ 138
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 411,
+ "armor": 725,
+ "armorPenetration": 950,
+ "dodge": 222,
+ "hp": 11570,
+ "intelligence": 119,
+ "magicResist": 495,
+ "physicalAttack": 1890,
+ "strength": 135
+ },
+ "items": [
+ 114,
+ 118,
+ 133,
+ 134,
+ 172,
+ 178
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 606,
+ "armor": 725,
+ "armorPenetration": 1110,
+ "dodge": 388,
+ "hp": 13170,
+ "intelligence": 145,
+ "magicResist": 751,
+ "physicalAttack": 2559,
+ "strength": 161
+ },
+ "items": [
+ 122,
+ 133,
+ 125,
+ 172,
+ 173,
+ 189
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 771,
+ "armor": 885,
+ "armorPenetration": 1710,
+ "dodge": 616,
+ "hp": 17970,
+ "intelligence": 171,
+ "magicResist": 911,
+ "physicalAttack": 2883,
+ "strength": 187
+ },
+ "items": [
+ 120,
+ 133,
+ 178,
+ 183,
+ 182,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1079,
+ "armor": 1205,
+ "armorPenetration": 2030,
+ "dodge": 782,
+ "hp": 22770,
+ "intelligence": 245,
+ "magicResist": 911,
+ "physicalAttack": 3311,
+ "strength": 261
+ },
+ "items": [
+ 133,
+ 118,
+ 175,
+ 179,
+ 189,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1165,
+ "armor": 1805,
+ "armorPenetration": 2190,
+ "dodge": 1010,
+ "hp": 37426,
+ "intelligence": 271,
+ "magicResist": 911,
+ "physicalAttack": 5390,
+ "strength": 287
+ },
+ "items": [
+ 134,
+ 139,
+ 179,
+ 182,
+ 189,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1522,
+ "armor": 1805,
+ "armorPenetration": 2510,
+ "dodge": 1238,
+ "hp": 45426,
+ "intelligence": 349,
+ "magicResist": 1576,
+ "physicalAttack": 7247,
+ "strength": 365
+ },
+ "items": [
+ 189,
+ 173,
+ 208,
+ 213,
+ 230,
+ 223
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2097,
+ "armor": 1805,
+ "armorPenetration": 3110,
+ "dodge": 1799,
+ "hp": 56882,
+ "intelligence": 427,
+ "magicResist": 1576,
+ "physicalAttack": 8578,
+ "strength": 443
+ },
+ "items": [
+ 187,
+ 189,
+ 208,
+ 228,
+ 231,
+ 237
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2321,
+ "armor": 3725,
+ "armorPenetration": 4310,
+ "dodge": 2027,
+ "hp": 73458,
+ "intelligence": 477,
+ "magicResist": 2776,
+ "physicalAttack": 10933,
+ "strength": 493
+ },
+ "items": [
+ 189,
+ 187,
+ 231,
+ 224,
+ 237,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 7,
+ 10,
+ 12,
+ 3
+ ],
+ "artifacts": [
+ 1016,
+ 2002,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 1004,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero16_dante",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0016",
+ "epicArtAsset": {
+ "name": "16_dante_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 26,
+ "y": 62
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "16_Dante",
+ "transform": [
+ {
+ "scale": [
+ -1,
+ 1
+ ],
+ "screen": "obtain",
+ "x": 0,
+ "y": 0
+ }
+ ]
+ },
+ "role": "middle",
+ "obtainType": "shop:tower",
+ "characterType": "snob",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "ranged_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 6,
+ 2
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 299,
+ "1": 350,
+ "2": 301,
+ "3": 302,
+ "4": 303
+ }
+ },
+ "17": {
+ "id": 17,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 21,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 4,
+ 6,
+ 7,
+ 9,
+ 13
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 585,
+ "intelligence": 12,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 4,
+ 7,
+ 11,
+ 19,
+ 24,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 1085,
+ "intelligence": 31,
+ "magicPower": 100,
+ "magicResist": 75,
+ "strength": 10
+ },
+ "items": [
+ 11,
+ 11,
+ 24,
+ 28,
+ 34,
+ 34
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 1585,
+ "intelligence": 67,
+ "magicPower": 100,
+ "magicResist": 125,
+ "strength": 24
+ },
+ "items": [
+ 32,
+ 34,
+ 40,
+ 52,
+ 56,
+ 28
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 31,
+ "hp": 2585,
+ "intelligence": 101,
+ "magicPenetration": 100,
+ "magicPower": 150,
+ "magicResist": 175,
+ "strength": 31
+ },
+ "items": [
+ 34,
+ 48,
+ 52,
+ 56,
+ 28,
+ 71
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 38,
+ "hp": 3585,
+ "intelligence": 140,
+ "magicPenetration": 230,
+ "magicPower": 314,
+ "magicResist": 267,
+ "strength": 38
+ },
+ "items": [
+ 22,
+ 52,
+ 56,
+ 67,
+ 71,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 45,
+ "hp": 4585,
+ "intelligence": 177,
+ "magicPenetration": 360,
+ "magicPower": 474,
+ "magicResist": 447,
+ "strength": 45
+ },
+ "items": [
+ 63,
+ 64,
+ 86,
+ 93,
+ 67,
+ 98
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 52,
+ "hp": 6185,
+ "intelligence": 194,
+ "magicPenetration": 560,
+ "magicPower": 834,
+ "magicResist": 707,
+ "strength": 52
+ },
+ "items": [
+ 60,
+ 71,
+ 86,
+ 91,
+ 95,
+ 117
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 59,
+ "hp": 8185,
+ "intelligence": 279,
+ "magicPenetration": 800,
+ "magicPower": 1074,
+ "magicResist": 907,
+ "strength": 59
+ },
+ "items": [
+ 64,
+ 64,
+ 98,
+ 116,
+ 126,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 85,
+ "hp": 11385,
+ "intelligence": 365,
+ "magicPenetration": 1000,
+ "magicPower": 1554,
+ "magicResist": 1227,
+ "strength": 85
+ },
+ "items": [
+ 115,
+ 86,
+ 86,
+ 98,
+ 132,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 121,
+ "hp": 15481,
+ "intelligence": 451,
+ "magicPenetration": 1200,
+ "magicPower": 2533,
+ "magicResist": 1587,
+ "strength": 121
+ },
+ "items": [
+ 98,
+ 115,
+ 117,
+ 140,
+ 176,
+ 171
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 123,
+ "hp": 19577,
+ "intelligence": 592,
+ "magicPenetration": 1560,
+ "magicPower": 3672,
+ "magicResist": 2347,
+ "strength": 123
+ },
+ "items": [
+ 132,
+ 115,
+ 135,
+ 171,
+ 176,
+ 181
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 149,
+ "hp": 22137,
+ "intelligence": 757,
+ "magicPenetration": 1880,
+ "magicPower": 4824,
+ "magicResist": 3107,
+ "strength": 149
+ },
+ "items": [
+ 119,
+ 126,
+ 174,
+ 181,
+ 184,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 199,
+ "hp": 28537,
+ "intelligence": 1011,
+ "magicPenetration": 2800,
+ "magicPower": 5784,
+ "magicResist": 3427,
+ "strength": 199
+ },
+ "items": [
+ 126,
+ 132,
+ 184,
+ 181,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 225,
+ "hp": 37497,
+ "intelligence": 1340,
+ "magicPenetration": 3120,
+ "magicPower": 9080,
+ "magicResist": 3747,
+ "strength": 225
+ },
+ "items": [
+ 135,
+ 140,
+ 181,
+ 184,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 303,
+ "hp": 48953,
+ "intelligence": 1849,
+ "magicPenetration": 3440,
+ "magicPower": 11851,
+ "magicResist": 4067,
+ "strength": 303
+ },
+ "items": [
+ 167,
+ 180,
+ 212,
+ 209,
+ 228,
+ 232
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 381,
+ "hp": 60713,
+ "intelligence": 2327,
+ "magicPenetration": 4640,
+ "magicPower": 14347,
+ "magicResist": 5267,
+ "strength": 381
+ },
+ "items": [
+ 181,
+ 186,
+ 212,
+ 224,
+ 228,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 507,
+ "hp": 77833,
+ "intelligence": 2906,
+ "magicPenetration": 4960,
+ "magicPower": 18283,
+ "magicResist": 6467,
+ "strength": 507
+ },
+ "items": [
+ 203,
+ 181,
+ 228,
+ 224,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 8,
+ 11,
+ 4,
+ 2
+ ],
+ "artifacts": [
+ 1017,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 1018,
+ "scale": null,
+ "type": "hero",
+ "asset": "shaman",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0017",
+ "epicArtAsset": {
+ "name": "17_mojo_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.11,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 70.39999999999998,
+ "y": 59.60000000000002
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "17_Mojo"
+ },
+ "role": "middle",
+ "obtainType": null,
+ "characterType": "snob",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "mage",
+ "support"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 5,
+ 2
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 380,
+ 381,
+ 382,
+ 383,
+ 384
+ ]
+ },
+ "18": {
+ "id": 18,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 14,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 19,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 22,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 9,
+ 16,
+ 7,
+ 13,
+ 11,
+ 16
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "hp": 385,
+ "intelligence": 21,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 3
+ },
+ "items": [
+ 8,
+ 11,
+ 16,
+ 19,
+ 32,
+ 24
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 25,
+ "hp": 885,
+ "intelligence": 42,
+ "magicPenetration": 50,
+ "magicPower": 75,
+ "magicResist": 25,
+ "strength": 6
+ },
+ "items": [
+ 11,
+ 13,
+ 26,
+ 45,
+ 46,
+ 41
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 75,
+ "hp": 1770,
+ "intelligence": 59,
+ "magicPenetration": 50,
+ "magicPower": 225,
+ "magicResist": 75,
+ "strength": 9
+ },
+ "items": [
+ 32,
+ 24,
+ 45,
+ 52,
+ 58,
+ 71
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 75,
+ "hp": 2270,
+ "intelligence": 71,
+ "magicPenetration": 230,
+ "magicPower": 455,
+ "magicResist": 125,
+ "strength": 11
+ },
+ "items": [
+ 46,
+ 41,
+ 40,
+ 56,
+ 60,
+ 63
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 125,
+ "hp": 4570,
+ "intelligence": 93,
+ "magicPenetration": 230,
+ "magicPower": 635,
+ "magicResist": 225,
+ "strength": 13
+ },
+ "items": [
+ 52,
+ 40,
+ 58,
+ 59,
+ 67,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 325,
+ "hp": 5370,
+ "intelligence": 115,
+ "magicPenetration": 280,
+ "magicPower": 945,
+ "magicResist": 305,
+ "strength": 15
+ },
+ "items": [
+ 58,
+ 64,
+ 71,
+ 68,
+ 95,
+ 119
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 405,
+ "hp": 6170,
+ "intelligence": 185,
+ "magicPenetration": 360,
+ "magicPower": 1365,
+ "magicResist": 385,
+ "strength": 17
+ },
+ "items": [
+ 56,
+ 75,
+ 68,
+ 98,
+ 119,
+ 116
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 485,
+ "hp": 7170,
+ "intelligence": 262,
+ "magicPenetration": 560,
+ "magicPower": 1765,
+ "magicResist": 545,
+ "strength": 24
+ },
+ "items": [
+ 58,
+ 63,
+ 115,
+ 126,
+ 117,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 50,
+ "armor": 485,
+ "hp": 9570,
+ "intelligence": 348,
+ "magicPenetration": 720,
+ "magicPower": 2585,
+ "magicResist": 705,
+ "strength": 50
+ },
+ "items": [
+ 71,
+ 88,
+ 98,
+ 116,
+ 135,
+ 137
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 90,
+ "armor": 585,
+ "hp": 12930,
+ "intelligence": 466,
+ "magicPenetration": 1000,
+ "magicPower": 3417,
+ "magicResist": 865,
+ "strength": 90
+ },
+ "items": [
+ 117,
+ 126,
+ 126,
+ 132,
+ 171,
+ 174
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 116,
+ "armor": 585,
+ "hp": 16130,
+ "intelligence": 661,
+ "magicPenetration": 1760,
+ "magicPower": 4217,
+ "magicResist": 865,
+ "strength": 116
+ },
+ "items": [
+ 119,
+ 115,
+ 117,
+ 135,
+ 171,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 118,
+ "armor": 585,
+ "hp": 21890,
+ "intelligence": 832,
+ "magicPenetration": 1920,
+ "magicPower": 6169,
+ "magicResist": 1025,
+ "strength": 118
+ },
+ "items": [
+ 115,
+ 117,
+ 135,
+ 183,
+ 181,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 168,
+ "armor": 905,
+ "hp": 29250,
+ "intelligence": 1086,
+ "magicPenetration": 2400,
+ "magicPower": 7481,
+ "magicResist": 1185,
+ "strength": 168
+ },
+ "items": [
+ 126,
+ 135,
+ 184,
+ 181,
+ 180,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 170,
+ "armor": 905,
+ "hp": 43970,
+ "intelligence": 1209,
+ "magicPenetration": 2720,
+ "magicPower": 11289,
+ "magicResist": 1505,
+ "strength": 170
+ },
+ "items": [
+ 132,
+ 137,
+ 183,
+ 180,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 310,
+ "armor": 1225,
+ "hp": 51970,
+ "intelligence": 1858,
+ "magicPenetration": 2720,
+ "magicPower": 13209,
+ "magicResist": 1505,
+ "strength": 310
+ },
+ "items": [
+ 183,
+ 180,
+ 209,
+ 212,
+ 222,
+ 232
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 388,
+ "armor": 1545,
+ "hp": 62530,
+ "intelligence": 2554,
+ "magicPenetration": 3920,
+ "magicPower": 15705,
+ "magicResist": 1505,
+ "strength": 388
+ },
+ "items": [
+ 184,
+ 186,
+ 212,
+ 226,
+ 224,
+ 233
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 514,
+ "armor": 1545,
+ "hp": 79330,
+ "intelligence": 3133,
+ "magicPenetration": 4688,
+ "magicPower": 19977,
+ "magicResist": 1825,
+ "strength": 514
+ },
+ "items": [
+ 180,
+ 203,
+ 232,
+ 227,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 11,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1018,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 1009,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero18_judge",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0018",
+ "epicArtAsset": {
+ "name": "18_judge_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.12,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": -55,
+ "y": 63.80000000000001
+ }
+ ]
+ },
+ "role": "middle",
+ "obtainType": "shop:arena",
+ "characterType": null,
+ "silhouette": "tall",
+ "ultCinematic": null,
+ "roleExtended": [
+ "mage",
+ "support"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 8,
+ 5,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 370,
+ "1": 371,
+ "2": 372,
+ "3": 373,
+ "4": 374,
+ "7": 8256,
+ "8": 8257
+ }
+ },
+ "19": {
+ "id": 19,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 60,
+ "strength": 10
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 14,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 3,
+ 13,
+ 14,
+ 6
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 585,
+ "intelligence": 7,
+ "physicalAttack": 37,
+ "strength": 7
+ },
+ "items": [
+ 2,
+ 9,
+ 12,
+ 20,
+ 24,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 1285,
+ "intelligence": 10,
+ "magicResist": 25,
+ "physicalAttack": 95,
+ "strength": 10
+ },
+ "items": [
+ 12,
+ 20,
+ 24,
+ 25,
+ 25,
+ 51
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 50,
+ "hp": 1785,
+ "intelligence": 23,
+ "magicResist": 25,
+ "physicalAttack": 186,
+ "strength": 23
+ },
+ "items": [
+ 31,
+ 38,
+ 42,
+ 53,
+ 62,
+ 54
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 62,
+ "armorPenetration": 100,
+ "dodge": 45,
+ "hp": 2785,
+ "intelligence": 25,
+ "magicResist": 25,
+ "physicalAttack": 285,
+ "strength": 25
+ },
+ "items": [
+ 39,
+ 50,
+ 53,
+ 56,
+ 62,
+ 70
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 89,
+ "armorPenetration": 230,
+ "dodge": 75,
+ "hp": 3785,
+ "intelligence": 27,
+ "magicResist": 117,
+ "physicalAttack": 430,
+ "strength": 27
+ },
+ "items": [
+ 39,
+ 53,
+ 56,
+ 62,
+ 70,
+ 87
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 116,
+ "armorPenetration": 360,
+ "dodge": 105,
+ "hp": 4785,
+ "intelligence": 34,
+ "magicResist": 167,
+ "physicalAttack": 589,
+ "strength": 34
+ },
+ "items": [
+ 57,
+ 65,
+ 73,
+ 87,
+ 97,
+ 102
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 133,
+ "armorPenetration": 560,
+ "dodge": 189,
+ "hp": 5585,
+ "intelligence": 41,
+ "magicResist": 247,
+ "physicalAttack": 785,
+ "strength": 41
+ },
+ "items": [
+ 62,
+ 66,
+ 73,
+ 76,
+ 118,
+ 121
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 226,
+ "armorPenetration": 720,
+ "dodge": 243,
+ "hp": 6385,
+ "intelligence": 48,
+ "magicResist": 407,
+ "physicalAttack": 949,
+ "strength": 48
+ },
+ "items": [
+ 91,
+ 92,
+ 102,
+ 118,
+ 121,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 342,
+ "armorPenetration": 880,
+ "dodge": 303,
+ "hp": 8385,
+ "intelligence": 74,
+ "magicResist": 567,
+ "physicalAttack": 1192,
+ "strength": 74
+ },
+ "items": [
+ 121,
+ 102,
+ 114,
+ 118,
+ 125,
+ 138
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 490,
+ "armorPenetration": 1040,
+ "dodge": 363,
+ "hp": 9985,
+ "intelligence": 114,
+ "magicResist": 887,
+ "physicalAttack": 1732,
+ "strength": 114
+ },
+ "items": [
+ 102,
+ 114,
+ 134,
+ 133,
+ 172,
+ 173
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 655,
+ "armorPenetration": 1640,
+ "dodge": 423,
+ "hp": 11585,
+ "intelligence": 140,
+ "magicResist": 1143,
+ "physicalAttack": 2293,
+ "strength": 140
+ },
+ "items": [
+ 121,
+ 114,
+ 138,
+ 172,
+ 178,
+ 182
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 882,
+ "armorPenetration": 1960,
+ "dodge": 589,
+ "hp": 13185,
+ "intelligence": 180,
+ "magicResist": 1303,
+ "physicalAttack": 2829,
+ "strength": 180
+ },
+ "items": [
+ 120,
+ 118,
+ 178,
+ 168,
+ 184,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1166,
+ "armorPenetration": 2120,
+ "dodge": 755,
+ "hp": 17985,
+ "intelligence": 230,
+ "magicResist": 1623,
+ "physicalAttack": 3445,
+ "strength": 230
+ },
+ "items": [
+ 118,
+ 139,
+ 178,
+ 168,
+ 189,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1198,
+ "armorPenetration": 2280,
+ "dodge": 1149,
+ "hp": 29441,
+ "intelligence": 232,
+ "magicResist": 2032,
+ "physicalAttack": 5836,
+ "strength": 232
+ },
+ "items": [
+ 118,
+ 139,
+ 189,
+ 168,
+ 201,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1585,
+ "armorPenetration": 2440,
+ "dodge": 1377,
+ "hp": 39361,
+ "intelligence": 310,
+ "magicResist": 2441,
+ "physicalAttack": 7920,
+ "strength": 310
+ },
+ "items": [
+ 184,
+ 179,
+ 213,
+ 208,
+ 230,
+ 231
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 1942,
+ "armorPenetration": 3640,
+ "dodge": 1710,
+ "hp": 54017,
+ "intelligence": 388,
+ "magicResist": 2761,
+ "physicalAttack": 9891,
+ "strength": 388
+ },
+ "items": [
+ 213,
+ 184,
+ 189,
+ 223,
+ 228,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2517,
+ "armorPenetration": 4600,
+ "dodge": 1938,
+ "hp": 68737,
+ "intelligence": 466,
+ "magicResist": 4281,
+ "physicalAttack": 11555,
+ "strength": 466
+ },
+ "items": [
+ 179,
+ 184,
+ 225,
+ 230,
+ 239,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 12,
+ 10,
+ 3
+ ],
+ "artifacts": [
+ 1019,
+ 2004,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 2009,
+ "scale": null,
+ "type": "hero",
+ "asset": "archer",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0019",
+ "epicArtAsset": {
+ "name": "19_dark_star_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 6,
+ "y": 73
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "19_DarkStar"
+ },
+ "role": "back",
+ "obtainType": "shop:arena",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "control",
+ "ranged_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 8,
+ 6,
+ 1
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 91,
+ "1": 92,
+ "2": 93,
+ "3": 94,
+ "4": 95,
+ "7": 8242,
+ "8": 8243
+ }
+ },
+ "20": {
+ "id": 20,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 60,
+ "strength": 10
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 14,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 6,
+ 8,
+ 9,
+ 14
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 7,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "strength": 7
+ },
+ "items": [
+ 8,
+ 9,
+ 20,
+ 20,
+ 23,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 33,
+ "armor": 50,
+ "hp": 200,
+ "intelligence": 9,
+ "magicResist": 50,
+ "physicalAttack": 120,
+ "strength": 9
+ },
+ "items": [
+ 12,
+ 20,
+ 27,
+ 28,
+ 38,
+ 38
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 67,
+ "armor": 100,
+ "hp": 200,
+ "intelligence": 12,
+ "magicResist": 100,
+ "physicalAttack": 211,
+ "strength": 12
+ },
+ "items": [
+ 31,
+ 55,
+ 42,
+ 53,
+ 61,
+ 57
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 69,
+ "armor": 100,
+ "armorPenetration": 100,
+ "hp": 700,
+ "intelligence": 14,
+ "magicResist": 100,
+ "physicalAttack": 380,
+ "physicalCritChance": 45,
+ "strength": 14
+ },
+ "items": [
+ 39,
+ 50,
+ 53,
+ 57,
+ 61,
+ 76
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 111,
+ "armor": 100,
+ "armorPenetration": 150,
+ "hp": 700,
+ "intelligence": 21,
+ "magicResist": 192,
+ "physicalAttack": 539,
+ "physicalCritChance": 75,
+ "strength": 21
+ },
+ "items": [
+ 24,
+ 44,
+ 53,
+ 70,
+ 76,
+ 89
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 144,
+ "armor": 150,
+ "armorPenetration": 280,
+ "hp": 1200,
+ "intelligence": 28,
+ "magicResist": 242,
+ "physicalAttack": 684,
+ "physicalCritChance": 105,
+ "strength": 28
+ },
+ "items": [
+ 65,
+ 66,
+ 72,
+ 76,
+ 89,
+ 97
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 193,
+ "armor": 150,
+ "armorPenetration": 480,
+ "hp": 1200,
+ "intelligence": 35,
+ "magicResist": 322,
+ "physicalAttack": 908,
+ "physicalCritChance": 159,
+ "strength": 35
+ },
+ "items": [
+ 118,
+ 64,
+ 72,
+ 76,
+ 101,
+ 122
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 240,
+ "armor": 310,
+ "armorPenetration": 640,
+ "hp": 2000,
+ "intelligence": 42,
+ "magicResist": 402,
+ "physicalAttack": 1180,
+ "physicalCritChance": 243,
+ "strength": 42
+ },
+ "items": [
+ 87,
+ 72,
+ 89,
+ 118,
+ 120,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 387,
+ "armor": 310,
+ "armorPenetration": 800,
+ "hp": 2000,
+ "intelligence": 73,
+ "magicResist": 402,
+ "physicalAttack": 1578,
+ "physicalCritChance": 297,
+ "strength": 73
+ },
+ "items": [
+ 76,
+ 101,
+ 114,
+ 118,
+ 114,
+ 138
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 520,
+ "armor": 310,
+ "armorPenetration": 960,
+ "hp": 5200,
+ "intelligence": 118,
+ "magicResist": 402,
+ "physicalAttack": 2118,
+ "physicalCritChance": 357,
+ "strength": 118
+ },
+ "items": [
+ 118,
+ 125,
+ 114,
+ 133,
+ 172,
+ 177
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 715,
+ "armor": 310,
+ "armorPenetration": 1120,
+ "hp": 6800,
+ "intelligence": 144,
+ "magicResist": 562,
+ "physicalAttack": 2658,
+ "physicalCritChance": 523,
+ "strength": 144
+ },
+ "items": [
+ 114,
+ 120,
+ 138,
+ 172,
+ 177,
+ 182
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 942,
+ "armor": 310,
+ "armorPenetration": 1440,
+ "hp": 8400,
+ "intelligence": 184,
+ "magicResist": 562,
+ "physicalAttack": 3302,
+ "physicalCritChance": 689,
+ "strength": 184
+ },
+ "items": [
+ 114,
+ 120,
+ 177,
+ 183,
+ 182,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1196,
+ "armor": 630,
+ "armorPenetration": 1760,
+ "hp": 14800,
+ "intelligence": 234,
+ "magicResist": 562,
+ "physicalAttack": 3946,
+ "physicalCritChance": 855,
+ "strength": 234
+ },
+ "items": [
+ 114,
+ 118,
+ 179,
+ 182,
+ 188,
+ 207
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1228,
+ "armor": 630,
+ "armorPenetration": 2240,
+ "hp": 19600,
+ "intelligence": 236,
+ "magicResist": 562,
+ "physicalAttack": 6324,
+ "physicalCritChance": 1477,
+ "strength": 236
+ },
+ "items": [
+ 138,
+ 139,
+ 173,
+ 177,
+ 202,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1671,
+ "armor": 630,
+ "armorPenetration": 2840,
+ "hp": 19600,
+ "intelligence": 352,
+ "magicResist": 971,
+ "physicalAttack": 7524,
+ "physicalCritChance": 1908,
+ "strength": 352
+ },
+ "items": [
+ 207,
+ 184,
+ 213,
+ 179,
+ 225,
+ 231
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2028,
+ "armor": 630,
+ "armorPenetration": 4040,
+ "hp": 27600,
+ "intelligence": 430,
+ "magicResist": 1291,
+ "physicalAttack": 9738,
+ "physicalCritChance": 2302,
+ "strength": 430
+ },
+ "items": [
+ 182,
+ 202,
+ 213,
+ 223,
+ 225,
+ 236
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2603,
+ "armor": 630,
+ "armorPenetration": 4360,
+ "hp": 32720,
+ "intelligence": 508,
+ "magicResist": 1291,
+ "physicalAttack": 12530,
+ "physicalCritChance": 3099,
+ "strength": 508
+ },
+ "items": [
+ 187,
+ 179,
+ 231,
+ 225,
+ 237,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 12,
+ 9,
+ 3
+ ],
+ "artifacts": [
+ 1020,
+ 2004,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 2021,
+ "scale": null,
+ "type": "hero",
+ "asset": "arbalester",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0020",
+ "epicArtAsset": {
+ "name": "20_artemis_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 12,
+ "y": 42
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "Artemis"
+ },
+ "role": "back",
+ "obtainType": null,
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero20_battle_animation"
+ },
+ "roleExtended": [
+ "ranged_dps"
+ ],
+ "sfxAsset": "hero20_sfx",
+ "musicAsset": null,
+ "perk": [
+ 6,
+ 1
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 351,
+ "1": 352,
+ "2": 353,
+ "3": 354,
+ "4": 355,
+ "7": 8248,
+ "8": 8249
+ }
+ },
+ "21": {
+ "id": 21,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 60,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 20,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 16
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 7,
+ 8,
+ 9,
+ 13,
+ 14,
+ 16
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 9,
+ "magicPower": 25,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 2
+ },
+ "items": [
+ 13,
+ 14,
+ 11,
+ 19,
+ 27,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 75,
+ "hp": 770,
+ "intelligence": 23,
+ "magicPower": 75,
+ "magicResist": 75,
+ "physicalAttack": 50,
+ "strength": 5
+ },
+ "items": [
+ 19,
+ 19,
+ 26,
+ 27,
+ 28,
+ 42
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 125,
+ "hp": 1270,
+ "intelligence": 39,
+ "magicPower": 225,
+ "magicResist": 125,
+ "physicalAttack": 83,
+ "strength": 7
+ },
+ "items": [
+ 24,
+ 34,
+ 45,
+ 48,
+ 58,
+ 59
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 225,
+ "hp": 1770,
+ "intelligence": 68,
+ "magicPower": 459,
+ "magicResist": 217,
+ "physicalAttack": 83,
+ "strength": 14
+ },
+ "items": [
+ 34,
+ 44,
+ 24,
+ 48,
+ 65,
+ 68
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 355,
+ "hp": 2270,
+ "intelligence": 97,
+ "magicPower": 623,
+ "magicResist": 389,
+ "physicalAttack": 139,
+ "strength": 21
+ },
+ "items": [
+ 40,
+ 58,
+ 56,
+ 57,
+ 68,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 435,
+ "hp": 3270,
+ "intelligence": 124,
+ "magicPower": 853,
+ "magicResist": 489,
+ "physicalAttack": 209,
+ "strength": 28
+ },
+ "items": [
+ 63,
+ 65,
+ 75,
+ 88,
+ 99,
+ 100
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 35,
+ "armor": 735,
+ "hp": 4870,
+ "intelligence": 141,
+ "magicPower": 1013,
+ "magicResist": 769,
+ "physicalAttack": 265,
+ "strength": 35
+ },
+ "items": [
+ 57,
+ 86,
+ 88,
+ 99,
+ 100,
+ 126
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 42,
+ "armor": 1035,
+ "hp": 7270,
+ "intelligence": 158,
+ "magicPower": 1413,
+ "magicResist": 1069,
+ "physicalAttack": 335,
+ "strength": 42
+ },
+ "items": [
+ 86,
+ 88,
+ 93,
+ 115,
+ 122,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 49,
+ "armor": 1295,
+ "hp": 10630,
+ "intelligence": 175,
+ "magicPower": 2365,
+ "magicResist": 1329,
+ "physicalAttack": 443,
+ "strength": 49
+ },
+ "items": [
+ 75,
+ 99,
+ 115,
+ 116,
+ 122,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 56,
+ "armor": 1655,
+ "hp": 14726,
+ "intelligence": 222,
+ "magicPower": 3504,
+ "magicResist": 1649,
+ "physicalAttack": 551,
+ "strength": 56
+ },
+ "items": [
+ 115,
+ 116,
+ 125,
+ 135,
+ 171,
+ 175
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 58,
+ "armor": 2255,
+ "hp": 17286,
+ "intelligence": 363,
+ "magicPower": 4336,
+ "magicResist": 2129,
+ "physicalAttack": 767,
+ "strength": 58
+ },
+ "items": [
+ 116,
+ 125,
+ 135,
+ 169,
+ 171,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 60,
+ "armor": 2575,
+ "hp": 24646,
+ "intelligence": 504,
+ "magicPower": 5608,
+ "magicResist": 2449,
+ "physicalAttack": 983,
+ "strength": 60
+ },
+ "items": [
+ 115,
+ 132,
+ 169,
+ 184,
+ 183,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 134,
+ "armor": 2895,
+ "hp": 34246,
+ "intelligence": 782,
+ "magicPower": 6368,
+ "magicResist": 2929,
+ "physicalAttack": 983,
+ "strength": 134
+ },
+ "items": [
+ 116,
+ 135,
+ 169,
+ 179,
+ 180,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 136,
+ "armor": 2895,
+ "hp": 45766,
+ "intelligence": 935,
+ "magicPower": 10136,
+ "magicResist": 3089,
+ "physicalAttack": 1623,
+ "strength": 136
+ },
+ "items": [
+ 137,
+ 139,
+ 175,
+ 183,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 252,
+ "armor": 3815,
+ "hp": 50566,
+ "intelligence": 1530,
+ "magicPower": 11096,
+ "magicResist": 3498,
+ "physicalAttack": 2175,
+ "strength": 252
+ },
+ "items": [
+ 203,
+ 210,
+ 212,
+ 169,
+ 227,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 330,
+ "armor": 5015,
+ "hp": 53126,
+ "intelligence": 2039,
+ "magicPower": 13904,
+ "magicResist": 4698,
+ "physicalAttack": 2175,
+ "strength": 504
+ },
+ "items": [
+ 179,
+ 180,
+ 212,
+ 227,
+ 224,
+ 238
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 408,
+ "armor": 6215,
+ "hp": 71526,
+ "intelligence": 2396,
+ "magicPower": 16784,
+ "magicResist": 6618,
+ "physicalAttack": 2815,
+ "strength": 582
+ },
+ "items": [
+ 203,
+ 184,
+ 224,
+ 226,
+ 237,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 7,
+ 6,
+ 2
+ ],
+ "artifacts": [
+ 1021,
+ 2006,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 21,
+ "scale": null,
+ "type": "hero",
+ "asset": "paladin",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0021",
+ "epicArtAsset": {
+ "name": "21_markus_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": -28.799999999999955,
+ "y": 11.800000000000011
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": "shop:grandArena",
+ "characterType": "healer",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "healer",
+ "support"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 9,
+ 5,
+ 2,
+ 16
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 294,
+ "1": 344,
+ "2": 296,
+ "3": 297,
+ "4": 298
+ }
+ },
+ "22": {
+ "id": 22,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 14,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 24,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 4,
+ 6,
+ 7,
+ 9,
+ 2,
+ 16
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 200,
+ "intelligence": 19,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 4,
+ 13,
+ 16,
+ 19,
+ 26,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 9,
+ "hp": 585,
+ "intelligence": 40,
+ "magicPower": 125,
+ "magicResist": 75,
+ "strength": 9
+ },
+ "items": [
+ 13,
+ 19,
+ 22,
+ 26,
+ 28,
+ 34
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 970,
+ "intelligence": 71,
+ "magicPower": 225,
+ "magicResist": 125,
+ "strength": 16
+ },
+ "items": [
+ 40,
+ 46,
+ 32,
+ 52,
+ 58,
+ 60
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 1470,
+ "intelligence": 93,
+ "magicPenetration": 100,
+ "magicPower": 425,
+ "magicResist": 225,
+ "strength": 18
+ },
+ "items": [
+ 45,
+ 52,
+ 52,
+ 58,
+ 60,
+ 75
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 25,
+ "hp": 1470,
+ "intelligence": 130,
+ "magicPenetration": 200,
+ "magicPower": 575,
+ "magicResist": 375,
+ "strength": 25
+ },
+ "items": [
+ 45,
+ 52,
+ 56,
+ 60,
+ 71,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 2470,
+ "intelligence": 157,
+ "magicPenetration": 330,
+ "magicPower": 705,
+ "magicResist": 625,
+ "strength": 32
+ },
+ "items": [
+ 63,
+ 64,
+ 75,
+ 86,
+ 93,
+ 98
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 44,
+ "hp": 4070,
+ "intelligence": 189,
+ "magicPenetration": 530,
+ "magicPower": 985,
+ "magicResist": 805,
+ "strength": 44
+ },
+ "items": [
+ 63,
+ 75,
+ 71,
+ 86,
+ 98,
+ 116
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 56,
+ "hp": 4870,
+ "intelligence": 251,
+ "magicPenetration": 810,
+ "magicPower": 1305,
+ "magicResist": 1065,
+ "strength": 56
+ },
+ "items": [
+ 71,
+ 126,
+ 100,
+ 93,
+ 117,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 82,
+ "hp": 6470,
+ "intelligence": 337,
+ "magicPenetration": 1050,
+ "magicPower": 2065,
+ "magicResist": 1265,
+ "strength": 82
+ },
+ "items": [
+ 71,
+ 115,
+ 116,
+ 117,
+ 119,
+ 137
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 122,
+ "hp": 6470,
+ "intelligence": 515,
+ "magicPenetration": 1290,
+ "magicPower": 2785,
+ "magicResist": 1585,
+ "strength": 122
+ },
+ "items": [
+ 115,
+ 116,
+ 117,
+ 135,
+ 171,
+ 174
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 124,
+ "hp": 9030,
+ "intelligence": 686,
+ "magicPenetration": 2050,
+ "magicPower": 3777,
+ "magicResist": 1905,
+ "strength": 124
+ },
+ "items": [
+ 116,
+ 117,
+ 135,
+ 171,
+ 176,
+ 181
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 126,
+ "hp": 11590,
+ "intelligence": 857,
+ "magicPenetration": 2530,
+ "magicPower": 5089,
+ "magicResist": 2665,
+ "strength": 126
+ },
+ "items": [
+ 115,
+ 135,
+ 169,
+ 174,
+ 181,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 176,
+ "hp": 14150,
+ "intelligence": 1081,
+ "magicPenetration": 3450,
+ "magicPower": 6841,
+ "magicResist": 2825,
+ "strength": 176
+ },
+ "items": [
+ 115,
+ 117,
+ 169,
+ 180,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 178,
+ "hp": 19910,
+ "intelligence": 1386,
+ "magicPenetration": 3610,
+ "magicPower": 11217,
+ "magicResist": 2985,
+ "strength": 178
+ },
+ "items": [
+ 135,
+ 140,
+ 184,
+ 181,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 256,
+ "hp": 31366,
+ "intelligence": 1895,
+ "magicPenetration": 3930,
+ "magicPower": 13988,
+ "magicResist": 3305,
+ "strength": 256
+ },
+ "items": [
+ 209,
+ 180,
+ 212,
+ 184,
+ 232,
+ 226
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 334,
+ "hp": 41926,
+ "intelligence": 2373,
+ "magicPenetration": 5130,
+ "magicPower": 17684,
+ "magicResist": 3625,
+ "strength": 334
+ },
+ "items": [
+ 203,
+ 184,
+ 212,
+ 232,
+ 228,
+ 238
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 412,
+ "hp": 46726,
+ "intelligence": 2882,
+ "magicPenetration": 6330,
+ "magicPower": 20564,
+ "magicResist": 7065,
+ "strength": 412
+ },
+ "items": [
+ 186,
+ 184,
+ 232,
+ 226,
+ 238,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 11,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1022,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2024,
+ "scale": null,
+ "type": "hero",
+ "asset": "jester",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0022",
+ "epicArtAsset": {
+ "name": "22_peppy_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": -10,
+ "y": 63
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": null,
+ "characterType": "cutie",
+ "silhouette": "tiny",
+ "ultCinematic": null,
+ "roleExtended": [
+ "mage",
+ "support"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 5,
+ 1,
+ 20
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 415,
+ "1": 416,
+ "2": 417,
+ "3": 418,
+ "4": 419,
+ "7": 8260,
+ "8": 8261
+ }
+ },
+ "23": {
+ "id": 23,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 17,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 24,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 4,
+ 6,
+ 7,
+ 8,
+ 9
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 12,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 8,
+ 9,
+ 11,
+ 19,
+ 26,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 50,
+ "hp": 200,
+ "intelligence": 26,
+ "magicPower": 175,
+ "magicResist": 50,
+ "strength": 10
+ },
+ "items": [
+ 11,
+ 19,
+ 27,
+ 28,
+ 45,
+ 46
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 100,
+ "hp": 700,
+ "intelligence": 40,
+ "magicPower": 325,
+ "magicResist": 150,
+ "strength": 13
+ },
+ "items": [
+ 41,
+ 45,
+ 46,
+ 48,
+ 56,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 150,
+ "hp": 2200,
+ "intelligence": 67,
+ "magicPower": 609,
+ "magicResist": 242,
+ "strength": 15
+ },
+ "items": [
+ 40,
+ 46,
+ 48,
+ 58,
+ 60,
+ 68
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 230,
+ "hp": 2700,
+ "intelligence": 94,
+ "magicPower": 973,
+ "magicResist": 384,
+ "strength": 17
+ },
+ "items": [
+ 41,
+ 45,
+ 48,
+ 56,
+ 67,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 380,
+ "hp": 4500,
+ "intelligence": 121,
+ "magicPower": 1267,
+ "magicResist": 556,
+ "strength": 19
+ },
+ "items": [
+ 56,
+ 68,
+ 75,
+ 88,
+ 93,
+ 100
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 560,
+ "hp": 6300,
+ "intelligence": 138,
+ "magicPower": 1627,
+ "magicResist": 756,
+ "strength": 26
+ },
+ "items": [
+ 68,
+ 86,
+ 88,
+ 91,
+ 100,
+ 119
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 33,
+ "armor": 740,
+ "hp": 9100,
+ "intelligence": 185,
+ "magicPower": 1947,
+ "magicResist": 1056,
+ "strength": 33
+ },
+ "items": [
+ 64,
+ 86,
+ 99,
+ 115,
+ 119,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 40,
+ "armor": 940,
+ "hp": 12460,
+ "intelligence": 232,
+ "magicPower": 2779,
+ "magicResist": 1396,
+ "strength": 40
+ },
+ "items": [
+ 86,
+ 99,
+ 100,
+ 115,
+ 116,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 47,
+ "armor": 1140,
+ "hp": 16556,
+ "intelligence": 279,
+ "magicPower": 3918,
+ "magicResist": 2016,
+ "strength": 47
+ },
+ "items": [
+ 115,
+ 116,
+ 132,
+ 135,
+ 171,
+ 167
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 73,
+ "armor": 1140,
+ "hp": 25116,
+ "intelligence": 474,
+ "magicPower": 4750,
+ "magicResist": 2336,
+ "strength": 73
+ },
+ "items": [
+ 119,
+ 132,
+ 135,
+ 171,
+ 175,
+ 184
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 99,
+ "armor": 1740,
+ "hp": 32476,
+ "intelligence": 669,
+ "magicPower": 5422,
+ "magicResist": 2656,
+ "strength": 99
+ },
+ "items": [
+ 115,
+ 137,
+ 169,
+ 176,
+ 184,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 187,
+ "armor": 1740,
+ "hp": 37276,
+ "intelligence": 979,
+ "magicPower": 6182,
+ "magicResist": 3736,
+ "strength": 187
+ },
+ "items": [
+ 126,
+ 135,
+ 169,
+ 184,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 189,
+ "armor": 1740,
+ "hp": 48796,
+ "intelligence": 1254,
+ "magicPower": 10110,
+ "magicResist": 4056,
+ "strength": 189
+ },
+ "items": [
+ 137,
+ 140,
+ 176,
+ 184,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 305,
+ "armor": 1740,
+ "hp": 57692,
+ "intelligence": 1849,
+ "magicPower": 11889,
+ "magicResist": 4976,
+ "strength": 305
+ },
+ "items": [
+ 209,
+ 180,
+ 212,
+ 184,
+ 227,
+ 226
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 383,
+ "armor": 2940,
+ "hp": 68252,
+ "intelligence": 2327,
+ "magicPower": 15585,
+ "magicResist": 5296,
+ "strength": 383
+ },
+ "items": [
+ 203,
+ 184,
+ 212,
+ 227,
+ 224,
+ 238
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 461,
+ "armor": 4140,
+ "hp": 85052,
+ "intelligence": 2836,
+ "magicPower": 18465,
+ "magicResist": 7536,
+ "strength": 461
+ },
+ "items": [
+ 186,
+ 184,
+ 224,
+ 226,
+ 238,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 7,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1023,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2019,
+ "scale": null,
+ "type": "hero",
+ "asset": "tailed",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0023",
+ "epicArtAsset": {
+ "name": "23_lian_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 33.400000000000034,
+ "y": 7.5999999999999375
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "23_Lian"
+ },
+ "role": "back",
+ "obtainType": "shop:grandArena",
+ "characterType": "cutie",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "control",
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 8,
+ 7,
+ 1
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 279,
+ 280,
+ 281,
+ 282,
+ 283
+ ]
+ },
+ "24": {
+ "id": 24,
+ "baseStats": {
+ "agility": 15,
+ "armor": 1000,
+ "hp": 500,
+ "intelligence": 10,
+ "magicResist": 300,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 24
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 5,
+ 8,
+ 13,
+ 14,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 2,
+ "physicalAttack": 37,
+ "strength": 14
+ },
+ "items": [
+ 8,
+ 14,
+ 10,
+ 18,
+ 21,
+ 27
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 100,
+ "hp": 770,
+ "intelligence": 5,
+ "physicalAttack": 62,
+ "strength": 38
+ },
+ "items": [
+ 8,
+ 18,
+ 21,
+ 25,
+ 36,
+ 43
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 175,
+ "hp": 1655,
+ "intelligence": 7,
+ "physicalAttack": 128,
+ "strength": 67
+ },
+ "items": [
+ 21,
+ 37,
+ 44,
+ 47,
+ 56,
+ 57
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 317,
+ "hp": 3075,
+ "intelligence": 9,
+ "magicResist": 50,
+ "physicalAttack": 226,
+ "strength": 104
+ },
+ "items": [
+ 37,
+ 44,
+ 47,
+ 56,
+ 57,
+ 77
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 459,
+ "hp": 4495,
+ "intelligence": 11,
+ "lifesteal": 5,
+ "magicResist": 100,
+ "physicalAttack": 324,
+ "strength": 131
+ },
+ "items": [
+ 33,
+ 47,
+ 56,
+ 59,
+ 77,
+ 90
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 681,
+ "hp": 5915,
+ "intelligence": 18,
+ "lifesteal": 10,
+ "magicResist": 100,
+ "physicalAttack": 422,
+ "strength": 176
+ },
+ "items": [
+ 57,
+ 65,
+ 85,
+ 77,
+ 94,
+ 99
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 25,
+ "armor": 881,
+ "hp": 6915,
+ "intelligence": 25,
+ "lifesteal": 15,
+ "magicResist": 180,
+ "physicalAttack": 548,
+ "strength": 231
+ },
+ "items": [
+ 90,
+ 92,
+ 94,
+ 91,
+ 99,
+ 124
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 1161,
+ "hp": 8915,
+ "intelligence": 27,
+ "lifesteal": 25,
+ "magicResist": 180,
+ "physicalAttack": 753,
+ "strength": 287
+ },
+ "items": [
+ 90,
+ 91,
+ 122,
+ 123,
+ 124,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 53,
+ "armor": 1401,
+ "hp": 12515,
+ "intelligence": 53,
+ "lifesteal": 35,
+ "magicResist": 180,
+ "physicalAttack": 931,
+ "strength": 389
+ },
+ "items": [
+ 114,
+ 122,
+ 123,
+ 124,
+ 125,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 93,
+ "armor": 1561,
+ "hp": 15715,
+ "intelligence": 93,
+ "lifesteal": 45,
+ "magicResist": 340,
+ "physicalAttack": 1471,
+ "strength": 507
+ },
+ "items": [
+ 136,
+ 123,
+ 124,
+ 125,
+ 170,
+ 175
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 133,
+ "armor": 2161,
+ "hp": 17315,
+ "intelligence": 133,
+ "lifesteal": 55,
+ "magicResist": 500,
+ "physicalAttack": 1687,
+ "strength": 734
+ },
+ "items": [
+ 114,
+ 125,
+ 136,
+ 168,
+ 170,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 173,
+ "armor": 2481,
+ "hp": 23715,
+ "intelligence": 173,
+ "lifesteal": 55,
+ "magicResist": 660,
+ "physicalAttack": 2519,
+ "strength": 931
+ },
+ "items": [
+ 123,
+ 122,
+ 167,
+ 168,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 223,
+ "armor": 2641,
+ "hp": 34515,
+ "intelligence": 223,
+ "lifesteal": 55,
+ "magicResist": 660,
+ "physicalAttack": 3667,
+ "strength": 1185
+ },
+ "items": [
+ 114,
+ 134,
+ 184,
+ 183,
+ 179,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 225,
+ "armor": 2961,
+ "hp": 55571,
+ "intelligence": 225,
+ "lifesteal": 55,
+ "magicResist": 1236,
+ "physicalAttack": 6199,
+ "strength": 1187
+ },
+ "items": [
+ 136,
+ 139,
+ 168,
+ 168,
+ 201,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 341,
+ "armor": 2961,
+ "hp": 60691,
+ "intelligence": 341,
+ "lifesteal": 55,
+ "magicResist": 1645,
+ "physicalAttack": 8575,
+ "strength": 1630
+ },
+ "items": [
+ 208,
+ 179,
+ 211,
+ 184,
+ 221,
+ 225
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 419,
+ "armor": 2961,
+ "hp": 75347,
+ "intelligence": 419,
+ "lifesteal": 55,
+ "magicResist": 1965,
+ "physicalAttack": 11346,
+ "strength": 2205
+ },
+ "items": [
+ 208,
+ 184,
+ 185,
+ 221,
+ 224,
+ 237
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 469,
+ "armor": 4881,
+ "hp": 103923,
+ "intelligence": 469,
+ "lifesteal": 55,
+ "magicResist": 2285,
+ "physicalAttack": 13701,
+ "strength": 2647
+ },
+ "items": [
+ 185,
+ 184,
+ 221,
+ 225,
+ 237,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 7,
+ 4,
+ 8,
+ 1
+ ],
+ "artifacts": [
+ 1024,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 3,
+ "scale": null,
+ "type": "hero",
+ "asset": "butcher",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0024",
+ "epicArtAsset": {
+ "name": "24_cleaver_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.16,
+ 1.15
+ ],
+ "screen": "obtain",
+ "x": 63,
+ "y": 0
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "24_cleaver",
+ "transform": [
+ {
+ "scale": [
+ -1.16,
+ 1.14
+ ],
+ "screen": "obtain",
+ "x": 78.39999999999998,
+ "y": 17.60000000000008
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": "chest:town",
+ "characterType": "warrior",
+ "silhouette": "wide",
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_tank",
+ "control"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 4,
+ 8,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 119,
+ "1": 120,
+ "2": 121,
+ "3": 122,
+ "4": 123,
+ "7": 8240,
+ "8": 8241
+ }
+ },
+ "25": {
+ "id": 25,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 17
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 3,
+ 8,
+ 9,
+ 13,
+ 14,
+ 17
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 2
+ },
+ "items": [
+ 8,
+ 9,
+ 13,
+ 20,
+ 23,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 33,
+ "armor": 50,
+ "hp": 770,
+ "intelligence": 4,
+ "magicResist": 50,
+ "physicalAttack": 83,
+ "strength": 4
+ },
+ "items": [
+ 20,
+ 27,
+ 28,
+ 35,
+ 38,
+ 42
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 64,
+ "armor": 100,
+ "hp": 1270,
+ "intelligence": 11,
+ "magicResist": 100,
+ "physicalAttack": 174,
+ "strength": 11
+ },
+ "items": [
+ 38,
+ 50,
+ 53,
+ 55,
+ 57,
+ 61
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 91,
+ "armor": 100,
+ "armorPenetration": 50,
+ "hp": 1270,
+ "intelligence": 13,
+ "magicResist": 142,
+ "physicalAttack": 399,
+ "physicalCritChance": 45,
+ "strength": 13
+ },
+ "items": [
+ 35,
+ 42,
+ 50,
+ 57,
+ 61,
+ 70
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 120,
+ "armor": 100,
+ "armorPenetration": 130,
+ "hp": 1770,
+ "intelligence": 20,
+ "magicResist": 184,
+ "physicalAttack": 614,
+ "physicalCritChance": 75,
+ "strength": 20
+ },
+ "items": [
+ 50,
+ 56,
+ 57,
+ 61,
+ 70,
+ 89
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 153,
+ "armor": 100,
+ "armorPenetration": 210,
+ "hp": 2770,
+ "intelligence": 22,
+ "magicResist": 226,
+ "physicalAttack": 852,
+ "physicalCritChance": 135,
+ "strength": 22
+ },
+ "items": [
+ 56,
+ 70,
+ 72,
+ 89,
+ 96,
+ 97
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 209,
+ "armor": 100,
+ "armorPenetration": 490,
+ "hp": 3770,
+ "intelligence": 24,
+ "magicResist": 226,
+ "physicalAttack": 1020,
+ "physicalCritChance": 189,
+ "strength": 24
+ },
+ "items": [
+ 76,
+ 66,
+ 72,
+ 97,
+ 101,
+ 118
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 272,
+ "armor": 100,
+ "armorPenetration": 850,
+ "hp": 3770,
+ "intelligence": 31,
+ "magicResist": 226,
+ "physicalAttack": 1240,
+ "physicalCritChance": 273,
+ "strength": 31
+ },
+ "items": [
+ 64,
+ 87,
+ 118,
+ 101,
+ 120,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 403,
+ "armor": 100,
+ "armorPenetration": 1010,
+ "hp": 4570,
+ "intelligence": 62,
+ "magicResist": 306,
+ "physicalAttack": 1526,
+ "physicalCritChance": 333,
+ "strength": 62
+ },
+ "items": [
+ 89,
+ 101,
+ 114,
+ 118,
+ 120,
+ 138
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 567,
+ "armor": 100,
+ "armorPenetration": 1170,
+ "hp": 6170,
+ "intelligence": 102,
+ "magicResist": 306,
+ "physicalAttack": 2014,
+ "physicalCritChance": 423,
+ "strength": 102
+ },
+ "items": [
+ 97,
+ 118,
+ 125,
+ 138,
+ 172,
+ 177
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 794,
+ "armor": 100,
+ "armorPenetration": 1530,
+ "hp": 6170,
+ "intelligence": 142,
+ "magicResist": 466,
+ "physicalAttack": 2338,
+ "physicalCritChance": 589,
+ "strength": 142
+ },
+ "items": [
+ 114,
+ 133,
+ 91,
+ 172,
+ 173,
+ 188
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 959,
+ "armor": 100,
+ "armorPenetration": 2130,
+ "hp": 9770,
+ "intelligence": 168,
+ "magicResist": 466,
+ "physicalAttack": 2874,
+ "physicalCritChance": 817,
+ "strength": 168
+ },
+ "items": [
+ 120,
+ 125,
+ 177,
+ 182,
+ 183,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1213,
+ "armor": 420,
+ "armorPenetration": 2450,
+ "hp": 14570,
+ "intelligence": 218,
+ "magicResist": 626,
+ "physicalAttack": 3518,
+ "physicalCritChance": 983,
+ "strength": 218
+ },
+ "items": [
+ 133,
+ 118,
+ 173,
+ 188,
+ 179,
+ 207
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1299,
+ "armor": 420,
+ "armorPenetration": 3210,
+ "hp": 17770,
+ "intelligence": 244,
+ "magicResist": 626,
+ "physicalAttack": 5360,
+ "physicalCritChance": 1605,
+ "strength": 244
+ },
+ "items": [
+ 139,
+ 138,
+ 173,
+ 182,
+ 201,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1742,
+ "armor": 420,
+ "armorPenetration": 4130,
+ "hp": 22890,
+ "intelligence": 360,
+ "magicResist": 1035,
+ "physicalAttack": 7256,
+ "physicalCritChance": 1605,
+ "strength": 360
+ },
+ "items": [
+ 207,
+ 183,
+ 213,
+ 188,
+ 231,
+ 225
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2099,
+ "armor": 740,
+ "armorPenetration": 5330,
+ "hp": 27690,
+ "intelligence": 438,
+ "magicResist": 1035,
+ "physicalAttack": 9150,
+ "physicalCritChance": 2227,
+ "strength": 438
+ },
+ "items": [
+ 208,
+ 184,
+ 187,
+ 229,
+ 223,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2541,
+ "armor": 740,
+ "armorPenetration": 6290,
+ "hp": 44266,
+ "intelligence": 488,
+ "magicResist": 1355,
+ "physicalAttack": 12145,
+ "physicalCritChance": 2560,
+ "strength": 488
+ },
+ "items": [
+ 187,
+ 184,
+ 229,
+ 223,
+ 239,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 12,
+ 9,
+ 3
+ ],
+ "artifacts": [
+ 1025,
+ 2004,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 19,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero25",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0025",
+ "epicArtAsset": {
+ "name": "25_ishmael_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": -27,
+ "y": 76
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "25_Ishmael",
+ "transform": [
+ {
+ "scale": [
+ -1,
+ 1
+ ],
+ "screen": "obtain",
+ "x": 0,
+ "y": 0
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": "shop:grandArena",
+ "characterType": "demon",
+ "silhouette": "wide",
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 10,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 390,
+ "1": 391,
+ "2": 392,
+ "3": 393,
+ "4": 394,
+ "7": 8259,
+ "8": 8258
+ }
+ },
+ "26": {
+ "id": 26,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 17,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 23,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 7,
+ 2,
+ 6,
+ 7,
+ 8,
+ 9
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 7,
+ "magicPower": 50,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 8,
+ 22,
+ 11,
+ 19,
+ 24,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 50,
+ "hp": 700,
+ "intelligence": 31,
+ "magicPower": 150,
+ "magicResist": 25,
+ "strength": 10
+ },
+ "items": [
+ 11,
+ 19,
+ 24,
+ 28,
+ 45,
+ 46
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 50,
+ "hp": 1700,
+ "intelligence": 45,
+ "magicPower": 300,
+ "magicResist": 125,
+ "strength": 13
+ },
+ "items": [
+ 28,
+ 52,
+ 46,
+ 48,
+ 58,
+ 41
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 100,
+ "hp": 2200,
+ "intelligence": 82,
+ "magicPenetration": 50,
+ "magicPower": 534,
+ "magicResist": 217,
+ "strength": 15
+ },
+ "items": [
+ 41,
+ 45,
+ 46,
+ 56,
+ 60,
+ 71
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 150,
+ "hp": 3700,
+ "intelligence": 94,
+ "magicPenetration": 130,
+ "magicPower": 714,
+ "magicResist": 367,
+ "strength": 17
+ },
+ "items": [
+ 40,
+ 46,
+ 58,
+ 60,
+ 71,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 250,
+ "hp": 5000,
+ "intelligence": 106,
+ "magicPenetration": 210,
+ "magicPower": 1074,
+ "magicResist": 467,
+ "strength": 19
+ },
+ "items": [
+ 64,
+ 67,
+ 71,
+ 88,
+ 93,
+ 95
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 350,
+ "hp": 6600,
+ "intelligence": 146,
+ "magicPenetration": 290,
+ "magicPower": 1514,
+ "magicResist": 627,
+ "strength": 21
+ },
+ "items": [
+ 58,
+ 95,
+ 64,
+ 67,
+ 88,
+ 117
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 450,
+ "hp": 8200,
+ "intelligence": 216,
+ "magicPenetration": 450,
+ "magicPower": 1934,
+ "magicResist": 787,
+ "strength": 23
+ },
+ "items": [
+ 132,
+ 60,
+ 67,
+ 88,
+ 98,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 49,
+ "armor": 550,
+ "hp": 11560,
+ "intelligence": 272,
+ "magicPenetration": 650,
+ "magicPower": 2606,
+ "magicResist": 967,
+ "strength": 49
+ },
+ "items": [
+ 126,
+ 67,
+ 88,
+ 98,
+ 135,
+ 137
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 89,
+ "armor": 650,
+ "hp": 16520,
+ "intelligence": 360,
+ "magicPenetration": 850,
+ "magicPower": 3598,
+ "magicResist": 1047,
+ "strength": 89
+ },
+ "items": [
+ 91,
+ 115,
+ 119,
+ 140,
+ 171,
+ 176
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 91,
+ "armor": 650,
+ "hp": 22616,
+ "intelligence": 501,
+ "magicPenetration": 850,
+ "magicPower": 4737,
+ "magicResist": 1807,
+ "strength": 91
+ },
+ "items": [
+ 115,
+ 116,
+ 137,
+ 171,
+ 167,
+ 181
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 131,
+ "armor": 650,
+ "hp": 28616,
+ "intelligence": 728,
+ "magicPenetration": 1170,
+ "magicPower": 5537,
+ "magicResist": 2127,
+ "strength": 131
+ },
+ "items": [
+ 126,
+ 116,
+ 169,
+ 181,
+ 184,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 181,
+ "armor": 650,
+ "hp": 35016,
+ "intelligence": 982,
+ "magicPenetration": 1490,
+ "magicPower": 7097,
+ "magicResist": 2607,
+ "strength": 181
+ },
+ "items": [
+ 117,
+ 140,
+ 169,
+ 181,
+ 180,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 183,
+ "armor": 650,
+ "hp": 44872,
+ "intelligence": 1135,
+ "magicPenetration": 1970,
+ "magicPower": 11652,
+ "magicResist": 2607,
+ "strength": 183
+ },
+ "items": [
+ 137,
+ 140,
+ 169,
+ 181,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 299,
+ "armor": 650,
+ "hp": 48968,
+ "intelligence": 1730,
+ "magicPenetration": 2290,
+ "magicPower": 14511,
+ "magicResist": 2607,
+ "strength": 299
+ },
+ "items": [
+ 209,
+ 174,
+ 212,
+ 180,
+ 226,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 377,
+ "armor": 650,
+ "hp": 54728,
+ "intelligence": 2208,
+ "magicPenetration": 2890,
+ "magicPower": 18207,
+ "magicResist": 3807,
+ "strength": 377
+ },
+ "items": [
+ 184,
+ 180,
+ 212,
+ 228,
+ 232,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 455,
+ "armor": 650,
+ "hp": 67848,
+ "intelligence": 2565,
+ "magicPenetration": 4090,
+ "magicPower": 22623,
+ "magicResist": 5327,
+ "strength": 455
+ },
+ "items": [
+ 184,
+ 203,
+ 227,
+ 224,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 11,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1026,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2030,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero26_lilith",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0026",
+ "epicArtAsset": {
+ "name": "26_lilith_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.25,
+ 1.25
+ ],
+ "screen": "obtain",
+ "x": 103.79999999999995,
+ "y": 7.199999999999989
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": "shop:tower",
+ "characterType": "demon",
+ "silhouette": "tall",
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_tank",
+ "control"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 8,
+ 1,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 365,
+ "1": 366,
+ "2": 367,
+ "3": 368,
+ "4": 369,
+ "7": 8254,
+ "8": 8255
+ }
+ },
+ "27": {
+ "id": 27,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 14,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 20
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 8,
+ 9,
+ 14,
+ 15,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 16
+ },
+ "items": [
+ 9,
+ 14,
+ 10,
+ 18,
+ 25,
+ 27
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 75,
+ "hp": 585,
+ "intelligence": 5,
+ "magicResist": 50,
+ "physicalAttack": 83,
+ "strength": 30
+ },
+ "items": [
+ 10,
+ 18,
+ 25,
+ 28,
+ 37,
+ 43
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 175,
+ "hp": 970,
+ "intelligence": 8,
+ "magicResist": 100,
+ "physicalAttack": 149,
+ "strength": 54
+ },
+ "items": [
+ 33,
+ 28,
+ 37,
+ 43,
+ 57,
+ 59
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 375,
+ "hp": 970,
+ "intelligence": 15,
+ "magicResist": 150,
+ "physicalAttack": 252,
+ "strength": 78
+ },
+ "items": [
+ 36,
+ 44,
+ 47,
+ 57,
+ 59,
+ 69
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 647,
+ "hp": 1890,
+ "intelligence": 17,
+ "magicResist": 200,
+ "physicalAttack": 350,
+ "strength": 121
+ },
+ "items": [
+ 36,
+ 42,
+ 57,
+ 65,
+ 59,
+ 90
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 827,
+ "hp": 2890,
+ "intelligence": 19,
+ "magicResist": 280,
+ "physicalAttack": 579,
+ "strength": 149
+ },
+ "items": [
+ 59,
+ 64,
+ 69,
+ 85,
+ 92,
+ 94
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 1007,
+ "hp": 4690,
+ "intelligence": 26,
+ "magicResist": 360,
+ "physicalAttack": 714,
+ "strength": 220
+ },
+ "items": [
+ 60,
+ 69,
+ 90,
+ 94,
+ 99,
+ 114
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 1367,
+ "hp": 6290,
+ "intelligence": 28,
+ "magicResist": 460,
+ "physicalAttack": 1000,
+ "strength": 292
+ },
+ "items": [
+ 65,
+ 90,
+ 92,
+ 122,
+ 114,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 54,
+ "armor": 1607,
+ "hp": 7890,
+ "intelligence": 54,
+ "magicResist": 540,
+ "physicalAttack": 1585,
+ "strength": 364
+ },
+ "items": [
+ 69,
+ 99,
+ 122,
+ 134,
+ 114,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 94,
+ "armor": 2047,
+ "hp": 9490,
+ "intelligence": 94,
+ "magicResist": 796,
+ "physicalAttack": 2254,
+ "strength": 468
+ },
+ "items": [
+ 114,
+ 122,
+ 134,
+ 131,
+ 170,
+ 175
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 120,
+ "armor": 2807,
+ "hp": 11090,
+ "intelligence": 120,
+ "magicResist": 1052,
+ "physicalAttack": 2923,
+ "strength": 633
+ },
+ "items": [
+ 122,
+ 125,
+ 136,
+ 168,
+ 170,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 160,
+ "armor": 3287,
+ "hp": 15890,
+ "intelligence": 160,
+ "magicResist": 1212,
+ "physicalAttack": 3647,
+ "strength": 830
+ },
+ "items": [
+ 123,
+ 127,
+ 167,
+ 183,
+ 184,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 240,
+ "armor": 3607,
+ "hp": 33090,
+ "intelligence": 240,
+ "magicResist": 1532,
+ "physicalAttack": 3647,
+ "strength": 1114
+ },
+ "items": [
+ 127,
+ 136,
+ 183,
+ 168,
+ 179,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 310,
+ "armor": 3927,
+ "hp": 47746,
+ "intelligence": 310,
+ "magicResist": 1532,
+ "physicalAttack": 6018,
+ "strength": 1232
+ },
+ "items": [
+ 136,
+ 183,
+ 167,
+ 179,
+ 201,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 426,
+ "armor": 4247,
+ "hp": 66866,
+ "intelligence": 426,
+ "magicResist": 1532,
+ "physicalAttack": 7682,
+ "strength": 1675
+ },
+ "items": [
+ 208,
+ 183,
+ 211,
+ 179,
+ 227,
+ 221
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 504,
+ "armor": 5767,
+ "hp": 81522,
+ "intelligence": 504,
+ "magicResist": 1532,
+ "physicalAttack": 9653,
+ "strength": 2250
+ },
+ "items": [
+ 201,
+ 184,
+ 211,
+ 225,
+ 228,
+ 237
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 582,
+ "armor": 7687,
+ "hp": 96562,
+ "intelligence": 582,
+ "magicResist": 3052,
+ "physicalAttack": 12501,
+ "strength": 2607
+ },
+ "items": [
+ 185,
+ 184,
+ 225,
+ 224,
+ 237,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 8,
+ 7,
+ 1
+ ],
+ "artifacts": [
+ 1027,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 7,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero27_paladin_warrior",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0027",
+ "epicArtAsset": {
+ "name": "27_luther_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.1,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": -18,
+ "y": 39
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": "shop:boss",
+ "characterType": "snob",
+ "silhouette": "wide",
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_tank",
+ "control"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 4,
+ 8,
+ 2,
+ 16,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 134,
+ 135,
+ 136,
+ 137,
+ 138
+ ]
+ },
+ "28": {
+ "id": 28,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 50,
+ "strength": 10
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 3,
+ 8,
+ 9,
+ 14,
+ 17
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 2
+ },
+ "items": [
+ 8,
+ 12,
+ 13,
+ 20,
+ 23,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 38,
+ "armor": 50,
+ "hp": 585,
+ "intelligence": 5,
+ "magicResist": 25,
+ "physicalAttack": 83,
+ "strength": 5
+ },
+ "items": [
+ 12,
+ 13,
+ 25,
+ 35,
+ 38,
+ 42
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 67,
+ "armor": 50,
+ "hp": 1470,
+ "intelligence": 13,
+ "magicResist": 25,
+ "physicalAttack": 182,
+ "strength": 13
+ },
+ "items": [
+ 27,
+ 44,
+ 50,
+ 53,
+ 57,
+ 62
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 84,
+ "armor": 150,
+ "armorPenetration": 50,
+ "dodge": 30,
+ "hp": 1470,
+ "intelligence": 15,
+ "magicResist": 117,
+ "physicalAttack": 341,
+ "strength": 15
+ },
+ "items": [
+ 39,
+ 43,
+ 54,
+ 57,
+ 62,
+ 70
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 96,
+ "armor": 200,
+ "armorPenetration": 130,
+ "dodge": 75,
+ "hp": 1970,
+ "intelligence": 17,
+ "magicResist": 167,
+ "physicalAttack": 500,
+ "strength": 17
+ },
+ "items": [
+ 44,
+ 53,
+ 62,
+ 70,
+ 73,
+ 87
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 113,
+ "armor": 250,
+ "armorPenetration": 260,
+ "dodge": 129,
+ "hp": 2770,
+ "intelligence": 24,
+ "magicResist": 217,
+ "physicalAttack": 659,
+ "strength": 24
+ },
+ "items": [
+ 59,
+ 64,
+ 87,
+ 70,
+ 96,
+ 102
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 168,
+ "armor": 350,
+ "armorPenetration": 340,
+ "dodge": 189,
+ "hp": 3570,
+ "intelligence": 31,
+ "magicResist": 297,
+ "physicalAttack": 785,
+ "strength": 31
+ },
+ "items": [
+ 57,
+ 64,
+ 90,
+ 96,
+ 102,
+ 118
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 238,
+ "armor": 430,
+ "armorPenetration": 500,
+ "dodge": 249,
+ "hp": 4370,
+ "intelligence": 33,
+ "magicResist": 377,
+ "physicalAttack": 1033,
+ "strength": 49
+ },
+ "items": [
+ 73,
+ 87,
+ 97,
+ 118,
+ 122,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 339,
+ "armor": 590,
+ "armorPenetration": 860,
+ "dodge": 273,
+ "hp": 5170,
+ "intelligence": 64,
+ "magicResist": 377,
+ "physicalAttack": 1319,
+ "strength": 80
+ },
+ "items": [
+ 73,
+ 96,
+ 102,
+ 120,
+ 133,
+ 139
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 463,
+ "armor": 590,
+ "armorPenetration": 860,
+ "dodge": 357,
+ "hp": 5970,
+ "intelligence": 90,
+ "magicResist": 786,
+ "physicalAttack": 1979,
+ "strength": 106
+ },
+ "items": [
+ 102,
+ 122,
+ 138,
+ 134,
+ 172,
+ 173
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 660,
+ "armor": 750,
+ "armorPenetration": 1460,
+ "dodge": 417,
+ "hp": 5970,
+ "intelligence": 130,
+ "magicResist": 1042,
+ "physicalAttack": 2432,
+ "strength": 146
+ },
+ "items": [
+ 133,
+ 120,
+ 122,
+ 172,
+ 168,
+ 189
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 855,
+ "armor": 910,
+ "armorPenetration": 1460,
+ "dodge": 645,
+ "hp": 10770,
+ "intelligence": 156,
+ "magicResist": 1042,
+ "physicalAttack": 3048,
+ "strength": 172
+ },
+ "items": [
+ 120,
+ 122,
+ 168,
+ 178,
+ 189,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1109,
+ "armor": 1070,
+ "armorPenetration": 1460,
+ "dodge": 1039,
+ "hp": 15570,
+ "intelligence": 206,
+ "magicResist": 1042,
+ "physicalAttack": 3664,
+ "strength": 222
+ },
+ "items": [
+ 133,
+ 138,
+ 168,
+ 182,
+ 179,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1251,
+ "armor": 1070,
+ "armorPenetration": 1780,
+ "dodge": 1039,
+ "hp": 25426,
+ "intelligence": 270,
+ "magicResist": 1042,
+ "physicalAttack": 6355,
+ "strength": 286
+ },
+ "items": [
+ 139,
+ 118,
+ 182,
+ 182,
+ 201,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1638,
+ "armor": 1070,
+ "armorPenetration": 2580,
+ "dodge": 1039,
+ "hp": 30546,
+ "intelligence": 348,
+ "magicResist": 1451,
+ "physicalAttack": 8679,
+ "strength": 364
+ },
+ "items": [
+ 208,
+ 175,
+ 213,
+ 179,
+ 230,
+ 223
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2213,
+ "armor": 1670,
+ "armorPenetration": 2580,
+ "dodge": 1372,
+ "hp": 40402,
+ "intelligence": 426,
+ "magicResist": 1451,
+ "physicalAttack": 10650,
+ "strength": 442
+ },
+ "items": [
+ 201,
+ 176,
+ 213,
+ 230,
+ 228,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2570,
+ "armor": 1670,
+ "armorPenetration": 3540,
+ "dodge": 1705,
+ "hp": 50642,
+ "intelligence": 504,
+ "magicResist": 3251,
+ "physicalAttack": 13338,
+ "strength": 520
+ },
+ "items": [
+ 201,
+ 176,
+ 227,
+ 230,
+ 239,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 12,
+ 10,
+ 3
+ ],
+ "artifacts": [
+ 1028,
+ 2002,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 26,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero28_asian_girl",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0028",
+ "epicArtAsset": {
+ "name": "28_qing_mao_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": -29.800000000000068,
+ "y": 34.80000000000007
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "QuingMao"
+ },
+ "role": "front",
+ "obtainType": "shop:boss",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 10,
+ 1
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 264,
+ "1": 265,
+ "2": 266,
+ "3": 267,
+ "4": 268,
+ "7": 8246,
+ "8": 8247
+ }
+ },
+ "29": {
+ "id": 29,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 21,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 4,
+ 7,
+ 8,
+ 13,
+ 16
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 14,
+ "magicPower": 25,
+ "strength": 2
+ },
+ "items": [
+ 9,
+ 11,
+ 13,
+ 19,
+ 22,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 25,
+ "hp": 970,
+ "intelligence": 38,
+ "magicPower": 125,
+ "magicResist": 25,
+ "strength": 5
+ },
+ "items": [
+ 13,
+ 19,
+ 22,
+ 40,
+ 44,
+ 24
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 75,
+ "hp": 1855,
+ "intelligence": 67,
+ "magicPower": 225,
+ "magicResist": 75,
+ "strength": 7
+ },
+ "items": [
+ 22,
+ 40,
+ 44,
+ 46,
+ 56,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 125,
+ "hp": 3355,
+ "intelligence": 89,
+ "magicPower": 425,
+ "magicResist": 125,
+ "strength": 9
+ },
+ "items": [
+ 40,
+ 48,
+ 49,
+ 56,
+ 58,
+ 63
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 167,
+ "hp": 5155,
+ "intelligence": 138,
+ "magicPower": 739,
+ "magicResist": 167,
+ "physicalAttack": 28,
+ "strength": 11
+ },
+ "items": [
+ 40,
+ 49,
+ 56,
+ 58,
+ 68,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 289,
+ "hp": 6155,
+ "intelligence": 187,
+ "magicPower": 969,
+ "magicResist": 267,
+ "physicalAttack": 56,
+ "strength": 18
+ },
+ "items": [
+ 56,
+ 63,
+ 64,
+ 88,
+ 91,
+ 95
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 389,
+ "hp": 11555,
+ "intelligence": 227,
+ "magicPower": 1129,
+ "magicResist": 347,
+ "physicalAttack": 56,
+ "strength": 20
+ },
+ "items": [
+ 63,
+ 68,
+ 88,
+ 91,
+ 95,
+ 116
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 569,
+ "hp": 15155,
+ "intelligence": 297,
+ "magicPower": 1529,
+ "magicResist": 507,
+ "physicalAttack": 56,
+ "strength": 22
+ },
+ "items": [
+ 67,
+ 88,
+ 91,
+ 95,
+ 116,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 669,
+ "hp": 20515,
+ "intelligence": 367,
+ "magicPower": 2361,
+ "magicResist": 747,
+ "physicalAttack": 56,
+ "strength": 24
+ },
+ "items": [
+ 88,
+ 75,
+ 91,
+ 119,
+ 132,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 55,
+ "armor": 769,
+ "hp": 27411,
+ "intelligence": 468,
+ "magicPower": 3420,
+ "magicResist": 747,
+ "physicalAttack": 56,
+ "strength": 55
+ },
+ "items": [
+ 126,
+ 119,
+ 116,
+ 137,
+ 167,
+ 171
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 95,
+ "armor": 769,
+ "hp": 35011,
+ "intelligence": 725,
+ "magicPower": 4060,
+ "magicResist": 907,
+ "physicalAttack": 56,
+ "strength": 95
+ },
+ "items": [
+ 95,
+ 119,
+ 132,
+ 169,
+ 171,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 121,
+ "armor": 769,
+ "hp": 38211,
+ "intelligence": 958,
+ "magicPower": 5780,
+ "magicResist": 907,
+ "physicalAttack": 56,
+ "strength": 121
+ },
+ "items": [
+ 119,
+ 135,
+ 167,
+ 183,
+ 169,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 171,
+ "armor": 1089,
+ "hp": 51571,
+ "intelligence": 1212,
+ "magicPower": 7052,
+ "magicResist": 907,
+ "physicalAttack": 56,
+ "strength": 171
+ },
+ "items": [
+ 132,
+ 126,
+ 167,
+ 184,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 197,
+ "armor": 1089,
+ "hp": 66531,
+ "intelligence": 1541,
+ "magicPower": 9868,
+ "magicResist": 1227,
+ "physicalAttack": 56,
+ "strength": 197
+ },
+ "items": [
+ 135,
+ 140,
+ 183,
+ 184,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 275,
+ "armor": 1409,
+ "hp": 82787,
+ "intelligence": 2050,
+ "magicPower": 12159,
+ "magicResist": 1547,
+ "physicalAttack": 56,
+ "strength": 275
+ },
+ "items": [
+ 180,
+ 183,
+ 212,
+ 209,
+ 222,
+ 224
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 353,
+ "armor": 1729,
+ "hp": 105347,
+ "intelligence": 2746,
+ "magicPower": 14655,
+ "magicResist": 1547,
+ "physicalAttack": 56,
+ "strength": 353
+ },
+ "items": [
+ 203,
+ 184,
+ 212,
+ 224,
+ 228,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 431,
+ "armor": 1729,
+ "hp": 127267,
+ "intelligence": 3255,
+ "magicPower": 19071,
+ "magicResist": 3067,
+ "physicalAttack": 56,
+ "strength": 431
+ },
+ "items": [
+ 186,
+ 184,
+ 227,
+ 224,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 7,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1029,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2022,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero29_vampire",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0029",
+ "epicArtAsset": {
+ "name": "29_dorian_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1,
+ 1
+ ],
+ "screen": "obtain",
+ "x": 0,
+ "y": 54
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": "chest:town",
+ "characterType": "demon",
+ "silhouette": "tall",
+ "ultCinematic": null,
+ "roleExtended": [
+ "healer",
+ "support"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 9,
+ 5,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 144,
+ 145,
+ 146,
+ 147,
+ 148
+ ]
+ },
+ "30": {
+ "id": 30,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 14,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 24,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 13,
+ 7,
+ 13,
+ 4,
+ 16
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 970,
+ "intelligence": 14,
+ "magicPower": 25,
+ "strength": 2
+ },
+ "items": [
+ 8,
+ 13,
+ 11,
+ 19,
+ 22,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 25,
+ "hp": 1355,
+ "intelligence": 38,
+ "magicPower": 125,
+ "strength": 5
+ },
+ "items": [
+ 11,
+ 19,
+ 22,
+ 26,
+ 41,
+ 46
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 75,
+ "hp": 1855,
+ "intelligence": 72,
+ "magicPower": 275,
+ "strength": 8
+ },
+ "items": [
+ 22,
+ 26,
+ 41,
+ 49,
+ 59,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 267,
+ "hp": 1855,
+ "intelligence": 116,
+ "magicPower": 425,
+ "physicalAttack": 28,
+ "strength": 10
+ },
+ "items": [
+ 41,
+ 49,
+ 48,
+ 59,
+ 58,
+ 68
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 539,
+ "hp": 1855,
+ "intelligence": 165,
+ "magicPower": 689,
+ "magicResist": 42,
+ "physicalAttack": 56,
+ "strength": 12
+ },
+ "items": [
+ 49,
+ 48,
+ 56,
+ 60,
+ 67,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 681,
+ "hp": 3655,
+ "intelligence": 204,
+ "magicPower": 933,
+ "magicResist": 264,
+ "physicalAttack": 84,
+ "strength": 14
+ },
+ "items": [
+ 58,
+ 64,
+ 68,
+ 88,
+ 93,
+ 95
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 861,
+ "hp": 5255,
+ "intelligence": 244,
+ "magicPower": 1393,
+ "magicResist": 344,
+ "physicalAttack": 84,
+ "strength": 16
+ },
+ "items": [
+ 64,
+ 68,
+ 86,
+ 93,
+ 95,
+ 126
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 941,
+ "hp": 7655,
+ "intelligence": 299,
+ "magicPower": 1993,
+ "magicResist": 524,
+ "physicalAttack": 84,
+ "strength": 23
+ },
+ "items": [
+ 86,
+ 86,
+ 93,
+ 95,
+ 116,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 35,
+ "armor": 941,
+ "hp": 10215,
+ "intelligence": 399,
+ "magicPower": 2865,
+ "magicResist": 884,
+ "physicalAttack": 84,
+ "strength": 35
+ },
+ "items": [
+ 86,
+ 95,
+ 95,
+ 116,
+ 135,
+ 137
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 80,
+ "armor": 941,
+ "hp": 12775,
+ "intelligence": 608,
+ "magicPower": 3537,
+ "magicResist": 1144,
+ "physicalAttack": 84,
+ "strength": 80
+ },
+ "items": [
+ 116,
+ 119,
+ 126,
+ 137,
+ 167,
+ 171
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 120,
+ "armor": 941,
+ "hp": 20375,
+ "intelligence": 865,
+ "magicPower": 4177,
+ "magicResist": 1304,
+ "physicalAttack": 84,
+ "strength": 120
+ },
+ "items": [
+ 126,
+ 119,
+ 137,
+ 171,
+ 176,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 160,
+ "armor": 1261,
+ "hp": 26775,
+ "intelligence": 1092,
+ "magicPower": 4657,
+ "magicResist": 1904,
+ "physicalAttack": 84,
+ "strength": 160
+ },
+ "items": [
+ 135,
+ 116,
+ 167,
+ 169,
+ 184,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 210,
+ "armor": 1261,
+ "hp": 40135,
+ "intelligence": 1346,
+ "magicPower": 5929,
+ "magicResist": 2384,
+ "physicalAttack": 84,
+ "strength": 210
+ },
+ "items": [
+ 116,
+ 132,
+ 167,
+ 183,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 236,
+ "armor": 1581,
+ "hp": 53495,
+ "intelligence": 1705,
+ "magicPower": 8585,
+ "magicResist": 2544,
+ "physicalAttack": 84,
+ "strength": 236
+ },
+ "items": [
+ 137,
+ 140,
+ 169,
+ 184,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 352,
+ "armor": 1581,
+ "hp": 62391,
+ "intelligence": 2300,
+ "magicPower": 10964,
+ "magicResist": 2864,
+ "physicalAttack": 84,
+ "strength": 352
+ },
+ "items": [
+ 180,
+ 183,
+ 212,
+ 209,
+ 228,
+ 227
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 430,
+ "armor": 3101,
+ "hp": 72951,
+ "intelligence": 2778,
+ "magicPower": 13460,
+ "magicResist": 4064,
+ "physicalAttack": 84,
+ "strength": 430
+ },
+ "items": [
+ 203,
+ 184,
+ 212,
+ 224,
+ 222,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 508,
+ "armor": 3101,
+ "hp": 94871,
+ "intelligence": 3505,
+ "magicPower": 17876,
+ "magicResist": 4384,
+ "physicalAttack": 84,
+ "strength": 508
+ },
+ "items": [
+ 186,
+ 184,
+ 227,
+ 224,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 7,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1030,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2016,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero30_antimage",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0030",
+ "epicArtAsset": {
+ "name": "30_cornelius_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 10,
+ "y": 72
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": "shop:socialShop",
+ "characterType": "snob",
+ "silhouette": "small",
+ "ultCinematic": null,
+ "roleExtended": [
+ "support",
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 5,
+ 7,
+ 2,
+ 20
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 149,
+ 150,
+ 151,
+ 152,
+ 153
+ ]
+ },
+ "31": {
+ "id": 31,
+ "baseStats": {
+ "agility": 15,
+ "dodge": 100,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 19,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 4,
+ 7,
+ 8,
+ 9,
+ 16
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 14,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 2
+ },
+ "items": [
+ 8,
+ 9,
+ 11,
+ 19,
+ 22,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 50,
+ "hp": 200,
+ "intelligence": 38,
+ "magicPower": 125,
+ "magicResist": 50,
+ "strength": 5
+ },
+ "items": [
+ 11,
+ 19,
+ 22,
+ 26,
+ 34,
+ 46
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 50,
+ "hp": 700,
+ "intelligence": 74,
+ "magicPower": 275,
+ "magicResist": 50,
+ "strength": 13
+ },
+ "items": [
+ 26,
+ 40,
+ 44,
+ 54,
+ 58,
+ 62
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 100,
+ "dodge": 45,
+ "hp": 1200,
+ "intelligence": 86,
+ "magicPower": 475,
+ "magicResist": 100,
+ "strength": 15
+ },
+ "items": [
+ 40,
+ 41,
+ 48,
+ 58,
+ 62,
+ 75
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 150,
+ "dodge": 75,
+ "hp": 1200,
+ "intelligence": 138,
+ "magicPower": 709,
+ "magicResist": 142,
+ "strength": 22
+ },
+ "items": [
+ 40,
+ 49,
+ 58,
+ 62,
+ 73,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 29,
+ "armor": 192,
+ "dodge": 129,
+ "hp": 2000,
+ "intelligence": 187,
+ "magicPower": 859,
+ "magicResist": 242,
+ "physicalAttack": 28,
+ "strength": 29
+ },
+ "items": [
+ 59,
+ 67,
+ 75,
+ 88,
+ 93,
+ 102
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 392,
+ "dodge": 189,
+ "hp": 2800,
+ "intelligence": 204,
+ "magicPower": 1219,
+ "magicResist": 322,
+ "physicalAttack": 28,
+ "strength": 36
+ },
+ "items": [
+ 58,
+ 73,
+ 86,
+ 93,
+ 102,
+ 116
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 43,
+ "armor": 392,
+ "dodge": 273,
+ "hp": 3600,
+ "intelligence": 251,
+ "magicPower": 1679,
+ "magicResist": 582,
+ "physicalAttack": 28,
+ "strength": 43
+ },
+ "items": [
+ 86,
+ 88,
+ 93,
+ 102,
+ 116,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 74,
+ "armor": 492,
+ "dodge": 333,
+ "hp": 4400,
+ "intelligence": 352,
+ "magicPower": 2119,
+ "magicResist": 842,
+ "physicalAttack": 28,
+ "strength": 74
+ },
+ "items": [
+ 88,
+ 102,
+ 115,
+ 119,
+ 135,
+ 137
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 114,
+ "armor": 592,
+ "dodge": 393,
+ "hp": 7760,
+ "intelligence": 470,
+ "magicPower": 3031,
+ "magicResist": 1002,
+ "physicalAttack": 28,
+ "strength": 114
+ },
+ "items": [
+ 102,
+ 119,
+ 135,
+ 137,
+ 171,
+ 178
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 154,
+ "armor": 592,
+ "dodge": 619,
+ "hp": 10320,
+ "intelligence": 697,
+ "magicPower": 3703,
+ "magicResist": 1002,
+ "physicalAttack": 28,
+ "strength": 154
+ },
+ "items": [
+ 115,
+ 119,
+ 132,
+ 169,
+ 171,
+ 189
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 180,
+ "armor": 592,
+ "dodge": 847,
+ "hp": 15120,
+ "intelligence": 892,
+ "magicPower": 4623,
+ "magicResist": 1162,
+ "physicalAttack": 28,
+ "strength": 180
+ },
+ "items": [
+ 127,
+ 132,
+ 178,
+ 169,
+ 184,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 284,
+ "armor": 592,
+ "dodge": 1013,
+ "hp": 19920,
+ "intelligence": 1200,
+ "magicPower": 5223,
+ "magicResist": 1482,
+ "physicalAttack": 28,
+ "strength": 284
+ },
+ "items": [
+ 132,
+ 116,
+ 178,
+ 184,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 310,
+ "armor": 592,
+ "dodge": 1179,
+ "hp": 27280,
+ "intelligence": 1559,
+ "magicPower": 7879,
+ "magicResist": 1962,
+ "physicalAttack": 28,
+ "strength": 310
+ },
+ "items": [
+ 137,
+ 135,
+ 184,
+ 180,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 426,
+ "armor": 592,
+ "dodge": 1179,
+ "hp": 37840,
+ "intelligence": 2154,
+ "magicPower": 10311,
+ "magicResist": 2282,
+ "physicalAttack": 28,
+ "strength": 426
+ },
+ "items": [
+ 180,
+ 183,
+ 212,
+ 209,
+ 228,
+ 230
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 504,
+ "armor": 912,
+ "dodge": 1512,
+ "hp": 48400,
+ "intelligence": 2632,
+ "magicPower": 12807,
+ "magicResist": 3482,
+ "physicalAttack": 28,
+ "strength": 504
+ },
+ "items": [
+ 203,
+ 184,
+ 212,
+ 230,
+ 222,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 582,
+ "armor": 912,
+ "dodge": 1845,
+ "hp": 58320,
+ "intelligence": 3359,
+ "magicPower": 17223,
+ "magicResist": 3802,
+ "physicalAttack": 28,
+ "strength": 582
+ },
+ "items": [
+ 186,
+ 184,
+ 227,
+ 230,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 8,
+ 10,
+ 2
+ ],
+ "artifacts": [
+ 1031,
+ 2002,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2025,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero31_alchemist",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0031",
+ "epicArtAsset": {
+ "name": "31_jet_epic.jpg"
+ },
+ "spineEpicArtAsset": {
+ "name": "31_jet",
+ "transform": [
+ {
+ "scale": [
+ 1.1,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 108,
+ "y": 52
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": "shop:soulshop",
+ "characterType": "cutie",
+ "silhouette": "small",
+ "ultCinematic": null,
+ "roleExtended": [
+ "healer",
+ "support"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 9,
+ 5,
+ 2,
+ 20
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 1000,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 154,
+ 155,
+ 156,
+ 157,
+ 158
+ ]
+ },
+ "32": {
+ "id": 32,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 10
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 14,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 24,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 4,
+ 4,
+ 7,
+ 19,
+ 9,
+ 13
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 385,
+ "intelligence": 19,
+ "magicPower": 75,
+ "magicResist": 25,
+ "strength": 2
+ },
+ "items": [
+ 2,
+ 7,
+ 19,
+ 11,
+ 22,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "hp": 585,
+ "intelligence": 43,
+ "magicPower": 200,
+ "magicResist": 25,
+ "strength": 5
+ },
+ "items": [
+ 19,
+ 19,
+ 26,
+ 22,
+ 26,
+ 40
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 585,
+ "intelligence": 79,
+ "magicPower": 450,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 26,
+ 28,
+ 40,
+ 28,
+ 51,
+ 48
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 19,
+ "hp": 585,
+ "intelligence": 116,
+ "magicPower": 634,
+ "magicResist": 167,
+ "strength": 19
+ },
+ "items": [
+ 45,
+ 40,
+ 46,
+ 40,
+ 48,
+ 63
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 21,
+ "hp": 1885,
+ "intelligence": 153,
+ "magicPower": 998,
+ "magicResist": 259,
+ "strength": 21
+ },
+ "items": [
+ 40,
+ 11,
+ 51,
+ 46,
+ 68,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 39,
+ "armor": 80,
+ "hp": 2385,
+ "intelligence": 195,
+ "magicPower": 1178,
+ "magicResist": 359,
+ "strength": 39
+ },
+ "items": [
+ 48,
+ 48,
+ 88,
+ 86,
+ 93,
+ 95
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 46,
+ "armor": 180,
+ "hp": 3185,
+ "intelligence": 280,
+ "magicPower": 1626,
+ "magicResist": 543,
+ "strength": 46
+ },
+ "items": [
+ 51,
+ 48,
+ 100,
+ 95,
+ 93,
+ 116
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 58,
+ "armor": 180,
+ "hp": 3185,
+ "intelligence": 375,
+ "magicPower": 2070,
+ "magicResist": 945,
+ "strength": 58
+ },
+ "items": [
+ 86,
+ 64,
+ 119,
+ 91,
+ 119,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 65,
+ "armor": 180,
+ "hp": 8545,
+ "intelligence": 452,
+ "magicPower": 2902,
+ "magicResist": 1125,
+ "strength": 65
+ },
+ "items": [
+ 93,
+ 86,
+ 119,
+ 93,
+ 116,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 72,
+ "armor": 180,
+ "hp": 12641,
+ "intelligence": 529,
+ "magicPower": 4441,
+ "magicResist": 1385,
+ "strength": 72
+ },
+ "items": [
+ 116,
+ 119,
+ 135,
+ 132,
+ 171,
+ 167
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 98,
+ "armor": 180,
+ "hp": 21201,
+ "intelligence": 754,
+ "magicPower": 5273,
+ "magicResist": 1545,
+ "strength": 98
+ },
+ "items": [
+ 132,
+ 119,
+ 171,
+ 135,
+ 167,
+ 184
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 124,
+ "armor": 180,
+ "hp": 34561,
+ "intelligence": 949,
+ "magicPower": 5945,
+ "magicResist": 1865,
+ "strength": 124
+ },
+ "items": [
+ 137,
+ 115,
+ 169,
+ 169,
+ 184,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 212,
+ "armor": 180,
+ "hp": 39361,
+ "intelligence": 1259,
+ "magicPower": 7305,
+ "magicResist": 2345,
+ "strength": 212
+ },
+ "items": [
+ 135,
+ 126,
+ 184,
+ 169,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 214,
+ "armor": 180,
+ "hp": 50881,
+ "intelligence": 1534,
+ "magicPower": 11233,
+ "magicResist": 2665,
+ "strength": 214
+ },
+ "items": [
+ 140,
+ 137,
+ 184,
+ 176,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 330,
+ "armor": 180,
+ "hp": 59777,
+ "intelligence": 2129,
+ "magicPower": 13012,
+ "magicResist": 3585,
+ "strength": 330
+ },
+ "items": [
+ 180,
+ 183,
+ 212,
+ 209,
+ 228,
+ 226
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 408,
+ "armor": 500,
+ "hp": 70337,
+ "intelligence": 2607,
+ "magicPower": 16708,
+ "magicResist": 4785,
+ "strength": 408
+ },
+ "items": [
+ 203,
+ 184,
+ 212,
+ 224,
+ 222,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 486,
+ "armor": 500,
+ "hp": 92257,
+ "intelligence": 3334,
+ "magicPower": 21124,
+ "magicResist": 5105,
+ "strength": 486
+ },
+ "items": [
+ 186,
+ 184,
+ 227,
+ 228,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 8,
+ 7,
+ 2
+ ],
+ "artifacts": [
+ 1032,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2029,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero32_sun",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0032",
+ "epicArtAsset": {
+ "name": "32_helios_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.1,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": -26,
+ "y": 107
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "helios",
+ "transform": [
+ {
+ "scale": [
+ -1,
+ 1
+ ],
+ "screen": "obtain",
+ "x": 0,
+ "y": 0
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": null,
+ "characterType": "snob",
+ "silhouette": "tall",
+ "ultCinematic": {
+ "ident": "hero32_battle_animation"
+ },
+ "roleExtended": [
+ "mage",
+ "support"
+ ],
+ "sfxAsset": "hero32_sfx",
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 5,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "assetsIdent": null,
+ "skill": [
+ 159,
+ 160,
+ 161,
+ 162,
+ 163
+ ]
+ },
+ "33": {
+ "id": 33,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 17,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 21,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 17
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 4,
+ 2,
+ 7,
+ 7,
+ 13,
+ 16
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 585,
+ "intelligence": 14,
+ "magicPower": 50,
+ "strength": 2
+ },
+ "items": [
+ 11,
+ 7,
+ 19,
+ 13,
+ 22,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "hp": 970,
+ "intelligence": 38,
+ "magicPower": 175,
+ "strength": 5
+ },
+ "items": [
+ 19,
+ 13,
+ 40,
+ 22,
+ 40,
+ 24
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 1855,
+ "intelligence": 77,
+ "magicPower": 325,
+ "strength": 7
+ },
+ "items": [
+ 40,
+ 22,
+ 46,
+ 40,
+ 58,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "hp": 2355,
+ "intelligence": 109,
+ "magicPower": 675,
+ "strength": 9
+ },
+ "items": [
+ 52,
+ 40,
+ 56,
+ 49,
+ 58,
+ 63
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 42,
+ "hp": 4155,
+ "intelligence": 153,
+ "magicPenetration": 50,
+ "magicPower": 905,
+ "physicalAttack": 28,
+ "strength": 11
+ },
+ "items": [
+ 52,
+ 40,
+ 58,
+ 56,
+ 68,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 122,
+ "hp": 5155,
+ "intelligence": 190,
+ "magicPenetration": 100,
+ "magicPower": 1135,
+ "magicResist": 100,
+ "physicalAttack": 28,
+ "strength": 18
+ },
+ "items": [
+ 63,
+ 58,
+ 88,
+ 64,
+ 91,
+ 95
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 222,
+ "hp": 9555,
+ "intelligence": 230,
+ "magicPenetration": 100,
+ "magicPower": 1395,
+ "magicResist": 180,
+ "physicalAttack": 28,
+ "strength": 20
+ },
+ "items": [
+ 68,
+ 63,
+ 91,
+ 88,
+ 95,
+ 116
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 402,
+ "hp": 13155,
+ "intelligence": 300,
+ "magicPenetration": 100,
+ "magicPower": 1795,
+ "magicResist": 340,
+ "physicalAttack": 28,
+ "strength": 22
+ },
+ "items": [
+ 88,
+ 67,
+ 95,
+ 98,
+ 116,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 502,
+ "hp": 16515,
+ "intelligence": 370,
+ "magicPenetration": 300,
+ "magicPower": 2627,
+ "magicResist": 580,
+ "physicalAttack": 28,
+ "strength": 24
+ },
+ "items": [
+ 75,
+ 88,
+ 119,
+ 98,
+ 132,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 55,
+ "armor": 602,
+ "hp": 21411,
+ "intelligence": 471,
+ "magicPenetration": 500,
+ "magicPower": 3686,
+ "magicResist": 580,
+ "physicalAttack": 28,
+ "strength": 55
+ },
+ "items": [
+ 119,
+ 126,
+ 137,
+ 116,
+ 167,
+ 171
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 95,
+ "armor": 602,
+ "hp": 29011,
+ "intelligence": 728,
+ "magicPenetration": 500,
+ "magicPower": 4326,
+ "magicResist": 740,
+ "physicalAttack": 28,
+ "strength": 95
+ },
+ "items": [
+ 119,
+ 95,
+ 169,
+ 132,
+ 171,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 121,
+ "armor": 602,
+ "hp": 32211,
+ "intelligence": 961,
+ "magicPenetration": 500,
+ "magicPower": 6046,
+ "magicResist": 740,
+ "physicalAttack": 28,
+ "strength": 121
+ },
+ "items": [
+ 135,
+ 119,
+ 181,
+ 174,
+ 169,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 171,
+ "armor": 602,
+ "hp": 34771,
+ "intelligence": 1215,
+ "magicPenetration": 1420,
+ "magicPower": 7798,
+ "magicResist": 740,
+ "physicalAttack": 28,
+ "strength": 171
+ },
+ "items": [
+ 117,
+ 132,
+ 181,
+ 174,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 197,
+ "armor": 602,
+ "hp": 37331,
+ "intelligence": 1574,
+ "magicPenetration": 2500,
+ "magicPower": 10934,
+ "magicResist": 740,
+ "physicalAttack": 28,
+ "strength": 197
+ },
+ "items": [
+ 140,
+ 135,
+ 181,
+ 181,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 275,
+ "armor": 602,
+ "hp": 43987,
+ "intelligence": 2083,
+ "magicPenetration": 3140,
+ "magicPower": 14185,
+ "magicResist": 740,
+ "physicalAttack": 28,
+ "strength": 275
+ },
+ "items": [
+ 180,
+ 183,
+ 212,
+ 209,
+ 224,
+ 232
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 353,
+ "armor": 922,
+ "hp": 66547,
+ "intelligence": 2561,
+ "magicPenetration": 4340,
+ "magicPower": 16681,
+ "magicResist": 740,
+ "physicalAttack": 28,
+ "strength": 353
+ },
+ "items": [
+ 203,
+ 184,
+ 212,
+ 226,
+ 222,
+ 233
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 431,
+ "armor": 922,
+ "hp": 71347,
+ "intelligence": 3288,
+ "magicPenetration": 5108,
+ "magicPower": 21913,
+ "magicResist": 1060,
+ "physicalAttack": 28,
+ "strength": 431
+ },
+ "items": [
+ 203,
+ 180,
+ 224,
+ 226,
+ 233,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 8,
+ 11,
+ 2
+ ],
+ "artifacts": [
+ 1033,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2010,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero33_deerboy",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0033",
+ "epicArtAsset": {
+ "name": "33_lars_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.2,
+ 1.2
+ ],
+ "screen": "obtain",
+ "x": 118,
+ "y": 53
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "33_lars",
+ "transform": [
+ {
+ "scale": [
+ 1.1,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 81.60000000000002,
+ "y": 21.600000000000023
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": null,
+ "characterType": "snob",
+ "silhouette": "tall",
+ "ultCinematic": null,
+ "roleExtended": [
+ "mage",
+ "control"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 8,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 164,
+ 165,
+ 166,
+ 167,
+ 168
+ ]
+ },
+ "34": {
+ "id": 34,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 10
+ },
+ "stars": {
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 17,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 22,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 16,
+ 2,
+ 13,
+ 7,
+ 4,
+ 16
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 585,
+ "intelligence": 21,
+ "magicPower": 25,
+ "strength": 2
+ },
+ "items": [
+ 16,
+ 8,
+ 19,
+ 11,
+ 22,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 52,
+ "magicPower": 125,
+ "strength": 5
+ },
+ "items": [
+ 19,
+ 11,
+ 26,
+ 22,
+ 41,
+ 46
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 75,
+ "hp": 1085,
+ "intelligence": 86,
+ "magicPower": 275,
+ "strength": 8
+ },
+ "items": [
+ 26,
+ 22,
+ 40,
+ 41,
+ 59,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 225,
+ "hp": 1085,
+ "intelligence": 118,
+ "magicPower": 475,
+ "strength": 10
+ },
+ "items": [
+ 49,
+ 41,
+ 59,
+ 48,
+ 58,
+ 68
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 497,
+ "hp": 1085,
+ "intelligence": 167,
+ "magicPower": 739,
+ "magicResist": 42,
+ "physicalAttack": 28,
+ "strength": 12
+ },
+ "items": [
+ 48,
+ 40,
+ 60,
+ 58,
+ 67,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 597,
+ "hp": 1885,
+ "intelligence": 194,
+ "magicPower": 1133,
+ "magicResist": 264,
+ "physicalAttack": 28,
+ "strength": 14
+ },
+ "items": [
+ 67,
+ 58,
+ 88,
+ 68,
+ 93,
+ 95
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 777,
+ "hp": 2685,
+ "intelligence": 234,
+ "magicPower": 1673,
+ "magicResist": 344,
+ "physicalAttack": 28,
+ "strength": 16
+ },
+ "items": [
+ 68,
+ 63,
+ 93,
+ 86,
+ 95,
+ 126
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 857,
+ "hp": 5085,
+ "intelligence": 289,
+ "magicPower": 2353,
+ "magicResist": 444,
+ "physicalAttack": 28,
+ "strength": 23
+ },
+ "items": [
+ 86,
+ 86,
+ 95,
+ 93,
+ 116,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 35,
+ "armor": 857,
+ "hp": 7645,
+ "intelligence": 389,
+ "magicPower": 3225,
+ "magicResist": 804,
+ "physicalAttack": 28,
+ "strength": 35
+ },
+ "items": [
+ 95,
+ 86,
+ 116,
+ 95,
+ 135,
+ 137
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 80,
+ "armor": 857,
+ "hp": 10205,
+ "intelligence": 598,
+ "magicPower": 3897,
+ "magicResist": 1064,
+ "physicalAttack": 28,
+ "strength": 80
+ },
+ "items": [
+ 119,
+ 116,
+ 137,
+ 126,
+ 169,
+ 171
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 120,
+ "armor": 857,
+ "hp": 11805,
+ "intelligence": 855,
+ "magicPower": 5137,
+ "magicResist": 1224,
+ "physicalAttack": 28,
+ "strength": 120
+ },
+ "items": [
+ 119,
+ 116,
+ 171,
+ 137,
+ 169,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 160,
+ "armor": 1177,
+ "hp": 16605,
+ "intelligence": 1112,
+ "magicPower": 6057,
+ "magicResist": 1384,
+ "physicalAttack": 28,
+ "strength": 160
+ },
+ "items": [
+ 116,
+ 135,
+ 169,
+ 171,
+ 184,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 210,
+ "armor": 1177,
+ "hp": 23965,
+ "intelligence": 1475,
+ "magicPower": 7329,
+ "magicResist": 1864,
+ "physicalAttack": 28,
+ "strength": 210
+ },
+ "items": [
+ 132,
+ 116,
+ 183,
+ 169,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 236,
+ "armor": 1497,
+ "hp": 31325,
+ "intelligence": 1834,
+ "magicPower": 10585,
+ "magicResist": 2024,
+ "physicalAttack": 28,
+ "strength": 236
+ },
+ "items": [
+ 140,
+ 137,
+ 184,
+ 169,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 352,
+ "armor": 1497,
+ "hp": 40221,
+ "intelligence": 2429,
+ "magicPower": 12964,
+ "magicResist": 2344,
+ "physicalAttack": 28,
+ "strength": 352
+ },
+ "items": [
+ 180,
+ 183,
+ 212,
+ 209,
+ 222,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 430,
+ "armor": 1817,
+ "hp": 50781,
+ "intelligence": 3125,
+ "magicPower": 15460,
+ "magicResist": 3544,
+ "physicalAttack": 28,
+ "strength": 430
+ },
+ "items": [
+ 203,
+ 184,
+ 212,
+ 222,
+ 227,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 508,
+ "armor": 3017,
+ "hp": 60701,
+ "intelligence": 3852,
+ "magicPower": 19876,
+ "magicResist": 3864,
+ "physicalAttack": 28,
+ "strength": 508
+ },
+ "items": [
+ 203,
+ 180,
+ 224,
+ 226,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 7,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1034,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 1005,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero34_deergirl",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0034",
+ "epicArtAsset": {
+ "name": "34_krista_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": -27,
+ "y": 17
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "34_Krista"
+ },
+ "role": "middle",
+ "obtainType": null,
+ "characterType": "cutie",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 1
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 488,
+ "1": 489,
+ "2": 490,
+ "3": 491,
+ "4": 492,
+ "7": 8280,
+ "8": 8281
+ }
+ },
+ "35": {
+ "id": 35,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 14,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 16
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 20
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 5,
+ 8,
+ 9,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 400,
+ "intelligence": 2,
+ "magicResist": 25,
+ "strength": 14
+ },
+ "items": [
+ 13,
+ 9,
+ 10,
+ 18,
+ 28,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 25,
+ "hp": 1170,
+ "intelligence": 5,
+ "magicPower": 50,
+ "magicResist": 100,
+ "strength": 28
+ },
+ "items": [
+ 18,
+ 19,
+ 26,
+ 28,
+ 33,
+ 24
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 25,
+ "hp": 2055,
+ "intelligence": 19,
+ "magicPower": 150,
+ "magicResist": 150,
+ "strength": 49
+ },
+ "items": [
+ 21,
+ 33,
+ 45,
+ 48,
+ 58,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 25,
+ "hp": 2055,
+ "intelligence": 41,
+ "magicPower": 484,
+ "magicResist": 242,
+ "strength": 73
+ },
+ "items": [
+ 22,
+ 36,
+ 33,
+ 56,
+ 58,
+ 69
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 105,
+ "hp": 3555,
+ "intelligence": 58,
+ "magicPower": 584,
+ "magicResist": 242,
+ "strength": 113
+ },
+ "items": [
+ 22,
+ 36,
+ 58,
+ 56,
+ 67,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 205,
+ "hp": 5855,
+ "intelligence": 70,
+ "magicPower": 844,
+ "magicResist": 322,
+ "strength": 125
+ },
+ "items": [
+ 74,
+ 64,
+ 67,
+ 85,
+ 93,
+ 94
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 40,
+ "armor": 205,
+ "hp": 7655,
+ "intelligence": 82,
+ "magicPower": 1124,
+ "magicResist": 482,
+ "strength": 195
+ },
+ "items": [
+ 100,
+ 69,
+ 85,
+ 88,
+ 94,
+ 123
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 47,
+ "armor": 385,
+ "hp": 11055,
+ "intelligence": 89,
+ "magicPower": 1204,
+ "magicResist": 682,
+ "strength": 296
+ },
+ "items": [
+ 64,
+ 69,
+ 69,
+ 85,
+ 131,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 78,
+ "armor": 545,
+ "hp": 15415,
+ "intelligence": 120,
+ "magicPower": 1716,
+ "magicResist": 762,
+ "strength": 399
+ },
+ "items": [
+ 88,
+ 69,
+ 131,
+ 91,
+ 123,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 104,
+ "armor": 725,
+ "hp": 23911,
+ "intelligence": 146,
+ "magicPower": 2615,
+ "magicResist": 762,
+ "strength": 501
+ },
+ "items": [
+ 119,
+ 123,
+ 131,
+ 135,
+ 167,
+ 170
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 130,
+ "armor": 725,
+ "hp": 34071,
+ "intelligence": 202,
+ "magicPower": 3287,
+ "magicResist": 762,
+ "strength": 696
+ },
+ "items": [
+ 91,
+ 119,
+ 135,
+ 170,
+ 176,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 132,
+ "armor": 725,
+ "hp": 41831,
+ "intelligence": 234,
+ "magicPower": 4919,
+ "magicResist": 1362,
+ "strength": 807
+ },
+ "items": [
+ 127,
+ 116,
+ 167,
+ 184,
+ 184,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 212,
+ "armor": 725,
+ "hp": 57431,
+ "intelligence": 344,
+ "magicPower": 5079,
+ "magicResist": 2162,
+ "strength": 1061
+ },
+ "items": [
+ 123,
+ 140,
+ 180,
+ 184,
+ 183,
+ 210
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 214,
+ "armor": 1045,
+ "hp": 78487,
+ "intelligence": 346,
+ "magicPower": 8106,
+ "magicResist": 2482,
+ "strength": 1267
+ },
+ "items": [
+ 135,
+ 140,
+ 180,
+ 184,
+ 180,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 292,
+ "armor": 1045,
+ "hp": 96343,
+ "intelligence": 424,
+ "magicPower": 11357,
+ "magicResist": 2802,
+ "strength": 1624
+ },
+ "items": [
+ 210,
+ 180,
+ 211,
+ 183,
+ 224,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 370,
+ "armor": 1365,
+ "hp": 118903,
+ "intelligence": 502,
+ "magicPower": 13565,
+ "magicResist": 4002,
+ "strength": 2155
+ },
+ "items": [
+ 180,
+ 183,
+ 211,
+ 221,
+ 224,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 448,
+ "armor": 1685,
+ "hp": 144023,
+ "intelligence": 580,
+ "magicPower": 17981,
+ "magicResist": 4002,
+ "strength": 2730
+ },
+ "items": [
+ 185,
+ 183,
+ 228,
+ 224,
+ 234,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 7,
+ 8,
+ 1
+ ],
+ "artifacts": [
+ 1035,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 1023,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero35_catooldan",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0035",
+ "epicArtAsset": {
+ "name": "35_jorgen_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.1,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": -38,
+ "y": 112
+ }
+ ]
+ },
+ "role": "middle",
+ "obtainType": "shop:boss",
+ "characterType": "demon",
+ "silhouette": "wide",
+ "ultCinematic": null,
+ "roleExtended": [
+ "control",
+ "support"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 8,
+ 5,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 174,
+ 175,
+ 176,
+ 177,
+ 178
+ ]
+ },
+ "36": {
+ "id": 36,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 20,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 4,
+ 7,
+ 7,
+ 11,
+ 9
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "hp": 200,
+ "intelligence": 12,
+ "magicPower": 50,
+ "magicResist": 25,
+ "strength": 3
+ },
+ "items": [
+ 7,
+ 9,
+ 11,
+ 19,
+ 26,
+ 34
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 11,
+ "hp": 200,
+ "intelligence": 38,
+ "magicPower": 175,
+ "magicResist": 50,
+ "strength": 11
+ },
+ "items": [
+ 11,
+ 19,
+ 26,
+ 28,
+ 46,
+ 48
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 700,
+ "intelligence": 67,
+ "magicPower": 409,
+ "magicResist": 142,
+ "strength": 14
+ },
+ "items": [
+ 40,
+ 45,
+ 46,
+ 48,
+ 56,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 2200,
+ "intelligence": 94,
+ "magicPower": 743,
+ "magicResist": 234,
+ "strength": 16
+ },
+ "items": [
+ 40,
+ 46,
+ 48,
+ 58,
+ 60,
+ 67
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 2700,
+ "intelligence": 121,
+ "magicPower": 1107,
+ "magicResist": 456,
+ "strength": 18
+ },
+ "items": [
+ 40,
+ 45,
+ 48,
+ 56,
+ 67,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 25,
+ "hp": 3700,
+ "intelligence": 163,
+ "magicPower": 1371,
+ "magicResist": 728,
+ "strength": 25
+ },
+ "items": [
+ 56,
+ 67,
+ 75,
+ 86,
+ 93,
+ 100
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 37,
+ "hp": 4700,
+ "intelligence": 195,
+ "magicPower": 1651,
+ "magicResist": 1108,
+ "strength": 37
+ },
+ "items": [
+ 67,
+ 86,
+ 86,
+ 93,
+ 100,
+ 119
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 49,
+ "hp": 4700,
+ "intelligence": 257,
+ "magicPower": 2091,
+ "magicResist": 1588,
+ "strength": 49
+ },
+ "items": [
+ 64,
+ 86,
+ 95,
+ 119,
+ 119,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 56,
+ "hp": 8060,
+ "intelligence": 372,
+ "magicPower": 2923,
+ "magicResist": 1768,
+ "strength": 56
+ },
+ "items": [
+ 86,
+ 95,
+ 100,
+ 115,
+ 116,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 63,
+ "hp": 12156,
+ "intelligence": 457,
+ "magicPower": 4062,
+ "magicResist": 2388,
+ "strength": 63
+ },
+ "items": [
+ 115,
+ 116,
+ 132,
+ 135,
+ 171,
+ 167
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 89,
+ "hp": 20716,
+ "intelligence": 652,
+ "magicPower": 4894,
+ "magicResist": 2708,
+ "strength": 89
+ },
+ "items": [
+ 119,
+ 132,
+ 135,
+ 171,
+ 169,
+ 184
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 115,
+ "hp": 28076,
+ "intelligence": 847,
+ "magicPower": 6166,
+ "magicResist": 3028,
+ "strength": 115
+ },
+ "items": [
+ 115,
+ 137,
+ 169,
+ 176,
+ 184,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 203,
+ "hp": 32876,
+ "intelligence": 1157,
+ "magicPower": 6926,
+ "magicResist": 4108,
+ "strength": 203
+ },
+ "items": [
+ 126,
+ 135,
+ 169,
+ 184,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 205,
+ "hp": 44396,
+ "intelligence": 1432,
+ "magicPower": 10854,
+ "magicResist": 4428,
+ "strength": 205
+ },
+ "items": [
+ 137,
+ 140,
+ 176,
+ 184,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 321,
+ "hp": 53292,
+ "intelligence": 2027,
+ "magicPower": 12633,
+ "magicResist": 5348,
+ "strength": 321
+ },
+ "items": [
+ 209,
+ 180,
+ 212,
+ 184,
+ 224,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 399,
+ "hp": 75852,
+ "intelligence": 2505,
+ "magicPower": 15129,
+ "magicResist": 6868,
+ "strength": 399
+ },
+ "items": [
+ 180,
+ 184,
+ 212,
+ 222,
+ 228,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 477,
+ "hp": 88972,
+ "intelligence": 3080,
+ "magicPower": 19545,
+ "magicResist": 8388,
+ "strength": 477
+ },
+ "items": [
+ 186,
+ 184,
+ 228,
+ 226,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 7,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1036,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 1001,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero36_flowey",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0036",
+ "epicArtAsset": {
+ "name": "36_maya_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.01,
+ 0.99
+ ],
+ "screen": "obtain",
+ "x": 2,
+ "y": 51.99999999999994
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "36_Maya"
+ },
+ "role": "middle",
+ "obtainType": "shop:arena",
+ "characterType": "healer",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "healer",
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 9,
+ 7,
+ 1
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 179,
+ 180,
+ 181,
+ 182,
+ 183
+ ]
+ },
+ "37": {
+ "id": 37,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 17
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 21
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 15,
+ 2,
+ 8,
+ 14,
+ 9
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "strength": 9
+ },
+ "items": [
+ 24,
+ 15,
+ 14,
+ 18,
+ 25,
+ 14
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 25,
+ "hp": 1085,
+ "intelligence": 4,
+ "magicResist": 25,
+ "physicalAttack": 120,
+ "strength": 25
+ },
+ "items": [
+ 14,
+ 42,
+ 43,
+ 25,
+ 27,
+ 18
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 125,
+ "hp": 1970,
+ "intelligence": 6,
+ "magicResist": 25,
+ "physicalAttack": 244,
+ "strength": 34
+ },
+ "items": [
+ 24,
+ 28,
+ 53,
+ 55,
+ 57,
+ 33
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 125,
+ "armorPenetration": 50,
+ "hp": 2470,
+ "intelligence": 13,
+ "magicResist": 75,
+ "physicalAttack": 380,
+ "physicalCritChance": 15,
+ "strength": 48
+ },
+ "items": [
+ 33,
+ 47,
+ 55,
+ 57,
+ 59,
+ 72
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 267,
+ "armorPenetration": 50,
+ "hp": 2890,
+ "intelligence": 20,
+ "magicResist": 75,
+ "physicalAttack": 567,
+ "physicalCritChance": 54,
+ "strength": 77
+ },
+ "items": [
+ 29,
+ 43,
+ 57,
+ 72,
+ 65,
+ 85
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 317,
+ "armorPenetration": 50,
+ "hp": 3890,
+ "intelligence": 27,
+ "magicResist": 155,
+ "physicalAttack": 782,
+ "physicalCritChance": 93,
+ "strength": 94
+ },
+ "items": [
+ 74,
+ 72,
+ 70,
+ 90,
+ 92,
+ 99
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 597,
+ "armorPenetration": 130,
+ "hp": 3890,
+ "intelligence": 34,
+ "magicResist": 155,
+ "physicalAttack": 1099,
+ "physicalCritChance": 117,
+ "strength": 127
+ },
+ "items": [
+ 92,
+ 65,
+ 90,
+ 97,
+ 101,
+ 123
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 677,
+ "armorPenetration": 330,
+ "hp": 5490,
+ "intelligence": 36,
+ "magicResist": 235,
+ "physicalAttack": 1360,
+ "physicalCritChance": 177,
+ "strength": 175
+ },
+ "items": [
+ 114,
+ 100,
+ 101,
+ 122,
+ 123,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 62,
+ "armor": 837,
+ "armorPenetration": 330,
+ "hp": 8690,
+ "intelligence": 62,
+ "magicResist": 435,
+ "physicalAttack": 1684,
+ "physicalCritChance": 237,
+ "strength": 261
+ },
+ "items": [
+ 101,
+ 114,
+ 122,
+ 120,
+ 125,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 132,
+ "armor": 997,
+ "armorPenetration": 330,
+ "hp": 10290,
+ "intelligence": 102,
+ "magicResist": 595,
+ "physicalAttack": 2332,
+ "physicalCritChance": 297,
+ "strength": 349
+ },
+ "items": [
+ 123,
+ 114,
+ 122,
+ 136,
+ 173,
+ 177
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 172,
+ "armor": 1157,
+ "armorPenetration": 930,
+ "hp": 13490,
+ "intelligence": 142,
+ "magicResist": 595,
+ "physicalAttack": 2656,
+ "physicalCritChance": 463,
+ "strength": 467
+ },
+ "items": [
+ 114,
+ 123,
+ 136,
+ 173,
+ 177,
+ 182
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 212,
+ "armor": 1157,
+ "armorPenetration": 1850,
+ "hp": 16690,
+ "intelligence": 182,
+ "magicResist": 595,
+ "physicalAttack": 3192,
+ "physicalCritChance": 629,
+ "strength": 585
+ },
+ "items": [
+ 122,
+ 122,
+ 173,
+ 177,
+ 188,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 262,
+ "armor": 1477,
+ "armorPenetration": 2450,
+ "hp": 16690,
+ "intelligence": 232,
+ "magicResist": 595,
+ "physicalAttack": 3728,
+ "physicalCritChance": 1023,
+ "strength": 809
+ },
+ "items": [
+ 114,
+ 131,
+ 170,
+ 170,
+ 201,
+ 207
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 288,
+ "armor": 1477,
+ "armorPenetration": 2450,
+ "hp": 23410,
+ "intelligence": 258,
+ "magicResist": 595,
+ "physicalAttack": 5742,
+ "physicalCritChance": 1417,
+ "strength": 1083
+ },
+ "items": [
+ 131,
+ 123,
+ 179,
+ 182,
+ 207,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 390,
+ "armor": 1477,
+ "armorPenetration": 2770,
+ "hp": 28210,
+ "intelligence": 360,
+ "magicResist": 595,
+ "physicalAttack": 7476,
+ "physicalCritChance": 1811,
+ "strength": 1524
+ },
+ "items": [
+ 207,
+ 188,
+ 211,
+ 184,
+ 224,
+ 225
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 468,
+ "armor": 1477,
+ "armorPenetration": 2770,
+ "hp": 45010,
+ "intelligence": 438,
+ "magicResist": 915,
+ "physicalAttack": 9370,
+ "physicalCritChance": 2433,
+ "strength": 1881
+ },
+ "items": [
+ 201,
+ 184,
+ 211,
+ 229,
+ 227,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 546,
+ "armor": 2677,
+ "armorPenetration": 3730,
+ "hp": 60050,
+ "intelligence": 516,
+ "magicResist": 1235,
+ "physicalAttack": 12058,
+ "physicalCritChance": 2766,
+ "strength": 2238
+ },
+ "items": [
+ 185,
+ 184,
+ 225,
+ 227,
+ 239,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 12,
+ 9,
+ 1
+ ],
+ "artifacts": [
+ 1037,
+ 2001,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 1015,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero37_boomerang",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0037",
+ "epicArtAsset": {
+ "name": "37_jhu_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": -34,
+ "y": 61.60000000000002
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "37_jhu"
+ },
+ "role": "middle",
+ "obtainType": "shop:clanWar",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "ranged_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 6,
+ 2
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 184,
+ 185,
+ 186,
+ 187,
+ 188
+ ]
+ },
+ "38": {
+ "id": 38,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 3,
+ 8,
+ 9,
+ 14,
+ 17
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 2
+ },
+ "items": [
+ 8,
+ 12,
+ 13,
+ 20,
+ 23,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 38,
+ "armor": 50,
+ "hp": 585,
+ "intelligence": 5,
+ "magicResist": 25,
+ "physicalAttack": 83,
+ "strength": 5
+ },
+ "items": [
+ 12,
+ 13,
+ 25,
+ 35,
+ 38,
+ 42
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 67,
+ "armor": 50,
+ "hp": 1470,
+ "intelligence": 13,
+ "magicResist": 25,
+ "physicalAttack": 182,
+ "strength": 13
+ },
+ "items": [
+ 27,
+ 44,
+ 50,
+ 53,
+ 57,
+ 62
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 84,
+ "armor": 150,
+ "armorPenetration": 50,
+ "dodge": 30,
+ "hp": 1470,
+ "intelligence": 15,
+ "magicResist": 117,
+ "physicalAttack": 341,
+ "strength": 15
+ },
+ "items": [
+ 39,
+ 43,
+ 43,
+ 57,
+ 62,
+ 70
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 96,
+ "armor": 250,
+ "armorPenetration": 130,
+ "dodge": 60,
+ "hp": 1470,
+ "intelligence": 17,
+ "magicResist": 167,
+ "physicalAttack": 533,
+ "strength": 17
+ },
+ "items": [
+ 44,
+ 53,
+ 62,
+ 70,
+ 70,
+ 87
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 113,
+ "armor": 300,
+ "armorPenetration": 340,
+ "dodge": 90,
+ "hp": 1470,
+ "intelligence": 24,
+ "magicResist": 217,
+ "physicalAttack": 748,
+ "strength": 24
+ },
+ "items": [
+ 59,
+ 64,
+ 87,
+ 70,
+ 96,
+ 102
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 168,
+ "armor": 400,
+ "armorPenetration": 420,
+ "dodge": 150,
+ "hp": 2270,
+ "intelligence": 31,
+ "magicResist": 297,
+ "physicalAttack": 874,
+ "strength": 31
+ },
+ "items": [
+ 57,
+ 64,
+ 90,
+ 96,
+ 102,
+ 118
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 238,
+ "armor": 480,
+ "armorPenetration": 580,
+ "dodge": 210,
+ "hp": 3070,
+ "intelligence": 33,
+ "magicResist": 377,
+ "physicalAttack": 1122,
+ "strength": 49
+ },
+ "items": [
+ 73,
+ 87,
+ 97,
+ 118,
+ 122,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 339,
+ "armor": 640,
+ "armorPenetration": 940,
+ "dodge": 234,
+ "hp": 3870,
+ "intelligence": 64,
+ "magicResist": 377,
+ "physicalAttack": 1408,
+ "strength": 80
+ },
+ "items": [
+ 73,
+ 96,
+ 102,
+ 120,
+ 133,
+ 139
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 463,
+ "armor": 640,
+ "armorPenetration": 940,
+ "dodge": 318,
+ "hp": 4670,
+ "intelligence": 90,
+ "magicResist": 786,
+ "physicalAttack": 2068,
+ "strength": 106
+ },
+ "items": [
+ 102,
+ 122,
+ 138,
+ 138,
+ 172,
+ 173
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 746,
+ "armor": 800,
+ "armorPenetration": 1540,
+ "dodge": 378,
+ "hp": 4670,
+ "intelligence": 168,
+ "magicResist": 786,
+ "physicalAttack": 2176,
+ "strength": 184
+ },
+ "items": [
+ 133,
+ 120,
+ 122,
+ 172,
+ 168,
+ 189
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 941,
+ "armor": 960,
+ "armorPenetration": 1540,
+ "dodge": 606,
+ "hp": 9470,
+ "intelligence": 194,
+ "magicResist": 786,
+ "physicalAttack": 2792,
+ "strength": 210
+ },
+ "items": [
+ 120,
+ 122,
+ 168,
+ 173,
+ 179,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1195,
+ "armor": 1120,
+ "armorPenetration": 2140,
+ "dodge": 606,
+ "hp": 12670,
+ "intelligence": 244,
+ "magicResist": 786,
+ "physicalAttack": 4048,
+ "strength": 260
+ },
+ "items": [
+ 133,
+ 138,
+ 172,
+ 182,
+ 179,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1446,
+ "armor": 1120,
+ "armorPenetration": 2460,
+ "dodge": 606,
+ "hp": 22526,
+ "intelligence": 308,
+ "magicResist": 786,
+ "physicalAttack": 6339,
+ "strength": 324
+ },
+ "items": [
+ 139,
+ 118,
+ 182,
+ 182,
+ 201,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1833,
+ "armor": 1120,
+ "armorPenetration": 3260,
+ "dodge": 606,
+ "hp": 27646,
+ "intelligence": 386,
+ "magicResist": 1195,
+ "physicalAttack": 8663,
+ "strength": 402
+ },
+ "items": [
+ 189,
+ 208,
+ 213,
+ 184,
+ 227,
+ 225
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2190,
+ "armor": 2320,
+ "armorPenetration": 3260,
+ "dodge": 834,
+ "hp": 43902,
+ "intelligence": 464,
+ "magicResist": 1515,
+ "physicalAttack": 10794,
+ "strength": 480
+ },
+ "items": [
+ 208,
+ 184,
+ 187,
+ 230,
+ 223,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2632,
+ "armor": 2320,
+ "armorPenetration": 4220,
+ "dodge": 1167,
+ "hp": 60478,
+ "intelligence": 514,
+ "magicResist": 1835,
+ "physicalAttack": 13789,
+ "strength": 530
+ },
+ "items": [
+ 187,
+ 189,
+ 225,
+ 223,
+ 239,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 12,
+ 10,
+ 3
+ ],
+ "artifacts": [
+ 1038,
+ 2004,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 24,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero38_sandphantom",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0038",
+ "epicArtAsset": {
+ "name": "38_elmir_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.1,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 68,
+ "y": 91
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": "shop:boss",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_dps",
+ "ranged_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 10,
+ 6,
+ 2
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 189,
+ 190,
+ 191,
+ 192,
+ 193
+ ]
+ },
+ "39": {
+ "id": 39,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 25
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 8,
+ 9,
+ 14,
+ 15,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 16
+ },
+ "items": [
+ 13,
+ 14,
+ 10,
+ 18,
+ 25,
+ 27
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 75,
+ "hp": 970,
+ "intelligence": 5,
+ "magicResist": 25,
+ "physicalAttack": 83,
+ "strength": 30
+ },
+ "items": [
+ 10,
+ 18,
+ 25,
+ 28,
+ 37,
+ 43
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 175,
+ "hp": 1355,
+ "intelligence": 8,
+ "magicResist": 75,
+ "physicalAttack": 149,
+ "strength": 54
+ },
+ "items": [
+ 33,
+ 28,
+ 37,
+ 43,
+ 57,
+ 59
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 375,
+ "hp": 1355,
+ "intelligence": 15,
+ "magicResist": 125,
+ "physicalAttack": 252,
+ "strength": 78
+ },
+ "items": [
+ 36,
+ 44,
+ 47,
+ 57,
+ 59,
+ 69
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 647,
+ "hp": 2275,
+ "intelligence": 17,
+ "magicResist": 175,
+ "physicalAttack": 350,
+ "strength": 121
+ },
+ "items": [
+ 36,
+ 42,
+ 57,
+ 65,
+ 59,
+ 90
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 827,
+ "hp": 3275,
+ "intelligence": 19,
+ "magicResist": 255,
+ "physicalAttack": 579,
+ "strength": 149
+ },
+ "items": [
+ 59,
+ 64,
+ 69,
+ 85,
+ 92,
+ 94
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 1007,
+ "hp": 5075,
+ "intelligence": 26,
+ "magicResist": 335,
+ "physicalAttack": 714,
+ "strength": 220
+ },
+ "items": [
+ 60,
+ 69,
+ 90,
+ 94,
+ 99,
+ 114
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 1367,
+ "hp": 6675,
+ "intelligence": 28,
+ "magicResist": 435,
+ "physicalAttack": 1000,
+ "strength": 292
+ },
+ "items": [
+ 65,
+ 90,
+ 92,
+ 122,
+ 114,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 54,
+ "armor": 1607,
+ "hp": 8275,
+ "intelligence": 54,
+ "magicResist": 515,
+ "physicalAttack": 1585,
+ "strength": 364
+ },
+ "items": [
+ 69,
+ 99,
+ 122,
+ 114,
+ 136,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 132,
+ "armor": 2047,
+ "hp": 9875,
+ "intelligence": 132,
+ "magicResist": 515,
+ "physicalAttack": 1909,
+ "strength": 554
+ },
+ "items": [
+ 114,
+ 122,
+ 134,
+ 131,
+ 170,
+ 175
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 158,
+ "armor": 2807,
+ "hp": 11475,
+ "intelligence": 158,
+ "magicResist": 771,
+ "physicalAttack": 2578,
+ "strength": 719
+ },
+ "items": [
+ 122,
+ 125,
+ 136,
+ 168,
+ 183,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 198,
+ "armor": 3607,
+ "hp": 21075,
+ "intelligence": 198,
+ "magicResist": 931,
+ "physicalAttack": 3302,
+ "strength": 807
+ },
+ "items": [
+ 123,
+ 131,
+ 167,
+ 183,
+ 175,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 272,
+ "armor": 4527,
+ "hp": 33475,
+ "intelligence": 272,
+ "magicResist": 931,
+ "physicalAttack": 3302,
+ "strength": 1115
+ },
+ "items": [
+ 125,
+ 136,
+ 183,
+ 168,
+ 179,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 312,
+ "armor": 4847,
+ "hp": 48131,
+ "intelligence": 312,
+ "magicResist": 1091,
+ "physicalAttack": 5889,
+ "strength": 1203
+ },
+ "items": [
+ 136,
+ 183,
+ 167,
+ 179,
+ 201,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 428,
+ "armor": 5167,
+ "hp": 67251,
+ "intelligence": 428,
+ "magicResist": 1091,
+ "physicalAttack": 7553,
+ "strength": 1646
+ },
+ "items": [
+ 179,
+ 208,
+ 211,
+ 184,
+ 227,
+ 224
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 506,
+ "armor": 6367,
+ "hp": 93907,
+ "intelligence": 506,
+ "magicResist": 1411,
+ "physicalAttack": 9524,
+ "strength": 2003
+ },
+ "items": [
+ 208,
+ 184,
+ 185,
+ 228,
+ 221,
+ 237
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 556,
+ "armor": 8287,
+ "hp": 110483,
+ "intelligence": 556,
+ "magicResist": 2931,
+ "physicalAttack": 11879,
+ "strength": 2445
+ },
+ "items": [
+ 185,
+ 179,
+ 225,
+ 221,
+ 237,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 8,
+ 7,
+ 1
+ ],
+ "artifacts": [
+ 1039,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 8,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero39_scorpio",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0039",
+ "epicArtAsset": {
+ "name": "39_ziri_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.2,
+ 1.2
+ ],
+ "screen": "obtain",
+ "x": 140,
+ "y": 21
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "39_Ziri"
+ },
+ "role": "front",
+ "obtainType": "shop:clanWar",
+ "characterType": "cutie",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_tank"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 4,
+ 1,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 194,
+ 195,
+ 196,
+ 197,
+ 198
+ ]
+ },
+ "40": {
+ "id": 40,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 50,
+ "strength": 10
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 3,
+ 8,
+ 9,
+ 14,
+ 17
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 2
+ },
+ "items": [
+ 8,
+ 12,
+ 13,
+ 20,
+ 23,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 38,
+ "armor": 50,
+ "hp": 585,
+ "intelligence": 5,
+ "magicResist": 25,
+ "physicalAttack": 83,
+ "strength": 5
+ },
+ "items": [
+ 12,
+ 13,
+ 27,
+ 35,
+ 38,
+ 42
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 67,
+ "armor": 100,
+ "hp": 1470,
+ "intelligence": 13,
+ "magicResist": 25,
+ "physicalAttack": 149,
+ "strength": 13
+ },
+ "items": [
+ 27,
+ 44,
+ 50,
+ 42,
+ 57,
+ 62
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 84,
+ "armor": 200,
+ "dodge": 30,
+ "hp": 1970,
+ "intelligence": 15,
+ "magicResist": 117,
+ "physicalAttack": 308,
+ "strength": 15
+ },
+ "items": [
+ 39,
+ 43,
+ 54,
+ 57,
+ 62,
+ 66
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 112,
+ "armor": 250,
+ "dodge": 75,
+ "hp": 2470,
+ "intelligence": 17,
+ "magicResist": 167,
+ "physicalAttack": 467,
+ "strength": 17
+ },
+ "items": [
+ 44,
+ 51,
+ 62,
+ 66,
+ 73,
+ 87
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 155,
+ "armor": 300,
+ "dodge": 129,
+ "hp": 3270,
+ "intelligence": 34,
+ "magicResist": 217,
+ "physicalAttack": 593,
+ "strength": 34
+ },
+ "items": [
+ 59,
+ 64,
+ 87,
+ 66,
+ 96,
+ 102
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 226,
+ "armor": 400,
+ "dodge": 189,
+ "hp": 4070,
+ "intelligence": 41,
+ "magicResist": 297,
+ "physicalAttack": 719,
+ "strength": 41
+ },
+ "items": [
+ 57,
+ 64,
+ 90,
+ 96,
+ 102,
+ 114
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 266,
+ "armor": 480,
+ "dodge": 249,
+ "hp": 6470,
+ "intelligence": 43,
+ "magicResist": 377,
+ "physicalAttack": 1075,
+ "strength": 59
+ },
+ "items": [
+ 73,
+ 87,
+ 99,
+ 114,
+ 122,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 337,
+ "armor": 840,
+ "dodge": 273,
+ "hp": 8870,
+ "intelligence": 74,
+ "magicResist": 377,
+ "physicalAttack": 1469,
+ "strength": 90
+ },
+ "items": [
+ 73,
+ 96,
+ 102,
+ 120,
+ 133,
+ 139
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 461,
+ "armor": 840,
+ "dodge": 357,
+ "hp": 9670,
+ "intelligence": 100,
+ "magicResist": 786,
+ "physicalAttack": 2129,
+ "strength": 116
+ },
+ "items": [
+ 102,
+ 122,
+ 138,
+ 134,
+ 178,
+ 168
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 549,
+ "armor": 1000,
+ "dodge": 583,
+ "hp": 9670,
+ "intelligence": 140,
+ "magicResist": 1042,
+ "physicalAttack": 2982,
+ "strength": 156
+ },
+ "items": [
+ 133,
+ 120,
+ 122,
+ 178,
+ 168,
+ 189
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 635,
+ "armor": 1160,
+ "dodge": 977,
+ "hp": 14470,
+ "intelligence": 166,
+ "magicResist": 1042,
+ "physicalAttack": 3598,
+ "strength": 182
+ },
+ "items": [
+ 120,
+ 122,
+ 168,
+ 178,
+ 189,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 889,
+ "armor": 1320,
+ "dodge": 1371,
+ "hp": 19270,
+ "intelligence": 216,
+ "magicResist": 1042,
+ "physicalAttack": 4214,
+ "strength": 232
+ },
+ "items": [
+ 131,
+ 134,
+ 168,
+ 175,
+ 179,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 915,
+ "armor": 1920,
+ "dodge": 1371,
+ "hp": 29126,
+ "intelligence": 242,
+ "magicResist": 1298,
+ "physicalAttack": 6930,
+ "strength": 288
+ },
+ "items": [
+ 134,
+ 139,
+ 179,
+ 184,
+ 189,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1272,
+ "armor": 1920,
+ "dodge": 1599,
+ "hp": 41926,
+ "intelligence": 320,
+ "magicResist": 2283,
+ "physicalAttack": 8467,
+ "strength": 366
+ },
+ "items": [
+ 179,
+ 208,
+ 213,
+ 184,
+ 227,
+ 230
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 1629,
+ "armor": 3120,
+ "dodge": 1932,
+ "hp": 56582,
+ "intelligence": 398,
+ "magicResist": 2603,
+ "physicalAttack": 10438,
+ "strength": 444
+ },
+ "items": [
+ 189,
+ 183,
+ 213,
+ 228,
+ 225,
+ 235
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 1986,
+ "armor": 3440,
+ "dodge": 2524,
+ "hp": 73862,
+ "intelligence": 476,
+ "magicResist": 3803,
+ "physicalAttack": 12518,
+ "strength": 522
+ },
+ "items": [
+ 187,
+ 183,
+ 225,
+ 223,
+ 235,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 7,
+ 8,
+ 4,
+ 3
+ ],
+ "artifacts": [
+ 1040,
+ 2002,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 1016,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero40_space_balls",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0040",
+ "epicArtAsset": {
+ "name": "40_nebula_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": -25,
+ "y": 45
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "40_Nebula"
+ },
+ "role": "middle",
+ "obtainType": "shop:titanTokenShop",
+ "characterType": "cutie",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "support",
+ "healer"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 5,
+ 9,
+ 1
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 199,
+ "1": 200,
+ "2": 201,
+ "3": 202,
+ "4": 203,
+ "7": 8244,
+ "8": 8245
+ }
+ },
+ "41": {
+ "id": 41,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 6,
+ 8,
+ 9,
+ 14
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 7,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "strength": 7
+ },
+ "items": [
+ 8,
+ 9,
+ 20,
+ 20,
+ 23,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 33,
+ "armor": 50,
+ "hp": 200,
+ "intelligence": 9,
+ "magicResist": 50,
+ "physicalAttack": 120,
+ "strength": 9
+ },
+ "items": [
+ 12,
+ 20,
+ 27,
+ 28,
+ 38,
+ 38
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 67,
+ "armor": 100,
+ "hp": 200,
+ "intelligence": 12,
+ "magicResist": 100,
+ "physicalAttack": 211,
+ "strength": 12
+ },
+ "items": [
+ 31,
+ 53,
+ 42,
+ 53,
+ 56,
+ 57
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 69,
+ "armor": 100,
+ "armorPenetration": 150,
+ "hp": 1700,
+ "intelligence": 14,
+ "magicResist": 100,
+ "physicalAttack": 380,
+ "strength": 14
+ },
+ "items": [
+ 39,
+ 50,
+ 53,
+ 57,
+ 59,
+ 76
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 111,
+ "armor": 200,
+ "armorPenetration": 200,
+ "hp": 1700,
+ "intelligence": 21,
+ "magicResist": 192,
+ "physicalAttack": 539,
+ "strength": 21
+ },
+ "items": [
+ 24,
+ 44,
+ 53,
+ 70,
+ 76,
+ 90
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 128,
+ "armor": 330,
+ "armorPenetration": 330,
+ "hp": 2200,
+ "intelligence": 28,
+ "magicResist": 242,
+ "physicalAttack": 698,
+ "strength": 44
+ },
+ "items": [
+ 65,
+ 66,
+ 70,
+ 76,
+ 90,
+ 97
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 161,
+ "armor": 410,
+ "armorPenetration": 610,
+ "hp": 2200,
+ "intelligence": 35,
+ "magicResist": 322,
+ "physicalAttack": 936,
+ "strength": 67
+ },
+ "items": [
+ 118,
+ 64,
+ 70,
+ 76,
+ 97,
+ 122
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 208,
+ "armor": 570,
+ "armorPenetration": 1050,
+ "hp": 3000,
+ "intelligence": 42,
+ "magicResist": 402,
+ "physicalAttack": 1208,
+ "strength": 74
+ },
+ "items": [
+ 87,
+ 70,
+ 90,
+ 118,
+ 120,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 339,
+ "armor": 650,
+ "armorPenetration": 1290,
+ "hp": 3000,
+ "intelligence": 73,
+ "magicResist": 402,
+ "physicalAttack": 1620,
+ "strength": 121
+ },
+ "items": [
+ 76,
+ 92,
+ 114,
+ 118,
+ 114,
+ 138
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 472,
+ "armor": 650,
+ "armorPenetration": 1450,
+ "hp": 6200,
+ "intelligence": 118,
+ "magicResist": 402,
+ "physicalAttack": 2295,
+ "strength": 166
+ },
+ "items": [
+ 118,
+ 125,
+ 114,
+ 133,
+ 172,
+ 168
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 667,
+ "armor": 650,
+ "armorPenetration": 1610,
+ "hp": 7800,
+ "intelligence": 144,
+ "magicResist": 562,
+ "physicalAttack": 3235,
+ "strength": 192
+ },
+ "items": [
+ 114,
+ 120,
+ 138,
+ 172,
+ 168,
+ 182
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 894,
+ "armor": 650,
+ "armorPenetration": 1930,
+ "hp": 9400,
+ "intelligence": 184,
+ "magicResist": 562,
+ "physicalAttack": 4279,
+ "strength": 232
+ },
+ "items": [
+ 114,
+ 120,
+ 168,
+ 183,
+ 182,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1148,
+ "armor": 970,
+ "armorPenetration": 2250,
+ "hp": 15800,
+ "intelligence": 234,
+ "magicResist": 562,
+ "physicalAttack": 5323,
+ "strength": 282
+ },
+ "items": [
+ 114,
+ 118,
+ 179,
+ 182,
+ 179,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1180,
+ "armor": 970,
+ "armorPenetration": 2730,
+ "hp": 30456,
+ "intelligence": 236,
+ "magicResist": 562,
+ "physicalAttack": 8578,
+ "strength": 284
+ },
+ "items": [
+ 138,
+ 139,
+ 173,
+ 168,
+ 201,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1623,
+ "armor": 970,
+ "armorPenetration": 3330,
+ "hp": 35576,
+ "intelligence": 352,
+ "magicResist": 971,
+ "physicalAttack": 10554,
+ "strength": 400
+ },
+ "items": [
+ 179,
+ 208,
+ 213,
+ 184,
+ 227,
+ 231
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 1980,
+ "armor": 2170,
+ "armorPenetration": 4530,
+ "hp": 50232,
+ "intelligence": 430,
+ "magicResist": 1291,
+ "physicalAttack": 12525,
+ "strength": 478
+ },
+ "items": [
+ 183,
+ 179,
+ 213,
+ 228,
+ 225,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2337,
+ "armor": 2490,
+ "armorPenetration": 5490,
+ "hp": 63352,
+ "intelligence": 508,
+ "magicResist": 2491,
+ "physicalAttack": 15629,
+ "strength": 556
+ },
+ "items": [
+ 187,
+ 182,
+ 225,
+ 223,
+ 239,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 12,
+ 7,
+ 3
+ ],
+ "artifacts": [
+ 1041,
+ 2004,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 20,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero41_tentacle",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0041",
+ "epicArtAsset": {
+ "name": "41_k_arkh_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.11,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 72,
+ "y": 104
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": "shop:titanTokenShop",
+ "characterType": "warrior",
+ "silhouette": "flying",
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 10,
+ 2,
+ 21
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 204,
+ 205,
+ 206,
+ 207,
+ 208
+ ]
+ },
+ "42": {
+ "id": 42,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 19
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 24
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 6,
+ 7,
+ 8,
+ 9,
+ 13
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 7,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 15,
+ 13,
+ 18,
+ 19,
+ 24,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 25,
+ "hp": 1855,
+ "intelligence": 16,
+ "magicPower": 125,
+ "magicResist": 25,
+ "strength": 23
+ },
+ "items": [
+ 18,
+ 13,
+ 26,
+ 28,
+ 36,
+ 45
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 25,
+ "hp": 3125,
+ "intelligence": 18,
+ "magicPower": 225,
+ "magicResist": 125,
+ "strength": 42
+ },
+ "items": [
+ 24,
+ 36,
+ 45,
+ 46,
+ 59,
+ 60
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 125,
+ "hp": 4625,
+ "intelligence": 20,
+ "magicPower": 325,
+ "magicResist": 275,
+ "strength": 54
+ },
+ "items": [
+ 26,
+ 33,
+ 46,
+ 56,
+ 63,
+ 64
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 125,
+ "hp": 7725,
+ "intelligence": 27,
+ "magicPower": 505,
+ "magicResist": 355,
+ "strength": 68
+ },
+ "items": [
+ 37,
+ 45,
+ 59,
+ 60,
+ 63,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 375,
+ "hp": 9325,
+ "intelligence": 29,
+ "magicPower": 715,
+ "magicResist": 505,
+ "strength": 80
+ },
+ "items": [
+ 67,
+ 58,
+ 85,
+ 88,
+ 91,
+ 94
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 29,
+ "armor": 475,
+ "hp": 13125,
+ "intelligence": 36,
+ "magicPower": 975,
+ "magicResist": 585,
+ "strength": 135
+ },
+ "items": [
+ 67,
+ 63,
+ 64,
+ 74,
+ 126,
+ 115
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 475,
+ "hp": 16325,
+ "intelligence": 43,
+ "magicPower": 1615,
+ "magicResist": 905,
+ "strength": 152
+ },
+ "items": [
+ 64,
+ 88,
+ 85,
+ 116,
+ 126,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 67,
+ "armor": 575,
+ "hp": 20525,
+ "intelligence": 104,
+ "magicPower": 2175,
+ "magicResist": 1145,
+ "strength": 223
+ },
+ "items": [
+ 88,
+ 85,
+ 115,
+ 123,
+ 135,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 112,
+ "armor": 675,
+ "hp": 26485,
+ "intelligence": 149,
+ "magicPower": 2927,
+ "magicResist": 1305,
+ "strength": 356
+ },
+ "items": [
+ 115,
+ 119,
+ 126,
+ 136,
+ 167,
+ 170
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 152,
+ "armor": 675,
+ "hp": 34085,
+ "intelligence": 219,
+ "magicPower": 3567,
+ "magicResist": 1465,
+ "strength": 553
+ },
+ "items": [
+ 127,
+ 123,
+ 135,
+ 170,
+ 169,
+ 184
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 184,
+ "armor": 675,
+ "hp": 43045,
+ "intelligence": 251,
+ "magicPower": 4679,
+ "magicResist": 1785,
+ "strength": 724
+ },
+ "items": [
+ 123,
+ 136,
+ 167,
+ 169,
+ 184,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 272,
+ "armor": 675,
+ "hp": 55445,
+ "intelligence": 339,
+ "magicPower": 5279,
+ "magicResist": 2105,
+ "strength": 1064
+ },
+ "items": [
+ 116,
+ 123,
+ 167,
+ 180,
+ 203,
+ 210
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 274,
+ "armor": 675,
+ "hp": 68805,
+ "intelligence": 523,
+ "magicPower": 8607,
+ "magicResist": 2265,
+ "strength": 1270
+ },
+ "items": [
+ 115,
+ 126,
+ 184,
+ 180,
+ 210,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 352,
+ "armor": 675,
+ "hp": 80965,
+ "intelligence": 601,
+ "magicPower": 11295,
+ "magicResist": 2745,
+ "strength": 1801
+ },
+ "items": [
+ 180,
+ 183,
+ 211,
+ 210,
+ 227,
+ 224
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 430,
+ "armor": 2195,
+ "hp": 103525,
+ "intelligence": 679,
+ "magicPower": 13503,
+ "magicResist": 2745,
+ "strength": 2332
+ },
+ "items": [
+ 183,
+ 180,
+ 211,
+ 228,
+ 224,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 508,
+ "armor": 2515,
+ "hp": 128645,
+ "intelligence": 757,
+ "magicPower": 17919,
+ "magicResist": 3945,
+ "strength": 2689
+ },
+ "items": [
+ 185,
+ 180,
+ 224,
+ 228,
+ 234,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 8,
+ 7,
+ 1
+ ],
+ "artifacts": [
+ 1042,
+ 2005,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 9,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero42_fatty",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0042",
+ "epicArtAsset": {
+ "name": "42_rufus_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.1,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": -68,
+ "y": 112
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": "shop:grandArena",
+ "characterType": "cutie",
+ "silhouette": "wide",
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_tank"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 4,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 209,
+ 210,
+ 211,
+ 212,
+ 213
+ ]
+ },
+ "43": {
+ "id": 43,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 20,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 4,
+ 7,
+ 9,
+ 16,
+ 13,
+ 16
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 385,
+ "intelligence": 21,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 2
+ },
+ "items": [
+ 11,
+ 16,
+ 13,
+ 19,
+ 24,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "hp": 1270,
+ "intelligence": 42,
+ "magicPower": 125,
+ "magicResist": 25,
+ "strength": 5
+ },
+ "items": [
+ 19,
+ 13,
+ 40,
+ 28,
+ 24,
+ 40
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 2155,
+ "intelligence": 71,
+ "magicPower": 275,
+ "magicResist": 75,
+ "strength": 7
+ },
+ "items": [
+ 40,
+ 22,
+ 45,
+ 40,
+ 56,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "hp": 3155,
+ "intelligence": 103,
+ "magicPower": 525,
+ "magicResist": 125,
+ "strength": 9
+ },
+ "items": [
+ 52,
+ 40,
+ 52,
+ 56,
+ 58,
+ 67
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 11,
+ "hp": 4155,
+ "intelligence": 135,
+ "magicPenetration": 100,
+ "magicPower": 755,
+ "magicResist": 205,
+ "strength": 11
+ },
+ "items": [
+ 52,
+ 40,
+ 58,
+ 56,
+ 68,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 80,
+ "hp": 5155,
+ "intelligence": 172,
+ "magicPenetration": 150,
+ "magicPower": 985,
+ "magicResist": 305,
+ "strength": 18
+ },
+ "items": [
+ 63,
+ 58,
+ 88,
+ 71,
+ 91,
+ 95
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 180,
+ "hp": 8755,
+ "intelligence": 212,
+ "magicPenetration": 230,
+ "magicPower": 1325,
+ "magicResist": 305,
+ "strength": 20
+ },
+ "items": [
+ 68,
+ 71,
+ 88,
+ 91,
+ 95,
+ 116
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 360,
+ "hp": 11555,
+ "intelligence": 282,
+ "magicPenetration": 310,
+ "magicPower": 1725,
+ "magicResist": 465,
+ "strength": 22
+ },
+ "items": [
+ 88,
+ 67,
+ 95,
+ 98,
+ 116,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 460,
+ "hp": 14915,
+ "intelligence": 352,
+ "magicPenetration": 510,
+ "magicPower": 2557,
+ "magicResist": 705,
+ "strength": 24
+ },
+ "items": [
+ 75,
+ 88,
+ 119,
+ 98,
+ 132,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 55,
+ "armor": 560,
+ "hp": 19811,
+ "intelligence": 453,
+ "magicPenetration": 710,
+ "magicPower": 3616,
+ "magicResist": 705,
+ "strength": 55
+ },
+ "items": [
+ 119,
+ 126,
+ 137,
+ 117,
+ 167,
+ 171
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 95,
+ "armor": 560,
+ "hp": 27411,
+ "intelligence": 710,
+ "magicPenetration": 870,
+ "magicPower": 4256,
+ "magicResist": 705,
+ "strength": 95
+ },
+ "items": [
+ 119,
+ 100,
+ 132,
+ 174,
+ 171,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 121,
+ "armor": 560,
+ "hp": 30611,
+ "intelligence": 905,
+ "magicPenetration": 1470,
+ "magicPower": 5376,
+ "magicResist": 905,
+ "strength": 121
+ },
+ "items": [
+ 135,
+ 119,
+ 181,
+ 174,
+ 169,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 171,
+ "armor": 560,
+ "hp": 33171,
+ "intelligence": 1159,
+ "magicPenetration": 2390,
+ "magicPower": 7128,
+ "magicResist": 905,
+ "strength": 171
+ },
+ "items": [
+ 117,
+ 132,
+ 184,
+ 174,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 197,
+ "armor": 560,
+ "hp": 40531,
+ "intelligence": 1518,
+ "magicPenetration": 3150,
+ "magicPower": 9784,
+ "magicResist": 1225,
+ "strength": 197
+ },
+ "items": [
+ 140,
+ 135,
+ 181,
+ 184,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 275,
+ "armor": 560,
+ "hp": 51987,
+ "intelligence": 2027,
+ "magicPenetration": 3470,
+ "magicPower": 12555,
+ "magicResist": 1545,
+ "strength": 275
+ },
+ "items": [
+ 183,
+ 212,
+ 180,
+ 209,
+ 228,
+ 224
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 353,
+ "armor": 880,
+ "hp": 74547,
+ "intelligence": 2505,
+ "magicPenetration": 3470,
+ "magicPower": 15051,
+ "magicResist": 2745,
+ "strength": 353
+ },
+ "items": [
+ 183,
+ 212,
+ 203,
+ 222,
+ 224,
+ 233
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 431,
+ "armor": 1200,
+ "hp": 91347,
+ "intelligence": 3232,
+ "magicPenetration": 4238,
+ "magicPower": 19083,
+ "magicResist": 2745,
+ "strength": 431
+ },
+ "items": [
+ 184,
+ 203,
+ 224,
+ 232,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 8,
+ 11,
+ 2
+ ],
+ "artifacts": [
+ 1043,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 1011,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero43_daynight",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0043_1",
+ "epicArtAsset": {
+ "name": "43_celeste_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.1,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 64,
+ "y": 85
+ }
+ ]
+ },
+ "role": "middle",
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "flying",
+ "ultCinematic": null,
+ "roleExtended": [
+ "healer",
+ "control"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 9,
+ 1,
+ 21
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 214,
+ 215,
+ 216,
+ 217,
+ 218
+ ]
+ },
+ "44": {
+ "id": 44,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 16
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 8,
+ 14,
+ 13,
+ 20
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 2,
+ "physicalAttack": 62,
+ "strength": 2
+ },
+ "items": [
+ 8,
+ 13,
+ 12,
+ 20,
+ 23,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 33,
+ "armor": 50,
+ "hp": 970,
+ "intelligence": 5,
+ "physicalAttack": 120,
+ "strength": 5
+ },
+ "items": [
+ 9,
+ 20,
+ 24,
+ 27,
+ 38,
+ 35
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 64,
+ "armor": 100,
+ "hp": 1470,
+ "intelligence": 12,
+ "magicResist": 25,
+ "physicalAttack": 178,
+ "strength": 12
+ },
+ "items": [
+ 31,
+ 42,
+ 53,
+ 43,
+ 56,
+ 57
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 66,
+ "armor": 150,
+ "armorPenetration": 100,
+ "hp": 2970,
+ "intelligence": 14,
+ "magicResist": 25,
+ "physicalAttack": 347,
+ "strength": 14
+ },
+ "items": [
+ 39,
+ 38,
+ 53,
+ 57,
+ 56,
+ 76
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 103,
+ "armor": 150,
+ "armorPenetration": 150,
+ "hp": 3970,
+ "intelligence": 21,
+ "magicResist": 75,
+ "physicalAttack": 483,
+ "strength": 21
+ },
+ "items": [
+ 24,
+ 44,
+ 53,
+ 70,
+ 76,
+ 90
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 120,
+ "armor": 280,
+ "armorPenetration": 280,
+ "hp": 4470,
+ "intelligence": 28,
+ "magicResist": 125,
+ "physicalAttack": 642,
+ "strength": 44
+ },
+ "items": [
+ 64,
+ 66,
+ 70,
+ 76,
+ 99,
+ 96
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 191,
+ "armor": 480,
+ "armorPenetration": 360,
+ "hp": 5270,
+ "intelligence": 35,
+ "magicResist": 205,
+ "physicalAttack": 754,
+ "strength": 51
+ },
+ "items": [
+ 66,
+ 64,
+ 76,
+ 70,
+ 120,
+ 118
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 284,
+ "armor": 480,
+ "armorPenetration": 600,
+ "hp": 6070,
+ "intelligence": 42,
+ "magicResist": 285,
+ "physicalAttack": 1082,
+ "strength": 58
+ },
+ "items": [
+ 76,
+ 70,
+ 90,
+ 118,
+ 114,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 385,
+ "armor": 560,
+ "armorPenetration": 840,
+ "hp": 7670,
+ "intelligence": 73,
+ "magicResist": 285,
+ "physicalAttack": 1532,
+ "strength": 105
+ },
+ "items": [
+ 70,
+ 87,
+ 114,
+ 118,
+ 125,
+ 138
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 518,
+ "armor": 560,
+ "armorPenetration": 1080,
+ "hp": 9270,
+ "intelligence": 118,
+ "magicResist": 445,
+ "physicalAttack": 2198,
+ "strength": 150
+ },
+ "items": [
+ 114,
+ 118,
+ 125,
+ 133,
+ 172,
+ 168
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 713,
+ "armor": 560,
+ "armorPenetration": 1240,
+ "hp": 10870,
+ "intelligence": 144,
+ "magicResist": 605,
+ "physicalAttack": 3138,
+ "strength": 176
+ },
+ "items": [
+ 118,
+ 121,
+ 138,
+ 172,
+ 168,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 970,
+ "armor": 880,
+ "armorPenetration": 1400,
+ "hp": 15670,
+ "intelligence": 184,
+ "magicResist": 765,
+ "physicalAttack": 3646,
+ "strength": 216
+ },
+ "items": [
+ 114,
+ 121,
+ 173,
+ 183,
+ 182,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1224,
+ "armor": 1200,
+ "armorPenetration": 2320,
+ "hp": 22070,
+ "intelligence": 234,
+ "magicResist": 925,
+ "physicalAttack": 4182,
+ "strength": 266
+ },
+ "items": [
+ 125,
+ 118,
+ 183,
+ 182,
+ 179,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1256,
+ "armor": 1520,
+ "armorPenetration": 2800,
+ "hp": 36726,
+ "intelligence": 236,
+ "magicResist": 1085,
+ "physicalAttack": 6797,
+ "strength": 268
+ },
+ "items": [
+ 138,
+ 139,
+ 168,
+ 173,
+ 201,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1699,
+ "armor": 1520,
+ "armorPenetration": 3400,
+ "hp": 41846,
+ "intelligence": 352,
+ "magicResist": 1494,
+ "physicalAttack": 8773,
+ "strength": 384
+ },
+ "items": [
+ 179,
+ 208,
+ 213,
+ 183,
+ 223,
+ 231
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2274,
+ "armor": 1840,
+ "armorPenetration": 4600,
+ "hp": 56502,
+ "intelligence": 430,
+ "magicResist": 1494,
+ "physicalAttack": 10744,
+ "strength": 462
+ },
+ "items": [
+ 183,
+ 179,
+ 213,
+ 224,
+ 225,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2631,
+ "armor": 2160,
+ "armorPenetration": 5560,
+ "hp": 81622,
+ "intelligence": 508,
+ "magicResist": 1494,
+ "physicalAttack": 13848,
+ "strength": 540
+ },
+ "items": [
+ 187,
+ 182,
+ 225,
+ 223,
+ 239,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 7,
+ 12,
+ 3
+ ],
+ "artifacts": [
+ 1044,
+ 2004,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 2013,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero44_petmaster",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0044",
+ "epicArtAsset": {
+ "name": "44_astrid_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.02,
+ 1
+ ],
+ "screen": "obtain",
+ "x": -6.399999999999977,
+ "y": 64
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "44_Astrid_lukas",
+ "transform": [
+ {
+ "scale": [
+ -1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 36,
+ "y": 4
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "small",
+ "ultCinematic": null,
+ "roleExtended": [
+ "ranged_dps",
+ "melee_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 6,
+ 10,
+ 1,
+ 15,
+ 20
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 219,
+ "1": 220,
+ "2": 364,
+ "3": 222,
+ "4": 223
+ }
+ },
+ "45": {
+ "id": 45,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 17,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 21,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 17
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 8,
+ 9,
+ 7,
+ 16,
+ 13,
+ 11
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 14,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 3
+ },
+ "items": [
+ 13,
+ 11,
+ 16,
+ 19,
+ 24,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 25,
+ "hp": 1270,
+ "intelligence": 35,
+ "magicPower": 125,
+ "magicResist": 25,
+ "strength": 6
+ },
+ "items": [
+ 19,
+ 13,
+ 46,
+ 28,
+ 24,
+ 40
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 25,
+ "hp": 2655,
+ "intelligence": 54,
+ "magicPower": 275,
+ "magicResist": 75,
+ "strength": 8
+ },
+ "items": [
+ 40,
+ 22,
+ 45,
+ 40,
+ 56,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 25,
+ "hp": 3655,
+ "intelligence": 86,
+ "magicPower": 525,
+ "magicResist": 125,
+ "strength": 10
+ },
+ "items": [
+ 52,
+ 40,
+ 44,
+ 56,
+ 58,
+ 75
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 75,
+ "hp": 4655,
+ "intelligence": 123,
+ "magicPenetration": 50,
+ "magicPower": 675,
+ "magicResist": 175,
+ "strength": 17
+ },
+ "items": [
+ 52,
+ 40,
+ 58,
+ 56,
+ 71,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 75,
+ "hp": 5655,
+ "intelligence": 160,
+ "magicPenetration": 180,
+ "magicPower": 905,
+ "magicResist": 275,
+ "strength": 24
+ },
+ "items": [
+ 63,
+ 58,
+ 88,
+ 71,
+ 100,
+ 95
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 175,
+ "hp": 7255,
+ "intelligence": 200,
+ "magicPenetration": 260,
+ "magicPower": 1245,
+ "magicResist": 475,
+ "strength": 26
+ },
+ "items": [
+ 68,
+ 71,
+ 88,
+ 91,
+ 95,
+ 116
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 355,
+ "hp": 10055,
+ "intelligence": 270,
+ "magicPenetration": 340,
+ "magicPower": 1645,
+ "magicResist": 635,
+ "strength": 28
+ },
+ "items": [
+ 88,
+ 67,
+ 93,
+ 98,
+ 116,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 30,
+ "armor": 455,
+ "hp": 13415,
+ "intelligence": 302,
+ "magicPenetration": 540,
+ "magicPower": 2677,
+ "magicResist": 875,
+ "strength": 30
+ },
+ "items": [
+ 68,
+ 88,
+ 98,
+ 115,
+ 132,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 56,
+ "armor": 635,
+ "hp": 18311,
+ "intelligence": 358,
+ "magicPenetration": 740,
+ "magicPower": 3816,
+ "magicResist": 1035,
+ "strength": 56
+ },
+ "items": [
+ 99,
+ 126,
+ 117,
+ 137,
+ 171,
+ 167
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 96,
+ "armor": 835,
+ "hp": 25911,
+ "intelligence": 585,
+ "magicPenetration": 900,
+ "magicPower": 4296,
+ "magicResist": 1035,
+ "strength": 96
+ },
+ "items": [
+ 115,
+ 115,
+ 132,
+ 171,
+ 174,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 122,
+ "armor": 835,
+ "hp": 29111,
+ "intelligence": 750,
+ "magicPenetration": 1500,
+ "magicPower": 5576,
+ "magicResist": 1355,
+ "strength": 122
+ },
+ "items": [
+ 135,
+ 119,
+ 181,
+ 169,
+ 174,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 172,
+ "armor": 835,
+ "hp": 31671,
+ "intelligence": 1004,
+ "magicPenetration": 2420,
+ "magicPower": 7328,
+ "magicResist": 1355,
+ "strength": 172
+ },
+ "items": [
+ 117,
+ 132,
+ 184,
+ 174,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 198,
+ "armor": 835,
+ "hp": 39031,
+ "intelligence": 1363,
+ "magicPenetration": 3180,
+ "magicPower": 9984,
+ "magicResist": 1675,
+ "strength": 198
+ },
+ "items": [
+ 140,
+ 135,
+ 181,
+ 184,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 276,
+ "armor": 835,
+ "hp": 50487,
+ "intelligence": 1872,
+ "magicPenetration": 3500,
+ "magicPower": 12755,
+ "magicResist": 1995,
+ "strength": 276
+ },
+ "items": [
+ 183,
+ 212,
+ 180,
+ 209,
+ 228,
+ 232
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 354,
+ "armor": 1155,
+ "hp": 61047,
+ "intelligence": 2350,
+ "magicPenetration": 4700,
+ "magicPower": 15251,
+ "magicResist": 3195,
+ "strength": 354
+ },
+ "items": [
+ 181,
+ 212,
+ 203,
+ 232,
+ 222,
+ 233
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 432,
+ "armor": 1155,
+ "hp": 61047,
+ "intelligence": 3077,
+ "magicPenetration": 6988,
+ "magicPower": 19763,
+ "magicResist": 3195,
+ "strength": 432
+ },
+ "items": [
+ 184,
+ 203,
+ 226,
+ 232,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 7,
+ 11,
+ 2
+ ],
+ "artifacts": [
+ 1045,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 27,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero45_blackfox",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0045",
+ "epicArtAsset": {
+ "name": "45_satory_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 8.399999999999977,
+ "y": 47.400000000000034
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "45_Satori"
+ },
+ "role": "front",
+ "obtainType": "chest:town",
+ "characterType": null,
+ "silhouette": "tall",
+ "ultCinematic": null,
+ "roleExtended": [
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 224,
+ 225,
+ 226,
+ 227,
+ 228
+ ]
+ },
+ "46": {
+ "id": 46,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 17,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 17
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 21,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 21
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 5,
+ 8,
+ 9,
+ 4
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 400,
+ "intelligence": 7,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 13,
+ 13,
+ 11,
+ 18,
+ 28,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 25,
+ "hp": 1555,
+ "intelligence": 14,
+ "magicPower": 50,
+ "magicResist": 75,
+ "strength": 17
+ },
+ "items": [
+ 18,
+ 19,
+ 26,
+ 28,
+ 34,
+ 24
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 25,
+ "hp": 2440,
+ "intelligence": 35,
+ "magicPower": 150,
+ "magicResist": 125,
+ "strength": 31
+ },
+ "items": [
+ 24,
+ 34,
+ 45,
+ 48,
+ 58,
+ 56
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 25,
+ "hp": 3940,
+ "intelligence": 64,
+ "magicPower": 384,
+ "magicResist": 217,
+ "strength": 38
+ },
+ "items": [
+ 22,
+ 46,
+ 34,
+ 56,
+ 58,
+ 63
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 25,
+ "hp": 6240,
+ "intelligence": 88,
+ "magicPower": 614,
+ "magicResist": 217,
+ "strength": 45
+ },
+ "items": [
+ 22,
+ 36,
+ 58,
+ 56,
+ 64,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 125,
+ "hp": 9340,
+ "intelligence": 100,
+ "magicPower": 794,
+ "magicResist": 297,
+ "strength": 57
+ },
+ "items": [
+ 75,
+ 64,
+ 67,
+ 85,
+ 93,
+ 91
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 40,
+ "armor": 125,
+ "hp": 13140,
+ "intelligence": 122,
+ "magicPower": 1074,
+ "magicResist": 457,
+ "strength": 79
+ },
+ "items": [
+ 88,
+ 63,
+ 85,
+ 88,
+ 91,
+ 123
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 47,
+ "armor": 325,
+ "hp": 20140,
+ "intelligence": 129,
+ "magicPower": 1314,
+ "magicResist": 457,
+ "strength": 126
+ },
+ "items": [
+ 64,
+ 63,
+ 85,
+ 91,
+ 132,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 78,
+ "armor": 325,
+ "hp": 27300,
+ "intelligence": 190,
+ "magicPower": 1906,
+ "magicResist": 537,
+ "strength": 167
+ },
+ "items": [
+ 88,
+ 64,
+ 132,
+ 91,
+ 119,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 104,
+ "armor": 425,
+ "hp": 34996,
+ "intelligence": 276,
+ "magicPower": 2965,
+ "magicResist": 617,
+ "strength": 193
+ },
+ "items": [
+ 119,
+ 123,
+ 132,
+ 135,
+ 167,
+ 171
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 130,
+ "armor": 425,
+ "hp": 45156,
+ "intelligence": 471,
+ "magicPower": 3637,
+ "magicResist": 617,
+ "strength": 249
+ },
+ "items": [
+ 91,
+ 119,
+ 135,
+ 171,
+ 176,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 132,
+ "armor": 425,
+ "hp": 52916,
+ "intelligence": 612,
+ "magicPower": 5269,
+ "magicResist": 1217,
+ "strength": 251
+ },
+ "items": [
+ 127,
+ 116,
+ 167,
+ 167,
+ 184,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 212,
+ "armor": 425,
+ "hp": 69716,
+ "intelligence": 896,
+ "magicPower": 5429,
+ "magicResist": 1697,
+ "strength": 331
+ },
+ "items": [
+ 123,
+ 140,
+ 180,
+ 184,
+ 180,
+ 210
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 214,
+ "armor": 425,
+ "hp": 89172,
+ "intelligence": 898,
+ "magicPower": 9416,
+ "magicResist": 2017,
+ "strength": 537
+ },
+ "items": [
+ 135,
+ 140,
+ 180,
+ 184,
+ 180,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 292,
+ "armor": 425,
+ "hp": 107028,
+ "intelligence": 976,
+ "magicPower": 12667,
+ "magicResist": 2337,
+ "strength": 894
+ },
+ "items": [
+ 210,
+ 180,
+ 211,
+ 183,
+ 224,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 370,
+ "armor": 745,
+ "hp": 129588,
+ "intelligence": 1054,
+ "magicPower": 14875,
+ "magicResist": 3537,
+ "strength": 1425
+ },
+ "items": [
+ 180,
+ 184,
+ 211,
+ 224,
+ 224,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 448,
+ "armor": 745,
+ "hp": 166708,
+ "intelligence": 1132,
+ "magicPower": 19291,
+ "magicResist": 3857,
+ "strength": 1782
+ },
+ "items": [
+ 184,
+ 186,
+ 228,
+ 224,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 8,
+ 7,
+ 2
+ ],
+ "artifacts": [
+ 1046,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2031,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero46_grandma",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0046",
+ "epicArtAsset": {
+ "name": "46_martha_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.11,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 61,
+ "y": 12
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": "chest:town",
+ "characterType": null,
+ "silhouette": "wide",
+ "ultCinematic": null,
+ "roleExtended": [
+ "healer",
+ "support"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 9,
+ 5,
+ 1,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 229,
+ 230,
+ 231,
+ 232,
+ 233
+ ]
+ },
+ "47": {
+ "id": 47,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 25
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 8,
+ 8,
+ 5,
+ 14,
+ 15,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 50,
+ "intelligence": 2,
+ "physicalAttack": 25,
+ "strength": 21
+ },
+ "items": [
+ 14,
+ 15,
+ 8,
+ 18,
+ 27,
+ 21
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 125,
+ "hp": 385,
+ "intelligence": 4,
+ "physicalAttack": 50,
+ "strength": 47
+ },
+ "items": [
+ 18,
+ 15,
+ 25,
+ 21,
+ 37,
+ 43
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 225,
+ "hp": 770,
+ "intelligence": 6,
+ "physicalAttack": 116,
+ "strength": 83
+ },
+ "items": [
+ 21,
+ 27,
+ 37,
+ 36,
+ 59,
+ 70
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 425,
+ "armorPenetration": 80,
+ "hp": 1270,
+ "intelligence": 8,
+ "physicalAttack": 172,
+ "strength": 115
+ },
+ "items": [
+ 37,
+ 53,
+ 36,
+ 59,
+ 57,
+ 69
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 655,
+ "armorPenetration": 130,
+ "hp": 1770,
+ "intelligence": 10,
+ "physicalAttack": 275,
+ "strength": 153
+ },
+ "items": [
+ 31,
+ 36,
+ 59,
+ 69,
+ 70,
+ 90
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 915,
+ "armorPenetration": 260,
+ "hp": 2270,
+ "intelligence": 12,
+ "physicalAttack": 401,
+ "strength": 197
+ },
+ "items": [
+ 59,
+ 69,
+ 69,
+ 85,
+ 97,
+ 92
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 1175,
+ "armorPenetration": 460,
+ "hp": 3270,
+ "intelligence": 19,
+ "physicalAttack": 536,
+ "strength": 246
+ },
+ "items": [
+ 59,
+ 69,
+ 90,
+ 94,
+ 97,
+ 114
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 1435,
+ "armorPenetration": 660,
+ "hp": 4870,
+ "intelligence": 21,
+ "physicalAttack": 822,
+ "strength": 318
+ },
+ "items": [
+ 59,
+ 70,
+ 99,
+ 114,
+ 131,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 71,
+ "armor": 1735,
+ "armorPenetration": 740,
+ "hp": 6470,
+ "intelligence": 71,
+ "physicalAttack": 1094,
+ "strength": 428
+ },
+ "items": [
+ 90,
+ 97,
+ 122,
+ 114,
+ 131,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 135,
+ "armor": 1975,
+ "armorPenetration": 940,
+ "hp": 8070,
+ "intelligence": 135,
+ "physicalAttack": 1488,
+ "strength": 586
+ },
+ "items": [
+ 97,
+ 114,
+ 114,
+ 136,
+ 170,
+ 175
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 175,
+ "armor": 2575,
+ "armorPenetration": 1140,
+ "hp": 11270,
+ "intelligence": 175,
+ "physicalAttack": 1920,
+ "strength": 783
+ },
+ "items": [
+ 122,
+ 114,
+ 136,
+ 175,
+ 170,
+ 182
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 215,
+ "armor": 3335,
+ "armorPenetration": 1460,
+ "hp": 12870,
+ "intelligence": 215,
+ "physicalAttack": 2564,
+ "strength": 980
+ },
+ "items": [
+ 99,
+ 99,
+ 173,
+ 183,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 265,
+ "armor": 4055,
+ "armorPenetration": 2060,
+ "hp": 20870,
+ "intelligence": 265,
+ "physicalAttack": 3204,
+ "strength": 1204
+ },
+ "items": [
+ 97,
+ 136,
+ 175,
+ 175,
+ 185,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 353,
+ "armor": 5255,
+ "armorPenetration": 2260,
+ "hp": 27526,
+ "intelligence": 353,
+ "physicalAttack": 4535,
+ "strength": 1514
+ },
+ "items": [
+ 97,
+ 175,
+ 175,
+ 201,
+ 185,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 479,
+ "armor": 6455,
+ "armorPenetration": 2460,
+ "hp": 32646,
+ "intelligence": 479,
+ "physicalAttack": 5559,
+ "strength": 2093
+ },
+ "items": [
+ 175,
+ 201,
+ 185,
+ 211,
+ 227,
+ 231
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 605,
+ "armor": 8255,
+ "armorPenetration": 3660,
+ "hp": 37766,
+ "intelligence": 605,
+ "physicalAttack": 6583,
+ "strength": 2672
+ },
+ "items": [
+ 183,
+ 185,
+ 211,
+ 227,
+ 221,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 731,
+ "armor": 9775,
+ "armorPenetration": 4620,
+ "hp": 47686,
+ "intelligence": 731,
+ "physicalAttack": 8247,
+ "strength": 3469
+ },
+ "items": [
+ 183,
+ 179,
+ 231,
+ 227,
+ 237,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 12,
+ 7,
+ 1
+ ],
+ "artifacts": [
+ 1047,
+ 2004,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 22,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero47_andvari",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0047",
+ "epicArtAsset": {
+ "name": "47_andvari_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.15,
+ 1.15
+ ],
+ "screen": "obtain",
+ "x": 62,
+ "y": 97
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "47_Andvari"
+ },
+ "role": "front",
+ "obtainType": "shop:titanTokenShop",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "support",
+ "control"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 5,
+ 8,
+ 2
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 234,
+ 235,
+ 236,
+ 237,
+ 238
+ ]
+ },
+ "48": {
+ "id": 48,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 50,
+ "physicalCritChance": 100,
+ "strength": 10
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 9,
+ 8,
+ 3,
+ 6
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 7,
+ "magicResist": 25,
+ "physicalAttack": 12,
+ "strength": 7
+ },
+ "items": [
+ 13,
+ 17,
+ 12,
+ 6,
+ 25,
+ 27
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 75,
+ "hp": 585,
+ "intelligence": 15,
+ "magicResist": 25,
+ "physicalAttack": 45,
+ "strength": 15
+ },
+ "items": [
+ 13,
+ 6,
+ 27,
+ 24,
+ 28,
+ 51
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 48,
+ "armor": 125,
+ "hp": 1470,
+ "intelligence": 32,
+ "magicResist": 75,
+ "physicalAttack": 45,
+ "strength": 32
+ },
+ "items": [
+ 29,
+ 35,
+ 24,
+ 44,
+ 56,
+ 61
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 62,
+ "armor": 175,
+ "hp": 2970,
+ "intelligence": 39,
+ "magicResist": 125,
+ "physicalAttack": 45,
+ "physicalCritChance": 45,
+ "strength": 39
+ },
+ "items": [
+ 24,
+ 53,
+ 36,
+ 61,
+ 65,
+ 76
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 79,
+ "armor": 175,
+ "armorPenetration": 50,
+ "hp": 3970,
+ "intelligence": 46,
+ "magicResist": 205,
+ "physicalAttack": 134,
+ "physicalCritChance": 75,
+ "strength": 56
+ },
+ "items": [
+ 28,
+ 24,
+ 66,
+ 64,
+ 69,
+ 87
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 112,
+ "armor": 255,
+ "armorPenetration": 50,
+ "hp": 5270,
+ "intelligence": 53,
+ "magicResist": 335,
+ "physicalAttack": 260,
+ "physicalCritChance": 75,
+ "strength": 79
+ },
+ "items": [
+ 66,
+ 60,
+ 64,
+ 85,
+ 89,
+ 91
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 151,
+ "armor": 255,
+ "armorPenetration": 50,
+ "hp": 9070,
+ "intelligence": 60,
+ "magicResist": 515,
+ "physicalAttack": 372,
+ "physicalCritChance": 105,
+ "strength": 96
+ },
+ "items": [
+ 56,
+ 64,
+ 72,
+ 89,
+ 91,
+ 127
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 199,
+ "armor": 255,
+ "armorPenetration": 50,
+ "hp": 12870,
+ "intelligence": 92,
+ "magicResist": 595,
+ "physicalAttack": 484,
+ "physicalCritChance": 159,
+ "strength": 128
+ },
+ "items": [
+ 70,
+ 56,
+ 87,
+ 91,
+ 133,
+ 134
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 270,
+ "armor": 255,
+ "armorPenetration": 130,
+ "hp": 15870,
+ "intelligence": 123,
+ "magicResist": 851,
+ "physicalAttack": 955,
+ "physicalCritChance": 159,
+ "strength": 159
+ },
+ "items": [
+ 56,
+ 61,
+ 91,
+ 122,
+ 138,
+ 139
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 358,
+ "armor": 415,
+ "armorPenetration": 130,
+ "hp": 18870,
+ "intelligence": 163,
+ "magicResist": 1260,
+ "physicalAttack": 1615,
+ "physicalCritChance": 189,
+ "strength": 199
+ },
+ "items": [
+ 122,
+ 114,
+ 133,
+ 134,
+ 173,
+ 167
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 414,
+ "armor": 575,
+ "armorPenetration": 730,
+ "hp": 26470,
+ "intelligence": 189,
+ "magicResist": 1516,
+ "physicalAttack": 2284,
+ "physicalCritChance": 189,
+ "strength": 225
+ },
+ "items": [
+ 121,
+ 123,
+ 133,
+ 173,
+ 167,
+ 188
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 500,
+ "armor": 575,
+ "armorPenetration": 1330,
+ "hp": 34070,
+ "intelligence": 215,
+ "magicResist": 1676,
+ "physicalAttack": 2604,
+ "physicalCritChance": 417,
+ "strength": 281
+ },
+ "items": [
+ 123,
+ 97,
+ 173,
+ 167,
+ 188,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 724,
+ "armor": 575,
+ "armorPenetration": 2130,
+ "hp": 41670,
+ "intelligence": 265,
+ "magicResist": 1676,
+ "physicalAttack": 2924,
+ "physicalCritChance": 645,
+ "strength": 361
+ },
+ "items": [
+ 125,
+ 123,
+ 167,
+ 201,
+ 187,
+ 202
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 948,
+ "armor": 575,
+ "armorPenetration": 2130,
+ "hp": 54390,
+ "intelligence": 315,
+ "magicResist": 1836,
+ "physicalAttack": 4812,
+ "physicalCritChance": 910,
+ "strength": 441
+ },
+ "items": [
+ 123,
+ 138,
+ 167,
+ 183,
+ 208,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1391,
+ "armor": 895,
+ "armorPenetration": 2130,
+ "hp": 73446,
+ "intelligence": 431,
+ "magicResist": 1836,
+ "physicalAttack": 6143,
+ "physicalCritChance": 910,
+ "strength": 587
+ },
+ "items": [
+ 183,
+ 208,
+ 179,
+ 213,
+ 224,
+ 231
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 1748,
+ "armor": 1215,
+ "armorPenetration": 3330,
+ "hp": 100102,
+ "intelligence": 509,
+ "magicResist": 1836,
+ "physicalAttack": 8114,
+ "physicalCritChance": 910,
+ "strength": 665
+ },
+ "items": [
+ 187,
+ 183,
+ 207,
+ 231,
+ 224,
+ 236
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 1972,
+ "armor": 1535,
+ "armorPenetration": 4530,
+ "hp": 122022,
+ "intelligence": 559,
+ "magicResist": 1836,
+ "physicalAttack": 9912,
+ "physicalCritChance": 1836,
+ "strength": 715
+ },
+ "items": [
+ 182,
+ 187,
+ 228,
+ 224,
+ 236,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 8,
+ 9,
+ 3
+ ],
+ "artifacts": [
+ 1048,
+ 2001,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 1017,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero48_sebastian",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0048",
+ "epicArtAsset": {
+ "name": "48_sebastian_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.1,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 10,
+ "y": 58
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "48_Sebastian",
+ "transform": [
+ {
+ "scale": [
+ 1.15,
+ 1.15
+ ],
+ "screen": "obtain",
+ "x": 104,
+ "y": 24
+ }
+ ]
+ },
+ "role": "middle",
+ "obtainType": "shop:titanTokenShop",
+ "characterType": null,
+ "silhouette": "tall",
+ "ultCinematic": null,
+ "roleExtended": [
+ "support"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 5,
+ 2
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 239,
+ 240,
+ 241,
+ 242,
+ 243
+ ]
+ },
+ "49": {
+ "id": 49,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 25,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 8,
+ 9,
+ 17,
+ 13,
+ 14,
+ 12
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 3,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 3
+ },
+ "items": [
+ 9,
+ 13,
+ 12,
+ 20,
+ 30,
+ 29
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 25,
+ "dodge": 15,
+ "hp": 770,
+ "intelligence": 6,
+ "magicResist": 50,
+ "physicalAttack": 50,
+ "physicalCritChance": 15,
+ "strength": 6
+ },
+ "items": [
+ 13,
+ 20,
+ 29,
+ 30,
+ 35,
+ 38
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 59,
+ "armor": 25,
+ "dodge": 30,
+ "hp": 1155,
+ "intelligence": 13,
+ "magicResist": 50,
+ "physicalAttack": 108,
+ "physicalCritChance": 30,
+ "strength": 13
+ },
+ "items": [
+ 30,
+ 24,
+ 28,
+ 43,
+ 72,
+ 76
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 76,
+ "armor": 75,
+ "dodge": 45,
+ "hp": 1655,
+ "intelligence": 20,
+ "magicResist": 100,
+ "physicalAttack": 197,
+ "physicalCritChance": 54,
+ "strength": 20
+ },
+ "items": [
+ 27,
+ 24,
+ 39,
+ 62,
+ 61,
+ 87
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 103,
+ "armor": 125,
+ "dodge": 75,
+ "hp": 2155,
+ "intelligence": 27,
+ "magicResist": 150,
+ "physicalAttack": 267,
+ "physicalCritChance": 84,
+ "strength": 27
+ },
+ "items": [
+ 54,
+ 55,
+ 56,
+ 62,
+ 76,
+ 89
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 136,
+ "armor": 125,
+ "dodge": 120,
+ "hp": 3655,
+ "intelligence": 34,
+ "magicResist": 150,
+ "physicalAttack": 356,
+ "physicalCritChance": 129,
+ "strength": 34
+ },
+ "items": [
+ 56,
+ 60,
+ 72,
+ 73,
+ 89,
+ 120
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 184,
+ "armor": 125,
+ "dodge": 144,
+ "hp": 5455,
+ "intelligence": 36,
+ "magicResist": 250,
+ "physicalAttack": 576,
+ "physicalCritChance": 183,
+ "strength": 36
+ },
+ "items": [
+ 62,
+ 66,
+ 73,
+ 90,
+ 101,
+ 127
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 232,
+ "armor": 205,
+ "dodge": 198,
+ "hp": 6255,
+ "intelligence": 68,
+ "magicResist": 250,
+ "physicalAttack": 702,
+ "physicalCritChance": 243,
+ "strength": 84
+ },
+ "items": [
+ 73,
+ 90,
+ 89,
+ 120,
+ 114,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 334,
+ "armor": 285,
+ "dodge": 222,
+ "hp": 8655,
+ "intelligence": 94,
+ "magicResist": 250,
+ "physicalAttack": 1152,
+ "physicalCritChance": 273,
+ "strength": 126
+ },
+ "items": [
+ 60,
+ 56,
+ 102,
+ 101,
+ 138,
+ 139
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 422,
+ "armor": 285,
+ "dodge": 282,
+ "hp": 9655,
+ "intelligence": 134,
+ "magicResist": 759,
+ "physicalAttack": 1704,
+ "physicalCritChance": 333,
+ "strength": 166
+ },
+ "items": [
+ 91,
+ 123,
+ 134,
+ 138,
+ 178,
+ 177
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 510,
+ "armor": 285,
+ "dodge": 448,
+ "hp": 13255,
+ "intelligence": 174,
+ "magicResist": 1015,
+ "physicalAttack": 2049,
+ "physicalCritChance": 499,
+ "strength": 236
+ },
+ "items": [
+ 100,
+ 99,
+ 138,
+ 167,
+ 178,
+ 188
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 598,
+ "armor": 485,
+ "dodge": 614,
+ "hp": 19255,
+ "intelligence": 214,
+ "magicResist": 1215,
+ "physicalAttack": 2369,
+ "physicalCritChance": 727,
+ "strength": 276
+ },
+ "items": [
+ 91,
+ 92,
+ 184,
+ 178,
+ 188,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 822,
+ "armor": 485,
+ "dodge": 780,
+ "hp": 26055,
+ "intelligence": 264,
+ "magicResist": 1535,
+ "physicalAttack": 2824,
+ "physicalCritChance": 955,
+ "strength": 326
+ },
+ "items": [
+ 99,
+ 100,
+ 184,
+ 189,
+ 187,
+ 207
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1046,
+ "armor": 685,
+ "dodge": 1008,
+ "hp": 35655,
+ "intelligence": 314,
+ "magicResist": 2055,
+ "physicalAttack": 3598,
+ "physicalCritChance": 1349,
+ "strength": 376
+ },
+ "items": [
+ 100,
+ 102,
+ 189,
+ 187,
+ 202,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1625,
+ "armor": 685,
+ "dodge": 1296,
+ "hp": 40455,
+ "intelligence": 440,
+ "magicResist": 2255,
+ "physicalAttack": 4246,
+ "physicalCritChance": 1614,
+ "strength": 502
+ },
+ "items": [
+ 176,
+ 189,
+ 207,
+ 213,
+ 230,
+ 225
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 1982,
+ "armor": 685,
+ "dodge": 1857,
+ "hp": 45255,
+ "intelligence": 518,
+ "magicResist": 2855,
+ "physicalAttack": 5820,
+ "physicalCritChance": 2008,
+ "strength": 580
+ },
+ "items": [
+ 183,
+ 187,
+ 213,
+ 224,
+ 230,
+ 236
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2561,
+ "armor": 1005,
+ "dodge": 2190,
+ "hp": 67175,
+ "intelligence": 644,
+ "magicResist": 2855,
+ "physicalAttack": 6844,
+ "physicalCritChance": 2540,
+ "strength": 706
+ },
+ "items": [
+ 184,
+ 189,
+ 224,
+ 229,
+ 235,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 9,
+ 10,
+ 3
+ ],
+ "artifacts": [
+ 1049,
+ 2001,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 23,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero49_naga",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0049",
+ "epicArtAsset": {
+ "name": "49_yasmine_epic.jpg"
+ },
+ "spineEpicArtAsset": {
+ "name": "Yasmin"
+ },
+ "role": "front",
+ "obtainType": "shop:clanWar",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero49_battle_animation"
+ },
+ "roleExtended": [
+ "melee_dps"
+ ],
+ "sfxAsset": "hero49_sfx",
+ "musicAsset": null,
+ "perk": [
+ 10,
+ 1,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "assetsIdent": null,
+ "skill": [
+ 244,
+ 245,
+ 246,
+ 247,
+ 248
+ ]
+ },
+ "50": {
+ "id": 50,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 25
+ },
+ "stars": {
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 8,
+ 9,
+ 5,
+ 13,
+ 14,
+ 10
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 3,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 12
+ },
+ "items": [
+ 9,
+ 13,
+ 10,
+ 18,
+ 27,
+ 24
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 75,
+ "hp": 1655,
+ "intelligence": 6,
+ "magicResist": 50,
+ "physicalAttack": 25,
+ "strength": 26
+ },
+ "items": [
+ 13,
+ 18,
+ 27,
+ 28,
+ 33,
+ 42
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 125,
+ "hp": 2925,
+ "intelligence": 13,
+ "magicResist": 100,
+ "physicalAttack": 58,
+ "strength": 47
+ },
+ "items": [
+ 21,
+ 27,
+ 24,
+ 44,
+ 69,
+ 74
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 305,
+ "hp": 3425,
+ "intelligence": 20,
+ "magicResist": 150,
+ "physicalAttack": 58,
+ "strength": 90
+ },
+ "items": [
+ 27,
+ 24,
+ 36,
+ 56,
+ 59,
+ 85
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 455,
+ "hp": 6425,
+ "intelligence": 27,
+ "magicResist": 150,
+ "physicalAttack": 58,
+ "strength": 117
+ },
+ "items": [
+ 37,
+ 43,
+ 59,
+ 56,
+ 74,
+ 90
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 735,
+ "hp": 7425,
+ "intelligence": 34,
+ "magicResist": 150,
+ "physicalAttack": 161,
+ "strength": 160
+ },
+ "items": [
+ 59,
+ 56,
+ 69,
+ 64,
+ 90,
+ 123
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 995,
+ "hp": 10825,
+ "intelligence": 36,
+ "magicResist": 230,
+ "physicalAttack": 231,
+ "strength": 224
+ },
+ "items": [
+ 56,
+ 69,
+ 65,
+ 90,
+ 91,
+ 127
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 68,
+ "armor": 1155,
+ "hp": 13825,
+ "intelligence": 68,
+ "magicResist": 310,
+ "physicalAttack": 357,
+ "strength": 288
+ },
+ "items": [
+ 74,
+ 85,
+ 90,
+ 123,
+ 114,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 104,
+ "armor": 1235,
+ "hp": 18025,
+ "intelligence": 104,
+ "magicResist": 310,
+ "physicalAttack": 643,
+ "strength": 420
+ },
+ "items": [
+ 59,
+ 56,
+ 99,
+ 94,
+ 136,
+ 139
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 144,
+ "armor": 1535,
+ "hp": 19025,
+ "intelligence": 144,
+ "magicResist": 719,
+ "physicalAttack": 1195,
+ "strength": 546
+ },
+ "items": [
+ 99,
+ 123,
+ 134,
+ 136,
+ 170,
+ 175
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 184,
+ "armor": 2335,
+ "hp": 20625,
+ "intelligence": 184,
+ "magicResist": 975,
+ "physicalAttack": 1540,
+ "strength": 773
+ },
+ "items": [
+ 94,
+ 99,
+ 136,
+ 170,
+ 175,
+ 179
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 224,
+ "armor": 3135,
+ "hp": 23825,
+ "intelligence": 224,
+ "magicResist": 975,
+ "physicalAttack": 2180,
+ "strength": 1008
+ },
+ "items": [
+ 99,
+ 100,
+ 184,
+ 183,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 274,
+ "armor": 3655,
+ "hp": 36625,
+ "intelligence": 274,
+ "magicResist": 1495,
+ "physicalAttack": 2820,
+ "strength": 1232
+ },
+ "items": [
+ 94,
+ 91,
+ 183,
+ 179,
+ 201,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 276,
+ "armor": 3975,
+ "hp": 58401,
+ "intelligence": 276,
+ "magicResist": 1495,
+ "physicalAttack": 5815,
+ "strength": 1272
+ },
+ "items": [
+ 94,
+ 99,
+ 179,
+ 184,
+ 208,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 354,
+ "armor": 4175,
+ "hp": 73057,
+ "intelligence": 354,
+ "magicResist": 1815,
+ "physicalAttack": 7786,
+ "strength": 1667
+ },
+ "items": [
+ 175,
+ 201,
+ 185,
+ 211,
+ 221,
+ 224
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 480,
+ "armor": 4775,
+ "hp": 90177,
+ "intelligence": 480,
+ "magicResist": 1815,
+ "physicalAttack": 8810,
+ "strength": 2464
+ },
+ "items": [
+ 183,
+ 185,
+ 211,
+ 225,
+ 221,
+ 237
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 606,
+ "armor": 7015,
+ "hp": 100097,
+ "intelligence": 606,
+ "magicResist": 1815,
+ "physicalAttack": 10634,
+ "strength": 3261
+ },
+ "items": [
+ 183,
+ 179,
+ 227,
+ 228,
+ 240,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 7,
+ 8,
+ 1
+ ],
+ "artifacts": [
+ 1050,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 6,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero50_corvus",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0050",
+ "epicArtAsset": {
+ "name": "50_corvus_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.11,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 37,
+ "y": -11
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "50_corvus"
+ },
+ "role": "front",
+ "obtainType": "chest:town",
+ "characterType": null,
+ "silhouette": "wide",
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_tank"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 4,
+ 2,
+ 12,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 249,
+ 250,
+ 251,
+ 252,
+ 253
+ ]
+ },
+ "51": {
+ "id": 51,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 20,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 8,
+ 9,
+ 4,
+ 13,
+ 16,
+ 11
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 19,
+ "magicResist": 25,
+ "strength": 3
+ },
+ "items": [
+ 9,
+ 13,
+ 11,
+ 19,
+ 28,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 25,
+ "hp": 770,
+ "intelligence": 33,
+ "magicPower": 100,
+ "magicResist": 100,
+ "strength": 6
+ },
+ "items": [
+ 13,
+ 19,
+ 26,
+ 28,
+ 34,
+ 40
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 25,
+ "hp": 1155,
+ "intelligence": 64,
+ "magicPower": 250,
+ "magicResist": 150,
+ "strength": 13
+ },
+ "items": [
+ 22,
+ 24,
+ 28,
+ 46,
+ 68,
+ 75
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 105,
+ "hp": 2155,
+ "intelligence": 91,
+ "magicPower": 380,
+ "magicResist": 200,
+ "strength": 20
+ },
+ "items": [
+ 27,
+ 24,
+ 41,
+ 58,
+ 60,
+ 86
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 205,
+ "hp": 2655,
+ "intelligence": 118,
+ "magicPower": 480,
+ "magicResist": 400,
+ "strength": 27
+ },
+ "items": [
+ 41,
+ 40,
+ 56,
+ 60,
+ 75,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 355,
+ "hp": 4455,
+ "intelligence": 155,
+ "magicPower": 610,
+ "magicResist": 500,
+ "strength": 34
+ },
+ "items": [
+ 56,
+ 60,
+ 67,
+ 68,
+ 88,
+ 119
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 535,
+ "hp": 6255,
+ "intelligence": 187,
+ "magicPower": 1010,
+ "magicResist": 680,
+ "strength": 36
+ },
+ "items": [
+ 60,
+ 63,
+ 64,
+ 88,
+ 100,
+ 127
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 68,
+ "armor": 635,
+ "hp": 8655,
+ "intelligence": 219,
+ "magicPower": 1170,
+ "magicResist": 1060,
+ "strength": 68
+ },
+ "items": [
+ 75,
+ 86,
+ 88,
+ 119,
+ 116,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 104,
+ "armor": 735,
+ "hp": 9455,
+ "intelligence": 365,
+ "magicPower": 1570,
+ "magicResist": 1320,
+ "strength": 104
+ },
+ "items": [
+ 60,
+ 56,
+ 93,
+ 95,
+ 137,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 144,
+ "armor": 735,
+ "hp": 14551,
+ "intelligence": 491,
+ "magicPower": 2589,
+ "magicResist": 1420,
+ "strength": 144
+ },
+ "items": [
+ 93,
+ 119,
+ 135,
+ 137,
+ 171,
+ 169
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 184,
+ "armor": 735,
+ "hp": 17111,
+ "intelligence": 718,
+ "magicPower": 4061,
+ "magicResist": 1420,
+ "strength": 184
+ },
+ "items": [
+ 100,
+ 93,
+ 137,
+ 171,
+ 167,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 224,
+ "armor": 735,
+ "hp": 26311,
+ "intelligence": 915,
+ "magicPower": 5221,
+ "magicResist": 1620,
+ "strength": 224
+ },
+ "items": [
+ 95,
+ 100,
+ 183,
+ 184,
+ 180,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 274,
+ "armor": 1055,
+ "hp": 39111,
+ "intelligence": 1177,
+ "magicPower": 6181,
+ "magicResist": 2140,
+ "strength": 274
+ },
+ "items": [
+ 95,
+ 100,
+ 184,
+ 180,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 276,
+ "armor": 1055,
+ "hp": 49671,
+ "intelligence": 1490,
+ "magicPower": 9637,
+ "magicResist": 2660,
+ "strength": 276
+ },
+ "items": [
+ 93,
+ 91,
+ 180,
+ 203,
+ 209,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 354,
+ "armor": 1055,
+ "hp": 57431,
+ "intelligence": 2120,
+ "magicPower": 13293,
+ "magicResist": 2660,
+ "strength": 354
+ },
+ "items": [
+ 183,
+ 212,
+ 180,
+ 209,
+ 228,
+ 224
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 432,
+ "armor": 1375,
+ "hp": 79991,
+ "intelligence": 2598,
+ "magicPower": 15789,
+ "magicResist": 3860,
+ "strength": 432
+ },
+ "items": [
+ 183,
+ 212,
+ 203,
+ 222,
+ 224,
+ 238
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 510,
+ "armor": 1695,
+ "hp": 96791,
+ "intelligence": 3325,
+ "magicPower": 18669,
+ "magicResist": 5780,
+ "strength": 510
+ },
+ "items": [
+ 184,
+ 203,
+ 224,
+ 226,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 8,
+ 7,
+ 2
+ ],
+ "artifacts": [
+ 1051,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 1010,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero51_morrigan",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0051",
+ "epicArtAsset": {
+ "name": "51_morrigan_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.11,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 29,
+ "y": 34
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "51_Morrigan",
+ "transform": [
+ {
+ "scale": [
+ -1,
+ 1
+ ],
+ "screen": "obtain",
+ "x": 0,
+ "y": 0
+ }
+ ]
+ },
+ "role": "middle",
+ "obtainType": "chest:town",
+ "characterType": null,
+ "silhouette": "flying",
+ "ultCinematic": null,
+ "roleExtended": [
+ "support",
+ "healer"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 5,
+ 9,
+ 1,
+ 12
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 304,
+ 305,
+ 306,
+ 307,
+ 308
+ ]
+ },
+ "52": {
+ "id": 52,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 6,
+ 8,
+ 9,
+ 14
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 7,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "strength": 7
+ },
+ "items": [
+ 8,
+ 9,
+ 20,
+ 18,
+ 23,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 50,
+ "hp": 585,
+ "intelligence": 9,
+ "magicResist": 50,
+ "physicalAttack": 95,
+ "strength": 16
+ },
+ "items": [
+ 12,
+ 20,
+ 24,
+ 28,
+ 38,
+ 38
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 60,
+ "armor": 50,
+ "hp": 1085,
+ "intelligence": 12,
+ "magicResist": 100,
+ "physicalAttack": 186,
+ "strength": 19
+ },
+ "items": [
+ 31,
+ 53,
+ 42,
+ 53,
+ 56,
+ 57
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 62,
+ "armor": 50,
+ "armorPenetration": 150,
+ "hp": 2585,
+ "intelligence": 14,
+ "magicResist": 100,
+ "physicalAttack": 355,
+ "strength": 21
+ },
+ "items": [
+ 39,
+ 42,
+ 43,
+ 57,
+ 56,
+ 76
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 89,
+ "armor": 100,
+ "armorPenetration": 150,
+ "hp": 4085,
+ "intelligence": 21,
+ "magicResist": 150,
+ "physicalAttack": 491,
+ "strength": 28
+ },
+ "items": [
+ 24,
+ 53,
+ 56,
+ 70,
+ 76,
+ 90
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 106,
+ "armor": 180,
+ "armorPenetration": 280,
+ "hp": 5585,
+ "intelligence": 28,
+ "magicResist": 150,
+ "physicalAttack": 650,
+ "strength": 51
+ },
+ "items": [
+ 65,
+ 66,
+ 76,
+ 90,
+ 91,
+ 97
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 139,
+ "armor": 260,
+ "armorPenetration": 480,
+ "hp": 7585,
+ "intelligence": 35,
+ "magicResist": 230,
+ "physicalAttack": 832,
+ "strength": 74
+ },
+ "items": [
+ 70,
+ 64,
+ 76,
+ 118,
+ 97,
+ 123
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 186,
+ "armor": 260,
+ "armorPenetration": 920,
+ "hp": 9985,
+ "intelligence": 42,
+ "magicResist": 310,
+ "physicalAttack": 996,
+ "strength": 111
+ },
+ "items": [
+ 87,
+ 70,
+ 90,
+ 118,
+ 123,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 287,
+ "armor": 340,
+ "armorPenetration": 1160,
+ "hp": 11585,
+ "intelligence": 73,
+ "magicResist": 310,
+ "physicalAttack": 1300,
+ "strength": 188
+ },
+ "items": [
+ 87,
+ 92,
+ 118,
+ 114,
+ 125,
+ 138
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 420,
+ "armor": 340,
+ "armorPenetration": 1320,
+ "hp": 13185,
+ "intelligence": 118,
+ "magicResist": 470,
+ "physicalAttack": 2045,
+ "strength": 233
+ },
+ "items": [
+ 118,
+ 114,
+ 133,
+ 134,
+ 172,
+ 168
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 615,
+ "armor": 340,
+ "armorPenetration": 1480,
+ "hp": 14785,
+ "intelligence": 144,
+ "magicResist": 726,
+ "physicalAttack": 3114,
+ "strength": 259
+ },
+ "items": [
+ 91,
+ 120,
+ 138,
+ 172,
+ 167,
+ 179
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 842,
+ "armor": 340,
+ "armorPenetration": 1480,
+ "hp": 25985,
+ "intelligence": 184,
+ "magicResist": 726,
+ "physicalAttack": 3862,
+ "strength": 299
+ },
+ "items": [
+ 114,
+ 125,
+ 172,
+ 183,
+ 182,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1175,
+ "armor": 660,
+ "armorPenetration": 1800,
+ "hp": 32385,
+ "intelligence": 234,
+ "magicResist": 886,
+ "physicalAttack": 4614,
+ "strength": 349
+ },
+ "items": [
+ 114,
+ 118,
+ 179,
+ 182,
+ 179,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1207,
+ "armor": 660,
+ "armorPenetration": 2280,
+ "hp": 47041,
+ "intelligence": 236,
+ "magicResist": 886,
+ "physicalAttack": 7869,
+ "strength": 351
+ },
+ "items": [
+ 138,
+ 139,
+ 173,
+ 167,
+ 201,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1650,
+ "armor": 660,
+ "armorPenetration": 2880,
+ "hp": 58161,
+ "intelligence": 352,
+ "magicResist": 1295,
+ "physicalAttack": 9445,
+ "strength": 467
+ },
+ "items": [
+ 179,
+ 208,
+ 184,
+ 213,
+ 228,
+ 224
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2007,
+ "armor": 660,
+ "armorPenetration": 2880,
+ "hp": 84817,
+ "intelligence": 430,
+ "magicResist": 2815,
+ "physicalAttack": 11416,
+ "strength": 545
+ },
+ "items": [
+ 187,
+ 179,
+ 213,
+ 224,
+ 225,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2586,
+ "armor": 660,
+ "armorPenetration": 3840,
+ "hp": 105137,
+ "intelligence": 556,
+ "magicResist": 2815,
+ "physicalAttack": 14520,
+ "strength": 671
+ },
+ "items": [
+ 187,
+ 182,
+ 225,
+ 223,
+ 239,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 7,
+ 8,
+ 3
+ ],
+ "artifacts": [
+ 1052,
+ 2004,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 1014,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero52_isaac",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0052",
+ "epicArtAsset": {
+ "name": "52_isaac_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.15,
+ 1.15
+ ],
+ "screen": "obtain",
+ "x": -16,
+ "y": 36
+ }
+ ]
+ },
+ "role": "middle",
+ "obtainType": "chest:town",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "support",
+ "control"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 5,
+ 8,
+ 2,
+ 13,
+ 15,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 309,
+ 310,
+ 311,
+ 312,
+ 313
+ ]
+ },
+ "53": {
+ "id": 53,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 19,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 26,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 7,
+ 9,
+ 4,
+ 13,
+ 16,
+ 11
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "hp": 385,
+ "intelligence": 19,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 3
+ },
+ "items": [
+ 9,
+ 16,
+ 11,
+ 19,
+ 26,
+ 32
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 385,
+ "intelligence": 40,
+ "magicPenetration": 50,
+ "magicPower": 125,
+ "magicResist": 50,
+ "strength": 6
+ },
+ "items": [
+ 16,
+ 19,
+ 28,
+ 26,
+ 46,
+ 34
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "hp": 885,
+ "intelligence": 68,
+ "magicPenetration": 50,
+ "magicPower": 275,
+ "magicResist": 100,
+ "strength": 13
+ },
+ "items": [
+ 32,
+ 26,
+ 46,
+ 44,
+ 60,
+ 75
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 50,
+ "hp": 1385,
+ "intelligence": 85,
+ "magicPenetration": 100,
+ "magicPower": 375,
+ "magicResist": 250,
+ "strength": 20
+ },
+ "items": [
+ 24,
+ 32,
+ 22,
+ 46,
+ 58,
+ 84
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 42,
+ "armor": 50,
+ "hp": 2385,
+ "intelligence": 117,
+ "magicPenetration": 150,
+ "magicPower": 525,
+ "magicResist": 250,
+ "strength": 42
+ },
+ "items": [
+ 40,
+ 34,
+ 58,
+ 59,
+ 75,
+ 86
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 59,
+ "armor": 150,
+ "hp": 2385,
+ "intelligence": 171,
+ "magicPenetration": 150,
+ "magicPower": 675,
+ "magicResist": 350,
+ "strength": 59
+ },
+ "items": [
+ 60,
+ 75,
+ 64,
+ 71,
+ 99,
+ 119
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 66,
+ "armor": 350,
+ "hp": 3185,
+ "intelligence": 218,
+ "magicPenetration": 230,
+ "magicPower": 915,
+ "magicResist": 530,
+ "strength": 66
+ },
+ "items": [
+ 56,
+ 63,
+ 86,
+ 98,
+ 95,
+ 117
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 73,
+ "armor": 350,
+ "hp": 4985,
+ "intelligence": 303,
+ "magicPenetration": 590,
+ "magicPower": 1155,
+ "magicResist": 630,
+ "strength": 73
+ },
+ "items": [
+ 88,
+ 86,
+ 115,
+ 95,
+ 116,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 104,
+ "armor": 450,
+ "hp": 5785,
+ "intelligence": 442,
+ "magicPenetration": 590,
+ "magicPower": 1555,
+ "magicResist": 1050,
+ "strength": 104
+ },
+ "items": [
+ 63,
+ 71,
+ 93,
+ 99,
+ 137,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 144,
+ "armor": 650,
+ "hp": 10681,
+ "intelligence": 530,
+ "magicPenetration": 670,
+ "magicPower": 2734,
+ "magicResist": 1050,
+ "strength": 144
+ },
+ "items": [
+ 115,
+ 117,
+ 116,
+ 137,
+ 175,
+ 171
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 184,
+ "armor": 1250,
+ "hp": 10681,
+ "intelligence": 787,
+ "magicPenetration": 830,
+ "magicPower": 3214,
+ "magicResist": 1370,
+ "strength": 184
+ },
+ "items": [
+ 95,
+ 115,
+ 137,
+ 174,
+ 171,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 224,
+ "armor": 1250,
+ "hp": 13881,
+ "intelligence": 1022,
+ "magicPenetration": 1430,
+ "magicPower": 4334,
+ "magicResist": 1530,
+ "strength": 224
+ },
+ "items": [
+ 98,
+ 93,
+ 181,
+ 183,
+ 180,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 274,
+ "armor": 1570,
+ "hp": 21881,
+ "intelligence": 1246,
+ "magicPenetration": 1950,
+ "magicPower": 5974,
+ "magicResist": 1530,
+ "strength": 274
+ },
+ "items": [
+ 115,
+ 119,
+ 174,
+ 180,
+ 186,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 324,
+ "armor": 1570,
+ "hp": 27641,
+ "intelligence": 1621,
+ "magicPenetration": 2550,
+ "magicPower": 8790,
+ "magicResist": 1690,
+ "strength": 324
+ },
+ "items": [
+ 119,
+ 117,
+ 183,
+ 203,
+ 209,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 402,
+ "armor": 1890,
+ "hp": 35001,
+ "intelligence": 2311,
+ "magicPenetration": 2710,
+ "magicPower": 11606,
+ "magicResist": 1690,
+ "strength": 402
+ },
+ "items": [
+ 183,
+ 212,
+ 180,
+ 209,
+ 227,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 480,
+ "armor": 3410,
+ "hp": 45561,
+ "intelligence": 2789,
+ "magicPenetration": 2710,
+ "magicPower": 14102,
+ "magicResist": 2890,
+ "strength": 480
+ },
+ "items": [
+ 181,
+ 203,
+ 212,
+ 222,
+ 226,
+ 233
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 558,
+ "armor": 3410,
+ "hp": 45561,
+ "intelligence": 3516,
+ "magicPenetration": 3798,
+ "magicPower": 19814,
+ "magicResist": 2890,
+ "strength": 558
+ },
+ "items": [
+ 184,
+ 203,
+ 224,
+ 232,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 11,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1053,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 28,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero53_alvanor",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0053",
+ "epicArtAsset": {
+ "name": "53_alvanor_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.15,
+ 1.15
+ ],
+ "screen": "obtain",
+ "x": 91,
+ "y": 39
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": "chest:town",
+ "characterType": "snob",
+ "silhouette": "tall",
+ "ultCinematic": null,
+ "roleExtended": [
+ "support",
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 5,
+ 7,
+ 2,
+ 14,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 314,
+ 315,
+ 316,
+ 317,
+ 318
+ ]
+ },
+ "54": {
+ "id": 54,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 5,
+ 13,
+ 15,
+ 10
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "hp": 585,
+ "intelligence": 3,
+ "physicalAttack": 12,
+ "strength": 19
+ },
+ "items": [
+ 9,
+ 14,
+ 10,
+ 18,
+ 25,
+ 31
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armorPenetration": 50,
+ "hp": 970,
+ "intelligence": 6,
+ "magicResist": 25,
+ "physicalAttack": 70,
+ "strength": 33
+ },
+ "items": [
+ 15,
+ 18,
+ 31,
+ 25,
+ 43,
+ 33
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 50,
+ "armorPenetration": 100,
+ "hp": 1355,
+ "intelligence": 13,
+ "magicResist": 25,
+ "physicalAttack": 136,
+ "strength": 61
+ },
+ "items": [
+ 25,
+ 24,
+ 44,
+ 42,
+ 57,
+ 74
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 100,
+ "armorPenetration": 100,
+ "hp": 2355,
+ "intelligence": 20,
+ "magicResist": 75,
+ "physicalAttack": 272,
+ "strength": 78
+ },
+ "items": [
+ 24,
+ 27,
+ 25,
+ 44,
+ 56,
+ 84
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 42,
+ "armor": 200,
+ "armorPenetration": 100,
+ "hp": 3855,
+ "intelligence": 42,
+ "magicResist": 125,
+ "physicalAttack": 305,
+ "strength": 100
+ },
+ "items": [
+ 42,
+ 33,
+ 57,
+ 60,
+ 74,
+ 85
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 59,
+ "armor": 200,
+ "armorPenetration": 100,
+ "hp": 5355,
+ "intelligence": 59,
+ "magicResist": 225,
+ "physicalAttack": 408,
+ "strength": 144
+ },
+ "items": [
+ 60,
+ 74,
+ 70,
+ 69,
+ 92,
+ 118
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 96,
+ "armor": 280,
+ "armorPenetration": 340,
+ "hp": 5355,
+ "intelligence": 66,
+ "magicResist": 325,
+ "physicalAttack": 707,
+ "strength": 177
+ },
+ "items": [
+ 57,
+ 65,
+ 70,
+ 85,
+ 94,
+ 125
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 103,
+ "armor": 280,
+ "armorPenetration": 420,
+ "hp": 6355,
+ "intelligence": 73,
+ "magicResist": 565,
+ "physicalAttack": 1105,
+ "strength": 232
+ },
+ "items": [
+ 69,
+ 85,
+ 90,
+ 94,
+ 116,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 134,
+ "armor": 440,
+ "armorPenetration": 420,
+ "hp": 7355,
+ "intelligence": 164,
+ "magicPower": 160,
+ "magicResist": 725,
+ "physicalAttack": 1175,
+ "strength": 343
+ },
+ "items": [
+ 56,
+ 92,
+ 94,
+ 97,
+ 136,
+ 139
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 174,
+ "armor": 440,
+ "armorPenetration": 620,
+ "hp": 8355,
+ "intelligence": 204,
+ "magicPower": 160,
+ "magicResist": 1134,
+ "physicalAttack": 1862,
+ "strength": 469
+ },
+ "items": [
+ 122,
+ 116,
+ 127,
+ 136,
+ 170,
+ 168
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 244,
+ "armor": 600,
+ "armorPenetration": 620,
+ "hp": 8355,
+ "intelligence": 304,
+ "magicPower": 320,
+ "magicResist": 1294,
+ "physicalAttack": 2370,
+ "strength": 696
+ },
+ "items": [
+ 99,
+ 123,
+ 136,
+ 167,
+ 182,
+ 182
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 284,
+ "armor": 800,
+ "armorPenetration": 1260,
+ "hp": 15955,
+ "intelligence": 344,
+ "magicPower": 320,
+ "magicResist": 1294,
+ "physicalAttack": 3010,
+ "strength": 814
+ },
+ "items": [
+ 92,
+ 94,
+ 183,
+ 182,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 334,
+ "armor": 1120,
+ "armorPenetration": 1580,
+ "hp": 23955,
+ "intelligence": 394,
+ "magicPower": 320,
+ "magicResist": 1294,
+ "physicalAttack": 4105,
+ "strength": 1076
+ },
+ "items": [
+ 123,
+ 125,
+ 167,
+ 179,
+ 185,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 384,
+ "armor": 1120,
+ "armorPenetration": 1580,
+ "hp": 41411,
+ "intelligence": 444,
+ "magicPower": 320,
+ "magicResist": 1454,
+ "physicalAttack": 6292,
+ "strength": 1330
+ },
+ "items": [
+ 122,
+ 114,
+ 182,
+ 201,
+ 208,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 462,
+ "armor": 1280,
+ "armorPenetration": 1900,
+ "hp": 54787,
+ "intelligence": 522,
+ "magicPower": 320,
+ "magicResist": 1454,
+ "physicalAttack": 9291,
+ "strength": 1687
+ },
+ "items": [
+ 182,
+ 185,
+ 201,
+ 208,
+ 227,
+ 221
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 512,
+ "armor": 2480,
+ "armorPenetration": 2220,
+ "hp": 66563,
+ "intelligence": 572,
+ "magicPower": 320,
+ "magicResist": 1454,
+ "physicalAttack": 11966,
+ "strength": 2129
+ },
+ "items": [
+ 183,
+ 185,
+ 211,
+ 227,
+ 221,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 638,
+ "armor": 4000,
+ "armorPenetration": 3180,
+ "hp": 76483,
+ "intelligence": 698,
+ "magicPower": 320,
+ "magicResist": 1454,
+ "physicalAttack": 13630,
+ "strength": 2926
+ },
+ "items": [
+ 184,
+ 201,
+ 231,
+ 221,
+ 237,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 7,
+ 12,
+ 1
+ ],
+ "artifacts": [
+ 1054,
+ 2004,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 16,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero54_tristan",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0054",
+ "epicArtAsset": {
+ "name": "54_tristan_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ 1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": 35,
+ "y": 4
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": "chest:town",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 10,
+ 2,
+ 16
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 319,
+ 320,
+ 321,
+ 322,
+ 323
+ ]
+ },
+ "55": {
+ "id": 55,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 20,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 4,
+ 2,
+ 8,
+ 16,
+ 13,
+ 11
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 19,
+ "strength": 3
+ },
+ "items": [
+ 13,
+ 11,
+ 16,
+ 19,
+ 24,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 25,
+ "hp": 1470,
+ "intelligence": 40,
+ "magicPower": 100,
+ "strength": 6
+ },
+ "items": [
+ 19,
+ 13,
+ 46,
+ 24,
+ 28,
+ 40
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 25,
+ "hp": 2855,
+ "intelligence": 59,
+ "magicPower": 250,
+ "magicResist": 50,
+ "strength": 8
+ },
+ "items": [
+ 22,
+ 24,
+ 26,
+ 34,
+ 51,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 25,
+ "armor": 25,
+ "hp": 3355,
+ "intelligence": 93,
+ "magicPower": 400,
+ "magicResist": 50,
+ "strength": 25
+ },
+ "items": [
+ 26,
+ 34,
+ 28,
+ 56,
+ 58,
+ 88
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 32,
+ "armor": 125,
+ "hp": 5155,
+ "intelligence": 107,
+ "magicPower": 630,
+ "magicResist": 100,
+ "strength": 32
+ },
+ "items": [
+ 44,
+ 40,
+ 56,
+ 58,
+ 88,
+ 63
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 275,
+ "hp": 7755,
+ "intelligence": 119,
+ "magicPower": 940,
+ "magicResist": 150,
+ "strength": 34
+ },
+ "items": [
+ 58,
+ 56,
+ 63,
+ 67,
+ 95,
+ 115
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 275,
+ "hp": 9555,
+ "intelligence": 159,
+ "magicPower": 1360,
+ "magicResist": 390,
+ "strength": 36
+ },
+ "items": [
+ 88,
+ 75,
+ 84,
+ 88,
+ 97,
+ 95
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 63,
+ "armor": 475,
+ "armorPenetration": 200,
+ "hp": 11155,
+ "intelligence": 234,
+ "magicPower": 1520,
+ "magicResist": 390,
+ "strength": 63
+ },
+ "items": [
+ 67,
+ 88,
+ 93,
+ 97,
+ 116,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 65,
+ "armor": 575,
+ "armorPenetration": 400,
+ "hp": 14515,
+ "intelligence": 266,
+ "magicPower": 2552,
+ "magicResist": 630,
+ "strength": 65
+ },
+ "items": [
+ 56,
+ 58,
+ 115,
+ 93,
+ 137,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 105,
+ "armor": 575,
+ "armorPenetration": 400,
+ "hp": 19611,
+ "intelligence": 354,
+ "magicPower": 3831,
+ "magicResist": 790,
+ "strength": 105
+ },
+ "items": [
+ 119,
+ 116,
+ 127,
+ 137,
+ 173,
+ 175
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 175,
+ "armor": 1175,
+ "armorPenetration": 1000,
+ "hp": 19611,
+ "intelligence": 532,
+ "magicPower": 4151,
+ "magicResist": 950,
+ "strength": 175
+ },
+ "items": [
+ 93,
+ 115,
+ 116,
+ 173,
+ 175,
+ 186
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 225,
+ "armor": 1775,
+ "armorPenetration": 1600,
+ "hp": 19611,
+ "intelligence": 786,
+ "magicPower": 4671,
+ "magicResist": 1270,
+ "strength": 225
+ },
+ "items": [
+ 135,
+ 119,
+ 173,
+ 183,
+ 186,
+ 173
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 275,
+ "armor": 2095,
+ "armorPenetration": 2800,
+ "hp": 26971,
+ "intelligence": 1040,
+ "magicPower": 5343,
+ "magicResist": 1270,
+ "strength": 275
+ },
+ "items": [
+ 119,
+ 132,
+ 173,
+ 180,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 301,
+ "armor": 2095,
+ "armorPenetration": 3400,
+ "hp": 32731,
+ "intelligence": 1399,
+ "magicPower": 8959,
+ "magicResist": 1270,
+ "strength": 301
+ },
+ "items": [
+ 135,
+ 137,
+ 180,
+ 203,
+ 173,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 417,
+ "armor": 2095,
+ "armorPenetration": 4000,
+ "hp": 38491,
+ "intelligence": 1994,
+ "magicPower": 11391,
+ "magicResist": 1270,
+ "strength": 417
+ },
+ "items": [
+ 183,
+ 186,
+ 209,
+ 212,
+ 231,
+ 227
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 543,
+ "armor": 3615,
+ "armorPenetration": 5200,
+ "hp": 45851,
+ "intelligence": 2694,
+ "magicPower": 12927,
+ "magicResist": 1270,
+ "strength": 543
+ },
+ "items": [
+ 186,
+ 184,
+ 212,
+ 226,
+ 222,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 669,
+ "armor": 3615,
+ "armorPenetration": 5200,
+ "hp": 55771,
+ "intelligence": 3491,
+ "magicPower": 17583,
+ "magicResist": 1590,
+ "strength": 669
+ },
+ "items": [
+ 175,
+ 212,
+ 222,
+ 231,
+ 238,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 8,
+ 12,
+ 2
+ ],
+ "artifacts": [
+ 1055,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2015,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero55_iris",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0055",
+ "spineEpicArtAsset": {
+ "name": "55_iris",
+ "transform": [
+ {
+ "scale": [
+ 1.4,
+ 1.4
+ ],
+ "screen": "obtain",
+ "x": -50,
+ "y": 140
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": "shop:crossGvGShop",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7,
+ 1
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 324,
+ 325,
+ 326,
+ 327,
+ 328
+ ]
+ },
+ "56": {
+ "id": 56,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 10
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 20,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 8,
+ 9,
+ 4,
+ 13,
+ 16,
+ 11
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 19,
+ "magicResist": 25,
+ "strength": 3
+ },
+ "items": [
+ 9,
+ 13,
+ 11,
+ 19,
+ 28,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 25,
+ "hp": 770,
+ "intelligence": 33,
+ "magicPower": 100,
+ "magicResist": 100,
+ "strength": 6
+ },
+ "items": [
+ 13,
+ 19,
+ 32,
+ 28,
+ 34,
+ 40
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 25,
+ "hp": 1155,
+ "intelligence": 64,
+ "magicPenetration": 50,
+ "magicPower": 200,
+ "magicResist": 150,
+ "strength": 13
+ },
+ "items": [
+ 22,
+ 24,
+ 28,
+ 46,
+ 68,
+ 75
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 105,
+ "hp": 2155,
+ "intelligence": 91,
+ "magicPenetration": 50,
+ "magicPower": 330,
+ "magicResist": 200,
+ "strength": 20
+ },
+ "items": [
+ 27,
+ 24,
+ 41,
+ 58,
+ 60,
+ 86
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 205,
+ "hp": 2655,
+ "intelligence": 118,
+ "magicPenetration": 50,
+ "magicPower": 430,
+ "magicResist": 400,
+ "strength": 27
+ },
+ "items": [
+ 41,
+ 40,
+ 56,
+ 60,
+ 75,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 355,
+ "hp": 4455,
+ "intelligence": 155,
+ "magicPenetration": 50,
+ "magicPower": 560,
+ "magicResist": 500,
+ "strength": 34
+ },
+ "items": [
+ 56,
+ 60,
+ 67,
+ 68,
+ 88,
+ 119
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 535,
+ "hp": 6255,
+ "intelligence": 187,
+ "magicPenetration": 50,
+ "magicPower": 960,
+ "magicResist": 680,
+ "strength": 36
+ },
+ "items": [
+ 60,
+ 63,
+ 64,
+ 88,
+ 100,
+ 127
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 68,
+ "armor": 635,
+ "hp": 8655,
+ "intelligence": 219,
+ "magicPenetration": 50,
+ "magicPower": 1120,
+ "magicResist": 1060,
+ "strength": 68
+ },
+ "items": [
+ 75,
+ 86,
+ 88,
+ 119,
+ 116,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 104,
+ "armor": 735,
+ "hp": 9455,
+ "intelligence": 365,
+ "magicPenetration": 50,
+ "magicPower": 1520,
+ "magicResist": 1320,
+ "strength": 104
+ },
+ "items": [
+ 60,
+ 56,
+ 98,
+ 95,
+ 137,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 144,
+ "armor": 735,
+ "hp": 14551,
+ "intelligence": 491,
+ "magicPenetration": 250,
+ "magicPower": 2339,
+ "magicResist": 1420,
+ "strength": 144
+ },
+ "items": [
+ 98,
+ 119,
+ 135,
+ 137,
+ 171,
+ 169
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 184,
+ "armor": 735,
+ "hp": 17111,
+ "intelligence": 718,
+ "magicPenetration": 450,
+ "magicPower": 3611,
+ "magicResist": 1420,
+ "strength": 184
+ },
+ "items": [
+ 100,
+ 98,
+ 137,
+ 171,
+ 167,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 224,
+ "armor": 735,
+ "hp": 26311,
+ "intelligence": 915,
+ "magicPenetration": 650,
+ "magicPower": 4571,
+ "magicResist": 1620,
+ "strength": 224
+ },
+ "items": [
+ 98,
+ 99,
+ 183,
+ 184,
+ 180,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 274,
+ "armor": 1255,
+ "hp": 39111,
+ "intelligence": 1139,
+ "magicPenetration": 850,
+ "magicPower": 5531,
+ "magicResist": 1940,
+ "strength": 274
+ },
+ "items": [
+ 95,
+ 100,
+ 183,
+ 180,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 276,
+ "armor": 1575,
+ "hp": 49671,
+ "intelligence": 1452,
+ "magicPenetration": 850,
+ "magicPower": 8987,
+ "magicResist": 2140,
+ "strength": 276
+ },
+ "items": [
+ 98,
+ 91,
+ 181,
+ 203,
+ 209,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 354,
+ "armor": 1575,
+ "hp": 54231,
+ "intelligence": 2082,
+ "magicPenetration": 1370,
+ "magicPower": 11963,
+ "magicResist": 2140,
+ "strength": 354
+ },
+ "items": [
+ 181,
+ 212,
+ 180,
+ 209,
+ 228,
+ 224
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 432,
+ "armor": 1575,
+ "hp": 71991,
+ "intelligence": 2560,
+ "magicPenetration": 1690,
+ "magicPower": 14939,
+ "magicResist": 3340,
+ "strength": 432
+ },
+ "items": [
+ 183,
+ 212,
+ 203,
+ 222,
+ 224,
+ 233
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 510,
+ "armor": 1895,
+ "hp": 88791,
+ "intelligence": 3287,
+ "magicPenetration": 2458,
+ "magicPower": 18971,
+ "magicResist": 3340,
+ "strength": 510
+ },
+ "items": [
+ 181,
+ 203,
+ 224,
+ 232,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 7,
+ 11,
+ 2
+ ],
+ "artifacts": [
+ 1056,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 1012,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero56_amira",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0056",
+ "spineEpicArtAsset": {
+ "name": "56_amira",
+ "transform": [
+ {
+ "scale": [
+ -1.4,
+ 1.4
+ ],
+ "screen": "obtain",
+ "x": -100,
+ "y": 160
+ }
+ ]
+ },
+ "role": "middle",
+ "obtainType": "shop:crossGvGShop",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "support",
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 5,
+ 7,
+ 1,
+ 21
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 375,
+ 376,
+ 377,
+ 378,
+ 379
+ ]
+ },
+ "57": {
+ "id": 57,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 5,
+ 2,
+ 14,
+ 9,
+ 10,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "hp": 200,
+ "intelligence": 3,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 19
+ },
+ "items": [
+ 9,
+ 10,
+ 13,
+ 18,
+ 27,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 50,
+ "hp": 970,
+ "intelligence": 6,
+ "magicResist": 50,
+ "physicalAttack": 58,
+ "strength": 33
+ },
+ "items": [
+ 10,
+ 18,
+ 25,
+ 28,
+ 36,
+ 43
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 100,
+ "hp": 1855,
+ "intelligence": 9,
+ "magicResist": 100,
+ "physicalAttack": 124,
+ "strength": 57
+ },
+ "items": [
+ 21,
+ 37,
+ 42,
+ 44,
+ 57,
+ 59
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 300,
+ "hp": 2355,
+ "intelligence": 11,
+ "magicResist": 150,
+ "physicalAttack": 227,
+ "strength": 79
+ },
+ "items": [
+ 33,
+ 36,
+ 43,
+ 57,
+ 59,
+ 74
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 450,
+ "hp": 2855,
+ "intelligence": 23,
+ "magicResist": 150,
+ "physicalAttack": 330,
+ "strength": 118
+ },
+ "items": [
+ 42,
+ 27,
+ 57,
+ 57,
+ 65,
+ 84
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 45,
+ "armor": 500,
+ "hp": 3355,
+ "intelligence": 45,
+ "magicResist": 230,
+ "physicalAttack": 559,
+ "strength": 140
+ },
+ "items": [
+ 64,
+ 74,
+ 74,
+ 90,
+ 92,
+ 99
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 57,
+ "armor": 780,
+ "hp": 4155,
+ "intelligence": 57,
+ "magicResist": 310,
+ "physicalAttack": 764,
+ "strength": 188
+ },
+ "items": [
+ 76,
+ 85,
+ 86,
+ 92,
+ 92,
+ 122
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 84,
+ "armor": 940,
+ "hp": 5155,
+ "intelligence": 84,
+ "magicResist": 410,
+ "physicalAttack": 1142,
+ "strength": 215
+ },
+ "items": [
+ 74,
+ 90,
+ 100,
+ 122,
+ 125,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 115,
+ "armor": 1180,
+ "hp": 5155,
+ "intelligence": 115,
+ "magicResist": 770,
+ "physicalAttack": 1536,
+ "strength": 302
+ },
+ "items": [
+ 85,
+ 122,
+ 123,
+ 125,
+ 131,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 184,
+ "armor": 1340,
+ "hp": 7755,
+ "intelligence": 184,
+ "magicResist": 930,
+ "physicalAttack": 1860,
+ "strength": 489
+ },
+ "items": [
+ 122,
+ 122,
+ 125,
+ 136,
+ 168,
+ 170
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 224,
+ "armor": 1660,
+ "hp": 7755,
+ "intelligence": 224,
+ "magicResist": 1090,
+ "physicalAttack": 2692,
+ "strength": 686
+ },
+ "items": [
+ 114,
+ 125,
+ 136,
+ 168,
+ 170,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 264,
+ "armor": 1980,
+ "hp": 14155,
+ "intelligence": 264,
+ "magicResist": 1250,
+ "physicalAttack": 3524,
+ "strength": 883
+ },
+ "items": [
+ 123,
+ 125,
+ 170,
+ 175,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 314,
+ "armor": 2580,
+ "hp": 18955,
+ "intelligence": 314,
+ "magicResist": 1410,
+ "physicalAttack": 4380,
+ "strength": 1246
+ },
+ "items": [
+ 122,
+ 131,
+ 176,
+ 175,
+ 201,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 340,
+ "armor": 3340,
+ "hp": 30731,
+ "intelligence": 340,
+ "magicResist": 2010,
+ "physicalAttack": 6843,
+ "strength": 1302
+ },
+ "items": [
+ 134,
+ 136,
+ 168,
+ 175,
+ 208,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 456,
+ "armor": 3940,
+ "hp": 37387,
+ "intelligence": 456,
+ "magicResist": 2266,
+ "physicalAttack": 8919,
+ "strength": 1745
+ },
+ "items": [
+ 176,
+ 179,
+ 208,
+ 211,
+ 221,
+ 227
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 534,
+ "armor": 5140,
+ "hp": 47243,
+ "intelligence": 534,
+ "magicResist": 2866,
+ "physicalAttack": 10890,
+ "strength": 2320
+ },
+ "items": [
+ 179,
+ 185,
+ 211,
+ 221,
+ 228,
+ 237
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 660,
+ "armor": 7060,
+ "hp": 55563,
+ "intelligence": 660,
+ "magicResist": 4066,
+ "physicalAttack": 12554,
+ "strength": 3117
+ },
+ "items": [
+ 176,
+ 185,
+ 221,
+ 225,
+ 237,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 7,
+ 8,
+ 1
+ ],
+ "artifacts": [
+ 1057,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 2028,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero57_fafnir",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0057",
+ "epicArtAsset": {
+ "name": "57_fafnir_epic.jpg"
+ },
+ "spineEpicArtAsset": {
+ "name": "57_fafnir",
+ "transform": [
+ {
+ "scale": [
+ -1.4,
+ 1.4
+ ],
+ "screen": "obtain",
+ "x": 230,
+ "y": 40
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": "chest:town",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "support",
+ "control"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 5,
+ 8,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 385,
+ 386,
+ 387,
+ 388,
+ 389
+ ]
+ },
+ "58": {
+ "id": 58,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 25,
+ "physicalAttack": 50,
+ "strength": 10
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 20,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 4,
+ 7,
+ 7,
+ 8,
+ 2,
+ 6
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 12,
+ "magicPower": 50,
+ "strength": 7
+ },
+ "items": [
+ 2,
+ 16,
+ 11,
+ 19,
+ 26,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 25,
+ "hp": 400,
+ "intelligence": 33,
+ "magicPower": 150,
+ "magicResist": 50,
+ "strength": 10
+ },
+ "items": [
+ 11,
+ 19,
+ 24,
+ 27,
+ 40,
+ 45
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 75,
+ "hp": 900,
+ "intelligence": 57,
+ "magicPower": 300,
+ "magicResist": 100,
+ "strength": 13
+ },
+ "items": [
+ 22,
+ 26,
+ 27,
+ 40,
+ 52,
+ 64
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 125,
+ "hp": 1700,
+ "intelligence": 89,
+ "magicPenetration": 50,
+ "magicPower": 400,
+ "magicResist": 180,
+ "strength": 15
+ },
+ "items": [
+ 52,
+ 40,
+ 46,
+ 52,
+ 59,
+ 71
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 225,
+ "hp": 2200,
+ "intelligence": 121,
+ "magicPenetration": 230,
+ "magicPower": 580,
+ "magicResist": 180,
+ "strength": 17
+ },
+ "items": [
+ 46,
+ 48,
+ 52,
+ 58,
+ 71,
+ 75
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 225,
+ "hp": 2700,
+ "intelligence": 163,
+ "magicPenetration": 360,
+ "magicPower": 894,
+ "magicResist": 222,
+ "strength": 24
+ },
+ "items": [
+ 63,
+ 71,
+ 75,
+ 88,
+ 71,
+ 93
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 325,
+ "hp": 4300,
+ "intelligence": 180,
+ "magicPenetration": 520,
+ "magicPower": 1414,
+ "magicResist": 222,
+ "strength": 31
+ },
+ "items": [
+ 63,
+ 64,
+ 67,
+ 86,
+ 93,
+ 116
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 38,
+ "armor": 325,
+ "hp": 5900,
+ "intelligence": 227,
+ "magicPenetration": 520,
+ "magicPower": 1934,
+ "magicResist": 642,
+ "strength": 38
+ },
+ "items": [
+ 86,
+ 71,
+ 119,
+ 99,
+ 117,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 45,
+ "armor": 525,
+ "hp": 8460,
+ "intelligence": 304,
+ "magicPenetration": 760,
+ "magicPower": 2846,
+ "magicResist": 742,
+ "strength": 45
+ },
+ "items": [
+ 69,
+ 99,
+ 115,
+ 119,
+ 132,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 71,
+ "armor": 805,
+ "hp": 12556,
+ "intelligence": 390,
+ "magicPenetration": 760,
+ "magicPower": 3985,
+ "magicResist": 902,
+ "strength": 87
+ },
+ "items": [
+ 99,
+ 95,
+ 126,
+ 140,
+ 171,
+ 175
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 73,
+ "armor": 1605,
+ "hp": 18252,
+ "intelligence": 539,
+ "magicPenetration": 760,
+ "magicPower": 5124,
+ "magicResist": 902,
+ "strength": 89
+ },
+ "items": [
+ 115,
+ 126,
+ 135,
+ 169,
+ 181,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 75,
+ "armor": 1925,
+ "hp": 27212,
+ "intelligence": 541,
+ "magicPenetration": 1080,
+ "magicPower": 7196,
+ "magicResist": 1062,
+ "strength": 91
+ },
+ "items": [
+ 117,
+ 135,
+ 171,
+ 181,
+ 183,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 125,
+ "armor": 2245,
+ "hp": 34572,
+ "intelligence": 904,
+ "magicPenetration": 1560,
+ "magicPower": 8348,
+ "magicResist": 1062,
+ "strength": 141
+ },
+ "items": [
+ 140,
+ 132,
+ 184,
+ 180,
+ 176,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 151,
+ "armor": 2245,
+ "hp": 49228,
+ "intelligence": 1081,
+ "magicPenetration": 1560,
+ "magicPower": 11663,
+ "magicResist": 1982,
+ "strength": 167
+ },
+ "items": [
+ 140,
+ 135,
+ 183,
+ 180,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 229,
+ "armor": 2565,
+ "hp": 63884,
+ "intelligence": 1590,
+ "magicPenetration": 1560,
+ "magicPower": 14914,
+ "magicResist": 1982,
+ "strength": 245
+ },
+ "items": [
+ 212,
+ 169,
+ 180,
+ 209,
+ 227,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 307,
+ "armor": 3765,
+ "hp": 69644,
+ "intelligence": 2068,
+ "magicPenetration": 1560,
+ "magicPower": 18010,
+ "magicResist": 3182,
+ "strength": 323
+ },
+ "items": [
+ 183,
+ 212,
+ 209,
+ 222,
+ 227,
+ 238
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 385,
+ "armor": 5285,
+ "hp": 77004,
+ "intelligence": 2764,
+ "magicPenetration": 1560,
+ "magicPower": 21466,
+ "magicResist": 5102,
+ "strength": 401
+ },
+ "items": [
+ 186,
+ 183,
+ 227,
+ 226,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 8,
+ 11,
+ 2
+ ],
+ "artifacts": [
+ 1058,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2014,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero58_aidan",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0058",
+ "spineEpicArtAsset": {
+ "name": "58_aidan",
+ "transform": [
+ {
+ "scale": [
+ -1.3,
+ 1.3
+ ],
+ "screen": "obtain",
+ "x": 230,
+ "y": 108
+ }
+ ]
+ },
+ "role": "back",
+ "obtainType": "shop:crossGvGShop",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "healer",
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 9,
+ 7,
+ 2,
+ 18,
+ 21
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 395,
+ 396,
+ 397,
+ 398,
+ 399
+ ]
+ },
+ "59": {
+ "id": 59,
+ "baseStats": {
+ "agility": 25,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 60,
+ "strength": 10
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 25,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 3,
+ 6,
+ 9,
+ 14
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 200,
+ "intelligence": 7,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "strength": 7
+ },
+ "items": [
+ 3,
+ 13,
+ 14,
+ 20,
+ 23,
+ 31
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 36,
+ "armorPenetration": 50,
+ "hp": 585,
+ "intelligence": 9,
+ "magicResist": 25,
+ "physicalAttack": 87,
+ "strength": 9
+ },
+ "items": [
+ 12,
+ 20,
+ 24,
+ 28,
+ 35,
+ 38
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 72,
+ "armorPenetration": 50,
+ "hp": 1085,
+ "intelligence": 17,
+ "magicResist": 75,
+ "physicalAttack": 145,
+ "strength": 17
+ },
+ "items": [
+ 23,
+ 39,
+ 43,
+ 53,
+ 56,
+ 57
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 94,
+ "armor": 50,
+ "armorPenetration": 100,
+ "hp": 2085,
+ "intelligence": 19,
+ "magicResist": 125,
+ "physicalAttack": 281,
+ "strength": 19
+ },
+ "items": [
+ 25,
+ 38,
+ 53,
+ 56,
+ 70,
+ 76
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 121,
+ "armor": 50,
+ "armorPenetration": 230,
+ "hp": 3085,
+ "intelligence": 26,
+ "magicResist": 125,
+ "physicalAttack": 436,
+ "strength": 26
+ },
+ "items": [
+ 25,
+ 38,
+ 56,
+ 70,
+ 76,
+ 87
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 163,
+ "armor": 50,
+ "armorPenetration": 310,
+ "hp": 4085,
+ "intelligence": 38,
+ "magicResist": 125,
+ "physicalAttack": 628,
+ "strength": 38
+ },
+ "items": [
+ 64,
+ 66,
+ 70,
+ 76,
+ 87,
+ 91
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 211,
+ "armor": 50,
+ "armorPenetration": 390,
+ "hp": 6885,
+ "intelligence": 50,
+ "magicResist": 205,
+ "physicalAttack": 810,
+ "strength": 50
+ },
+ "items": [
+ 66,
+ 70,
+ 87,
+ 91,
+ 92,
+ 118
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 274,
+ "armor": 50,
+ "armorPenetration": 630,
+ "hp": 8885,
+ "intelligence": 57,
+ "magicResist": 205,
+ "physicalAttack": 1235,
+ "strength": 57
+ },
+ "items": [
+ 64,
+ 76,
+ 92,
+ 114,
+ 118,
+ 125
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 321,
+ "armor": 50,
+ "armorPenetration": 790,
+ "hp": 11285,
+ "intelligence": 64,
+ "magicResist": 445,
+ "physicalAttack": 1910,
+ "strength": 64
+ },
+ "items": [
+ 76,
+ 91,
+ 118,
+ 120,
+ 125,
+ 139
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 398,
+ "armor": 50,
+ "armorPenetration": 950,
+ "hp": 13285,
+ "intelligence": 71,
+ "magicResist": 1014,
+ "physicalAttack": 2894,
+ "strength": 71
+ },
+ "items": [
+ 91,
+ 118,
+ 120,
+ 125,
+ 139,
+ 168
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 460,
+ "armor": 50,
+ "armorPenetration": 1110,
+ "hp": 15285,
+ "intelligence": 73,
+ "magicResist": 1583,
+ "physicalAttack": 4278,
+ "strength": 73
+ },
+ "items": [
+ 91,
+ 114,
+ 118,
+ 139,
+ 172,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 601,
+ "armor": 370,
+ "armorPenetration": 1270,
+ "hp": 23685,
+ "intelligence": 75,
+ "magicResist": 1992,
+ "physicalAttack": 5154,
+ "strength": 75
+ },
+ "items": [
+ 91,
+ 125,
+ 167,
+ 179,
+ 182,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 825,
+ "armor": 370,
+ "armorPenetration": 1590,
+ "hp": 34885,
+ "intelligence": 125,
+ "magicResist": 2152,
+ "physicalAttack": 6330,
+ "strength": 125
+ },
+ "items": [
+ 114,
+ 118,
+ 179,
+ 172,
+ 179,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 966,
+ "armor": 370,
+ "armorPenetration": 1750,
+ "hp": 49541,
+ "intelligence": 127,
+ "magicResist": 2152,
+ "physicalAttack": 9265,
+ "strength": 127
+ },
+ "items": [
+ 125,
+ 139,
+ 172,
+ 167,
+ 208,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1432,
+ "armor": 370,
+ "armorPenetration": 1750,
+ "hp": 62197,
+ "intelligence": 205,
+ "magicResist": 2721,
+ "physicalAttack": 11364,
+ "strength": 205
+ },
+ "items": [
+ 167,
+ 179,
+ 208,
+ 213,
+ 225,
+ 231
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 1789,
+ "armor": 370,
+ "armorPenetration": 2950,
+ "hp": 78053,
+ "intelligence": 283,
+ "magicResist": 2721,
+ "physicalAttack": 14135,
+ "strength": 283
+ },
+ "items": [
+ 179,
+ 187,
+ 213,
+ 224,
+ 231,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2368,
+ "armor": 370,
+ "armorPenetration": 5110,
+ "hp": 98373,
+ "intelligence": 409,
+ "magicResist": 2721,
+ "physicalAttack": 16439,
+ "strength": 409
+ },
+ "items": [
+ 172,
+ 179,
+ 224,
+ 231,
+ 239,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 8,
+ 12,
+ 3
+ ],
+ "artifacts": [
+ 1059,
+ 2006,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 14,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero59_keila",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0059",
+ "spineEpicArtAsset": {
+ "name": "59_kayla",
+ "transform": [
+ {
+ "scale": [
+ -1.4,
+ 1.4
+ ],
+ "screen": "obtain",
+ "x": 230,
+ "y": 40
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": "shop:crossGvGShop",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_dps"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 10,
+ 1,
+ 18
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 400,
+ 401,
+ 402,
+ 403,
+ 404
+ ]
+ },
+ "60": {
+ "id": 60,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 55,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 19
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 6,
+ 7,
+ 8,
+ 8,
+ 13
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 50,
+ "hp": 585,
+ "intelligence": 7,
+ "magicPower": 25,
+ "strength": 7
+ },
+ "items": [
+ 8,
+ 14,
+ 10,
+ 18,
+ 27,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 125,
+ "hp": 970,
+ "intelligence": 10,
+ "magicPower": 25,
+ "magicResist": 50,
+ "physicalAttack": 25,
+ "strength": 21
+ },
+ "items": [
+ 18,
+ 14,
+ 27,
+ 32,
+ 36,
+ 44
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 225,
+ "hp": 1855,
+ "intelligence": 12,
+ "magicPenetration": 50,
+ "magicPower": 25,
+ "magicResist": 100,
+ "physicalAttack": 50,
+ "strength": 40
+ },
+ "items": [
+ 25,
+ 41,
+ 52,
+ 47,
+ 59,
+ 52
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 417,
+ "hp": 2275,
+ "intelligence": 44,
+ "magicPenetration": 150,
+ "magicPower": 25,
+ "magicResist": 100,
+ "physicalAttack": 111,
+ "strength": 57
+ },
+ "items": [
+ 26,
+ 33,
+ 47,
+ 58,
+ 59,
+ 68
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 639,
+ "hp": 2695,
+ "intelligence": 51,
+ "magicPenetration": 150,
+ "magicPower": 255,
+ "magicResist": 100,
+ "physicalAttack": 139,
+ "strength": 86
+ },
+ "items": [
+ 37,
+ 45,
+ 59,
+ 59,
+ 71,
+ 90
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 969,
+ "hp": 2695,
+ "intelligence": 53,
+ "magicPenetration": 230,
+ "magicPower": 385,
+ "magicResist": 150,
+ "physicalAttack": 209,
+ "strength": 114
+ },
+ "items": [
+ 67,
+ 67,
+ 74,
+ 88,
+ 99,
+ 100
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 30,
+ "armor": 1269,
+ "hp": 3495,
+ "intelligence": 60,
+ "magicPenetration": 230,
+ "magicPower": 625,
+ "magicResist": 510,
+ "physicalAttack": 209,
+ "strength": 131
+ },
+ "items": [
+ 71,
+ 68,
+ 88,
+ 74,
+ 122,
+ 119
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 37,
+ "armor": 1609,
+ "hp": 4295,
+ "intelligence": 97,
+ "magicPenetration": 310,
+ "magicPower": 1025,
+ "magicResist": 510,
+ "physicalAttack": 317,
+ "strength": 148
+ },
+ "items": [
+ 115,
+ 67,
+ 88,
+ 119,
+ 122,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 63,
+ "armor": 1869,
+ "hp": 5095,
+ "intelligence": 153,
+ "magicPenetration": 310,
+ "magicPower": 1505,
+ "magicResist": 750,
+ "physicalAttack": 425,
+ "strength": 204
+ },
+ "items": [
+ 90,
+ 87,
+ 115,
+ 117,
+ 135,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 118,
+ "armor": 1949,
+ "hp": 7655,
+ "intelligence": 228,
+ "magicPenetration": 470,
+ "magicPower": 2337,
+ "magicResist": 910,
+ "physicalAttack": 565,
+ "strength": 313
+ },
+ "items": [
+ 115,
+ 122,
+ 126,
+ 136,
+ 170,
+ 175
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 158,
+ "armor": 2709,
+ "hp": 9255,
+ "intelligence": 268,
+ "magicPenetration": 470,
+ "magicPower": 2817,
+ "magicResist": 1070,
+ "physicalAttack": 673,
+ "strength": 510
+ },
+ "items": [
+ 127,
+ 117,
+ 135,
+ 170,
+ 175,
+ 181
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 190,
+ "armor": 3309,
+ "hp": 11815,
+ "intelligence": 330,
+ "magicPenetration": 950,
+ "magicPower": 3969,
+ "magicResist": 1070,
+ "physicalAttack": 673,
+ "strength": 651
+ },
+ "items": [
+ 123,
+ 136,
+ 169,
+ 175,
+ 181,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 278,
+ "armor": 3909,
+ "hp": 13415,
+ "intelligence": 418,
+ "magicPenetration": 1270,
+ "magicPower": 5049,
+ "magicResist": 1070,
+ "physicalAttack": 673,
+ "strength": 991
+ },
+ "items": [
+ 117,
+ 115,
+ 169,
+ 181,
+ 209,
+ 210
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 280,
+ "armor": 3909,
+ "hp": 18535,
+ "intelligence": 571,
+ "magicPenetration": 1750,
+ "magicPower": 9233,
+ "magicResist": 1230,
+ "physicalAttack": 673,
+ "strength": 1167
+ },
+ "items": [
+ 98,
+ 92,
+ 183,
+ 180,
+ 210,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 358,
+ "armor": 4229,
+ "hp": 29095,
+ "intelligence": 649,
+ "magicPenetration": 1950,
+ "magicPower": 11441,
+ "magicResist": 1230,
+ "physicalAttack": 808,
+ "strength": 1698
+ },
+ "items": [
+ 210,
+ 169,
+ 211,
+ 180,
+ 232,
+ 227
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 436,
+ "armor": 5429,
+ "hp": 34855,
+ "intelligence": 727,
+ "magicPenetration": 3150,
+ "magicPower": 14249,
+ "magicResist": 1230,
+ "physicalAttack": 808,
+ "strength": 2229
+ },
+ "items": [
+ 185,
+ 210,
+ 180,
+ 232,
+ 226,
+ 237
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 486,
+ "armor": 7349,
+ "hp": 45735,
+ "intelligence": 777,
+ "magicPenetration": 4350,
+ "magicPower": 17657,
+ "magicResist": 1230,
+ "physicalAttack": 1832,
+ "strength": 2627
+ },
+ "items": [
+ 181,
+ 185,
+ 228,
+ 225,
+ 238,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 7,
+ 4,
+ 11,
+ 1
+ ],
+ "artifacts": [
+ 1060,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 12,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero60_mushroom",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0060",
+ "epicArtAsset": {
+ "name": "60_mushi_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.11,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 35,
+ "y": 43
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "60_mushroom",
+ "transform": [
+ {
+ "scale": [
+ -1.11,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 35,
+ "y": 43
+ }
+ ]
+ },
+ "role": "front",
+ "obtainType": "chest:town",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_tank"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 4,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 405,
+ "1": 406,
+ "2": 409,
+ "3": 407,
+ "4": 408
+ }
+ },
+ "61": {
+ "id": 61,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 55,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 19
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 24
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 5,
+ 1,
+ 2,
+ 13,
+ 14,
+ 18
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 970,
+ "intelligence": 2,
+ "physicalAttack": 37,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 10,
+ 13,
+ 18,
+ 27,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 50,
+ "hp": 1740,
+ "intelligence": 5,
+ "physicalAttack": 82,
+ "strength": 28
+ },
+ "items": [
+ 10,
+ 18,
+ 24,
+ 29,
+ 42,
+ 43
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 100,
+ "hp": 3125,
+ "intelligence": 8,
+ "physicalAttack": 148,
+ "physicalCritChance": 15,
+ "strength": 42
+ },
+ "items": [
+ 21,
+ 37,
+ 42,
+ 43,
+ 56,
+ 59
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 300,
+ "hp": 4625,
+ "intelligence": 10,
+ "physicalAttack": 214,
+ "physicalCritChance": 15,
+ "strength": 64
+ },
+ "items": [
+ 33,
+ 36,
+ 43,
+ 56,
+ 59,
+ 74
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 450,
+ "hp": 6125,
+ "intelligence": 22,
+ "physicalAttack": 247,
+ "physicalCritChance": 15,
+ "strength": 103
+ },
+ "items": [
+ 42,
+ 27,
+ 56,
+ 57,
+ 72,
+ 84
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 44,
+ "armor": 500,
+ "hp": 7625,
+ "intelligence": 44,
+ "physicalAttack": 406,
+ "physicalCritChance": 39,
+ "strength": 125
+ },
+ "items": [
+ 64,
+ 74,
+ 74,
+ 90,
+ 91,
+ 99
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 56,
+ "armor": 780,
+ "hp": 10425,
+ "intelligence": 56,
+ "magicResist": 80,
+ "physicalAttack": 476,
+ "physicalCritChance": 39,
+ "strength": 173
+ },
+ "items": [
+ 74,
+ 87,
+ 90,
+ 91,
+ 101,
+ 122
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 78,
+ "armor": 1020,
+ "hp": 12425,
+ "intelligence": 68,
+ "magicResist": 80,
+ "physicalAttack": 724,
+ "physicalCritChance": 99,
+ "strength": 211
+ },
+ "items": [
+ 61,
+ 90,
+ 91,
+ 122,
+ 125,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 104,
+ "armor": 1260,
+ "hp": 14425,
+ "intelligence": 94,
+ "magicResist": 240,
+ "physicalAttack": 1118,
+ "physicalCritChance": 129,
+ "strength": 283
+ },
+ "items": [
+ 85,
+ 122,
+ 101,
+ 125,
+ 131,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 173,
+ "armor": 1420,
+ "hp": 15425,
+ "intelligence": 163,
+ "magicResist": 400,
+ "physicalAttack": 1442,
+ "physicalCritChance": 189,
+ "strength": 440
+ },
+ "items": [
+ 101,
+ 122,
+ 125,
+ 136,
+ 167,
+ 170
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 213,
+ "armor": 1580,
+ "hp": 21425,
+ "intelligence": 203,
+ "magicResist": 560,
+ "physicalAttack": 1766,
+ "physicalCritChance": 249,
+ "strength": 637
+ },
+ "items": [
+ 125,
+ 134,
+ 136,
+ 167,
+ 170,
+ 177
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 253,
+ "armor": 1580,
+ "hp": 27425,
+ "intelligence": 243,
+ "magicResist": 976,
+ "physicalAttack": 2327,
+ "physicalCritChance": 415,
+ "strength": 834
+ },
+ "items": [
+ 125,
+ 125,
+ 167,
+ 177,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 303,
+ "armor": 1580,
+ "hp": 36625,
+ "intelligence": 293,
+ "magicResist": 1296,
+ "physicalAttack": 3399,
+ "physicalCritChance": 581,
+ "strength": 1058
+ },
+ "items": [
+ 122,
+ 131,
+ 177,
+ 183,
+ 201,
+ 207
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 329,
+ "armor": 2060,
+ "hp": 46545,
+ "intelligence": 319,
+ "magicResist": 1296,
+ "physicalAttack": 5305,
+ "physicalCritChance": 1141,
+ "strength": 1114
+ },
+ "items": [
+ 134,
+ 139,
+ 167,
+ 177,
+ 207,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 407,
+ "armor": 2060,
+ "hp": 52545,
+ "intelligence": 397,
+ "magicResist": 1961,
+ "physicalAttack": 6976,
+ "physicalCritChance": 1701,
+ "strength": 1471
+ },
+ "items": [
+ 183,
+ 201,
+ 185,
+ 211,
+ 221,
+ 224
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 533,
+ "armor": 2380,
+ "hp": 74465,
+ "intelligence": 523,
+ "magicResist": 1961,
+ "physicalAttack": 8000,
+ "physicalCritChance": 1701,
+ "strength": 2268
+ },
+ "items": [
+ 183,
+ 185,
+ 211,
+ 229,
+ 221,
+ 237
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 659,
+ "armor": 4620,
+ "hp": 84385,
+ "intelligence": 649,
+ "magicResist": 1961,
+ "physicalAttack": 9024,
+ "physicalCritChance": 2034,
+ "strength": 3065
+ },
+ "items": [
+ 183,
+ 179,
+ 229,
+ 228,
+ 240,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 9,
+ 7,
+ 1
+ ],
+ "artifacts": [
+ 1061,
+ 2001,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 10,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero61_julius",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0061",
+ "epicArtAsset": {
+ "name": "61_julius_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": -2,
+ "y": 19
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "61_julius"
+ },
+ "role": "front",
+ "obtainType": "chest:town",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_tank"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 4,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 410,
+ 411,
+ 412,
+ 413,
+ 414
+ ]
+ },
+ "62": {
+ "id": 62,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 20,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 4,
+ 7,
+ 7,
+ 8,
+ 11
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 12,
+ "magicPower": 50,
+ "strength": 3
+ },
+ "items": [
+ 7,
+ 8,
+ 11,
+ 19,
+ 28,
+ 32
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 50,
+ "hp": 200,
+ "intelligence": 26,
+ "magicPenetration": 50,
+ "magicPower": 125,
+ "magicResist": 50,
+ "strength": 6
+ },
+ "items": [
+ 13,
+ 19,
+ 26,
+ 24,
+ 34,
+ 41
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 100,
+ "hp": 1085,
+ "intelligence": 57,
+ "magicPenetration": 50,
+ "magicPower": 225,
+ "magicResist": 50,
+ "strength": 13
+ },
+ "items": [
+ 22,
+ 27,
+ 40,
+ 44,
+ 58,
+ 75
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 200,
+ "hp": 1085,
+ "intelligence": 94,
+ "magicPenetration": 50,
+ "magicPower": 375,
+ "magicResist": 100,
+ "strength": 20
+ },
+ "items": [
+ 26,
+ 24,
+ 41,
+ 56,
+ 58,
+ 86
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 250,
+ "hp": 2585,
+ "intelligence": 121,
+ "magicPenetration": 50,
+ "magicPower": 525,
+ "magicResist": 200,
+ "strength": 27
+ },
+ "items": [
+ 41,
+ 44,
+ 56,
+ 58,
+ 75,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 450,
+ "hp": 4385,
+ "intelligence": 148,
+ "magicPenetration": 50,
+ "magicPower": 705,
+ "magicResist": 250,
+ "strength": 34
+ },
+ "items": [
+ 59,
+ 56,
+ 71,
+ 63,
+ 88,
+ 126
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 650,
+ "hp": 8585,
+ "intelligence": 150,
+ "magicPenetration": 130,
+ "magicPower": 1265,
+ "magicResist": 250,
+ "strength": 36
+ },
+ "items": [
+ 56,
+ 68,
+ 71,
+ 95,
+ 115,
+ 127
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 68,
+ "armor": 730,
+ "hp": 9585,
+ "intelligence": 220,
+ "magicPenetration": 210,
+ "magicPower": 1585,
+ "magicResist": 410,
+ "strength": 68
+ },
+ "items": [
+ 75,
+ 85,
+ 88,
+ 116,
+ 126,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 104,
+ "armor": 830,
+ "hp": 12985,
+ "intelligence": 326,
+ "magicPenetration": 210,
+ "magicPower": 2145,
+ "magicResist": 570,
+ "strength": 114
+ },
+ "items": [
+ 58,
+ 56,
+ 98,
+ 99,
+ 137,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 144,
+ "armor": 1030,
+ "hp": 18081,
+ "intelligence": 414,
+ "magicPenetration": 410,
+ "magicPower": 3064,
+ "magicResist": 570,
+ "strength": 154
+ },
+ "items": [
+ 93,
+ 98,
+ 137,
+ 137,
+ 171,
+ 169
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 222,
+ "armor": 1030,
+ "hp": 18081,
+ "intelligence": 697,
+ "magicPenetration": 610,
+ "magicPower": 3864,
+ "magicResist": 570,
+ "strength": 232
+ },
+ "items": [
+ 95,
+ 93,
+ 137,
+ 171,
+ 169,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 262,
+ "armor": 1030,
+ "hp": 21281,
+ "intelligence": 932,
+ "magicPenetration": 610,
+ "magicPower": 5624,
+ "magicResist": 570,
+ "strength": 272
+ },
+ "items": [
+ 99,
+ 93,
+ 181,
+ 183,
+ 180,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 312,
+ "armor": 1550,
+ "hp": 29281,
+ "intelligence": 1156,
+ "magicPenetration": 930,
+ "magicPower": 7264,
+ "magicResist": 570,
+ "strength": 322
+ },
+ "items": [
+ 95,
+ 91,
+ 180,
+ 181,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 314,
+ "armor": 1550,
+ "hp": 37041,
+ "intelligence": 1469,
+ "magicPenetration": 1250,
+ "magicPower": 11200,
+ "magicResist": 570,
+ "strength": 324
+ },
+ "items": [
+ 95,
+ 93,
+ 181,
+ 203,
+ 210,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 392,
+ "armor": 1550,
+ "hp": 39601,
+ "intelligence": 2016,
+ "magicPenetration": 1570,
+ "magicPower": 14088,
+ "magicResist": 570,
+ "strength": 576
+ },
+ "items": [
+ 171,
+ 210,
+ 186,
+ 212,
+ 222,
+ 226
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 518,
+ "armor": 1550,
+ "hp": 42161,
+ "intelligence": 2922,
+ "magicPenetration": 1570,
+ "magicPower": 16536,
+ "magicResist": 570,
+ "strength": 876
+ },
+ "items": [
+ 181,
+ 186,
+ 212,
+ 226,
+ 222,
+ 238
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 644,
+ "armor": 1550,
+ "hp": 42161,
+ "intelligence": 3719,
+ "magicPenetration": 1890,
+ "magicPower": 20136,
+ "magicResist": 2490,
+ "strength": 1002
+ },
+ "items": [
+ 181,
+ 203,
+ 226,
+ 232,
+ 233,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 7,
+ 11,
+ 2
+ ],
+ "artifacts": [
+ 1062,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2023,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero62_polaris",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0062",
+ "spineEpicArtAsset": {
+ "name": "62_polaris"
+ },
+ "role": "back",
+ "obtainType": "chest:town",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero62_battle_animation",
+ "summonCinematic": "hero62_cinematic_long.mp4",
+ "summonSound": "hero62_summon_cinematic_audio"
+ },
+ "roleExtended": [
+ "control",
+ "mage"
+ ],
+ "sfxAsset": "hero62_sfx",
+ "musicAsset": {
+ "assetIdent": "sound_theme_62_polaris",
+ "clipIdent": "theme_polaris"
+ },
+ "perk": [
+ 8,
+ 7,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 436,
+ 437,
+ 438,
+ 439,
+ 440
+ ]
+ },
+ "63": {
+ "id": 63,
+ "baseStats": {
+ "agility": 25,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 60,
+ "strength": 10
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 25,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 3,
+ 6,
+ 2,
+ 14,
+ 9
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 400,
+ "intelligence": 7,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 7
+ },
+ "items": [
+ 1,
+ 2,
+ 12,
+ 24,
+ 29,
+ 35
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 31,
+ "hp": 1100,
+ "intelligence": 15,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "physicalCritChance": 15,
+ "strength": 15
+ },
+ "items": [
+ 12,
+ 20,
+ 25,
+ 31,
+ 42,
+ 39
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 55,
+ "armorPenetration": 50,
+ "hp": 1600,
+ "intelligence": 18,
+ "magicResist": 75,
+ "physicalAttack": 128,
+ "physicalCritChance": 15,
+ "strength": 18
+ },
+ "items": [
+ 24,
+ 29,
+ 42,
+ 50,
+ 57,
+ 61
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 72,
+ "armorPenetration": 50,
+ "hp": 2600,
+ "intelligence": 20,
+ "magicResist": 117,
+ "physicalAttack": 287,
+ "physicalCritChance": 60,
+ "strength": 20
+ },
+ "items": [
+ 25,
+ 42,
+ 55,
+ 61,
+ 72,
+ 76
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 89,
+ "armorPenetration": 50,
+ "hp": 3100,
+ "intelligence": 27,
+ "magicResist": 117,
+ "physicalAttack": 442,
+ "physicalCritChance": 129,
+ "strength": 27
+ },
+ "items": [
+ 42,
+ 55,
+ 53,
+ 56,
+ 76,
+ 89
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 122,
+ "armorPenetration": 100,
+ "hp": 4600,
+ "intelligence": 34,
+ "magicResist": 117,
+ "physicalAttack": 597,
+ "physicalCritChance": 174,
+ "strength": 34
+ },
+ "items": [
+ 56,
+ 61,
+ 72,
+ 87,
+ 97,
+ 101
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 139,
+ "armorPenetration": 300,
+ "hp": 5600,
+ "intelligence": 41,
+ "magicResist": 117,
+ "physicalAttack": 723,
+ "physicalCritChance": 288,
+ "strength": 41
+ },
+ "items": [
+ 61,
+ 66,
+ 87,
+ 101,
+ 114,
+ 120
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 202,
+ "armorPenetration": 300,
+ "hp": 7200,
+ "intelligence": 48,
+ "magicResist": 117,
+ "physicalAttack": 1173,
+ "physicalCritChance": 378,
+ "strength": 48
+ },
+ "items": [
+ 76,
+ 87,
+ 101,
+ 114,
+ 118,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 318,
+ "armorPenetration": 460,
+ "hp": 8800,
+ "intelligence": 84,
+ "magicResist": 117,
+ "physicalAttack": 1567,
+ "physicalCritChance": 438,
+ "strength": 84
+ },
+ "items": [
+ 85,
+ 101,
+ 91,
+ 114,
+ 133,
+ 139
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 379,
+ "armorPenetration": 460,
+ "hp": 13400,
+ "intelligence": 115,
+ "magicResist": 526,
+ "physicalAttack": 2335,
+ "physicalCritChance": 498,
+ "strength": 125
+ },
+ "items": [
+ 101,
+ 114,
+ 118,
+ 138,
+ 167,
+ 177
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 497,
+ "armorPenetration": 620,
+ "hp": 21000,
+ "intelligence": 155,
+ "magicResist": 526,
+ "physicalAttack": 2659,
+ "physicalCritChance": 724,
+ "strength": 165
+ },
+ "items": [
+ 120,
+ 114,
+ 118,
+ 177,
+ 179,
+ 182
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 559,
+ "armorPenetration": 1100,
+ "hp": 25800,
+ "intelligence": 157,
+ "magicResist": 526,
+ "physicalAttack": 4051,
+ "physicalCritChance": 890,
+ "strength": 167
+ },
+ "items": [
+ 114,
+ 122,
+ 167,
+ 172,
+ 187,
+ 188
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 892,
+ "armor": 160,
+ "armorPenetration": 1100,
+ "hp": 33400,
+ "intelligence": 207,
+ "magicResist": 526,
+ "physicalAttack": 4695,
+ "physicalCritChance": 1118,
+ "strength": 217
+ },
+ "items": [
+ 133,
+ 138,
+ 172,
+ 183,
+ 188,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1143,
+ "armor": 480,
+ "armorPenetration": 1100,
+ "hp": 44856,
+ "intelligence": 271,
+ "magicResist": 526,
+ "physicalAttack": 6346,
+ "physicalCritChance": 1346,
+ "strength": 281
+ },
+ "items": [
+ 138,
+ 118,
+ 182,
+ 202,
+ 201,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1616,
+ "armor": 480,
+ "armorPenetration": 1580,
+ "hp": 49976,
+ "intelligence": 387,
+ "magicResist": 526,
+ "physicalAttack": 8446,
+ "physicalCritChance": 1611,
+ "strength": 397
+ },
+ "items": [
+ 167,
+ 201,
+ 208,
+ 213,
+ 224,
+ 231
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 1973,
+ "armor": 480,
+ "armorPenetration": 2780,
+ "hp": 79752,
+ "intelligence": 465,
+ "magicResist": 526,
+ "physicalAttack": 10801,
+ "physicalCritChance": 1611,
+ "strength": 475
+ },
+ "items": [
+ 183,
+ 187,
+ 213,
+ 224,
+ 231,
+ 236
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2552,
+ "armor": 800,
+ "armorPenetration": 3980,
+ "hp": 101672,
+ "intelligence": 591,
+ "magicResist": 526,
+ "physicalAttack": 11825,
+ "physicalCritChance": 2143,
+ "strength": 601
+ },
+ "items": [
+ 184,
+ 188,
+ 224,
+ 231,
+ 236,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 9,
+ 12,
+ 3
+ ],
+ "artifacts": [
+ 1063,
+ 2001,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 2008,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero63_laracroft",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0063",
+ "spineEpicArtAsset": {
+ "name": "63_laracroft_epic"
+ },
+ "role": "back",
+ "obtainType": "unavailable",
+ "characterType": "cutie",
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero63_battle_animation"
+ },
+ "roleExtended": [
+ "ranged_dps"
+ ],
+ "sfxAsset": "hero63_sfx",
+ "musicAsset": {
+ "assetIdent": "sound_theme_63_laracroft",
+ "clipIdent": "theme_laracroft"
+ },
+ "perk": [
+ 6,
+ 1,
+ 21
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 441,
+ "1": 442,
+ "2": 443,
+ "3": 444,
+ "4": 445,
+ "7": 8272,
+ "8": 8273
+ }
+ },
+ "64": {
+ "id": 64,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 20,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 8,
+ 9,
+ 4,
+ 13,
+ 16,
+ 11
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 19,
+ "magicResist": 25,
+ "strength": 3
+ },
+ "items": [
+ 9,
+ 13,
+ 11,
+ 19,
+ 28,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 25,
+ "hp": 770,
+ "intelligence": 33,
+ "magicPower": 100,
+ "magicResist": 100,
+ "strength": 6
+ },
+ "items": [
+ 13,
+ 19,
+ 32,
+ 28,
+ 34,
+ 40
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 25,
+ "hp": 1155,
+ "intelligence": 64,
+ "magicPenetration": 50,
+ "magicPower": 200,
+ "magicResist": 150,
+ "strength": 13
+ },
+ "items": [
+ 22,
+ 24,
+ 28,
+ 46,
+ 68,
+ 75
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 105,
+ "hp": 2155,
+ "intelligence": 91,
+ "magicPenetration": 50,
+ "magicPower": 330,
+ "magicResist": 200,
+ "strength": 20
+ },
+ "items": [
+ 27,
+ 24,
+ 41,
+ 58,
+ 60,
+ 86
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 205,
+ "hp": 2655,
+ "intelligence": 118,
+ "magicPenetration": 50,
+ "magicPower": 430,
+ "magicResist": 400,
+ "strength": 27
+ },
+ "items": [
+ 41,
+ 40,
+ 56,
+ 60,
+ 75,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 355,
+ "hp": 4455,
+ "intelligence": 155,
+ "magicPenetration": 50,
+ "magicPower": 560,
+ "magicResist": 500,
+ "strength": 34
+ },
+ "items": [
+ 56,
+ 59,
+ 67,
+ 68,
+ 88,
+ 119
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 635,
+ "hp": 6255,
+ "intelligence": 187,
+ "magicPenetration": 50,
+ "magicPower": 960,
+ "magicResist": 580,
+ "strength": 36
+ },
+ "items": [
+ 71,
+ 63,
+ 68,
+ 88,
+ 100,
+ 127
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 68,
+ "armor": 815,
+ "hp": 7855,
+ "intelligence": 219,
+ "magicPenetration": 130,
+ "magicPower": 1280,
+ "magicResist": 780,
+ "strength": 68
+ },
+ "items": [
+ 75,
+ 86,
+ 88,
+ 119,
+ 116,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 104,
+ "armor": 915,
+ "hp": 8655,
+ "intelligence": 365,
+ "magicPenetration": 130,
+ "magicPower": 1680,
+ "magicResist": 1040,
+ "strength": 104
+ },
+ "items": [
+ 59,
+ 56,
+ 98,
+ 95,
+ 137,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 144,
+ "armor": 1015,
+ "hp": 13751,
+ "intelligence": 491,
+ "magicPenetration": 330,
+ "magicPower": 2499,
+ "magicResist": 1040,
+ "strength": 144
+ },
+ "items": [
+ 98,
+ 119,
+ 135,
+ 137,
+ 171,
+ 169
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 184,
+ "armor": 1015,
+ "hp": 16311,
+ "intelligence": 718,
+ "magicPenetration": 530,
+ "magicPower": 3771,
+ "magicResist": 1040,
+ "strength": 184
+ },
+ "items": [
+ 100,
+ 98,
+ 137,
+ 171,
+ 167,
+ 180
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 224,
+ "armor": 1015,
+ "hp": 25511,
+ "intelligence": 915,
+ "magicPenetration": 730,
+ "magicPower": 4731,
+ "magicResist": 1240,
+ "strength": 224
+ },
+ "items": [
+ 98,
+ 99,
+ 183,
+ 184,
+ 180,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 274,
+ "armor": 1535,
+ "hp": 38311,
+ "intelligence": 1139,
+ "magicPenetration": 930,
+ "magicPower": 5691,
+ "magicResist": 1560,
+ "strength": 274
+ },
+ "items": [
+ 95,
+ 100,
+ 183,
+ 180,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 276,
+ "armor": 1855,
+ "hp": 48871,
+ "intelligence": 1452,
+ "magicPenetration": 930,
+ "magicPower": 9147,
+ "magicResist": 1760,
+ "strength": 276
+ },
+ "items": [
+ 98,
+ 91,
+ 181,
+ 203,
+ 209,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 354,
+ "armor": 1855,
+ "hp": 53431,
+ "intelligence": 2082,
+ "magicPenetration": 1450,
+ "magicPower": 12123,
+ "magicResist": 1760,
+ "strength": 354
+ },
+ "items": [
+ 181,
+ 212,
+ 180,
+ 209,
+ 234,
+ 224
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 432,
+ "armor": 1855,
+ "hp": 76311,
+ "intelligence": 2560,
+ "magicPenetration": 1770,
+ "magicPower": 18555,
+ "magicResist": 1760,
+ "strength": 432
+ },
+ "items": [
+ 183,
+ 212,
+ 203,
+ 222,
+ 224,
+ 233
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 510,
+ "armor": 2175,
+ "hp": 93111,
+ "intelligence": 3287,
+ "magicPenetration": 2538,
+ "magicPower": 22587,
+ "magicResist": 1760,
+ "strength": 510
+ },
+ "items": [
+ 181,
+ 203,
+ 224,
+ 232,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 7,
+ 11,
+ 2
+ ],
+ "artifacts": [
+ 1064,
+ 2005,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2012,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero64_augustus",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0064",
+ "epicArtAsset": {
+ "name": "61_julius_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.05,
+ 1.05
+ ],
+ "screen": "obtain",
+ "x": -2,
+ "y": 19
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "64_augustus"
+ },
+ "role": "back",
+ "obtainType": "chest:town",
+ "characterType": "snob",
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero64_battle_animation"
+ },
+ "roleExtended": [
+ "mage"
+ ],
+ "sfxAsset": "hero64_sfx",
+ "musicAsset": {
+ "assetIdent": "sound_theme_64_augustus",
+ "clipIdent": "theme_augustus"
+ },
+ "perk": [
+ 7,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "assetsIdent": null,
+ "skill": [
+ 446,
+ 447,
+ 448,
+ 449,
+ 450
+ ]
+ },
+ "65": {
+ "id": 65,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 14,
+ 3,
+ 9,
+ 13,
+ 8,
+ 20
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 50,
+ "strength": 2
+ },
+ "items": [
+ 20,
+ 18,
+ 12,
+ 24,
+ 28,
+ 27
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 75,
+ "hp": 1270,
+ "intelligence": 5,
+ "magicResist": 75,
+ "physicalAttack": 75,
+ "strength": 12
+ },
+ "items": [
+ 20,
+ 24,
+ 24,
+ 39,
+ 20,
+ 43
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 54,
+ "armor": 125,
+ "hp": 2270,
+ "intelligence": 7,
+ "magicResist": 125,
+ "physicalAttack": 158,
+ "strength": 14
+ },
+ "items": [
+ 23,
+ 24,
+ 28,
+ 50,
+ 59,
+ 56
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 81,
+ "armor": 225,
+ "hp": 3770,
+ "intelligence": 9,
+ "magicResist": 217,
+ "physicalAttack": 214,
+ "strength": 16
+ },
+ "items": [
+ 23,
+ 38,
+ 53,
+ 59,
+ 64,
+ 87
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 118,
+ "armor": 325,
+ "armorPenetration": 50,
+ "hp": 4570,
+ "intelligence": 16,
+ "magicResist": 297,
+ "physicalAttack": 350,
+ "strength": 23
+ },
+ "items": [
+ 44,
+ 42,
+ 56,
+ 65,
+ 76,
+ 87
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 150,
+ "armor": 375,
+ "armorPenetration": 50,
+ "hp": 6070,
+ "intelligence": 28,
+ "magicResist": 427,
+ "physicalAttack": 509,
+ "strength": 35
+ },
+ "items": [
+ 69,
+ 76,
+ 64,
+ 70,
+ 87,
+ 121
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 212,
+ "armor": 455,
+ "armorPenetration": 130,
+ "hp": 6870,
+ "intelligence": 40,
+ "magicResist": 667,
+ "physicalAttack": 635,
+ "strength": 63
+ },
+ "items": [
+ 66,
+ 64,
+ 56,
+ 64,
+ 118,
+ 114
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 260,
+ "armor": 455,
+ "armorPenetration": 290,
+ "hp": 11070,
+ "intelligence": 42,
+ "magicResist": 827,
+ "physicalAttack": 1015,
+ "strength": 65
+ },
+ "items": [
+ 87,
+ 85,
+ 69,
+ 134,
+ 122,
+ 133
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 336,
+ "armor": 695,
+ "armorPenetration": 290,
+ "hp": 12070,
+ "intelligence": 78,
+ "magicResist": 1083,
+ "physicalAttack": 1538,
+ "strength": 127
+ },
+ "items": [
+ 69,
+ 66,
+ 91,
+ 134,
+ 138,
+ 134
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 440,
+ "armor": 775,
+ "armorPenetration": 290,
+ "hp": 14070,
+ "intelligence": 118,
+ "magicResist": 1595,
+ "physicalAttack": 2284,
+ "strength": 183
+ },
+ "items": [
+ 133,
+ 122,
+ 138,
+ 134,
+ 172,
+ 167
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 691,
+ "armor": 935,
+ "armorPenetration": 290,
+ "hp": 20070,
+ "intelligence": 182,
+ "magicResist": 1851,
+ "physicalAttack": 2737,
+ "strength": 247
+ },
+ "items": [
+ 114,
+ 139,
+ 138,
+ 172,
+ 176,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 888,
+ "armor": 1255,
+ "armorPenetration": 290,
+ "hp": 26470,
+ "intelligence": 222,
+ "magicResist": 2860,
+ "physicalAttack": 3505,
+ "strength": 287
+ },
+ "items": [
+ 139,
+ 114,
+ 175,
+ 187,
+ 183,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1334,
+ "armor": 2175,
+ "armorPenetration": 290,
+ "hp": 32870,
+ "intelligence": 320,
+ "magicResist": 3269,
+ "physicalAttack": 4273,
+ "strength": 385
+ },
+ "items": [
+ 139,
+ 138,
+ 179,
+ 182,
+ 187,
+ 201
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1644,
+ "armor": 2175,
+ "armorPenetration": 610,
+ "hp": 41190,
+ "intelligence": 408,
+ "magicResist": 3678,
+ "physicalAttack": 6809,
+ "strength": 473
+ },
+ "items": [
+ 139,
+ 138,
+ 187,
+ 179,
+ 183,
+ 255
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1954,
+ "armor": 3895,
+ "armorPenetration": 610,
+ "hp": 49190,
+ "intelligence": 496,
+ "magicResist": 5487,
+ "physicalAttack": 8001,
+ "strength": 561
+ },
+ "items": [
+ 179,
+ 201,
+ 213,
+ 208,
+ 256,
+ 259
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2311,
+ "armor": 6481,
+ "armorPenetration": 610,
+ "hp": 90038,
+ "intelligence": 574,
+ "magicResist": 7211,
+ "physicalAttack": 12144,
+ "strength": 639
+ },
+ "items": [
+ 201,
+ 213,
+ 208,
+ 257,
+ 258,
+ 260
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 3497,
+ "armor": 8205,
+ "armorPenetration": 610,
+ "hp": 119062,
+ "intelligence": 983,
+ "magicResist": 9797,
+ "physicalAttack": 16222,
+ "strength": 1048
+ },
+ "items": [
+ 208,
+ 256,
+ 257,
+ 258,
+ 259,
+ 260
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 12,
+ 7,
+ 3
+ ],
+ "artifacts": [
+ 1065,
+ 2004,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 25,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero65_tmnt",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0065",
+ "epicArtAsset": {
+ "name": "41_k_arkh_epic.jpg",
+ "transform": [
+ {
+ "scale": [
+ -1.11,
+ 1.1
+ ],
+ "screen": "obtain",
+ "x": 72,
+ "y": 104
+ }
+ ]
+ },
+ "spineEpicArtAsset": {
+ "name": "65_tmnt"
+ },
+ "role": "front",
+ "obtainType": "unavailable",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "melee_dps"
+ ],
+ "musicAsset": {
+ "assetIdent": "sound_theme_65_tmnt",
+ "clipIdent": "theme_TMNT"
+ },
+ "perk": [
+ 10,
+ 2,
+ 21
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 456,
+ "1": 457,
+ "2": 458,
+ "3": 459,
+ "4": 460,
+ "7": 8276,
+ "8": 8277
+ }
+ },
+ "66": {
+ "id": 66,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 10
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 20,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 4,
+ 9,
+ 16,
+ 11,
+ 8,
+ 13
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 19,
+ "magicResist": 25,
+ "strength": 3
+ },
+ "items": [
+ 7,
+ 13,
+ 8,
+ 19,
+ 32,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 50,
+ "hp": 770,
+ "intelligence": 28,
+ "magicPenetration": 50,
+ "magicPower": 75,
+ "magicResist": 75,
+ "strength": 5
+ },
+ "items": [
+ 16,
+ 2,
+ 34,
+ 40,
+ 41,
+ 32
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 100,
+ "hp": 970,
+ "intelligence": 69,
+ "magicPenetration": 100,
+ "magicPower": 125,
+ "magicResist": 75,
+ "strength": 12
+ },
+ "items": [
+ 52,
+ 40,
+ 24,
+ 26,
+ 58,
+ 71
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 100,
+ "hp": 1470,
+ "intelligence": 91,
+ "magicPenetration": 230,
+ "magicPower": 405,
+ "magicResist": 75,
+ "strength": 14
+ },
+ "items": [
+ 41,
+ 45,
+ 22,
+ 60,
+ 67,
+ 76
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 150,
+ "hp": 1470,
+ "intelligence": 118,
+ "magicPenetration": 230,
+ "magicPower": 535,
+ "magicResist": 305,
+ "strength": 21
+ },
+ "items": [
+ 45,
+ 34,
+ 58,
+ 63,
+ 71,
+ 71
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 38,
+ "armor": 150,
+ "hp": 2270,
+ "intelligence": 132,
+ "magicPenetration": 390,
+ "magicPower": 925,
+ "magicResist": 355,
+ "strength": 28
+ },
+ "items": [
+ 88,
+ 63,
+ 86,
+ 67,
+ 58,
+ 93
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 45,
+ "armor": 250,
+ "hp": 3870,
+ "intelligence": 149,
+ "magicPenetration": 390,
+ "magicPower": 1465,
+ "magicResist": 535,
+ "strength": 35
+ },
+ "items": [
+ 58,
+ 68,
+ 75,
+ 71,
+ 117,
+ 115
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 52,
+ "armor": 330,
+ "hp": 3870,
+ "intelligence": 196,
+ "magicPenetration": 630,
+ "magicPower": 2045,
+ "magicResist": 695,
+ "strength": 42
+ },
+ "items": [
+ 88,
+ 67,
+ 75,
+ 119,
+ 126,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 59,
+ "armor": 430,
+ "hp": 8830,
+ "intelligence": 243,
+ "magicPenetration": 630,
+ "magicPower": 3197,
+ "magicResist": 775,
+ "strength": 49
+ },
+ "items": [
+ 86,
+ 71,
+ 100,
+ 117,
+ 132,
+ 140
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 90,
+ "armor": 430,
+ "hp": 12926,
+ "intelligence": 344,
+ "magicPenetration": 870,
+ "magicPower": 4256,
+ "magicResist": 1075,
+ "strength": 80
+ },
+ "items": [
+ 115,
+ 137,
+ 135,
+ 93,
+ 169,
+ 176
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 130,
+ "armor": 430,
+ "hp": 15486,
+ "intelligence": 432,
+ "magicPenetration": 870,
+ "magicPower": 5728,
+ "magicResist": 1835,
+ "strength": 120
+ },
+ "items": [
+ 115,
+ 126,
+ 132,
+ 174,
+ 181,
+ 184
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 156,
+ "armor": 430,
+ "hp": 21886,
+ "intelligence": 488,
+ "magicPenetration": 1790,
+ "magicPower": 6688,
+ "magicResist": 2315,
+ "strength": 146
+ },
+ "items": [
+ 140,
+ 119,
+ 181,
+ 174,
+ 180,
+ 171
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 158,
+ "armor": 430,
+ "hp": 29182,
+ "intelligence": 629,
+ "magicPenetration": 2710,
+ "magicPower": 9107,
+ "magicResist": 2315,
+ "strength": 148
+ },
+ "items": [
+ 126,
+ 135,
+ 169,
+ 209,
+ 180,
+ 186
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 208,
+ "armor": 430,
+ "hp": 39102,
+ "intelligence": 974,
+ "magicPenetration": 2710,
+ "magicPower": 13035,
+ "magicResist": 2315,
+ "strength": 198
+ },
+ "items": [
+ 119,
+ 116,
+ 203,
+ 209,
+ 203,
+ 180
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 210,
+ "armor": 430,
+ "hp": 44862,
+ "intelligence": 1461,
+ "magicPenetration": 2710,
+ "magicPower": 17771,
+ "magicResist": 2475,
+ "strength": 200
+ },
+ "items": [
+ 181,
+ 169,
+ 171,
+ 212,
+ 233,
+ 222
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 288,
+ "armor": 430,
+ "hp": 44862,
+ "intelligence": 2145,
+ "magicPenetration": 3798,
+ "magicPower": 21923,
+ "magicResist": 2475,
+ "strength": 278
+ },
+ "items": [
+ 183,
+ 212,
+ 203,
+ 222,
+ 224,
+ 233
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 366,
+ "armor": 750,
+ "hp": 61662,
+ "intelligence": 2872,
+ "magicPenetration": 4566,
+ "magicPower": 25955,
+ "magicResist": 2475,
+ "strength": 356
+ },
+ "items": [
+ 169,
+ 175,
+ 222,
+ 234,
+ 242,
+ 238
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 7,
+ 11,
+ 2
+ ],
+ "artifacts": [
+ 1066,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2003,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero66_folio",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0066",
+ "spineEpicArtAsset": {
+ "name": "66_folio"
+ },
+ "role": "back",
+ "obtainType": "chest:town",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero66_battle_animation"
+ },
+ "roleExtended": [
+ "mage"
+ ],
+ "sfxAsset": "hero66_sfx",
+ "musicAsset": {
+ "assetIdent": "sound_theme_66_folio",
+ "clipIdent": "theme_folio"
+ },
+ "perk": [
+ 7,
+ 2
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 463,
+ 464,
+ 465,
+ 466,
+ 467
+ ]
+ },
+ "67": {
+ "id": 67,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 5,
+ 8,
+ 14,
+ 9,
+ 10,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 25,
+ "intelligence": 3,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 19
+ },
+ "items": [
+ 9,
+ 10,
+ 13,
+ 18,
+ 25,
+ 31
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 25,
+ "armorPenetration": 50,
+ "hp": 770,
+ "intelligence": 6,
+ "magicResist": 50,
+ "physicalAttack": 58,
+ "strength": 33
+ },
+ "items": [
+ 10,
+ 18,
+ 21,
+ 31,
+ 44,
+ 43
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 125,
+ "armorPenetration": 100,
+ "hp": 1155,
+ "intelligence": 9,
+ "magicResist": 100,
+ "physicalAttack": 91,
+ "strength": 57
+ },
+ "items": [
+ 21,
+ 37,
+ 42,
+ 44,
+ 57,
+ 59
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 325,
+ "armorPenetration": 100,
+ "hp": 1655,
+ "intelligence": 11,
+ "magicResist": 150,
+ "physicalAttack": 194,
+ "strength": 79
+ },
+ "items": [
+ 33,
+ 36,
+ 43,
+ 74,
+ 57,
+ 59
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 475,
+ "armorPenetration": 100,
+ "hp": 2155,
+ "intelligence": 23,
+ "magicResist": 150,
+ "physicalAttack": 297,
+ "strength": 118
+ },
+ "items": [
+ 42,
+ 24,
+ 59,
+ 57,
+ 69,
+ 84
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 45,
+ "armor": 655,
+ "armorPenetration": 100,
+ "hp": 3155,
+ "intelligence": 45,
+ "magicResist": 150,
+ "physicalAttack": 400,
+ "strength": 156
+ },
+ "items": [
+ 74,
+ 69,
+ 74,
+ 90,
+ 99,
+ 92
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 57,
+ "armor": 1015,
+ "armorPenetration": 100,
+ "hp": 3155,
+ "intelligence": 57,
+ "magicResist": 150,
+ "physicalAttack": 605,
+ "strength": 220
+ },
+ "items": [
+ 74,
+ 85,
+ 69,
+ 92,
+ 127,
+ 92
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 99,
+ "armor": 1095,
+ "armorPenetration": 100,
+ "hp": 4155,
+ "intelligence": 99,
+ "magicResist": 150,
+ "physicalAttack": 875,
+ "strength": 298
+ },
+ "items": [
+ 74,
+ 90,
+ 122,
+ 92,
+ 125,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 130,
+ "armor": 1335,
+ "armorPenetration": 100,
+ "hp": 4155,
+ "intelligence": 130,
+ "magicResist": 310,
+ "physicalAttack": 1404,
+ "strength": 385
+ },
+ "items": [
+ 85,
+ 122,
+ 123,
+ 125,
+ 114,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 175,
+ "armor": 1495,
+ "armorPenetration": 100,
+ "hp": 8355,
+ "intelligence": 175,
+ "magicResist": 470,
+ "physicalAttack": 1944,
+ "strength": 518
+ },
+ "items": [
+ 123,
+ 122,
+ 134,
+ 136,
+ 168,
+ 170
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 215,
+ "armor": 1655,
+ "armorPenetration": 100,
+ "hp": 9955,
+ "intelligence": 215,
+ "magicResist": 726,
+ "physicalAttack": 2797,
+ "strength": 745
+ },
+ "items": [
+ 114,
+ 125,
+ 136,
+ 170,
+ 168,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 255,
+ "armor": 1975,
+ "armorPenetration": 100,
+ "hp": 16355,
+ "intelligence": 255,
+ "magicResist": 886,
+ "physicalAttack": 3629,
+ "strength": 942
+ },
+ "items": [
+ 123,
+ 125,
+ 168,
+ 170,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 305,
+ "armor": 1975,
+ "armorPenetration": 100,
+ "hp": 21155,
+ "intelligence": 305,
+ "magicResist": 1046,
+ "physicalAttack": 4885,
+ "strength": 1305
+ },
+ "items": [
+ 123,
+ 131,
+ 170,
+ 168,
+ 201,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 331,
+ "armor": 1975,
+ "armorPenetration": 100,
+ "hp": 34531,
+ "intelligence": 331,
+ "magicResist": 1046,
+ "physicalAttack": 7640,
+ "strength": 1500
+ },
+ "items": [
+ 134,
+ 136,
+ 168,
+ 175,
+ 208,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 447,
+ "armor": 2575,
+ "armorPenetration": 100,
+ "hp": 41187,
+ "intelligence": 447,
+ "magicResist": 1302,
+ "physicalAttack": 9716,
+ "strength": 1943
+ },
+ "items": [
+ 168,
+ 179,
+ 208,
+ 211,
+ 227,
+ 221
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 525,
+ "armor": 3775,
+ "armorPenetration": 100,
+ "hp": 51043,
+ "intelligence": 525,
+ "magicResist": 1302,
+ "physicalAttack": 12087,
+ "strength": 2518
+ },
+ "items": [
+ 179,
+ 185,
+ 211,
+ 225,
+ 221,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 651,
+ "armor": 3775,
+ "armorPenetration": 1060,
+ "hp": 59363,
+ "intelligence": 651,
+ "magicResist": 1302,
+ "physicalAttack": 15191,
+ "strength": 3315
+ },
+ "items": [
+ 179,
+ 208,
+ 221,
+ 225,
+ 240,
+ 237
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 7,
+ 4,
+ 8,
+ 1
+ ],
+ "artifacts": [
+ 1067,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 15,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero67_lyria",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0067",
+ "spineEpicArtAsset": {
+ "name": "67_lyria"
+ },
+ "role": "front",
+ "obtainType": "chest:town",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero67_battle_animation"
+ },
+ "roleExtended": [
+ "melee_dps",
+ "support"
+ ],
+ "sfxAsset": "hero67_sfx",
+ "musicAsset": {
+ "assetIdent": "sound_theme_67_lyria",
+ "clipIdent": "theme_lyria"
+ },
+ "perk": [
+ 10,
+ 5,
+ 2,
+ 21,
+ 12
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": {
+ "preorder": "2025-05-05 02:00:00",
+ "full": "2025-05-12 02:00:00"
+ },
+ "epicArtAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 468,
+ 469,
+ 470,
+ 471,
+ 472
+ ]
+ },
+ "68": {
+ "id": 68,
+ "baseStats": {
+ "agility": 15,
+ "hp": 500,
+ "intelligence": 10,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 5,
+ 14,
+ 8,
+ 9,
+ 15,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 25,
+ "strength": 21
+ },
+ "items": [
+ 9,
+ 18,
+ 14,
+ 15,
+ 21,
+ 28
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 4,
+ "magicResist": 100,
+ "physicalAttack": 50,
+ "strength": 47
+ },
+ "items": [
+ 18,
+ 15,
+ 37,
+ 43,
+ 25,
+ 21
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 125,
+ "hp": 770,
+ "intelligence": 6,
+ "magicResist": 100,
+ "physicalAttack": 116,
+ "strength": 83
+ },
+ "items": [
+ 27,
+ 21,
+ 36,
+ 37,
+ 60,
+ 70
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 225,
+ "armorPenetration": 80,
+ "hp": 1270,
+ "intelligence": 8,
+ "magicResist": 200,
+ "physicalAttack": 172,
+ "strength": 115
+ },
+ "items": [
+ 44,
+ 36,
+ 53,
+ 57,
+ 60,
+ 64
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 275,
+ "armorPenetration": 130,
+ "hp": 2570,
+ "intelligence": 10,
+ "magicResist": 430,
+ "physicalAttack": 275,
+ "strength": 127
+ },
+ "items": [
+ 36,
+ 31,
+ 69,
+ 70,
+ 90,
+ 60
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 435,
+ "armorPenetration": 260,
+ "hp": 3070,
+ "intelligence": 12,
+ "magicResist": 530,
+ "physicalAttack": 401,
+ "strength": 171
+ },
+ "items": [
+ 69,
+ 60,
+ 85,
+ 64,
+ 97,
+ 92
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 515,
+ "armorPenetration": 460,
+ "hp": 4870,
+ "intelligence": 19,
+ "magicResist": 710,
+ "physicalAttack": 536,
+ "strength": 204
+ },
+ "items": [
+ 90,
+ 64,
+ 60,
+ 114,
+ 97,
+ 94
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 595,
+ "armorPenetration": 660,
+ "hp": 7270,
+ "intelligence": 21,
+ "magicResist": 890,
+ "physicalAttack": 822,
+ "strength": 260
+ },
+ "items": [
+ 70,
+ 60,
+ 131,
+ 100,
+ 114,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 71,
+ "armor": 595,
+ "armorPenetration": 740,
+ "hp": 8870,
+ "intelligence": 71,
+ "magicResist": 1190,
+ "physicalAttack": 1094,
+ "strength": 370
+ },
+ "items": [
+ 90,
+ 131,
+ 136,
+ 114,
+ 97,
+ 122
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 135,
+ "armor": 835,
+ "armorPenetration": 940,
+ "hp": 10470,
+ "intelligence": 135,
+ "magicResist": 1190,
+ "physicalAttack": 1488,
+ "strength": 528
+ },
+ "items": [
+ 114,
+ 136,
+ 97,
+ 114,
+ 176,
+ 170
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 175,
+ "armor": 835,
+ "armorPenetration": 1140,
+ "hp": 13670,
+ "intelligence": 175,
+ "magicResist": 1790,
+ "physicalAttack": 1920,
+ "strength": 725
+ },
+ "items": [
+ 136,
+ 114,
+ 122,
+ 176,
+ 182,
+ 170
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 215,
+ "armor": 995,
+ "armorPenetration": 1460,
+ "hp": 15270,
+ "intelligence": 215,
+ "magicResist": 2390,
+ "physicalAttack": 2564,
+ "strength": 922
+ },
+ "items": [
+ 100,
+ 99,
+ 185,
+ 173,
+ 179,
+ 183
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 265,
+ "armor": 1515,
+ "armorPenetration": 2060,
+ "hp": 23270,
+ "intelligence": 265,
+ "magicResist": 2590,
+ "physicalAttack": 3204,
+ "strength": 1146
+ },
+ "items": [
+ 97,
+ 136,
+ 176,
+ 185,
+ 208,
+ 176
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 353,
+ "armor": 1515,
+ "armorPenetration": 2260,
+ "hp": 29926,
+ "intelligence": 353,
+ "magicResist": 3790,
+ "physicalAttack": 4535,
+ "strength": 1456
+ },
+ "items": [
+ 97,
+ 175,
+ 201,
+ 176,
+ 185,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 479,
+ "armor": 2115,
+ "armorPenetration": 2460,
+ "hp": 35046,
+ "intelligence": 479,
+ "magicResist": 4390,
+ "physicalAttack": 5559,
+ "strength": 2035
+ },
+ "items": [
+ 201,
+ 184,
+ 211,
+ 185,
+ 227,
+ 231
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 605,
+ "armor": 3315,
+ "armorPenetration": 3660,
+ "hp": 44966,
+ "intelligence": 605,
+ "magicResist": 4710,
+ "physicalAttack": 6583,
+ "strength": 2614
+ },
+ "items": [
+ 185,
+ 211,
+ 184,
+ 239,
+ 221,
+ 228
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 731,
+ "armor": 3315,
+ "armorPenetration": 4620,
+ "hp": 54886,
+ "intelligence": 731,
+ "magicResist": 6230,
+ "physicalAttack": 8247,
+ "strength": 3411
+ },
+ "items": [
+ 179,
+ 183,
+ 228,
+ 237,
+ 231,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 12,
+ 7,
+ 1
+ ],
+ "artifacts": [
+ 1068,
+ 2004,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 1003,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero_68_gus",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0068",
+ "spineEpicArtAsset": {
+ "name": "68_gus"
+ },
+ "role": "middle",
+ "obtainType": "shop:invasion:1070",
+ "characterType": "healer",
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero68_battle_animation"
+ },
+ "roleExtended": [
+ "healer",
+ "support"
+ ],
+ "sfxAsset": "hero68_sfx",
+ "musicAsset": {
+ "assetIdent": "sound_theme_68_Guus",
+ "clipIdent": "theme_guus"
+ },
+ "perk": [
+ 9,
+ 5,
+ 2
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": {
+ "preorder": "2025-07-07 02:00:00",
+ "full": "2025-07-14 02:00:00"
+ },
+ "epicArtAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 473,
+ 474,
+ 475,
+ 476,
+ 477
+ ]
+ },
+ "69": {
+ "id": 69,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 20,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 9,
+ 8,
+ 4,
+ 16,
+ 13,
+ 11
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 19,
+ "magicResist": 25,
+ "strength": 3
+ },
+ "items": [
+ 7,
+ 9,
+ 16,
+ 19,
+ 28,
+ 22
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 45,
+ "magicPower": 75,
+ "magicResist": 100,
+ "strength": 5
+ },
+ "items": [
+ 19,
+ 9,
+ 26,
+ 22,
+ 40,
+ 41
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 75,
+ "hp": 385,
+ "intelligence": 84,
+ "magicPower": 225,
+ "magicResist": 125,
+ "strength": 7
+ },
+ "items": [
+ 22,
+ 32,
+ 41,
+ 40,
+ 60,
+ 71
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 125,
+ "hp": 385,
+ "intelligence": 116,
+ "magicPenetration": 130,
+ "magicPower": 355,
+ "magicResist": 225,
+ "strength": 9
+ },
+ "items": [
+ 45,
+ 52,
+ 40,
+ 56,
+ 58,
+ 68
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 205,
+ "hp": 1385,
+ "intelligence": 138,
+ "magicPenetration": 180,
+ "magicPower": 635,
+ "magicResist": 275,
+ "strength": 11
+ },
+ "items": [
+ 22,
+ 40,
+ 58,
+ 63,
+ 68,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 385,
+ "hp": 2985,
+ "intelligence": 160,
+ "magicPenetration": 180,
+ "magicPower": 1025,
+ "magicResist": 275,
+ "strength": 13
+ },
+ "items": [
+ 63,
+ 75,
+ 67,
+ 86,
+ 95,
+ 93
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 25,
+ "armor": 385,
+ "hp": 3785,
+ "intelligence": 230,
+ "magicPenetration": 180,
+ "magicPower": 1385,
+ "magicResist": 455,
+ "strength": 25
+ },
+ "items": [
+ 58,
+ 63,
+ 88,
+ 98,
+ 95,
+ 126
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 485,
+ "hp": 6985,
+ "intelligence": 270,
+ "magicPenetration": 380,
+ "magicPower": 1965,
+ "magicResist": 455,
+ "strength": 27
+ },
+ "items": [
+ 58,
+ 71,
+ 91,
+ 126,
+ 132,
+ 132
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 77,
+ "armor": 485,
+ "hp": 10585,
+ "intelligence": 380,
+ "magicPenetration": 460,
+ "magicPower": 2465,
+ "magicResist": 455,
+ "strength": 77
+ },
+ "items": [
+ 88,
+ 91,
+ 117,
+ 119,
+ 116,
+ 137
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 117,
+ "armor": 585,
+ "hp": 13385,
+ "intelligence": 558,
+ "magicPenetration": 620,
+ "magicPower": 3025,
+ "magicResist": 615,
+ "strength": 117
+ },
+ "items": [
+ 95,
+ 126,
+ 126,
+ 137,
+ 171,
+ 167
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 157,
+ "armor": 585,
+ "hp": 22585,
+ "intelligence": 793,
+ "magicPenetration": 620,
+ "magicPower": 3665,
+ "magicResist": 615,
+ "strength": 157
+ },
+ "items": [
+ 126,
+ 116,
+ 137,
+ 175,
+ 171,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 197,
+ "armor": 1505,
+ "hp": 28985,
+ "intelligence": 1020,
+ "magicPenetration": 620,
+ "magicPower": 4145,
+ "magicResist": 775,
+ "strength": 197
+ },
+ "items": [
+ 91,
+ 99,
+ 181,
+ 171,
+ 180,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 247,
+ "armor": 1705,
+ "hp": 34185,
+ "intelligence": 1353,
+ "magicPenetration": 940,
+ "magicPower": 5585,
+ "magicResist": 775,
+ "strength": 247
+ },
+ "items": [
+ 91,
+ 137,
+ 183,
+ 181,
+ 203,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 287,
+ "armor": 2025,
+ "hp": 43545,
+ "intelligence": 1714,
+ "magicPenetration": 1260,
+ "magicPower": 8561,
+ "magicResist": 775,
+ "strength": 287
+ },
+ "items": [
+ 98,
+ 167,
+ 186,
+ 209,
+ 186,
+ 203
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 385,
+ "armor": 2025,
+ "hp": 52105,
+ "intelligence": 2433,
+ "magicPenetration": 1460,
+ "magicPower": 11057,
+ "magicResist": 775,
+ "strength": 385
+ },
+ "items": [
+ 183,
+ 203,
+ 209,
+ 209,
+ 226,
+ 232
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 387,
+ "armor": 2345,
+ "hp": 62025,
+ "intelligence": 2829,
+ "magicPenetration": 2660,
+ "magicPower": 16289,
+ "magicResist": 775,
+ "strength": 387
+ },
+ "items": [
+ 184,
+ 203,
+ 212,
+ 224,
+ 224,
+ 233
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 465,
+ "armor": 2345,
+ "hp": 90825,
+ "intelligence": 3338,
+ "magicPenetration": 3428,
+ "magicPower": 20321,
+ "magicResist": 1095,
+ "strength": 465
+ },
+ "items": [
+ 180,
+ 180,
+ 224,
+ 233,
+ 242,
+ 222
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 11,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1069,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 1024,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero_69_cascade",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0069",
+ "spineEpicArtAsset": {
+ "name": "69_cascade"
+ },
+ "role": "middle",
+ "obtainType": "shop:invasion:1071",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero69_battle_animation"
+ },
+ "roleExtended": [
+ "mage",
+ "support"
+ ],
+ "sfxAsset": "hero69_sfx",
+ "musicAsset": {
+ "assetIdent": "sound_theme_69_Cascade",
+ "clipIdent": "theme_cascade"
+ },
+ "perk": [
+ 7,
+ 5,
+ 2
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": {
+ "preorder": "2025-09-15 02:00:00",
+ "full": "2025-09-22 02:00:00"
+ },
+ "epicArtAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 483,
+ 484,
+ 485,
+ 486,
+ 487
+ ]
+ },
+ "70": {
+ "id": 70,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 55,
+ "strength": 20
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 19
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 7,
+ 10,
+ 8,
+ 9,
+ 18
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 3,
+ "magicPower": 25,
+ "magicResist": 25,
+ "strength": 14
+ },
+ "items": [
+ 10,
+ 8,
+ 9,
+ 19,
+ 24,
+ 21
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 50,
+ "hp": 1085,
+ "intelligence": 13,
+ "magicPower": 75,
+ "magicResist": 50,
+ "strength": 31
+ },
+ "items": [
+ 18,
+ 19,
+ 27,
+ 28,
+ 32,
+ 33
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 100,
+ "hp": 1470,
+ "intelligence": 27,
+ "magicPenetration": 50,
+ "magicPower": 125,
+ "magicResist": 100,
+ "strength": 52
+ },
+ "items": [
+ 24,
+ 37,
+ 39,
+ 52,
+ 56,
+ 58
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 25,
+ "armor": 150,
+ "hp": 2970,
+ "intelligence": 39,
+ "magicPenetration": 100,
+ "magicPower": 225,
+ "magicResist": 150,
+ "strength": 64
+ },
+ "items": [
+ 36,
+ 46,
+ 44,
+ 59,
+ 60,
+ 63
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 300,
+ "hp": 4770,
+ "intelligence": 41,
+ "magicPenetration": 100,
+ "magicPower": 355,
+ "magicResist": 300,
+ "strength": 76
+ },
+ "items": [
+ 45,
+ 37,
+ 64,
+ 67,
+ 68,
+ 69
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 29,
+ "armor": 510,
+ "hp": 5570,
+ "intelligence": 43,
+ "magicPenetration": 100,
+ "magicPower": 565,
+ "magicResist": 510,
+ "strength": 104
+ },
+ "items": [
+ 71,
+ 74,
+ 88,
+ 68,
+ 69,
+ 99
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 970,
+ "hp": 6370,
+ "intelligence": 50,
+ "magicPenetration": 180,
+ "magicPower": 805,
+ "magicResist": 510,
+ "strength": 137
+ },
+ "items": [
+ 69,
+ 84,
+ 63,
+ 69,
+ 123,
+ 115
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 58,
+ "armor": 1130,
+ "hp": 8770,
+ "intelligence": 72,
+ "magicPenetration": 180,
+ "magicPower": 1045,
+ "magicResist": 670,
+ "strength": 221
+ },
+ "items": [
+ 85,
+ 86,
+ 88,
+ 117,
+ 121,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 124,
+ "armor": 1230,
+ "hp": 10570,
+ "intelligence": 148,
+ "magicPenetration": 340,
+ "magicPower": 1285,
+ "magicResist": 930,
+ "strength": 297
+ },
+ "items": [
+ 88,
+ 85,
+ 121,
+ 126,
+ 135,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 199,
+ "armor": 1330,
+ "hp": 16530,
+ "intelligence": 193,
+ "magicPenetration": 340,
+ "magicPower": 2197,
+ "magicResist": 1090,
+ "strength": 400
+ },
+ "items": [
+ 131,
+ 99,
+ 136,
+ 116,
+ 174,
+ 170
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 263,
+ "armor": 1530,
+ "hp": 16530,
+ "intelligence": 287,
+ "magicPenetration": 940,
+ "magicPower": 2357,
+ "magicResist": 1250,
+ "strength": 651
+ },
+ "items": [
+ 135,
+ 126,
+ 126,
+ 183,
+ 176,
+ 169
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 265,
+ "armor": 1850,
+ "hp": 27090,
+ "intelligence": 289,
+ "magicPenetration": 940,
+ "magicPower": 4109,
+ "magicResist": 1850,
+ "strength": 653
+ },
+ "items": [
+ 117,
+ 116,
+ 181,
+ 184,
+ 180,
+ 180
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 267,
+ "armor": 1850,
+ "hp": 38290,
+ "intelligence": 351,
+ "magicPenetration": 1420,
+ "magicPower": 6829,
+ "magicResist": 2330,
+ "strength": 655
+ },
+ "items": [
+ 140,
+ 136,
+ 185,
+ 183,
+ 184,
+ 203
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 355,
+ "armor": 2170,
+ "hp": 51986,
+ "intelligence": 591,
+ "magicPenetration": 1420,
+ "magicPower": 8608,
+ "magicResist": 2650,
+ "strength": 965
+ },
+ "items": [
+ 140,
+ 136,
+ 185,
+ 181,
+ 180,
+ 210
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 443,
+ "armor": 2170,
+ "hp": 61842,
+ "intelligence": 679,
+ "magicPenetration": 1740,
+ "magicPower": 12115,
+ "magicResist": 2650,
+ "strength": 1449
+ },
+ "items": [
+ 201,
+ 180,
+ 210,
+ 203,
+ 227,
+ 224
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 445,
+ "armor": 3370,
+ "hp": 84722,
+ "intelligence": 833,
+ "magicPenetration": 1740,
+ "magicPower": 15283,
+ "magicResist": 2650,
+ "physicalAttack": 1024,
+ "strength": 1625
+ },
+ "items": [
+ 210,
+ 211,
+ 181,
+ 224,
+ 232,
+ 233
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 523,
+ "armor": 3370,
+ "hp": 99282,
+ "intelligence": 911,
+ "magicPenetration": 4028,
+ "magicPower": 20083,
+ "magicResist": 2650,
+ "physicalAttack": 1024,
+ "strength": 2156
+ },
+ "items": [
+ 180,
+ 185,
+ 221,
+ 234,
+ 241,
+ 232
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 7,
+ 4,
+ 11,
+ 1
+ ],
+ "artifacts": [
+ 1070,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 5,
+ "scale": null,
+ "type": "hero",
+ "asset": "hero_70_electra",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0070",
+ "spineEpicArtAsset": {
+ "name": "70_Necro"
+ },
+ "role": "front",
+ "obtainType": "unavailable",
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": {
+ "ident": "hero70_battle_animation"
+ },
+ "roleExtended": [
+ "melee_tank"
+ ],
+ "sfxAsset": "hero70_sfx",
+ "musicAsset": {
+ "assetIdent": "sound_theme_70_Electra",
+ "clipIdent": "theme_electra"
+ },
+ "perk": [
+ 4,
+ 1,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": {
+ "preorder": "2025-11-07 02:00:00",
+ "full": "2025-11-14 02:00:00"
+ },
+ "epicArtAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 493,
+ 494,
+ 495,
+ 496,
+ 497
+ ]
+ },
+ "1000": {
+ "id": 1000,
+ "baseStats": {
+ "agility": 1,
+ "hp": 50,
+ "intelligence": 1,
+ "physicalAttack": 15,
+ "strength": 2
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 17
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 19
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 200,
+ "intelligence": 2,
+ "physicalAttack": 60,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 99,
+ 99,
+ 92,
+ 92,
+ 56
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 400,
+ "hp": 1200,
+ "intelligence": 4,
+ "physicalAttack": 465,
+ "strength": 4
+ },
+ "items": [
+ 92,
+ 99,
+ 99,
+ 92,
+ 92,
+ 56
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 6,
+ "physicalAttack": 870,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 8,
+ "physicalAttack": 942,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 10,
+ "physicalAttack": 1014,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 12,
+ "physicalAttack": 1086,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 14,
+ "physicalAttack": 1158,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 16,
+ "physicalAttack": 1230,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 18,
+ "physicalAttack": 1302,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 20,
+ "physicalAttack": 1374,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 22,
+ "physicalAttack": 1446,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 24,
+ "physicalAttack": 1518,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 26,
+ "physicalAttack": 1590,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 28,
+ "physicalAttack": 1662,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 30,
+ "physicalAttack": 1734,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 32,
+ "physicalAttack": 1806,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 800,
+ "hp": 2200,
+ "intelligence": 34,
+ "physicalAttack": 1878,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 44,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_demon_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1000",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1000
+ ]
+ },
+ "1001": {
+ "id": 1001,
+ "baseStats": {
+ "agility": 2,
+ "intelligence": 1,
+ "physicalAttack": 25,
+ "strength": 1
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 2
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 17,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 200,
+ "intelligence": 2,
+ "physicalAttack": 60,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 101,
+ 56,
+ 92,
+ 92,
+ 101
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 1200,
+ "intelligence": 4,
+ "physicalAttack": 465,
+ "physicalCritChance": 120,
+ "strength": 4
+ },
+ "items": [
+ 92,
+ 101,
+ 56,
+ 92,
+ 92,
+ 101
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 2200,
+ "intelligence": 6,
+ "physicalAttack": 870,
+ "physicalCritChance": 240,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 2200,
+ "intelligence": 8,
+ "physicalAttack": 942,
+ "physicalCritChance": 240,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 2200,
+ "intelligence": 10,
+ "physicalAttack": 1014,
+ "physicalCritChance": 240,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 2200,
+ "intelligence": 12,
+ "physicalAttack": 1086,
+ "physicalCritChance": 240,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 2200,
+ "intelligence": 14,
+ "physicalAttack": 1158,
+ "physicalCritChance": 240,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 2200,
+ "intelligence": 16,
+ "physicalAttack": 1230,
+ "physicalCritChance": 240,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 2200,
+ "intelligence": 18,
+ "physicalAttack": 1302,
+ "physicalCritChance": 240,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 2200,
+ "intelligence": 20,
+ "physicalAttack": 1374,
+ "physicalCritChance": 240,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 2200,
+ "intelligence": 22,
+ "physicalAttack": 1446,
+ "physicalCritChance": 240,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 2200,
+ "intelligence": 24,
+ "physicalAttack": 1518,
+ "physicalCritChance": 240,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 2200,
+ "intelligence": 26,
+ "physicalAttack": 1590,
+ "physicalCritChance": 240,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 2200,
+ "intelligence": 28,
+ "physicalAttack": 1662,
+ "physicalCritChance": 240,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 2200,
+ "intelligence": 30,
+ "physicalAttack": 1734,
+ "physicalCritChance": 240,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 2200,
+ "intelligence": 32,
+ "physicalAttack": 1806,
+ "physicalCritChance": 240,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 2200,
+ "intelligence": 34,
+ "physicalAttack": 1878,
+ "physicalCritChance": 240,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 1007,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_demon_archer",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1001",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1001
+ ]
+ },
+ "1002": {
+ "id": 1002,
+ "baseStats": {
+ "agility": 1,
+ "hp": 50,
+ "intelligence": 10,
+ "magicPower": 50,
+ "strength": 1
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 17,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 19,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 17
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 200,
+ "intelligence": 2,
+ "physicalAttack": 60,
+ "strength": 2
+ },
+ "items": [
+ 56,
+ 93,
+ 93,
+ 98,
+ 98,
+ 117
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 1200,
+ "intelligence": 34,
+ "magicPenetration": 560,
+ "magicPower": 560,
+ "physicalAttack": 60,
+ "strength": 4
+ },
+ "items": [
+ 56,
+ 93,
+ 93,
+ 98,
+ 98,
+ 117
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 2200,
+ "intelligence": 66,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 60,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 2200,
+ "intelligence": 68,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 132,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 2200,
+ "intelligence": 70,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 204,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 2200,
+ "intelligence": 72,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 276,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 2200,
+ "intelligence": 74,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 348,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 2200,
+ "intelligence": 76,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 420,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 2200,
+ "intelligence": 78,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 492,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 2200,
+ "intelligence": 80,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 564,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 2200,
+ "intelligence": 82,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 636,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 2200,
+ "intelligence": 84,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 708,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 2200,
+ "intelligence": 86,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 780,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 2200,
+ "intelligence": 88,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 852,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 2200,
+ "intelligence": 90,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 924,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 2200,
+ "intelligence": 92,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 996,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 2200,
+ "intelligence": 94,
+ "magicPenetration": 1120,
+ "magicPower": 1120,
+ "physicalAttack": 1068,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2004,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_demon_warlock",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1003",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1002
+ ]
+ },
+ "1003": {
+ "id": 1003,
+ "baseStats": {
+ "agility": 10,
+ "intelligence": 1,
+ "physicalAttack": 50,
+ "strength": 1
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 17
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 200,
+ "intelligence": 2,
+ "physicalAttack": 60,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 99,
+ 100
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 200,
+ "hp": 200,
+ "intelligence": 4,
+ "magicResist": 200,
+ "physicalAttack": 600,
+ "strength": 4
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 99,
+ 100
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 6,
+ "magicResist": 400,
+ "physicalAttack": 1140,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 8,
+ "magicResist": 400,
+ "physicalAttack": 1212,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 10,
+ "magicResist": 400,
+ "physicalAttack": 1284,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 12,
+ "magicResist": 400,
+ "physicalAttack": 1356,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 14,
+ "magicResist": 400,
+ "physicalAttack": 1428,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 16,
+ "magicResist": 400,
+ "physicalAttack": 1500,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 18,
+ "magicResist": 400,
+ "physicalAttack": 1572,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 20,
+ "magicResist": 400,
+ "physicalAttack": 1644,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 22,
+ "magicResist": 400,
+ "physicalAttack": 1716,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 24,
+ "magicResist": 400,
+ "physicalAttack": 1788,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 26,
+ "magicResist": 400,
+ "physicalAttack": 1860,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 28,
+ "magicResist": 400,
+ "physicalAttack": 1932,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 30,
+ "magicResist": 400,
+ "physicalAttack": 2004,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 32,
+ "magicResist": 400,
+ "physicalAttack": 2076,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 400,
+ "hp": 200,
+ "intelligence": 34,
+ "magicResist": 400,
+ "physicalAttack": 2148,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2032,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_demon_catapult",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1002",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1003
+ ]
+ },
+ "1004": {
+ "id": 1004,
+ "baseStats": {
+ "agility": 1,
+ "intelligence": 1,
+ "physicalAttack": 85,
+ "strength": 8
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 16
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 14,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 20
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 200,
+ "intelligence": 2,
+ "physicalAttack": 60,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 122,
+ 122,
+ 100,
+ 100,
+ 92
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 320,
+ "hp": 200,
+ "intelligence": 4,
+ "magicResist": 400,
+ "physicalAttack": 546,
+ "strength": 4
+ },
+ "items": [
+ 92,
+ 122,
+ 122,
+ 100,
+ 100,
+ 92
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 6,
+ "magicResist": 800,
+ "physicalAttack": 1032,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 8,
+ "magicResist": 800,
+ "physicalAttack": 1104,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 10,
+ "magicResist": 800,
+ "physicalAttack": 1176,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 12,
+ "magicResist": 800,
+ "physicalAttack": 1248,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 14,
+ "magicResist": 800,
+ "physicalAttack": 1320,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 16,
+ "magicResist": 800,
+ "physicalAttack": 1392,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 18,
+ "magicResist": 800,
+ "physicalAttack": 1464,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 20,
+ "magicResist": 800,
+ "physicalAttack": 1536,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 22,
+ "magicResist": 800,
+ "physicalAttack": 1608,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 24,
+ "magicResist": 800,
+ "physicalAttack": 1680,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 26,
+ "magicResist": 800,
+ "physicalAttack": 1752,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 28,
+ "magicResist": 800,
+ "physicalAttack": 1824,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 30,
+ "magicResist": 800,
+ "physicalAttack": 1896,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 32,
+ "magicResist": 800,
+ "physicalAttack": 1968,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 640,
+ "hp": 200,
+ "intelligence": 34,
+ "magicResist": 800,
+ "physicalAttack": 2040,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 45,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_demon_tank",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1004",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1004
+ ]
+ },
+ "1005": {
+ "id": 1005,
+ "baseStats": {
+ "agility": 10,
+ "hp": 110,
+ "intelligence": 1,
+ "physicalAttack": 25,
+ "strength": 1
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 14
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 14,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 16
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 200,
+ "intelligence": 2,
+ "physicalAttack": 60,
+ "strength": 2
+ },
+ "items": [
+ 61,
+ 62,
+ 92,
+ 92,
+ 101,
+ 92
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "dodge": 30,
+ "hp": 200,
+ "intelligence": 4,
+ "physicalAttack": 465,
+ "physicalCritChance": 90,
+ "strength": 4
+ },
+ "items": [
+ 61,
+ 62,
+ 92,
+ 92,
+ 101,
+ 92
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 6,
+ "physicalAttack": 870,
+ "physicalCritChance": 180,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 8,
+ "physicalAttack": 942,
+ "physicalCritChance": 180,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 10,
+ "physicalAttack": 1014,
+ "physicalCritChance": 180,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 12,
+ "physicalAttack": 1086,
+ "physicalCritChance": 180,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 14,
+ "physicalAttack": 1158,
+ "physicalCritChance": 180,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 16,
+ "physicalAttack": 1230,
+ "physicalCritChance": 180,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 18,
+ "physicalAttack": 1302,
+ "physicalCritChance": 180,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 20,
+ "physicalAttack": 1374,
+ "physicalCritChance": 180,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 22,
+ "physicalAttack": 1446,
+ "physicalCritChance": 180,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 24,
+ "physicalAttack": 1518,
+ "physicalCritChance": 180,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 26,
+ "physicalAttack": 1590,
+ "physicalCritChance": 180,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 28,
+ "physicalAttack": 1662,
+ "physicalCritChance": 180,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 30,
+ "physicalAttack": 1734,
+ "physicalCritChance": 180,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 32,
+ "physicalAttack": 1806,
+ "physicalCritChance": 180,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 34,
+ "physicalAttack": 1878,
+ "physicalCritChance": 180,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 46,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_demon_swords",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1005",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1005
+ ]
+ },
+ "1006": {
+ "id": 1006,
+ "baseStats": {
+ "agility": 15,
+ "hp": 2000,
+ "intelligence": 15,
+ "magicPower": 150,
+ "physicalAttack": 75,
+ "strength": 15
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 35,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 35,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 65
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 92,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 390,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 462,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 534,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 606,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 678,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 750,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 822,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 894,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 966,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 1038,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 1110,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 1182,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1254,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1326,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1398,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1470,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 47,
+ "scale": null,
+ "type": "creep",
+ "asset": "boss_demon",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1006",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1006,
+ 1007
+ ]
+ },
+ "1010": {
+ "id": 1010,
+ "baseStats": {
+ "agility": 5,
+ "intelligence": 5,
+ "physicalAttack": 20,
+ "strength": 10
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 2
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 200,
+ "intelligence": 7,
+ "physicalAttack": 24,
+ "strength": 7
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 9,
+ "hp": 200,
+ "intelligence": 9,
+ "physicalAttack": 219,
+ "strength": 9
+ },
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "hp": 1400,
+ "intelligence": 11,
+ "physicalAttack": 219,
+ "strength": 11
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "hp": 1400,
+ "intelligence": 13,
+ "physicalAttack": 291,
+ "strength": 13
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "hp": 1400,
+ "intelligence": 15,
+ "physicalAttack": 363,
+ "strength": 15
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 17,
+ "hp": 1400,
+ "intelligence": 17,
+ "physicalAttack": 435,
+ "strength": 17
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 19,
+ "hp": 1400,
+ "intelligence": 19,
+ "physicalAttack": 507,
+ "strength": 19
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 21,
+ "hp": 1400,
+ "intelligence": 21,
+ "physicalAttack": 579,
+ "strength": 21
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 23,
+ "hp": 1400,
+ "intelligence": 23,
+ "physicalAttack": 651,
+ "strength": 23
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 25,
+ "hp": 1400,
+ "intelligence": 25,
+ "physicalAttack": 723,
+ "strength": 25
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 27,
+ "hp": 1400,
+ "intelligence": 27,
+ "physicalAttack": 795,
+ "strength": 27
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 29,
+ "hp": 1400,
+ "intelligence": 29,
+ "physicalAttack": 867,
+ "strength": 29
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 31,
+ "hp": 1400,
+ "intelligence": 31,
+ "physicalAttack": 939,
+ "strength": 31
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 33,
+ "hp": 1400,
+ "intelligence": 33,
+ "physicalAttack": 1011,
+ "strength": 33
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 35,
+ "hp": 1400,
+ "intelligence": 35,
+ "physicalAttack": 1083,
+ "strength": 35
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 37,
+ "hp": 1400,
+ "intelligence": 37,
+ "physicalAttack": 1155,
+ "strength": 37
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 39,
+ "hp": 1400,
+ "intelligence": 39,
+ "physicalAttack": 1227,
+ "strength": 39
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 48,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_forest_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1010",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1010
+ ]
+ },
+ "1011": {
+ "id": 1011,
+ "baseStats": {
+ "agility": 20,
+ "intelligence": 5,
+ "physicalAttack": 20,
+ "strength": 5
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 2
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 200,
+ "intelligence": 7,
+ "physicalAttack": 24,
+ "strength": 7
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 1,
+ 101
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 9,
+ "hp": 200,
+ "intelligence": 9,
+ "physicalAttack": 207,
+ "physicalCritChance": 60,
+ "strength": 9
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 1,
+ 101
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "hp": 200,
+ "intelligence": 11,
+ "physicalAttack": 390,
+ "physicalCritChance": 120,
+ "strength": 11
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "hp": 200,
+ "intelligence": 13,
+ "physicalAttack": 462,
+ "physicalCritChance": 120,
+ "strength": 13
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "hp": 200,
+ "intelligence": 15,
+ "physicalAttack": 534,
+ "physicalCritChance": 120,
+ "strength": 15
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 17,
+ "hp": 200,
+ "intelligence": 17,
+ "physicalAttack": 606,
+ "physicalCritChance": 120,
+ "strength": 17
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 19,
+ "hp": 200,
+ "intelligence": 19,
+ "physicalAttack": 678,
+ "physicalCritChance": 120,
+ "strength": 19
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 21,
+ "hp": 200,
+ "intelligence": 21,
+ "physicalAttack": 750,
+ "physicalCritChance": 120,
+ "strength": 21
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 23,
+ "hp": 200,
+ "intelligence": 23,
+ "physicalAttack": 822,
+ "physicalCritChance": 120,
+ "strength": 23
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 25,
+ "hp": 200,
+ "intelligence": 25,
+ "physicalAttack": 894,
+ "physicalCritChance": 120,
+ "strength": 25
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 27,
+ "hp": 200,
+ "intelligence": 27,
+ "physicalAttack": 966,
+ "physicalCritChance": 120,
+ "strength": 27
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 29,
+ "hp": 200,
+ "intelligence": 29,
+ "physicalAttack": 1038,
+ "physicalCritChance": 120,
+ "strength": 29
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 31,
+ "hp": 200,
+ "intelligence": 31,
+ "physicalAttack": 1110,
+ "physicalCritChance": 120,
+ "strength": 31
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 33,
+ "hp": 200,
+ "intelligence": 33,
+ "physicalAttack": 1182,
+ "physicalCritChance": 120,
+ "strength": 33
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 35,
+ "hp": 200,
+ "intelligence": 35,
+ "physicalAttack": 1254,
+ "physicalCritChance": 120,
+ "strength": 35
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 37,
+ "hp": 200,
+ "intelligence": 37,
+ "physicalAttack": 1326,
+ "physicalCritChance": 120,
+ "strength": 37
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 39,
+ "hp": 200,
+ "intelligence": 39,
+ "physicalAttack": 1398,
+ "physicalCritChance": 120,
+ "strength": 39
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 1020,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_forest_archer",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1011",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1011
+ ]
+ },
+ "1012": {
+ "id": 1012,
+ "baseStats": {
+ "agility": 5,
+ "hp": 250,
+ "intelligence": 20,
+ "magicPower": 150,
+ "strength": 5
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 2
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 200,
+ "intelligence": 7,
+ "physicalAttack": 24,
+ "strength": 7
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 1,
+ 93
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 9,
+ "hp": 200,
+ "intelligence": 9,
+ "magicPower": 200,
+ "physicalAttack": 207,
+ "strength": 9
+ },
+ "items": [
+ 93,
+ 93,
+ 93,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "hp": 800,
+ "intelligence": 11,
+ "magicPower": 800,
+ "physicalAttack": 207,
+ "strength": 11
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "hp": 800,
+ "intelligence": 13,
+ "magicPower": 800,
+ "physicalAttack": 279,
+ "strength": 13
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "hp": 800,
+ "intelligence": 15,
+ "magicPower": 800,
+ "physicalAttack": 351,
+ "strength": 15
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 17,
+ "hp": 800,
+ "intelligence": 17,
+ "magicPower": 800,
+ "physicalAttack": 423,
+ "strength": 17
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 19,
+ "hp": 800,
+ "intelligence": 19,
+ "magicPower": 800,
+ "physicalAttack": 495,
+ "strength": 19
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 21,
+ "hp": 800,
+ "intelligence": 21,
+ "magicPower": 800,
+ "physicalAttack": 567,
+ "strength": 21
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 23,
+ "hp": 800,
+ "intelligence": 23,
+ "magicPower": 800,
+ "physicalAttack": 639,
+ "strength": 23
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 25,
+ "hp": 800,
+ "intelligence": 25,
+ "magicPower": 800,
+ "physicalAttack": 711,
+ "strength": 25
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 27,
+ "hp": 800,
+ "intelligence": 27,
+ "magicPower": 800,
+ "physicalAttack": 783,
+ "strength": 27
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 29,
+ "hp": 800,
+ "intelligence": 29,
+ "magicPower": 800,
+ "physicalAttack": 855,
+ "strength": 29
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 31,
+ "hp": 800,
+ "intelligence": 31,
+ "magicPower": 800,
+ "physicalAttack": 927,
+ "strength": 31
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 33,
+ "hp": 800,
+ "intelligence": 33,
+ "magicPower": 800,
+ "physicalAttack": 999,
+ "strength": 33
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 35,
+ "hp": 800,
+ "intelligence": 35,
+ "magicPower": 800,
+ "physicalAttack": 1071,
+ "strength": 35
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 37,
+ "hp": 800,
+ "intelligence": 37,
+ "magicPower": 800,
+ "physicalAttack": 1143,
+ "strength": 37
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 39,
+ "hp": 800,
+ "intelligence": 39,
+ "magicPower": 800,
+ "physicalAttack": 1215,
+ "strength": 39
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2033,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_forest_fairy",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1012",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1012
+ ]
+ },
+ "1013": {
+ "id": 1013,
+ "baseStats": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 25,
+ "strength": 10
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 2
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 200,
+ "intelligence": 7,
+ "physicalAttack": 24,
+ "strength": 7
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 92,
+ 92
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 9,
+ "hp": 200,
+ "intelligence": 9,
+ "physicalAttack": 465,
+ "strength": 9
+ },
+ "items": [
+ 92,
+ 92,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "hp": 1000,
+ "intelligence": 11,
+ "physicalAttack": 735,
+ "strength": 11
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "hp": 1000,
+ "intelligence": 13,
+ "physicalAttack": 807,
+ "strength": 13
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "hp": 1000,
+ "intelligence": 15,
+ "physicalAttack": 879,
+ "strength": 15
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 17,
+ "hp": 1000,
+ "intelligence": 17,
+ "physicalAttack": 951,
+ "strength": 17
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 19,
+ "hp": 1000,
+ "intelligence": 19,
+ "physicalAttack": 1023,
+ "strength": 19
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 21,
+ "hp": 1000,
+ "intelligence": 21,
+ "physicalAttack": 1095,
+ "strength": 21
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 23,
+ "hp": 1000,
+ "intelligence": 23,
+ "physicalAttack": 1167,
+ "strength": 23
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 25,
+ "hp": 1000,
+ "intelligence": 25,
+ "physicalAttack": 1239,
+ "strength": 25
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 27,
+ "hp": 1000,
+ "intelligence": 27,
+ "physicalAttack": 1311,
+ "strength": 27
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 29,
+ "hp": 1000,
+ "intelligence": 29,
+ "physicalAttack": 1383,
+ "strength": 29
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 31,
+ "hp": 1000,
+ "intelligence": 31,
+ "physicalAttack": 1455,
+ "strength": 31
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 33,
+ "hp": 1000,
+ "intelligence": 33,
+ "physicalAttack": 1527,
+ "strength": 33
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 35,
+ "hp": 1000,
+ "intelligence": 35,
+ "physicalAttack": 1599,
+ "strength": 35
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 37,
+ "hp": 1000,
+ "intelligence": 37,
+ "physicalAttack": 1671,
+ "strength": 37
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 39,
+ "hp": 1000,
+ "intelligence": 39,
+ "physicalAttack": 1743,
+ "strength": 39
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2034,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_forest_satyr",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1013",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1013
+ ]
+ },
+ "1014": {
+ "id": 1014,
+ "baseStats": {
+ "agility": 5,
+ "hp": 50,
+ "intelligence": 5,
+ "physicalAttack": 75,
+ "strength": 10
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 200,
+ "intelligence": 7,
+ "physicalAttack": 24,
+ "strength": 7
+ },
+ "items": [
+ 92,
+ 1,
+ 44,
+ 44,
+ 44,
+ 44
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 200,
+ "hp": 200,
+ "intelligence": 9,
+ "magicResist": 200,
+ "physicalAttack": 171,
+ "strength": 9
+ },
+ "items": [
+ 2,
+ 2,
+ 44,
+ 44,
+ 44,
+ 44
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 11,
+ "magicResist": 400,
+ "physicalAttack": 171,
+ "strength": 11
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 13,
+ "magicResist": 400,
+ "physicalAttack": 243,
+ "strength": 13
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 15,
+ "magicResist": 400,
+ "physicalAttack": 315,
+ "strength": 15
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 17,
+ "magicResist": 400,
+ "physicalAttack": 387,
+ "strength": 17
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 19,
+ "magicResist": 400,
+ "physicalAttack": 459,
+ "strength": 19
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 21,
+ "magicResist": 400,
+ "physicalAttack": 531,
+ "strength": 21
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 23,
+ "magicResist": 400,
+ "physicalAttack": 603,
+ "strength": 23
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 25,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 25,
+ "magicResist": 400,
+ "physicalAttack": 675,
+ "strength": 25
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 27,
+ "magicResist": 400,
+ "physicalAttack": 747,
+ "strength": 27
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 29,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 29,
+ "magicResist": 400,
+ "physicalAttack": 819,
+ "strength": 29
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 31,
+ "magicResist": 400,
+ "physicalAttack": 891,
+ "strength": 31
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 33,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 33,
+ "magicResist": 400,
+ "physicalAttack": 963,
+ "strength": 33
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 35,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 35,
+ "magicResist": 400,
+ "physicalAttack": 1035,
+ "strength": 35
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 37,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 37,
+ "magicResist": 400,
+ "physicalAttack": 1107,
+ "strength": 37
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 39,
+ "armor": 400,
+ "hp": 600,
+ "intelligence": 39,
+ "magicResist": 400,
+ "physicalAttack": 1179,
+ "strength": 39
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 49,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_forest_tank",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1014",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1014
+ ]
+ },
+ "1015": {
+ "id": 1015,
+ "baseStats": {
+ "agility": 30,
+ "hp": 750,
+ "intelligence": 5,
+ "physicalAttack": 150,
+ "physicalCritChance": 20,
+ "strength": 5
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 2
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "hp": 200,
+ "intelligence": 7,
+ "physicalAttack": 24,
+ "strength": 7
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 101,
+ 102
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 9,
+ "dodge": 60,
+ "hp": 200,
+ "intelligence": 9,
+ "physicalAttack": 195,
+ "physicalCritChance": 60,
+ "strength": 9
+ },
+ "items": [
+ 92,
+ 1,
+ 2,
+ 2,
+ 2,
+ 101
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 11,
+ "physicalAttack": 342,
+ "physicalCritChance": 120,
+ "strength": 11
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 13,
+ "physicalAttack": 414,
+ "physicalCritChance": 120,
+ "strength": 13
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 15,
+ "physicalAttack": 486,
+ "physicalCritChance": 120,
+ "strength": 15
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 17,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 17,
+ "physicalAttack": 558,
+ "physicalCritChance": 120,
+ "strength": 17
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 19,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 19,
+ "physicalAttack": 630,
+ "physicalCritChance": 120,
+ "strength": 19
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 21,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 21,
+ "physicalAttack": 702,
+ "physicalCritChance": 120,
+ "strength": 21
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 23,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 23,
+ "physicalAttack": 774,
+ "physicalCritChance": 120,
+ "strength": 23
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 25,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 25,
+ "physicalAttack": 846,
+ "physicalCritChance": 120,
+ "strength": 25
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 27,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 27,
+ "physicalAttack": 918,
+ "physicalCritChance": 120,
+ "strength": 27
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 29,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 29,
+ "physicalAttack": 990,
+ "physicalCritChance": 120,
+ "strength": 29
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 31,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 31,
+ "physicalAttack": 1062,
+ "physicalCritChance": 120,
+ "strength": 31
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 33,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 33,
+ "physicalAttack": 1134,
+ "physicalCritChance": 120,
+ "strength": 33
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 35,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 35,
+ "physicalAttack": 1206,
+ "physicalCritChance": 120,
+ "strength": 35
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 37,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 37,
+ "physicalAttack": 1278,
+ "physicalCritChance": 120,
+ "strength": 37
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 39,
+ "dodge": 60,
+ "hp": 800,
+ "intelligence": 39,
+ "physicalAttack": 1350,
+ "physicalCritChance": 120,
+ "strength": 39
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 50,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_forest_savage",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1015",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1015
+ ]
+ },
+ "1016": {
+ "id": 1016,
+ "baseStats": {
+ "agility": 15,
+ "hp": 27000,
+ "intelligence": 15,
+ "magicPower": 1350,
+ "physicalAttack": 1200,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 16,
+ 16,
+ 16,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 1800,
+ "intelligence": 25,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 1800,
+ "intelligence": 27,
+ "physicalAttack": 72,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 1800,
+ "intelligence": 29,
+ "physicalAttack": 144,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 1800,
+ "intelligence": 31,
+ "physicalAttack": 216,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 1800,
+ "intelligence": 33,
+ "physicalAttack": 288,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 1800,
+ "intelligence": 35,
+ "physicalAttack": 360,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 1800,
+ "intelligence": 37,
+ "physicalAttack": 432,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 1800,
+ "intelligence": 39,
+ "physicalAttack": 504,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 1800,
+ "intelligence": 41,
+ "physicalAttack": 576,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 1800,
+ "intelligence": 43,
+ "physicalAttack": 648,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 1800,
+ "intelligence": 45,
+ "physicalAttack": 720,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 1800,
+ "intelligence": 47,
+ "physicalAttack": 792,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 1800,
+ "intelligence": 49,
+ "physicalAttack": 864,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 1800,
+ "intelligence": 51,
+ "physicalAttack": 936,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 1800,
+ "intelligence": 53,
+ "physicalAttack": 1008,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 1800,
+ "intelligence": 55,
+ "physicalAttack": 1080,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 51,
+ "scale": null,
+ "type": "creep",
+ "asset": "boss_forest",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1016",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1016,
+ 1017
+ ]
+ },
+ "1020": {
+ "id": 1020,
+ "baseStats": {
+ "agility": 5,
+ "intelligence": 5,
+ "physicalAttack": 20,
+ "strength": 10
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 200,
+ "intelligence": 2,
+ "physicalAttack": 60,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 99,
+ 99,
+ 99,
+ 99
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 800,
+ "hp": 200,
+ "intelligence": 4,
+ "physicalAttack": 84,
+ "strength": 4
+ },
+ "items": [
+ 92,
+ 99,
+ 99,
+ 92,
+ 92,
+ 56
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 6,
+ "physicalAttack": 489,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 8,
+ "physicalAttack": 561,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 10,
+ "physicalAttack": 633,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 4
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 17,
+ "physicalAttack": 693,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 19,
+ "physicalAttack": 765,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 21,
+ "physicalAttack": 837,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 23,
+ "physicalAttack": 909,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 25,
+ "physicalAttack": 981,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 5
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 27,
+ "physicalAttack": 1041,
+ "strength": 27
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 29,
+ "physicalAttack": 1113,
+ "strength": 29
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 31,
+ "physicalAttack": 1185,
+ "strength": 31
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 33,
+ "physicalAttack": 1257,
+ "strength": 33
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 35,
+ "physicalAttack": 1329,
+ "strength": 35
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 37,
+ "physicalAttack": 1401,
+ "strength": 37
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 1200,
+ "hp": 1200,
+ "intelligence": 39,
+ "physicalAttack": 1473,
+ "strength": 39
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 53,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_ork_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1020",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1020
+ ]
+ },
+ "1021": {
+ "id": 1021,
+ "baseStats": {
+ "agility": 20,
+ "intelligence": 5,
+ "physicalAttack": 100,
+ "physicalCritChance": 50,
+ "strength": 5
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 2
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 6
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "intelligence": 7,
+ "physicalAttack": 60,
+ "strength": 7
+ },
+ "items": [
+ 1,
+ 92,
+ 101,
+ 56,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 9,
+ "hp": 1000,
+ "intelligence": 9,
+ "physicalAttack": 231,
+ "physicalCritChance": 60,
+ "strength": 9
+ },
+ "items": [
+ 92,
+ 101,
+ 56,
+ 92,
+ 92,
+ 92
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "hp": 2000,
+ "intelligence": 11,
+ "physicalAttack": 771,
+ "physicalCritChance": 120,
+ "strength": 11
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "hp": 2000,
+ "intelligence": 13,
+ "physicalAttack": 843,
+ "physicalCritChance": 120,
+ "strength": 13
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "hp": 2000,
+ "intelligence": 15,
+ "physicalAttack": 915,
+ "physicalCritChance": 120,
+ "strength": 15
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 8
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 25,
+ "hp": 2000,
+ "intelligence": 17,
+ "physicalAttack": 975,
+ "physicalCritChance": 120,
+ "strength": 17
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 25,
+ "hp": 2000,
+ "intelligence": 19,
+ "physicalAttack": 1047,
+ "physicalCritChance": 120,
+ "strength": 19
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 25,
+ "hp": 2000,
+ "intelligence": 21,
+ "physicalAttack": 1119,
+ "physicalCritChance": 120,
+ "strength": 21
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 25,
+ "hp": 2000,
+ "intelligence": 23,
+ "physicalAttack": 1191,
+ "physicalCritChance": 120,
+ "strength": 23
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 25,
+ "armor": 25,
+ "hp": 2000,
+ "intelligence": 25,
+ "physicalAttack": 1263,
+ "physicalCritChance": 120,
+ "strength": 25
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 9
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 25,
+ "hp": 2000,
+ "intelligence": 27,
+ "magicResist": 25,
+ "physicalAttack": 1323,
+ "physicalCritChance": 120,
+ "strength": 27
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 29,
+ "armor": 25,
+ "hp": 2000,
+ "intelligence": 29,
+ "magicResist": 25,
+ "physicalAttack": 1395,
+ "physicalCritChance": 120,
+ "strength": 29
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 25,
+ "hp": 2000,
+ "intelligence": 31,
+ "magicResist": 25,
+ "physicalAttack": 1467,
+ "physicalCritChance": 120,
+ "strength": 31
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 33,
+ "armor": 25,
+ "hp": 2000,
+ "intelligence": 33,
+ "magicResist": 25,
+ "physicalAttack": 1539,
+ "physicalCritChance": 120,
+ "strength": 33
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 35,
+ "armor": 25,
+ "hp": 2000,
+ "intelligence": 35,
+ "magicResist": 25,
+ "physicalAttack": 1611,
+ "physicalCritChance": 120,
+ "strength": 35
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 37,
+ "armor": 25,
+ "hp": 2000,
+ "intelligence": 37,
+ "magicResist": 25,
+ "physicalAttack": 1683,
+ "physicalCritChance": 120,
+ "strength": 37
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 39,
+ "armor": 25,
+ "hp": 2000,
+ "intelligence": 39,
+ "magicResist": 25,
+ "physicalAttack": 1755,
+ "physicalCritChance": 120,
+ "strength": 39
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 1021,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_ork_archer",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1021",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1021
+ ]
+ },
+ "1022": {
+ "id": 1022,
+ "baseStats": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 150
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 92,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 1200,
+ "intelligence": 4,
+ "physicalAttack": 195,
+ "strength": 4
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 99,
+ 100
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 6,
+ "magicResist": 200,
+ "physicalAttack": 735,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 8,
+ "magicResist": 200,
+ "physicalAttack": 807,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 10,
+ "magicResist": 200,
+ "physicalAttack": 879,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 12,
+ "magicResist": 200,
+ "physicalAttack": 951,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 14,
+ "magicResist": 200,
+ "physicalAttack": 1023,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 16,
+ "magicResist": 200,
+ "physicalAttack": 1095,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 18,
+ "magicResist": 200,
+ "physicalAttack": 1167,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 20,
+ "magicResist": 200,
+ "physicalAttack": 1239,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 22,
+ "magicResist": 200,
+ "physicalAttack": 1311,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 24,
+ "magicResist": 200,
+ "physicalAttack": 1383,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 26,
+ "magicResist": 200,
+ "physicalAttack": 1455,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 28,
+ "magicResist": 200,
+ "physicalAttack": 1527,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 30,
+ "magicResist": 200,
+ "physicalAttack": 1599,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 32,
+ "magicResist": 200,
+ "physicalAttack": 1671,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 200,
+ "hp": 1200,
+ "intelligence": 34,
+ "magicResist": 200,
+ "physicalAttack": 1743,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2035,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_ork_catapult",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1022",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1022
+ ]
+ },
+ "1023": {
+ "id": 1023,
+ "baseStats": {
+ "agility": 5,
+ "hp": 250,
+ "intelligence": 20,
+ "magicPower": 225,
+ "strength": 5
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 2
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 56,
+ 1,
+ 1,
+ 1,
+ 93,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 2200,
+ "intelligence": 4,
+ "magicPower": 200,
+ "physicalAttack": 48,
+ "strength": 4
+ },
+ "items": [
+ 56,
+ 93,
+ 93,
+ 98,
+ 98,
+ 117
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 3200,
+ "intelligence": 36,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 48,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 3200,
+ "intelligence": 38,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 120,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 3200,
+ "intelligence": 40,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 192,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 3200,
+ "intelligence": 42,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 264,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 3200,
+ "intelligence": 44,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 336,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 3200,
+ "intelligence": 46,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 408,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 3200,
+ "intelligence": 48,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 480,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 3200,
+ "intelligence": 50,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 552,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 3200,
+ "intelligence": 52,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 624,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 3200,
+ "intelligence": 54,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 696,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 3200,
+ "intelligence": 56,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 768,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 3200,
+ "intelligence": 58,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 840,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 3200,
+ "intelligence": 60,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 912,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 3200,
+ "intelligence": 62,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 984,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 3200,
+ "intelligence": 64,
+ "magicPenetration": 560,
+ "magicPower": 760,
+ "physicalAttack": 1056,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2036,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_ork_shaman",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1023",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1023
+ ]
+ },
+ "1024": {
+ "id": 1024,
+ "baseStats": {
+ "agility": 5,
+ "hp": 50,
+ "intelligence": 5,
+ "physicalAttack": 75,
+ "strength": 10
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 92,
+ 99,
+ 99,
+ 100,
+ 100
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 400,
+ "hp": 1200,
+ "intelligence": 4,
+ "magicResist": 400,
+ "physicalAttack": 147,
+ "strength": 4
+ },
+ "items": [
+ 92,
+ 122,
+ 122,
+ 100,
+ 100,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 6,
+ "magicResist": 800,
+ "physicalAttack": 510,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 8,
+ "magicResist": 800,
+ "physicalAttack": 582,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 10,
+ "magicResist": 800,
+ "physicalAttack": 654,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 12,
+ "magicResist": 800,
+ "physicalAttack": 726,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 14,
+ "magicResist": 800,
+ "physicalAttack": 798,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 16,
+ "magicResist": 800,
+ "physicalAttack": 870,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 18,
+ "magicResist": 800,
+ "physicalAttack": 942,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 20,
+ "magicResist": 800,
+ "physicalAttack": 1014,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 22,
+ "magicResist": 800,
+ "physicalAttack": 1086,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 24,
+ "magicResist": 800,
+ "physicalAttack": 1158,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 26,
+ "magicResist": 800,
+ "physicalAttack": 1230,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 28,
+ "magicResist": 800,
+ "physicalAttack": 1302,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 30,
+ "magicResist": 800,
+ "physicalAttack": 1374,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 32,
+ "magicResist": 800,
+ "physicalAttack": 1446,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 720,
+ "hp": 1200,
+ "intelligence": 34,
+ "magicResist": 800,
+ "physicalAttack": 1518,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 54,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_ork_troll",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1024",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1024
+ ]
+ },
+ "1025": {
+ "id": 1025,
+ "baseStats": {
+ "agility": 25,
+ "hp": 750,
+ "intelligence": 5,
+ "physicalAttack": 100,
+ "physicalCritChance": 30,
+ "strength": 5
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 2
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 92,
+ 102,
+ 101,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "dodge": 60,
+ "hp": 1600,
+ "intelligence": 4,
+ "physicalAttack": 147,
+ "physicalCritChance": 60,
+ "strength": 4
+ },
+ "items": [
+ 61,
+ 62,
+ 92,
+ 92,
+ 101,
+ 92
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 6,
+ "physicalAttack": 552,
+ "physicalCritChance": 150,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 8,
+ "physicalAttack": 624,
+ "physicalCritChance": 150,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 10,
+ "physicalAttack": 696,
+ "physicalCritChance": 150,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 12,
+ "physicalAttack": 768,
+ "physicalCritChance": 150,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 14,
+ "physicalAttack": 840,
+ "physicalCritChance": 150,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 16,
+ "physicalAttack": 912,
+ "physicalCritChance": 150,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 18,
+ "physicalAttack": 984,
+ "physicalCritChance": 150,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 20,
+ "physicalAttack": 1056,
+ "physicalCritChance": 150,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 22,
+ "physicalAttack": 1128,
+ "physicalCritChance": 150,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 24,
+ "physicalAttack": 1200,
+ "physicalCritChance": 150,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 26,
+ "physicalAttack": 1272,
+ "physicalCritChance": 150,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 28,
+ "physicalAttack": 1344,
+ "physicalCritChance": 150,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 30,
+ "physicalAttack": 1416,
+ "physicalCritChance": 150,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 32,
+ "physicalAttack": 1488,
+ "physicalCritChance": 150,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 34,
+ "physicalAttack": 1560,
+ "physicalCritChance": 150,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 55,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_ork_assassin",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1025",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1025
+ ]
+ },
+ "1026": {
+ "id": 1026,
+ "baseStats": {
+ "agility": 5,
+ "armor": 250,
+ "hp": 5000,
+ "intelligence": 5,
+ "magicPower": 650,
+ "magicResist": 250,
+ "physicalAttack": 650,
+ "strength": 5
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 2400,
+ "intelligence": 4,
+ "strength": 4
+ },
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 3600,
+ "intelligence": 6,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 3600,
+ "intelligence": 8,
+ "physicalAttack": 72,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 3600,
+ "intelligence": 10,
+ "physicalAttack": 144,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 3600,
+ "intelligence": 12,
+ "physicalAttack": 216,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 3600,
+ "intelligence": 14,
+ "physicalAttack": 288,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 3600,
+ "intelligence": 16,
+ "physicalAttack": 360,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 3600,
+ "intelligence": 18,
+ "physicalAttack": 432,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 3600,
+ "intelligence": 20,
+ "physicalAttack": 504,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 3600,
+ "intelligence": 22,
+ "physicalAttack": 576,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 3600,
+ "intelligence": 24,
+ "physicalAttack": 648,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 3600,
+ "intelligence": 26,
+ "physicalAttack": 720,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 3600,
+ "intelligence": 28,
+ "physicalAttack": 792,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 3600,
+ "intelligence": 30,
+ "physicalAttack": 864,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 3600,
+ "intelligence": 32,
+ "physicalAttack": 936,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 3600,
+ "intelligence": 34,
+ "physicalAttack": 1008,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 59,
+ "scale": null,
+ "type": "creep",
+ "asset": "boss_ork",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1026",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1026,
+ 1027
+ ]
+ },
+ "1030": {
+ "id": 1030,
+ "baseStats": {
+ "physicalAttack": 20,
+ "strength": 15
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 267,
+ "strength": 4
+ },
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 1200,
+ "intelligence": 6,
+ "physicalAttack": 267,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 1200,
+ "intelligence": 8,
+ "physicalAttack": 339,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 1200,
+ "intelligence": 10,
+ "physicalAttack": 411,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 28
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 1200,
+ "intelligence": 12,
+ "magicResist": 50,
+ "physicalAttack": 471,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 1200,
+ "intelligence": 14,
+ "magicResist": 50,
+ "physicalAttack": 543,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 1200,
+ "intelligence": 16,
+ "magicResist": 50,
+ "physicalAttack": 615,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 1200,
+ "intelligence": 18,
+ "magicResist": 50,
+ "physicalAttack": 687,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 1200,
+ "intelligence": 20,
+ "magicResist": 50,
+ "physicalAttack": 759,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 29
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 1200,
+ "intelligence": 22,
+ "magicResist": 50,
+ "physicalAttack": 819,
+ "physicalCritChance": 15,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 1200,
+ "intelligence": 24,
+ "magicResist": 50,
+ "physicalAttack": 891,
+ "physicalCritChance": 15,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 1200,
+ "intelligence": 26,
+ "magicResist": 50,
+ "physicalAttack": 963,
+ "physicalCritChance": 15,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 1200,
+ "intelligence": 28,
+ "magicResist": 50,
+ "physicalAttack": 1035,
+ "physicalCritChance": 15,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 1200,
+ "intelligence": 30,
+ "magicResist": 50,
+ "physicalAttack": 1107,
+ "physicalCritChance": 15,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 1200,
+ "intelligence": 32,
+ "magicResist": 50,
+ "physicalAttack": 1179,
+ "physicalCritChance": 15,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 1200,
+ "intelligence": 34,
+ "magicResist": 50,
+ "physicalAttack": 1251,
+ "physicalCritChance": 15,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 56,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_undead_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1030",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1030
+ ]
+ },
+ "1031": {
+ "id": 1031,
+ "baseStats": {
+ "agility": 20,
+ "physicalAttack": 100,
+ "physicalCritChance": 50
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 1200,
+ "intelligence": 4,
+ "physicalAttack": 195,
+ "strength": 4
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 1,
+ 101
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 1200,
+ "intelligence": 6,
+ "physicalAttack": 378,
+ "physicalCritChance": 60,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 1200,
+ "intelligence": 8,
+ "physicalAttack": 450,
+ "physicalCritChance": 60,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 1200,
+ "intelligence": 10,
+ "physicalAttack": 522,
+ "physicalCritChance": 60,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 1200,
+ "intelligence": 12,
+ "physicalAttack": 594,
+ "physicalCritChance": 60,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 1200,
+ "intelligence": 14,
+ "physicalAttack": 666,
+ "physicalCritChance": 60,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 1200,
+ "intelligence": 16,
+ "physicalAttack": 738,
+ "physicalCritChance": 60,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 1200,
+ "intelligence": 18,
+ "physicalAttack": 810,
+ "physicalCritChance": 60,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 1200,
+ "intelligence": 20,
+ "physicalAttack": 882,
+ "physicalCritChance": 60,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 1200,
+ "intelligence": 22,
+ "physicalAttack": 954,
+ "physicalCritChance": 60,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 1200,
+ "intelligence": 24,
+ "physicalAttack": 1026,
+ "physicalCritChance": 60,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 1200,
+ "intelligence": 26,
+ "physicalAttack": 1098,
+ "physicalCritChance": 60,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 1200,
+ "intelligence": 28,
+ "physicalAttack": 1170,
+ "physicalCritChance": 60,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 1200,
+ "intelligence": 30,
+ "physicalAttack": 1242,
+ "physicalCritChance": 60,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 1200,
+ "intelligence": 32,
+ "physicalAttack": 1314,
+ "physicalCritChance": 60,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 1200,
+ "intelligence": 34,
+ "physicalAttack": 1386,
+ "physicalCritChance": 60,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 1022,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_undead_archer",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1031",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1031
+ ]
+ },
+ "1032": {
+ "id": 1032,
+ "baseStats": {
+ "physicalAttack": 200
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 1,
+ 92
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 1200,
+ "intelligence": 4,
+ "physicalAttack": 318,
+ "strength": 4
+ },
+ "items": [
+ 92,
+ 92,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 2000,
+ "intelligence": 6,
+ "physicalAttack": 588,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 2000,
+ "intelligence": 8,
+ "physicalAttack": 660,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 2000,
+ "intelligence": 10,
+ "physicalAttack": 732,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 2000,
+ "intelligence": 12,
+ "physicalAttack": 804,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 2000,
+ "intelligence": 14,
+ "physicalAttack": 876,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 2000,
+ "intelligence": 16,
+ "physicalAttack": 948,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 2000,
+ "intelligence": 18,
+ "physicalAttack": 1020,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 2000,
+ "intelligence": 20,
+ "physicalAttack": 1092,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 2000,
+ "intelligence": 22,
+ "physicalAttack": 1164,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 2000,
+ "intelligence": 24,
+ "physicalAttack": 1236,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 2000,
+ "intelligence": 26,
+ "physicalAttack": 1308,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 2000,
+ "intelligence": 28,
+ "physicalAttack": 1380,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 2000,
+ "intelligence": 30,
+ "physicalAttack": 1452,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 2000,
+ "intelligence": 32,
+ "physicalAttack": 1524,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 2000,
+ "intelligence": 34,
+ "physicalAttack": 1596,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2037,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_undead_ballista",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1032",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1032
+ ]
+ },
+ "1033": {
+ "id": 1033,
+ "baseStats": {
+ "hp": 250,
+ "intelligence": 15,
+ "magicPower": 350
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 1,
+ 93
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 1200,
+ "intelligence": 4,
+ "magicPower": 200,
+ "physicalAttack": 183,
+ "strength": 4
+ },
+ "items": [
+ 93,
+ 93,
+ 93,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 1800,
+ "intelligence": 6,
+ "magicPower": 800,
+ "physicalAttack": 183,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 1800,
+ "intelligence": 8,
+ "magicPower": 800,
+ "physicalAttack": 255,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 1800,
+ "intelligence": 10,
+ "magicPower": 800,
+ "physicalAttack": 327,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 1800,
+ "intelligence": 12,
+ "magicPower": 800,
+ "physicalAttack": 399,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 1800,
+ "intelligence": 14,
+ "magicPower": 800,
+ "physicalAttack": 471,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 1800,
+ "intelligence": 16,
+ "magicPower": 800,
+ "physicalAttack": 543,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 1800,
+ "intelligence": 18,
+ "magicPower": 800,
+ "physicalAttack": 615,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 1800,
+ "intelligence": 20,
+ "magicPower": 800,
+ "physicalAttack": 687,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 1800,
+ "intelligence": 22,
+ "magicPower": 800,
+ "physicalAttack": 759,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 1800,
+ "intelligence": 24,
+ "magicPower": 800,
+ "physicalAttack": 831,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 1800,
+ "intelligence": 26,
+ "magicPower": 800,
+ "physicalAttack": 903,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 1800,
+ "intelligence": 28,
+ "magicPower": 800,
+ "physicalAttack": 975,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 1800,
+ "intelligence": 30,
+ "magicPower": 800,
+ "physicalAttack": 1047,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 1800,
+ "intelligence": 32,
+ "magicPower": 800,
+ "physicalAttack": 1119,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 1800,
+ "intelligence": 34,
+ "magicPower": 800,
+ "physicalAttack": 1191,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2038,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_undead_leech",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1033",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1033
+ ]
+ },
+ "1034": {
+ "id": 1034,
+ "baseStats": {
+ "agility": 20,
+ "dodge": 45,
+ "hp": 750,
+ "physicalAttack": 150,
+ "physicalCritChance": 30
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 1200,
+ "intelligence": 4,
+ "physicalAttack": 195,
+ "strength": 4
+ },
+ "items": [
+ 92,
+ 1,
+ 2,
+ 2,
+ 2,
+ 101
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 1800,
+ "intelligence": 6,
+ "physicalAttack": 342,
+ "physicalCritChance": 60,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 1800,
+ "intelligence": 8,
+ "physicalAttack": 414,
+ "physicalCritChance": 60,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 1800,
+ "intelligence": 10,
+ "physicalAttack": 486,
+ "physicalCritChance": 60,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 1800,
+ "intelligence": 12,
+ "physicalAttack": 558,
+ "physicalCritChance": 60,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 1800,
+ "intelligence": 14,
+ "physicalAttack": 630,
+ "physicalCritChance": 60,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 1800,
+ "intelligence": 16,
+ "physicalAttack": 702,
+ "physicalCritChance": 60,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 1800,
+ "intelligence": 18,
+ "physicalAttack": 774,
+ "physicalCritChance": 60,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 1800,
+ "intelligence": 20,
+ "physicalAttack": 846,
+ "physicalCritChance": 60,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 1800,
+ "intelligence": 22,
+ "physicalAttack": 918,
+ "physicalCritChance": 60,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 1800,
+ "intelligence": 24,
+ "physicalAttack": 990,
+ "physicalCritChance": 60,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 1800,
+ "intelligence": 26,
+ "physicalAttack": 1062,
+ "physicalCritChance": 60,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 1800,
+ "intelligence": 28,
+ "physicalAttack": 1134,
+ "physicalCritChance": 60,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 1800,
+ "intelligence": 30,
+ "physicalAttack": 1206,
+ "physicalCritChance": 60,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 1800,
+ "intelligence": 32,
+ "physicalAttack": 1278,
+ "physicalCritChance": 60,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 1800,
+ "intelligence": 34,
+ "physicalAttack": 1350,
+ "physicalCritChance": 60,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 57,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_undead_reaper",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1034",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1034
+ ]
+ },
+ "1035": {
+ "id": 1035,
+ "baseStats": {
+ "strength": 10
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 1,
+ 44,
+ 44,
+ 44,
+ 44
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 200,
+ "intelligence": 4,
+ "magicResist": 200,
+ "physicalAttack": 219,
+ "strength": 4
+ },
+ "items": [
+ 2,
+ 2,
+ 44,
+ 44,
+ 44,
+ 44
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 6,
+ "magicResist": 400,
+ "physicalAttack": 219,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 8,
+ "magicResist": 400,
+ "physicalAttack": 291,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 10,
+ "magicResist": 400,
+ "physicalAttack": 363,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 12,
+ "magicResist": 400,
+ "physicalAttack": 435,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 14,
+ "magicResist": 400,
+ "physicalAttack": 507,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 16,
+ "magicResist": 400,
+ "physicalAttack": 579,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 18,
+ "magicResist": 400,
+ "physicalAttack": 651,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 20,
+ "magicResist": 400,
+ "physicalAttack": 723,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 22,
+ "magicResist": 400,
+ "physicalAttack": 795,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 24,
+ "magicResist": 400,
+ "physicalAttack": 867,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 26,
+ "magicResist": 400,
+ "physicalAttack": 939,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 28,
+ "magicResist": 400,
+ "physicalAttack": 1011,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 30,
+ "magicResist": 400,
+ "physicalAttack": 1083,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 32,
+ "magicResist": 400,
+ "physicalAttack": 1155,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 400,
+ "hp": 400,
+ "intelligence": 34,
+ "magicResist": 400,
+ "physicalAttack": 1227,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 58,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_undead_homunculus",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1035",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1035
+ ]
+ },
+ "1036": {
+ "id": 1036,
+ "baseStats": {
+ "agility": 15,
+ "hp": 35000,
+ "intelligence": 15,
+ "magicPower": 2000,
+ "physicalAttack": 500,
+ "strength": 1000
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 29,
+ "scale": null,
+ "type": "creep",
+ "asset": "boss_undead",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1036",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1036,
+ 1037
+ ]
+ },
+ "1040": {
+ "id": 1040,
+ "baseStats": {
+ "agility": -450,
+ "intelligence": -450,
+ "strength": -1750
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 21,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 49
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 24,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 54
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 27,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 60
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 27,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 60
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 1212,
+ "strength": 20
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 1776,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 1848,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 1920,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1992,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 2064,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 2136,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 2208,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 37,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_sea_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1040",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1040
+ ]
+ },
+ "1041": {
+ "id": 1041,
+ "baseStats": {
+ "agility": -2200,
+ "armorPenetration": 750,
+ "intelligence": -450,
+ "physicalAttack": -1500,
+ "physicalCritChance": 500,
+ "strength": -450
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 63,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 21,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 21
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 70,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 24,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 24
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 77,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 27,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 77,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 27,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 177,
+ 173
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "armorPenetration": 600,
+ "intelligence": 20,
+ "physicalAttack": 1188,
+ "physicalCritChance": 166,
+ "strength": 20
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 177,
+ 173
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "armorPenetration": 1200,
+ "intelligence": 22,
+ "physicalAttack": 1728,
+ "physicalCritChance": 332,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "armorPenetration": 1200,
+ "intelligence": 24,
+ "physicalAttack": 1800,
+ "physicalCritChance": 332,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "armorPenetration": 1200,
+ "intelligence": 26,
+ "physicalAttack": 1872,
+ "physicalCritChance": 332,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "armorPenetration": 1200,
+ "intelligence": 28,
+ "physicalAttack": 1944,
+ "physicalCritChance": 332,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "armorPenetration": 1200,
+ "intelligence": 30,
+ "physicalAttack": 2016,
+ "physicalCritChance": 332,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "armorPenetration": 1200,
+ "intelligence": 32,
+ "physicalAttack": 2088,
+ "physicalCritChance": 332,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "armorPenetration": 1200,
+ "intelligence": 34,
+ "physicalAttack": 2160,
+ "physicalCritChance": 332,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 42,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_sea_sniper",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1041",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1041
+ ]
+ },
+ "1042": {
+ "id": 1042,
+ "baseStats": {
+ "agility": -1900,
+ "dodge": 500,
+ "intelligence": -450,
+ "strength": -450
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 56,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 21,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 21
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 62,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 24,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 24
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 69,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 27,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 69,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 27,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 178,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "dodge": 166,
+ "intelligence": 20,
+ "physicalAttack": 1200,
+ "strength": 20
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 178,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "dodge": 332,
+ "intelligence": 22,
+ "physicalAttack": 1752,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "dodge": 332,
+ "intelligence": 24,
+ "physicalAttack": 1824,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "dodge": 332,
+ "intelligence": 26,
+ "physicalAttack": 1896,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "dodge": 332,
+ "intelligence": 28,
+ "physicalAttack": 1968,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "dodge": 332,
+ "intelligence": 30,
+ "physicalAttack": 2040,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "dodge": 332,
+ "intelligence": 32,
+ "physicalAttack": 2112,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "dodge": 332,
+ "intelligence": 34,
+ "physicalAttack": 2184,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 41,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_sea_archer",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1042",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1042
+ ]
+ },
+ "1043": {
+ "id": 1043,
+ "baseStats": {
+ "agility": -450,
+ "armor": 1000,
+ "intelligence": -450,
+ "magicResist": 1000,
+ "physicalAttack": -1000,
+ "strength": -2200
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 20,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 63
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 22,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 70
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 25,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 77
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 25,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 25,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 77
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 175,
+ 176
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 600,
+ "intelligence": 20,
+ "magicResist": 600,
+ "physicalAttack": 1188,
+ "strength": 20
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 175,
+ 176
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 1200,
+ "intelligence": 22,
+ "magicResist": 1200,
+ "physicalAttack": 1728,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 1200,
+ "intelligence": 24,
+ "magicResist": 1200,
+ "physicalAttack": 1800,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 1200,
+ "intelligence": 26,
+ "magicResist": 1200,
+ "physicalAttack": 1872,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 1200,
+ "intelligence": 28,
+ "magicResist": 1200,
+ "physicalAttack": 1944,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "armor": 1200,
+ "intelligence": 30,
+ "magicResist": 1200,
+ "physicalAttack": 2016,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "armor": 1200,
+ "intelligence": 32,
+ "magicResist": 1200,
+ "physicalAttack": 2088,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "armor": 1200,
+ "intelligence": 34,
+ "magicResist": 1200,
+ "physicalAttack": 2160,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 38,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_sea_vasilisk",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1043",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1043
+ ]
+ },
+ "1044": {
+ "id": 1044,
+ "baseStats": {
+ "agility": -450,
+ "intelligence": -1100,
+ "magicPower": -500,
+ "strength": -450
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 49,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 21
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 54,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 24
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 60,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 60,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 169,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "magicPower": 600,
+ "physicalAttack": 1200,
+ "strength": 20
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 169,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "magicPower": 1200,
+ "physicalAttack": 1752,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "magicPower": 1200,
+ "physicalAttack": 1824,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "magicPower": 1200,
+ "physicalAttack": 1896,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "magicPower": 1200,
+ "physicalAttack": 1968,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "magicPower": 1200,
+ "physicalAttack": 2040,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "magicPower": 1200,
+ "physicalAttack": 2112,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "magicPower": 1200,
+ "physicalAttack": 2184,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 40,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_sea_ghost",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1044",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1044
+ ]
+ },
+ "1045": {
+ "id": 1045,
+ "baseStats": {
+ "agility": -650,
+ "intelligence": -1100,
+ "magicPenetration": 1500,
+ "magicPower": -1500,
+ "strength": -700
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 28,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 56,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 35
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 62,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 39
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 35,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 69,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 43
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 35,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 69,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 43
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 174,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "magicPenetration": 600,
+ "physicalAttack": 1200,
+ "strength": 20
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 174,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "magicPenetration": 1200,
+ "physicalAttack": 1752,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "magicPenetration": 1200,
+ "physicalAttack": 1824,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "magicPenetration": 1200,
+ "physicalAttack": 1896,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "magicPenetration": 1200,
+ "physicalAttack": 1968,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "magicPenetration": 1200,
+ "physicalAttack": 2040,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "magicPenetration": 1200,
+ "physicalAttack": 2112,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "magicPenetration": 1200,
+ "physicalAttack": 2184,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 39,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_sea_cannoneer",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1045",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1045
+ ]
+ },
+ "1046": {
+ "id": 1046,
+ "baseStats": {
+ "armor": 2000,
+ "hp": 450000,
+ "magicPower": 20500,
+ "magicResist": 2000,
+ "physicalAttack": 10500
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 1212,
+ "strength": 20
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 1776,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 1848,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 1920,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1992,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 2064,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 2136,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 2208,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 43,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_sea_boss",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1046",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1046,
+ 1047
+ ]
+ },
+ "1050": {
+ "id": 1050,
+ "baseStats": {
+ "agility": -1181,
+ "intelligence": -1181,
+ "strength": -2230
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 36,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 68
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 36,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 68
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 37,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 37,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 69
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 100,
+ "intelligence": 100,
+ "physicalAttack": 1305,
+ "strength": 100
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 92,
+ 92
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 2115,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 2187,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 2259,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 2331,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 61,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_winter_golem",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1050",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1050
+ ]
+ },
+ "1051": {
+ "id": 1051,
+ "baseStats": {
+ "agility": -1181,
+ "armor": -100,
+ "intelligence": -1181,
+ "strength": -2230
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 36,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 68
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 36,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 68
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 37,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 37,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 69
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 92,
+ 92
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 100,
+ "intelligence": 100,
+ "physicalAttack": 1674,
+ "strength": 100
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 92,
+ 92
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 2484,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 2556,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 2628,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 2700,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 62,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_winter_valkyrie",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1051",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 1051,
+ "2": 1052
+ }
+ },
+ "1052": {
+ "id": 1052,
+ "baseStats": {
+ "agility": -1181,
+ "intelligence": -2631,
+ "strength": -1181
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 79,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 36
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 79,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 36
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 37,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 80,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 37
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 93,
+ 93,
+ 93,
+ 93,
+ 93,
+ 93
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 100,
+ "intelligence": 100,
+ "magicPower": 1200,
+ "physicalAttack": 864,
+ "strength": 100
+ },
+ "items": [
+ 93,
+ 93,
+ 93,
+ 93,
+ 93,
+ 93
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "magicPower": 2400,
+ "physicalAttack": 864,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "magicPower": 2400,
+ "physicalAttack": 936,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "magicPower": 2400,
+ "physicalAttack": 1008,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "magicPower": 2400,
+ "physicalAttack": 1080,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 1025,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_winter_draugr",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1052",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1053
+ ]
+ },
+ "1053": {
+ "id": 1053,
+ "baseStats": {
+ "agility": -1181,
+ "hp": -5000,
+ "intelligence": -2631,
+ "strength": -1181
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 79,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 26
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 26,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 79,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 26
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 80,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 93,
+ 93,
+ 93,
+ 93,
+ 93,
+ 93
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 100,
+ "intelligence": 100,
+ "magicPower": 1200,
+ "physicalAttack": 864,
+ "strength": 100
+ },
+ "items": [
+ 93,
+ 93,
+ 93,
+ 93,
+ 93,
+ 93
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "magicPower": 2400,
+ "physicalAttack": 864,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "magicPower": 2400,
+ "physicalAttack": 936,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "magicPower": 2400,
+ "physicalAttack": 1008,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "magicPower": 2400,
+ "physicalAttack": 1080,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 2041,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_winter_head",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1053",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 1054,
+ "2": 1055
+ }
+ },
+ "1054": {
+ "id": 1054,
+ "baseStats": {
+ "agility": -1181,
+ "hp": -5000,
+ "intelligence": -2631,
+ "strength": -1181
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 79,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 36
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 36,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 79,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 36
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 37,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 80,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 37
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 93,
+ 93,
+ 93,
+ 93,
+ 93,
+ 93
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 100,
+ "intelligence": 100,
+ "magicPower": 1200,
+ "physicalAttack": 864,
+ "strength": 100
+ },
+ "items": [
+ 93,
+ 93,
+ 93,
+ 93,
+ 93,
+ 93
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "magicPower": 2400,
+ "physicalAttack": 864,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "magicPower": 2400,
+ "physicalAttack": 936,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "magicPower": 2400,
+ "physicalAttack": 1008,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "magicPower": 2400,
+ "physicalAttack": 1080,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 2039,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_winter_spirit",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1054",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1056
+ ]
+ },
+ "1055": {
+ "id": 1055,
+ "baseStats": {
+ "agility": -2986,
+ "intelligence": -1181,
+ "strength": -1181
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 89,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 36,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 36
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 89,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 36,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 36
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 90,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 37,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 37
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 100,
+ "intelligence": 100,
+ "physicalAttack": 936,
+ "strength": 100
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "agility",
+ "battleOrder": 2040,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_winter_singer",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1055",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 1057,
+ "2": 1058
+ }
+ },
+ "1056": {
+ "id": 1056,
+ "baseStats": {
+ "armor": 6000,
+ "hp": 450000,
+ "magicPower": 40000,
+ "physicalAttack": 6000
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 64,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_winter_bird",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1056",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 1059,
+ "2": 1061,
+ "3": 1060
+ }
+ },
+ "1057": {
+ "id": 1057,
+ "baseStats": {
+ "armor": 2000,
+ "hp": 500000,
+ "magicPower": 5000,
+ "magicResist": 6000,
+ "physicalAttack": 15000
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 63,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_winter_boss",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1057",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 1062,
+ "1": 1064,
+ "2": 1063
+ }
+ },
+ "1061": {
+ "id": 1061,
+ "baseStats": {
+ "agility": -138340,
+ "armor": 10000,
+ "intelligence": -69170,
+ "strength": -69170
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 1200,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 600
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 1300,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 650
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "agility",
+ "battleOrder": 77,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_dwarven_drill",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1061",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1070
+ ]
+ },
+ "1062": {
+ "id": 1062,
+ "baseStats": {
+ "agility": -138340,
+ "armor": 10000,
+ "intelligence": -69170,
+ "strength": -69170
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 1200,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 600
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 1300,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 650
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "agility",
+ "battleOrder": 74,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_dwarven_golem",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1062",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 1071,
+ "2": 1072
+ }
+ },
+ "1063": {
+ "id": 1063,
+ "baseStats": {
+ "agility": -69170,
+ "intelligence": -69170,
+ "physicalAttack": 10000,
+ "strength": -138340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 75,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_dwarven_electric",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1063",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1073,
+ 1074
+ ]
+ },
+ "1064": {
+ "id": 1064,
+ "baseStats": {
+ "agility": -69170,
+ "hp": -50000,
+ "intelligence": -69170,
+ "magicResist": 10000,
+ "physicalAttack": 10000,
+ "strength": -138340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 76,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_dwarven_axe",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1064",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1075
+ ]
+ },
+ "1065": {
+ "id": 1065,
+ "baseStats": {
+ "agility": -69170,
+ "intelligence": -69170,
+ "physicalAttack": 10000,
+ "strength": -138340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 1002,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_dwarven_turret",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1065",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 1076,
+ "2": 1077
+ }
+ },
+ "1066": {
+ "id": 1066,
+ "baseStats": {
+ "agility": -69170,
+ "hp": -50000,
+ "intelligence": -69170,
+ "magicResist": 10000,
+ "physicalAttack": 10000,
+ "strength": -138340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 1001,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_dwarven_gunner",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1066",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1078
+ ]
+ },
+ "1067": {
+ "id": 1067,
+ "baseStats": {
+ "agility": -69170,
+ "hp": -50000,
+ "intelligence": -138340,
+ "magicPower": 10000,
+ "magicResist": 10000,
+ "strength": -69170
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1200,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 600
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1300,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 650
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 2052,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_dwarven_priest",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1067",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1079,
+ 1080
+ ]
+ },
+ "1068": {
+ "id": 1068,
+ "baseStats": {
+ "agility": -69170,
+ "hp": -50000,
+ "intelligence": -138340,
+ "magicPower": 10000,
+ "magicResist": 10000,
+ "strength": -69170
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1200,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 600
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1300,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 650
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 2053,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_dwarven_shaman",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1068",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1081
+ ]
+ },
+ "1069": {
+ "id": 1069,
+ "baseStats": {
+ "agility": -69170,
+ "hp": 1000000,
+ "intelligence": -69170,
+ "magicResist": 20000,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 78,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_dwarven_boss_king",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1069",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 1082,
+ "2": 1083,
+ "3": 1084
+ }
+ },
+ "1070": {
+ "id": 1070,
+ "baseStats": {
+ "physicalAttack": 300000
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 80,
+ "scale": 2.86,
+ "type": "creep",
+ "asset": "creep_dwarven_boss_last",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1070",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 1085,
+ "2": 1086,
+ "3": 1087,
+ "4": 1088,
+ "5": 1089,
+ "6": 1090,
+ "7": 1091
+ }
+ },
+ "1071": {
+ "id": 1071,
+ "baseStats": {
+ "physicalAttack": 300000
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": 2.86,
+ "type": "creep",
+ "asset": "creep_seimour_epicstart",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1070",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 1092,
+ "2": 1093
+ }
+ },
+ "1072": {
+ "id": 1072,
+ "baseStats": {
+ "agility": 2,
+ "intelligence": 1,
+ "physicalAttack": 25,
+ "strength": 1
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 2
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 11
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 15,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 20,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 17,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 200,
+ "intelligence": 2,
+ "physicalAttack": 60,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 101,
+ 56,
+ 92,
+ 92,
+ 101
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 1200,
+ "intelligence": 4,
+ "physicalAttack": 465,
+ "physicalCritChance": 120,
+ "strength": 4
+ },
+ "items": [
+ 92,
+ 101,
+ 56,
+ 92,
+ 92,
+ 101
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 2200,
+ "intelligence": 6,
+ "physicalAttack": 870,
+ "physicalCritChance": 240,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 2200,
+ "intelligence": 8,
+ "physicalAttack": 942,
+ "physicalCritChance": 240,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 2200,
+ "intelligence": 10,
+ "physicalAttack": 1014,
+ "physicalCritChance": 240,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 2200,
+ "intelligence": 12,
+ "physicalAttack": 1086,
+ "physicalCritChance": 240,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 2200,
+ "intelligence": 14,
+ "physicalAttack": 1158,
+ "physicalCritChance": 240,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 2200,
+ "intelligence": 16,
+ "physicalAttack": 1230,
+ "physicalCritChance": 240,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 2200,
+ "intelligence": 18,
+ "physicalAttack": 1302,
+ "physicalCritChance": 240,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 2200,
+ "intelligence": 20,
+ "physicalAttack": 1374,
+ "physicalCritChance": 240,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 2200,
+ "intelligence": 22,
+ "physicalAttack": 1446,
+ "physicalCritChance": 240,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 2200,
+ "intelligence": 24,
+ "physicalAttack": 1518,
+ "physicalCritChance": 240,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 2200,
+ "intelligence": 26,
+ "physicalAttack": 1590,
+ "physicalCritChance": 240,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 2200,
+ "intelligence": 28,
+ "physicalAttack": 1662,
+ "physicalCritChance": 240,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 2200,
+ "intelligence": 30,
+ "physicalAttack": 1734,
+ "physicalCritChance": 240,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 2200,
+ "intelligence": 32,
+ "physicalAttack": 1806,
+ "physicalCritChance": 240,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 2200,
+ "intelligence": 34,
+ "physicalAttack": 1878,
+ "physicalCritChance": 240,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "artifacts": [
+ 0,
+ 0,
+ 0
+ ],
+ "mainStat": "strength",
+ "battleOrder": 1008,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_demon_archer",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1001",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 1094
+ ]
+ },
+ "2000": {
+ "id": 2000,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 25,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": [],
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "physicalAttack": 1152
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "physicalAttack": 1224
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "asset": "boss_firegolem",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2000",
+ "fragmentSpecialCost": 0,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 3000,
+ 3001,
+ 3002
+ ]
+ },
+ "2001": {
+ "id": 2001,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 26,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": [],
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "physicalAttack": 1152
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "physicalAttack": 1224
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "asset": "boss_spider",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2001",
+ "fragmentSpecialCost": 0,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 3010,
+ 3011,
+ 3012
+ ]
+ },
+ "2002": {
+ "id": 2002,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 27,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": [],
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "physicalAttack": 1152
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "physicalAttack": 1224
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "asset": "boss_boar",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2002",
+ "fragmentSpecialCost": 0,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 3020,
+ 3021
+ ]
+ },
+ "2003": {
+ "id": 2003,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 28,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": [],
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "physicalAttack": 1152
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "physicalAttack": 1224
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "asset": "boss_fish",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2003",
+ "fragmentSpecialCost": 0,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 3030,
+ 3031,
+ 3032
+ ]
+ },
+ "2004": {
+ "id": 2004,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 29,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": [],
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "physicalAttack": 1152
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "physicalAttack": 1224
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "asset": "boss_fish",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1036",
+ "fragmentSpecialCost": 0,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 3040,
+ "1": 3041,
+ "3": 3042
+ }
+ },
+ "2005": {
+ "id": 2005,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 30,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": [],
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "physicalAttack": 1152
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "physicalAttack": 1224
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "asset": "boss_firegolem",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1036",
+ "fragmentSpecialCost": 0,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "2006": {
+ "id": 2006,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 31,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": [],
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "physicalAttack": 1152
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "physicalAttack": 1224
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "asset": "boss_firegolem",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1036",
+ "fragmentSpecialCost": 0,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "2007": {
+ "id": 2007,
+ "baseStats": [],
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "physicalAttack": 1152
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "physicalAttack": 1224
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 71,
+ "scale": null,
+ "type": "boss",
+ "asset": "boss_firegolem",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2000",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 3043,
+ 3044,
+ 3045
+ ]
+ },
+ "2008": {
+ "id": 2008,
+ "baseStats": [],
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "physicalAttack": 1152
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "physicalAttack": 1224
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 72,
+ "scale": null,
+ "type": "boss",
+ "asset": "boss_spider",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2001",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 3046,
+ 3047,
+ 3048
+ ]
+ },
+ "2009": {
+ "id": 2009,
+ "baseStats": [],
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "physicalAttack": 1152
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "physicalAttack": 1224
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 73,
+ "scale": null,
+ "type": "boss",
+ "asset": "boss_boar",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2002",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 3049,
+ 3050
+ ]
+ },
+ "2010": {
+ "id": 2010,
+ "baseStats": {
+ "agility": 15,
+ "intelligence": 15,
+ "magicPower": 1500,
+ "strength": 15
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": 1.3,
+ "type": "creep",
+ "asset": "boss_event_archdemon",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1006",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 2016,
+ "1": 2015
+ }
+ },
+ "2011": {
+ "id": 2011,
+ "baseStats": {
+ "agility": -69170,
+ "hp": 1000000,
+ "intelligence": -69170,
+ "magicResist": 20000,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 81,
+ "scale": 1.3,
+ "type": "creep",
+ "asset": "creep_invasion_leviathan",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2011",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 2018,
+ 2019
+ ]
+ },
+ "2012": {
+ "id": 2012,
+ "baseStats": {
+ "agility": -69170,
+ "hp": 2000000,
+ "intelligence": -69170,
+ "magicResist": 20000,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 1003,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_storm_sphere",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2012",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 2020
+ ]
+ },
+ "2013": {
+ "id": 2013,
+ "baseStats": {
+ "agility": -69170,
+ "hp": -1700000,
+ "intelligence": -69170,
+ "magicResist": 20000,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 1005,
+ "scale": 1.1,
+ "type": "creep",
+ "asset": "creep_storm_eye",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2013",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 2021,
+ "2": 2022
+ }
+ },
+ "2014": {
+ "id": 2014,
+ "baseStats": {
+ "agility": -69170,
+ "hp": -200000,
+ "intelligence": -69170,
+ "magicResist": 20000,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 1004,
+ "scale": 1.8,
+ "type": "creep",
+ "asset": "creep_storm_tentacle",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2014",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 2023,
+ "2": 2024
+ }
+ },
+ "2015": {
+ "id": 2015,
+ "baseStats": {
+ "agility": -69170,
+ "hp": 2000000,
+ "intelligence": -69170,
+ "magicResist": 20000,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": 2,
+ "type": "creep",
+ "asset": "creep_boss_star_child",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2015",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 2025,
+ "2": 2026,
+ "3": 2027,
+ "4": 2028,
+ "5": 2029
+ }
+ },
+ "2016": {
+ "id": 2016,
+ "baseStats": {
+ "agility": -69170,
+ "hp": 2000000,
+ "intelligence": -69170,
+ "magicResist": 20000,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": 1.2,
+ "type": "creep",
+ "asset": "creep_asgard_miniboss_blackholes",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2016",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 2031,
+ 2032
+ ]
+ },
+ "2017": {
+ "id": 2017,
+ "baseStats": {
+ "agility": -69170,
+ "hp": 2000000,
+ "intelligence": -69170,
+ "magicResist": 20000,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": 1.7,
+ "type": "creep",
+ "asset": "creep_asgard_miniboss_stars",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2017",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 2033,
+ 2034
+ ]
+ },
+ "2018": {
+ "id": 2018,
+ "baseStats": {
+ "agility": -69170,
+ "hp": 2000000,
+ "intelligence": -69170,
+ "magicResist": 20000,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": 1.5,
+ "type": "creep",
+ "asset": "creep_asgard_miniboss_nightmares",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2018",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 2035,
+ 2036
+ ]
+ },
+ "2019": {
+ "id": 2019,
+ "baseStats": {
+ "physicalAttack": 20,
+ "strength": 15
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 267,
+ "strength": 4
+ },
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 1200,
+ "intelligence": 6,
+ "physicalAttack": 267,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 1200,
+ "intelligence": 8,
+ "physicalAttack": 339,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 1200,
+ "intelligence": 10,
+ "physicalAttack": 411,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 28
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 1200,
+ "intelligence": 12,
+ "magicResist": 50,
+ "physicalAttack": 471,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 1200,
+ "intelligence": 14,
+ "magicResist": 50,
+ "physicalAttack": 543,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 1200,
+ "intelligence": 16,
+ "magicResist": 50,
+ "physicalAttack": 615,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 1200,
+ "intelligence": 18,
+ "magicResist": 50,
+ "physicalAttack": 687,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 1200,
+ "intelligence": 20,
+ "magicResist": 50,
+ "physicalAttack": 759,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 29
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 1200,
+ "intelligence": 22,
+ "magicResist": 50,
+ "physicalAttack": 819,
+ "physicalCritChance": 15,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 1200,
+ "intelligence": 24,
+ "magicResist": 50,
+ "physicalAttack": 891,
+ "physicalCritChance": 15,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 1200,
+ "intelligence": 26,
+ "magicResist": 50,
+ "physicalAttack": 963,
+ "physicalCritChance": 15,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 1200,
+ "intelligence": 28,
+ "magicResist": 50,
+ "physicalAttack": 1035,
+ "physicalCritChance": 15,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 1200,
+ "intelligence": 30,
+ "magicResist": 50,
+ "physicalAttack": 1107,
+ "physicalCritChance": 15,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 1200,
+ "intelligence": 32,
+ "magicResist": 50,
+ "physicalAttack": 1179,
+ "physicalCritChance": 15,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 1200,
+ "intelligence": 34,
+ "magicResist": 50,
+ "physicalAttack": 1251,
+ "physicalCritChance": 15,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_asgard_tank",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2019",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 2037
+ ]
+ },
+ "2020": {
+ "id": 2020,
+ "baseStats": {
+ "agility": 25,
+ "hp": 750,
+ "intelligence": 5,
+ "physicalAttack": 100,
+ "physicalCritChance": 30,
+ "strength": 5
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 2
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 92,
+ 102,
+ 101,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "dodge": 60,
+ "hp": 1600,
+ "intelligence": 4,
+ "physicalAttack": 147,
+ "physicalCritChance": 60,
+ "strength": 4
+ },
+ "items": [
+ 61,
+ 62,
+ 92,
+ 92,
+ 101,
+ 92
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 6,
+ "physicalAttack": 552,
+ "physicalCritChance": 150,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 8,
+ "physicalAttack": 624,
+ "physicalCritChance": 150,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 10,
+ "physicalAttack": 696,
+ "physicalCritChance": 150,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 12,
+ "physicalAttack": 768,
+ "physicalCritChance": 150,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 14,
+ "physicalAttack": 840,
+ "physicalCritChance": 150,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 16,
+ "physicalAttack": 912,
+ "physicalCritChance": 150,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 18,
+ "physicalAttack": 984,
+ "physicalCritChance": 150,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 20,
+ "physicalAttack": 1056,
+ "physicalCritChance": 150,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 22,
+ "physicalAttack": 1128,
+ "physicalCritChance": 150,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 24,
+ "physicalAttack": 1200,
+ "physicalCritChance": 150,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 26,
+ "physicalAttack": 1272,
+ "physicalCritChance": 150,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 28,
+ "physicalAttack": 1344,
+ "physicalCritChance": 150,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 30,
+ "physicalAttack": 1416,
+ "physicalCritChance": 150,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 32,
+ "physicalAttack": 1488,
+ "physicalCritChance": 150,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "dodge": 90,
+ "hp": 1600,
+ "intelligence": 34,
+ "physicalAttack": 1560,
+ "physicalCritChance": 150,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_asgard_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2020",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 2038
+ ]
+ },
+ "2021": {
+ "id": 2021,
+ "baseStats": {
+ "agility": -450,
+ "intelligence": -1100,
+ "magicPower": -500,
+ "strength": -450
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 49,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 21
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 54,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 24
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 60,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 60,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 169,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "magicPower": 600,
+ "physicalAttack": 1200,
+ "strength": 20
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 169,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "magicPower": 1200,
+ "physicalAttack": 1752,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "magicPower": 1200,
+ "physicalAttack": 1824,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "magicPower": 1200,
+ "physicalAttack": 1896,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "magicPower": 1200,
+ "physicalAttack": 1968,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "magicPower": 1200,
+ "physicalAttack": 2040,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "magicPower": 1200,
+ "physicalAttack": 2112,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "magicPower": 1200,
+ "physicalAttack": 2184,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_asgard_mage",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2021",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 2039
+ ]
+ },
+ "2022": {
+ "id": 2022,
+ "baseStats": {
+ "agility": -69170,
+ "hp": -50000,
+ "intelligence": -138340,
+ "magicPower": 10000,
+ "magicResist": 10000,
+ "strength": -69170
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1200,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 600
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1300,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 650
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 0,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_asgard_priest",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2022",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 2040,
+ 2041
+ ]
+ },
+ "2023": {
+ "id": 2023,
+ "baseStats": {
+ "agility": -69170,
+ "hp": -50000,
+ "intelligence": -138340,
+ "magicPower": 10000,
+ "magicResist": 10000,
+ "strength": -69170
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1200,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 600
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1300,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 650
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 0,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_asgard_mage_aoe",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2023",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 2042,
+ "2": 2043
+ }
+ },
+ "2024": {
+ "id": 2024,
+ "baseStats": {
+ "agility": -69170,
+ "hp": 1000000,
+ "intelligence": -69170,
+ "magicResist": 20000,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": 1.3,
+ "type": "creep",
+ "asset": "boss_invasion_hydra_black",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2024",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 2045,
+ "2": 2046,
+ "3": 2047
+ }
+ },
+ "2025": {
+ "id": 2025,
+ "baseStats": {
+ "agility": -69170,
+ "hp": 2000000,
+ "intelligence": -69170,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": 1.5,
+ "type": "creep",
+ "asset": "boss2025_astral_orchestra",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2025",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 3051,
+ "1": 3052,
+ "2": 3053,
+ "3": 3054,
+ "4": 3055,
+ "6": 3056
+ }
+ },
+ "2026": {
+ "id": 2026,
+ "baseStats": {
+ "physicalAttack": 20,
+ "strength": 15
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 92,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 267,
+ "strength": 4
+ },
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 1200,
+ "intelligence": 6,
+ "physicalAttack": 267,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 1200,
+ "intelligence": 8,
+ "physicalAttack": 339,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 1200,
+ "intelligence": 10,
+ "physicalAttack": 411,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 28
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 1200,
+ "intelligence": 12,
+ "magicResist": 50,
+ "physicalAttack": 471,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 1200,
+ "intelligence": 14,
+ "magicResist": 50,
+ "physicalAttack": 543,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 1200,
+ "intelligence": 16,
+ "magicResist": 50,
+ "physicalAttack": 615,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 1200,
+ "intelligence": 18,
+ "magicResist": 50,
+ "physicalAttack": 687,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 1200,
+ "intelligence": 20,
+ "magicResist": 50,
+ "physicalAttack": 759,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 29
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 1200,
+ "intelligence": 22,
+ "magicResist": 50,
+ "physicalAttack": 819,
+ "physicalCritChance": 15,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 1200,
+ "intelligence": 24,
+ "magicResist": 50,
+ "physicalAttack": 891,
+ "physicalCritChance": 15,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 1200,
+ "intelligence": 26,
+ "magicResist": 50,
+ "physicalAttack": 963,
+ "physicalCritChance": 15,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 1200,
+ "intelligence": 28,
+ "magicResist": 50,
+ "physicalAttack": 1035,
+ "physicalCritChance": 15,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 1200,
+ "intelligence": 30,
+ "magicResist": 50,
+ "physicalAttack": 1107,
+ "physicalCritChance": 15,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 1200,
+ "intelligence": 32,
+ "magicResist": 50,
+ "physicalAttack": 1179,
+ "physicalCritChance": 15,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 1200,
+ "intelligence": 34,
+ "magicResist": 50,
+ "physicalAttack": 1251,
+ "physicalCritChance": 15,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 18,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_astral_tank",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2026",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 3057,
+ "2": 3058
+ }
+ },
+ "2027": {
+ "id": 2027,
+ "baseStats": {
+ "agility": -450,
+ "intelligence": -1100,
+ "magicPower": -500,
+ "strength": -450
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 49,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 21
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 54,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 24
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 60,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 60,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 27
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 169,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "magicPower": 600,
+ "physicalAttack": 1200,
+ "strength": 20
+ },
+ "items": [
+ 92,
+ 92,
+ 92,
+ 92,
+ 169,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "magicPower": 1200,
+ "physicalAttack": 1752,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "magicPower": 1200,
+ "physicalAttack": 1824,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "magicPower": 1200,
+ "physicalAttack": 1896,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "magicPower": 1200,
+ "physicalAttack": 1968,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "magicPower": 1200,
+ "physicalAttack": 2040,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "magicPower": 1200,
+ "physicalAttack": 2112,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "magicPower": 1200,
+ "physicalAttack": 2184,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2011,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_astral_mage",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2027",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 3059,
+ "2": 3060
+ }
+ },
+ "2028": {
+ "id": 2028,
+ "baseStats": {
+ "agility": -69170,
+ "hp": -50000,
+ "intelligence": -138340,
+ "magicPower": 10000,
+ "magicResist": 10000,
+ "strength": -69170
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1200,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 600
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1300,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 650
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 2026,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_astral_support",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2028",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 3061,
+ "2": 3062
+ }
+ },
+ "2029": {
+ "id": 2029,
+ "baseStats": {
+ "agility": -69170,
+ "hp": 2000000,
+ "intelligence": -69170,
+ "magicResist": 20000,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": 1.2,
+ "type": "creep",
+ "asset": "creep_astral_miniboss_paralyse",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2029",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 3063,
+ "2": 3064
+ }
+ },
+ "2030": {
+ "id": 2030,
+ "baseStats": {
+ "agility": -69170,
+ "hp": 2000000,
+ "intelligence": -69170,
+ "magicResist": 20000,
+ "strength": -88340
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 600,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1200
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 650,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1300
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": 1.2,
+ "type": "creep",
+ "asset": "creep_astral_miniboss_fatigue",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2030",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 3065,
+ "2": 3066
+ }
+ },
+ "2031": {
+ "id": 2031,
+ "baseStats": {
+ "agility": -69170,
+ "hp": -50000,
+ "intelligence": -138340,
+ "magicPower": 10000,
+ "magicResist": 10000,
+ "strength": -69170
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 600,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1200,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 600
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 650,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1300,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 650
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1152,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1224,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 0,
+ "scale": 1.2,
+ "type": "creep",
+ "asset": "creep_astral_support_bg",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "2028",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 3069,
+ "2": 3070
+ }
+ },
+ "4000": {
+ "id": 4000,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 15913,
+ "physicalAttack": 977
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 66,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4000_water_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4000",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4000",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4000,
+ 4001
+ ]
+ },
+ "4001": {
+ "id": 4001,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 13020,
+ "physicalAttack": 1194
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 1027,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4001_water_range",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4001",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4001",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4002,
+ 4003
+ ]
+ },
+ "4002": {
+ "id": 4002,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 14467,
+ "physicalAttack": 1085
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 2043,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4002_water_mage",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4002",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4002",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4004,
+ 4005
+ ]
+ },
+ "4003": {
+ "id": 4003,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 15190,
+ "physicalAttack": 1031
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 2048,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4003_water_ultra",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4003",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4003",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4006,
+ 4007,
+ 4008
+ ]
+ },
+ "4010": {
+ "id": 4010,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 12990,
+ "physicalAttack": 1196
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "agility",
+ "battleOrder": 69,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4010_fire_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4010",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4010",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4009,
+ 4010
+ ]
+ },
+ "4011": {
+ "id": 4011,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 10212,
+ "physicalAttack": 1404
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "agility",
+ "battleOrder": 1028,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4011_fire_range",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4011",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4011",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4011,
+ 4012
+ ]
+ },
+ "4012": {
+ "id": 4012,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 11573,
+ "physicalAttack": 1302
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "agility",
+ "battleOrder": 2050,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4012_fire_mage",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4012",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4012",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4013,
+ 4014
+ ]
+ },
+ "4013": {
+ "id": 4013,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 12275,
+ "physicalAttack": 1249
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "agility",
+ "battleOrder": 2045,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4013_fire_ultra",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4013",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4013",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4015,
+ 4016,
+ 4017
+ ]
+ },
+ "4014": {
+ "id": 4014,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 17360,
+ "physicalAttack": 868
+ },
+ "stars": [],
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": []
+ },
+ "17": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": []
+ },
+ "18": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": []
+ }
+ },
+ "runes": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "artifacts": [
+ 0,
+ 0,
+ 0
+ ],
+ "mainStat": "agility",
+ "battleOrder": 2001,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4014_fire_summon",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4014",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": {
+ "preorder": "2025-12-05 02:00:00",
+ "full": "2025-12-12 02:00:00"
+ },
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4049,
+ 4050,
+ 4051
+ ]
+ },
+ "4020": {
+ "id": 4020,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 18722,
+ "physicalAttack": 766
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 70,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4020_earth_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4020",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4020",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4018,
+ 4019
+ ]
+ },
+ "4021": {
+ "id": 4021,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 15943,
+ "physicalAttack": 974
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2051,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4021_earth_range",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4021",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4021",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4020,
+ 4021
+ ]
+ },
+ "4022": {
+ "id": 4022,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 17360,
+ "physicalAttack": 868
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 1030,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4022_earth_mage",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4022",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4022",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4022,
+ 4023
+ ]
+ },
+ "4023": {
+ "id": 4023,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 18048,
+ "physicalAttack": 816
+ },
+ "stars": [],
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "artifacts": [
+ 0,
+ 0,
+ 0
+ ],
+ "mainStat": "strength",
+ "battleOrder": 2046,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4023_earth_ultra",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4023",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4023",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4024,
+ 4025,
+ 4026
+ ]
+ },
+ "4024": {
+ "id": 4024,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 17360,
+ "physicalAttack": 868
+ },
+ "stars": [],
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": []
+ },
+ "17": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": []
+ },
+ "18": {
+ "battleStatData": {
+ "physicalAttack": 1080
+ },
+ "items": []
+ }
+ },
+ "runes": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "artifacts": [
+ 0,
+ 0,
+ 0
+ ],
+ "mainStat": "strength",
+ "battleOrder": 2002,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4024_earth_summon",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4024",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": {
+ "preorder": "2025-10-17 02:00:00",
+ "full": "2025-10-24 02:00:00"
+ },
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4045,
+ 4047,
+ 4048
+ ]
+ },
+ "4030": {
+ "id": 4030,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 16120,
+ "physicalAttack": 1070
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 67,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4030_dark_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4030",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4030",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4027,
+ 4028
+ ]
+ },
+ "4031": {
+ "id": 4031,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 12777,
+ "physicalAttack": 1321
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 2042,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4031_dark_range",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4031",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4031",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4029,
+ 4030
+ ]
+ },
+ "4032": {
+ "id": 4032,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 14278,
+ "physicalAttack": 1208
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 1026,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4032_dark_mage",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4032",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4032",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4031,
+ 4032
+ ]
+ },
+ "4033": {
+ "id": 4033,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 13075,
+ "physicalAttack": 1298
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 2049,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4033_dark_ultra",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4033",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4033",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4033,
+ 4034,
+ 4035
+ ]
+ },
+ "4040": {
+ "id": 4040,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 18202,
+ "physicalAttack": 914
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 68,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4040_light_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4040",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4040",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4036,
+ 4037
+ ]
+ },
+ "4041": {
+ "id": 4041,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 13643,
+ "physicalAttack": 1256
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 1029,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4041_light_range",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4041",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4041",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4038,
+ 4039
+ ]
+ },
+ "4042": {
+ "id": 4042,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 15193,
+ "physicalAttack": 1140
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 2047,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4042_light_mage",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4042",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4042",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4040,
+ 4041
+ ]
+ },
+ "4043": {
+ "id": 4043,
+ "baseStats": {
+ "anticrit": 1,
+ "antidodge": 1,
+ "hp": 16713,
+ "physicalAttack": 1026
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 0,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 0
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 2044,
+ "scale": 0.8,
+ "type": "titan",
+ "asset": "titan_4043_light_ultra",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "titan_icon_4043",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": "titan_4043",
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 4042,
+ 4043,
+ 4044
+ ]
+ },
+ "5000": {
+ "id": 5000,
+ "baseStats": {
+ "agility": 5,
+ "armor": 250,
+ "hp": 5000,
+ "intelligence": 5,
+ "magicPower": 650,
+ "magicResist": 250,
+ "physicalAttack": 650,
+ "strength": 5
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 2400,
+ "intelligence": 4,
+ "strength": 4
+ },
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 3600,
+ "intelligence": 6,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 3600,
+ "intelligence": 8,
+ "physicalAttack": 72,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 3600,
+ "intelligence": 10,
+ "physicalAttack": 144,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 3600,
+ "intelligence": 12,
+ "physicalAttack": 216,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 3600,
+ "intelligence": 14,
+ "physicalAttack": 288,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 3600,
+ "intelligence": 16,
+ "physicalAttack": 360,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 3600,
+ "intelligence": 18,
+ "physicalAttack": 432,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 3600,
+ "intelligence": 20,
+ "physicalAttack": 504,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 3600,
+ "intelligence": 22,
+ "physicalAttack": 576,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 3600,
+ "intelligence": 24,
+ "physicalAttack": 648,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 3600,
+ "intelligence": 26,
+ "physicalAttack": 720,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 3600,
+ "intelligence": 28,
+ "physicalAttack": 792,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 3600,
+ "intelligence": 30,
+ "physicalAttack": 864,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 5
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 3600,
+ "intelligence": 32,
+ "physicalAttack": 924,
+ "strength": 37
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 5
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 3600,
+ "intelligence": 34,
+ "physicalAttack": 984,
+ "strength": 44
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 5
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 60,
+ "scale": null,
+ "type": "boss",
+ "asset": "boss_harvest_ork",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1026",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 5000,
+ 5001
+ ]
+ },
+ "5001": {
+ "id": 5001,
+ "baseStats": {
+ "agility": 15,
+ "hp": 27000,
+ "intelligence": 15,
+ "magicPower": 1350,
+ "physicalAttack": 1200,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 16,
+ 16,
+ 16,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 1800,
+ "intelligence": 25,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 1800,
+ "intelligence": 27,
+ "physicalAttack": 72,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 1800,
+ "intelligence": 29,
+ "physicalAttack": 144,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 1800,
+ "intelligence": 31,
+ "physicalAttack": 216,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 1800,
+ "intelligence": 33,
+ "physicalAttack": 288,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 1800,
+ "intelligence": 35,
+ "physicalAttack": 360,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 1800,
+ "intelligence": 37,
+ "physicalAttack": 432,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 1800,
+ "intelligence": 39,
+ "physicalAttack": 504,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 1800,
+ "intelligence": 41,
+ "physicalAttack": 576,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 1800,
+ "intelligence": 43,
+ "physicalAttack": 648,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 1800,
+ "intelligence": 45,
+ "physicalAttack": 720,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 1800,
+ "intelligence": 47,
+ "physicalAttack": 792,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 1800,
+ "intelligence": 49,
+ "physicalAttack": 864,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 1800,
+ "intelligence": 51,
+ "physicalAttack": 936,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 4
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 1800,
+ "intelligence": 58,
+ "physicalAttack": 996,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 4
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 1800,
+ "intelligence": 65,
+ "physicalAttack": 1056,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 4
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 52,
+ "scale": null,
+ "type": "boss",
+ "asset": "boss_harvest_forest",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1016",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 5002,
+ 5003
+ ]
+ },
+ "5002": {
+ "id": 5002,
+ "baseStats": {
+ "armor": 2000,
+ "hp": 500000,
+ "magicPower": 5000,
+ "magicResist": 6000,
+ "physicalAttack": 15000
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 3
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 37,
+ "intelligence": 32,
+ "physicalAttack": 1140,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 3
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 44,
+ "intelligence": 34,
+ "physicalAttack": 1200,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 3
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "intelligence",
+ "battleOrder": 65,
+ "scale": null,
+ "type": "boss",
+ "asset": "creep_harvest_winter_bird",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1057",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 5004,
+ "2": 5006,
+ "3": 5005
+ }
+ },
+ "5003": {
+ "id": 5003,
+ "baseStats": {
+ "agility": 5,
+ "armor": 250,
+ "hp": 5000,
+ "intelligence": 5,
+ "magicPower": 650,
+ "magicResist": 250,
+ "physicalAttack": 650,
+ "strength": 5
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 2400,
+ "intelligence": 4,
+ "strength": 4
+ },
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 3600,
+ "intelligence": 6,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 3600,
+ "intelligence": 8,
+ "physicalAttack": 72,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 3600,
+ "intelligence": 10,
+ "physicalAttack": 144,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 3600,
+ "intelligence": 12,
+ "physicalAttack": 216,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 3600,
+ "intelligence": 14,
+ "physicalAttack": 288,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 3600,
+ "intelligence": 16,
+ "physicalAttack": 360,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 3600,
+ "intelligence": 18,
+ "physicalAttack": 432,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 3600,
+ "intelligence": 20,
+ "physicalAttack": 504,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 3600,
+ "intelligence": 22,
+ "physicalAttack": 576,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 3600,
+ "intelligence": 24,
+ "physicalAttack": 648,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 3600,
+ "intelligence": 26,
+ "physicalAttack": 720,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 3600,
+ "intelligence": 28,
+ "physicalAttack": 792,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 3600,
+ "intelligence": 30,
+ "physicalAttack": 864,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 3800,
+ "intelligence": 32,
+ "physicalAttack": 924,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 4000,
+ "intelligence": 34,
+ "physicalAttack": 984,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": null,
+ "type": "boss",
+ "asset": "creep_halloween_winter_head",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1026",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 5009,
+ "2": 5010
+ }
+ },
+ "5004": {
+ "id": 5004,
+ "baseStats": {
+ "agility": 15,
+ "hp": 27000,
+ "intelligence": 15,
+ "magicPower": 1350,
+ "physicalAttack": 1200,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 16,
+ 16,
+ 16,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 1800,
+ "intelligence": 25,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 1800,
+ "intelligence": 27,
+ "physicalAttack": 72,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 1800,
+ "intelligence": 29,
+ "physicalAttack": 144,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 1800,
+ "intelligence": 31,
+ "physicalAttack": 216,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 1800,
+ "intelligence": 33,
+ "physicalAttack": 288,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 1800,
+ "intelligence": 35,
+ "physicalAttack": 360,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 1800,
+ "intelligence": 37,
+ "physicalAttack": 432,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 1800,
+ "intelligence": 39,
+ "physicalAttack": 504,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 1800,
+ "intelligence": 41,
+ "physicalAttack": 576,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 1800,
+ "intelligence": 43,
+ "physicalAttack": 648,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 1800,
+ "intelligence": 45,
+ "physicalAttack": 720,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 1800,
+ "intelligence": 47,
+ "physicalAttack": 792,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 1800,
+ "intelligence": 49,
+ "physicalAttack": 864,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 1800,
+ "intelligence": 51,
+ "physicalAttack": 936,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 1800,
+ "intelligence": 53,
+ "physicalAttack": 1008,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 1800,
+ "intelligence": 55,
+ "physicalAttack": 1080,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": null,
+ "type": "boss",
+ "asset": "creep_halloween_winter_singer",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "5004",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 5007,
+ "2": 5008
+ }
+ },
+ "5005": {
+ "id": 5005,
+ "baseStats": {
+ "armor": 2000,
+ "hp": 500000,
+ "magicPower": 5000,
+ "magicResist": 6000,
+ "physicalAttack": 15000
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 1,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 1,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 1
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "intelligence": 2,
+ "physicalAttack": 72,
+ "strength": 2
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "intelligence": 4,
+ "physicalAttack": 144,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "intelligence": 6,
+ "physicalAttack": 216,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "intelligence": 8,
+ "physicalAttack": 288,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "intelligence": 10,
+ "physicalAttack": 360,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "intelligence": 12,
+ "physicalAttack": 432,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "intelligence": 14,
+ "physicalAttack": 504,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "intelligence": 16,
+ "physicalAttack": 576,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "intelligence": 18,
+ "physicalAttack": 648,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "intelligence": 20,
+ "physicalAttack": 720,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "intelligence": 22,
+ "physicalAttack": 792,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "intelligence": 24,
+ "physicalAttack": 864,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "intelligence": 26,
+ "physicalAttack": 936,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "intelligence": 28,
+ "physicalAttack": 1008,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "intelligence": 30,
+ "physicalAttack": 1080,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "intelligence": 32,
+ "physicalAttack": 1140,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "intelligence": 34,
+ "physicalAttack": 1200,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": null,
+ "type": "creep",
+ "asset": "creep_halloween_winter_boss",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1057",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 5011,
+ "1": 5013,
+ "2": 5012
+ }
+ },
+ "5024": {
+ "id": 5024,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 2020,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_forest_fairy",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1012",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5025": {
+ "id": 5025,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 39,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_ork_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1020",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5026": {
+ "id": 5026,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 1016,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_ork_archer",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1021",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5027": {
+ "id": 5027,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 2023,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_ork_shaman",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1023",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5028": {
+ "id": 5028,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 41,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_ork_assassin",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1025",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5029": {
+ "id": 5029,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 45,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "boss_ork",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1026",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5030": {
+ "id": 5030,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 23,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_sea_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1040",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5031": {
+ "id": 5031,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 28,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_sea_sniper",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1041",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5032": {
+ "id": 5032,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 27,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_sea_archer",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1042",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5033": {
+ "id": 5033,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 24,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_sea_vasilisk",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1043",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5034": {
+ "id": 5034,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 36,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": [],
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ }
+ },
+ "asset": "creep_forest_savage",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1015",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5035": {
+ "id": 5035,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 29,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": [],
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ }
+ },
+ "asset": "creep_sea_boss",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1046",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "5": 2017
+ }
+ },
+ "5036": {
+ "id": 5036,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 26,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_sea_ghost",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1044",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5037": {
+ "id": 5037,
+ "perk": null,
+ "role": "front",
+ "roleExtended": [
+ "melee_tank"
+ ],
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 8,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": {
+ "agility": 10,
+ "strength": 20,
+ "intelligence": 15,
+ "hp": 500,
+ "physicalAttack": 55
+ },
+ "runes": [
+ 5,
+ 7,
+ 4,
+ 8,
+ 1
+ ],
+ "artifacts": [
+ 1002,
+ 2006,
+ 3002
+ ],
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 8,
+ 9,
+ 14,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "strength": 9
+ },
+ "items": [
+ 13,
+ 9,
+ 10,
+ 18,
+ 27,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 75,
+ "hp": 1355,
+ "intelligence": 5,
+ "magicResist": 50,
+ "physicalAttack": 70,
+ "strength": 23
+ },
+ "items": [
+ 10,
+ 18,
+ 28,
+ 25,
+ 43,
+ 36
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 125,
+ "hp": 2240,
+ "intelligence": 8,
+ "magicResist": 100,
+ "physicalAttack": 136,
+ "strength": 47
+ },
+ "items": [
+ 21,
+ 42,
+ 37,
+ 44,
+ 57,
+ 59
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 325,
+ "hp": 2740,
+ "intelligence": 10,
+ "magicResist": 150,
+ "physicalAttack": 239,
+ "strength": 69
+ },
+ "items": [
+ 33,
+ 36,
+ 43,
+ 57,
+ 59,
+ 77
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 475,
+ "hp": 3240,
+ "intelligence": 17,
+ "lifesteal": 5,
+ "magicResist": 150,
+ "physicalAttack": 342,
+ "strength": 93
+ },
+ "items": [
+ 42,
+ 43,
+ 57,
+ 65,
+ 77,
+ 85
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 525,
+ "hp": 4740,
+ "intelligence": 24,
+ "lifesteal": 10,
+ "magicResist": 230,
+ "physicalAttack": 534,
+ "strength": 110
+ },
+ "items": [
+ 74,
+ 64,
+ 77,
+ 90,
+ 92,
+ 99
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 805,
+ "hp": 5540,
+ "intelligence": 31,
+ "lifesteal": 15,
+ "magicResist": 310,
+ "physicalAttack": 739,
+ "strength": 143
+ },
+ "items": [
+ 87,
+ 77,
+ 90,
+ 99,
+ 91,
+ 123
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 48,
+ "armor": 1085,
+ "hp": 9140,
+ "intelligence": 38,
+ "lifesteal": 20,
+ "magicResist": 310,
+ "physicalAttack": 879,
+ "strength": 196
+ },
+ "items": [
+ 77,
+ 90,
+ 91,
+ 125,
+ 122,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 74,
+ "armor": 1325,
+ "hp": 11140,
+ "intelligence": 64,
+ "lifesteal": 25,
+ "magicResist": 470,
+ "physicalAttack": 1273,
+ "strength": 268
+ },
+ "items": [
+ 85,
+ 125,
+ 122,
+ 124,
+ 131,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 143,
+ "armor": 1485,
+ "hp": 12140,
+ "intelligence": 133,
+ "lifesteal": 35,
+ "magicResist": 630,
+ "physicalAttack": 1597,
+ "strength": 425
+ },
+ "items": [
+ 123,
+ 122,
+ 125,
+ 136,
+ 170,
+ 168
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 183,
+ "armor": 1645,
+ "hp": 13740,
+ "intelligence": 173,
+ "lifesteal": 35,
+ "magicResist": 790,
+ "physicalAttack": 2321,
+ "strength": 652
+ },
+ "items": [
+ 114,
+ 124,
+ 136,
+ 170,
+ 168,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 223,
+ "armor": 1965,
+ "hp": 20140,
+ "intelligence": 213,
+ "lifesteal": 45,
+ "magicResist": 790,
+ "physicalAttack": 2937,
+ "strength": 849
+ },
+ "items": [
+ 122,
+ 123,
+ 170,
+ 175,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 273,
+ "armor": 2725,
+ "hp": 24940,
+ "intelligence": 263,
+ "lifesteal": 45,
+ "magicResist": 790,
+ "physicalAttack": 3685,
+ "strength": 1212
+ },
+ "items": [
+ 131,
+ 122,
+ 176,
+ 183,
+ 201,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 299,
+ "armor": 3205,
+ "hp": 41516,
+ "intelligence": 289,
+ "lifesteal": 45,
+ "magicResist": 1390,
+ "physicalAttack": 6148,
+ "strength": 1268
+ },
+ "items": [
+ 122,
+ 134,
+ 167,
+ 183,
+ 208,
+ 211
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 4,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 3,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 4,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 7,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 12,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 10,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 16,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 13,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 13,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 20,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 17,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "garen_creep_01",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0002",
+ "fragmentSpecialCost": 100,
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "obtainType": "",
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5038": {
+ "id": 5038,
+ "perk": null,
+ "role": "front",
+ "roleExtended": [
+ "melee_tank"
+ ],
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 8,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": {
+ "agility": 10,
+ "strength": 20,
+ "intelligence": 15,
+ "hp": 500,
+ "physicalAttack": 55
+ },
+ "runes": [
+ 5,
+ 7,
+ 4,
+ 8,
+ 1
+ ],
+ "artifacts": [
+ 1002,
+ 2006,
+ 3002
+ ],
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 8,
+ 9,
+ 14,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "strength": 9
+ },
+ "items": [
+ 13,
+ 9,
+ 10,
+ 18,
+ 27,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 75,
+ "hp": 1355,
+ "intelligence": 5,
+ "magicResist": 50,
+ "physicalAttack": 70,
+ "strength": 23
+ },
+ "items": [
+ 10,
+ 18,
+ 28,
+ 25,
+ 43,
+ 36
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 125,
+ "hp": 2240,
+ "intelligence": 8,
+ "magicResist": 100,
+ "physicalAttack": 136,
+ "strength": 47
+ },
+ "items": [
+ 21,
+ 42,
+ 37,
+ 44,
+ 57,
+ 59
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 325,
+ "hp": 2740,
+ "intelligence": 10,
+ "magicResist": 150,
+ "physicalAttack": 239,
+ "strength": 69
+ },
+ "items": [
+ 33,
+ 36,
+ 43,
+ 57,
+ 59,
+ 77
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 475,
+ "hp": 3240,
+ "intelligence": 17,
+ "lifesteal": 5,
+ "magicResist": 150,
+ "physicalAttack": 342,
+ "strength": 93
+ },
+ "items": [
+ 42,
+ 43,
+ 57,
+ 65,
+ 77,
+ 85
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 525,
+ "hp": 4740,
+ "intelligence": 24,
+ "lifesteal": 10,
+ "magicResist": 230,
+ "physicalAttack": 534,
+ "strength": 110
+ },
+ "items": [
+ 74,
+ 64,
+ 77,
+ 90,
+ 92,
+ 99
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 805,
+ "hp": 5540,
+ "intelligence": 31,
+ "lifesteal": 15,
+ "magicResist": 310,
+ "physicalAttack": 739,
+ "strength": 143
+ },
+ "items": [
+ 87,
+ 77,
+ 90,
+ 99,
+ 91,
+ 123
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 48,
+ "armor": 1085,
+ "hp": 9140,
+ "intelligence": 38,
+ "lifesteal": 20,
+ "magicResist": 310,
+ "physicalAttack": 879,
+ "strength": 196
+ },
+ "items": [
+ 77,
+ 90,
+ 91,
+ 125,
+ 122,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 74,
+ "armor": 1325,
+ "hp": 11140,
+ "intelligence": 64,
+ "lifesteal": 25,
+ "magicResist": 470,
+ "physicalAttack": 1273,
+ "strength": 268
+ },
+ "items": [
+ 85,
+ 125,
+ 122,
+ 124,
+ 131,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 143,
+ "armor": 1485,
+ "hp": 12140,
+ "intelligence": 133,
+ "lifesteal": 35,
+ "magicResist": 630,
+ "physicalAttack": 1597,
+ "strength": 425
+ },
+ "items": [
+ 123,
+ 122,
+ 125,
+ 136,
+ 170,
+ 168
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 183,
+ "armor": 1645,
+ "hp": 13740,
+ "intelligence": 173,
+ "lifesteal": 35,
+ "magicResist": 790,
+ "physicalAttack": 2321,
+ "strength": 652
+ },
+ "items": [
+ 114,
+ 124,
+ 136,
+ 170,
+ 168,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 223,
+ "armor": 1965,
+ "hp": 20140,
+ "intelligence": 213,
+ "lifesteal": 45,
+ "magicResist": 790,
+ "physicalAttack": 2937,
+ "strength": 849
+ },
+ "items": [
+ 122,
+ 123,
+ 170,
+ 175,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 273,
+ "armor": 2725,
+ "hp": 24940,
+ "intelligence": 263,
+ "lifesteal": 45,
+ "magicResist": 790,
+ "physicalAttack": 3685,
+ "strength": 1212
+ },
+ "items": [
+ 131,
+ 122,
+ 176,
+ 183,
+ 201,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 299,
+ "armor": 3205,
+ "hp": 41516,
+ "intelligence": 289,
+ "lifesteal": 45,
+ "magicResist": 1390,
+ "physicalAttack": 6148,
+ "strength": 1268
+ },
+ "items": [
+ 122,
+ 134,
+ 167,
+ 183,
+ 208,
+ 211
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 4,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 3,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 4,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 7,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 12,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 10,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 16,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 13,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 13,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 20,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 17,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "garen_creep_02",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0002",
+ "fragmentSpecialCost": 100,
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "obtainType": "",
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5039": {
+ "id": 5039,
+ "perk": null,
+ "role": "front",
+ "roleExtended": [
+ "melee_tank"
+ ],
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 8,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": {
+ "agility": 10,
+ "strength": 20,
+ "intelligence": 15,
+ "hp": 500,
+ "physicalAttack": 55
+ },
+ "runes": [
+ 5,
+ 7,
+ 4,
+ 8,
+ 1
+ ],
+ "artifacts": [
+ 1002,
+ 2006,
+ 3002
+ ],
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 8,
+ 9,
+ 14,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "strength": 9
+ },
+ "items": [
+ 13,
+ 9,
+ 10,
+ 18,
+ 27,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 75,
+ "hp": 1355,
+ "intelligence": 5,
+ "magicResist": 50,
+ "physicalAttack": 70,
+ "strength": 23
+ },
+ "items": [
+ 10,
+ 18,
+ 28,
+ 25,
+ 43,
+ 36
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 125,
+ "hp": 2240,
+ "intelligence": 8,
+ "magicResist": 100,
+ "physicalAttack": 136,
+ "strength": 47
+ },
+ "items": [
+ 21,
+ 42,
+ 37,
+ 44,
+ 57,
+ 59
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 325,
+ "hp": 2740,
+ "intelligence": 10,
+ "magicResist": 150,
+ "physicalAttack": 239,
+ "strength": 69
+ },
+ "items": [
+ 33,
+ 36,
+ 43,
+ 57,
+ 59,
+ 77
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 475,
+ "hp": 3240,
+ "intelligence": 17,
+ "lifesteal": 5,
+ "magicResist": 150,
+ "physicalAttack": 342,
+ "strength": 93
+ },
+ "items": [
+ 42,
+ 43,
+ 57,
+ 65,
+ 77,
+ 85
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 525,
+ "hp": 4740,
+ "intelligence": 24,
+ "lifesteal": 10,
+ "magicResist": 230,
+ "physicalAttack": 534,
+ "strength": 110
+ },
+ "items": [
+ 74,
+ 64,
+ 77,
+ 90,
+ 92,
+ 99
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 805,
+ "hp": 5540,
+ "intelligence": 31,
+ "lifesteal": 15,
+ "magicResist": 310,
+ "physicalAttack": 739,
+ "strength": 143
+ },
+ "items": [
+ 87,
+ 77,
+ 90,
+ 99,
+ 91,
+ 123
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 48,
+ "armor": 1085,
+ "hp": 9140,
+ "intelligence": 38,
+ "lifesteal": 20,
+ "magicResist": 310,
+ "physicalAttack": 879,
+ "strength": 196
+ },
+ "items": [
+ 77,
+ 90,
+ 91,
+ 125,
+ 122,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 74,
+ "armor": 1325,
+ "hp": 11140,
+ "intelligence": 64,
+ "lifesteal": 25,
+ "magicResist": 470,
+ "physicalAttack": 1273,
+ "strength": 268
+ },
+ "items": [
+ 85,
+ 125,
+ 122,
+ 124,
+ 131,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 143,
+ "armor": 1485,
+ "hp": 12140,
+ "intelligence": 133,
+ "lifesteal": 35,
+ "magicResist": 630,
+ "physicalAttack": 1597,
+ "strength": 425
+ },
+ "items": [
+ 123,
+ 122,
+ 125,
+ 136,
+ 170,
+ 168
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 183,
+ "armor": 1645,
+ "hp": 13740,
+ "intelligence": 173,
+ "lifesteal": 35,
+ "magicResist": 790,
+ "physicalAttack": 2321,
+ "strength": 652
+ },
+ "items": [
+ 114,
+ 124,
+ 136,
+ 170,
+ 168,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 223,
+ "armor": 1965,
+ "hp": 20140,
+ "intelligence": 213,
+ "lifesteal": 45,
+ "magicResist": 790,
+ "physicalAttack": 2937,
+ "strength": 849
+ },
+ "items": [
+ 122,
+ 123,
+ 170,
+ 175,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 273,
+ "armor": 2725,
+ "hp": 24940,
+ "intelligence": 263,
+ "lifesteal": 45,
+ "magicResist": 790,
+ "physicalAttack": 3685,
+ "strength": 1212
+ },
+ "items": [
+ 131,
+ 122,
+ 176,
+ 183,
+ 201,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 299,
+ "armor": 3205,
+ "hp": 41516,
+ "intelligence": 289,
+ "lifesteal": 45,
+ "magicResist": 1390,
+ "physicalAttack": 6148,
+ "strength": 1268
+ },
+ "items": [
+ 122,
+ 134,
+ 167,
+ 183,
+ 208,
+ 211
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 4,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 3,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 4,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 7,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 12,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 10,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 16,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 13,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 13,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 20,
+ "hp": 0,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 17,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "garen_creep_03",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0002",
+ "fragmentSpecialCost": 100,
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "obtainType": "",
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5113": {
+ "id": 5113,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 51,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "boss",
+ "baseStats": [],
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "magicPower": 0,
+ "strength": 0,
+ "lifesteal": 0,
+ "intelligence": 0,
+ "hp": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "physicalAttack": 0,
+ "dodge": 0,
+ "armorPenetration": 0,
+ "armor": 0,
+ "magicResist": 0
+ }
+ }
+ },
+ "asset": "skin_33_artemis_angel",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "0033",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5114": {
+ "id": 5114,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 35,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "hp": 50,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 4,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 3,
+ "hp": 105,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 105,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 4,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 10,
+ "hp": 145,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 7,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 12,
+ "hp": 210,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 9,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 16,
+ "hp": 270,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 12,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 18,
+ "hp": 280,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 17,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_forest_tank",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1014",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5115": {
+ "id": 5115,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 45,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "boss_ork",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1026",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5116": {
+ "id": 5116,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 23,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_sea_melee",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1040",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5117": {
+ "id": 5117,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 2020,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_forest_fairy",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1012",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5118": {
+ "id": 5118,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 2023,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_ork_shaman",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1023",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "5119": {
+ "id": 5119,
+ "perk": null,
+ "role": null,
+ "roleExtended": null,
+ "characterType": null,
+ "silhouette": null,
+ "battleOrder": 28,
+ "scale": null,
+ "mainStat": "strength",
+ "type": "creep",
+ "baseStats": {
+ "agility": 5,
+ "strength": 5,
+ "intelligence": 5,
+ "physicalAttack": 0
+ },
+ "runes": null,
+ "artifacts": null,
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "physicalAttack": 72
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "physicalAttack": 144
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "physicalAttack": 216
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "physicalAttack": 288
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "physicalAttack": 360
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "physicalAttack": 432
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "physicalAttack": 504
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "physicalAttack": 576
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "physicalAttack": 648
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "physicalAttack": 720
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "physicalAttack": 792
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "physicalAttack": 864
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "physicalAttack": 936
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "physicalAttack": 1008
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "intelligence": 3,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 2,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 1,
+ "armor": 0
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 6,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 5,
+ "hp": 140,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 2,
+ "armor": 0
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 8,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 6,
+ "hp": 195,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 5,
+ "armor": 0
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 11,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 9,
+ "hp": 295,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 8,
+ "armor": 0
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 14,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 13,
+ "hp": 335,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 11,
+ "armor": 0
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 18,
+ "armorPenetration": 0,
+ "physicalCritChance": 0,
+ "magicPenetration": 0,
+ "dodge": 0,
+ "strength": 15,
+ "hp": 380,
+ "physicalAttack": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "lifesteal": 0,
+ "agility": 16,
+ "armor": 0
+ }
+ }
+ },
+ "asset": "creep_sea_sniper",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "1041",
+ "fragmentSpecialCost": null,
+ "fragmentSellCost": null,
+ "fragmentBuyCost": null,
+ "obtainType": null,
+ "epicArtAsset": null,
+ "lockedUntil": null,
+ "ultCinematic": null,
+ "musicAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": []
+ },
+ "6000": {
+ "id": 6000,
+ "baseStats": {
+ "armorPenetration": 1500,
+ "intelligence": 25,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 23,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 30,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 30
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "armorPenetration": 1752,
+ "intelligence": 2,
+ "strength": 320
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "armorPenetration": 4130,
+ "intelligence": 4,
+ "strength": 754
+ },
+ "items": [
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "armorPenetration": 7134,
+ "intelligence": 6,
+ "strength": 1302
+ },
+ "items": [
+ 2,
+ 2,
+ 3,
+ 13,
+ 13,
+ 14
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "armorPenetration": 9303,
+ "intelligence": 401,
+ "strength": 1697
+ },
+ "items": [
+ 2,
+ 3,
+ 3,
+ 13,
+ 14,
+ 14
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "armorPenetration": 11826,
+ "intelligence": 859,
+ "strength": 2155
+ },
+ "items": [
+ 3,
+ 3,
+ 4,
+ 14,
+ 14,
+ 15
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "armorPenetration": 15202,
+ "intelligence": 1472,
+ "strength": 2768
+ },
+ "items": [
+ 4,
+ 4,
+ 5,
+ 15,
+ 15,
+ 16
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "armorPenetration": 20330,
+ "intelligence": 2404,
+ "strength": 3700
+ },
+ "items": [
+ 4,
+ 5,
+ 5,
+ 15,
+ 16,
+ 16
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "armorPenetration": 26212,
+ "intelligence": 3471,
+ "strength": 4767
+ },
+ "items": [
+ 5,
+ 5,
+ 6,
+ 16,
+ 16,
+ 17
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "armorPenetration": 35157,
+ "intelligence": 5095,
+ "strength": 6391
+ },
+ "items": [
+ 5,
+ 6,
+ 6,
+ 16,
+ 17,
+ 17
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2054,
+ "scale": null,
+ "type": "pet",
+ "asset": "pet01",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "pet_160_6000",
+ "role": "back",
+ "obtainType": "pet_summon",
+ "characterType": "healer",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "control"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 8
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "1": 6000,
+ "2": 6001,
+ "4": 6002
+ }
+ },
+ "6001": {
+ "id": 6001,
+ "baseStats": {
+ "intelligence": 25,
+ "magicPenetration": 1500,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 23,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 30,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 30
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 7,
+ 7,
+ 7,
+ 7,
+ 7,
+ 7
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 2,
+ "magicPenetration": 1752,
+ "strength": 320
+ },
+ "items": [
+ 7,
+ 7,
+ 7,
+ 7,
+ 8,
+ 8
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 4,
+ "magicPenetration": 4130,
+ "strength": 754
+ },
+ "items": [
+ 7,
+ 7,
+ 8,
+ 8,
+ 8,
+ 8
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 6,
+ "magicPenetration": 7134,
+ "strength": 1302
+ },
+ "items": [
+ 8,
+ 8,
+ 9,
+ 23,
+ 23,
+ 24
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 401,
+ "magicPenetration": 9303,
+ "strength": 1697
+ },
+ "items": [
+ 8,
+ 9,
+ 9,
+ 23,
+ 24,
+ 24
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 859,
+ "magicPenetration": 11826,
+ "strength": 2155
+ },
+ "items": [
+ 9,
+ 9,
+ 10,
+ 24,
+ 24,
+ 25
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "intelligence": 1472,
+ "magicPenetration": 15202,
+ "strength": 2768
+ },
+ "items": [
+ 10,
+ 10,
+ 11,
+ 25,
+ 25,
+ 26
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "intelligence": 2404,
+ "magicPenetration": 20330,
+ "strength": 3700
+ },
+ "items": [
+ 10,
+ 11,
+ 11,
+ 25,
+ 26,
+ 26
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "intelligence": 3471,
+ "magicPenetration": 26212,
+ "strength": 4767
+ },
+ "items": [
+ 11,
+ 11,
+ 12,
+ 26,
+ 26,
+ 27
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "intelligence": 5095,
+ "magicPenetration": 35157,
+ "strength": 6391
+ },
+ "items": [
+ 11,
+ 12,
+ 12,
+ 26,
+ 27,
+ 27
+ ]
+ }
+ },
+ "runes": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "artifacts": [
+ 0,
+ 0,
+ 0
+ ],
+ "mainStat": "strength",
+ "battleOrder": 2055,
+ "scale": null,
+ "type": "pet",
+ "asset": "pet02",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "pet_160_6001",
+ "spineEpicArtAsset": {
+ "name": "6001_Oliver"
+ },
+ "role": "back",
+ "obtainType": "pet_summon",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "support",
+ "healer"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 5,
+ 9
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "1": 6005,
+ "2": 6006,
+ "4": 6007
+ }
+ },
+ "6002": {
+ "id": 6002,
+ "baseStats": {
+ "intelligence": 25,
+ "magicPenetration": 1500,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 23,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 30,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 30
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 7,
+ 7,
+ 7,
+ 7,
+ 7,
+ 7
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 2,
+ "magicPenetration": 1752,
+ "strength": 320
+ },
+ "items": [
+ 7,
+ 7,
+ 7,
+ 7,
+ 8,
+ 8
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 4,
+ "magicPenetration": 4130,
+ "strength": 754
+ },
+ "items": [
+ 7,
+ 7,
+ 8,
+ 8,
+ 8,
+ 8
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 6,
+ "magicPenetration": 7134,
+ "strength": 1302
+ },
+ "items": [
+ 8,
+ 8,
+ 9,
+ 13,
+ 13,
+ 14
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 401,
+ "magicPenetration": 9303,
+ "strength": 1697
+ },
+ "items": [
+ 8,
+ 9,
+ 9,
+ 13,
+ 14,
+ 14
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 859,
+ "magicPenetration": 11826,
+ "strength": 2155
+ },
+ "items": [
+ 9,
+ 9,
+ 10,
+ 14,
+ 14,
+ 15
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "intelligence": 1472,
+ "magicPenetration": 15202,
+ "strength": 2768
+ },
+ "items": [
+ 10,
+ 10,
+ 11,
+ 15,
+ 15,
+ 16
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "intelligence": 2404,
+ "magicPenetration": 20330,
+ "strength": 3700
+ },
+ "items": [
+ 10,
+ 11,
+ 11,
+ 15,
+ 16,
+ 16
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "intelligence": 3471,
+ "magicPenetration": 26212,
+ "strength": 4767
+ },
+ "items": [
+ 11,
+ 11,
+ 12,
+ 16,
+ 16,
+ 17
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "intelligence": 5095,
+ "magicPenetration": 35157,
+ "strength": 6391
+ },
+ "items": [
+ 11,
+ 12,
+ 12,
+ 16,
+ 17,
+ 17
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2056,
+ "scale": null,
+ "type": "pet",
+ "asset": "pet03",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "pet_160_6002",
+ "role": "back",
+ "obtainType": "pet_summon",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "1": 6010,
+ "2": 6011,
+ "4": 6012
+ }
+ },
+ "6003": {
+ "id": 6003,
+ "baseStats": {
+ "intelligence": 25,
+ "magicPenetration": 1500,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 23,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 30,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 30
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 7,
+ 7,
+ 7,
+ 7,
+ 7,
+ 7
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 2,
+ "magicPenetration": 1752,
+ "strength": 320
+ },
+ "items": [
+ 7,
+ 7,
+ 7,
+ 7,
+ 8,
+ 8
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 4,
+ "magicPenetration": 4130,
+ "strength": 754
+ },
+ "items": [
+ 7,
+ 7,
+ 8,
+ 8,
+ 8,
+ 8
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 6,
+ "magicPenetration": 7134,
+ "strength": 1302
+ },
+ "items": [
+ 8,
+ 8,
+ 9,
+ 18,
+ 18,
+ 19
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 401,
+ "magicPenetration": 9303,
+ "strength": 1697
+ },
+ "items": [
+ 8,
+ 9,
+ 9,
+ 18,
+ 19,
+ 19
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 859,
+ "magicPenetration": 11826,
+ "strength": 2155
+ },
+ "items": [
+ 9,
+ 9,
+ 10,
+ 19,
+ 19,
+ 20
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "intelligence": 1472,
+ "magicPenetration": 15202,
+ "strength": 2768
+ },
+ "items": [
+ 10,
+ 10,
+ 11,
+ 20,
+ 20,
+ 21
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "intelligence": 2404,
+ "magicPenetration": 20330,
+ "strength": 3700
+ },
+ "items": [
+ 10,
+ 11,
+ 11,
+ 20,
+ 21,
+ 21
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "intelligence": 3471,
+ "magicPenetration": 26212,
+ "strength": 4767
+ },
+ "items": [
+ 11,
+ 11,
+ 12,
+ 21,
+ 21,
+ 22
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "intelligence": 5095,
+ "magicPenetration": 35157,
+ "strength": 6391
+ },
+ "items": [
+ 11,
+ 12,
+ 12,
+ 21,
+ 22,
+ 22
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2057,
+ "scale": null,
+ "type": "pet",
+ "asset": "pet04",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "pet_160_6003",
+ "role": "back",
+ "obtainType": "pet_summon",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "control"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 8
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "1": 6015,
+ "2": 6016,
+ "4": 6017
+ }
+ },
+ "6004": {
+ "id": 6004,
+ "baseStats": {
+ "armorPenetration": 1500,
+ "intelligence": 25,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 23,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 30,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 30
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "armorPenetration": 1752,
+ "intelligence": 2,
+ "strength": 320
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "armorPenetration": 4130,
+ "intelligence": 4,
+ "strength": 754
+ },
+ "items": [
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "armorPenetration": 7134,
+ "intelligence": 6,
+ "strength": 1302
+ },
+ "items": [
+ 2,
+ 2,
+ 3,
+ 18,
+ 18,
+ 19
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "armorPenetration": 9303,
+ "intelligence": 401,
+ "strength": 1697
+ },
+ "items": [
+ 2,
+ 3,
+ 3,
+ 18,
+ 19,
+ 19
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "armorPenetration": 11826,
+ "intelligence": 859,
+ "strength": 2155
+ },
+ "items": [
+ 3,
+ 3,
+ 4,
+ 19,
+ 19,
+ 20
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "armorPenetration": 15202,
+ "intelligence": 1472,
+ "strength": 2768
+ },
+ "items": [
+ 4,
+ 4,
+ 5,
+ 20,
+ 20,
+ 21
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "armorPenetration": 20330,
+ "intelligence": 2404,
+ "strength": 3700
+ },
+ "items": [
+ 4,
+ 5,
+ 5,
+ 20,
+ 21,
+ 21
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "armorPenetration": 26212,
+ "intelligence": 3471,
+ "strength": 4767
+ },
+ "items": [
+ 5,
+ 5,
+ 6,
+ 21,
+ 21,
+ 22
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "armorPenetration": 35157,
+ "intelligence": 5095,
+ "strength": 6391
+ },
+ "items": [
+ 5,
+ 6,
+ 6,
+ 21,
+ 22,
+ 22
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2058,
+ "scale": null,
+ "type": "pet",
+ "asset": "pet05",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "pet_160_6004",
+ "role": "back",
+ "obtainType": "pet_summon",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "support"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 5
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "1": 6020,
+ "2": 6021,
+ "4": 6022
+ }
+ },
+ "6005": {
+ "id": 6005,
+ "baseStats": {
+ "armorPenetration": 1500,
+ "intelligence": 25,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 23,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 30,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 30
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "armorPenetration": 1752,
+ "intelligence": 2,
+ "strength": 320
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "armorPenetration": 4130,
+ "intelligence": 4,
+ "strength": 754
+ },
+ "items": [
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "armorPenetration": 7134,
+ "intelligence": 6,
+ "strength": 1302
+ },
+ "items": [
+ 2,
+ 2,
+ 3,
+ 23,
+ 23,
+ 24
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "armorPenetration": 9303,
+ "intelligence": 401,
+ "strength": 1697
+ },
+ "items": [
+ 2,
+ 3,
+ 3,
+ 23,
+ 24,
+ 24
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "armorPenetration": 11826,
+ "intelligence": 859,
+ "strength": 2155
+ },
+ "items": [
+ 3,
+ 3,
+ 4,
+ 24,
+ 24,
+ 25
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "armorPenetration": 15202,
+ "intelligence": 1472,
+ "strength": 2768
+ },
+ "items": [
+ 4,
+ 4,
+ 5,
+ 25,
+ 25,
+ 26
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "armorPenetration": 20330,
+ "intelligence": 2404,
+ "strength": 3700
+ },
+ "items": [
+ 4,
+ 5,
+ 5,
+ 25,
+ 26,
+ 26
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "armorPenetration": 26212,
+ "intelligence": 3471,
+ "strength": 4767
+ },
+ "items": [
+ 5,
+ 5,
+ 6,
+ 26,
+ 26,
+ 27
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "armorPenetration": 35157,
+ "intelligence": 5095,
+ "strength": 6391
+ },
+ "items": [
+ 5,
+ 6,
+ 6,
+ 26,
+ 27,
+ 27
+ ]
+ }
+ },
+ "runes": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "artifacts": [
+ 0,
+ 0,
+ 0
+ ],
+ "mainStat": "strength",
+ "battleOrder": 2059,
+ "scale": null,
+ "type": "pet",
+ "asset": "pet06",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "pet_160_6005",
+ "spineEpicArtAsset": {
+ "name": "6005_Albus"
+ },
+ "role": "back",
+ "obtainType": "pet_summon",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "mage"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 7
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "1": 6025,
+ "2": 6026,
+ "4": 6027
+ }
+ },
+ "6006": {
+ "id": 6006,
+ "baseStats": {
+ "intelligence": 25,
+ "magicPenetration": 1500,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 23,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 30,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 30
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 7,
+ 7,
+ 7,
+ 7,
+ 7,
+ 7
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 2,
+ "magicPenetration": 1752,
+ "strength": 320
+ },
+ "items": [
+ 7,
+ 7,
+ 7,
+ 7,
+ 8,
+ 8
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 4,
+ "magicPenetration": 4130,
+ "strength": 754
+ },
+ "items": [
+ 7,
+ 7,
+ 8,
+ 8,
+ 8,
+ 8
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 6,
+ "magicPenetration": 7134,
+ "strength": 1302
+ },
+ "items": [
+ 8,
+ 8,
+ 9,
+ 23,
+ 23,
+ 24
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 401,
+ "magicPenetration": 9303,
+ "strength": 1697
+ },
+ "items": [
+ 8,
+ 9,
+ 9,
+ 23,
+ 24,
+ 24
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 859,
+ "magicPenetration": 11826,
+ "strength": 2155
+ },
+ "items": [
+ 9,
+ 9,
+ 10,
+ 24,
+ 24,
+ 25
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "intelligence": 1472,
+ "magicPenetration": 15202,
+ "strength": 2768
+ },
+ "items": [
+ 10,
+ 10,
+ 11,
+ 25,
+ 25,
+ 26
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "intelligence": 2404,
+ "magicPenetration": 20330,
+ "strength": 3700
+ },
+ "items": [
+ 10,
+ 11,
+ 11,
+ 25,
+ 26,
+ 26
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "intelligence": 3471,
+ "magicPenetration": 26212,
+ "strength": 4767
+ },
+ "items": [
+ 11,
+ 11,
+ 12,
+ 26,
+ 26,
+ 27
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "intelligence": 5095,
+ "magicPenetration": 35157,
+ "strength": 6391
+ },
+ "items": [
+ 11,
+ 12,
+ 12,
+ 26,
+ 27,
+ 27
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2060,
+ "scale": null,
+ "type": "pet",
+ "asset": "pet07",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "pet_160_6006",
+ "role": "back",
+ "obtainType": "pet_summon",
+ "characterType": "cutie",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": [
+ 5,
+ 9
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "1": 6030,
+ "2": 6031,
+ "4": 6032
+ }
+ },
+ "6007": {
+ "id": 6007,
+ "baseStats": {
+ "intelligence": 25,
+ "magicPenetration": 1500,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 23,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 30,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 30
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 7,
+ 7,
+ 7,
+ 7,
+ 7,
+ 7
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "intelligence": 2,
+ "magicPenetration": 1752,
+ "strength": 320
+ },
+ "items": [
+ 7,
+ 7,
+ 7,
+ 7,
+ 8,
+ 8
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "intelligence": 4,
+ "magicPenetration": 4130,
+ "strength": 754
+ },
+ "items": [
+ 7,
+ 7,
+ 8,
+ 8,
+ 8,
+ 8
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "intelligence": 6,
+ "magicPenetration": 7134,
+ "strength": 1302
+ },
+ "items": [
+ 8,
+ 8,
+ 9,
+ 23,
+ 23,
+ 24
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "intelligence": 401,
+ "magicPenetration": 9303,
+ "strength": 1697
+ },
+ "items": [
+ 8,
+ 9,
+ 9,
+ 23,
+ 24,
+ 24
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "intelligence": 859,
+ "magicPenetration": 11826,
+ "strength": 2155
+ },
+ "items": [
+ 9,
+ 9,
+ 10,
+ 24,
+ 24,
+ 25
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "intelligence": 1472,
+ "magicPenetration": 15202,
+ "strength": 2768
+ },
+ "items": [
+ 10,
+ 10,
+ 11,
+ 25,
+ 25,
+ 26
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "intelligence": 2404,
+ "magicPenetration": 20330,
+ "strength": 3700
+ },
+ "items": [
+ 10,
+ 11,
+ 11,
+ 25,
+ 26,
+ 26
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "intelligence": 3471,
+ "magicPenetration": 26212,
+ "strength": 4767
+ },
+ "items": [
+ 11,
+ 11,
+ 12,
+ 26,
+ 26,
+ 27
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "intelligence": 5095,
+ "magicPenetration": 35157,
+ "strength": 6391
+ },
+ "items": [
+ 11,
+ 12,
+ 12,
+ 26,
+ 27,
+ 27
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2061,
+ "scale": null,
+ "type": "pet",
+ "asset": "pet08",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "pet_160_6007",
+ "spineEpicArtAsset": {
+ "name": "6007_biscuit"
+ },
+ "role": "back",
+ "obtainType": "pet_summon",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": [
+ 8
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "1": 6033,
+ "2": 6034,
+ "4": 6035
+ }
+ },
+ "6008": {
+ "id": 6008,
+ "baseStats": {
+ "armorPenetration": 1500,
+ "intelligence": 25,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 23,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 30,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 30
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "armorPenetration": 1752,
+ "intelligence": 2,
+ "strength": 320
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "armorPenetration": 4130,
+ "intelligence": 4,
+ "strength": 754
+ },
+ "items": [
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "armorPenetration": 7134,
+ "intelligence": 6,
+ "strength": 1302
+ },
+ "items": [
+ 2,
+ 2,
+ 3,
+ 18,
+ 18,
+ 19
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "armorPenetration": 9303,
+ "intelligence": 401,
+ "strength": 1697
+ },
+ "items": [
+ 2,
+ 3,
+ 3,
+ 18,
+ 19,
+ 19
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "armorPenetration": 11826,
+ "intelligence": 859,
+ "strength": 2155
+ },
+ "items": [
+ 3,
+ 3,
+ 4,
+ 19,
+ 19,
+ 20
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "armorPenetration": 15202,
+ "intelligence": 1472,
+ "strength": 2768
+ },
+ "items": [
+ 4,
+ 4,
+ 5,
+ 20,
+ 20,
+ 21
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "armorPenetration": 20330,
+ "intelligence": 2404,
+ "strength": 3700
+ },
+ "items": [
+ 4,
+ 5,
+ 5,
+ 20,
+ 21,
+ 21
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "armorPenetration": 26212,
+ "intelligence": 3471,
+ "strength": 4767
+ },
+ "items": [
+ 5,
+ 5,
+ 6,
+ 21,
+ 21,
+ 22
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "armorPenetration": 35157,
+ "intelligence": 5095,
+ "strength": 6391
+ },
+ "items": [
+ 5,
+ 6,
+ 6,
+ 21,
+ 22,
+ 22
+ ]
+ }
+ },
+ "runes": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "artifacts": [
+ 0,
+ 0,
+ 0
+ ],
+ "mainStat": "strength",
+ "battleOrder": 2062,
+ "scale": null,
+ "type": "pet",
+ "asset": "pet09",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "pet_160_6008",
+ "role": "back",
+ "obtainType": "pet_summon",
+ "characterType": "cutie",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": [
+ 5
+ ],
+ "fragmentBuyCost": {
+ "starmoney": 40
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "1": 6036,
+ "2": 6037,
+ "4": 6038
+ }
+ },
+ "6009": {
+ "id": 6009,
+ "baseStats": {
+ "armorPenetration": 1500,
+ "intelligence": 25,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 18,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 18
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 23,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 23
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 0,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 30,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 30
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "armorPenetration": 1752,
+ "intelligence": 2,
+ "strength": 320
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "armorPenetration": 4130,
+ "intelligence": 4,
+ "strength": 754
+ },
+ "items": [
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "armorPenetration": 7134,
+ "intelligence": 6,
+ "strength": 1302
+ },
+ "items": [
+ 2,
+ 2,
+ 3,
+ 18,
+ 18,
+ 19
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "armorPenetration": 9303,
+ "intelligence": 401,
+ "strength": 1697
+ },
+ "items": [
+ 2,
+ 3,
+ 3,
+ 18,
+ 19,
+ 19
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "armorPenetration": 11826,
+ "intelligence": 859,
+ "strength": 2155
+ },
+ "items": [
+ 3,
+ 3,
+ 4,
+ 19,
+ 19,
+ 20
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "armorPenetration": 15202,
+ "intelligence": 1472,
+ "strength": 2768
+ },
+ "items": [
+ 4,
+ 4,
+ 5,
+ 20,
+ 20,
+ 21
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "armorPenetration": 20330,
+ "intelligence": 2404,
+ "strength": 3700
+ },
+ "items": [
+ 4,
+ 5,
+ 5,
+ 20,
+ 21,
+ 21
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "armorPenetration": 26212,
+ "intelligence": 3471,
+ "strength": 4767
+ },
+ "items": [
+ 5,
+ 5,
+ 6,
+ 21,
+ 21,
+ 22
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "armorPenetration": 35157,
+ "intelligence": 5095,
+ "strength": 6391
+ },
+ "items": [
+ 5,
+ 6,
+ 6,
+ 21,
+ 22,
+ 22
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 2063,
+ "scale": null,
+ "type": "pet",
+ "asset": "pet10",
+ "iconAssetAtlas": 2,
+ "iconAssetTexture": "pet_160_6009",
+ "role": "back",
+ "obtainType": "pet_summon",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": [
+ 5
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "1": 6039,
+ "2": 6040,
+ "4": 6041
+ }
+ },
+ "7002": {
+ "id": 7002,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 55,
+ "strength": 20
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 6,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 8,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 16
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 20
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 8,
+ 9,
+ 14,
+ 18
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 2,
+ "magicResist": 25,
+ "physicalAttack": 37,
+ "strength": 9
+ },
+ "items": [
+ 13,
+ 9,
+ 10,
+ 18,
+ 27,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 75,
+ "hp": 1355,
+ "intelligence": 5,
+ "magicResist": 50,
+ "physicalAttack": 70,
+ "strength": 23
+ },
+ "items": [
+ 10,
+ 18,
+ 28,
+ 25,
+ 43,
+ 36
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 125,
+ "hp": 2240,
+ "intelligence": 8,
+ "magicResist": 100,
+ "physicalAttack": 136,
+ "strength": 47
+ },
+ "items": [
+ 21,
+ 42,
+ 37,
+ 44,
+ 57,
+ 59
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 325,
+ "hp": 2740,
+ "intelligence": 10,
+ "magicResist": 150,
+ "physicalAttack": 239,
+ "strength": 69
+ },
+ "items": [
+ 33,
+ 36,
+ 43,
+ 57,
+ 59,
+ 77
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 475,
+ "hp": 3240,
+ "intelligence": 17,
+ "lifesteal": 5,
+ "magicResist": 150,
+ "physicalAttack": 342,
+ "strength": 93
+ },
+ "items": [
+ 42,
+ 43,
+ 57,
+ 65,
+ 77,
+ 85
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 24,
+ "armor": 525,
+ "hp": 4740,
+ "intelligence": 24,
+ "lifesteal": 10,
+ "magicResist": 230,
+ "physicalAttack": 534,
+ "strength": 110
+ },
+ "items": [
+ 74,
+ 64,
+ 77,
+ 90,
+ 92,
+ 99
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 31,
+ "armor": 805,
+ "hp": 5540,
+ "intelligence": 31,
+ "lifesteal": 15,
+ "magicResist": 310,
+ "physicalAttack": 739,
+ "strength": 143
+ },
+ "items": [
+ 87,
+ 77,
+ 90,
+ 99,
+ 91,
+ 123
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 48,
+ "armor": 1085,
+ "hp": 9140,
+ "intelligence": 38,
+ "lifesteal": 20,
+ "magicResist": 310,
+ "physicalAttack": 879,
+ "strength": 196
+ },
+ "items": [
+ 77,
+ 90,
+ 91,
+ 125,
+ 122,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 74,
+ "armor": 1325,
+ "hp": 11140,
+ "intelligence": 64,
+ "lifesteal": 25,
+ "magicResist": 470,
+ "physicalAttack": 1273,
+ "strength": 268
+ },
+ "items": [
+ 85,
+ 125,
+ 122,
+ 124,
+ 131,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 143,
+ "armor": 1485,
+ "hp": 12140,
+ "intelligence": 133,
+ "lifesteal": 35,
+ "magicResist": 630,
+ "physicalAttack": 1597,
+ "strength": 425
+ },
+ "items": [
+ 123,
+ 122,
+ 125,
+ 136,
+ 170,
+ 168
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 183,
+ "armor": 1645,
+ "hp": 13740,
+ "intelligence": 173,
+ "lifesteal": 35,
+ "magicResist": 790,
+ "physicalAttack": 2321,
+ "strength": 652
+ },
+ "items": [
+ 114,
+ 124,
+ 136,
+ 170,
+ 168,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 223,
+ "armor": 1965,
+ "hp": 20140,
+ "intelligence": 213,
+ "lifesteal": 45,
+ "magicResist": 790,
+ "physicalAttack": 2937,
+ "strength": 849
+ },
+ "items": [
+ 122,
+ 123,
+ 170,
+ 175,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 273,
+ "armor": 2725,
+ "hp": 24940,
+ "intelligence": 263,
+ "lifesteal": 45,
+ "magicResist": 790,
+ "physicalAttack": 3685,
+ "strength": 1212
+ },
+ "items": [
+ 131,
+ 122,
+ 176,
+ 183,
+ 201,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 299,
+ "armor": 3205,
+ "hp": 41516,
+ "intelligence": 289,
+ "lifesteal": 45,
+ "magicResist": 1390,
+ "physicalAttack": 6148,
+ "strength": 1268
+ },
+ "items": [
+ 122,
+ 134,
+ 167,
+ 183,
+ 208,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 377,
+ "armor": 3685,
+ "hp": 58972,
+ "intelligence": 367,
+ "lifesteal": 45,
+ "magicResist": 1646,
+ "physicalAttack": 7932,
+ "strength": 1625
+ },
+ "items": [
+ 211,
+ 208,
+ 179,
+ 183,
+ 221,
+ 227
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 455,
+ "armor": 5205,
+ "hp": 73628,
+ "intelligence": 445,
+ "lifesteal": 45,
+ "magicResist": 1646,
+ "physicalAttack": 9903,
+ "strength": 2200
+ },
+ "items": [
+ 185,
+ 208,
+ 183,
+ 221,
+ 227,
+ 240
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 505,
+ "armor": 6725,
+ "hp": 90204,
+ "intelligence": 495,
+ "lifesteal": 45,
+ "magicResist": 1646,
+ "physicalAttack": 13538,
+ "strength": 2642
+ },
+ "items": [
+ 201,
+ 183,
+ 221,
+ 225,
+ 237,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 7,
+ 4,
+ 8,
+ 1
+ ],
+ "artifacts": [
+ 1002,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 17,
+ "scale": 1.43,
+ "type": "hero",
+ "asset": "hero7002_corrupted_galahad",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "0002_redeye",
+ "role": "front",
+ "obtainType": "disabled",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "boss"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 11,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 7001,
+ 7002,
+ 7003,
+ 7004,
+ 7005
+ ]
+ },
+ "7013": {
+ "id": 7013,
+ "baseStats": {
+ "agility": 10,
+ "hp": 500,
+ "intelligence": 20,
+ "physicalAttack": 50,
+ "strength": 15
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 10,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 6
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 12,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 9
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 12,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 16,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 12
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 21,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 7,
+ 2,
+ 6,
+ 7,
+ 8,
+ 9
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 7,
+ "magicPower": 50,
+ "magicResist": 25,
+ "strength": 7
+ },
+ "items": [
+ 8,
+ 22,
+ 11,
+ 19,
+ 24,
+ 26
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 10,
+ "armor": 50,
+ "hp": 700,
+ "intelligence": 31,
+ "magicPower": 150,
+ "magicResist": 25,
+ "strength": 10
+ },
+ "items": [
+ 11,
+ 19,
+ 24,
+ 27,
+ 45,
+ 46
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 13,
+ "armor": 100,
+ "hp": 1700,
+ "intelligence": 45,
+ "magicPower": 300,
+ "magicResist": 75,
+ "strength": 13
+ },
+ "items": [
+ 32,
+ 52,
+ 46,
+ 48,
+ 58,
+ 41
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 150,
+ "hp": 2200,
+ "intelligence": 82,
+ "magicPenetration": 100,
+ "magicPower": 534,
+ "magicResist": 117,
+ "strength": 15
+ },
+ "items": [
+ 41,
+ 45,
+ 46,
+ 56,
+ 60,
+ 71
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 17,
+ "armor": 200,
+ "hp": 3700,
+ "intelligence": 94,
+ "magicPenetration": 180,
+ "magicPower": 714,
+ "magicResist": 267,
+ "strength": 17
+ },
+ "items": [
+ 40,
+ 46,
+ 58,
+ 60,
+ 71,
+ 88
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 19,
+ "armor": 300,
+ "hp": 5000,
+ "intelligence": 106,
+ "magicPenetration": 260,
+ "magicPower": 1074,
+ "magicResist": 367,
+ "strength": 19
+ },
+ "items": [
+ 64,
+ 67,
+ 71,
+ 88,
+ 93,
+ 95
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 21,
+ "armor": 400,
+ "hp": 6600,
+ "intelligence": 146,
+ "magicPenetration": 340,
+ "magicPower": 1514,
+ "magicResist": 527,
+ "strength": 21
+ },
+ "items": [
+ 58,
+ 95,
+ 64,
+ 67,
+ 88,
+ 117
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 23,
+ "armor": 500,
+ "hp": 8200,
+ "intelligence": 216,
+ "magicPenetration": 500,
+ "magicPower": 1934,
+ "magicResist": 687,
+ "strength": 23
+ },
+ "items": [
+ 132,
+ 60,
+ 67,
+ 88,
+ 98,
+ 135
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 49,
+ "armor": 600,
+ "hp": 11560,
+ "intelligence": 272,
+ "magicPenetration": 700,
+ "magicPower": 2606,
+ "magicResist": 867,
+ "strength": 49
+ },
+ "items": [
+ 132,
+ 67,
+ 88,
+ 98,
+ 135,
+ 137
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 113,
+ "armor": 700,
+ "hp": 14920,
+ "intelligence": 414,
+ "magicPenetration": 900,
+ "magicPower": 3278,
+ "magicResist": 947,
+ "strength": 113
+ },
+ "items": [
+ 98,
+ 115,
+ 119,
+ 140,
+ 171,
+ 176
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 115,
+ "armor": 700,
+ "hp": 19016,
+ "intelligence": 555,
+ "magicPenetration": 1100,
+ "magicPower": 4417,
+ "magicResist": 1707,
+ "strength": 115
+ },
+ "items": [
+ 115,
+ 116,
+ 137,
+ 171,
+ 167,
+ 181
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 155,
+ "armor": 700,
+ "hp": 25016,
+ "intelligence": 782,
+ "magicPenetration": 1420,
+ "magicPower": 5217,
+ "magicResist": 2027,
+ "strength": 155
+ },
+ "items": [
+ 126,
+ 116,
+ 169,
+ 181,
+ 184,
+ 186
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 205,
+ "armor": 700,
+ "hp": 31416,
+ "intelligence": 1036,
+ "magicPenetration": 1740,
+ "magicPower": 6777,
+ "magicResist": 2507,
+ "strength": 205
+ },
+ "items": [
+ 117,
+ 140,
+ 169,
+ 181,
+ 180,
+ 209
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 207,
+ "armor": 700,
+ "hp": 41272,
+ "intelligence": 1189,
+ "magicPenetration": 2220,
+ "magicPower": 11332,
+ "magicResist": 2507,
+ "strength": 207
+ },
+ "items": [
+ 137,
+ 140,
+ 169,
+ 181,
+ 203,
+ 212
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 323,
+ "armor": 700,
+ "hp": 45368,
+ "intelligence": 1784,
+ "magicPenetration": 2540,
+ "magicPower": 14191,
+ "magicResist": 2507,
+ "strength": 323
+ },
+ "items": [
+ 209,
+ 174,
+ 212,
+ 180,
+ 226,
+ 228
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 401,
+ "armor": 700,
+ "hp": 51128,
+ "intelligence": 2262,
+ "magicPenetration": 3140,
+ "magicPower": 17887,
+ "magicResist": 3707,
+ "strength": 401
+ },
+ "items": [
+ 183,
+ 180,
+ 212,
+ 228,
+ 232,
+ 234
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 479,
+ "armor": 1020,
+ "hp": 64248,
+ "intelligence": 2619,
+ "magicPenetration": 4340,
+ "magicPower": 22303,
+ "magicResist": 4907,
+ "strength": 479
+ },
+ "items": [
+ 183,
+ 203,
+ 227,
+ 224,
+ 234,
+ 242
+ ]
+ }
+ },
+ "runes": [
+ 6,
+ 4,
+ 11,
+ 8,
+ 2
+ ],
+ "artifacts": [
+ 1013,
+ 2003,
+ 3001
+ ],
+ "mainStat": "intelligence",
+ "battleOrder": 2002,
+ "scale": 1.35,
+ "type": "hero",
+ "asset": "hero7013_corrupted_orion",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "0013_redeye",
+ "role": "back",
+ "obtainType": "disabled",
+ "characterType": "snob",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "boss"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 11,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 7006,
+ 7007,
+ 7008,
+ 7009,
+ 7010
+ ]
+ },
+ "7015": {
+ "id": 7015,
+ "baseStats": {
+ "agility": 20,
+ "hp": 500,
+ "intelligence": 15,
+ "physicalAttack": 60,
+ "strength": 10
+ },
+ "stars": {
+ "2": {
+ "battleStatData": {
+ "agility": 6,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 5
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 8,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 7,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 7
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 9,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 16,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 13
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 22,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 13,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 15
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 2,
+ 3,
+ 8,
+ 14,
+ 20
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 14,
+ "armor": 25,
+ "hp": 200,
+ "intelligence": 2,
+ "physicalAttack": 62,
+ "strength": 2
+ },
+ "items": [
+ 9,
+ 13,
+ 20,
+ 12,
+ 23,
+ 25
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 38,
+ "armor": 25,
+ "hp": 585,
+ "intelligence": 5,
+ "magicResist": 25,
+ "physicalAttack": 120,
+ "strength": 5
+ },
+ "items": [
+ 14,
+ 18,
+ 27,
+ 24,
+ 39,
+ 35
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 62,
+ "armor": 75,
+ "hp": 1470,
+ "intelligence": 12,
+ "magicResist": 75,
+ "physicalAttack": 145,
+ "strength": 19
+ },
+ "items": [
+ 28,
+ 27,
+ 38,
+ 53,
+ 66,
+ 56
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 90,
+ "armor": 125,
+ "armorPenetration": 50,
+ "hp": 2470,
+ "intelligence": 14,
+ "magicResist": 125,
+ "physicalAttack": 267,
+ "strength": 21
+ },
+ "items": [
+ 24,
+ 31,
+ 50,
+ 57,
+ 59,
+ 66
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 123,
+ "armor": 225,
+ "armorPenetration": 100,
+ "hp": 2970,
+ "intelligence": 16,
+ "magicResist": 167,
+ "physicalAttack": 449,
+ "strength": 23
+ },
+ "items": [
+ 38,
+ 43,
+ 60,
+ 56,
+ 70,
+ 87
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 150,
+ "armor": 275,
+ "armorPenetration": 180,
+ "hp": 3970,
+ "intelligence": 23,
+ "magicResist": 267,
+ "physicalAttack": 641,
+ "strength": 30
+ },
+ "items": [
+ 59,
+ 64,
+ 70,
+ 64,
+ 92,
+ 120
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 182,
+ "armor": 375,
+ "armorPenetration": 260,
+ "hp": 5570,
+ "intelligence": 25,
+ "magicResist": 427,
+ "physicalAttack": 940,
+ "strength": 32
+ },
+ "items": [
+ 69,
+ 66,
+ 84,
+ 91,
+ 97,
+ 121
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 250,
+ "armor": 455,
+ "armorPenetration": 460,
+ "hp": 7570,
+ "intelligence": 47,
+ "magicResist": 587,
+ "physicalAttack": 996,
+ "strength": 70
+ },
+ "items": [
+ 70,
+ 65,
+ 133,
+ 99,
+ 91,
+ 134
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 306,
+ "armor": 655,
+ "armorPenetration": 540,
+ "hp": 9570,
+ "intelligence": 73,
+ "magicResist": 923,
+ "physicalAttack": 1453,
+ "strength": 96
+ },
+ "items": [
+ 87,
+ 125,
+ 114,
+ 91,
+ 133,
+ 138
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 463,
+ "armor": 655,
+ "armorPenetration": 540,
+ "hp": 13170,
+ "intelligence": 142,
+ "magicResist": 1083,
+ "physicalAttack": 1955,
+ "strength": 165
+ },
+ "items": [
+ 114,
+ 120,
+ 133,
+ 134,
+ 172,
+ 173
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 658,
+ "armor": 655,
+ "armorPenetration": 1140,
+ "hp": 14770,
+ "intelligence": 168,
+ "magicResist": 1339,
+ "physicalAttack": 2624,
+ "strength": 191
+ },
+ "items": [
+ 121,
+ 120,
+ 122,
+ 168,
+ 172,
+ 187
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 1051,
+ "armor": 815,
+ "armorPenetration": 1140,
+ "hp": 14770,
+ "intelligence": 218,
+ "magicResist": 1499,
+ "physicalAttack": 3240,
+ "strength": 241
+ },
+ "items": [
+ 120,
+ 138,
+ 175,
+ 168,
+ 176,
+ 187
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 1391,
+ "armor": 1415,
+ "armorPenetration": 1140,
+ "hp": 14770,
+ "intelligence": 306,
+ "magicResist": 2099,
+ "physicalAttack": 3748,
+ "strength": 329
+ },
+ "items": [
+ 122,
+ 127,
+ 182,
+ 184,
+ 201,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 1423,
+ "armor": 1575,
+ "armorPenetration": 1460,
+ "hp": 31346,
+ "intelligence": 338,
+ "magicResist": 2419,
+ "physicalAttack": 6531,
+ "strength": 361
+ },
+ "items": [
+ 121,
+ 127,
+ 173,
+ 201,
+ 201,
+ 213
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 1840,
+ "armor": 1575,
+ "armorPenetration": 2060,
+ "hp": 41586,
+ "intelligence": 446,
+ "magicResist": 2579,
+ "physicalAttack": 8579,
+ "strength": 469
+ },
+ "items": [
+ 182,
+ 184,
+ 213,
+ 208,
+ 227,
+ 223
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 2415,
+ "armor": 2775,
+ "armorPenetration": 2380,
+ "hp": 53042,
+ "intelligence": 524,
+ "magicResist": 2899,
+ "physicalAttack": 10230,
+ "strength": 547
+ },
+ "items": [
+ 213,
+ 183,
+ 179,
+ 228,
+ 223,
+ 239
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 2990,
+ "armor": 3095,
+ "armorPenetration": 3340,
+ "hp": 66162,
+ "intelligence": 602,
+ "magicResist": 4099,
+ "physicalAttack": 12534,
+ "strength": 625
+ },
+ "items": [
+ 168,
+ 187,
+ 228,
+ 225,
+ 237,
+ 243
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 4,
+ 8,
+ 12,
+ 3
+ ],
+ "artifacts": [
+ 1015,
+ 2004,
+ 3003
+ ],
+ "mainStat": "agility",
+ "battleOrder": 2006,
+ "scale": 1.33,
+ "type": "hero",
+ "asset": "hero7015_corrupted_ginger",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "0015_redeye",
+ "role": "back",
+ "obtainType": "disabled",
+ "characterType": "snob",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "boss"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 11,
+ 1,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 7011,
+ 7012,
+ 7013,
+ 7014,
+ 7015
+ ]
+ },
+ "7024": {
+ "id": 7024,
+ "baseStats": {
+ "agility": 15,
+ "armor": 1000,
+ "hp": 500,
+ "intelligence": 10,
+ "magicResist": 300,
+ "physicalAttack": 50,
+ "strength": 20
+ },
+ "stars": {
+ "6": {
+ "battleStatData": {
+ "agility": 15,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 11,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 24
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 1,
+ 5,
+ 8,
+ 13,
+ 14,
+ 15
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 25,
+ "hp": 385,
+ "intelligence": 2,
+ "physicalAttack": 37,
+ "strength": 14
+ },
+ "items": [
+ 8,
+ 14,
+ 10,
+ 18,
+ 21,
+ 27
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 100,
+ "hp": 770,
+ "intelligence": 5,
+ "physicalAttack": 62,
+ "strength": 38
+ },
+ "items": [
+ 8,
+ 18,
+ 21,
+ 25,
+ 36,
+ 43
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 7,
+ "armor": 175,
+ "hp": 1655,
+ "intelligence": 7,
+ "physicalAttack": 128,
+ "strength": 67
+ },
+ "items": [
+ 21,
+ 37,
+ 44,
+ 47,
+ 56,
+ 57
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 9,
+ "armor": 317,
+ "hp": 3075,
+ "intelligence": 9,
+ "magicResist": 50,
+ "physicalAttack": 226,
+ "strength": 104
+ },
+ "items": [
+ 37,
+ 44,
+ 47,
+ 56,
+ 57,
+ 77
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 11,
+ "armor": 459,
+ "hp": 4495,
+ "intelligence": 11,
+ "lifesteal": 5,
+ "magicResist": 100,
+ "physicalAttack": 324,
+ "strength": 131
+ },
+ "items": [
+ 33,
+ 47,
+ 56,
+ 59,
+ 77,
+ 90
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 18,
+ "armor": 681,
+ "hp": 5915,
+ "intelligence": 18,
+ "lifesteal": 10,
+ "magicResist": 100,
+ "physicalAttack": 422,
+ "strength": 176
+ },
+ "items": [
+ 57,
+ 65,
+ 85,
+ 77,
+ 94,
+ 99
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 25,
+ "armor": 881,
+ "hp": 6915,
+ "intelligence": 25,
+ "lifesteal": 15,
+ "magicResist": 180,
+ "physicalAttack": 548,
+ "strength": 231
+ },
+ "items": [
+ 90,
+ 92,
+ 94,
+ 91,
+ 99,
+ 124
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 27,
+ "armor": 1161,
+ "hp": 8915,
+ "intelligence": 27,
+ "lifesteal": 25,
+ "magicResist": 180,
+ "physicalAttack": 753,
+ "strength": 287
+ },
+ "items": [
+ 90,
+ 91,
+ 122,
+ 123,
+ 124,
+ 131
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 53,
+ "armor": 1401,
+ "hp": 12515,
+ "intelligence": 53,
+ "lifesteal": 35,
+ "magicResist": 180,
+ "physicalAttack": 931,
+ "strength": 389
+ },
+ "items": [
+ 114,
+ 122,
+ 123,
+ 124,
+ 125,
+ 136
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 93,
+ "armor": 1561,
+ "hp": 15715,
+ "intelligence": 93,
+ "lifesteal": 45,
+ "magicResist": 340,
+ "physicalAttack": 1471,
+ "strength": 507
+ },
+ "items": [
+ 136,
+ 123,
+ 124,
+ 125,
+ 170,
+ 175
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 133,
+ "armor": 2161,
+ "hp": 17315,
+ "intelligence": 133,
+ "lifesteal": 55,
+ "magicResist": 500,
+ "physicalAttack": 1687,
+ "strength": 734
+ },
+ "items": [
+ 114,
+ 125,
+ 136,
+ 168,
+ 170,
+ 183
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 173,
+ "armor": 2481,
+ "hp": 23715,
+ "intelligence": 173,
+ "lifesteal": 55,
+ "magicResist": 660,
+ "physicalAttack": 2519,
+ "strength": 931
+ },
+ "items": [
+ 123,
+ 122,
+ 167,
+ 168,
+ 179,
+ 185
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 223,
+ "armor": 2641,
+ "hp": 34515,
+ "intelligence": 223,
+ "lifesteal": 55,
+ "magicResist": 660,
+ "physicalAttack": 3667,
+ "strength": 1185
+ },
+ "items": [
+ 114,
+ 134,
+ 184,
+ 183,
+ 179,
+ 208
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 225,
+ "armor": 2961,
+ "hp": 55571,
+ "intelligence": 225,
+ "lifesteal": 55,
+ "magicResist": 1236,
+ "physicalAttack": 6199,
+ "strength": 1187
+ },
+ "items": [
+ 136,
+ 139,
+ 168,
+ 168,
+ 201,
+ 211
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 341,
+ "armor": 2961,
+ "hp": 60691,
+ "intelligence": 341,
+ "lifesteal": 55,
+ "magicResist": 1645,
+ "physicalAttack": 8575,
+ "strength": 1630
+ },
+ "items": [
+ 208,
+ 179,
+ 211,
+ 184,
+ 221,
+ 225
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 419,
+ "armor": 2961,
+ "hp": 75347,
+ "intelligence": 419,
+ "lifesteal": 55,
+ "magicResist": 1965,
+ "physicalAttack": 11346,
+ "strength": 2205
+ },
+ "items": [
+ 208,
+ 184,
+ 185,
+ 221,
+ 224,
+ 237
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 469,
+ "armor": 4881,
+ "hp": 103923,
+ "intelligence": 469,
+ "lifesteal": 55,
+ "magicResist": 2285,
+ "physicalAttack": 13701,
+ "strength": 2647
+ },
+ "items": [
+ 185,
+ 184,
+ 221,
+ 225,
+ 237,
+ 241
+ ]
+ }
+ },
+ "runes": [
+ 5,
+ 7,
+ 4,
+ 8,
+ 1
+ ],
+ "artifacts": [
+ 1024,
+ 2006,
+ 3002
+ ],
+ "mainStat": "strength",
+ "battleOrder": 4,
+ "scale": 1.35,
+ "type": "hero",
+ "asset": "hero7024_corrupted_cleaver",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "0024_redeye",
+ "role": "front",
+ "obtainType": "disabled",
+ "characterType": "warrior",
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": [
+ "boss"
+ ],
+ "musicAsset": null,
+ "perk": [
+ 11,
+ 2,
+ 22
+ ],
+ "fragmentBuyCost": {
+ "starmoney": "40"
+ },
+ "fragmentSellCost": {
+ "gold": 4000
+ },
+ "fragmentSpecialCost": 100,
+ "lockedUntil": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": [
+ 7016,
+ 7017,
+ 7018,
+ 7019,
+ 7020
+ ]
+ },
+ "7025": {
+ "id": 7025,
+ "baseStats": {
+ "agility": 15,
+ "hp": 27000,
+ "intelligence": 15,
+ "magicPower": 1350,
+ "physicalAttack": 1200,
+ "strength": 25
+ },
+ "stars": {
+ "1": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 2,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 4
+ }
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 3,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 3,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 3
+ }
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 4,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 4,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 8
+ }
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 5,
+ "armor": 0,
+ "armorPenetration": 0,
+ "dodge": 0,
+ "hp": 0,
+ "intelligence": 5,
+ "lifesteal": 0,
+ "magicPenetration": 0,
+ "magicPower": 0,
+ "magicResist": 0,
+ "physicalAttack": 0,
+ "physicalCritChance": 0,
+ "strength": 10
+ }
+ }
+ },
+ "color": {
+ "1": {
+ "items": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "2": {
+ "battleStatData": {
+ "agility": 2,
+ "hp": 1200,
+ "intelligence": 2,
+ "strength": 2
+ },
+ "items": [
+ 16,
+ 16,
+ 16,
+ 2,
+ 2,
+ 2
+ ]
+ },
+ "3": {
+ "battleStatData": {
+ "agility": 4,
+ "hp": 1800,
+ "intelligence": 25,
+ "strength": 4
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "4": {
+ "battleStatData": {
+ "agility": 6,
+ "hp": 1800,
+ "intelligence": 27,
+ "physicalAttack": 72,
+ "strength": 6
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "5": {
+ "battleStatData": {
+ "agility": 8,
+ "hp": 1800,
+ "intelligence": 29,
+ "physicalAttack": 144,
+ "strength": 8
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "6": {
+ "battleStatData": {
+ "agility": 10,
+ "hp": 1800,
+ "intelligence": 31,
+ "physicalAttack": 216,
+ "strength": 10
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "7": {
+ "battleStatData": {
+ "agility": 12,
+ "hp": 1800,
+ "intelligence": 33,
+ "physicalAttack": 288,
+ "strength": 12
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "8": {
+ "battleStatData": {
+ "agility": 14,
+ "hp": 1800,
+ "intelligence": 35,
+ "physicalAttack": 360,
+ "strength": 14
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "9": {
+ "battleStatData": {
+ "agility": 16,
+ "hp": 1800,
+ "intelligence": 37,
+ "physicalAttack": 432,
+ "strength": 16
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "10": {
+ "battleStatData": {
+ "agility": 18,
+ "hp": 1800,
+ "intelligence": 39,
+ "physicalAttack": 504,
+ "strength": 18
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "11": {
+ "battleStatData": {
+ "agility": 20,
+ "hp": 1800,
+ "intelligence": 41,
+ "physicalAttack": 576,
+ "strength": 20
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "12": {
+ "battleStatData": {
+ "agility": 22,
+ "hp": 1800,
+ "intelligence": 43,
+ "physicalAttack": 648,
+ "strength": 22
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "13": {
+ "battleStatData": {
+ "agility": 24,
+ "hp": 1800,
+ "intelligence": 45,
+ "physicalAttack": 720,
+ "strength": 24
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "14": {
+ "battleStatData": {
+ "agility": 26,
+ "hp": 1800,
+ "intelligence": 47,
+ "physicalAttack": 792,
+ "strength": 26
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "15": {
+ "battleStatData": {
+ "agility": 28,
+ "hp": 1800,
+ "intelligence": 49,
+ "physicalAttack": 864,
+ "strength": 28
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "16": {
+ "battleStatData": {
+ "agility": 30,
+ "hp": 1800,
+ "intelligence": 51,
+ "physicalAttack": 936,
+ "strength": 30
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "17": {
+ "battleStatData": {
+ "agility": 32,
+ "hp": 1800,
+ "intelligence": 53,
+ "physicalAttack": 1008,
+ "strength": 32
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "18": {
+ "battleStatData": {
+ "agility": 34,
+ "hp": 1800,
+ "intelligence": 55,
+ "physicalAttack": 1080,
+ "strength": 34
+ },
+ "items": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "runes": null,
+ "artifacts": null,
+ "mainStat": "strength",
+ "battleOrder": 0,
+ "scale": 1.5,
+ "type": "creep",
+ "asset": "creep_halloween_winter_singer",
+ "iconAssetAtlas": 5,
+ "iconAssetTexture": "5004",
+ "role": null,
+ "obtainType": null,
+ "characterType": null,
+ "silhouette": null,
+ "ultCinematic": null,
+ "roleExtended": null,
+ "musicAsset": null,
+ "perk": null,
+ "fragmentSellCost": null,
+ "fragmentSpecialCost": 0,
+ "lockedUntil": null,
+ "fragmentBuyCost": null,
+ "epicArtAsset": null,
+ "spineEpicArtAsset": null,
+ "sfxAsset": null,
+ "assetsIdent": null,
+ "skill": {
+ "0": 7021,
+ "2": 7022
+ }
+ }
+}
\ No newline at end of file
diff --git a/hwh-autobuyer-settings-2025-09-19.json b/hwh-autobuyer-settings-2025-09-19.json
new file mode 100644
index 0000000..3023b24
--- /dev/null
+++ b/hwh-autobuyer-settings-2025-09-19.json
@@ -0,0 +1,33 @@
+{
+ "advAutoBuyer_1": {
+ "197": true,
+ "199": true
+ },
+ "advAutoBuyer_4": {
+ "93": true,
+ "196": true,
+ "Flaming Heart": true,
+ "Giant-Slayer": true,
+ "Pastor's Seal": true,
+ "Minotaur's Head": true,
+ "Hand of Glory": true,
+ "Desert Blade": true,
+ "La Mort's Card": true,
+ "Alchemist's Set": true,
+ "Lycanthrope's Fang": true,
+ "Harunian Helm": true,
+ "Enigma's Chronicles": true
+ },
+ "advAutoBuyer_5": {
+ "Flaming Heart": true,
+ "Giant-Slayer": true,
+ "Pastor's Seal": true,
+ "Minotaur's Head": true,
+ "Desert Blade": true,
+ "Hand of Glory": true,
+ "Alchemist's Set": true,
+ "Lycanthrope's Fang": true,
+ "La Mort's Card": true,
+ "Harunian Helm": true
+ }
+}
\ No newline at end of file
diff --git a/import-training-json.mjs b/import-training-json.mjs
new file mode 100644
index 0000000..5280acb
--- /dev/null
+++ b/import-training-json.mjs
@@ -0,0 +1,51 @@
+#!/usr/bin/env node
+/**
+ * Import existing arena-training-results/*.json files into PostgreSQL.
+ *
+ * Usage:
+ * npm run db:import-json
+ */
+
+import fs from 'fs/promises';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import { initDatabase, saveTrainingRound, getTrainingSummary, closeDatabase } from './training-db.mjs';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const TRAINING_DIR = path.join(__dirname, 'arena-training-results');
+
+async function main() {
+ await initDatabase();
+
+ let files = [];
+ try {
+ files = (await fs.readdir(TRAINING_DIR)).filter((f) => f.endsWith('.json')).sort();
+ } catch {
+ console.log('No arena-training-results directory found.');
+ return;
+ }
+
+ if (!files.length) {
+ console.log('No JSON training files to import.');
+ return;
+ }
+
+ let imported = 0;
+ for (const file of files) {
+ const raw = await fs.readFile(path.join(TRAINING_DIR, file), 'utf8');
+ const body = JSON.parse(raw);
+ await saveTrainingRound(body);
+ imported++;
+ console.log(`Imported ${file}`);
+ }
+
+ const summary = await getTrainingSummary();
+ console.log(`Done. Imported ${imported} files. Total rounds in DB: ${summary.roundCount}`);
+ await closeDatabase();
+}
+
+main().catch(async (error) => {
+ console.error('Import failed:', error.message);
+ await closeDatabase();
+ process.exit(1);
+});
diff --git a/llm-bridge-server.mjs b/llm-bridge-server.mjs
new file mode 100644
index 0000000..2090ae9
--- /dev/null
+++ b/llm-bridge-server.mjs
@@ -0,0 +1,504 @@
+#!/usr/bin/env node
+/**
+ * Local bridge server for Cursor -> Hero Wars (LLMHWH).
+ *
+ * Usage:
+ * npm install
+ * node llm-bridge-server.mjs
+ *
+ * Environment:
+ * DATABASE_URL=postgresql://user:pass@localhost:5432/autohero
+ *
+ * Cursor / shell:
+ * curl http://127.0.0.1:9876/health
+ * curl -X POST http://127.0.0.1:9876/run -H "Content-Type: application/json" -d "{\"method\":\"getUserInfo\",\"args\":[]}"
+ */
+
+import http from 'http';
+import fs from 'fs';
+import path from 'path';
+import {
+ initDatabase,
+ saveTrainingRound,
+ getTrainingSummary,
+ getMatchups,
+ getTrainingResults,
+ getTrainingResultCount,
+ getTrainingResultStats,
+ getTrainingTesters,
+ getOpponentSkipCheck,
+ getUserCounterSkipCheck,
+ getMetaTeamSnapshots,
+ getMetaTeamSnapshotById,
+ getMetaTeamCountForSnapshot,
+ getMetaTeamsForSnapshot,
+ getMetaTeamCandidates,
+ getDatabaseStatus,
+ closeDatabase,
+} from './training-db.mjs';
+import { formatTrainingResultRow, renderTrainingResultsPage, renderMetaTeamsPage, parseHeroFilterParams } from './training-view.mjs';
+import { ICONS_DIR, LOCAL_ICON_WEB_PATH } from './hero-icons.mjs';
+
+const PORT = 9876;
+const HOST = '127.0.0.1';
+const DEFAULT_TIMEOUT_MS = 120000;
+
+let commandQueue = [];
+let lastBrowserPollAt = 0;
+const waiters = new Map();
+let databaseReady = false;
+
+function sendJson(res, status, body) {
+ res.writeHead(status, {
+ 'Content-Type': 'application/json',
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type',
+ });
+ res.end(JSON.stringify(body));
+}
+
+function readBody(req) {
+ return new Promise((resolve, reject) => {
+ let data = '';
+ req.on('data', chunk => { data += chunk; });
+ req.on('end', () => {
+ if (!data) return resolve({});
+ try {
+ resolve(JSON.parse(data));
+ } catch (e) {
+ reject(new Error('Invalid JSON body'));
+ }
+ });
+ req.on('error', reject);
+ });
+}
+
+function isBrowserConnected() {
+ return Date.now() - lastBrowserPollAt < 5000;
+}
+
+function serveCachedIcon(req, res, url) {
+ if (req.method !== 'GET' || !url.pathname.startsWith(`${LOCAL_ICON_WEB_PATH}/`)) {
+ return false;
+ }
+
+ const filename = decodeURIComponent(url.pathname.slice(`${LOCAL_ICON_WEB_PATH}/`.length));
+ if (!filename || filename.includes('/') || filename.includes('..') || !/^[\w-]+\.png$/i.test(filename)) {
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
+ res.end('Invalid icon path');
+ return true;
+ }
+
+ const filePath = path.join(ICONS_DIR, filename);
+ const resolvedDir = path.resolve(ICONS_DIR);
+ if (!path.resolve(filePath).startsWith(resolvedDir)) {
+ res.writeHead(403, { 'Content-Type': 'text/plain' });
+ res.end('Forbidden');
+ return true;
+ }
+
+ if (!fs.existsSync(filePath)) {
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
+ res.end('Icon not found');
+ return true;
+ }
+
+ res.writeHead(200, {
+ 'Content-Type': 'image/png',
+ 'Cache-Control': 'public, max-age=604800',
+ 'Access-Control-Allow-Origin': '*',
+ });
+ fs.createReadStream(filePath).pipe(res);
+ return true;
+}
+
+const server = http.createServer(async (req, res) => {
+ if (req.method === 'OPTIONS') {
+ res.writeHead(204, {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type',
+ });
+ return res.end();
+ }
+
+ try {
+ const url = new URL(req.url, `http://${HOST}`);
+
+ if (serveCachedIcon(req, res, url)) {
+ return;
+ }
+
+ if (req.method === 'GET' && url.pathname === '/health') {
+ return sendJson(res, 200, {
+ ok: true,
+ browserConnected: isBrowserConnected(),
+ lastBrowserPollAt,
+ queuedCommands: commandQueue.length,
+ port: PORT,
+ database: getDatabaseStatus(),
+ });
+ }
+
+ if (req.method === 'GET' && url.pathname === '/poll') {
+ lastBrowserPollAt = Date.now();
+ if (commandQueue.length > 0) {
+ const command = commandQueue.shift();
+ return sendJson(res, 200, command);
+ }
+ res.writeHead(204, {
+ 'Access-Control-Allow-Origin': '*',
+ });
+ return res.end();
+ }
+
+ if (req.method === 'GET' && (url.pathname === '/' || url.pathname === '/training/view')) {
+ if (!databaseReady) {
+ res.writeHead(503, { 'Content-Type': 'text/plain' });
+ return res.end('Database not ready');
+ }
+ const comboKey = url.searchParams.get('comboKey') || undefined;
+ const opponentHeroIds = parseHeroFilterParams(url.searchParams, 'opponentHero');
+ const myHeroIds = parseHeroFilterParams(url.searchParams, 'myHero');
+ const testerUserId = url.searchParams.get('tester')
+ || url.searchParams.get('testerUserId')
+ || undefined;
+ const offset = Math.max(0, Number(url.searchParams.get('offset')) || 0);
+ const limitParam = url.searchParams.get('limit');
+ const pageSize = limitParam == null ? 500 : Math.max(0, Number(limitParam) || 0);
+ const filterArgs = { comboKey, opponentHeroIds, myHeroIds, testerUserId };
+ const [rows, summary, total, stats, testers] = await Promise.all([
+ getTrainingResults({
+ ...filterArgs,
+ offset,
+ limit: pageSize > 0 ? pageSize : undefined,
+ }),
+ getTrainingSummary(),
+ getTrainingResultCount(filterArgs),
+ getTrainingResultStats(filterArgs),
+ getTrainingTesters(),
+ ]);
+ const results = rows.map(formatTrainingResultRow);
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
+ return res.end(renderTrainingResultsPage(results, summary, {
+ total,
+ offset,
+ pageSize: pageSize > 0 ? pageSize : total,
+ comboKey,
+ opponentHeroIds,
+ myHeroIds,
+ testerUserId,
+ testers,
+ stats,
+ }));
+ }
+
+ if (req.method === 'GET' && url.pathname === '/training/results') {
+ if (!databaseReady) {
+ return sendJson(res, 503, {
+ ok: false,
+ error: 'Database not ready. Check DATABASE_URL and PostgreSQL.',
+ database: getDatabaseStatus(),
+ });
+ }
+ const comboKey = url.searchParams.get('comboKey') || undefined;
+ const opponentHeroIds = parseHeroFilterParams(url.searchParams, 'opponentHero');
+ const myHeroIds = parseHeroFilterParams(url.searchParams, 'myHero');
+ const testerUserId = url.searchParams.get('tester')
+ || url.searchParams.get('testerUserId')
+ || undefined;
+ const offset = Math.max(0, Number(url.searchParams.get('offset')) || 0);
+ const limitParam = url.searchParams.get('limit');
+ const limit = limitParam == null ? 100 : Math.max(0, Number(limitParam) || 0);
+ const filterArgs = { comboKey, opponentHeroIds, myHeroIds, testerUserId };
+ const [rows, total] = await Promise.all([
+ getTrainingResults({
+ ...filterArgs,
+ offset,
+ limit: limit > 0 ? limit : undefined,
+ }),
+ getTrainingResultCount(filterArgs),
+ ]);
+ const results = rows.map(formatTrainingResultRow);
+ return sendJson(res, 200, {
+ ok: true,
+ count: results.length,
+ total,
+ offset,
+ limit: limit > 0 ? limit : total,
+ filters: {
+ opponentHeroIds,
+ myHeroIds,
+ testerUserId,
+ },
+ results,
+ });
+ }
+
+ if (req.method === 'GET' && url.pathname === '/training/skip-check') {
+ if (!databaseReady) {
+ return sendJson(res, 503, {
+ ok: false,
+ error: 'Database not ready. Check DATABASE_URL and PostgreSQL.',
+ database: getDatabaseStatus(),
+ });
+ }
+ const comboKey = url.searchParams.get('comboKey');
+ if (!comboKey) {
+ return sendJson(res, 400, { ok: false, error: 'comboKey query param is required' });
+ }
+ const maxAgeDays = Number(url.searchParams.get('maxAgeDays')) || 30;
+ const mode = url.searchParams.get('mode') || 'max';
+
+ if (mode === 'user') {
+ const testerUserId = url.searchParams.get('testerUserId');
+ const myHeroIds = parseHeroFilterParams(url.searchParams, 'myHero');
+ const myPet = Number(url.searchParams.get('myPet')) || undefined;
+ const skip = await getUserCounterSkipCheck({
+ comboKey,
+ testerUserId,
+ myHeroIds,
+ myPet,
+ maxAgeDays,
+ });
+ return sendJson(res, 200, { ok: true, ...skip });
+ }
+
+ const minWinRate = Number(url.searchParams.get('minWinRate')) || 90;
+ const opponentHeroIds = parseHeroFilterParams(url.searchParams, 'opponentHero');
+ const skip = await getOpponentSkipCheck({
+ comboKey,
+ opponentHeroIds,
+ minWinRate,
+ maxAgeDays,
+ });
+ return sendJson(res, 200, { ok: true, ...skip });
+ }
+
+ if (req.method === 'GET' && url.pathname === '/training/meta-view') {
+ if (!databaseReady) {
+ res.writeHead(503, { 'Content-Type': 'text/plain' });
+ return res.end('Database not ready');
+ }
+
+ const snapshots = await getMetaTeamSnapshots({ limit: 50 });
+ if (!snapshots.length) {
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
+ return res.end(renderMetaTeamsPage([], null, [], { total: 0 }));
+ }
+
+ const requestedId = Number(url.searchParams.get('snapshotId'));
+ const snapshot = requestedId
+ ? await getMetaTeamSnapshotById(requestedId)
+ : snapshots[0];
+ if (!snapshot) {
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
+ return res.end('Snapshot not found');
+ }
+
+ const offset = Math.max(0, Number(url.searchParams.get('offset')) || 0);
+ const limitParam = url.searchParams.get('limit');
+ const pageSize = limitParam == null ? 500 : Math.max(0, Number(limitParam) || 0);
+ const [teams, total] = await Promise.all([
+ getMetaTeamsForSnapshot(snapshot.id, {
+ offset,
+ limit: pageSize > 0 ? pageSize : undefined,
+ }),
+ getMetaTeamCountForSnapshot(snapshot.id),
+ ]);
+
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
+ return res.end(renderMetaTeamsPage(teams, snapshot, snapshots, {
+ total,
+ offset,
+ pageSize: pageSize > 0 ? pageSize : total,
+ }));
+ }
+
+ if (req.method === 'GET' && url.pathname === '/training/meta-candidates') {
+ if (!databaseReady) {
+ return sendJson(res, 503, {
+ ok: false,
+ error: 'Database not ready. Check DATABASE_URL and PostgreSQL.',
+ database: getDatabaseStatus(),
+ });
+ }
+ const snapshotId = Number(url.searchParams.get('snapshotId')) || undefined;
+ const limitParam = url.searchParams.get('limit');
+ const limit = limitParam == null ? undefined : Math.max(0, Number(limitParam) || 0);
+ const result = await getMetaTeamCandidates({
+ snapshotId,
+ limit: limit > 0 ? limit : undefined,
+ });
+ return sendJson(res, 200, {
+ ok: true,
+ snapshotId: result.snapshotId,
+ count: result.candidates.length,
+ candidates: result.candidates,
+ });
+ }
+
+ if (req.method === 'GET' && url.pathname === '/training/meta-snapshots') {
+ if (!databaseReady) {
+ return sendJson(res, 503, {
+ ok: false,
+ error: 'Database not ready. Check DATABASE_URL and PostgreSQL.',
+ database: getDatabaseStatus(),
+ });
+ }
+ const limit = Number(url.searchParams.get('limit')) || 20;
+ const snapshots = await getMetaTeamSnapshots({ limit });
+ return sendJson(res, 200, { ok: true, count: snapshots.length, snapshots });
+ }
+
+ if (req.method === 'GET' && url.pathname === '/training/meta-teams') {
+ if (!databaseReady) {
+ return sendJson(res, 503, {
+ ok: false,
+ error: 'Database not ready. Check DATABASE_URL and PostgreSQL.',
+ database: getDatabaseStatus(),
+ });
+ }
+ const snapshotId = Number(url.searchParams.get('snapshotId'));
+ if (!snapshotId) {
+ return sendJson(res, 400, { ok: false, error: 'snapshotId query param is required' });
+ }
+ const limit = Number(url.searchParams.get('limit')) || 500;
+ const offset = Math.max(0, Number(url.searchParams.get('offset')) || 0);
+ const teams = await getMetaTeamsForSnapshot(snapshotId, { limit, offset });
+ return sendJson(res, 200, { ok: true, snapshotId, count: teams.length, teams });
+ }
+
+ if (req.method === 'GET' && url.pathname === '/training/matchups') {
+ if (!databaseReady) {
+ return sendJson(res, 503, {
+ ok: false,
+ error: 'Database not ready. Check DATABASE_URL and PostgreSQL.',
+ database: getDatabaseStatus(),
+ });
+ }
+ const comboKey = url.searchParams.get('comboKey') || undefined;
+ const limit = Number(url.searchParams.get('limit')) || 50;
+ const matchups = await getMatchups({ comboKey, limit });
+ return sendJson(res, 200, { ok: true, matchups });
+ }
+
+ if (req.method === 'GET' && url.pathname === '/training/summary') {
+ if (!databaseReady) {
+ return sendJson(res, 503, {
+ ok: false,
+ error: 'Database not ready. Check DATABASE_URL and PostgreSQL.',
+ database: getDatabaseStatus(),
+ });
+ }
+ const summary = await getTrainingSummary();
+ return sendJson(res, 200, { ok: true, ...summary });
+ }
+
+ if (req.method === 'POST' && url.pathname === '/training/save') {
+ if (!databaseReady) {
+ return sendJson(res, 503, {
+ ok: false,
+ error: 'Database not ready. Check DATABASE_URL and PostgreSQL.',
+ database: getDatabaseStatus(),
+ });
+ }
+ const body = await readBody(req);
+ const saved = await saveTrainingRound(body);
+ return sendJson(res, 200, { ok: true, ...saved });
+ }
+
+ if (req.method === 'POST' && url.pathname === '/result') {
+ const body = await readBody(req);
+ const waiter = waiters.get(body.id);
+ if (waiter) {
+ clearTimeout(waiter.timer);
+ waiters.delete(body.id);
+ waiter.resolve(body);
+ }
+ return sendJson(res, 200, { ok: true });
+ }
+
+ if (req.method === 'POST' && url.pathname === '/run') {
+ const body = await readBody(req);
+ const method = body.method;
+ const args = Array.isArray(body.args) ? body.args : [];
+ const timeoutMs = Number(body.timeoutMs) > 0 ? Number(body.timeoutMs) : DEFAULT_TIMEOUT_MS;
+
+ if (!method || typeof method !== 'string') {
+ return sendJson(res, 400, { ok: false, error: 'method is required' });
+ }
+
+ if (!isBrowserConnected()) {
+ return sendJson(res, 503, {
+ ok: false,
+ error: 'Browser bridge not connected. Open Hero Wars with LLM Controller loaded.',
+ });
+ }
+
+ const id = `cmd_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
+ const command = { id, method, args };
+
+ const resultPromise = new Promise((resolve, reject) => {
+ const timer = setTimeout(() => {
+ waiters.delete(id);
+ commandQueue = commandQueue.filter(c => c.id !== id);
+ reject(new Error(`Command timed out after ${timeoutMs}ms`));
+ }, timeoutMs);
+ waiters.set(id, { resolve, reject, timer });
+ });
+
+ commandQueue.push(command);
+
+ try {
+ const result = await resultPromise;
+ if (!result.ok) {
+ return sendJson(res, 500, { ok: false, error: result.error || 'Command failed', id });
+ }
+ return sendJson(res, 200, { ok: true, id, result: result.result });
+ } catch (e) {
+ return sendJson(res, 504, { ok: false, error: e.message, id });
+ }
+ }
+
+ sendJson(res, 404, { ok: false, error: 'Not found' });
+ } catch (e) {
+ sendJson(res, 500, { ok: false, error: e.message });
+ }
+});
+
+async function startServer() {
+ try {
+ await initDatabase();
+ databaseReady = true;
+ console.log('PostgreSQL connected:', getDatabaseStatus().url);
+ } catch (error) {
+ databaseReady = false;
+ console.error('PostgreSQL init failed:', error.message);
+ console.error('Set DATABASE_URL or start PostgreSQL, then restart the bridge.');
+ }
+
+ server.on('error', (error) => {
+ console.error('Bridge server error:', error.message);
+ process.exit(1);
+ });
+
+ server.listen(PORT, HOST, () => {
+ console.log(`LLM bridge listening on http://${HOST}:${PORT}`);
+ console.log('Arena training: GET /training/view, /training/meta-view (HTML), /training/results (JSON)');
+ console.log('Waiting for Hero Wars tab (LLM Controller) to poll /poll ...');
+ });
+}
+
+startServer();
+
+process.on('SIGINT', async () => {
+ await closeDatabase();
+ process.exit(0);
+});
+
+process.on('SIGTERM', async () => {
+ await closeDatabase();
+ process.exit(0);
+});
diff --git a/loop-arena-training.ps1 b/loop-arena-training.ps1
new file mode 100644
index 0000000..7dadef5
--- /dev/null
+++ b/loop-arena-training.ps1
@@ -0,0 +1,75 @@
+param(
+ [string]$Label = 'auto-loop',
+ [int]$TopLimit = 0,
+ [int]$HeroPoolSize = 12,
+ [int]$MaxCombinations = 20,
+ [int]$SimulationsPerCombo = 10,
+ [int]$DelaySeconds = 2,
+ [int]$MaxRounds = 0,
+ [int]$StatusIntervalSeconds = 30
+)
+
+function Invoke-Bridge {
+ param(
+ [string]$Method,
+ [array]$Args = @(),
+ [int]$Timeout = 15000
+ )
+ $body = @{ method = $Method; args = $Args; timeoutMs = $Timeout } | ConvertTo-Json -Compress -Depth 12
+ Invoke-RestMethod -Method POST -Uri http://127.0.0.1:9876/run -ContentType 'application/json' -Body $body
+}
+
+$health = Invoke-RestMethod http://127.0.0.1:9876/health
+if (-not $health.browserConnected) {
+ throw 'Bridge not connected. Open Hero Wars with LLM Controller + Arena Training loaded.'
+}
+
+$options = @{
+ label = $Label
+ opponentSource = 'topGet'
+ topLimit = if ($TopLimit -gt 0) { $TopLimit } else { $HeroPoolSize }
+ heroPoolSize = $HeroPoolSize
+ maxCombinations = $MaxCombinations
+ simulationsPerCombo = $SimulationsPerCombo
+ delayBetweenRoundsMs = $DelaySeconds * 1000
+ saveToBridge = $true
+ repeatCycle = $true
+ maxRounds = $MaxRounds
+}
+
+Write-Host 'Starting arena loop training (topGet arena list -> PostgreSQL via bridge)...'
+$start = Invoke-Bridge -Method 'arenaTrainingStartLoop' -Args @($options)
+$start | ConvertTo-Json -Depth 6
+
+Write-Host ''
+Write-Host 'Loop is running in the browser. Press Ctrl+C to stop.'
+Write-Host ''
+
+try {
+ while ($true) {
+ Start-Sleep -Seconds $StatusIntervalSeconds
+ $status = Invoke-Bridge -Method 'arenaTrainingGetStatus'
+ $summary = Invoke-RestMethod http://127.0.0.1:9876/training/summary
+ $savedRounds = if ($summary.roundCount -ne $null) { $summary.roundCount } else { $summary.roundFiles }
+ $latestSession = if ($summary.latestSessionId) { $summary.latestSessionId } else { $summary.latestFile }
+ Write-Host ("[{0}] loop={1} round={2} savedRounds={3} latest={4}" -f (
+ (Get-Date).ToString('HH:mm:ss'),
+ $status.loopRunning,
+ $status.roundCount,
+ $savedRounds,
+ $latestSession
+ ))
+ if (-not $status.loopRunning -and $status.roundCount -gt 0) {
+ Write-Host 'Loop finished.'
+ break
+ }
+ }
+}
+finally {
+ Write-Host 'Stopping loop...'
+ Invoke-Bridge -Method 'arenaTrainingStopLoop' | Out-Null
+ $summary = Invoke-RestMethod http://127.0.0.1:9876/training/summary
+ $savedRounds = if ($summary.roundCount -ne $null) { $summary.roundCount } else { $summary.roundFiles }
+ $storage = if ($summary.storage) { $summary.storage } else { 'postgresql' }
+ Write-Host "Saved $savedRounds training rounds in $storage ($($summary.databaseUrl))"
+}
diff --git a/minions.side b/minions.side
deleted file mode 100644
index 2a11d8b..0000000
--- a/minions.side
+++ /dev/null
@@ -1,86 +0,0 @@
-{
- "id": "5ffa0bb1-6d70-4683-a69a-56e55c5544ed",
- "version": "2.0",
- "name": "minions",
- "url": "https://www.hero-wars.com",
- "tests": [{
- "id": "abdc2af8-76c6-4a3d-9525-207312c54205",
- "name": "Minions",
- "commands": [{
- "id": "58740a64-0af8-452f-9ef2-c245abf4be48",
- "comment": "",
- "command": "open",
- "target": "/",
- "targets": [],
- "value": ""
- }, {
- "id": "95adb173-c55a-49c5-a714-a06fd854b873",
- "comment": "",
- "command": "setWindowSize",
- "target": "2576x1408",
- "targets": [],
- "value": ""
- }, {
- "id": "646ee724-e6eb-49cd-93dc-3c2871f6f933",
- "comment": "",
- "command": "pause",
- "target": "15000",
- "targets": [],
- "value": ""
- }, {
- "id": "5e7f42a6-1795-44cf-9fbb-ea72026815f3",
- "comment": "",
- "command": "click",
- "target": "css=.scriptMenu_button:nth-child(7) > .scriptMenu_buttonText",
- "targets": [
- ["css=.scriptMenu_button:nth-child(7) > .scriptMenu_buttonText", "css:finder"],
- ["xpath=//div[8]/div[2]/div[4]/div", "xpath:position"]
- ],
- "value": ""
- }, {
- "id": "36b6bbd9-b865-478b-bd6b-0b49df7f4cda",
- "comment": "",
- "command": "click",
- "target": "css=.PopUp_buttons:nth-child(4) .PopUp_text",
- "targets": [
- ["css=.PopUp_buttons:nth-child(4) .PopUp_text", "css:finder"],
- ["xpath=//div[3]/div[4]/div/div", "xpath:position"]
- ],
- "value": ""
- }, {
- "id": "de64422b-6b36-4582-9d0e-ef64b37332b1",
- "comment": "",
- "command": "click",
- "target": "css=.PopUp_buttons:nth-child(1) .PopUp_text",
- "targets": [
- ["css=.PopUp_buttons:nth-child(1) .PopUp_text", "css:finder"],
- ["xpath=//div[3]/div/div/div", "xpath:position"]
- ],
- "value": ""
- }, {
- "id": "401c24e2-3a7d-4885-8ad8-3e6d4ced3f33",
- "comment": "",
- "command": "pause",
- "target": "120000",
- "targets": [],
- "value": ""
- }, {
- "id": "878647b7-8cc2-47af-a602-68792a2296bd",
- "comment": "",
- "command": "close",
- "target": "",
- "targets": [],
- "value": ""
- }]
- }],
- "suites": [{
- "id": "54b721d1-a8ea-44e2-86e2-55d2313dbd97",
- "name": "Default Suite",
- "persistSession": false,
- "parallel": false,
- "timeout": 150000,
- "tests": ["abdc2af8-76c6-4a3d-9525-207312c54205"]
- }],
- "urls": ["https://www.hero-wars.com/"],
- "plugins": []
-}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..e56826e
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,161 @@
+{
+ "name": "autohero-bridge",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "autohero-bridge",
+ "version": "1.0.0",
+ "dependencies": {
+ "pg": "^8.16.3"
+ }
+ },
+ "node_modules/pg": {
+ "version": "8.23.0",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.0.tgz",
+ "integrity": "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-connection-string": "^2.14.0",
+ "pg-pool": "^3.14.0",
+ "pg-protocol": "^1.16.0",
+ "pg-types": "2.2.0",
+ "pgpass": "1.0.5"
+ },
+ "engines": {
+ "node": ">= 16.0.0"
+ },
+ "optionalDependencies": {
+ "pg-cloudflare": "^1.4.0"
+ },
+ "peerDependencies": {
+ "pg-native": ">=3.0.1"
+ },
+ "peerDependenciesMeta": {
+ "pg-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pg-cloudflare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz",
+ "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/pg-connection-string": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz",
+ "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==",
+ "license": "MIT"
+ },
+ "node_modules/pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/pg-pool": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz",
+ "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "pg": ">=8.0"
+ }
+ },
+ "node_modules/pg-protocol": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.0.tgz",
+ "integrity": "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==",
+ "license": "MIT"
+ },
+ "node_modules/pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pgpass": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+ "license": "MIT",
+ "dependencies": {
+ "split2": "^4.1.0"
+ }
+ },
+ "node_modules/postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postgres-bytea": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
+ "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..d17f043
--- /dev/null
+++ b/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "autohero-bridge",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module",
+ "description": "Local LLM bridge and arena training storage for AutoHero",
+ "scripts": {
+ "bridge": "node llm-bridge-server.mjs",
+ "bridge:dev": "node --watch llm-bridge-server.mjs",
+ "icons:cache": "node cache-hero-icons.mjs",
+ "db:init": "node -e \"import('./training-db.mjs').then(m => m.initDatabase().then(() => process.exit(0)).catch(e => { console.error(e); process.exit(1); }))\"",
+ "db:import-json": "node import-training-json.mjs",
+ "db:backfill-matchups": "node db-backfill-matchups.mjs",
+ "db:scrape-meta-teams": "python scrape_meta_teams_to_db.py"
+ },
+ "dependencies": {
+ "pg": "^8.16.3"
+ }
+}
diff --git a/poll-api-recording.ps1 b/poll-api-recording.ps1
new file mode 100644
index 0000000..8372db3
--- /dev/null
+++ b/poll-api-recording.ps1
@@ -0,0 +1,59 @@
+param(
+ [string]$Label = "manual-ui",
+ [string]$OutputDir = "api-captures",
+ [int]$PollSeconds = 2,
+ [int]$DurationMinutes = 0
+)
+
+function Invoke-Bridge {
+ param(
+ [string]$Method,
+ [array]$Args = @(),
+ [int]$TimeoutMs = 30000
+ )
+ $body = @{ method = $Method; args = $Args; timeoutMs = $TimeoutMs } | ConvertTo-Json -Compress -Depth 12
+ Invoke-RestMethod -Method POST -Uri http://127.0.0.1:9876/run -ContentType 'application/json' -Body $body
+}
+
+$health = Invoke-RestMethod http://127.0.0.1:9876/health
+if (-not $health.browserConnected) {
+ throw 'Bridge browser not connected. Open Hero Wars with LLM Controller v1.3+ loaded.'
+}
+
+New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
+$sessionStamp = Get-Date -Format 'yyyyMMdd-HHmmss'
+$sessionDir = Join-Path $OutputDir $sessionStamp
+New-Item -ItemType Directory -Force -Path $sessionDir | Out-Null
+
+Write-Host "Starting API recording: $Label"
+$start = Invoke-Bridge -Method 'startApiRecording' -Args @(@{ label = $Label })
+$start | ConvertTo-Json -Depth 8 | Set-Content -Encoding utf8 (Join-Path $sessionDir 'session-start.json')
+
+$lastId = 0
+$deadline = if ($DurationMinutes -gt 0) { (Get-Date).AddMinutes($DurationMinutes) } else { $null }
+
+try {
+ while ($true) {
+ if ($deadline -and (Get-Date) -gt $deadline) {
+ Write-Host 'Duration reached, stopping recording.'
+ break
+ }
+
+ $chunk = Invoke-Bridge -Method 'getApiRecording' -Args @(@{ sinceId = $lastId })
+ if ($chunk.entries.Count -gt 0) {
+ $chunkFile = Join-Path $sessionDir ("chunk-{0}.json" -f (Get-Date -Format 'HHmmss'))
+ $chunk | ConvertTo-Json -Depth 20 | Set-Content -Encoding utf8 $chunkFile
+ $lastId = $chunk.lastEntryId
+ Write-Host ("Captured {0} new API calls (total id {1})" -f $chunk.entries.Count, $lastId)
+ }
+
+ Start-Sleep -Seconds $PollSeconds
+ }
+}
+finally {
+ $final = Invoke-Bridge -Method 'stopApiRecording'
+ $export = Invoke-Bridge -Method 'exportApiRecording'
+ $final | ConvertTo-Json -Depth 8 | Set-Content -Encoding utf8 (Join-Path $sessionDir 'session-stop.json')
+ $export | ConvertTo-Json -Depth 20 | Set-Content -Encoding utf8 (Join-Path $sessionDir 'recording-full.json')
+ Write-Host "Saved recording to $sessionDir"
+}
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..8d71786
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,7 @@
+selenium>=4.0.0
+webdriver-manager>=4.0.0
+python-dotenv>=1.0.0
+requests>=2.31.0
+beautifulsoup4>=4.12.0
+psycopg2-binary>=2.9.9
+
diff --git a/run-arena-training.ps1 b/run-arena-training.ps1
new file mode 100644
index 0000000..7250421
--- /dev/null
+++ b/run-arena-training.ps1
@@ -0,0 +1,64 @@
+# Single-round training. For continuous loop + auto-save, use loop-arena-training.ps1 instead.
+param(
+ [int]$OpponentIndex = 0,
+ [string]$OpponentUserId = '',
+ [int]$HeroPoolSize = 12,
+ [int]$MaxCombinations = 40,
+ [int]$SimulationsPerCombo = 10,
+ [string]$Label = 'single-round',
+ [string]$OutputDir = 'arena-training-results',
+ [switch]$SkipLocalCopy,
+ [int]$TimeoutMs = 1800000
+)
+
+function Invoke-Bridge {
+ param(
+ [string]$Method,
+ [array]$Args = @(),
+ [int]$Timeout = 30000
+ )
+ $body = @{ method = $Method; args = $Args; timeoutMs = $Timeout } | ConvertTo-Json -Compress -Depth 12
+ Invoke-RestMethod -Method POST -Uri http://127.0.0.1:9876/run -ContentType 'application/json' -Body $body
+}
+
+$health = Invoke-RestMethod http://127.0.0.1:9876/health
+if (-not $health.browserConnected) {
+ throw 'Bridge browser not connected. Open Hero Wars with LLM Controller + Arena Training loaded.'
+}
+
+$outFile = $null
+if (-not $SkipLocalCopy) {
+ New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
+ $stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
+ $outFile = Join-Path $OutputDir "arena-training-$stamp.json"
+}
+
+$options = @{
+ label = $Label
+ opponentSource = 'topGet'
+ opponentIndex = $OpponentIndex
+ heroPoolSize = $HeroPoolSize
+ maxCombinations = $MaxCombinations
+ simulationsPerCombo = $SimulationsPerCombo
+ includeCurrentTeam = $true
+ saveToBridge = $true
+}
+if ($OpponentUserId) {
+ $options.opponentUserId = $OpponentUserId
+ $options.Remove('opponentIndex')
+}
+
+Write-Host "Starting single arena training round vs opponent index $OpponentIndex..."
+$result = Invoke-Bridge -Method 'arenaTrainingRun' -Args @($options) -Timeout $TimeoutMs
+if ($outFile) {
+ $result | ConvertTo-Json -Depth 20 | Set-Content -Encoding utf8 $outFile
+}
+
+if ($result.best) {
+ Write-Host "Best combo: $($result.best.heroNames -join ', ') + pet $($result.best.pet)"
+ Write-Host "Win rate: $([math]::Round($result.best.winRate, 1))%"
+}
+Write-Host 'Also saved via bridge to PostgreSQL (training_rounds).'
+if ($outFile) {
+ Write-Host "Local copy: $outFile"
+}
diff --git a/scrape_arena_teams.py b/scrape_arena_teams.py
new file mode 100644
index 0000000..821972f
--- /dev/null
+++ b/scrape_arena_teams.py
@@ -0,0 +1,234 @@
+"""
+Scrape Hero Wars Arena teams from hw-recruit.com and count hero occurrences
+"""
+import requests
+from bs4 import BeautifulSoup
+from collections import Counter
+import time
+import os
+import smtplib
+from pathlib import Path
+from datetime import datetime
+from email.mime.text import MIMEText
+from email.mime.multipart import MIMEMultipart
+from dotenv import load_dotenv
+
+# Load environment variables from .env file
+load_dotenv()
+
+
+def get_page_content(url, retries=3):
+ """Fetch page content with retry logic"""
+ headers = {
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
+ }
+
+ for attempt in range(retries):
+ try:
+ response = requests.get(url, headers=headers, timeout=30)
+ response.raise_for_status()
+ return response.text
+ except requests.exceptions.RequestException as e:
+ if attempt < retries - 1:
+ print(f"[WARNING] Failed to fetch {url}, retrying... ({attempt + 1}/{retries})")
+ time.sleep(2)
+ else:
+ print(f"[ERROR] Failed to fetch {url} after {retries} attempts: {e}")
+ return None
+ return None
+
+
+def extract_heroes_from_page(html_content):
+ """Extract hero names from team cells"""
+ soup = BeautifulSoup(html_content, 'html.parser')
+ heroes = []
+
+ # Find all team cells
+ team_cells = soup.find_all('td', class_='views-field views-field-team')
+
+ for cell in team_cells:
+ # Find all img tags within the cell
+ images = cell.find_all('img')
+ for img in images:
+ src = img.get('src', '')
+ if src and '/modules/hwrecruit/images/' in src:
+ # Extract hero name from path like /modules/hwrecruit/images/Sebastian.png
+ hero_name = src.split('/')[-1] # Get filename
+ if hero_name.endswith('.png'):
+ # Remove .png extension
+ hero_name_clean = hero_name[:-4]
+ heroes.append(hero_name_clean)
+
+ return heroes
+
+
+def scrape_all_pages(base_url, max_page=128):
+ """Scrape all pages from first page to max_page"""
+ all_heroes = []
+ total_teams = 0
+
+ # First page (no page parameter)
+ print(f"[INFO] Scraping page 1 (first page, no page parameter)...")
+ url = base_url
+ html = get_page_content(url)
+ if html:
+ heroes = extract_heroes_from_page(html)
+ all_heroes.extend(heroes)
+ teams_on_page = len(BeautifulSoup(html, 'html.parser').find_all('td', class_='views-field views-field-team'))
+ total_teams += teams_on_page
+ print(f" Found {teams_on_page} teams, {len(heroes)} heroes")
+ else:
+ print(f" [ERROR] Failed to fetch page 1")
+
+ time.sleep(1) # Be polite with requests
+
+ # Pages 1 to max_page (note: page=1 is the second page)
+ for page in range(1, max_page + 1):
+ print(f"[INFO] Scraping page {page + 1} (page={page})...")
+ url = f"{base_url}&page={page}"
+ html = get_page_content(url)
+ if html:
+ heroes = extract_heroes_from_page(html)
+ all_heroes.extend(heroes)
+ teams_on_page = len(BeautifulSoup(html, 'html.parser').find_all('td', class_='views-field views-field-team'))
+ total_teams += teams_on_page
+ print(f" Found {teams_on_page} teams, {len(heroes)} heroes")
+ else:
+ print(f" [ERROR] Failed to fetch page {page + 1}")
+
+ time.sleep(1) # Be polite with requests
+
+ return all_heroes, total_teams
+
+
+def send_email(email_body, to_email="mailming@gmail.com"):
+ """Send email with arena hero counts to recipient"""
+ # Gmail SMTP configuration - loaded from environment variables
+ smtp_host = os.getenv("SMTP_HOST", "smtp.gmail.com")
+ smtp_port = int(os.getenv("SMTP_PORT", "587"))
+ smtp_user = os.getenv("SMTP_USER")
+ smtp_password = os.getenv("SMTP_PASSWORD")
+
+ # Validate required credentials
+ if not smtp_user or not smtp_password:
+ print("[ERROR] SMTP credentials not found in .env file")
+ print("[ERROR] Please ensure SMTP_USER and SMTP_PASSWORD are set in .env")
+ return False
+
+ try:
+ # Create message
+ msg = MIMEMultipart()
+ msg['From'] = smtp_user
+ msg['To'] = to_email
+ msg['Subject'] = f"Hero Wars Arena Hero Counts - {datetime.now().strftime('%B %d, %Y')}"
+
+ # Add body
+ msg.attach(MIMEText(email_body, 'plain', 'utf-8'))
+
+ # Connect to server and send
+ print(f"[INFO] Connecting to SMTP server...")
+ server = smtplib.SMTP(smtp_host, smtp_port)
+ server.starttls()
+ server.login(smtp_user, smtp_password)
+
+ print(f"[INFO] Sending email to {to_email}...")
+ text = msg.as_string()
+ server.sendmail(smtp_user, to_email, text)
+ server.quit()
+
+ print(f"[SUCCESS] Email sent successfully to {to_email}")
+ return True
+
+ except Exception as e:
+ print(f"[ERROR] Failed to send email: {e}")
+ import traceback
+ traceback.print_exc()
+ return False
+
+
+def main():
+ """Main scraping function"""
+ base_url = "https://hw-recruit.com/arena?server=&server_1=&position=10"
+ max_page = 128
+
+ print(f"[INFO] Starting scrape of arena teams from hw-recruit.com")
+ print(f"[INFO] Will scrape from first page to page {max_page + 1} (page={max_page})")
+ print()
+
+ # Scrape all pages
+ all_heroes, total_teams = scrape_all_pages(base_url, max_page)
+
+ if not all_heroes:
+ print("[ERROR] No heroes found. Check if the website structure has changed.")
+ return
+
+ # Count hero occurrences
+ hero_counts = Counter(all_heroes)
+
+ # Sort by count (descending)
+ sorted_heroes = sorted(hero_counts.items(), key=lambda x: x[1], reverse=True)
+
+ # Print results
+ print()
+ print("=" * 60)
+ print("SCRAPING RESULTS")
+ print("=" * 60)
+ print(f"Total teams scraped: {total_teams}")
+ print(f"Total hero occurrences: {len(all_heroes)}")
+ print(f"Unique heroes found: {len(hero_counts)}")
+ print()
+ print("=" * 60)
+ print("HERO OCCURRENCE COUNTS (sorted by frequency)")
+ print("=" * 60)
+
+ for hero_name, count in sorted_heroes:
+ print(f"{hero_name:30s} : {count:5d}")
+
+ print()
+ print("=" * 60)
+ print("SPECIFIC HERO COUNTS")
+ print("=" * 60)
+
+ # Show some specific examples
+ specific_heroes = ['Sebastian', 'Axel', 'Lara_Croft', 'Lyria', 'Galahad']
+ for hero in specific_heroes:
+ count = hero_counts.get(hero, 0)
+ print(f"{hero:30s} : {count:5d}")
+
+ # Save to file
+ output_file = Path('arena_hero_counts.txt')
+ today_date = datetime.now().strftime('%Y-%m-%d')
+ with open(output_file, 'w', encoding='utf-8') as f:
+ f.write("Hero Wars Arena Team Hero Counts\n")
+ f.write(f"Date: {today_date}\n")
+ f.write("=" * 60 + "\n")
+ f.write(f"Total teams scraped: {total_teams}\n")
+ f.write(f"Total hero occurrences: {len(all_heroes)}\n")
+ f.write(f"Unique heroes found: {len(hero_counts)}\n")
+ f.write("\n")
+ f.write("=" * 60 + "\n")
+ f.write("HERO OCCURRENCE COUNTS (sorted by frequency)\n")
+ f.write("=" * 60 + "\n")
+ for hero_name, count in sorted_heroes:
+ f.write(f"{hero_name:30s} : {count:5d}\n")
+
+ print()
+ print(f"[SUCCESS] Results saved to {output_file}")
+ print()
+ print(f"[INFO] Sebastian appears {hero_counts.get('Sebastian', 0)} times across all pages")
+
+ # Read the output file and send via email
+ try:
+ with open(output_file, 'r', encoding='utf-8') as f:
+ email_body = f.read()
+
+ print()
+ print("[INFO] Sending results via email...")
+ send_email(email_body)
+ except Exception as e:
+ print(f"[WARNING] Failed to send email: {e}")
+
+
+if __name__ == '__main__':
+ main()
+
diff --git a/scrape_meta_teams_to_db.py b/scrape_meta_teams_to_db.py
new file mode 100644
index 0000000..62600e9
--- /dev/null
+++ b/scrape_meta_teams_to_db.py
@@ -0,0 +1,454 @@
+#!/usr/bin/env python3
+"""
+Scrape Hero Wars arena meta teams from hw-recruit.com and save each run as a
+timestamped snapshot in PostgreSQL.
+
+Each run creates:
+ - one row in meta_team_snapshots (capture time + scrape metadata)
+ - many rows in meta_teams (team combos with popularity counts)
+
+After saving, deletes entire snapshots older than 30 days; all meta_teams rows
+for those snapshots are removed automatically (FK ON DELETE CASCADE).
+
+Usage:
+ pip install -r requirements.txt
+ npm run db:init
+ python scrape_meta_teams_to_db.py
+ python scrape_meta_teams_to_db.py --position 10 --max-page 20
+"""
+from __future__ import annotations
+
+import argparse
+import os
+import time
+from datetime import datetime, timedelta, timezone
+from pathlib import Path
+from typing import Any
+from urllib.parse import urlencode
+
+import psycopg2
+import psycopg2.extras
+import requests
+from bs4 import BeautifulSoup
+from dotenv import load_dotenv
+
+load_dotenv()
+
+HERO_NAMES = {
+ 1: 'Aurora', 2: 'Galahad', 3: 'Keira', 4: 'Astaroth', 5: 'Kai', 6: 'Phobos', 7: 'Thea',
+ 8: 'Daredevil', 9: 'Heidi', 10: 'Faceless', 11: 'Chabba', 12: 'Arachne', 13: 'Orion',
+ 14: 'Fox', 15: 'Ginger', 16: 'Dante', 17: 'Mojo', 18: 'Judge', 19: 'Dark Star', 20: 'Artemis',
+ 21: 'Markus', 22: 'Peppy', 23: 'Lian', 24: 'Cleaver', 25: 'Ishmael', 26: 'Lilith', 27: 'Luther',
+ 28: 'Qing Mao', 29: 'Dorian', 30: 'Cornelius', 31: 'Jet', 32: 'Helios', 33: 'Lars', 34: 'Krista',
+ 35: 'Jorgen', 36: 'Maya', 37: 'Jhu', 38: 'Elmir', 39: 'Ziri', 40: 'Nebula', 41: "K'arkh",
+ 42: 'Rufus', 43: 'Celeste', 44: 'Astrid and Lucas', 45: 'Satori', 46: 'Martha', 47: 'Andvari',
+ 48: 'Sebastian', 49: 'Yasmine', 50: 'Corvus', 51: 'Morrigan', 52: 'Isaac', 53: 'Alvanor',
+ 54: 'Tristan', 55: 'Iris', 56: 'Amira', 57: 'Fafnir', 58: 'Aidan', 59: 'Kayla',
+ 60: 'Mushy and Shroom', 61: 'Julius', 62: 'Polaris', 63: 'Lara Croft', 64: 'Augustus',
+ 65: 'Ninja Turtles', 66: 'Folio', 67: 'Lyria', 68: 'Guus', 69: 'Cascade', 70: 'Electra von Grave',
+ 71: 'Fluffy', 72: 'Byrna', 73: 'Adam', 74: 'Somna',
+}
+
+PET_NAMES = {
+ 6000: 'Fenris', 6001: 'Oliver', 6002: 'Merlin', 6003: 'Mara', 6004: 'Cain',
+ 6005: 'Albus', 6006: 'Axel', 6007: 'Biscuit', 6008: 'Khorus', 6009: 'Vex',
+}
+
+DEFAULT_DATABASE_URL = 'postgresql://postgres:postgres@localhost:5432/autohero'
+DEFAULT_BASE_URL = 'https://hw-recruit.com/arena'
+
+
+def get_database_url() -> str:
+ return os.getenv('DATABASE_URL', DEFAULT_DATABASE_URL)
+
+
+def build_combo_key(hero_ids: list[int], pet: int | None = None, banner: int | None = None) -> str:
+ heroes = [int(h) for h in hero_ids if 0 < int(h) < 6000]
+ pet_id = int(pet or 0)
+ banner_id = int(banner or 0)
+ return f"{','.join(str(h) for h in heroes)}|{pet_id}|{banner_id}"
+
+
+def resolve_hero_name(hero_id: int) -> str:
+ if hero_id >= 6000:
+ return PET_NAMES.get(hero_id, f'Pet {hero_id}')
+ return HERO_NAMES.get(hero_id, f'Hero {hero_id}')
+
+
+def parse_pet_filename(base: str) -> int | None:
+ """hw-recruit pet icons: 6--8.png -> 6008 (Khorus), 6--6.png -> 6006 (Axel)."""
+ if '--' not in base:
+ return None
+ left, right = base.split('--', 1)
+ if not left.isdigit() or not right.isdigit():
+ return None
+ return int(f'{left}00{right}')
+
+
+def parse_team_images(image_names: list[str]) -> dict[str, Any]:
+ hero_ids: list[int] = []
+ pet_id: int | None = None
+
+ for image_name in image_names:
+ base = image_name.replace('.png', '').replace('.webp', '')
+ parsed_pet = parse_pet_filename(base)
+ if parsed_pet is not None:
+ pet_id = parsed_pet
+ continue
+ if not base.isdigit():
+ continue
+
+ value = int(base, 10)
+ if value >= 6000:
+ pet_id = value
+ elif value < 6000:
+ hero_ids.append(value)
+
+ hero_ids = hero_ids[:5]
+ hero_names = [resolve_hero_name(h) for h in hero_ids]
+ pet_name = PET_NAMES.get(pet_id) if pet_id else None
+
+ return {
+ 'hero_ids': hero_ids,
+ 'hero_names': hero_names,
+ 'pet': pet_id,
+ 'pet_name': pet_name,
+ 'banner': None,
+ 'combo_key': build_combo_key(hero_ids, pet_id, None),
+ }
+
+
+def get_page_content(url: str, retries: int = 3) -> str | None:
+ headers = {
+ 'User-Agent': (
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
+ '(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
+ )
+ }
+
+ for attempt in range(retries):
+ try:
+ response = requests.get(url, headers=headers, timeout=30)
+ response.raise_for_status()
+ return response.text
+ except requests.exceptions.RequestException as exc:
+ if attempt < retries - 1:
+ print(f'[WARNING] Failed to fetch {url}, retrying... ({attempt + 1}/{retries})')
+ time.sleep(2)
+ else:
+ print(f'[ERROR] Failed to fetch {url} after {retries} attempts: {exc}')
+ return None
+ return None
+
+
+def extract_teams_from_page(html_content: str, page_number: int) -> list[dict[str, Any]]:
+ soup = BeautifulSoup(html_content, 'html.parser')
+ teams: list[dict[str, Any]] = []
+
+ for row in soup.select('table tbody tr'):
+ team_cell = row.find('td', class_=lambda c: c and 'views-field-team' in c and 'team-1' not in c)
+ if not team_cell:
+ continue
+
+ rank_cell = row.find('td', class_=lambda c: c and 'views-field-counter' in c)
+ count_cell = row.find('td', class_=lambda c: c and 'views-field-team-1' in c)
+
+ image_names = [
+ img.get('src', '').split('/')[-1]
+ for img in team_cell.find_all('img')
+ if img.get('src')
+ ]
+ if not image_names:
+ continue
+
+ parsed = parse_team_images(image_names)
+ if len(parsed['hero_ids']) != 5:
+ print(f'[WARNING] Skipping row with {len(parsed["hero_ids"])} heroes on page {page_number}: {image_names}')
+ continue
+
+ popularity = None
+ row_rank = None
+ if count_cell:
+ try:
+ popularity = int(count_cell.get_text(strip=True).replace(',', ''))
+ except ValueError:
+ popularity = None
+ if rank_cell:
+ try:
+ row_rank = int(rank_cell.get_text(strip=True))
+ except ValueError:
+ row_rank = None
+
+ teams.append({
+ **parsed,
+ 'popularity_count': popularity,
+ 'row_rank': row_rank,
+ 'page_number': page_number,
+ })
+
+ return teams
+
+
+def build_page_url(
+ base_url: str,
+ position: int,
+ page_index: int,
+ *,
+ server_min: str = '',
+ server_max: str = '',
+) -> str:
+ params = {
+ 'server': str(server_min),
+ 'server_1': str(server_max),
+ 'position': str(position),
+ }
+ query = urlencode(params)
+ if page_index <= 1:
+ return f'{base_url}?{query}'
+ return f'{base_url}?{query}&page={page_index - 1}'
+
+
+def scrape_all_teams(
+ base_url: str,
+ position: int,
+ max_page: int,
+ delay_seconds: float,
+ *,
+ server_min: str = '',
+ server_max: str = '',
+) -> tuple[list[dict[str, Any]], int]:
+ all_teams: list[dict[str, Any]] = []
+ pages_scraped = 0
+ page_number = 1
+
+ while True:
+ if max_page > 0 and page_number > max_page:
+ break
+
+ url = build_page_url(
+ base_url,
+ position,
+ page_number,
+ server_min=server_min,
+ server_max=server_max,
+ )
+ print(f'[INFO] Scraping page {page_number}: {url}')
+ html = get_page_content(url)
+ if not html:
+ print(f'[ERROR] Stopping after failed fetch on page {page_number}')
+ break
+
+ teams = extract_teams_from_page(html, page_number)
+ if not teams:
+ print(f'[INFO] No teams found on page {page_number}, stopping')
+ break
+
+ all_teams.extend(teams)
+ pages_scraped += 1
+ print(f' Found {len(teams)} teams (running total: {len(all_teams)})')
+
+ page_number += 1
+ if delay_seconds > 0:
+ time.sleep(delay_seconds)
+
+ return all_teams, pages_scraped
+
+
+def save_snapshot_to_db(
+ teams: list[dict[str, Any]],
+ *,
+ source_url: str,
+ position_max: int,
+ pages_scraped: int,
+ notes: str | None = None,
+) -> int:
+ captured_at = datetime.now(timezone.utc)
+ unique_combo_keys = {team['combo_key'] for team in teams}
+
+ conn = psycopg2.connect(get_database_url())
+ try:
+ with conn:
+ with conn.cursor() as cur:
+ cur.execute(
+ '''
+ INSERT INTO meta_team_snapshots (
+ captured_at, source, source_url, position_max,
+ pages_scraped, total_teams, unique_combos, notes
+ )
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
+ RETURNING id
+ ''',
+ (
+ captured_at,
+ 'hw-recruit',
+ source_url,
+ position_max,
+ pages_scraped,
+ len(teams),
+ len(unique_combo_keys),
+ notes,
+ ),
+ )
+ snapshot_id = cur.fetchone()[0]
+
+ rows = [
+ (
+ snapshot_id,
+ team['combo_key'],
+ team['hero_ids'],
+ team['hero_names'],
+ team.get('pet'),
+ team.get('pet_name'),
+ team.get('banner'),
+ team.get('popularity_count'),
+ team.get('row_rank'),
+ team.get('page_number', 1),
+ )
+ for team in teams
+ ]
+
+ psycopg2.extras.execute_batch(
+ cur,
+ '''
+ INSERT INTO meta_teams (
+ snapshot_id, combo_key, hero_ids, hero_names, pet, pet_name,
+ banner, popularity_count, row_rank, page_number
+ )
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
+ ON CONFLICT (snapshot_id, row_rank) DO UPDATE SET
+ combo_key = EXCLUDED.combo_key,
+ hero_ids = EXCLUDED.hero_ids,
+ hero_names = EXCLUDED.hero_names,
+ pet = EXCLUDED.pet,
+ pet_name = EXCLUDED.pet_name,
+ banner = EXCLUDED.banner,
+ popularity_count = EXCLUDED.popularity_count,
+ page_number = EXCLUDED.page_number
+ ''',
+ rows,
+ page_size=200,
+ )
+
+ return snapshot_id
+ finally:
+ conn.close()
+
+
+def prune_old_snapshots(retention_days: int = 30) -> int:
+ """Remove whole snapshots older than retention_days (not individual teams)."""
+ cutoff = datetime.now(timezone.utc) - timedelta(days=retention_days)
+
+ conn = psycopg2.connect(get_database_url())
+ try:
+ with conn:
+ with conn.cursor() as cur:
+ # One delete per snapshot; meta_teams rows cascade via FK.
+ cur.execute(
+ '''
+ DELETE FROM meta_team_snapshots
+ WHERE captured_at < %s
+ ''',
+ (cutoff,),
+ )
+ return cur.rowcount
+ finally:
+ conn.close()
+
+
+def parse_args() -> argparse.Namespace:
+ parser = argparse.ArgumentParser(description='Scrape hw-recruit arena teams into PostgreSQL meta snapshots')
+ parser.add_argument('--position', type=int, default=10, help='Max arena position filter')
+ parser.add_argument('--server-min', default='', help='Server min filter (hw-recruit server param)')
+ parser.add_argument('--server-max', '--server-1', dest='server_max', default='', help='Server max filter (hw-recruit server_1 param)')
+ parser.add_argument('--max-page', type=int, default=0, help='Max pages to scrape (0 = until empty)')
+ parser.add_argument('--delay', type=float, default=1.0, help='Delay between page requests in seconds')
+ parser.add_argument('--base-url', default=DEFAULT_BASE_URL, help='hw-recruit arena base URL')
+ parser.add_argument('--notes', default='', help='Optional note stored on the snapshot row')
+ parser.add_argument('--dry-run', action='store_true', help='Scrape only; do not write to PostgreSQL')
+ parser.add_argument(
+ '--retention-days',
+ type=int,
+ default=30,
+ help='Delete entire snapshots older than this many days after saving (0 = keep all)',
+ )
+ return parser.parse_args()
+
+
+def main() -> None:
+ args = parse_args()
+ source_url = build_page_url(
+ args.base_url,
+ args.position,
+ 1,
+ server_min=args.server_min,
+ server_max=args.server_max,
+ )
+ notes = args.notes or None
+ if args.server_max:
+ server_note = f'server_1={args.server_max}'
+ notes = f'{notes}; {server_note}' if notes else server_note
+
+ print('[INFO] Starting hw-recruit meta team scrape')
+ print(
+ f'[INFO] server={args.server_min or "*"} server_1={args.server_max or "*"} '
+ f'position={args.position}, max_page={args.max_page or "all"}, '
+ f'database={get_database_url().split("@")[-1]}'
+ )
+ print()
+
+ teams, pages_scraped = scrape_all_teams(
+ args.base_url,
+ args.position,
+ args.max_page,
+ args.delay,
+ server_min=args.server_min,
+ server_max=args.server_max,
+ )
+
+ if not teams:
+ print('[ERROR] No teams scraped')
+ return
+
+ unique_combos = len({team['combo_key'] for team in teams})
+ print()
+ print('=' * 60)
+ print('SCRAPE RESULTS')
+ print('=' * 60)
+ print(f'Pages scraped: {pages_scraped}')
+ print(f'Total teams: {len(teams)}')
+ print(f'Unique combos: {unique_combos}')
+ print()
+ print('Top 5 by popularity:')
+ for team in sorted(teams, key=lambda t: t.get('popularity_count') or 0, reverse=True)[:5]:
+ heroes = ', '.join(team['hero_names'])
+ print(f" #{team.get('row_rank')} {heroes} — count {team.get('popularity_count')}")
+
+ if args.dry_run:
+ print()
+ print('[INFO] Dry run complete — nothing written to database')
+ return
+
+ snapshot_id = save_snapshot_to_db(
+ teams,
+ source_url=source_url,
+ position_max=args.position,
+ pages_scraped=pages_scraped,
+ notes=notes,
+ )
+
+ print()
+ print(f'[SUCCESS] Saved snapshot {snapshot_id} with {len(teams)} meta teams')
+
+ if args.retention_days > 0:
+ deleted = prune_old_snapshots(args.retention_days)
+ print(
+ f'[INFO] Removed {deleted} snapshot(s) older than {args.retention_days} days '
+ f'(all teams in those snapshots deleted via cascade)'
+ )
+
+ print('[INFO] View snapshots: GET http://127.0.0.1:9876/training/meta-snapshots')
+ print(f'[INFO] View teams: GET http://127.0.0.1:9876/training/meta-teams?snapshotId={snapshot_id}')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scrape_schedule_to_csv.py b/scrape_schedule_to_csv.py
new file mode 100644
index 0000000..01cdf7b
--- /dev/null
+++ b/scrape_schedule_to_csv.py
@@ -0,0 +1,491 @@
+"""
+Scrape Hero Wars schedule page and extract event list to CSV format
+"""
+import csv
+import time
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.chrome.service import Service
+from selenium.webdriver.chrome.options import Options
+from webdriver_manager.chrome import ChromeDriverManager
+from pathlib import Path
+
+
+def setup_driver():
+ """Setup Chrome WebDriver with appropriate options"""
+ chrome_options = Options()
+ chrome_options.add_argument('--headless')
+ chrome_options.add_argument('--no-sandbox')
+ chrome_options.add_argument('--disable-dev-shm-usage')
+ chrome_options.add_argument('--disable-gpu')
+ chrome_options.add_argument('--window-size=1920,1080')
+ chrome_options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')
+
+ service = Service(ChromeDriverManager().install())
+ driver = webdriver.Chrome(service=service, options=chrome_options)
+ return driver
+
+
+def find_and_click_list_tab(driver):
+ """Find and click the List tab using JavaScript"""
+ wait = WebDriverWait(driver, 20)
+
+ # First, check if we're already on the List view
+ js_check_list_active = """
+ // Check if List tab is already active/selected
+ var buttons = document.querySelectorAll('ion-segment-button');
+ for (var i = 0; i < buttons.length; i++) {
+ var btn = buttons[i];
+ var text = btn.textContent || btn.innerText || '';
+ if (text.toLowerCase().includes('list')) {
+ // Check if button has 'checked' attribute or 'selected' class
+ if (btn.hasAttribute('checked') || btn.classList.contains('segment-button-checked') ||
+ btn.classList.contains('selected') || btn.getAttribute('aria-pressed') === 'true') {
+ return true;
+ }
+ }
+ }
+
+ // Also check if schedule grid is visible (indicates we're on List view)
+ var grid = document.querySelector('ion-grid.schedule-grid') || document.querySelector('.schedule-grid');
+ if (grid && grid.offsetParent !== null) {
+ // Grid is visible, likely already on List view
+ return true;
+ }
+
+ return false;
+ """
+
+ try:
+ is_already_on_list = driver.execute_script(js_check_list_active)
+ if is_already_on_list:
+ print("[OK] Already on List view, no need to click")
+ return True
+ except Exception as e:
+ # If check fails, proceed with clicking attempt
+ pass
+
+ # Use JavaScript to find and click the List tab
+ js_click_list = """
+ // Find all ion-segment-button elements
+ var buttons = document.querySelectorAll('ion-segment-button');
+ for (var i = 0; i < buttons.length; i++) {
+ var btn = buttons[i];
+ var text = btn.textContent || btn.innerText || '';
+ if (text.toLowerCase().includes('list')) {
+ btn.click();
+ return true;
+ }
+ }
+
+ // Try finding by shadow DOM
+ buttons = document.querySelectorAll('ion-segment ion-segment-button');
+ for (var i = 0; i < buttons.length; i++) {
+ var btn = buttons[i];
+ var text = '';
+ // Try accessing shadow root
+ if (btn.shadowRoot) {
+ var slot = btn.shadowRoot.querySelector('slot');
+ if (slot) {
+ var assignedNodes = slot.assignedNodes();
+ for (var j = 0; j < assignedNodes.length; j++) {
+ text += assignedNodes[j].textContent || '';
+ }
+ }
+ }
+ text = text || btn.textContent || btn.innerText || '';
+ if (text.toLowerCase().includes('list')) {
+ btn.click();
+ return true;
+ }
+ }
+
+ // Fallback: click second button (usually List is second)
+ buttons = document.querySelectorAll('ion-segment-button');
+ if (buttons.length > 1) {
+ buttons[1].click();
+ return true;
+ }
+
+ return false;
+ """
+
+ js_click_succeeded = False
+ try:
+ result = driver.execute_script(js_click_list)
+ if result:
+ print("[OK] Clicked List tab using JavaScript")
+ time.sleep(3) # Wait for content to load
+ return True
+ except Exception as e:
+ print(f"[DEBUG] JavaScript click failed: {e}")
+ js_click_succeeded = False
+
+ # Fallback: try XPath selectors (only if JavaScript failed)
+ if not js_click_succeeded:
+ list_tab_selectors = [
+ "//ion-segment-button[contains(., 'List')]",
+ "//ion-segment-button[2]", # Second button is usually List
+ "//ion-segment//ion-segment-button[position()=2]"
+ ]
+
+ for selector in list_tab_selectors:
+ try:
+ list_tab = wait.until(EC.presence_of_element_located((By.XPATH, selector)))
+ driver.execute_script("arguments[0].scrollIntoView(true);", list_tab)
+ time.sleep(0.5)
+ driver.execute_script("arguments[0].click();", list_tab)
+ print(f"[OK] Clicked List tab using XPath: {selector}")
+ time.sleep(3)
+ return True
+ except Exception as e:
+ # Only show debug message if this is the last attempt
+ if selector == list_tab_selectors[-1]:
+ print(f"[DEBUG] All XPath selectors failed, last attempt: {selector}")
+ continue
+
+ return False
+
+
+def scroll_to_load_all(driver):
+ """Scroll through the page to trigger lazy loading of all events"""
+ try:
+ # Get initial page height
+ last_height = driver.execute_script("return document.body.scrollHeight")
+
+ scroll_attempts = 0
+ max_scrolls = 10 # Prevent infinite scrolling
+
+ while scroll_attempts < max_scrolls:
+ # Scroll to bottom
+ driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
+ time.sleep(1) # Wait for content to load
+
+ # Calculate new scroll height
+ new_height = driver.execute_script("return document.body.scrollHeight")
+
+ # If height didn't change, we've reached the bottom
+ if new_height == last_height:
+ break
+
+ last_height = new_height
+ scroll_attempts += 1
+
+ # Scroll back to top
+ driver.execute_script("window.scrollTo(0, 0);")
+ time.sleep(1)
+ print(f"[OK] Scrolled through page ({scroll_attempts} scrolls)")
+
+ except Exception as e:
+ print(f"[WARNING] Scrolling failed: {e}")
+
+
+def extract_schedule_data(driver):
+ """Extract schedule data from the page"""
+ wait = WebDriverWait(driver, 20)
+
+ # Wait for the schedule grid to load - try multiple selectors
+ schedule_grid = None
+ selectors = [
+ "ion-grid.schedule-grid",
+ ".schedule-grid",
+ "ion-grid",
+ "[class*='schedule']",
+ "ion-grid[class*='grid']"
+ ]
+
+ for selector in selectors:
+ try:
+ schedule_grid = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, selector)))
+ print(f"[OK] Found schedule grid using selector: {selector}")
+ break
+ except:
+ continue
+
+ if not schedule_grid:
+ print("[ERROR] Could not find schedule grid")
+ return []
+
+ # Use JavaScript to extract data from Shadow DOM and regular DOM
+ js_code = r"""
+ // Try to find the grid element if not passed
+ var grid = arguments[0] || document.querySelector('ion-grid.schedule-grid') || document.querySelector('.schedule-grid') || document.querySelector('ion-grid');
+
+ if (!grid) {
+ return [];
+ }
+
+ // Try to expand/scroll to ensure all rows are visible
+ grid.scrollIntoView();
+
+ // Get all rows - try multiple methods
+ var rows = grid.querySelectorAll('ion-row');
+
+ // If no rows found, try finding rows in the document
+ if (rows.length === 0) {
+ rows = document.querySelectorAll('ion-grid ion-row, .schedule-grid ion-row');
+ }
+
+ var events = [];
+ var currentEvent = null;
+ var seenEvents = new Set(); // Track event names to avoid duplicates
+
+ for (var i = 0; i < rows.length; i++) {
+ var row = rows[i];
+ var cols = row.querySelectorAll('ion-col');
+
+ if (cols.length === 0) continue;
+
+ var col = cols[0];
+
+ // Get text content, handling shadow DOM
+ var fullText = '';
+ if (col.shadowRoot) {
+ var slot = col.shadowRoot.querySelector('slot');
+ if (slot) {
+ var assignedNodes = slot.assignedNodes();
+ for (var k = 0; k < assignedNodes.length; k++) {
+ fullText += assignedNodes[k].textContent || '';
+ }
+ }
+ }
+ if (!fullText) {
+ fullText = col.textContent || col.innerText || '';
+ }
+ fullText = fullText.trim();
+
+ if (!fullText) continue;
+
+ var style = row.getAttribute('style') || '';
+ var paddingLeft = 0;
+
+ // Check padding to determine hierarchy
+ if (style.includes('padding-left')) {
+ var match = style.match(/padding-left:\s*(\d+)em/);
+ if (match) {
+ paddingLeft = parseInt(match[1]);
+ }
+ }
+
+ // Check if this is a main event (has date pattern)
+ var datePattern = /\d{4}-\d{2}-\d{2}.*\d{2}:\d{2}:\d{2}.*[AP]M.*-\s*\d{4}-\d{2}-\d{2}.*\d{2}:\d{2}:\d{2}.*[AP]M/;
+
+ if (datePattern.test(fullText) && paddingLeft === 0) {
+ // This is a new main event
+ if (currentEvent) {
+ // Only add if we haven't seen this event before
+ var eventKey = currentEvent.name + '|' + currentEvent.dateRange;
+ if (!seenEvents.has(eventKey)) {
+ events.push(currentEvent);
+ seenEvents.add(eventKey);
+ }
+ }
+
+ // Extract event name and date
+ var link = col.querySelector('a');
+ var eventName = '';
+
+ if (link) {
+ eventName = link.textContent || link.innerText || '';
+ if (link.shadowRoot) {
+ var linkSlot = link.shadowRoot.querySelector('slot');
+ if (linkSlot) {
+ var linkNodes = linkSlot.assignedNodes();
+ for (var k = 0; k < linkNodes.length; k++) {
+ eventName = linkNodes[k].textContent || '';
+ }
+ }
+ }
+ }
+
+ if (!eventName) {
+ // Try to extract from text before date
+ var dateMatch = fullText.match(datePattern);
+ if (dateMatch) {
+ var beforeDate = fullText.substring(0, fullText.indexOf(dateMatch[0])).trim();
+ if (beforeDate.endsWith(':')) {
+ beforeDate = beforeDate.slice(0, -1).trim();
+ }
+ eventName = beforeDate;
+ }
+ }
+
+ var dateMatch = fullText.match(datePattern);
+ var dateRange = dateMatch ? dateMatch[0] : '';
+
+ currentEvent = {
+ name: eventName,
+ dateRange: dateRange,
+ fullLine: eventName + ': ' + dateRange,
+ quests: []
+ };
+ } else if (currentEvent && paddingLeft === 2) {
+ // This is a quest/requirement name (2em padding)
+ var questName = fullText;
+ // Remove trailing colons if any
+ if (questName.endsWith(':')) {
+ questName = questName.slice(0, -1).trim();
+ }
+ // Skip empty quests
+ if (questName) {
+ currentEvent.quests.push({
+ name: questName,
+ values: null // Will be filled by next row
+ });
+ }
+ } else if (currentEvent && paddingLeft === 4 && currentEvent.quests.length > 0) {
+ // This is the values for the last quest (4em padding)
+ var values = fullText.trim();
+ // Clean up values - remove extra whitespace
+ values = values.replace(/\s+/g, ' ');
+ if (values && currentEvent.quests.length > 0) {
+ currentEvent.quests[currentEvent.quests.length - 1].values = values;
+ }
+ }
+ }
+
+ // Add the last event
+ if (currentEvent) {
+ var eventKey = currentEvent.name + '|' + currentEvent.dateRange;
+ if (!seenEvents.has(eventKey)) {
+ events.push(currentEvent);
+ seenEvents.add(eventKey);
+ }
+ }
+
+ return events;
+ """
+
+ try:
+ events = driver.execute_script(js_code, schedule_grid)
+ print(f"[DEBUG] Extracted {len(events) if events else 0} events")
+ return events if events else []
+ except Exception as e:
+ print(f"[ERROR] Failed to extract data: {e}")
+ import traceback
+ traceback.print_exc()
+ return []
+
+
+def format_to_csv(events, output_file='schedule_extracted.csv'):
+ """Format events to CSV matching Callist.csv format"""
+ output_path = Path(output_file)
+
+ with open(output_path, 'w', encoding='utf-8', newline='') as f:
+ writer = csv.writer(f)
+
+ for event in events:
+ # Write main event line with date range
+ writer.writerow([event['fullLine']])
+
+ # Write quests and their values
+ for quest in event['quests']:
+ writer.writerow([quest['name']])
+ if quest['values']:
+ writer.writerow([quest['values']])
+
+ print(f"[OK] Saved {len(events)} events to {output_file}")
+ return output_file
+
+
+def main():
+ """Main scraping function"""
+ url = "https://hero-wars-guide.web.app/schedule"
+
+ print(f"[INFO] Starting scrape of {url}")
+ driver = None
+
+ try:
+ driver = setup_driver()
+ print("[OK] WebDriver initialized")
+
+ driver.get(url)
+ print("[OK] Page loaded")
+
+ # Wait for page to fully load
+ time.sleep(3)
+
+ # Find and click List tab
+ if not find_and_click_list_tab(driver):
+ print("[WARNING] Could not find or click List tab, proceeding anyway")
+ # Try to proceed anyway - maybe we're already on List tab
+
+ # Wait a bit more for content to render
+ time.sleep(5)
+
+ # Scroll to load all content (lazy loading)
+ print("[INFO] Scrolling to load all events...")
+ scroll_to_load_all(driver)
+
+ # Wait for content to settle after scrolling
+ time.sleep(3)
+
+ # Try extracting multiple times to catch any dynamically loaded content
+ all_events = []
+ seen_event_keys = set()
+
+ for attempt in range(3):
+ print(f"[INFO] Extraction attempt {attempt + 1}/3...")
+ events = extract_schedule_data(driver)
+
+ if events:
+ for event in events:
+ event_key = event.get('name', '') + '|' + event.get('dateRange', '')
+ if event_key and event_key not in seen_event_keys:
+ all_events.append(event)
+ seen_event_keys.add(event_key)
+
+ if attempt < 2: # Don't wait after last attempt
+ time.sleep(2)
+ # Scroll again to trigger any lazy loading
+ driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
+ time.sleep(1)
+ driver.execute_script("window.scrollTo(0, 0);")
+ time.sleep(1)
+
+ events = all_events
+ print(f"[INFO] Total unique events found: {len(events)}")
+
+ if not events:
+ print("[ERROR] No events extracted")
+ # Save page source for debugging
+ with open('debug_page_source.html', 'w', encoding='utf-8') as f:
+ f.write(driver.page_source)
+ print("[DEBUG] Saved page source to debug_page_source.html")
+
+ # Try taking a screenshot
+ driver.save_screenshot('debug_screenshot.png')
+ print("[DEBUG] Saved screenshot to debug_screenshot.png")
+ return
+
+ print(f"[OK] Extracted {len(events)} events")
+
+ # Format and save to CSV
+ output_file = format_to_csv(events)
+
+ print(f"\n[SUCCESS] Scraping complete!")
+ print(f" Output file: {output_file}")
+ print(f" Events extracted: {len(events)}")
+
+ except Exception as e:
+ print(f"[ERROR] Scraping failed: {e}")
+ import traceback
+ traceback.print_exc()
+
+ if driver:
+ try:
+ driver.save_screenshot('error_screenshot.png')
+ print("[DEBUG] Saved error screenshot")
+ except:
+ pass
+
+ finally:
+ if driver:
+ driver.quit()
+ print("[OK] Browser closed")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/training-db.mjs b/training-db.mjs
new file mode 100644
index 0000000..41511aa
--- /dev/null
+++ b/training-db.mjs
@@ -0,0 +1,1368 @@
+import pg from 'pg';
+import { findGrandArenaSelections } from './grand-arena-selection.mjs';
+
+const { Pool } = pg;
+
+const DEFAULT_DATABASE_URL = 'postgresql://postgres:postgres@localhost:5432/autohero';
+
+let pool = null;
+let ready = false;
+let lastError = null;
+
+function getDatabaseUrl() {
+ return process.env.DATABASE_URL || DEFAULT_DATABASE_URL;
+}
+
+function maskDatabaseUrl(url) {
+ try {
+ const parsed = new URL(url);
+ if (parsed.password) parsed.password = '****';
+ return parsed.toString();
+ } catch {
+ return 'postgresql://****';
+ }
+}
+
+export function getDatabaseStatus() {
+ return {
+ ready,
+ url: maskDatabaseUrl(getDatabaseUrl()),
+ lastError: lastError?.message || null,
+ };
+}
+
+const SCHEMA_SQL = `
+CREATE TABLE IF NOT EXISTS opponent_combos (
+ id SERIAL PRIMARY KEY,
+ combo_key TEXT NOT NULL UNIQUE,
+ hero_ids INTEGER[] NOT NULL,
+ hero_names TEXT[],
+ pet INTEGER,
+ banner INTEGER,
+ opponent_user_id TEXT,
+ opponent_name TEXT,
+ opponent_place TEXT,
+ opponent_power BIGINT,
+ first_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+CREATE TABLE IF NOT EXISTS matchup_tests (
+ id SERIAL PRIMARY KEY,
+ opponent_combo_id INTEGER NOT NULL REFERENCES opponent_combos(id) ON DELETE CASCADE,
+ session_id TEXT,
+ my_hero_ids INTEGER[] NOT NULL,
+ my_hero_names TEXT[],
+ my_pet INTEGER,
+ wins INTEGER,
+ losses INTEGER,
+ win_rate NUMERIC NOT NULL,
+ rank INTEGER,
+ tested_at TIMESTAMPTZ NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+CREATE UNIQUE INDEX IF NOT EXISTS idx_matchup_tests_unique_session
+ ON matchup_tests (opponent_combo_id, session_id, my_hero_ids, my_pet);
+
+CREATE INDEX IF NOT EXISTS idx_opponent_combos_last_seen
+ ON opponent_combos (last_seen_at DESC);
+
+CREATE INDEX IF NOT EXISTS idx_matchup_tests_opponent_combo_id
+ ON matchup_tests (opponent_combo_id);
+
+CREATE INDEX IF NOT EXISTS idx_matchup_tests_tested_at
+ ON matchup_tests (tested_at DESC);
+
+CREATE INDEX IF NOT EXISTS idx_matchup_tests_win_rate
+ ON matchup_tests (win_rate DESC);
+
+-- Legacy round archive (optional full payload)
+CREATE TABLE IF NOT EXISTS training_rounds (
+ id SERIAL PRIMARY KEY,
+ session_id TEXT NOT NULL UNIQUE,
+ label TEXT,
+ started_at TIMESTAMPTZ,
+ completed_at TIMESTAMPTZ,
+ stopped_early BOOLEAN NOT NULL DEFAULT FALSE,
+ opponent_combo_key TEXT,
+ opponent_user_id TEXT,
+ opponent_name TEXT,
+ opponent_place TEXT,
+ opponent_power BIGINT,
+ tested_combos INTEGER,
+ best_win_rate NUMERIC,
+ best_hero_ids INTEGER[],
+ best_hero_names TEXT[],
+ best_pet INTEGER,
+ payload JSONB,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+CREATE INDEX IF NOT EXISTS idx_training_rounds_completed_at
+ ON training_rounds (completed_at DESC);
+
+CREATE TABLE IF NOT EXISTS meta_team_snapshots (
+ id SERIAL PRIMARY KEY,
+ captured_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ source TEXT NOT NULL DEFAULT 'hw-recruit',
+ source_url TEXT,
+ position_max INTEGER,
+ pages_scraped INTEGER NOT NULL DEFAULT 0,
+ total_teams INTEGER NOT NULL DEFAULT 0,
+ unique_combos INTEGER NOT NULL DEFAULT 0,
+ notes TEXT,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+CREATE INDEX IF NOT EXISTS idx_meta_team_snapshots_captured_at
+ ON meta_team_snapshots (captured_at DESC);
+
+CREATE TABLE IF NOT EXISTS meta_teams (
+ id SERIAL PRIMARY KEY,
+ snapshot_id INTEGER NOT NULL REFERENCES meta_team_snapshots(id) ON DELETE CASCADE,
+ combo_key TEXT NOT NULL,
+ hero_ids INTEGER[] NOT NULL,
+ hero_names TEXT[],
+ pet INTEGER,
+ pet_name TEXT,
+ banner INTEGER,
+ popularity_count INTEGER,
+ row_rank INTEGER,
+ page_number INTEGER NOT NULL DEFAULT 1,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ UNIQUE (snapshot_id, row_rank)
+);
+
+CREATE INDEX IF NOT EXISTS idx_meta_teams_snapshot_id
+ ON meta_teams (snapshot_id);
+
+CREATE INDEX IF NOT EXISTS idx_meta_teams_combo_key
+ ON meta_teams (combo_key);
+
+CREATE INDEX IF NOT EXISTS idx_meta_teams_popularity
+ ON meta_teams (popularity_count DESC NULLS LAST);
+`;
+
+const MIGRATION_SQL = `
+ALTER TABLE training_rounds ADD COLUMN IF NOT EXISTS opponent_combo_key TEXT;
+ALTER TABLE training_rounds ALTER COLUMN payload DROP NOT NULL;
+
+CREATE INDEX IF NOT EXISTS idx_training_rounds_opponent_combo_key
+ ON training_rounds (opponent_combo_key);
+
+ALTER TABLE training_rounds ADD COLUMN IF NOT EXISTS tester_user_id TEXT;
+ALTER TABLE training_rounds ADD COLUMN IF NOT EXISTS tester_name TEXT;
+ALTER TABLE training_rounds ADD COLUMN IF NOT EXISTS max_upgrade BOOLEAN NOT NULL DEFAULT TRUE;
+
+ALTER TABLE matchup_tests ADD COLUMN IF NOT EXISTS tester_user_id TEXT;
+ALTER TABLE matchup_tests ADD COLUMN IF NOT EXISTS tester_name TEXT;
+ALTER TABLE matchup_tests ADD COLUMN IF NOT EXISTS max_upgrade BOOLEAN NOT NULL DEFAULT TRUE;
+
+DROP INDEX IF EXISTS idx_matchup_tests_unique_session;
+CREATE UNIQUE INDEX IF NOT EXISTS idx_matchup_tests_unique_session
+ ON matchup_tests (opponent_combo_id, session_id, my_hero_ids, my_pet, max_upgrade);
+
+CREATE INDEX IF NOT EXISTS idx_matchup_tests_max_upgrade
+ ON matchup_tests (max_upgrade);
+`;
+
+function parseTimestamp(value) {
+ if (!value) return null;
+ const date = new Date(value);
+ return Number.isNaN(date.getTime()) ? null : date.toISOString();
+}
+
+function parseBigInt(value) {
+ if (value == null || value === '') return null;
+ const n = Number(value);
+ return Number.isFinite(n) ? Math.trunc(n) : null;
+}
+
+export function buildComboKey(heroIds, pet, banner = 0) {
+ const heroes = (Array.isArray(heroIds) ? heroIds : []).map(Number).filter((id) => id > 0 && id < 6000);
+ return `${heroes.join(',')}|${Number(pet) || 0}|${Number(banner) || 0}`;
+}
+
+function extractOpponentTeam(body) {
+ const team = body?.opponent?.team || {};
+ const heroIds = Array.isArray(team.heroes)
+ ? team.heroes.map(Number).filter((id) => id > 0 && id < 6000)
+ : [];
+ const pet = team.pet != null ? Number(team.pet) : null;
+ const banner = team.banner != null ? Number(team.banner) : null;
+ const comboKey = buildComboKey(heroIds, pet, banner);
+
+ return {
+ comboKey,
+ heroIds,
+ pet,
+ banner,
+ opponentUserId: body?.opponent?.userId != null ? String(body.opponent.userId) : null,
+ opponentName: body?.opponent?.name || null,
+ opponentPlace: body?.opponent?.place != null ? String(body.opponent.place) : null,
+ opponentPower: parseBigInt(body?.opponent?.power),
+ };
+}
+
+function extractTesterInfo(body) {
+ const tester = body?.tester || {};
+ const maxUpgrade = body?.config?.maxUpgrade ?? tester.maxUpgrade ?? body?.maxUpgrade;
+ const isMaxUpgrade = maxUpgrade !== false;
+
+ if (isMaxUpgrade) {
+ return {
+ userId: '0',
+ name: 'maxHeros',
+ maxUpgrade: true,
+ };
+ }
+
+ const userId = tester.userId ?? body?.testerUserId ?? null;
+ const name = tester.name ?? body?.testerName ?? null;
+ return {
+ userId: userId != null ? String(userId) : null,
+ name: name != null ? String(name) : null,
+ maxUpgrade: false,
+ };
+}
+
+function extractRoundFields(body) {
+ const best = body?.best || null;
+ const opponent = extractOpponentTeam(body);
+ const tester = extractTesterInfo(body);
+ return {
+ sessionId: body?.sessionId || `round_${Date.now()}`,
+ label: body?.label || null,
+ startedAt: parseTimestamp(body?.startedAt),
+ completedAt: parseTimestamp(body?.completedAt),
+ stoppedEarly: !!body?.stoppedEarly,
+ opponent,
+ tester,
+ testedCombos: Number.isFinite(Number(body?.testedCombos)) ? Number(body.testedCombos) : null,
+ bestWinRate: best?.winRate != null ? Number(best.winRate) : null,
+ bestHeroIds: Array.isArray(best?.heroes) ? best.heroes.map(Number) : null,
+ bestHeroNames: Array.isArray(best?.heroNames) ? best.heroNames : null,
+ bestPet: best?.pet != null ? Number(best.pet) : null,
+ rankings: Array.isArray(body?.rankings) ? body.rankings : [],
+ };
+}
+
+async function upsertOpponentCombo(client, opponent, testedAt) {
+ const result = await client.query(
+ `INSERT INTO opponent_combos (
+ combo_key, hero_ids, pet, banner,
+ opponent_user_id, opponent_name, opponent_place, opponent_power,
+ first_seen_at, last_seen_at
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $9)
+ ON CONFLICT (combo_key) DO UPDATE SET
+ opponent_user_id = COALESCE(EXCLUDED.opponent_user_id, opponent_combos.opponent_user_id),
+ opponent_name = COALESCE(EXCLUDED.opponent_name, opponent_combos.opponent_name),
+ opponent_place = COALESCE(EXCLUDED.opponent_place, opponent_combos.opponent_place),
+ opponent_power = COALESCE(EXCLUDED.opponent_power, opponent_combos.opponent_power),
+ last_seen_at = GREATEST(opponent_combos.last_seen_at, EXCLUDED.last_seen_at)
+ RETURNING id, combo_key`,
+ [
+ opponent.comboKey,
+ opponent.heroIds,
+ opponent.pet,
+ opponent.banner,
+ opponent.opponentUserId,
+ opponent.opponentName,
+ opponent.opponentPlace,
+ opponent.opponentPower,
+ testedAt || new Date().toISOString(),
+ ]
+ );
+ return result.rows[0];
+}
+
+async function saveMatchupTests(client, opponentComboId, sessionId, rankings, testedAt, tester = {}) {
+ let saved = 0;
+ const maxUpgrade = tester.maxUpgrade !== false;
+ for (const ranking of rankings) {
+ const myHeroIds = Array.isArray(ranking.heroes) ? ranking.heroes.map(Number) : [];
+ if (!myHeroIds.length) continue;
+
+ await client.query(
+ `INSERT INTO matchup_tests (
+ opponent_combo_id, session_id, my_hero_ids, my_hero_names, my_pet,
+ wins, losses, win_rate, rank, tested_at,
+ tester_user_id, tester_name, max_upgrade
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
+ ON CONFLICT (opponent_combo_id, session_id, my_hero_ids, my_pet, max_upgrade) DO UPDATE SET
+ my_hero_names = EXCLUDED.my_hero_names,
+ wins = EXCLUDED.wins,
+ losses = EXCLUDED.losses,
+ win_rate = EXCLUDED.win_rate,
+ rank = EXCLUDED.rank,
+ tested_at = EXCLUDED.tested_at,
+ tester_user_id = EXCLUDED.tester_user_id,
+ tester_name = EXCLUDED.tester_name`,
+ [
+ opponentComboId,
+ sessionId,
+ myHeroIds,
+ Array.isArray(ranking.heroNames) ? ranking.heroNames : null,
+ ranking.pet != null ? Number(ranking.pet) : null,
+ ranking.wins != null ? Number(ranking.wins) : null,
+ ranking.losses != null ? Number(ranking.losses) : null,
+ ranking.winRate != null ? Number(ranking.winRate) : 0,
+ ranking.rank != null ? Number(ranking.rank) : null,
+ testedAt,
+ tester.userId ?? null,
+ tester.name ?? null,
+ maxUpgrade,
+ ]
+ );
+ saved++;
+ }
+ return saved;
+}
+
+export async function initDatabase() {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const client = await pool.connect();
+ try {
+ await client.query(SCHEMA_SQL);
+ await client.query(MIGRATION_SQL);
+ ready = true;
+ lastError = null;
+ } catch (error) {
+ ready = false;
+ lastError = error;
+ throw error;
+ } finally {
+ client.release();
+ }
+}
+
+export async function saveTrainingRound(body) {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const fields = extractRoundFields(body);
+ if (!fields.opponent.heroIds.length) {
+ throw new Error('Opponent team missing hero combo data');
+ }
+
+ const testedAt = fields.completedAt || new Date().toISOString();
+ const client = await pool.connect();
+
+ try {
+ await client.query('BEGIN');
+
+ const opponentRow = await upsertOpponentCombo(client, fields.opponent, testedAt);
+ const matchupCount = await saveMatchupTests(
+ client,
+ opponentRow.id,
+ fields.sessionId,
+ fields.rankings,
+ testedAt,
+ fields.tester
+ );
+
+ const roundResult = await client.query(
+ `INSERT INTO training_rounds (
+ session_id, label, started_at, completed_at, stopped_early,
+ opponent_combo_key, opponent_user_id, opponent_name, opponent_place, opponent_power,
+ tested_combos, best_win_rate, best_hero_ids, best_hero_names, best_pet, payload,
+ tester_user_id, tester_name, max_upgrade
+ ) VALUES (
+ $1, $2, $3, $4, $5,
+ $6, $7, $8, $9, $10,
+ $11, $12, $13, $14, $15, NULL,
+ $16, $17, $18
+ )
+ ON CONFLICT (session_id) DO UPDATE SET
+ label = EXCLUDED.label,
+ started_at = EXCLUDED.started_at,
+ completed_at = EXCLUDED.completed_at,
+ stopped_early = EXCLUDED.stopped_early,
+ opponent_combo_key = EXCLUDED.opponent_combo_key,
+ opponent_user_id = EXCLUDED.opponent_user_id,
+ opponent_name = EXCLUDED.opponent_name,
+ opponent_place = EXCLUDED.opponent_place,
+ opponent_power = EXCLUDED.opponent_power,
+ tested_combos = EXCLUDED.tested_combos,
+ best_win_rate = EXCLUDED.best_win_rate,
+ best_hero_ids = EXCLUDED.best_hero_ids,
+ best_hero_names = EXCLUDED.best_hero_names,
+ best_pet = EXCLUDED.best_pet,
+ tester_user_id = EXCLUDED.tester_user_id,
+ tester_name = EXCLUDED.tester_name,
+ max_upgrade = EXCLUDED.max_upgrade
+ RETURNING id, session_id, completed_at`,
+ [
+ fields.sessionId,
+ fields.label,
+ fields.startedAt,
+ testedAt,
+ fields.stoppedEarly,
+ fields.opponent.comboKey,
+ fields.opponent.opponentUserId,
+ fields.opponent.opponentName,
+ fields.opponent.opponentPlace,
+ fields.opponent.opponentPower,
+ fields.testedCombos,
+ fields.bestWinRate,
+ fields.bestHeroIds,
+ fields.bestHeroNames,
+ fields.bestPet,
+ fields.tester.userId,
+ fields.tester.name,
+ fields.tester.maxUpgrade !== false,
+ ]
+ );
+
+ await client.query('COMMIT');
+ ready = true;
+ lastError = null;
+
+ return {
+ storage: 'postgresql',
+ roundId: roundResult.rows[0].id,
+ sessionId: roundResult.rows[0].session_id,
+ completedAt: roundResult.rows[0].completed_at,
+ opponentComboKey: opponentRow.combo_key,
+ matchupCount,
+ summary: {
+ sessionId: fields.sessionId,
+ label: fields.label,
+ opponentComboKey: opponentRow.combo_key,
+ opponentHeroes: fields.opponent.heroIds,
+ opponentPet: fields.opponent.pet,
+ opponent: fields.opponent.opponentName || fields.opponent.opponentUserId,
+ bestWinRate: fields.bestWinRate,
+ bestHeroes: fields.bestHeroNames,
+ bestPet: fields.bestPet,
+ comboCount: matchupCount,
+ },
+ };
+ } catch (error) {
+ await client.query('ROLLBACK');
+ lastError = error;
+ throw error;
+ } finally {
+ client.release();
+ }
+}
+
+export async function getTrainingSummary() {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const [roundCount, opponentCount, matchupCount, latestMatchup] = await Promise.all([
+ pool.query('SELECT COUNT(*)::int AS count FROM training_rounds'),
+ pool.query('SELECT COUNT(*)::int AS count FROM opponent_combos'),
+ pool.query('SELECT COUNT(*)::int AS count FROM matchup_tests'),
+ pool.query(
+ `SELECT oc.combo_key, oc.hero_ids, oc.pet, oc.opponent_name,
+ mt.my_hero_ids, mt.my_pet, mt.win_rate, mt.tested_at
+ FROM matchup_tests mt
+ JOIN opponent_combos oc ON oc.id = mt.opponent_combo_id
+ ORDER BY mt.tested_at DESC NULLS LAST, mt.id DESC
+ LIMIT 1`
+ ),
+ ]);
+
+ const latestRow = latestMatchup.rows[0] || null;
+ const latestSummary = latestRow
+ ? {
+ opponentComboKey: latestRow.combo_key,
+ opponentHeroes: latestRow.hero_ids,
+ opponentPet: latestRow.pet,
+ opponentName: latestRow.opponent_name,
+ myHeroes: latestRow.my_hero_ids,
+ myPet: latestRow.my_pet,
+ winRate: latestRow.win_rate != null ? Number(latestRow.win_rate) : null,
+ testedAt: latestRow.tested_at,
+ }
+ : null;
+
+ ready = true;
+ lastError = null;
+
+ return {
+ storage: 'postgresql',
+ databaseUrl: maskDatabaseUrl(getDatabaseUrl()),
+ roundCount: roundCount.rows[0]?.count || 0,
+ opponentComboCount: opponentCount.rows[0]?.count || 0,
+ matchupTestCount: matchupCount.rows[0]?.count || 0,
+ latestSessionId: latestRow ? null : null,
+ latestSummary,
+ roundFiles: roundCount.rows[0]?.count || 0,
+ latestFile: latestRow?.combo_key || null,
+ };
+}
+
+function normalizeHeroFilterIds(heroIds) {
+ if (!Array.isArray(heroIds)) {
+ return [];
+ }
+ return [...new Set(
+ heroIds.map((id) => Number(id)).filter((id) => Number.isFinite(id) && id > 0)
+ )];
+}
+
+function parseComboKey(comboKey) {
+ if (!comboKey || typeof comboKey !== 'string') {
+ return { heroIds: [], pet: 0, banner: 0 };
+ }
+ const [heroesPart = '', petPart = '0', bannerPart = '0'] = comboKey.split('|');
+ const heroIds = heroesPart
+ .split(',')
+ .map((id) => Number(id))
+ .filter((id) => id > 0 && id < 6000);
+ return {
+ heroIds,
+ pet: Number(petPart) || 0,
+ banner: Number(bannerPart) || 0,
+ };
+}
+
+function buildOpponentSetMatchClauses({ heroIds, pet, banner, params, alias = 'oc' }) {
+ const heroes = normalizeHeroFilterIds(heroIds).filter((id) => id < 6000);
+ if (heroes.length !== 5) {
+ return { clauses: [], valid: false };
+ }
+
+ const clauses = [];
+ params.push(heroes);
+ clauses.push(`${alias}.hero_ids @> $${params.length}::int[]`);
+ clauses.push(`cardinality(${alias}.hero_ids) = 5`);
+
+ const petValue = Number(pet) || 0;
+ if (petValue > 0) {
+ params.push(petValue);
+ clauses.push(`${alias}.pet = $${params.length}`);
+ }
+
+ const bannerValue = Number(banner) || 0;
+ if (bannerValue > 0) {
+ params.push(bannerValue);
+ clauses.push(`${alias}.banner = $${params.length}`);
+ }
+
+ return { clauses, valid: true };
+}
+
+function appendComboHeroFilterClauses({ heroIds, heroColumn, petColumn, params }) {
+ const ids = normalizeHeroFilterIds(heroIds);
+ if (!ids.length) {
+ return [];
+ }
+
+ const clauses = [];
+ const heroes = ids.filter((id) => id < 6000);
+ const pets = ids.filter((id) => id >= 6000);
+
+ if (heroes.length) {
+ params.push(heroes);
+ clauses.push(`${heroColumn} @> $${params.length}::int[]`);
+ }
+ for (const petId of pets) {
+ params.push(petId);
+ clauses.push(`${petColumn} = $${params.length}`);
+ }
+ return clauses;
+}
+
+function buildTrainingResultWhere({ comboKey, opponentHeroIds, myHeroIds, testerUserId, params }) {
+ const clauses = [];
+ if (comboKey) {
+ params.push(comboKey);
+ clauses.push(`oc.combo_key = $${params.length}`);
+ }
+ if (testerUserId != null && testerUserId !== '') {
+ if (String(testerUserId) === '0') {
+ clauses.push(`COALESCE(mt.tester_user_id, '0') = '0' AND COALESCE(mt.max_upgrade, TRUE) = TRUE`);
+ } else {
+ params.push(String(testerUserId));
+ clauses.push(`mt.tester_user_id = $${params.length}`);
+ }
+ }
+ clauses.push(...appendComboHeroFilterClauses({
+ heroIds: opponentHeroIds,
+ heroColumn: 'oc.hero_ids',
+ petColumn: 'oc.pet',
+ params,
+ }));
+ clauses.push(...appendComboHeroFilterClauses({
+ heroIds: myHeroIds,
+ heroColumn: 'mt.my_hero_ids',
+ petColumn: 'mt.my_pet',
+ params,
+ }));
+ return clauses.length ? `WHERE ${clauses.join(' AND ')}` : '';
+}
+
+export async function getTrainingTesters() {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const result = await pool.query(
+ `SELECT
+ COALESCE(tester_user_id, '0') AS tester_user_id,
+ COALESCE(
+ NULLIF(tester_name, ''),
+ CASE WHEN COALESCE(max_upgrade, TRUE) THEN 'maxHeros' ELSE 'Unknown user' END
+ ) AS tester_name,
+ COALESCE(max_upgrade, TRUE) AS max_upgrade,
+ COUNT(*)::int AS test_count,
+ MAX(tested_at) AS last_tested_at
+ FROM matchup_tests
+ GROUP BY 1, 2, 3
+ ORDER BY max_upgrade DESC, test_count DESC, tester_name ASC`
+ );
+
+ return result.rows.map((row) => ({
+ testerUserId: row.tester_user_id,
+ testerName: row.tester_name,
+ maxUpgrade: row.max_upgrade,
+ testCount: row.test_count,
+ lastTestedAt: row.last_tested_at,
+ }));
+}
+
+async function queryMyComboStats({ comboKey, opponentHeroIds, myHeroIds, testerUserId, minWinRate, limit } = {}) {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const params = [];
+ const where = buildTrainingResultWhere({ comboKey, opponentHeroIds, myHeroIds, testerUserId, params });
+ params.push(minWinRate);
+ const minWinRateParam = `$${params.length}`;
+
+ let limitClause = '';
+ if (limit != null && limit > 0) {
+ params.push(limit);
+ limitClause = `LIMIT $${params.length}`;
+ }
+
+ const result = await pool.query(
+ `SELECT
+ mt.my_hero_ids,
+ (array_agg(mt.my_hero_names ORDER BY mt.tested_at DESC NULLS LAST))[1] AS my_hero_names,
+ mt.my_pet,
+ COUNT(*)::int AS test_count,
+ ROUND(AVG(mt.win_rate)::numeric, 1) AS avg_win_rate,
+ COUNT(*) FILTER (WHERE mt.win_rate >= ${minWinRateParam})::int AS high_win_count,
+ ROUND(MAX(mt.win_rate)::numeric, 1) AS best_win_rate
+ FROM matchup_tests mt
+ JOIN opponent_combos oc ON oc.id = mt.opponent_combo_id
+ ${where}
+ GROUP BY mt.my_hero_ids, mt.my_pet
+ ORDER BY high_win_count DESC, test_count DESC, avg_win_rate DESC
+ ${limitClause}`,
+ params
+ );
+
+ return result.rows.map((row) => ({
+ myHeroIds: row.my_hero_ids,
+ myHeroNames: row.my_hero_names,
+ myPet: row.my_pet,
+ testCount: row.test_count,
+ avgWinRate: row.avg_win_rate != null ? Number(row.avg_win_rate) : null,
+ highWinCount: row.high_win_count,
+ bestWinRate: row.best_win_rate != null ? Number(row.best_win_rate) : null,
+ }));
+}
+
+export async function getTrainingResultCount({ comboKey, opponentHeroIds, myHeroIds, testerUserId } = {}) {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const params = [];
+ const where = buildTrainingResultWhere({ comboKey, opponentHeroIds, myHeroIds, testerUserId, params });
+
+ const result = await pool.query(
+ `SELECT COUNT(*)::int AS count
+ FROM matchup_tests mt
+ JOIN opponent_combos oc ON oc.id = mt.opponent_combo_id
+ ${where}`,
+ params
+ );
+
+ return result.rows[0]?.count || 0;
+}
+
+export async function getTrainingResults({
+ limit,
+ offset = 0,
+ comboKey,
+ opponentHeroIds,
+ myHeroIds,
+ testerUserId,
+} = {}) {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const params = [];
+ const where = buildTrainingResultWhere({ comboKey, opponentHeroIds, myHeroIds, testerUserId, params });
+
+ let paging = '';
+ if (offset > 0) {
+ params.push(offset);
+ paging += ` OFFSET $${params.length}`;
+ }
+ if (limit != null && limit > 0) {
+ params.push(limit);
+ paging += ` LIMIT $${params.length}`;
+ }
+
+ const result = await pool.query(
+ `SELECT
+ oc.combo_key,
+ oc.hero_ids AS opponent_hero_ids,
+ oc.pet AS opponent_pet,
+ oc.opponent_name,
+ oc.opponent_place,
+ oc.opponent_power,
+ mt.my_hero_ids,
+ mt.my_hero_names,
+ mt.my_pet,
+ mt.win_rate,
+ mt.wins,
+ mt.losses,
+ mt.rank,
+ mt.tested_at,
+ mt.session_id,
+ mt.tester_user_id,
+ mt.tester_name,
+ mt.max_upgrade
+ FROM matchup_tests mt
+ JOIN opponent_combos oc ON oc.id = mt.opponent_combo_id
+ ${where}
+ ORDER BY mt.tested_at DESC NULLS LAST, mt.id DESC
+ ${paging}`,
+ params
+ );
+
+ return result.rows;
+}
+
+export async function getTrainingResultStats({
+ comboKey,
+ opponentHeroIds,
+ myHeroIds,
+ testerUserId,
+ topN = 10,
+ minWinRate = 90,
+ grandArenaMaxResults = 5,
+} = {}) {
+ const [allCombos, topMyCombos, topHeroesResult] = await Promise.all([
+ queryMyComboStats({ comboKey, opponentHeroIds, myHeroIds: [], testerUserId, minWinRate }),
+ queryMyComboStats({ comboKey, opponentHeroIds, myHeroIds, testerUserId, minWinRate, limit: topN }),
+ (async () => {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const params = [];
+ const where = buildTrainingResultWhere({ comboKey, opponentHeroIds, myHeroIds, testerUserId, params });
+ params.push(topN);
+ const topLimit = `$${params.length}`;
+ params.push(minWinRate);
+ const minWinRateParam = `$${params.length}`;
+
+ const result = await pool.query(
+ `WITH filtered AS (
+ SELECT mt.my_hero_ids, mt.my_pet, mt.win_rate
+ FROM matchup_tests mt
+ JOIN opponent_combos oc ON oc.id = mt.opponent_combo_id
+ ${where}
+ ),
+ hero_rows AS (
+ SELECT unnest(my_hero_ids) AS hero_id, win_rate FROM filtered
+ UNION ALL
+ SELECT my_pet AS hero_id, win_rate FROM filtered WHERE my_pet IS NOT NULL
+ )
+ SELECT
+ hero_id,
+ COUNT(*)::int AS appearances,
+ COUNT(*) FILTER (WHERE win_rate >= ${minWinRateParam})::int AS wins_90,
+ ROUND(AVG(win_rate)::numeric, 1) AS avg_win_rate
+ FROM hero_rows
+ WHERE hero_id IS NOT NULL
+ GROUP BY hero_id
+ ORDER BY wins_90 DESC, avg_win_rate DESC, appearances DESC
+ LIMIT ${topLimit}`,
+ params
+ );
+ return result.rows;
+ })(),
+ ]);
+
+ const grandArenaRequiredHeroes = normalizeHeroFilterIds(myHeroIds);
+ const grandArenaAll = findGrandArenaSelections(allCombos, {
+ maxResults: 0,
+ requiredHeroIds: grandArenaRequiredHeroes,
+ });
+ const grandArenaSelections = grandArenaMaxResults > 0
+ ? grandArenaAll.slice(0, grandArenaMaxResults)
+ : grandArenaAll;
+
+ return {
+ topMyCombos: topMyCombos,
+ topMyHeroes: topHeroesResult.map((row) => ({
+ heroId: row.hero_id,
+ appearances: row.appearances,
+ wins90: row.wins_90,
+ avgWinRate: row.avg_win_rate != null ? Number(row.avg_win_rate) : null,
+ })),
+ grandArenaSelections,
+ grandArenaSelectionCount: grandArenaAll.length,
+ grandArenaShownCount: grandArenaSelections.length,
+ comboPoolSize: allCombos.length,
+ grandArenaRequiredHeroes,
+ minWinRate,
+ };
+}
+
+export async function getOpponentSkipCheck({
+ comboKey,
+ opponentHeroIds,
+ minWinRate = 90,
+ maxAgeDays = 30,
+} = {}) {
+ if (!comboKey) {
+ return { shouldSkip: false, reason: 'missing_combo_key' };
+ }
+
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const parsed = parseComboKey(comboKey);
+ const explicitHeroIds = normalizeHeroFilterIds(opponentHeroIds).filter((id) => id < 6000);
+ const matchHeroIds = explicitHeroIds.length === 5 ? explicitHeroIds : parsed.heroIds;
+ const params = [];
+ const { clauses, valid } = buildOpponentSetMatchClauses({
+ heroIds: matchHeroIds,
+ pet: parsed.pet,
+ banner: parsed.banner,
+ params,
+ });
+ const opponentWhere = valid
+ ? clauses.join(' AND ')
+ : (() => {
+ params.push(comboKey);
+ return `oc.combo_key = $${params.length}`;
+ })();
+
+ params.push(minWinRate);
+ const minWinRateParam = `$${params.length}`;
+ params.push(String(maxAgeDays));
+ const maxAgeParam = `$${params.length}`;
+
+ const result = await pool.query(
+ `SELECT
+ oc.combo_key,
+ oc.opponent_name,
+ MAX(mt.win_rate) AS best_win_rate,
+ MAX(mt.tested_at) AS last_tested_at,
+ (
+ SELECT json_build_object(
+ 'myHeroIds', best.my_hero_ids,
+ 'myHeroNames', best.my_hero_names,
+ 'myPet', best.my_pet,
+ 'winRate', best.win_rate,
+ 'testedAt', best.tested_at
+ )
+ FROM matchup_tests best
+ WHERE best.opponent_combo_id = oc.id
+ AND best.win_rate >= ${minWinRateParam}
+ AND best.tested_at >= NOW() - (${maxAgeParam}::text || ' days')::interval
+ AND COALESCE(best.max_upgrade, TRUE) = TRUE
+ ORDER BY best.win_rate DESC, best.tested_at DESC
+ LIMIT 1
+ ) AS best_match
+ FROM opponent_combos oc
+ JOIN matchup_tests mt ON mt.opponent_combo_id = oc.id
+ WHERE ${opponentWhere}
+ AND mt.win_rate >= ${minWinRateParam}
+ AND mt.tested_at >= NOW() - (${maxAgeParam}::text || ' days')::interval
+ AND COALESCE(mt.max_upgrade, TRUE) = TRUE
+ GROUP BY oc.id, oc.combo_key, oc.opponent_name`,
+ params
+ );
+
+ const row = result.rows[0];
+ if (!row?.best_match) {
+ return {
+ shouldSkip: false,
+ comboKey,
+ minWinRate,
+ maxAgeDays,
+ };
+ }
+
+ return {
+ shouldSkip: true,
+ comboKey: row.combo_key,
+ opponentName: row.opponent_name,
+ bestWinRate: row.best_win_rate != null ? Number(row.best_win_rate) : null,
+ lastTestedAt: row.last_tested_at,
+ bestMatch: row.best_match,
+ minWinRate,
+ maxAgeDays,
+ };
+}
+
+export async function getUserCounterSkipCheck({
+ comboKey,
+ testerUserId,
+ myHeroIds,
+ myPet,
+ maxAgeDays = 30,
+} = {}) {
+ if (!comboKey) {
+ return { shouldSkip: false, reason: 'missing_combo_key' };
+ }
+ if (!testerUserId) {
+ return { shouldSkip: false, reason: 'missing_tester_user_id' };
+ }
+
+ const heroIds = (Array.isArray(myHeroIds) ? myHeroIds : [])
+ .map(Number)
+ .filter((id) => id > 0 && id < 6000);
+ const pet = myPet != null ? Number(myPet) : null;
+
+ if (heroIds.length !== 5 || !pet) {
+ return { shouldSkip: false, reason: 'missing_counter_lineup' };
+ }
+
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const parsed = parseComboKey(comboKey);
+ const params = [];
+ const { clauses, valid } = buildOpponentSetMatchClauses({
+ heroIds: parsed.heroIds,
+ pet: parsed.pet,
+ banner: parsed.banner,
+ params,
+ });
+ const opponentWhere = valid
+ ? clauses.join(' AND ')
+ : (() => {
+ params.push(comboKey);
+ return `oc.combo_key = $${params.length}`;
+ })();
+
+ params.push(String(testerUserId));
+ const testerParam = `$${params.length}`;
+ params.push(heroIds);
+ const myHeroesParam = `$${params.length}`;
+ params.push(pet);
+ const myPetParam = `$${params.length}`;
+ params.push(String(maxAgeDays));
+ const maxAgeParam = `$${params.length}`;
+
+ const result = await pool.query(
+ `SELECT
+ mt.my_hero_ids,
+ mt.my_hero_names,
+ mt.my_pet,
+ mt.win_rate,
+ mt.wins,
+ mt.losses,
+ mt.tested_at,
+ mt.tester_user_id,
+ mt.tester_name
+ FROM matchup_tests mt
+ JOIN opponent_combos oc ON oc.id = mt.opponent_combo_id
+ WHERE ${opponentWhere}
+ AND mt.tester_user_id = ${testerParam}
+ AND COALESCE(mt.max_upgrade, TRUE) = FALSE
+ AND mt.my_hero_ids = ${myHeroesParam}::int[]
+ AND mt.my_pet = ${myPetParam}
+ AND mt.tested_at >= NOW() - (${maxAgeParam}::text || ' days')::interval
+ ORDER BY mt.tested_at DESC, mt.id DESC
+ LIMIT 1`,
+ params
+ );
+
+ const row = result.rows[0];
+ if (!row) {
+ return {
+ shouldSkip: false,
+ comboKey,
+ testerUserId: String(testerUserId),
+ maxAgeDays,
+ };
+ }
+
+ return {
+ shouldSkip: true,
+ comboKey,
+ testerUserId: row.tester_user_id,
+ testerName: row.tester_name,
+ cachedWinRate: row.win_rate != null ? Number(row.win_rate) : null,
+ cachedWins: row.wins != null ? Number(row.wins) : null,
+ cachedLosses: row.losses != null ? Number(row.losses) : null,
+ lastTestedAt: row.tested_at,
+ cachedMatch: {
+ myHeroIds: row.my_hero_ids,
+ myHeroNames: row.my_hero_names,
+ myPet: row.my_pet,
+ winRate: row.win_rate != null ? Number(row.win_rate) : null,
+ wins: row.wins != null ? Number(row.wins) : null,
+ losses: row.losses != null ? Number(row.losses) : null,
+ testedAt: row.tested_at,
+ },
+ maxAgeDays,
+ };
+}
+
+export async function getMatchups({ comboKey, limit = 50 } = {}) {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ if (comboKey) {
+ const result = await pool.query(
+ `SELECT
+ oc.combo_key,
+ oc.hero_ids AS opponent_hero_ids,
+ oc.pet AS opponent_pet,
+ oc.banner AS opponent_banner,
+ oc.opponent_name,
+ oc.opponent_place,
+ oc.opponent_power,
+ oc.last_seen_at,
+ COALESCE(
+ json_agg(
+ json_build_object(
+ 'myHeroIds', mt.my_hero_ids,
+ 'myHeroNames', mt.my_hero_names,
+ 'myPet', mt.my_pet,
+ 'winRate', mt.win_rate,
+ 'wins', mt.wins,
+ 'losses', mt.losses,
+ 'rank', mt.rank,
+ 'testedAt', mt.tested_at,
+ 'sessionId', mt.session_id
+ )
+ ORDER BY mt.win_rate DESC, mt.tested_at DESC
+ ) FILTER (WHERE mt.id IS NOT NULL),
+ '[]'::json
+ ) AS tests
+ FROM opponent_combos oc
+ LEFT JOIN matchup_tests mt ON mt.opponent_combo_id = oc.id
+ WHERE oc.combo_key = $1
+ GROUP BY oc.id`,
+ [comboKey]
+ );
+ return result.rows[0] || null;
+ }
+
+ const result = await pool.query(
+ `SELECT
+ oc.combo_key,
+ oc.hero_ids AS opponent_hero_ids,
+ oc.pet AS opponent_pet,
+ oc.banner AS opponent_banner,
+ oc.opponent_name,
+ oc.opponent_place,
+ oc.opponent_power,
+ oc.last_seen_at,
+ COUNT(mt.id)::int AS test_count,
+ MAX(mt.tested_at) AS last_tested_at,
+ MAX(mt.win_rate) AS best_win_rate
+ FROM opponent_combos oc
+ LEFT JOIN matchup_tests mt ON mt.opponent_combo_id = oc.id
+ GROUP BY oc.id
+ ORDER BY oc.last_seen_at DESC NULLS LAST, oc.id DESC
+ LIMIT $1`,
+ [limit]
+ );
+
+ return result.rows.map((row) => ({
+ comboKey: row.combo_key,
+ opponentHeroIds: row.opponent_hero_ids,
+ opponentPet: row.opponent_pet,
+ opponentBanner: row.opponent_banner,
+ opponentName: row.opponent_name,
+ opponentPlace: row.opponent_place,
+ opponentPower: row.opponent_power != null ? Number(row.opponent_power) : null,
+ lastSeenAt: row.last_seen_at,
+ testCount: row.test_count,
+ lastTestedAt: row.last_tested_at,
+ bestWinRate: row.best_win_rate != null ? Number(row.best_win_rate) : null,
+ }));
+}
+
+export async function getMetaTeamSnapshots({ limit = 20 } = {}) {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const result = await pool.query(
+ `SELECT
+ id,
+ captured_at,
+ source,
+ source_url,
+ position_max,
+ pages_scraped,
+ total_teams,
+ unique_combos,
+ notes
+ FROM meta_team_snapshots
+ ORDER BY captured_at DESC
+ LIMIT $1`,
+ [limit]
+ );
+
+ return result.rows.map((row) => ({
+ id: row.id,
+ capturedAt: row.captured_at,
+ source: row.source,
+ sourceUrl: row.source_url,
+ positionMax: row.position_max,
+ pagesScraped: row.pages_scraped,
+ totalTeams: row.total_teams,
+ uniqueCombos: row.unique_combos,
+ notes: row.notes,
+ }));
+}
+
+export async function getMetaTeamSnapshotById(snapshotId) {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const result = await pool.query(
+ `SELECT
+ id,
+ captured_at,
+ source,
+ source_url,
+ position_max,
+ pages_scraped,
+ total_teams,
+ unique_combos,
+ notes
+ FROM meta_team_snapshots
+ WHERE id = $1`,
+ [snapshotId]
+ );
+
+ const row = result.rows[0];
+ if (!row) return null;
+
+ return {
+ id: row.id,
+ capturedAt: row.captured_at,
+ source: row.source,
+ sourceUrl: row.source_url,
+ positionMax: row.position_max,
+ pagesScraped: row.pages_scraped,
+ totalTeams: row.total_teams,
+ uniqueCombos: row.unique_combos,
+ notes: row.notes,
+ };
+}
+
+export async function getMetaTeamCountForSnapshot(snapshotId) {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const result = await pool.query(
+ 'SELECT COUNT(*)::int AS count FROM meta_teams WHERE snapshot_id = $1',
+ [snapshotId]
+ );
+ return result.rows[0]?.count || 0;
+}
+
+export async function getMetaTeamsForSnapshot(snapshotId, { limit, offset = 0 } = {}) {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const params = [snapshotId];
+ let paging = '';
+ if (offset > 0) {
+ params.push(offset);
+ paging += ` OFFSET $${params.length}`;
+ }
+ if (limit != null && limit > 0) {
+ params.push(limit);
+ paging += ` LIMIT $${params.length}`;
+ }
+
+ const result = await pool.query(
+ `SELECT
+ combo_key,
+ hero_ids,
+ hero_names,
+ pet,
+ pet_name,
+ banner,
+ popularity_count,
+ row_rank,
+ page_number
+ FROM meta_teams
+ WHERE snapshot_id = $1
+ ORDER BY row_rank ASC NULLS LAST, popularity_count DESC NULLS LAST
+ ${paging}`,
+ params
+ );
+
+ return result.rows.map((row) => ({
+ comboKey: row.combo_key,
+ heroIds: row.hero_ids,
+ heroNames: row.hero_names,
+ pet: row.pet,
+ petName: row.pet_name,
+ banner: row.banner,
+ popularityCount: row.popularity_count,
+ rowRank: row.row_rank,
+ pageNumber: row.page_number,
+ }));
+}
+
+export async function getMetaTeamCandidates({ snapshotId, limit } = {}) {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ let resolvedSnapshotId = snapshotId;
+ if (!resolvedSnapshotId) {
+ const latest = await pool.query(
+ `SELECT id FROM meta_team_snapshots ORDER BY captured_at DESC NULLS LAST, id DESC LIMIT 1`
+ );
+ resolvedSnapshotId = latest.rows[0]?.id;
+ if (!resolvedSnapshotId) {
+ return { snapshotId: null, candidates: [] };
+ }
+ }
+
+ const params = [resolvedSnapshotId];
+ let limitClause = '';
+ if (limit != null && limit > 0) {
+ params.push(limit);
+ limitClause = ` LIMIT $${params.length}`;
+ }
+
+ const result = await pool.query(
+ `SELECT
+ combo_key,
+ hero_ids,
+ hero_names,
+ pet,
+ pet_name,
+ banner,
+ popularity_count,
+ row_rank
+ FROM (
+ SELECT
+ combo_key,
+ hero_ids,
+ hero_names,
+ pet,
+ pet_name,
+ banner,
+ popularity_count,
+ row_rank,
+ ROW_NUMBER() OVER (
+ PARTITION BY combo_key
+ ORDER BY popularity_count DESC NULLS LAST, row_rank ASC NULLS LAST
+ ) AS dedupe_rank
+ FROM meta_teams
+ WHERE snapshot_id = $1
+ ) ranked
+ WHERE dedupe_rank = 1
+ ORDER BY popularity_count DESC NULLS LAST, row_rank ASC NULLS LAST
+ ${limitClause}`,
+ params
+ );
+
+ return {
+ snapshotId: resolvedSnapshotId,
+ candidates: result.rows.map((row) => ({
+ comboKey: row.combo_key,
+ heroIds: row.hero_ids,
+ heroNames: row.hero_names,
+ pet: row.pet,
+ petName: row.pet_name,
+ banner: row.banner,
+ popularityCount: row.popularity_count,
+ rowRank: row.row_rank,
+ })),
+ };
+}
+
+export async function backfillMatchupsFromRounds() {
+ if (!pool) {
+ pool = new Pool({ connectionString: getDatabaseUrl() });
+ }
+
+ const legacy = await pool.query(
+ `SELECT session_id, payload, completed_at
+ FROM training_rounds
+ WHERE payload IS NOT NULL
+ ORDER BY id`
+ );
+
+ let imported = 0;
+ for (const row of legacy.rows) {
+ const body = typeof row.payload === 'string' ? JSON.parse(row.payload) : row.payload;
+ if (!body?.rankings?.length || !body?.opponent?.team?.heroes?.length) continue;
+ body.sessionId = body.sessionId || row.session_id;
+ body.completedAt = body.completedAt || row.completed_at;
+ await saveTrainingRound(body);
+ imported++;
+ }
+
+ return { importedFromPayload: imported };
+}
+
+export async function backfillMatchupsFromJsonDir() {
+ const fs = await import('fs/promises');
+ const path = await import('path');
+ const { fileURLToPath } = await import('url');
+ const dir = path.join(path.dirname(fileURLToPath(import.meta.url)), 'arena-training-results');
+
+ let imported = 0;
+ try {
+ const files = (await fs.readdir(dir)).filter((f) => f.endsWith('.json'));
+ for (const file of files) {
+ const raw = await fs.readFile(path.join(dir, file), 'utf8');
+ const body = JSON.parse(raw);
+ if (!body?.rankings?.length || !body?.opponent?.team?.heroes?.length) continue;
+ await saveTrainingRound(body);
+ imported++;
+ }
+ } catch {
+ // directory may not exist
+ }
+ return { importedFromJson: imported };
+}
+
+export async function backfillAllMatchups() {
+ const fromPayload = await backfillMatchupsFromRounds();
+ const fromJson = await backfillMatchupsFromJsonDir();
+ return { ...fromPayload, ...fromJson };
+}
+
+export async function closeDatabase() {
+ if (pool) {
+ await pool.end();
+ pool = null;
+ }
+ ready = false;
+}
diff --git a/training-view.mjs b/training-view.mjs
new file mode 100644
index 0000000..a5be80c
--- /dev/null
+++ b/training-view.mjs
@@ -0,0 +1,1190 @@
+import { formatCombo, HERO_NAMES, PET_NAMES, resolveHeroName } from './hero-names.mjs';
+import { HERO_ICON_TOOLTIP_CSS, iconUrlForUnit, renderComboLabelHtml, renderHeroListHtml, renderUnitNameHtml } from './hero-icons.mjs';
+
+export function parseHeroFilterParams(searchParams, key) {
+ const values = searchParams.getAll(key);
+ const ids = values
+ .map((value) => Number(value))
+ .filter((id) => Number.isFinite(id) && id > 0);
+ return [...new Set(ids)];
+}
+
+function buildHeroFilterOptions(selectedIds = []) {
+ const selected = new Set(selectedIds.map(Number));
+ const options = [];
+ for (const [id, name] of Object.entries(HERO_NAMES)) {
+ const heroId = Number(id);
+ if (!selected.has(heroId)) {
+ options.push({ id: heroId, label: name, group: 'Heroes' });
+ }
+ }
+ for (const [id, name] of Object.entries(PET_NAMES)) {
+ const petId = Number(id);
+ if (!selected.has(petId)) {
+ options.push({ id: petId, label: name, group: 'Pets' });
+ }
+ }
+ options.sort((a, b) => a.label.localeCompare(b.label));
+ return options;
+}
+
+function buildTrainingQueryBase(paging = {}) {
+ const parts = [];
+ if (paging.comboKey) {
+ parts.push(`comboKey=${encodeURIComponent(paging.comboKey)}`);
+ }
+ if (paging.testerUserId) {
+ parts.push(`tester=${encodeURIComponent(paging.testerUserId)}`);
+ }
+ for (const id of paging.opponentHeroIds || []) {
+ parts.push(`opponentHero=${encodeURIComponent(id)}`);
+ }
+ for (const id of paging.myHeroIds || []) {
+ parts.push(`myHero=${encodeURIComponent(id)}`);
+ }
+ return parts.length ? `${parts.join('&')}&` : '';
+}
+
+function formatTesterLabel(tester) {
+ if (!tester) return '—';
+ const name = tester.testerName
+ || (tester.maxUpgrade !== false ? 'maxHeros' : null)
+ || (tester.testerUserId ? `User ${tester.testerUserId}` : '—');
+ if (tester.maxUpgrade !== false && name === 'maxHeros') {
+ return 'maxHeros';
+ }
+ return name;
+}
+
+function renderTesterFilter(paging = {}, testers = []) {
+ const selected = paging.testerUserId ? String(paging.testerUserId) : '';
+ const options = testers.map((tester) => {
+ const value = String(tester.testerUserId ?? '');
+ const label = formatTesterLabel(tester);
+ const mode = tester.maxUpgrade !== false ? 'max upgrade' : 'user team';
+ const selectedAttr = selected === value ? ' selected' : '';
+ return `${escapeHtml(label)} (${mode}) · ${tester.testCount ?? 0} tests `;
+ }).join('');
+
+ return `
+
+
Tester account
+
Show only tests from a specific account — maxHeros for max-upgrade sims, or a player name for real-team tests.
+
+
+ All testers
+ ${options}
+
+ ${selected ? 'Clear ' : ''}
+
+
`;
+}
+
+function renderHeroFilterChips(side, heroIds) {
+ return (heroIds || []).map((id) => {
+ const label = resolveHeroName(id);
+ return `${renderUnitNameHtml(id, label)} × `;
+ }).join('');
+}
+
+function renderFilterPickerOption(option) {
+ const iconUrl = iconUrlForUnit(option.id);
+ const iconHtml = iconUrl
+ ? ` `
+ : ' ';
+ return `${iconHtml}${escapeHtml(option.label)} `;
+}
+
+function renderHeroFilterSelect(side, heroIds) {
+ const options = buildHeroFilterOptions(heroIds);
+ const heroes = options.filter((option) => option.group === 'Heroes');
+ const pets = options.filter((option) => option.group === 'Pets');
+ const sideLabel = side === 'opponent' ? 'opponent' : 'your team';
+
+ return `
+
+
+
+ Choose a hero or pet…
+ ▾
+
+
+
`;
+}
+
+function renderHeroFilters(paging = {}) {
+ const opponentHeroIds = paging.opponentHeroIds || [];
+ const myHeroIds = paging.myHeroIds || [];
+ const testers = paging.testers || [];
+ const hasFilters = opponentHeroIds.length > 0 || myHeroIds.length > 0 || !!paging.testerUserId;
+
+ return `
+
+
+
Filter results
+
Pick a tester account and/or heroes to narrow what you see. Everything below updates to match.
+
+
+ ${renderTesterFilter(paging, testers)}
+
+
Opponent team includes
+
Show opponents whose lineup has all of these heroes or pets.
+
+ ${renderHeroFilterChips('opponent', opponentHeroIds) || 'No filter — any opponent '}
+
+
+ ${renderHeroFilterSelect('opponent', opponentHeroIds)}
+ Add
+ ${opponentHeroIds.length ? `Clear all ` : ''}
+
+
+
+
Your counter team includes
+
Show only your teams that have all of these heroes or pets. For Grand Arena below, each selected hero must appear on one of the 3 teams (15 unique heroes total).
+
+ ${renderHeroFilterChips('my', myHeroIds) || 'No filter — any of your teams '}
+
+
+ ${renderHeroFilterSelect('my', myHeroIds)}
+ Add
+ ${myHeroIds.length ? `Clear all ` : ''}
+
+
+ ${hasFilters ? '
Filters on — you are viewing a subset of saved tests.
' : ''}
+
+ `;
+}
+
+function escapeHtml(value) {
+ return String(value ?? '')
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"');
+}
+
+function formatPct(winRate) {
+ if (winRate == null) return '—';
+ return `${Number(winRate).toFixed(1)}%`;
+}
+
+function formatWhen(iso) {
+ if (!iso) return '—';
+ try {
+ return new Date(iso).toLocaleString();
+ } catch {
+ return String(iso);
+ }
+}
+
+function formatRecord(wins, losses) {
+ if (wins == null && losses == null) return '—';
+ return `${wins ?? 0}W / ${losses ?? 0}L`;
+}
+
+function winRateClass(winRate, minWinRate = 90) {
+ if (winRate == null) return '';
+ if (winRate >= minWinRate) return 'good';
+ if (winRate >= 50) return 'mid';
+ return 'bad';
+}
+
+function renderWinRateCell(winRate, minWinRate = 90) {
+ const cls = winRateClass(winRate, minWinRate);
+ const solved = winRate != null && winRate >= minWinRate
+ ? `Strong counter `
+ : '';
+ return `${escapeHtml(formatPct(winRate))} ${solved}`;
+}
+
+function renderPageIntro(minWinRate = 90) {
+ return `
+
+ How to use this page
+
+ Hero Wars Helper’s Arena Training runs practice battles (no arena attempts spent).
+ It tries to find a team that beats each opponent at least ${minWinRate}% of the time, then saves the results here.
+
+
+ Run training in HWH — start Arena Training or the loop; it checks arena opponents, then popular meta teams.
+ Filter (optional) — focus on specific heroes on either side; all sections below follow your filters.
+ Pick teams — use the best-team tables and Grand Arena section; scroll to the bottom for every individual test.
+
+ `;
+}
+
+function renderPageNav() {
+ return `
+
+ Jump to:
+ Filters
+ Best teams
+ Grand Arena
+ Full log
+ `;
+}
+
+function renderSummaryFooter(summary = {}, paging = {}) {
+ const minWinRate = paging.stats?.minWinRate ?? 90;
+ const filteredTotal = paging.total ?? 0;
+ const latest = summary.latestSummary;
+ const latestLine = latest
+ ? `${escapeHtml(formatPct(latest.winRate))} vs ${escapeHtml(latest.opponentName || 'opponent')} · ${escapeHtml(formatWhen(latest.testedAt))}`
+ : 'No tests saved yet';
+ const filteredHighlight = filteredTotal !== summary.matchupTestCount ? ' footer-stat-highlight' : '';
+
+ return `
+ `;
+}
+
+function renderDevLinks() {
+ return `
+
+ API & raw data
+
+ All results (JSON) ·
+ Summary (JSON) ·
+ Meta teams
+
+ `;
+}
+
+function renderTrainingStats(stats = {}) {
+ const minWinRate = stats.minWinRate ?? 90;
+ const topCombos = stats.topMyCombos || [];
+ const topHeroes = stats.topMyHeroes || [];
+
+ const comboRows = topCombos.map((row, index) => {
+ return `
+
+ ${index + 1}
+ ${renderComboLabelHtml(row.myHeroIds, row.myPet, row.myHeroNames)}
+ ${escapeHtml(formatPct(row.avgWinRate))}
+ ${escapeHtml(formatPct(row.bestWinRate))}
+ ${row.highWinCount ?? 0}
+ ${row.testCount ?? 0}
+ `;
+ }).join('');
+
+ const heroRows = topHeroes.map((row, index) => {
+ const label = resolveHeroName(row.heroId);
+ const isPet = Number(row.heroId) >= 6000;
+ return `
+
+ ${index + 1}
+ ${renderUnitNameHtml(row.heroId, label)}${isPet ? ' (pet) ' : ''}
+ ${row.wins90 ?? 0}
+ ${escapeHtml(formatPct(row.avgWinRate))}
+ ${row.appearances ?? 0}
+ `;
+ }).join('');
+
+ return `
+
+
+
Your best counters
+
Teams and heroes that win most often against the opponents you filtered. A strong counter means ${minWinRate}%+ win rate in simulations.
+
+
+
+
Top 10 full teams
+
Best 5-hero + pet lineups, ranked by how often they reach ${minWinRate}%+.
+
+
+
+
+ #
+ Your team
+ Avg win %
+ Best win %
+ Times ${minWinRate}%+
+ Tests
+
+
+
+ ${comboRows || `No data yet — run Arena Training in HWH first. `}
+
+
+
+
+
+
Top 10 heroes & pets
+
Who shows up most in your ${minWinRate}%+ winning lineups.
+
+
+
+
+ #
+ Hero / pet
+ ${minWinRate}%+ wins
+ Avg win %
+ Lineups
+
+
+
+ ${heroRows || `No data yet. `}
+
+
+
+
+
+ `;
+}
+
+function renderGrandArenaTeamCell(team, minWinRate) {
+ return `
+
+
${renderComboLabelHtml(team.myHeroIds, team.myPet, team.myHeroNames)}
+
${minWinRate}%+ wins: ${team.highWinCount ?? 0} · avg ${escapeHtml(formatPct(team.avgWinRate))}
+
`;
+}
+
+function renderGrandArenaSelections(stats = {}) {
+ const minWinRate = stats.minWinRate ?? 90;
+ const selections = stats.grandArenaSelections || [];
+ const totalFound = stats.grandArenaSelectionCount ?? selections.length;
+ const shownCount = stats.grandArenaShownCount ?? selections.length;
+ const poolSize = stats.comboPoolSize ?? 0;
+ const myHeroFilterNote = (stats.grandArenaRequiredHeroes || []).length
+ ? 'With your hero filter on, each selected hero must appear on exactly one of the 3 teams below.'
+ : '';
+
+ const rows = selections.map((selection, index) => {
+ const [team1, team2, team3] = selection.teams;
+ return `
+
+ ${index + 1}
+ ${renderGrandArenaTeamCell(team1, minWinRate)}
+ ${renderGrandArenaTeamCell(team2, minWinRate)}
+ ${renderGrandArenaTeamCell(team3, minWinRate)}
+ ${selection.totalHighWinCount}
+ ${selection.minHighWinCount}
+ `;
+ }).join('');
+
+ return `
+
+
+
Grand Arena — suggested defense
+
+ Three teams built from your best counters. Each hero is used once across all teams (15 heroes total); pets can repeat.
+ ${totalFound ? `Found ${totalFound} valid lineup${totalFound === 1 ? '' : 's'} from ${poolSize} strong teams${shownCount < totalFound ? ` — top ${shownCount} shown` : ''}.` : ''}
+ ${myHeroFilterNote ? `${myHeroFilterNote}` : ''}
+
+
+
+
+
+
+
+ #
+ Defense team 1
+ Defense team 2
+ Defense team 3
+ Combined ${minWinRate}%+ wins
+ Lowest team score
+
+
+
+ ${rows || `Not enough ${minWinRate}%+ teams yet — need 15 different heroes across 3 five-hero lineups. `}
+
+
+
+
+ `;
+}
+
+export function renderTrainingResultsPage(results, summary = {}, paging = {}) {
+ const total = paging.total ?? results.length;
+ const offset = paging.offset ?? 0;
+ const pageSize = paging.pageSize ?? results.length;
+ const minWinRate = paging.stats?.minWinRate ?? 90;
+ const shownFrom = total === 0 ? 0 : offset + 1;
+ const shownTo = Math.min(offset + results.length, total);
+ const nextOffset = offset + results.length;
+ const hasMore = nextOffset < total;
+
+ const queryBase = buildTrainingQueryBase(paging);
+ const allLink = `/?${queryBase}limit=0`;
+ const nextLink = `/?${queryBase}offset=${nextOffset}&limit=${pageSize}`;
+
+ const rows = results.map((row) => {
+ const opponentLabel = row.opponentPlayer || 'Unknown';
+ const source = row.opponentPlace ? `#${row.opponentPlace}` : '—';
+ const testerLabel = formatTesterLabel(row);
+ return `
+
+ ${escapeHtml(source)}
+ ${renderComboLabelHtml(row.opponentCombo.heroIds, row.opponentCombo.pet, row.opponentCombo.heroNames)}
+ ${escapeHtml(opponentLabel)}
+ ${escapeHtml(testerLabel)}
+ ${renderComboLabelHtml(row.myCombo.heroIds, row.myCombo.pet, row.myCombo.heroNames)}
+ ${renderWinRateCell(row.winRate, minWinRate)}
+ ${escapeHtml(formatRecord(row.wins, row.losses))}
+ ${escapeHtml(formatWhen(row.testedAt))}
+ `;
+ }).join('');
+
+ return `
+
+
+
+
+ Arena Training Results
+
+
+
+
+
+
+ ${renderPageIntro(minWinRate)}
+ ${renderPageNav()}
+
+ ${renderHeroFilters(paging)}
+ ${renderTrainingStats(paging.stats)}
+ ${renderGrandArenaSelections(paging.stats)}
+
+
+
+
Full test log
+
Every saved simulation — one row per opponent and the team you tested against them. Newest first.
+
+
+
+
+
+
+ List #
+ Opponent team
+ Label
+ Tester
+ Your team
+ Win rate
+ Sim record
+ When
+
+
+
+ ${rows || 'Nothing here yet — run Arena Training in HWH to start collecting results. '}
+
+
+
+ ${hasMore ? `Load more results · Show all ${total}
` : ''}
+
+
+ ${renderSummaryFooter(summary, paging)}
+
+
+
+`;
+}
+
+export function renderMetaTeamsPage(teams, snapshot, snapshots = [], paging = {}) {
+ const total = paging.total ?? teams.length;
+ const offset = paging.offset ?? 0;
+ const pageSize = paging.pageSize ?? teams.length;
+ if (!snapshot) {
+ return `
+Meta Arena Teams
+
+
+Meta Arena Teams
+No snapshots yet. Run python scrape_meta_teams_to_db.py first.
+Training results
+`;
+ }
+
+ const snapshotId = snapshot.id;
+ const shownFrom = total === 0 ? 0 : offset + 1;
+ const shownTo = Math.min(offset + teams.length, total);
+ const nextOffset = offset + teams.length;
+ const hasMore = nextOffset < total;
+
+ const queryBase = `snapshotId=${snapshotId}&`;
+ const nextLink = `/training/meta-view?${queryBase}offset=${nextOffset}&limit=${pageSize}`;
+ const allLink = `/training/meta-view?${queryBase}limit=0`;
+ const startLink = `/training/meta-view?${queryBase}limit=${pageSize}`;
+
+ const snapshotOptions = snapshots.map((s) => {
+ const selected = s.id === snapshotId ? ' selected' : '';
+ const label = `#${s.id} — ${formatWhen(s.capturedAt)} (${s.totalTeams} teams)`;
+ return `${escapeHtml(label)} `;
+ }).join('');
+
+ const rows = teams.map((team) => {
+ const heroLabel = Array.isArray(team.heroNames) && team.heroNames.length
+ ? team.heroNames
+ : (team.heroIds || []).map((id) => resolveHeroName(id));
+ const petLabel = team.petName || (team.pet ? resolveHeroName(team.pet) : '—');
+ return `
+
+ ${escapeHtml(team.rowRank ?? '—')}
+ ${escapeHtml(team.popularityCount ?? '—')}
+ ${renderHeroListHtml(team.heroIds, heroLabel)}
+ ${team.pet ? renderUnitNameHtml(team.pet, petLabel) : escapeHtml(petLabel)}
+ ${escapeHtml(team.banner ?? '—')}
+ ${escapeHtml(team.comboKey)}
+ ${escapeHtml(team.pageNumber ?? '—')}
+
+ `;
+ }).join('');
+
+ return `
+
+
+
+
+ Meta Arena Teams
+
+
+
+ Meta Arena Teams
+
+ Snapshot #${escapeHtml(snapshotId)} · captured ${escapeHtml(formatWhen(snapshot?.capturedAt))} ·
+ ${escapeHtml(snapshot?.source || 'hw-recruit')} · position ≤ ${escapeHtml(snapshot?.positionMax ?? '—')} ·
+ ${escapeHtml(snapshot?.totalTeams ?? 0)} teams (${escapeHtml(snapshot?.uniqueCombos ?? 0)} unique) ·
+ JSON snapshots ·
+ JSON teams ·
+ Training results
+
+
+
+
+
+
+ Rank
+ Count
+ Team
+ Pet
+ Banner
+ Combo key
+ Page
+
+
+
+ ${rows || 'No meta teams in this snapshot. '}
+
+
+
+ ${hasMore ? `Load more · Show all ${total}
` : ''}
+
+`;
+}
+
+export function formatTrainingResultRow(row) {
+ const opponentCombo = formatCombo(row.opponent_hero_ids, row.opponent_pet);
+ const myCombo = formatCombo(
+ row.my_hero_ids,
+ row.my_pet,
+ sanitizeHeroNames(row.my_hero_names)
+ );
+
+ return {
+ opponentComboKey: row.combo_key,
+ opponentCombo,
+ opponentPlayer: row.opponent_name,
+ opponentPlace: row.opponent_place,
+ opponentPower: row.opponent_power != null ? Number(row.opponent_power) : null,
+ myCombo,
+ winRate: row.win_rate != null ? Number(row.win_rate) : null,
+ wins: row.wins != null ? Number(row.wins) : null,
+ losses: row.losses != null ? Number(row.losses) : null,
+ rank: row.rank != null ? Number(row.rank) : null,
+ testedAt: row.tested_at,
+ sessionId: row.session_id,
+ testerUserId: row.tester_user_id != null ? String(row.tester_user_id) : null,
+ testerName: row.tester_name || null,
+ maxUpgrade: row.max_upgrade !== false,
+ };
+}
+
+function sanitizeHeroNames(names) {
+ if (!Array.isArray(names)) return null;
+ const cleaned = names.map((n) => (
+ n && !String(n).startsWith('Hero ') ? n : null
+ ));
+ return cleaned.some(Boolean) ? cleaned : null;
+}