forked from 0ad/0ad
491 lines
19 KiB
JavaScript
Executable File
491 lines
19 KiB
JavaScript
Executable File
/*
|
|
* Military Manager.
|
|
* Basically this deals with constructing defense and attack buildings, but it's not very developped yet.
|
|
* There's a lot of work still to do here.
|
|
* It also handles the attack plans (see attack_plan.js)
|
|
* Not completely cleaned up from the original version in qBot.
|
|
*/
|
|
|
|
var MilitaryAttackManager = function() {
|
|
|
|
this.fortressStartTime = 0;
|
|
this.fortressLapseTime = Config.Military.fortressLapseTime * 1000;
|
|
this.defenceBuildingTime = Config.Military.defenceBuildingTime * 1000;
|
|
this.attackPlansStartTime = Config.Military.attackPlansStartTime * 1000;
|
|
this.defenceManager = new Defence();
|
|
|
|
|
|
this.TotalAttackNumber = 0;
|
|
this.upcomingAttacks = { "CityAttack" : [] };
|
|
this.startedAttacks = { "CityAttack" : [] };
|
|
};
|
|
|
|
MilitaryAttackManager.prototype.init = function(gameState) {
|
|
var civ = gameState.playerData.civ;
|
|
|
|
// load units and buildings from the config files
|
|
|
|
if (civ in Config.buildings.moderate){
|
|
this.bModerate = Config.buildings.moderate[civ];
|
|
}else{
|
|
this.bModerate = Config.buildings.moderate['default'];
|
|
}
|
|
|
|
if (civ in Config.buildings.advanced){
|
|
this.bAdvanced = Config.buildings.advanced[civ];
|
|
}else{
|
|
this.bAdvanced = Config.buildings.advanced['default'];
|
|
}
|
|
|
|
if (civ in Config.buildings.fort){
|
|
this.bFort = Config.buildings.fort[civ];
|
|
}else{
|
|
this.bFort = Config.buildings.fort['default'];
|
|
}
|
|
|
|
for (var i in this.bAdvanced){
|
|
this.bAdvanced[i] = gameState.applyCiv(this.bAdvanced[i]);
|
|
}
|
|
for (var i in this.bFort){
|
|
this.bFort[i] = gameState.applyCiv(this.bFort[i]);
|
|
}
|
|
|
|
// TODO: figure out how to make this generic
|
|
for (var i in this.attackManagers){
|
|
this.availableAttacks[i] = new this.attackManagers[i](gameState, this);
|
|
}
|
|
|
|
var enemies = gameState.getEnemyEntities();
|
|
var filter = Filters.byClassesOr(["CitizenSoldier", "Champion", "Hero", "Siege"]);
|
|
this.enemySoldiers = enemies.filter(filter); // TODO: cope with diplomacy changes
|
|
this.enemySoldiers.registerUpdates();
|
|
|
|
// each enemy watchers keeps a list of entity collections about the enemy it watches
|
|
// It also keeps track of enemy armies, merging/splitting as needed
|
|
this.enemyWatchers = {};
|
|
this.ennWatcherIndex = [];
|
|
for (var i = 1; i <= 8; i++)
|
|
if (PlayerID != i && gameState.isPlayerEnemy(i)) {
|
|
this.enemyWatchers[i] = new enemyWatcher(gameState, i);
|
|
this.ennWatcherIndex.push(i);
|
|
this.defenceManager.enemyArmy[i] = [];
|
|
}
|
|
|
|
};
|
|
|
|
// picks the best template based on parameters and classes
|
|
MilitaryAttackManager.prototype.findBestTrainableUnit = function(gameState, classes, parameters) {
|
|
var units = gameState.findTrainableUnits(classes);
|
|
|
|
if (units.length === 0)
|
|
return undefined;
|
|
|
|
|
|
units.sort(function(a, b) { //}) {
|
|
var aDivParam = 0, bDivParam = 0;
|
|
var aTopParam = 0, bTopParam = 0;
|
|
for (var i in parameters) {
|
|
var param = parameters[i];
|
|
|
|
if (param[0] == "base") {
|
|
aTopParam = param[1];
|
|
bTopParam = param[1];
|
|
}
|
|
if (param[0] == "strength") {
|
|
aTopParam += getMaxStrength(a[1]) * param[1];
|
|
bTopParam += getMaxStrength(b[1]) * param[1];
|
|
}
|
|
if (param[0] == "siegeStrength") {
|
|
aTopParam += getMaxStrength(a[1], "Structure") * param[1];
|
|
bTopParam += getMaxStrength(b[1], "Structure") * param[1];
|
|
}
|
|
if (param[0] == "speed") {
|
|
aTopParam += a[1].walkSpeed() * param[1];
|
|
bTopParam += b[1].walkSpeed() * param[1];
|
|
}
|
|
|
|
if (param[0] == "cost") {
|
|
aDivParam += a[1].costSum() * param[1];
|
|
bDivParam += b[1].costSum() * param[1];
|
|
}
|
|
if (param[0] == "canGather") {
|
|
// checking against wood, could be anything else really.
|
|
if (a[1].resourceGatherRates() && a[1].resourceGatherRates()["wood.tree"])
|
|
aTopParam *= param[1];
|
|
if (b[1].resourceGatherRates() && b[1].resourceGatherRates()["wood.tree"])
|
|
bTopParam *= param[1];
|
|
}
|
|
}
|
|
return -(aTopParam/(aDivParam+1)) + (bTopParam/(bDivParam+1));
|
|
});
|
|
return units[0][0];
|
|
};
|
|
|
|
// Deals with building fortresses and towers.
|
|
// Currently build towers next to every useful dropsites.
|
|
// TODO: Fortresses are placed randomly atm.
|
|
MilitaryAttackManager.prototype.buildDefences = function(gameState, queues){
|
|
|
|
var workersNumber = gameState.getOwnEntitiesByRole("worker").filter(Filters.not(Filters.byHasMetadata(PlayerID,"plan"))).length;
|
|
|
|
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv('structures/{civ}_defense_tower'))
|
|
+ queues.defenceBuilding.totalLength() < gameState.getEntityLimits()["DefenseTower"] && queues.defenceBuilding.totalLength() < 4
|
|
&& gameState.currentPhase() > 1 && queues.defenceBuilding.totalLength() < 3) {
|
|
gameState.getOwnEntities().forEach(function(dropsiteEnt) {
|
|
if (dropsiteEnt.resourceDropsiteTypes() && dropsiteEnt.getMetadata(PlayerID, "defenseTower") !== true
|
|
&& (dropsiteEnt.getMetadata(PlayerID, "resource-quantity-wood") > 400 || dropsiteEnt.getMetadata(PlayerID, "resource-quantity-stone") > 500
|
|
|| dropsiteEnt.getMetadata(PlayerID, "resource-quantity-metal") > 500) ){
|
|
var position = dropsiteEnt.position();
|
|
if (position){
|
|
queues.defenceBuilding.addItem(new BuildingConstructionPlan(gameState, 'structures/{civ}_defense_tower', position));
|
|
}
|
|
dropsiteEnt.setMetadata(PlayerID, "defenseTower", true);
|
|
}
|
|
});
|
|
}
|
|
|
|
var numFortresses = 0;
|
|
for (var i in this.bFort){
|
|
numFortresses += gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bFort[i]));
|
|
}
|
|
|
|
if (queues.defenceBuilding.totalLength() < 1 && (gameState.currentPhase() > 2 || gameState.isResearching("phase_city_generic")))
|
|
{
|
|
if (workersNumber >= 80 && gameState.getTimeElapsed() > numFortresses * this.fortressLapseTime + this.fortressStartTime)
|
|
{
|
|
if (!this.fortressStartTime)
|
|
this.fortressStartTime = gameState.getTimeElapsed();
|
|
queues.defenceBuilding.addItem(new BuildingConstructionPlan(gameState, this.bFort[0]));
|
|
debug ("Building a fortress");
|
|
}
|
|
}
|
|
if (gameState.countEntitiesByType(gameState.applyCiv(this.bFort[i])) >= 1) {
|
|
// let's add a siege building plan to the current attack plan if there is none currently.
|
|
if (this.upcomingAttacks["CityAttack"].length !== 0)
|
|
{
|
|
var attack = this.upcomingAttacks["CityAttack"][0];
|
|
if (!attack.unitStat["Siege"])
|
|
{
|
|
// no minsize as we don't want the plan to fail at the last minute though.
|
|
var stat = { "priority" : 1.1, "minSize" : 0, "targetSize" : 4, "batchSize" : 2, "classes" : ["Siege"],
|
|
"interests" : [ ["siegeStrength", 3], ["cost",1] ] ,"templates" : [] };
|
|
if (gameState.civ() == "cart" || gameState.civ() == "maur")
|
|
stat["classes"] = ["Elephant"];
|
|
attack.addBuildOrder(gameState, "Siege", stat, true);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Deals with constructing military buildings (barracks, stables…)
|
|
// They are mostly defined by Config.js. This is unreliable since changes could be done easily.
|
|
// TODO: We need to determine these dynamically. Also doesn't build fortresses since the above function does that.
|
|
// TODO: building placement is bad. Choice of buildings is also fairly dumb.
|
|
MilitaryAttackManager.prototype.constructTrainingBuildings = function(gameState, queues) {
|
|
Engine.ProfileStart("Build buildings");
|
|
var workersNumber = gameState.getOwnEntitiesByRole("worker").filter(Filters.not(Filters.byHasMetadata(PlayerID, "plan"))).length;
|
|
|
|
if (workersNumber > 30 && (gameState.currentPhase() > 1 || gameState.isResearching("phase_town_generic")
|
|
|| gameState.isResearching("phase_town_athens"))) {
|
|
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) + queues.militaryBuilding.totalLength() < 1) {
|
|
debug ("Trying to build barracks");
|
|
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bModerate[0]));
|
|
}
|
|
}
|
|
|
|
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) < 2 && workersNumber > 85)
|
|
if (queues.militaryBuilding.totalLength() < 1)
|
|
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bModerate[0]));
|
|
|
|
if (gameState.countEntitiesByType(gameState.applyCiv(this.bModerate[0])) === 2 && gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) < 3 && workersNumber > 125)
|
|
if (queues.militaryBuilding.totalLength() < 1)
|
|
{
|
|
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bModerate[0]));
|
|
if (gameState.civ() == "gaul" || gameState.civ() == "brit" || gameState.civ() == "iber") {
|
|
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bModerate[0]));
|
|
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bModerate[0]));
|
|
}
|
|
}
|
|
//build advanced military buildings
|
|
if (workersNumber >= 75 && gameState.currentPhase() > 2){
|
|
if (queues.militaryBuilding.totalLength() === 0){
|
|
var inConst = 0;
|
|
for (var i in this.bAdvanced)
|
|
inConst += gameState.countFoundationsWithType(gameState.applyCiv(this.bAdvanced[i]));
|
|
if (inConst == 0 && this.bAdvanced && this.bAdvanced.length !== 0) {
|
|
var i = Math.floor(Math.random() * this.bAdvanced.length);
|
|
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bAdvanced[i])) < 1){
|
|
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bAdvanced[i]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (gameState.civ() !== "gaul" && gameState.civ() !== "brit" && gameState.civ() !== "iber" &&
|
|
workersNumber > 130 && gameState.currentPhase() > 2)
|
|
{
|
|
var Const = 0;
|
|
for (var i in this.bAdvanced)
|
|
Const += gameState.countEntitiesByType(gameState.applyCiv(this.bAdvanced[i]));
|
|
if (inConst == 1) {
|
|
var i = Math.floor(Math.random() * this.bAdvanced.length);
|
|
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bAdvanced[i])) < 1){
|
|
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bAdvanced[i]));
|
|
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bAdvanced[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
Engine.ProfileStop();
|
|
};
|
|
|
|
// TODO: use pop(). Currently unused as this is too gameable.
|
|
MilitaryAttackManager.prototype.garrisonAllFemales = function(gameState) {
|
|
var buildings = gameState.getOwnEntities().filter(Filters.byCanGarrison()).toEntityArray();
|
|
var females = gameState.getOwnEntities().filter(Filters.byClass("Support"));
|
|
|
|
var cache = {};
|
|
|
|
females.forEach( function (ent) {
|
|
for (var i in buildings)
|
|
{
|
|
if (ent.position())
|
|
{
|
|
var struct = buildings[i];
|
|
if (!cache[struct.id()])
|
|
cache[struct.id()] = 0;
|
|
if (struct.garrisoned() && struct.garrisonMax() - struct.garrisoned().length - cache[struct.id()] > 0)
|
|
{
|
|
ent.garrison(struct);
|
|
cache[struct.id()]++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
this.hasGarrisonedFemales = true;
|
|
};
|
|
MilitaryAttackManager.prototype.ungarrisonAll = function(gameState) {
|
|
this.hasGarrisonedFemales = false;
|
|
var buildings = gameState.getOwnEntities().filter(Filters.byCanGarrison()).toEntityArray();
|
|
buildings.forEach( function (struct) {
|
|
if (struct.garrisoned() && struct.garrisoned().length)
|
|
struct.unloadAll();
|
|
});
|
|
};
|
|
|
|
MilitaryAttackManager.prototype.pausePlan = function(gameState, planName) {
|
|
for (var attackType in this.upcomingAttacks) {
|
|
for (var i in this.upcomingAttacks[attackType]) {
|
|
var attack = this.upcomingAttacks[attackType][i];
|
|
if (attack.getName() == planName)
|
|
attack.setPaused(gameState, true);
|
|
}
|
|
}
|
|
for (var attackType in this.startedAttacks) {
|
|
for (var i in this.startedAttacks[attackType]) {
|
|
var attack = this.startedAttacks[attackType][i];
|
|
if (attack.getName() == planName)
|
|
attack.setPaused(gameState, true);
|
|
}
|
|
}
|
|
}
|
|
MilitaryAttackManager.prototype.unpausePlan = function(gameState, planName) {
|
|
for (var attackType in this.upcomingAttacks) {
|
|
for (var i in this.upcomingAttacks[attackType]) {
|
|
var attack = this.upcomingAttacks[attackType][i];
|
|
if (attack.getName() == planName)
|
|
attack.setPaused(gameState, false);
|
|
}
|
|
}
|
|
for (var attackType in this.startedAttacks) {
|
|
for (var i in this.startedAttacks[attackType]) {
|
|
var attack = this.startedAttacks[attackType][i];
|
|
if (attack.getName() == planName)
|
|
attack.setPaused(gameState, false);
|
|
}
|
|
}
|
|
}
|
|
MilitaryAttackManager.prototype.pauseAllPlans = function(gameState) {
|
|
for (var attackType in this.upcomingAttacks) {
|
|
for (var i in this.upcomingAttacks[attackType]) {
|
|
var attack = this.upcomingAttacks[attackType][i];
|
|
attack.setPaused(gameState, true);
|
|
}
|
|
}
|
|
for (var attackType in this.startedAttacks) {
|
|
for (var i in this.startedAttacks[attackType]) {
|
|
var attack = this.startedAttacks[attackType][i];
|
|
attack.setPaused(gameState, true);
|
|
}
|
|
}
|
|
}
|
|
MilitaryAttackManager.prototype.unpauseAllPlans = function(gameState) {
|
|
for (var attackType in this.upcomingAttacks) {
|
|
for (var i in this.upcomingAttacks[attackType]) {
|
|
var attack = this.upcomingAttacks[attackType][i];
|
|
attack.setPaused(gameState, false);
|
|
}
|
|
}
|
|
for (var attackType in this.startedAttacks) {
|
|
for (var i in this.startedAttacks[attackType]) {
|
|
var attack = this.startedAttacks[attackType][i];
|
|
attack.setPaused(gameState, false);
|
|
}
|
|
}
|
|
}
|
|
MilitaryAttackManager.prototype.update = function(gameState, queues, events) {
|
|
var self = this;
|
|
|
|
Engine.ProfileStart("military update");
|
|
|
|
this.gameState = gameState;
|
|
|
|
Engine.ProfileStart("Constructing military buildings and building defences");
|
|
this.constructTrainingBuildings(gameState, queues);
|
|
|
|
if(gameState.getTimeElapsed() > this.defenceBuildingTime)
|
|
this.buildDefences(gameState, queues);
|
|
Engine.ProfileStop();
|
|
|
|
this.defenceManager.update(gameState, events, this);
|
|
|
|
Engine.ProfileStart("Looping through attack plans");
|
|
// TODO: implement some form of check before starting a new attack plans. Sometimes it is not the priority.
|
|
if (1) {
|
|
for (var attackType in this.upcomingAttacks) {
|
|
for (var i = 0;i < this.upcomingAttacks[attackType].length; ++i) {
|
|
|
|
var attack = this.upcomingAttacks[attackType][i];
|
|
|
|
// okay so we'll get the support plan
|
|
if (!attack.isStarted()) {
|
|
var updateStep = attack.updatePreparation(gameState, this,events);
|
|
|
|
// now we're gonna check if the preparation time is over
|
|
if (updateStep === 1 || attack.isPaused() ) {
|
|
// just chillin'
|
|
} else if (updateStep === 0 || updateStep === 3) {
|
|
debug ("Military Manager: " +attack.getType() +" plan " +attack.getName() +" aborted.");
|
|
if (updateStep === 3) {
|
|
this.attackPlansEncounteredWater = true;
|
|
debug("No attack path found. Aborting.");
|
|
}
|
|
attack.Abort(gameState, this);
|
|
this.upcomingAttacks[attackType].splice(i--,1);
|
|
} else if (updateStep === 2) {
|
|
var chatText = "I am launching an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
|
|
if (Math.random() < 0.2)
|
|
chatText = "Attacking " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
|
|
else if (Math.random() < 0.3)
|
|
chatText = "I have sent an army against " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
|
|
else if (Math.random() < 0.3)
|
|
chatText = "I'm starting an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
|
|
gameState.ai.chatTeam(chatText);
|
|
|
|
debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName());
|
|
attack.StartAttack(gameState,this);
|
|
this.startedAttacks[attackType].push(attack);
|
|
this.upcomingAttacks[attackType].splice(i--,1);
|
|
}
|
|
} else {
|
|
var chatText = "I am launching an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
|
|
if (Math.random() < 0.2)
|
|
chatText = "Attacking " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
|
|
else if (Math.random() < 0.3)
|
|
chatText = "I have sent an army against " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
|
|
else if (Math.random() < 0.3)
|
|
chatText = "I'm starting an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
|
|
gameState.ai.chatTeam(chatText);
|
|
|
|
debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName());
|
|
this.startedAttacks[attackType].push(attack);
|
|
this.upcomingAttacks[attackType].splice(i--,1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (var attackType in this.startedAttacks) {
|
|
for (var i = 0; i < this.startedAttacks[attackType].length; ++i) {
|
|
var attack = this.startedAttacks[attackType][i];
|
|
// okay so then we'll update the attack.
|
|
if (!attack.isPaused())
|
|
{
|
|
var remaining = attack.update(gameState,this,events);
|
|
if (remaining == 0 || remaining == undefined) {
|
|
debug ("Military Manager: " +attack.getType() +" plan " +attack.getName() +" is now finished.");
|
|
attack.Abort(gameState);
|
|
this.startedAttacks[attackType].splice(i--,1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Note: these indications of "rush" are currently unused.
|
|
if (gameState.ai.strategy === "rush" && this.startedAttacks["CityAttack"].length !== 0) {
|
|
// and then we revert.
|
|
gameState.ai.strategy = "normal";
|
|
Config.Economy.femaleRatio = 0.4;
|
|
gameState.ai.modules.economy.targetNumWorkers = Math.max(Math.floor(gameState.getPopulationMax()*0.55), 1);
|
|
} else if (gameState.ai.strategy === "rush" && this.upcomingAttacks["CityAttack"].length === 0)
|
|
{
|
|
Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1, "rush")
|
|
this.TotalAttackNumber++;
|
|
this.upcomingAttacks["CityAttack"].push(Lalala);
|
|
debug ("Starting a little something");
|
|
} else if (gameState.ai.strategy !== "rush")
|
|
{
|
|
// creating plans after updating because an aborted plan might be reused in that case.
|
|
if (gameState.countEntitiesByType(gameState.applyCiv(this.bModerate[0])) >= 1 && !this.attackPlansEncounteredWater
|
|
&& gameState.getTimeElapsed() > this.attackPlansStartTime) {
|
|
if (gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_dock")) === 0 && gameState.ai.waterMap)
|
|
{
|
|
// wait till we get a dock.
|
|
} else {
|
|
// basically only the first plan, really.
|
|
if (this.upcomingAttacks["CityAttack"].length == 0 && (gameState.getTimeElapsed() < 12*60000 || Config.difficulty < 1)) {
|
|
var Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1);
|
|
if (Lalala.failed)
|
|
{
|
|
this.attackPlansEncounteredWater = true; // hack
|
|
} else {
|
|
debug ("Military Manager: Creating the plan " +this.TotalAttackNumber);
|
|
this.TotalAttackNumber++;
|
|
this.upcomingAttacks["CityAttack"].push(Lalala);
|
|
}
|
|
} else if (this.upcomingAttacks["CityAttack"].length == 0 && Config.difficulty !== 0) {
|
|
var Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1, "superSized");
|
|
if (Lalala.failed)
|
|
{
|
|
this.attackPlansEncounteredWater = true; // hack
|
|
} else {
|
|
debug ("Military Manager: Creating the super sized plan " +this.TotalAttackNumber);
|
|
this.TotalAttackNumber++;
|
|
this.upcomingAttacks["CityAttack"].push(Lalala);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
// very old relic. This should be reimplemented someday so the code stays here.
|
|
|
|
if (this.HarassRaiding && this.preparingRaidNumber + this.startedRaidNumber < 1 && gameState.getTimeElapsed() < 780000) {
|
|
var Lalala = new CityAttack(gameState, this,this.totalStartedAttackNumber, -1, "harass_raid");
|
|
if (!Lalala.createSupportPlans(gameState, this, )) {
|
|
debug ("Military Manager: harrassing plan not a valid option");
|
|
this.HarassRaiding = false;
|
|
} else {
|
|
debug ("Military Manager: Creating the harass raid plan " +this.totalStartedAttackNumber);
|
|
|
|
this.totalStartedAttackNumber++;
|
|
this.preparingRaidNumber++;
|
|
this.currentAttacks.push(Lalala);
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
Engine.ProfileStop();
|
|
Engine.ProfileStop();
|
|
};
|