diff --git a/binaries/data/mods/public/simulation/ai/petra/attackManager.js b/binaries/data/mods/public/simulation/ai/petra/attackManager.js index ffffc5efff..f291848ad2 100644 --- a/binaries/data/mods/public/simulation/ai/petra/attackManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/attackManager.js @@ -204,7 +204,7 @@ m.AttackManager.prototype.update = function(gameState, queues, events) if (this.upcomingAttacks["Raid"].length === 0 && gameState.ai.HQ.defenseManager.targetList.length) { var target = undefined; - for each (var targetId in gameState.ai.HQ.defenseManager.targetList) + for (var targetId of gameState.ai.HQ.defenseManager.targetList) { target = gameState.getEntityById(targetId); if (target) diff --git a/binaries/data/mods/public/simulation/ai/petra/attackPlan.js b/binaries/data/mods/public/simulation/ai/petra/attackPlan.js index 0e08e16405..1bc389144f 100644 --- a/binaries/data/mods/public/simulation/ai/petra/attackPlan.js +++ b/binaries/data/mods/public/simulation/ai/petra/attackPlan.js @@ -498,7 +498,7 @@ m.AttackPlan.prototype.trainMoreUnits = function(gameState) { warn("===================================="); warn("======== build order for plan " + this.name); - for each (var order in this.buildOrder) + for (var order of this.buildOrder) { var specialData = "Plan_"+this.name+"_"+order[4]; var inTraining = gameState.countOwnQueuedEntitiesWithMetadata("special", specialData); @@ -687,7 +687,7 @@ m.AttackPlan.prototype.rushTargetFinder = function(gameState) var minDist = Math.min(); var target = undefined; - for each (var building in buildings) + for (var building of buildings) { if (building.owner() === 0) continue; @@ -696,7 +696,7 @@ m.AttackPlan.prototype.rushTargetFinder = function(gameState) continue; var pos = building.position(); var defended = false; - for each (var defense in buildings) + for (var defense of buildings) { if (!defense.hasClass("CivCentre") && !defense.hasClass("Tower") && !defense.hasClass("Fortress")) continue; @@ -728,7 +728,7 @@ m.AttackPlan.prototype.rushTargetFinder = function(gameState) m.AttackPlan.prototype.raidTargetFinder = function(gameState) { var targets = new API3.EntityCollection(gameState.sharedScript); - for each (var targetId in gameState.ai.HQ.defenseManager.targetList) + for (var targetId of gameState.ai.HQ.defenseManager.targetList) { var target = gameState.getEntityById(targetId); if (target && target.position()) @@ -1144,7 +1144,7 @@ m.AttackPlan.prototype.update = function(gameState, events) { // if siege units are attacked, we'll send some units to deal with enemies. var collec = this.unitCollection.filter(API3.Filters.not(API3.Filters.byClass("Siege"))).filterNearest(ourUnit.position(), 5).toEntityArray(); - for each (var ent in collec) + for (var ent of collec) if (!this.isSiegeUnit(gameState, ent)) ent.attack(attacker.id()); } @@ -1422,7 +1422,7 @@ m.AttackPlan.prototype.hasForceOrder = function(data, value) if (data && +(ent.getMetadata(PlayerID, data)) !== value) return; var orders = ent.unitAIOrderData(); - for each (var order in orders) + for (var order of orders) if (order.force) forced = true; }); diff --git a/binaries/data/mods/public/simulation/ai/petra/baseManager.js b/binaries/data/mods/public/simulation/ai/petra/baseManager.js index fb1f51bdf0..ec45bc00d3 100644 --- a/binaries/data/mods/public/simulation/ai/petra/baseManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/baseManager.js @@ -51,7 +51,7 @@ m.BaseManager.prototype.init = function(gameState, unconstructed) this.dropsites = {}; this.dropsiteSupplies = {}; this.gatherers = {}; - for each (var type in this.Config.resources) + for (var type of this.Config.resources) { this.dropsiteSupplies[type] = {"nearby": [], "medium": [], "faraway": []}; this.gatherers[type] = {"nextCheck": 0, "used": 0, "lost": 0}; @@ -343,7 +343,7 @@ m.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource) if (total == 0) continue; - for each (var cc in ccEnts) + for (var cc of ccEnts) { var ccPos = cc.position(); if (!ccPos) @@ -381,7 +381,7 @@ m.BaseManager.prototype.getResourceLevel = function (gameState, type) var count = 0; var check = {}; var nearby = this.dropsiteSupplies[type]["nearby"]; - for each (var supply in nearby) + for (var supply of nearby) { if (check[supply.id]) // avoid double counting as same resource can appear several time continue; @@ -389,7 +389,7 @@ m.BaseManager.prototype.getResourceLevel = function (gameState, type) count += supply.ent.resourceSupplyAmount(); } var medium = this.dropsiteSupplies[type]["medium"]; - for each (var supply in medium) + for (var supply of medium) { if (check[supply.id]) continue; @@ -402,7 +402,7 @@ m.BaseManager.prototype.getResourceLevel = function (gameState, type) // check our resource levels and react accordingly m.BaseManager.prototype.checkResourceLevels = function (gameState, queues) { - for each (var type in this.Config.resources) + for (var type of this.Config.resources) { if (type == "food") { @@ -543,12 +543,12 @@ m.BaseManager.prototype.setWorkersIdleByPriority = function(gameState) var resources = gameState.ai.queueManager.getAvailableResources(gameState); var avgOverdraft = 0; - for each (var type in resources.types) + for (var type of resources.types) avgOverdraft += resources[type]; avgOverdraft /= 4; var mostNeeded = gameState.ai.HQ.pickMostNeededResources(gameState); - for each (var type in resources.types) + for (var type of resources.types) { if (type === mostNeeded[0]) continue; diff --git a/binaries/data/mods/public/simulation/ai/petra/data.json b/binaries/data/mods/public/simulation/ai/petra/data.json index e67f94dabe..f622697e69 100644 --- a/binaries/data/mods/public/simulation/ai/petra/data.json +++ b/binaries/data/mods/public/simulation/ai/petra/data.json @@ -1,6 +1,6 @@ { "name": "Petra Bot", - "description": "Based on Aegis, but heavily modified and expected to be more robust. Support for naval maps is also added. Note that it doesn't support saved games yet and is not as well tested as Aegis. Please report issues to Wildfire Games (see the link in the main menu).", + "description": "Based on Aegis, but heavily modified by mimo and expected to be more robust. Support for naval maps is also added. Note that it doesn't support saved games yet. Please report issues to Wildfire Games (see the link in the main menu).", "moduleName" : "PETRA", "constructor": "PetraBot", "useShared": true diff --git a/binaries/data/mods/public/simulation/ai/petra/defenseArmy.js b/binaries/data/mods/public/simulation/ai/petra/defenseArmy.js index fcfc6d7c47..e8d8748731 100644 --- a/binaries/data/mods/public/simulation/ai/petra/defenseArmy.js +++ b/binaries/data/mods/public/simulation/ai/petra/defenseArmy.js @@ -25,7 +25,7 @@ m.DefenseArmy.prototype.assignUnit = function (gameState, entID) var distMin = undefined; var idMinAll = undefined; var distMinAll = undefined; - for each (var id in this.foeEntities) + for (var id of this.foeEntities) { var eEnt = gameState.getEntityById(id); if (!eEnt || !eEnt.position()) // probably can't happen. @@ -95,7 +95,7 @@ m.DefenseArmy.prototype.getState = function (gameState) m.DefenseArmy.prototype.update = function (gameState) { - for each (var entId in this.ownEntities) + for (var entId of this.ownEntities) { var ent = gameState.getEntityById(entId); if (!ent) @@ -118,7 +118,7 @@ m.DefenseArmy.prototype.debug = function (gameState) // m.debug ("Strength " + this.foeStrength); // debug (gameState.getEntityById(ent)._templateName + ", ID " + ent); //debug ("Defenders " + this.ownEntities.length); - for each (ent in this.foeEntities) + for (var ent of this.foeEntities) { if (gameState.getEntityById(ent) !== undefined) { diff --git a/binaries/data/mods/public/simulation/ai/petra/map-module.js b/binaries/data/mods/public/simulation/ai/petra/map-module.js index 66f552d523..2628547b53 100644 --- a/binaries/data/mods/public/simulation/ai/petra/map-module.js +++ b/binaries/data/mods/public/simulation/ai/petra/map-module.js @@ -117,12 +117,15 @@ m.createObstructionMap = function(gameState, accessIndex, template) var map = new API3.Map(gameState.sharedScript, obstructionTiles); map.setMaxVal(255); - if (template && template.buildDistance()) { + if (template && template.buildDistance()) + { var minDist = template.buildDistance().MinDistance; var category = template.buildDistance().FromCategory; - if (minDist !== undefined && category !== undefined){ + if (minDist !== undefined && category !== undefined) + { gameState.getOwnStructures().forEach(function(ent) { - if (ent.buildCategory() === category && ent.position()){ + if (ent.buildCategory() === category && ent.position()) + { var pos = ent.position(); var x = Math.round(pos[0] / gameState.cellSize); var z = Math.round(pos[1] / gameState.cellSize); diff --git a/binaries/data/mods/public/simulation/ai/petra/navalManager.js b/binaries/data/mods/public/simulation/ai/petra/navalManager.js index f6e8370c12..8e16990d2c 100644 --- a/binaries/data/mods/public/simulation/ai/petra/navalManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/navalManager.js @@ -288,7 +288,7 @@ m.NavalManager.prototype.requireTransport = function(gameState, entity, startInd return false; } - for each (var plan in this.transportPlans) + for (var plan of this.transportPlans) { if (plan.startIndex !== startIndex || plan.endIndex !== endIndex) continue @@ -328,7 +328,7 @@ m.NavalManager.prototype.checkLevels = function(gameState, queues) for (var sea = 0; sea < this.neededTransportShips.length; sea++) this.neededTransportShips[sea] = 0; - for each (var plan in this.transportPlans) + for (var plan of this.transportPlans) { if (!plan.needTransportShips || plan.units.length < 2) continue; @@ -357,30 +357,29 @@ m.NavalManager.prototype.maintainFleet = function(gameState, queues) gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_super_dock"), true) === 0) return; // check if we have enough transport ships per region. - for (var i = 0; i < this.seaShips.length; ++i) + for (var sea = 0; sea < this.seaShips.length; ++sea) { - if (this.accessibleSeas.indexOf(i) === -1) + if (this.accessibleSeas.indexOf(sea) === -1) continue; - if (gameState.countOwnQueuedEntitiesWithMetadata("sea", i) > 0) + if (gameState.countOwnQueuedEntitiesWithMetadata("sea", sea) > 0) continue; - if (this.seaTransportShips[i].length < this.wantedTransportShips[i]) + if (this.seaTransportShips[sea].length < this.wantedTransportShips[sea]) { - if ((gameState.civ() === "cart" && gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_super_dock"), true) === 0) - || gameState.civ() === "iber") - queues.ships.addItem(new m.TrainingPlan(gameState, "units/{civ}_ship_merchant", { "sea": i }, 1, 1)); - else - queues.ships.addItem(new m.TrainingPlan(gameState, "units/{civ}_ship_trireme", { "sea": i }, 1, 1)); + var template = this.getBestShip(gameState, sea, "transport"); + if (!template) + continue; + queues.ships.addItem(new m.TrainingPlan(gameState, template, { "sea": sea }, 1, 1)); } - else if (this.seaFishShips[i].length < this.wantedFishShips[i]) - queues.ships.addItem(new m.TrainingPlan(gameState, "units/{civ}_ship_fishing", { "base": 0, "role": "worker", "sea": i }, 1, 1)); + else if (this.seaFishShips[sea].length < this.wantedFishShips[sea]) + queues.ships.addItem(new m.TrainingPlan(gameState, "units/{civ}_ship_fishing", { "base": 0, "role": "worker", "sea": sea }, 1, 1)); } }; // assigns free ships to plans that need some m.NavalManager.prototype.assignToPlans = function(gameState) { - for each (var plan in this.transportPlans) + for (var plan of this.transportPlans) { if (!plan.needTransportShips) continue; @@ -467,6 +466,47 @@ m.NavalManager.prototype.buildNavalStructures = function(gameState, queues) } }; +// goal can be either attack (choose ship with best arrowCount) or transport (choose ship with best capacity) +m.NavalManager.prototype.getBestShip = function(gameState, sea, goal) +{ + var trainableShips = []; + gameState.getOwnTrainingFacilities().filter(API3.Filters.byMetadata(PlayerID, "sea", sea)).forEach(function(ent) { + var trainables = ent.trainableEntities(); + for (var trainable of trainables) + { + var template = gameState.getTemplate(trainable); + if (template.hasClass("Ship") && trainableShips.indexOf(trainable) === -1) + trainableShips.push(trainable); + } + }); + + var best = 0; + var bestShip = undefined; + for (var trainable of trainableShips) + { + var template = gameState.getTemplate(trainable); + var arrows = +(template.getDefaultArrow() || 0); + if (goal === "attack") // choose the maximum default arrows + { + if (best > arrows) + continue; + best = arrows; + } + else if (goal === "transport") // choose the maximum capacity, with a bonus if arrows or if siege transport + { + var capacity = +(template.garrisonMax() || 0); + capacity += 10*arrows; + if (MatchesClassList(template.garrisonableClasses(), "Siege")) + capacity += 50; + if (best > capacity) + continue; + best = capacity; + } + bestShip = trainable; + } + return bestShip; +}; + m.NavalManager.prototype.update = function(gameState, queues, events) { Engine.ProfileStart("Naval Manager update"); diff --git a/binaries/data/mods/public/simulation/ai/petra/queueplan-building.js b/binaries/data/mods/public/simulation/ai/petra/queueplan-building.js index 2c8e44a7de..1e53956930 100644 --- a/binaries/data/mods/public/simulation/ai/petra/queueplan-building.js +++ b/binaries/data/mods/public/simulation/ai/petra/queueplan-building.js @@ -393,14 +393,10 @@ m.ConstructionPlan.prototype.findDockPosition = function(gameState) } } - var radius = 1; // not used - - if (bestVal === undefined || bestVal === -1) - { - var bestTile = friendlyTiles.findBestTile(radius, obstructionMap); - var bestIdx = bestTile[0]; - var bestVal = bestTile[1]; - } + var radius = 1; + var bestTile = friendlyTiles.findBestTile(radius, obstructionMap); + var bestIdx = bestTile[0]; + var bestVal = bestTile[1]; if (bestVal <= 0) return false; @@ -463,10 +459,12 @@ m.ConstructionPlan.prototype.getDockAngle = function(gameState, template, x, z) var seaAccess = gameState.ai.accessibility.navalPassMap[j]; var landAccess = gameState.ai.accessibility.landPassMap[j]; if (seaAccess === seaRef && landAccess < 2) - waterPoints.push(i); + waterPoints.push(i); } - var consec = []; var length = waterPoints.length; + if (!length) + continue; + var consec = []; for (var i = 0; i < length; ++i) { var count = 0; diff --git a/binaries/data/mods/public/simulation/ai/petra/tradeManager.js b/binaries/data/mods/public/simulation/ai/petra/tradeManager.js index 2e462a8075..1c636e12dc 100644 --- a/binaries/data/mods/public/simulation/ai/petra/tradeManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/tradeManager.js @@ -131,12 +131,12 @@ m.TradeManager.prototype.buildTradeRoute = function(gameState, queues) var distmax = -1; var imax = -1; var jmax = -1; - for each (var m1 in market1) + for (var m1 of market1) { if (!m1.position()) continue; var index1 = gameState.ai.accessibility.getAccessValue(m1.position()); - for each (var m2 in market2) + for (var m2 of market2) { if (m1.id() === m2.id()) continue; @@ -230,7 +230,7 @@ m.TradeManager.prototype.performBarter = function(gameState) var getBarterRate = function (prices,buy,sell) { return Math.round(100 * prices["sell"][sell] / prices["buy"][buy]); }; // loop through each queues checking if we could barter and help finishing a queue quickly. - for each (var buy in needs.types) + for (var buy of needs.types) { if (needs[buy] == 0 || needs[buy] < rates[buy]*30) // check if our rate allows to gather it fast enough continue; @@ -238,7 +238,7 @@ m.TradeManager.prototype.performBarter = function(gameState) // pick the best resource to barter. var bestToSell = undefined; var bestRate = 0; - for each (var sell in needs.types) + for (var sell of needs.types) { if (sell === buy) continue; @@ -287,7 +287,7 @@ m.TradeManager.prototype.performBarter = function(gameState) return false; var bestToBuy = undefined; var bestChoice = 0; - for each (var buy in needs.types) + for (var buy of needs.types) { if (buy === "food") continue;