diff --git a/binaries/data/mods/public/simulation/ai/petra/config.js b/binaries/data/mods/public/simulation/ai/petra/config.js index d86d46157c..32373758c2 100644 --- a/binaries/data/mods/public/simulation/ai/petra/config.js +++ b/binaries/data/mods/public/simulation/ai/petra/config.js @@ -56,7 +56,7 @@ m.Config = function(difficulty) "gaul": [ "structures/{civ}_rotarymill", "structures/{civ}_tavern" ], "iber": [ "structures/{civ}_monument" ], "mace": [ "structures/{civ}_library", "structures/{civ}_theatron" ], - "maur": [ "structures/{civ}_elephant_stables", "structures/{civ}_pillar_ashoka" ], + "maur": [ "structures/{civ}_pillar_ashoka" ], "pers": [ "structures/{civ}_apadana", "structures/{civ}_hall"], "ptol": [ "structures/{civ}_library" ], "rome": [ "structures/{civ}_army_camp" ], diff --git a/binaries/data/mods/public/simulation/ai/petra/headquarters.js b/binaries/data/mods/public/simulation/ai/petra/headquarters.js index ebe8457fcb..26331f2dd1 100644 --- a/binaries/data/mods/public/simulation/ai/petra/headquarters.js +++ b/binaries/data/mods/public/simulation/ai/petra/headquarters.js @@ -1758,44 +1758,38 @@ m.HQ.prototype.constructTrainingBuildings = function(gameState, queues) if (numBarracks == 0) { gameState.ai.queueManager.changePriority("militaryBuilding", 2*this.Config.priorities.militaryBuilding); - let metadata = { "preferredBase": this.findBestBaseForMilitary(gameState) }; - let plan = new m.ConstructionPlan(gameState, "structures/{civ}_barracks", metadata); + let plan = new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "militaryBase": true }); plan.queueToReset = "militaryBuilding"; queues.militaryBuilding.addPlan(plan); return; } if (numStables == 0) { - let metadata = { "preferredBase": this.findBestBaseForMilitary(gameState) }; - queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_stables", metadata)); + queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_stables", { "militaryBase": true })); return; } // Second barracks and stables if (numBarracks == 1 && gameState.getPopulation() > this.Config.Military.popForBarracks2) { - let metadata = { "preferredBase": this.findBestBaseForMilitary(gameState) }; - queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", metadata)); + queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "militaryBase": true })); return; } if (numStables == 1 && gameState.getPopulation() > this.Config.Military.popForBarracks2) { - let metadata = { "preferredBase": this.findBestBaseForMilitary(gameState) }; - queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_stables", metadata)); + queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_stables", { "militaryBase": true })); return; } // Then 3rd barracks/stables if needed if (numBarracks == 2 && numStables == -1 && gameState.getPopulation() > this.Config.Military.popForBarracks2 + 30) { - let metadata = { "preferredBase": this.findBestBaseForMilitary(gameState) }; - queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", metadata)); + queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "militaryBase": true })); return; } if (numBarracks == -1 && numStables == 2 && gameState.getPopulation() > this.Config.Military.popForBarracks2 + 30) { - let metadata = { "preferredBase": this.findBestBaseForMilitary(gameState) }; - queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_stables", metadata)); + queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_stables", { "militaryBase": true })); return; } } @@ -1806,11 +1800,15 @@ m.HQ.prototype.constructTrainingBuildings = function(gameState, queues) if (this.currentPhase < 3) return; - let numWorkshop = this.canBuild(gameState, "structures/{civ}_siege_workshop") ? gameState.getOwnEntitiesByClass("Workshop", true).length : -1; - if (numWorkshop == 0) + if (this.canBuild(gameState, "structures/{civ}_elephant_stables") && !gameState.getOwnEntitiesByClass("ElephantStables", true).hasEntities()) { - let metadata = { "preferredBase": this.findBestBaseForMilitary(gameState) }; - queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_siege_workshop", metadata)); + queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_elephant_stables", { "militaryBase": true })); + return; + } + + if (this.canBuild(gameState, "structures/{civ}_siege_workshop") && !gameState.getOwnEntitiesByClass("Workshop", true).hasEntities()) + { + queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_siege_workshop", { "militaryBase": true })); return; } @@ -1832,10 +1830,7 @@ m.HQ.prototype.constructTrainingBuildings = function(gameState, queues) if (!template) continue; if (template.hasDefensiveFire() || template.trainableEntities()) - { - let metadata = { "preferredBase": this.findBestBaseForMilitary(gameState) }; - queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, advanced, metadata)); - } + queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, advanced, { "militaryBase": true })); else // not a military building, but still use this queue queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, advanced)); return; @@ -1844,23 +1839,32 @@ m.HQ.prototype.constructTrainingBuildings = function(gameState, queues) }; /** - * Construct military building in bases nearest to the ennemies TODO revisit as the nearest one may not be accessible + * Find base nearest to ennemies for military buildings. */ m.HQ.prototype.findBestBaseForMilitary = function(gameState) { let ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")).toEntityArray(); - let bestBase = 1; + let bestBase; + let enemyFound = false; let distMin = Math.min(); for (let cce of ccEnts) { if (gameState.isPlayerAlly(cce.owner())) continue; + if (enemyFound && !gameState.isPlayerEnemy(cce.owner())) + continue; + let access = m.getLandAccess(gameState, cce); + let isEnemy = gameState.isPlayerEnemy(cce.owner()); for (let cc of ccEnts) { if (cc.owner() != PlayerID) continue; + if (m.getLandAccess(gameState, cc) != access) + continue; let dist = API3.SquareVectorDistance(cc.position(), cce.position()); - if (dist > distMin) + if (!enemyFound && isEnemy) + enemyFound = true; + else if (dist > distMin) continue; bestBase = cc.getMetadata(PlayerID, "base"); distMin = dist; @@ -2449,7 +2453,8 @@ m.HQ.prototype.update = function(gameState, queues, events) this.garrisonManager.update(gameState, events); this.defenseManager.update(gameState, events); - this.constructTrainingBuildings(gameState, queues); + if (gameState.ai.playedTurn % 3 == 0) + this.constructTrainingBuildings(gameState, queues); if (this.Config.difficulty > 0) this.buildDefenses(gameState, queues); diff --git a/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js b/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js index 5488aadbd6..44b8c9a5e9 100644 --- a/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js +++ b/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js @@ -109,10 +109,11 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState) if (template.buildPlacementType() === "shore") return this.findDockPosition(gameState); + let HQ = gameState.ai.HQ; if (template.hasClass("Storehouse") && this.metadata.base) { // recompute the best dropsite location in case some conditions have changed - let base = gameState.ai.HQ.getBaseByID(this.metadata.base); + let base = HQ.getBaseByID(this.metadata.base); let type = this.metadata.type ? this.metadata.type : "wood"; let newpos = base.findBestDropsiteLocation(gameState, type); if (newpos && newpos.quality > 0) @@ -130,10 +131,10 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState) if (this.metadata && this.metadata.resource) { let proximity = this.metadata.proximity ? this.metadata.proximity : undefined; - pos = gameState.ai.HQ.findEconomicCCLocation(gameState, template, this.metadata.resource, proximity); + pos = HQ.findEconomicCCLocation(gameState, template, this.metadata.resource, proximity); } else - pos = gameState.ai.HQ.findStrategicCCLocation(gameState, template); + pos = HQ.findStrategicCCLocation(gameState, template); if (pos) return { "x": pos[0], "z": pos[1], "angle": 3*Math.PI/4, "base": 0 }; @@ -141,7 +142,7 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState) } else if (template.hasClass("DefenseTower") || template.hasClass("Fortress") || template.hasClass("ArmyCamp")) { - let pos = gameState.ai.HQ.findDefensiveLocation(gameState, template); + let pos = HQ.findDefensiveLocation(gameState, template); if (pos) return { "x": pos[0], "z": pos[1], "angle": 3*Math.PI/4, "base": pos[2] }; // if this fortress is our first one, just try the standard placement @@ -150,7 +151,7 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState) } else if (template.hasClass("Market")) // Docks (i.e. NavalMarket) are done before { - let pos = gameState.ai.HQ.findMarketLocation(gameState, template); + let pos = HQ.findMarketLocation(gameState, template); if (pos && pos[2] > 0) { if (!this.metadata) @@ -184,17 +185,17 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState) { let base = this.metadata.base; for (let j = 0; j < placement.map.length; ++j) - if (gameState.ai.HQ.basesMap.map[j] == base) + if (HQ.basesMap.map[j] == base) placement.map[j] = 45; } else { for (let j = 0; j < placement.map.length; ++j) - if (gameState.ai.HQ.basesMap.map[j] !== 0) + if (HQ.basesMap.map[j] != 0) placement.map[j] = 45; } - if (!gameState.ai.HQ.requireHouses || !template.hasClass("House")) + if (!HQ.requireHouses || !template.hasClass("House")) { gameState.getOwnStructures().forEach(function(ent) { let pos = ent.position(); @@ -236,7 +237,7 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState) { let value = placement.map[j] - gameState.sharedScript.resourceMaps.wood.map[j]/3; placement.map[j] = value >= 0 ? value : 0; - if (gameState.ai.HQ.borderMap.map[j] & m.fullBorder_Mask) + if (HQ.borderMap.map[j] & m.fullBorder_Mask) placement.map[j] /= 2; // we need space around farmstead, so disfavor map border } } @@ -246,24 +247,24 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState) // and if our first market, put it on border if possible to maximize distance with next market let favorBorder = template.hasClass("BarterMarket"); let disfavorBorder = gameState.currentPhase() > 1 && !template.hasDefensiveFire(); - let preferredBase = this.metadata && this.metadata.preferredBase; + let militaryBase = this.metadata && this.metadata.militaryBase ? HQ.findBestBaseForMilitary(gameState) : undefined; if (this.metadata && this.metadata.base !== undefined) { let base = this.metadata.base; for (let j = 0; j < placement.map.length; ++j) { - if (gameState.ai.HQ.basesMap.map[j] != base) + if (HQ.basesMap.map[j] != base) placement.map[j] = 0; - else if (favorBorder && gameState.ai.HQ.borderMap.map[j] & m.border_Mask) - placement.map[j] += 50; - else if (disfavorBorder && !(gameState.ai.HQ.borderMap.map[j] & m.fullBorder_Mask) && placement.map[j] > 0) - placement.map[j] += 10; - - if (placement.map[j] > 0) + else if (placement.map[j] > 0) { + if (favorBorder && HQ.borderMap.map[j] & m.border_Mask) + placement.map[j] += 50; + else if (disfavorBorder && !(HQ.borderMap.map[j] & m.fullBorder_Mask)) + placement.map[j] += 10; + let x = (j % placement.width + 0.5) * cellSize; let z = (Math.floor(j / placement.width) + 0.5) * cellSize; - if (gameState.ai.HQ.isNearInvadingArmy([x, z])) + if (HQ.isNearInvadingArmy([x, z])) placement.map[j] = 0; } } @@ -272,22 +273,21 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState) { for (let j = 0; j < placement.map.length; ++j) { - if (gameState.ai.HQ.basesMap.map[j] === 0) + if (HQ.basesMap.map[j] == 0) placement.map[j] = 0; - else if (favorBorder && gameState.ai.HQ.borderMap.map[j] & m.border_Mask) - placement.map[j] += 50; - else if (disfavorBorder && !(gameState.ai.HQ.borderMap.map[j] & m.fullBorder_Mask) && placement.map[j] > 0) - placement.map[j] += 10; - - if (preferredBase && gameState.ai.HQ.basesMap.map[j] == this.metadata.preferredBase) - placement.map[j] += 200; - - if (placement.map[j] > 0) + else if (placement.map[j] > 0) { + if (favorBorder && HQ.borderMap.map[j] & m.border_Mask) + placement.map[j] += 50; + else if (disfavorBorder && !(HQ.borderMap.map[j] & m.fullBorder_Mask)) + placement.map[j] += 10; + let x = (j % placement.width + 0.5) * cellSize; let z = (Math.floor(j / placement.width) + 0.5) * cellSize; - if (gameState.ai.HQ.isNearInvadingArmy([x, z])) + if (HQ.isNearInvadingArmy([x, z])) placement.map[j] = 0; + else if (militaryBase && HQ.basesMap.map[j] == militaryBase) + placement.map[j] += 200; } } } @@ -333,7 +333,7 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState) let territorypos = placement.gamePosToMapPos([x, z]); let territoryIndex = territorypos[0] + territorypos[1]*placement.width; // default angle = 3*Math.PI/4; - return { "x": x, "z": z, "angle": 3*Math.PI/4, "base": gameState.ai.HQ.basesMap.map[territoryIndex] }; + return { "x": x, "z": z, "angle": 3*Math.PI/4, "base": HQ.basesMap.map[territoryIndex] }; }; /**