petra: react to more capture events and some tweaks on attacks

This was SVN commit r16559.
This commit is contained in:
mimo
2015-04-21 17:15:19 +00:00
parent 2972cb3202
commit 9be10939cb
4 changed files with 83 additions and 54 deletions
@@ -64,7 +64,7 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, data)
// priority of the queues we'll create.
var priority = 70;
// priority is relative. If all are 0, the only relevant criteria is "currentsize/targetsize".
// unitStat priority is relative. If all are 0, the only relevant criteria is "currentsize/targetsize".
// if not, this is a "bonus". The higher the priority, the faster this unit will get built.
// Should really be clamped to [0.1-1.5] (assuming 1 is default/the norm)
// Eg: if all are priority 1, and the siege is 0.5, the siege units will get built
@@ -470,6 +470,16 @@ m.AttackPlan.prototype.updatePreparation = function(gameState)
if (this.canBuildUnits)
{
// We still have time left to recruit units and do stuffs.
if (!this.unitStat["Siege"])
{
var numSiegeBuilder = 0;
if (gameState.civ() !== "mace" && gameState.civ() !== "maur")
numSiegeBuilder += gameState.getOwnEntitiesByClass("Fortress", true).filter(API3.Filters.isBuilt()).length;
if (gameState.civ() === "mace" || gameState.civ() === "maur" || gameState.civ() === "rome")
numSiegeBuilder += gameState.countEntitiesByType(this.bAdvanced[0], true);
if (numSiegeBuilder > 0)
this.addSiegeUnits(gameState);
}
this.trainMoreUnits(gameState);
// may happen if we have no more training facilities and build orders are canceled
if (this.buildOrder.length == 0)
@@ -1385,9 +1395,16 @@ m.AttackPlan.prototype.update = function(gameState, events)
targetClassesUnit = {"attack": ["Unit", "Structure"], "avoid": ["Fortress", "StoneWall"], "vetoEntities": veto};
}
if (this.target.hasClass("Structure"))
targetClassesSiege = {"attack": ["Structure"], "vetoEntities": veto};
targetClassesSiege = {"attack": ["Structure"], "avoid": [], "vetoEntities": veto};
else
targetClassesSiege = {"attack": ["Unit", "Structure"], "vetoEntities": veto};
targetClassesSiege = {"attack": ["Unit", "Structure"], "avoid": [], "vetoEntities": veto};
// do not loose time destroying buildings which do not help enemy's defense and can be easily captured later
if (this.target.getDefaultArrow() || this.target.getArrowMultiplier())
{
targetClassesUnit.avoid = targetClassesUnit.avoid.concat("House", "Storehouse", "Farmstead", "Field", "Blacksmith");
targetClassesSiege.avoid = targetClassesSiege.avoid.concat("House", "Storehouse", "Farmstead", "Field", "Blacksmith");
}
if (this.unitCollUpdateArray === undefined || this.unitCollUpdateArray.length == 0)
this.unitCollUpdateArray = this.unitCollection.toIdArray();
@@ -1509,7 +1526,7 @@ m.AttackPlan.prototype.update = function(gameState, events)
{
if (!ent.hasClass("Ranged"))
{
let targetClasses = {"attack": targetClassesSiege.attack, "avoid": ["Ship"], "vetoEntities": veto};
let targetClasses = {"attack": targetClassesSiege.attack, "avoid": targetClassesSiege.avoid.concat("Ship"), "vetoEntities": veto};
ent.attackMove(this.targetPos[0], this.targetPos[1], targetClasses);
}
else
@@ -110,21 +110,24 @@ m.BaseManager.prototype.checkEvents = function (gameState, events, queues)
if (ent.resourceDropsiteTypes() && !ent.hasClass("Elephant"))
this.removeDropsite(gameState, ent);
if (evt.metadata[PlayerID]["baseAnchor"] && evt.metadata[PlayerID]["baseAnchor"] === true)
{
// sounds like we lost our anchor. Let's reaffect our units and buildings
this.anchor = undefined;
this.anchorId = undefined;
this.neededDefenders = 0;
let bestbase = m.getBestBase(ent, gameState);
this.newbaseID = bestbase.ID;
for (let entity of this.units.values())
bestbase.assignEntity(gameState, entity);
for (let entity of this.buildings.values())
bestbase.assignEntity(gameState, entity);
}
this.anchorLost(gameState, ent);
}
}
let captureEvents = events["OwnershipChanged"];
for (let evt of captureEvents)
{
if (evt.from !== PlayerID)
continue;
let ent = gameState.getEntityById(evt.entity);
if (!ent || ent.getMetadata(PlayerID, "base") !== this.ID)
continue;
if (ent.resourceDropsiteTypes() && !ent.hasClass("Elephant"))
this.removeDropsite(gameState, ent);
if (ent.getMetadata(PlayerID, "baseAnchor") === true)
this.anchorLost(gameState, ent);
}
for (var evt of cFinishedEvents)
{
if (!evt || !evt.newentity)
@@ -163,6 +166,20 @@ m.BaseManager.prototype.checkEvents = function (gameState, events, queues)
}
};
/* we lost our anchor. Let's reaffect our units and buildings */
m.BaseManager.prototype.anchorLost = function (gameState, ent)
{
this.anchor = undefined;
this.anchorId = undefined;
this.neededDefenders = 0;
let bestbase = m.getBestBase(ent, gameState);
this.newbaseID = bestbase.ID;
for (let entity of this.units.values())
bestbase.assignEntity(gameState, entity);
for (let entity of this.buildings.values())
bestbase.assignEntity(gameState, entity);
};
/**
* Assign the resources around the dropsites of this basis in three areas according to distance, and sort them in each area.
* Moving resources (animals) and buildable resources (fields) are treated elsewhere.
@@ -289,6 +289,9 @@ m.HQ.prototype.OnCityPhase = function(gameState)
if (this.Config.difficulty > 2 && this.femaleRatio > 0.3)
this.femaleRatio = 0.3;
// increase the priority of defense buildings to free this queue for our first fortress
gameState.ai.queueManager.changePriority("defenseBuilding", 2*this.Config.priorities.defenseBuilding);
this.phaseStarted = 3;
};
@@ -1249,50 +1252,34 @@ m.HQ.prototype.buildNewBase = function(gameState, queues, resource)
// Deals with building fortresses and towers along our border with enemies.
m.HQ.prototype.buildDefenses = function(gameState, queues)
{
if (this.saveResources)
if (this.saveResources || queues.defenseBuilding.length() !== 0)
return;
if (gameState.currentPhase() > 2 || gameState.isResearching(gameState.cityPhase()))
{
// try to build fortresses
var fortressType = "structures/{civ}_fortress";
if (queues.defenseBuilding.length() == 0 && this.canBuild(gameState, fortressType))
if (this.canBuild(gameState, "structures/{civ}_fortress"))
{
var numFortresses = gameState.getOwnEntitiesByClass("Fortress", true).length;
if (gameState.ai.elapsedTime > (1 + 0.10*numFortresses)*this.fortressLapseTime + this.fortressStartTime)
let numFortresses = gameState.getOwnEntitiesByClass("Fortress", true).length;
if ((numFortresses == 0 || gameState.ai.elapsedTime > (1 + 0.10*numFortresses)*this.fortressLapseTime + this.fortressStartTime)
&& gameState.getOwnFoundationsByClass("Fortress").length < 2)
{
this.fortressStartTime = gameState.ai.elapsedTime;
queues.defenseBuilding.addItem(new m.ConstructionPlan(gameState, fortressType));
if (numFortresses == 0)
gameState.ai.queueManager.changePriority("defenseBuilding", 2*this.Config.priorities.defenseBuilding);
let plan = new m.ConstructionPlan(gameState, "structures/{civ}_fortress");
plan.onStart = function(gameState) { gameState.ai.queueManager.changePriority("defenseBuilding", gameState.ai.Config.priorities.defenseBuilding); };
queues.defenseBuilding.addItem(plan);
}
}
// let's add a siege building plan to the current attack plan if there is none currently.
var numSiegeBuilder = 0;
if (gameState.civ() !== "mace" && gameState.civ() !== "maur")
numSiegeBuilder += gameState.getOwnEntitiesByClass("Fortress", true).filter(API3.Filters.isBuilt()).length;
if (gameState.civ() === "mace" || gameState.civ() === "maur" || gameState.civ() === "rome")
numSiegeBuilder += gameState.countEntitiesByType(this.bAdvanced[0], true);
if (numSiegeBuilder > 0)
{
var attack = undefined;
// There can only be one upcoming attack
if (this.attackManager.upcomingAttacks["Attack"].length != 0)
attack = this.attackManager.upcomingAttacks["Attack"][0];
else if (this.attackManager.upcomingAttacks["HugeAttack"].length != 0)
attack = this.attackManager.upcomingAttacks["HugeAttack"][0];
if (attack && !attack.unitStat["Siege"])
attack.addSiegeUnits(gameState);
}
}
if (gameState.currentPhase() < 2
|| queues.defenseBuilding.length() != 0
|| !this.canBuild(gameState, "structures/{civ}_defense_tower"))
if (gameState.currentPhase() < 2 || !this.canBuild(gameState, "structures/{civ}_defense_tower"))
return;
var numTowers = gameState.getOwnEntitiesByClass("DefenseTower", true).length;
if (gameState.ai.elapsedTime > (1 + 0.10*numTowers)*this.towerLapseTime + this.towerStartTime)
let numTowers = gameState.getOwnEntitiesByClass("DefenseTower", true).length;
if ((numTowers == 0 || gameState.ai.elapsedTime > (1 + 0.10*numTowers)*this.towerLapseTime + this.towerStartTime)
&& gameState.getOwnFoundationsByClass("DefenseTower").length < 3)
{
this.towerStartTime = gameState.ai.elapsedTime;
queues.defenseBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_defense_tower"));
@@ -322,7 +322,7 @@ m.TradeManager.prototype.checkEvents = function(gameState, events)
}
}
// if one market is destroyed or built, we may have to look for a better route
// if one market is destroyed or built (i.e. its foundation is destroyed), we should look for a better route
let destroyEvents = events["Destroy"];
for (let evt of destroyEvents)
{
@@ -331,13 +331,21 @@ m.TradeManager.prototype.checkEvents = function(gameState, events)
let ent = evt.entityObj;
if (!ent || !ent.hasClass("Market") || !gameState.isPlayerAlly(ent.owner()))
continue;
if (this.Config.debug > 1)
{
if (evt.SuccessfulFoundation)
API3.warn("new market build ... checking routes");
else
API3.warn("one market (or foundation) has been destroyed ... checking routes");
}
this.routeProspection = true;
gameState.ai.HQ.restartBuild(gameState, "structures/{civ}_market");
gameState.ai.HQ.restartBuild(gameState, "structures/{civ}_dock");
return true;
}
// same thing for captured markets
let capturedEvents = events["OwnershipChanged"];
for (let evt of capturedEvents)
{
if (!gameState.isPlayerAlly(evt.from) && !gameState.isPlayerAlly(evt.to))
continue;
let ent = gameState.getEntityById(evt.entity);
if (!ent || !ent.hasClass("Market"))
continue;
this.routeProspection = true;
gameState.ai.HQ.restartBuild(gameState, "structures/{civ}_market");
gameState.ai.HQ.restartBuild(gameState, "structures/{civ}_dock");