forked from 0ad/0ad
wraitii
9d02495a96
Improve the AI gamestate to make better use of entity collections, should be very slightly faster, and it's cleaner. Remove enemy watchers that were no longer used. This was SVN commit r14574.
237 lines
7.6 KiB
JavaScript
237 lines
7.6 KiB
JavaScript
var AEGIS = function(m)
|
|
{
|
|
|
|
m.ConstructionPlan = function(gameState, type, metadata, startTime, expectedTime, position) {
|
|
this.type = gameState.applyCiv(type);
|
|
this.position = position;
|
|
|
|
this.metadata = metadata;
|
|
|
|
this.ID = m.playerGlobals[PlayerID].uniqueIDBOPlans++;
|
|
|
|
this.template = gameState.getTemplate(this.type);
|
|
if (!this.template) {
|
|
return false;
|
|
}
|
|
|
|
this.category = "building";
|
|
this.cost = new API3.Resources(this.template.cost());
|
|
this.number = 1; // The number of buildings to build
|
|
|
|
if (!startTime)
|
|
this.startTime = 0;
|
|
else
|
|
this.startTime = startTime;
|
|
|
|
if (!expectedTime)
|
|
this.expectedTime = -1;
|
|
else
|
|
this.expectedTime = expectedTime;
|
|
return true;
|
|
};
|
|
|
|
// return true if we willstart amassing resource for this plan
|
|
m.ConstructionPlan.prototype.isGo = function(gameState) {
|
|
return (gameState.getTimeElapsed() > this.startTime);
|
|
};
|
|
|
|
// checks other than resource ones.
|
|
// TODO: change this.
|
|
m.ConstructionPlan.prototype.canStart = function(gameState) {
|
|
if (gameState.buildingsBuilt > 0)
|
|
return false;
|
|
|
|
if (!this.isGo(gameState))
|
|
return false;
|
|
|
|
// TODO: verify numeric limits etc
|
|
if (this.template.requiredTech() && !gameState.isResearched(this.template.requiredTech()))
|
|
{
|
|
return false;
|
|
}
|
|
var builders = gameState.findBuilders(this.type);
|
|
|
|
return (builders.length != 0);
|
|
};
|
|
|
|
m.ConstructionPlan.prototype.start = 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){
|
|
if (this.template.hasClass("Naval"))
|
|
gameState.ai.HQ.dockFailed = true;
|
|
m.debug("No room to place " + this.type);
|
|
return;
|
|
}
|
|
if (this.template.hasClass("Naval"))
|
|
m.debug (pos);
|
|
gameState.buildingsBuilt++;
|
|
|
|
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, this.metadata);
|
|
}
|
|
} else
|
|
builders[0].construct(this.type, pos.x, pos.z, pos.angle, this.metadata);
|
|
};
|
|
|
|
m.ConstructionPlan.prototype.getCost = function() {
|
|
var costs = new API3.Resources();
|
|
costs.add(this.cost);
|
|
return costs;
|
|
};
|
|
|
|
m.ConstructionPlan.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 = m.createObstructionMap(gameState,0, template);
|
|
|
|
//obstructionMap.dumpIm(template.buildCategory() + "_obstructions_pre.png");
|
|
|
|
if (template.buildCategory() !== "Dock")
|
|
obstructionMap.expandInfluences();
|
|
|
|
//obstructionMap.dumpIm(template.buildCategory() + "_obstructions.png");
|
|
|
|
// Compute each tile's closeness to friendly structures:
|
|
|
|
var friendlyTiles = new API3.Map(gameState.sharedScript);
|
|
|
|
var alreadyHasHouses = false;
|
|
|
|
// If a position was specified then place the building as close to it as possible
|
|
if (this.position) {
|
|
var x = Math.floor(this.position[0] / cellSize);
|
|
var z = Math.floor(this.position[1] / cellSize);
|
|
friendlyTiles.addInfluence(x, z, 255);
|
|
} else {
|
|
// No position was specified so try and find a sensible place to build
|
|
gameState.getOwnStructures().forEach(function(ent) {
|
|
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{
|
|
if (template.genericName() == "House" && ent.genericName() == "House") {
|
|
friendlyTiles.addInfluence(x, z, 15.0,20,'linear'); // houses are close to other houses
|
|
alreadyHasHouses = true;
|
|
} else if (template.hasClass("GarrisonFortress") && ent.genericName() == "House")
|
|
{
|
|
friendlyTiles.addInfluence(x, z, 30, -50);
|
|
} else if (template.genericName() == "House") {
|
|
friendlyTiles.addInfluence(x, z, Math.ceil(infl/4.0),-infl/2.0); // houses are farther away from other buildings but houses
|
|
} else if (template.hasClass("GarrisonFortress"))
|
|
{
|
|
friendlyTiles.addInfluence(x, z, 20, 10);
|
|
friendlyTiles.addInfluence(x, z, 10, -40, 'linear');
|
|
} else if (ent.genericName() != "House") // houses have no influence on other buildings
|
|
{
|
|
friendlyTiles.addInfluence(x, z, infl);
|
|
//avoid building too close to each other if possible.
|
|
friendlyTiles.addInfluence(x, z, 5, -5, 'linear');
|
|
}
|
|
// 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")) {
|
|
friendlyTiles.addInfluence(x, z, infl/3.0, infl + 1);
|
|
friendlyTiles.addInfluence(x, z, Math.ceil(infl/5.0), -(infl/2.0), 'linear');
|
|
}
|
|
}
|
|
});
|
|
if (this.metadata && this.metadata.base !== undefined)
|
|
for (var base in gameState.ai.HQ.baseManagers)
|
|
if (base != this.metadata.base)
|
|
for (var j in gameState.ai.HQ.baseManagers[base].territoryIndices)
|
|
friendlyTiles.map[gameState.ai.HQ.baseManagers[base].territoryIndices[j]] = 0;
|
|
}
|
|
|
|
//friendlyTiles.dumpIm(template.buildCategory() + "_" +gameState.getTimeElapsed() + ".png", 200);
|
|
|
|
// 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
|
|
var radius = 0;
|
|
|
|
if (template.hasClass("GarrisonFortress"))
|
|
radius = Math.floor(template.obstructionRadius() / cellSize) + 2;
|
|
else if (template.buildCategory() === "Dock")
|
|
radius = 1;
|
|
else if (template.resourceDropsiteTypes() === undefined)
|
|
radius = Math.ceil(template.obstructionRadius() / cellSize) + 1;
|
|
else
|
|
radius = Math.ceil(template.obstructionRadius() / cellSize);
|
|
|
|
// further contract cause walls
|
|
// Note: I'm currently destroying them so that doesn't matter.
|
|
//if (gameState.playerData.civ == "iber")
|
|
// radius *= 0.95;
|
|
|
|
// 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];
|
|
}
|
|
|
|
if (bestVal === undefined || bestVal === -1) {
|
|
var bestTile = friendlyTiles.findBestTile(radius, obstructionMap);
|
|
var bestIdx = bestTile[0];
|
|
var bestVal = bestTile[1];
|
|
}
|
|
if (bestVal === -1) {
|
|
return false;
|
|
}
|
|
|
|
//friendlyTiles.setInfluence((bestIdx % friendlyTiles.width), Math.floor(bestIdx / friendlyTiles.width), 1, 200);
|
|
//friendlyTiles.dumpIm(template.buildCategory() + "_" +gameState.getTimeElapsed() + ".png", 200);
|
|
|
|
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;
|
|
|
|
return {
|
|
"x" : x,
|
|
"z" : z,
|
|
"angle" : angle
|
|
};
|
|
};
|
|
|
|
|
|
return m;
|
|
}(AEGIS);
|