Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/baseManager.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/baseManager.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/baseManager.js @@ -245,12 +245,37 @@ }; /** - * Returns the position of the best place to build a new dropsite for the specified resource + * @return {Object} - The position of the best place to build a new dropsite for the specified resource, + * its quality and its template name. */ -PETRA.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource) +PETRA.BaseManager.prototype.findBestDropsiteAndLocation = function(gameState, resource) { + let bestResult = { + "quality": 0, + "pos": [0, 0] + }; + for (const templateName of gameState.ai.HQ.buildManager.findStructuresByFilter(gameState, API3.Filters.isDropsite(resource))) + { + const dp = this.findBestDropsiteLocation(gameState, resource, templateName); + if (dp.quality < bestResult.quality) + continue; + bestResult = dp; + bestResult.templateName = templateName; + } + return bestResult; +}; + +/** + * Returns the position of the best place to build a new dropsite for the specified resource and dropsite template. + */ +PETRA.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource, templateName) +{ + const template = gameState.getTemplate(gameState.applyCiv(templateName)); + + // CCs and Docks are handled elsewhere. + if (template.hasClass("CivCentre") || template.hasClass("Dock")) + return { "quality": 0, "pos": [0, 0] }; - let template = gameState.getTemplate(gameState.applyCiv("structures/{civ}/storehouse")); let halfSize = 0; if (template.get("Footprint/Square")) halfSize = Math.max(+template.get("Footprint/Square/@depth"), +template.get("Footprint/Square/@width")) / 2; @@ -430,17 +455,17 @@ let ratio = this.gatherers[type].lost / total; if (ratio > 0.15) { - let newDP = this.findBestDropsiteLocation(gameState, type); - if (newDP.quality > 50 && gameState.ai.HQ.canBuild(gameState, "structures/{civ}/storehouse")) - queues.dropsites.addPlan(new PETRA.ConstructionPlan(gameState, "structures/{civ}/storehouse", { "base": this.ID, "type": type }, newDP.pos)); + const newDP = this.findBestDropsiteAndLocation(gameState, type); + if (newDP.quality > 50 && gameState.ai.HQ.canBuild(gameState, newDP.templateName)) + queues.dropsites.addPlan(new PETRA.ConstructionPlan(gameState, newDP.templateName, { "base": this.ID, "type": type }, newDP.pos)); else if (!gameState.getOwnFoundations().filter(API3.Filters.byClass("CivCentre")).hasEntities() && !queues.civilCentre.hasQueuedUnits()) { // No good dropsite, try to build a new base if no base already planned, // and if not possible, be less strict on dropsite quality. if ((!gameState.ai.HQ.canExpand || !gameState.ai.HQ.buildNewBase(gameState, queues, type)) && newDP.quality > Math.min(25, 50*0.15/ratio) && - gameState.ai.HQ.canBuild(gameState, "structures/{civ}/storehouse")) - queues.dropsites.addPlan(new PETRA.ConstructionPlan(gameState, "structures/{civ}/storehouse", { "base": this.ID, "type": type }, newDP.pos)); + gameState.ai.HQ.canBuild(gameState, newDP.templateName)) + queues.dropsites.addPlan(new PETRA.ConstructionPlan(gameState, newDP.templateName, { "base": this.ID, "type": type }, newDP.pos)); } } this.gatherers[type].nextCheck = gameState.ai.playedTurn + 20; Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/buildManager.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/buildManager.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/buildManager.js @@ -98,22 +98,31 @@ /** - * Get the first buildable structure with a given class - * TODO when several available, choose the best one + * Get the buildable structures passing a filter. */ -PETRA.BuildManager.prototype.findStructureWithClass = function(gameState, classes) +PETRA.BuildManager.prototype.findStructuresByFilter = function(gameState, filter) { + const result = []; for (let [templateName, count] of this.builderCounters) { - if (count == 0 || gameState.isTemplateDisabled(templateName)) + if (!count || gameState.isTemplateDisabled(templateName)) continue; let template = gameState.getTemplate(templateName); if (!template || !template.available(gameState)) continue; - if (MatchesClassList(template.classes(), classes)) - return templateName; + if (filter.func(template)) + result.push(templateName); } - return undefined; + return result; +}; + +/** + * Get the first buildable structure with a given class + * TODO when several available, choose the best one + */ +PETRA.BuildManager.prototype.findStructureWithClass = function(gameState, classes) +{ + return this.findStructuresByFilter(gameState, API3.Filters.byClassesOr(classes))[0]; }; PETRA.BuildManager.prototype.hasBuilder = function(template) Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js @@ -119,7 +119,7 @@ // recompute the best dropsite location in case some conditions have changed let base = HQ.getBaseByID(this.metadata.base); let type = this.metadata.type ? this.metadata.type : "wood"; - let newpos = base.findBestDropsiteLocation(gameState, type); + const newpos = base.findBestDropsiteLocation(gameState, type, template._templateName); if (newpos && newpos.quality > 0) { let pos = newpos.pos; Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js @@ -545,11 +545,10 @@ } // immediatly build a wood dropsite if possible. - let template = gameState.applyCiv("structures/{civ}/storehouse"); - if (!gameState.getOwnEntitiesByClass("Storehouse", true).hasEntities() && this.canBuild(gameState, template)) + if (!gameState.getOwnEntitiesByClass("DropsiteWood", true).hasEntities()) { - let newDP = this.baseManagers[1].findBestDropsiteLocation(gameState, "wood"); - if (newDP.quality > 40) + const newDP = this.baseManagers[1].findBestDropsiteAndLocation(gameState, "wood"); + if (newDP.quality > 40 && this.canBuild(gameState, newDP.templateName)) { // if we start with enough workers, put our available resources in this first dropsite // same thing if our pop exceed the allowed one, as we will need several houses @@ -557,16 +556,16 @@ if (numWorkers > 12 && newDP.quality > 60 || gameState.getPopulation() > gameState.getPopulationLimit() + 20) { - let cost = new API3.Resources(gameState.getTemplate(template).cost()); + const cost = new API3.Resources(gameState.getTemplate(newDP.templateName).cost()); gameState.ai.queueManager.setAccounts(gameState, cost, "dropsites"); } - gameState.ai.queues.dropsites.addPlan(new PETRA.ConstructionPlan(gameState, template, { "base": this.baseManagers[1].ID }, newDP.pos)); + gameState.ai.queues.dropsites.addPlan(new PETRA.ConstructionPlan(gameState, newDP.templateName, { "base": this.baseManagers[1].ID }, newDP.pos)); } } // and build immediately a corral if needed if (this.needCorral) { - template = gameState.applyCiv("structures/{civ}/corral"); + const template = gameState.applyCiv("structures/{civ}/corral"); if (!gameState.getOwnEntitiesByClass("Corral", true).hasEntities() && this.canBuild(gameState, template)) gameState.ai.queues.corral.addPlan(new PETRA.ConstructionPlan(gameState, template, { "base": this.baseManagers[1].ID })); }