Complex dungeon auto-battle for Hero Wars with titan-specific strategies. REQUIRES HeroWarsHelper installed
// ==UserScript==
// @name HeroWarsDungeonFull
// @name:en HeroWarsDungeonFull
// @name:ru HeroWarsDungeonFull
// @namespace HeroWarsDungeonFull
// @version 1.0
// @description Complex dungeon auto-battle for Hero Wars with titan-specific strategies. REQUIRES HeroWarsHelper installed
// @description:en Complex dungeon auto-battle for Hero Wars with titan-specific strategies. REQUIRES HeroWarsHelper installed
// @description:ru Сложное автопрохождение подземелья для фуловых титанов. ТРЕБУЕТСЯ установленный HeroWarsHelper
// @author Yipol (based on HeroWarsDungeon by ZingerY, ApuoH, Gudwin)
// @license MIT
// @match https://www.hero-wars.com/*
// @match https://apps-1701433570146040.apps.fbsbx.com/*
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
'use strict';
// ========== 轮询等待 hwhelper.js 就绪 ==========
function waitForHWH(callback) {
if (typeof HWHData !== 'undefined' && typeof HWHClasses !== 'undefined') {
callback();
} else {
setTimeout(() => waitForHWH(callback), 200);
}
}
waitForHWH(function() {
// ========== 1. 注入 I18N 标签 ==========
const { i18nLangData } = HWHData;
i18nLangData.en.DUNGEON2 = 'Dungeon full';
i18nLangData.en.DUNGEON_FULL_TITLE = 'Dungeon for Full Titans';
i18nLangData.en.STOP_DUNGEON = 'Stop Dungeon';
i18nLangData.en.STOP_DUNGEON_TITLE = 'Stop digging';
i18nLangData.en.STOP_SCRIPT = 'Stop the';
i18nLangData.en.COMPLETE_DUNGEON_FULL = 'Complete the dungeon for Full Titans';
i18nLangData.ru.DUNGEON2 = 'Подземелье фулл';
i18nLangData.ru.DUNGEON_FULL_TITLE = 'Подземелье для фуловых титанов';
i18nLangData.ru.STOP_DUNGEON = 'Стоп подземка';
i18nLangData.ru.STOP_DUNGEON_TITLE = 'Остановить копание';
i18nLangData.ru.STOP_SCRIPT = 'Остановить скрипт';
i18nLangData.ru.COMPLETE_DUNGEON_FULL = 'Пройти подземелье фулл';
// ========== 2. 全局 stopDung 开关 ==========
window.stopDung = false;
window.stopDungeon = function() { window.stopDung = true; };
// ========== 3. DungeonFull 入口函数 ==========
window.DungeonFull = function() {
const { executeDungeon } = HWHClasses;
return new Promise((resolve, reject) => {
const dung = new executeDungeon(resolve, reject);
const titanit = window.HWHFuncs.getInput('countTitanit');
dung.start(titanit);
});
};
// ========== 4. 注入 UI 按钮 ==========
const I18N = window.HWHFuncs.I18N;
const confShow = window.HWHFuncs.confShow;
// 在 testDungeon 的 combineList 中插入停止按钮
HWHData.buttons.testDungeon.combineList.push({
get name() {
return I18N('STOP_DUNGEON');
},
onClick: function() {
confShow(I18N('STOP_SCRIPT') + ' ' + I18N('STOP_DUNGEON_TITLE') + '?', window.stopDungeon);
},
get title() {
return I18N('STOP_DUNGEON_TITLE');
},
color: 'red',
});
// DungeonFull 按钮(独立 combine 按钮)
HWHData.buttons.DungeonFull = {
isCombine: true,
combineList: [
{
get name() {
return I18N('DUNGEON2');
},
onClick: function() {
confShow(I18N('RUN_SCRIPT') + ' ' + I18N('DUNGEON_FULL_TITLE') + '?', window.DungeonFull);
},
get title() {
return I18N('DUNGEON_FULL_TITLE');
},
},
{
name: '>>',
onClick: cheats.goClanIsland,
get title() {
return I18N('GUILD_ISLAND_TITLE');
},
color: 'green',
},
],
};
// ========== 5. 覆盖 executeDungeon(照搬 executeDungeon2) ==========
{
const send = window.HWHFuncs.send;
const BattleCalc = window.BattleCalc;
const Calc = window.Calc;
const Send = window.Send;
const cheats = window.cheats;
const getInput = window.HWHFuncs.getInput;
const getTimer = window.HWHFuncs.getTimer;
const countdownTimer = window.HWHFuncs.countdownTimer;
const setProgress = window.testFuntions.setProgress;
window.HWHClasses.executeDungeon = function(resolve, reject) {
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 battleData, battleType, battleResult;
let callsExecuteDungeon = {
calls: [{
name: "dungeonGetInfo",
args: {},
ident: "dungeonGetInfo"
}, {
name: "teamGetAll",
args: {},
ident: "teamGetAll"
}, {
name: "teamGetFavor",
args: {},
ident: "teamGetFavor"
}, {
name: "clanGetInfo",
args: {},
ident: "clanGetInfo"
}]
}
this.start = async function(titanit) {
//maxDungeonActivity = titanit > limitDungeonActivity ? limitDungeonActivity : titanit;
maxDungeonActivity = titanit || getInput('countTitanit');
send(callsExecuteDungeon, startDungeon);
}
/** Получаем данные по подземелью */
function startDungeon(e) {
window.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;
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];
case 'water':
return [4000, 4001, 4002, 4003]
.filter(e => !titansStates[e]?.isDead);
case 'earth':
return [4020, 4022, 4021, 4023]
.filter(e => !titansStates[e]?.isDead);
case 'fire':
return [4010, 4011, 4012, 4013]
.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);
setProgress(`${I18N('DUNGEON2')}: ${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 (window.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 += `<br> ${count} ${itemName}`;
doorsAmount++;
}
talentMsg = `<br>TMNT Talent: ${doorsAmount}/3 ${talentMsgReward}<br>`;
}
/** Выбираем огнем или землей атаковать */
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 j = 0; j < dark.length; j++) {
let darkId = dark[j];
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 j = 0; j < light.length; j++) {
let lightId = light[j];
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 j = 0; j < dark.length; j++) {
let darkId = dark[j];
actions.push(startBattle(teamNum, attackerType, getNeutralTeam(darkId, 4001, 4013)));
actions.push(startBattle(teamNum, attackerType, getNeutralTeam(darkId, 4002, 4013)));
}
for (let j = 0; j < light.length; j++) {
let lightId = light[j];
actions.push(startBattle(teamNum, attackerType, getNeutralTeam(lightId, 4001, 4013)));
actions.push(startBattle(teamNum, attackerType, getNeutralTeam(lightId, 4002, 4013)));
}
}
for (let j = 0; j < dark.length; j++) {
let firstId = dark[j];
actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId)));
for (let k = j + 1; k < dark.length; k++) {
let secondId = dark[k];
actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4001, secondId)));
actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId, 4002, secondId)));
}
}
for (let j = 0; j < light.length; j++) {
let firstId = light[j];
actions.push(startBattle(teamNum, attackerType, getNeutralTeam(firstId)));
for (let k = j + 1; k < light.length; k++) {
let secondId = light[k];
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.toString().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.toString()) {
case '4020':
return percentHP > 0.25 || (energy == 1000 && percentHP > 0.05);
case '4010':
return percentHP + energy / 2000.0 > 0.63;
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(startBattleCall, resultBattle, {
resolve,
teamNum,
attackerType
});
});
}
/** Возращает результат боя в промис */
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 (HWHData.countPredictionCard > 0) {
args.isRaid = true;
} else {
const timer = getTimer(battleInfo.battleTime);
console.log(timer);
await countdownTimer(timer, `${I18N('DUNGEON2')}: ${I18N('TITANIT')} ${dungeonActivity}/${maxDungeonActivity} ${talentMsg}`);
}
const calls = [{
name: "dungeonEndBattle",
args,
ident: "body"
}];
send({ 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(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];
let hours = Math.round(timeNow / 3600);
let mins = Math.round(timeNow % 3600 / 60);
console.log(i + ": ", hours + " ч. " + mins + " мин. " + 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Потеряна связь с сервером игры!", true);
} else {
setProgress('Dungeon completed: Титанит ' + dungeonActivity + '/' + maxDungeonActivity, true);
}
resolve();
}
}
};
} // end executeDungeon override scope
// ========== 6. 版本号显示 [dungeon] ==========
(function patchVersion() {
const scriptMenu = HWHClasses.ScriptMenu.getInst();
if (!scriptMenu || !scriptMenu.mainMenu) {
setTimeout(patchVersion, 500);
return;
}
const headers = scriptMenu.mainMenu.querySelectorAll('.scriptMenu_header');
if (headers.length >= 2) {
const ver = headers[1];
if (!ver.innerText.includes('[dungeon]')) {
ver.innerText += '[dungeon]';
ver.style.color = '#ff9900';
}
}
})();
console.log('%c[HeroWarsDungeonFull] 已注入复杂地下城策略', 'color: orange');
});
})();