2012-08-03 17:52:18 +02:00
|
|
|
var BuildingConstructionPlan = function(gameState, type, position) {
|
|
|
|
this.type = gameState.applyCiv(type);
|
|
|
|
this.position = position;
|
|
|
|
|
|
|
|
this.template = gameState.getTemplate(this.type);
|
|
|
|
if (!this.template) {
|
|
|
|
this.invalidTemplate = true;
|
|
|
|
this.template = undefined;
|
|
|
|
debug("Cannot build " + this.type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.category = "building";
|
|
|
|
this.cost = new Resources(this.template.cost());
|
|
|
|
this.number = 1; // The number of buildings to build
|
|
|
|
};
|
|
|
|
|
|
|
|
BuildingConstructionPlan.prototype.canExecute = function(gameState) {
|
|
|
|
if (this.invalidTemplate){
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: verify numeric limits etc
|
2013-03-05 23:52:48 +01:00
|
|
|
if (this.template.requiredTech() && !gameState.isResearched(this.template.requiredTech()))
|
|
|
|
return false;
|
|
|
|
|
2012-08-03 17:52:18 +02:00
|
|
|
var builders = gameState.findBuilders(this.type);
|
|
|
|
|
|
|
|
return (builders.length != 0);
|
|
|
|
};
|
|
|
|
|
|
|
|
BuildingConstructionPlan.prototype.execute = function(gameState) {
|
|
|
|
|
|
|
|
var builders = gameState.findBuilders(this.type).toEntityArray();
|
|
|
|
|
|
|
|
// We don't care which builder we assign, since they won't actually
|
|
|
|
// do the building themselves - all we care about is that there is
|
|
|
|
// some unit that can start the foundation
|
|
|
|
|
|
|
|
var pos = this.findGoodPosition(gameState);
|
|
|
|
if (!pos){
|
2013-03-06 12:52:41 +01:00
|
|
|
if (this.template.hasClass("Naval"))
|
|
|
|
gameState.ai.modules.economy.dockFailed = true;
|
2012-08-03 17:52:18 +02:00
|
|
|
debug("No room to place " + this.type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-05 23:52:48 +01:00
|
|
|
if (gameState.getTemplate(this.type).buildCategory() === "Dock")
|
|
|
|
{
|
|
|
|
for (var angle = 0; angle < Math.PI * 2; angle += Math.PI/4)
|
|
|
|
{
|
|
|
|
builders[0].construct(this.type, pos.x, pos.z, angle);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
builders[0].construct(this.type, pos.x, pos.z, pos.angle);
|
2012-08-03 17:52:18 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
BuildingConstructionPlan.prototype.getCost = function() {
|
|
|
|
return this.cost;
|
|
|
|
};
|
|
|
|
|
|
|
|
BuildingConstructionPlan.prototype.findGoodPosition = function(gameState) {
|
|
|
|
var template = gameState.getTemplate(this.type);
|
|
|
|
|
|
|
|
var cellSize = gameState.cellSize; // size of each tile
|
|
|
|
|
|
|
|
// First, find all tiles that are far enough away from obstructions:
|
|
|
|
|
|
|
|
var obstructionMap = Map.createObstructionMap(gameState,template);
|
|
|
|
|
2013-03-05 23:52:48 +01:00
|
|
|
//obstructionMap.dumpIm(template.buildCategory() + "_obstructions.png");
|
|
|
|
|
|
|
|
if (template.buildCategory() !== "Dock")
|
2013-03-16 10:59:43 +01:00
|
|
|
obstructionMap.expandInfluences();
|
2012-08-03 17:52:18 +02:00
|
|
|
|
|
|
|
// Compute each tile's closeness to friendly structures:
|
|
|
|
|
|
|
|
var friendlyTiles = new Map(gameState);
|
|
|
|
|
2012-08-10 18:33:58 +02:00
|
|
|
var alreadyHasHouses = false;
|
|
|
|
|
2012-08-03 17:52:18 +02:00
|
|
|
// If a position was specified then place the building as close to it as possible
|
|
|
|
if (this.position){
|
|
|
|
var x = Math.round(this.position[0] / cellSize);
|
|
|
|
var z = Math.round(this.position[1] / cellSize);
|
|
|
|
friendlyTiles.addInfluence(x, z, 200);
|
2013-03-05 23:52:48 +01:00
|
|
|
} else {
|
2012-08-03 17:52:18 +02:00
|
|
|
// No position was specified so try and find a sensible place to build
|
|
|
|
gameState.getOwnEntities().forEach(function(ent) {
|
|
|
|
if (ent.hasClass("Structure")) {
|
|
|
|
var infl = 32;
|
|
|
|
if (ent.hasClass("CivCentre"))
|
|
|
|
infl *= 4;
|
|
|
|
|
|
|
|
var pos = ent.position();
|
|
|
|
var x = Math.round(pos[0] / cellSize);
|
|
|
|
var z = Math.round(pos[1] / cellSize);
|
|
|
|
|
|
|
|
if (ent.buildCategory() == "Wall") { // no real blockers, but can't build where they are
|
|
|
|
friendlyTiles.addInfluence(x, z, 2,-1000);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (template._template.BuildRestrictions.Category === "Field"){
|
|
|
|
if (ent.resourceDropsiteTypes() && ent.resourceDropsiteTypes().indexOf("food") !== -1){
|
|
|
|
if (ent.hasClass("CivCentre"))
|
|
|
|
friendlyTiles.addInfluence(x, z, infl/4, infl);
|
|
|
|
else
|
|
|
|
friendlyTiles.addInfluence(x, z, infl, infl);
|
|
|
|
|
|
|
|
}
|
|
|
|
}else{
|
2012-08-10 18:33:58 +02:00
|
|
|
if (template.genericName() == "House" && ent.genericName() == "House") {
|
|
|
|
friendlyTiles.addInfluence(x, z, 15.0,20,'linear'); // houses are close to other houses
|
|
|
|
alreadyHasHouses = true;
|
2013-03-12 22:58:25 +01:00
|
|
|
} else if (template.hasClass("GarrisonFortress") && ent.genericName() == "House")
|
|
|
|
{
|
|
|
|
friendlyTiles.addInfluence(x, z, 30, -50);
|
2012-08-10 18:33:58 +02:00
|
|
|
} else if (template.genericName() == "House") {
|
2012-08-03 17:52:18 +02:00
|
|
|
friendlyTiles.addInfluence(x, z, Math.ceil(infl/2.0),infl); // houses are farther away from other buildings but houses
|
|
|
|
friendlyTiles.addInfluence(x, z, Math.ceil(infl/4.0),-infl/2.0); // houses are farther away from other buildings but houses
|
2013-03-11 20:58:29 +01:00
|
|
|
} else if (template.hasClass("GarrisonFortress"))
|
|
|
|
{
|
2013-03-12 22:58:25 +01:00
|
|
|
friendlyTiles.addInfluence(x, z, 20, 10);
|
2013-03-13 21:44:48 +01:00
|
|
|
friendlyTiles.addInfluence(x, z, 10, -40, 'linear');
|
2012-08-03 17:52:18 +02:00
|
|
|
} else if (ent.genericName() != "House") // houses have no influence on other buildings
|
2013-03-10 08:37:05 +01:00
|
|
|
{
|
2012-08-03 17:52:18 +02:00
|
|
|
friendlyTiles.addInfluence(x, z, infl);
|
2013-03-10 08:37:05 +01:00
|
|
|
//avoid building too close to each other if possible.
|
|
|
|
friendlyTiles.addInfluence(x, z, 5, -5, 'linear');
|
|
|
|
}
|
2012-08-03 17:52:18 +02:00
|
|
|
// If this is not a field add a negative influence near the CivCentre because we want to leave this
|
|
|
|
// area for fields.
|
|
|
|
if (ent.hasClass("CivCentre") && template.genericName() != "House"){
|
|
|
|
friendlyTiles.addInfluence(x, z, Math.floor(infl/8), Math.floor(-infl/2));
|
|
|
|
} else if (ent.hasClass("CivCentre")) {
|
2012-08-10 18:33:58 +02:00
|
|
|
friendlyTiles.addInfluence(x, z, infl/3.0, infl + 1);
|
|
|
|
friendlyTiles.addInfluence(x, z, Math.ceil(infl/5.0), -(infl/2.0), 'linear');
|
2012-08-03 17:52:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-03-10 08:37:05 +01:00
|
|
|
//friendlyTiles.dumpIm(template.buildCategory() + "_" +gameState.getTimeElapsed() + ".png", 200);
|
2012-08-10 18:33:58 +02:00
|
|
|
|
2012-08-03 17:52:18 +02:00
|
|
|
// Find target building's approximate obstruction radius, and expand by a bit to make sure we're not too close, this
|
|
|
|
// allows room for units to walk between buildings.
|
|
|
|
// note: not for houses and dropsites who ought to be closer to either each other or a resource.
|
|
|
|
// also not for fields who can be stacked quite a bit
|
2013-03-05 23:52:48 +01:00
|
|
|
var radius = 0;
|
2012-08-03 17:52:18 +02:00
|
|
|
if (template.genericName() == "Field")
|
2013-03-11 20:58:29 +01:00
|
|
|
radius = Math.ceil(template.obstructionRadius() / cellSize) - 0.4;
|
2013-03-12 22:58:25 +01:00
|
|
|
else if (template.hasClass("GarrisonFortress"))
|
2013-03-13 21:44:48 +01:00
|
|
|
radius = Math.ceil(template.obstructionRadius() / cellSize) + 2;
|
2012-08-03 17:52:18 +02:00
|
|
|
else if (template.buildCategory() === "Dock")
|
2013-03-05 23:52:48 +01:00
|
|
|
radius = 1;//Math.floor(template.obstructionRadius() / cellSize);
|
2013-03-13 21:44:48 +01:00
|
|
|
else if (!template.hasClass("DropsiteWood") && !template.hasClass("DropsiteStone") && !template.hasClass("DropsiteMetal"))
|
|
|
|
radius = Math.ceil(template.obstructionRadius() / cellSize + 1);
|
2013-03-11 20:58:29 +01:00
|
|
|
else
|
2013-03-13 21:44:48 +01:00
|
|
|
radius = Math.ceil(template.obstructionRadius() / cellSize);
|
2012-08-05 13:19:31 +02:00
|
|
|
|
2012-08-10 18:33:58 +02:00
|
|
|
// further contract cause walls
|
2013-03-05 23:52:48 +01:00
|
|
|
// Note: I'm currently destroying them so that doesn't matter.
|
|
|
|
//if (gameState.playerData.civ == "iber")
|
|
|
|
// radius *= 0.95;
|
2012-08-03 17:52:18 +02:00
|
|
|
|
2012-08-10 18:33:58 +02:00
|
|
|
// Find the best non-obstructed
|
|
|
|
if (template.genericName() == "House" && !alreadyHasHouses) {
|
|
|
|
// try to get some space first
|
|
|
|
var bestTile = friendlyTiles.findBestTile(10, obstructionMap);
|
|
|
|
var bestIdx = bestTile[0];
|
|
|
|
var bestVal = bestTile[1];
|
|
|
|
}
|
2013-03-05 23:52:48 +01:00
|
|
|
|
2012-08-10 18:33:58 +02:00
|
|
|
if (bestVal === undefined || bestVal === -1) {
|
|
|
|
var bestTile = friendlyTiles.findBestTile(radius, obstructionMap);
|
|
|
|
var bestIdx = bestTile[0];
|
|
|
|
var bestVal = bestTile[1];
|
|
|
|
}
|
2013-03-05 23:52:48 +01:00
|
|
|
if (bestVal === -1) {
|
2012-08-03 17:52:18 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-03-05 23:52:48 +01:00
|
|
|
//friendlyTiles.setInfluence((bestIdx % friendlyTiles.width), Math.floor(bestIdx / friendlyTiles.width), 1, 200);
|
|
|
|
//friendlyTiles.dumpIm(template.buildCategory() + "_" +gameState.getTimeElapsed() + ".png", 200);
|
|
|
|
|
2012-08-03 17:52:18 +02:00
|
|
|
var x = ((bestIdx % friendlyTiles.width) + 0.5) * cellSize;
|
|
|
|
var z = (Math.floor(bestIdx / friendlyTiles.width) + 0.5) * cellSize;
|
|
|
|
|
|
|
|
// default angle
|
|
|
|
var angle = 3*Math.PI/4;
|
2013-03-05 23:52:48 +01:00
|
|
|
|
2012-08-03 17:52:18 +02:00
|
|
|
return {
|
|
|
|
"x" : x,
|
|
|
|
"z" : z,
|
|
|
|
"angle" : angle
|
|
|
|
};
|
|
|
|
};
|