forked from 0ad/0ad
AI completes the removal of the JS pathfinder and fixes naval transport
This was SVN commit r16819.
This commit is contained in:
parent
575d708fca
commit
3efa4be02c
@ -73,21 +73,10 @@ m.AttackManager.prototype.checkEvents = function(gameState, events)
|
||||
other = attack.targetPlayer;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!attack.targetPlayer || attack.targetPlayer !== targetPlayer)
|
||||
{
|
||||
let oldTargetPlayer = attack.targetPlayer;
|
||||
let oldTarget = attack.target;
|
||||
attack.targetPlayer = targetPlayer;
|
||||
attack.target = attack.getNearestTarget(gameState, attack.rallyPoint);
|
||||
if (!attack.target)
|
||||
{
|
||||
attack.targetPlayer = oldTargetPlayer;
|
||||
attack.target = oldTarget;
|
||||
continue;
|
||||
}
|
||||
attack.targetPos = attack.target.position();
|
||||
attack.resetPath();
|
||||
}
|
||||
|
||||
if (attack.targetPlayer && attack.targetPlayer === targetPlayer)
|
||||
available += attack.unitCollection.length;
|
||||
}
|
||||
@ -153,7 +142,7 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
|
||||
if (attack.state === "unexecuted")
|
||||
++unexecutedAttacks[attackType];
|
||||
}
|
||||
else if (updateStep === 0 || updateStep === 3)
|
||||
else if (updateStep === 0)
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("Attack Manager: " + attack.getType() + " plan " + attack.getName() + " aborted.");
|
||||
|
@ -24,23 +24,25 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, data)
|
||||
{
|
||||
this.target = undefined;
|
||||
this.targetPos = undefined;
|
||||
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
|
||||
this.targetPlayer = undefined;
|
||||
}
|
||||
|
||||
if (this.targetPlayer === undefined)
|
||||
{
|
||||
this.failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// get a starting rallyPoint ... will be improved later
|
||||
var rallyPoint;
|
||||
let rallyPoint;
|
||||
let rallyAccess;
|
||||
let allAccesses = {};
|
||||
for (let base of gameState.ai.HQ.baseManagers)
|
||||
{
|
||||
if (!base.anchor || !base.anchor.position())
|
||||
continue;
|
||||
rallyPoint = base.anchor.position();
|
||||
break;
|
||||
let access = gameState.ai.accessibility.getAccessValue(base.anchor.position());
|
||||
if (!rallyPoint)
|
||||
{
|
||||
rallyPoint = base.anchor.position();
|
||||
rallyAccess = access;
|
||||
}
|
||||
if (!allAccesses[access])
|
||||
allAccesses[access] = base.anchor.position();
|
||||
}
|
||||
if (!rallyPoint) // no base ? take the position of any of our entities
|
||||
{
|
||||
@ -48,7 +50,10 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, data)
|
||||
{
|
||||
if (!ent.position())
|
||||
continue;
|
||||
let access = gameState.ai.accessibility.getAccessValue(ent.position());
|
||||
rallyPoint = ent.position();
|
||||
rallyAccess = access;
|
||||
allAccesses[access] = rallyPoint;
|
||||
break;
|
||||
}
|
||||
if (!rallyPoint)
|
||||
@ -58,8 +63,30 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, data)
|
||||
}
|
||||
}
|
||||
this.rallyPoint = rallyPoint;
|
||||
|
||||
this.overseas = undefined;
|
||||
this.overseas = 0;
|
||||
if (gameState.ai.HQ.navalMap)
|
||||
{
|
||||
for (let structure of gameState.getEnemyStructures().values())
|
||||
{
|
||||
if (!structure.position())
|
||||
continue;
|
||||
let access = gameState.ai.accessibility.getAccessValue(structure.position());
|
||||
if (access in allAccesses)
|
||||
{
|
||||
this.overseas = 0;
|
||||
this.rallyPoint = allAccesses[access];
|
||||
break;
|
||||
}
|
||||
else if (!this.overseas)
|
||||
{
|
||||
let sea = gameState.ai.HQ.getSeaIndex(gameState, rallyAccess, access);
|
||||
if (!sea)
|
||||
continue;
|
||||
this.overseas = sea;
|
||||
gameState.ai.HQ.navalManager.setMinimalTransportShips(gameState, sea, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.paused = false;
|
||||
this.maxCompletingTurn = 0;
|
||||
|
||||
@ -182,9 +209,9 @@ m.AttackPlan.prototype.init = function(gameState)
|
||||
|
||||
// defining the entity collections. Will look for units I own, that are part of this plan.
|
||||
// Also defining the buildOrders.
|
||||
for (var cat in this.unitStat)
|
||||
for (let cat in this.unitStat)
|
||||
{
|
||||
var Unit = this.unitStat[cat];
|
||||
let Unit = this.unitStat[cat];
|
||||
this.unit[cat] = this.unitCollection.filter(API3.Filters.byClassesAnd(Unit["classes"]));
|
||||
this.unit[cat].registerUpdates();
|
||||
if (this.canBuildUnits)
|
||||
@ -224,9 +251,9 @@ m.AttackPlan.prototype.canStart = function()
|
||||
if (!this.canBuildUnits)
|
||||
return true;
|
||||
|
||||
for (var unitCat in this.unitStat)
|
||||
for (let unitCat in this.unitStat)
|
||||
{
|
||||
var Unit = this.unitStat[unitCat];
|
||||
let Unit = this.unitStat[unitCat];
|
||||
if (this.unit[unitCat].length < Unit["minSize"])
|
||||
return false;
|
||||
}
|
||||
@ -235,7 +262,7 @@ m.AttackPlan.prototype.canStart = function()
|
||||
|
||||
m.AttackPlan.prototype.mustStart = function()
|
||||
{
|
||||
if (this.isPaused() || this.path === undefined)
|
||||
if (this.isPaused())
|
||||
return false;
|
||||
|
||||
if (!this.canBuildUnits)
|
||||
@ -243,9 +270,9 @@ m.AttackPlan.prototype.mustStart = function()
|
||||
|
||||
var MaxReachedEverywhere = true;
|
||||
var MinReachedEverywhere = true;
|
||||
for (var unitCat in this.unitStat)
|
||||
for (let unitCat in this.unitStat)
|
||||
{
|
||||
var Unit = this.unitStat[unitCat];
|
||||
let Unit = this.unitStat[unitCat];
|
||||
if (this.unit[unitCat].length < Unit["targetSize"])
|
||||
MaxReachedEverywhere = false;
|
||||
if (this.unit[unitCat].length < Unit["minSize"])
|
||||
@ -282,9 +309,8 @@ m.AttackPlan.prototype.addBuildOrder = function(gameState, name, unitStats, rese
|
||||
{
|
||||
// no minsize as we don't want the plan to fail at the last minute though.
|
||||
this.unitStat[name] = unitStats;
|
||||
var Unit = this.unitStat[name];
|
||||
var filter = API3.Filters.and(API3.Filters.byClassesAnd(Unit["classes"]), API3.Filters.byMetadata(PlayerID, "plan", this.name));
|
||||
this.unit[name] = gameState.getOwnUnits().filter(filter);
|
||||
let Unit = this.unitStat[name];
|
||||
this.unit[name] = this.unitCollection.filter(API3.Filters.byClassesAnd(Unit["classes"]));
|
||||
this.unit[name].registerUpdates();
|
||||
this.buildOrder.push([0, Unit["classes"], this.unit[name], Unit, name]);
|
||||
if (resetQueue)
|
||||
@ -315,105 +341,33 @@ m.AttackPlan.prototype.addSiegeUnits = function(gameState)
|
||||
};
|
||||
|
||||
// Three returns possible: 1 is "keep going", 0 is "failed plan", 2 is "start"
|
||||
// 3 is a special case: no valid path returned. Right now I stop attacking alltogether.
|
||||
m.AttackPlan.prototype.updatePreparation = function(gameState)
|
||||
{
|
||||
// the completing step is used to return resources and regroup the units
|
||||
// so we check that we have no more forced order before starting the attack
|
||||
if (this.state === "completing")
|
||||
{
|
||||
// check that all units have finished with their transport if needed
|
||||
if (this.waitingForTransport())
|
||||
return 1;
|
||||
// bloqued units which cannot finish their order should not stop the attack
|
||||
if (gameState.ai.playedTurn < this.maxCompletingTurn && this.hasForceOrder())
|
||||
return 1;
|
||||
return 2;
|
||||
// if our target was destroyed, go back to "unexecuted" state
|
||||
if (!this.targetPlayer || !this.target || !gameState.getEntityById(this.target.id()))
|
||||
{
|
||||
this.state === "unexecuted";
|
||||
this.target = undefined;
|
||||
}
|
||||
else
|
||||
{
|
||||
// check that all units have finished with their transport if needed
|
||||
if (this.waitingForTransport())
|
||||
return 1;
|
||||
// bloqued units which cannot finish their order should not stop the attack
|
||||
if (gameState.ai.playedTurn < this.maxCompletingTurn && this.hasForceOrder())
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.Config.debug > 3 && gameState.ai.playedTurn % 50 == 0)
|
||||
this.debugAttack();
|
||||
|
||||
// find our target (if not yet done or because our previous one was destroyed)
|
||||
if (!this.targetPlayer)
|
||||
{
|
||||
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
|
||||
if (!this.targetPlayer)
|
||||
return 0;
|
||||
if (this.target && this.target.owner() !== this.targetPlayer)
|
||||
this.target = undefined;
|
||||
}
|
||||
if (!this.target || !gameState.getEntityById(this.target.id()))
|
||||
{
|
||||
this.target = this.getNearestTarget(gameState, this.rallyPoint);
|
||||
if (!this.target)
|
||||
{
|
||||
// may-be all our previous enemey targets have been destroyed ?
|
||||
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
|
||||
if (this.targetPlayer !== undefined)
|
||||
this.target = this.getNearestTarget(gameState, this.rallyPoint);
|
||||
if (!this.target)
|
||||
return 0;
|
||||
}
|
||||
this.targetPos = this.target.position();
|
||||
// redefine a new rally point for this target if we have a base on the same land
|
||||
// find a new one on the pseudo-nearest base (dist weighted by the size of the island)
|
||||
var targetIndex = gameState.ai.accessibility.getAccessValue(this.targetPos);
|
||||
var rallyIndex = gameState.ai.accessibility.getAccessValue(this.rallyPoint);
|
||||
if (targetIndex !== rallyIndex)
|
||||
{
|
||||
var distminSame = Math.min();
|
||||
var rallySame = undefined;
|
||||
var distminDiff = Math.min();
|
||||
var rallyDiff = undefined;
|
||||
for (var base of gameState.ai.HQ.baseManagers)
|
||||
{
|
||||
var anchor = base.anchor;
|
||||
if (!anchor || !anchor.position())
|
||||
continue;
|
||||
var dist = API3.SquareVectorDistance(anchor.position(), this.targetPos);
|
||||
if (base.accessIndex === targetIndex)
|
||||
{
|
||||
if (dist < distminSame)
|
||||
{
|
||||
distminSame = dist;
|
||||
rallySame = anchor.position();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dist = dist / Math.sqrt(gameState.ai.accessibility.regionSize[base.accessIndex]);
|
||||
if (dist < distminDiff)
|
||||
{
|
||||
distminDiff = dist;
|
||||
rallyDiff = anchor.position();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rallySame)
|
||||
this.rallyPoint = rallySame;
|
||||
else if (rallyDiff)
|
||||
{
|
||||
this.rallyPoint = rallyDiff;
|
||||
this.overseas = gameState.ai.HQ.getSeaIndex(gameState, rallyIndex, targetIndex);
|
||||
if (this.overseas)
|
||||
gameState.ai.HQ.navalManager.setMinimalTransportShips(gameState, this.overseas, this.neededShips);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// reset the path so that we recompute it for this new target
|
||||
this.resetPath();
|
||||
}
|
||||
|
||||
// when we have a target, we path to it.
|
||||
if (!this.path || this.path === "toBeContinued")
|
||||
{
|
||||
let ret = this.getPathToTarget(gameState);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// if we need a transport, wait for some transport ships
|
||||
if (this.overseas && !gameState.ai.HQ.navalManager.seaTransportShips[this.overseas].length)
|
||||
return 1;
|
||||
@ -484,6 +438,12 @@ m.AttackPlan.prototype.updatePreparation = function(gameState)
|
||||
|
||||
// if we're here, it means we must start
|
||||
this.state = "completing";
|
||||
|
||||
if (!this.chooseTarget(gameState))
|
||||
return 0;
|
||||
if (!this.overseas)
|
||||
this.getPathToTarget(gameState);
|
||||
|
||||
if (this.type === "Raid")
|
||||
this.maxCompletingTurn = gameState.ai.playedTurn + 20;
|
||||
else
|
||||
@ -496,28 +456,28 @@ m.AttackPlan.prototype.updatePreparation = function(gameState)
|
||||
|
||||
var rallyPoint = this.rallyPoint;
|
||||
var rallyIndex = gameState.ai.accessibility.getAccessValue(rallyPoint);
|
||||
for (var entity of this.unitCollection.values())
|
||||
for (let ent of this.unitCollection.values())
|
||||
{
|
||||
// For the time being, if occupied in a transport, remove the unit from this plan TODO improve that
|
||||
if (entity.getMetadata(PlayerID, "transport") !== undefined || entity.getMetadata(PlayerID, "transporter") !== undefined)
|
||||
if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined)
|
||||
{
|
||||
entity.setMetadata(PlayerID, "plan", -1);
|
||||
ent.setMetadata(PlayerID, "plan", -1);
|
||||
continue;
|
||||
}
|
||||
entity.setMetadata(PlayerID, "role", "attack");
|
||||
entity.setMetadata(PlayerID, "subrole", "completing");
|
||||
var queued = false;
|
||||
if (entity.resourceCarrying() && entity.resourceCarrying().length)
|
||||
queued = m.returnResources(gameState, entity);
|
||||
var index = gameState.ai.accessibility.getAccessValue(entity.position());
|
||||
ent.setMetadata(PlayerID, "role", "attack");
|
||||
ent.setMetadata(PlayerID, "subrole", "completing");
|
||||
let queued = false;
|
||||
if (ent.resourceCarrying() && ent.resourceCarrying().length)
|
||||
queued = m.returnResources(gameState, ent);
|
||||
let index = gameState.ai.accessibility.getAccessValue(ent.position());
|
||||
if (index === rallyIndex)
|
||||
entity.moveToRange(rallyPoint[0], rallyPoint[1], 0, 15, queued);
|
||||
ent.moveToRange(rallyPoint[0], rallyPoint[1], 0, 15, queued);
|
||||
else
|
||||
gameState.ai.HQ.navalManager.requireTransport(gameState, entity, index, rallyIndex, rallyPoint);
|
||||
gameState.ai.HQ.navalManager.requireTransport(gameState, ent, index, rallyIndex, rallyPoint);
|
||||
}
|
||||
|
||||
// reset all queued units
|
||||
var plan = this.name;
|
||||
let plan = this.name;
|
||||
gameState.ai.queueManager.removeQueue("plan_" + plan);
|
||||
gameState.ai.queueManager.removeQueue("plan_" + plan + "_champ");
|
||||
gameState.ai.queueManager.removeQueue("plan_" + plan + "_siege");
|
||||
@ -540,10 +500,10 @@ m.AttackPlan.prototype.trainMoreUnits = function(gameState)
|
||||
this.buildOrder[i][0] = this.buildOrder[i][2].length + aQueued;
|
||||
}
|
||||
this.buildOrder.sort(function (a,b) {
|
||||
var va = a[0]/a[3]["targetSize"] - a[3]["priority"];
|
||||
let va = a[0]/a[3]["targetSize"] - a[3]["priority"];
|
||||
if (a[0] >= a[3]["targetSize"])
|
||||
va += 1000;
|
||||
var vb = b[0]/b[3]["targetSize"] - b[3]["priority"];
|
||||
let vb = b[0]/b[3]["targetSize"] - b[3]["priority"];
|
||||
if (b[0] >= b[3]["targetSize"])
|
||||
vb += 1000;
|
||||
return va - vb;
|
||||
@ -743,6 +703,75 @@ m.AttackPlan.prototype.reassignCavUnit = function(gameState)
|
||||
raid.unitCollection.updateEnt(found);
|
||||
};
|
||||
|
||||
m.AttackPlan.prototype.chooseTarget = function(gameState)
|
||||
{
|
||||
if (!this.targetPlayer)
|
||||
{
|
||||
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
|
||||
if (!this.targetPlayer)
|
||||
return false;
|
||||
}
|
||||
|
||||
this.target = this.getNearestTarget(gameState, this.rallyPoint);
|
||||
if (!this.target)
|
||||
{
|
||||
// may-be all our previous enemey target (if not recomputed here) have been destroyed ?
|
||||
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
|
||||
if (this.targetPlayer !== undefined)
|
||||
this.target = this.getNearestTarget(gameState, this.rallyPoint);
|
||||
if (!this.target)
|
||||
return false;
|
||||
}
|
||||
this.targetPos = this.target.position();
|
||||
// redefine a new rally point for this target if we have a base on the same land
|
||||
// find a new one on the pseudo-nearest base (dist weighted by the size of the island)
|
||||
let targetIndex = gameState.ai.accessibility.getAccessValue(this.targetPos);
|
||||
let rallyIndex = gameState.ai.accessibility.getAccessValue(this.rallyPoint);
|
||||
if (targetIndex !== rallyIndex)
|
||||
{
|
||||
let distminSame = Math.min();
|
||||
let rallySame;
|
||||
let distminDiff = Math.min();
|
||||
let rallyDiff;
|
||||
for (let base of gameState.ai.HQ.baseManagers)
|
||||
{
|
||||
let anchor = base.anchor;
|
||||
if (!anchor || !anchor.position())
|
||||
continue;
|
||||
let dist = API3.SquareVectorDistance(anchor.position(), this.targetPos);
|
||||
if (base.accessIndex === targetIndex)
|
||||
{
|
||||
if (dist >= distminSame)
|
||||
continue;
|
||||
distminSame = dist;
|
||||
rallySame = anchor.position();
|
||||
}
|
||||
else
|
||||
{
|
||||
dist = dist / Math.sqrt(gameState.ai.accessibility.regionSize[base.accessIndex]);
|
||||
if (dist >= distminDiff)
|
||||
continue;
|
||||
distminDiff = dist;
|
||||
rallyDiff = anchor.position();
|
||||
}
|
||||
}
|
||||
|
||||
if (rallySame)
|
||||
this.rallyPoint = rallySame;
|
||||
else if (rallyDiff)
|
||||
{
|
||||
rallyIndex = gameState.ai.accessibility.getAccessValue(rallyDiff);
|
||||
this.rallyPoint = rallyDiff;
|
||||
this.overseas = gameState.ai.HQ.getSeaIndex(gameState, rallyIndex, targetIndex);
|
||||
if (this.overseas)
|
||||
gameState.ai.HQ.navalManager.setMinimalTransportShips(gameState, this.overseas, this.neededShips);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// sameLand true means that we look for a target for which we do not need to take a transport
|
||||
m.AttackPlan.prototype.getNearestTarget = function(gameState, position, sameLand)
|
||||
{
|
||||
@ -759,7 +788,7 @@ m.AttackPlan.prototype.getNearestTarget = function(gameState, position, sameLand
|
||||
|
||||
// picking the nearest target
|
||||
var minDist = -1;
|
||||
var target = undefined;
|
||||
var target;
|
||||
for (let ent of targets.values())
|
||||
{
|
||||
if (!ent.position())
|
||||
@ -783,7 +812,7 @@ m.AttackPlan.prototype.getNearestTarget = function(gameState, position, sameLand
|
||||
// Default target finder aims for conquest critical targets
|
||||
m.AttackPlan.prototype.defaultTargetFinder = function(gameState, playerEnemy)
|
||||
{
|
||||
var targets = undefined;
|
||||
var targets;
|
||||
if (gameState.getGameType() === "wonder")
|
||||
{
|
||||
targets = gameState.getEnemyStructures(playerEnemy).filter(API3.Filters.byClass("Wonder"));
|
||||
@ -791,7 +820,7 @@ m.AttackPlan.prototype.defaultTargetFinder = function(gameState, playerEnemy)
|
||||
return targets;
|
||||
}
|
||||
|
||||
var targets = gameState.getEnemyStructures(playerEnemy).filter(API3.Filters.byClass("CivCentre"));
|
||||
targets = gameState.getEnemyStructures(playerEnemy).filter(API3.Filters.byClass("CivCentre"));
|
||||
if (!targets.length)
|
||||
targets = gameState.getEnemyStructures(playerEnemy).filter(API3.Filters.byClass("ConquestCritical"));
|
||||
// If there's nothing, attack anything else that's less critical
|
||||
@ -878,46 +907,49 @@ m.AttackPlan.prototype.raidTargetFinder = function(gameState)
|
||||
|
||||
m.AttackPlan.prototype.getPathToTarget = function(gameState)
|
||||
{
|
||||
if (!this.path)
|
||||
{
|
||||
Engine.ProfileStart("AI Compute path");
|
||||
let startPos = { "x": this.rallyPoint[0], "y": this.rallyPoint[1] };
|
||||
let endPos = { "x": this.targetPos[0], "y": this.targetPos[1] };
|
||||
let path = Engine.ComputePath(startPos, endPos, gameState.getPassabilityClassMask("siege-large"));
|
||||
this.path = [];
|
||||
this.path.push(this.targetPos);
|
||||
for (let p in path)
|
||||
this.path.push([path[p].x, path[p].y])
|
||||
this.path.push(this.rallyPoint);
|
||||
this.path.reverse();
|
||||
// Change the rally point to something useful
|
||||
this.setRallyPoint(gameState);
|
||||
Engine.ProfileStop();
|
||||
}
|
||||
else if (this.path === "toBeContinued")
|
||||
return 1; // carry on
|
||||
let startAccess = gameState.ai.accessibility.getAccessValue(this.rallyPoint);
|
||||
let endAccess = gameState.ai.accessibility.getAccessValue(this.targetPos);
|
||||
if (startAccess != endAccess)
|
||||
return false;
|
||||
|
||||
return -1;
|
||||
Engine.ProfileStart("AI Compute path");
|
||||
let startPos = { "x": this.rallyPoint[0], "y": this.rallyPoint[1] };
|
||||
let endPos = { "x": this.targetPos[0], "y": this.targetPos[1] };
|
||||
let path = Engine.ComputePath(startPos, endPos, gameState.getPassabilityClassMask("siege-large"));
|
||||
this.path = [];
|
||||
this.path.push(this.targetPos);
|
||||
for (let p in path)
|
||||
this.path.push([path[p].x, path[p].y])
|
||||
this.path.push(this.rallyPoint);
|
||||
this.path.reverse();
|
||||
// Change the rally point to something useful
|
||||
this.setRallyPoint(gameState);
|
||||
Engine.ProfileStop();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Set rally point at the border of our territory
|
||||
m.AttackPlan.prototype.setRallyPoint = function(gameState)
|
||||
{
|
||||
for (let i = 0; i < this.path.length; ++i)
|
||||
{
|
||||
let waypointPos = this.path[i];
|
||||
if (gameState.ai.HQ.territoryMap.getOwner(waypointPos) !== PlayerID)
|
||||
{
|
||||
// Set rally point at the border of our territory
|
||||
// or where we need to change transportation method.
|
||||
if (i !== 0)
|
||||
this.rallyPoint = this.path[i-1];
|
||||
else
|
||||
this.rallyPoint = this.path[0];
|
||||
if (gameState.ai.HQ.territoryMap.getOwner(this.path[i]) === PlayerID)
|
||||
continue;
|
||||
|
||||
if (i >= 2)
|
||||
this.path.splice(0, i-1);
|
||||
break;
|
||||
if (i == 0)
|
||||
this.rallyPoint = this.path[0];
|
||||
else if (i > 1 && gameState.ai.HQ.isDangerousLocation(gameState, this.path[i-1], 20))
|
||||
{
|
||||
this.rallyPoint = this.path[i-2];
|
||||
this.path.splice(0, i-2);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.rallyPoint = this.path[i-1];
|
||||
this.path.splice(0, i-1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@ -928,46 +960,15 @@ m.AttackPlan.prototype.StartAttack = function(gameState)
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("start attack " + this.name + " with type " + this.type);
|
||||
|
||||
if (!this.targetPlayer)
|
||||
// if our target was destroyed during preparation, choose a new one
|
||||
if (!this.targetPlayer || !this.target || !gameState.getEntityById(this.target.id()))
|
||||
{
|
||||
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
|
||||
if (!this.targetPlayer)
|
||||
if (!this.chooseTarget(gameState))
|
||||
return false;
|
||||
if (this.target && this.target.owner() !== this.targetPlayer)
|
||||
this.target = undefined;
|
||||
}
|
||||
if (!this.target || !gameState.getEntityById(this.target.id())) // our target was destroyed during our preparation
|
||||
{
|
||||
if (!this.targetPos) // should not happen
|
||||
return false;
|
||||
var targetIndex = gameState.ai.accessibility.getAccessValue(this.targetPos);
|
||||
var rallyIndex = gameState.ai.accessibility.getAccessValue(this.rallyPoint);
|
||||
if (targetIndex === rallyIndex)
|
||||
{
|
||||
// If on the same index: if we are doing a raid, look for a better target,
|
||||
// otherwise proceed with the previous target position
|
||||
// and we will look for a better target there
|
||||
if (this.type === "Raid")
|
||||
{
|
||||
this.target = this.getNearestTarget(gameState, this.rallyPoint);
|
||||
if (!this.target)
|
||||
return false;
|
||||
this.targetPos = this.target.position();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not on the same index, do not loose time to go to previous targetPos if nothing there
|
||||
// so directly look for a new target right now
|
||||
this.target = this.getNearestTarget(gameState, this.rallyPoint);
|
||||
if (!this.target)
|
||||
return false;
|
||||
this.targetPos = this.target.position();
|
||||
}
|
||||
}
|
||||
|
||||
// check we have a target and a path.
|
||||
if (this.targetPos && this.path !== undefined)
|
||||
if (this.targetPos && (this.overseas || this.path))
|
||||
{
|
||||
// erase our queue. This will stop any leftover unit from being trained.
|
||||
gameState.ai.queueManager.removeQueue("plan_" + this.name);
|
||||
@ -1203,7 +1204,23 @@ m.AttackPlan.prototype.update = function(gameState, events)
|
||||
// basic state of attacking.
|
||||
if (this.state === "")
|
||||
{
|
||||
// First update the target if needed:
|
||||
// First update the target position in case it's a unit (and check if it has garrisoned)
|
||||
if (this.target && this.target.hasClass("Unit"))
|
||||
{
|
||||
this.targetPos = this.target.position();
|
||||
if (!this.targetPos)
|
||||
{
|
||||
let holder = m.getHolder(gameState, this.target);
|
||||
if (holder && gameState.isPlayerEnemy(holder.owner()))
|
||||
{
|
||||
this.target = holder;
|
||||
this.targetPos = holder.position();
|
||||
}
|
||||
else
|
||||
this.target = undefined;
|
||||
}
|
||||
}
|
||||
// Then update the target if needed:
|
||||
if (!this.targetPlayer)
|
||||
{
|
||||
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
|
||||
@ -1266,9 +1283,6 @@ m.AttackPlan.prototype.update = function(gameState, events)
|
||||
}
|
||||
this.targetPos = this.target.position();
|
||||
}
|
||||
// and regularly update the target position in case it's a unit.
|
||||
if (this.target.hasClass("Unit"))
|
||||
this.targetPos = this.target.position();
|
||||
|
||||
var time = gameState.ai.elapsedTime;
|
||||
for (var evt of events["Attacked"])
|
||||
@ -1678,12 +1692,6 @@ m.AttackPlan.prototype.removeUnit = function(ent, update)
|
||||
this.unitCollection.updateEnt(ent);
|
||||
};
|
||||
|
||||
// Reset the path so that it can be recomputed for a new target
|
||||
m.AttackPlan.prototype.resetPath = function()
|
||||
{
|
||||
this.path = undefined;
|
||||
};
|
||||
|
||||
m.AttackPlan.prototype.checkEvents = function(gameState, events)
|
||||
{
|
||||
for (let evt of events["EntityRenamed"])
|
||||
@ -1706,6 +1714,28 @@ m.AttackPlan.prototype.checkEvents = function(gameState, events)
|
||||
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
|
||||
this.target = undefined;
|
||||
}
|
||||
|
||||
if (!this.overseas || this.state !== "unexecuted")
|
||||
return;
|
||||
// let's check if an enemy has built a structure at our access
|
||||
for (let evt of events["Create"])
|
||||
{
|
||||
let ent = gameState.getEntityById(evt.entity);
|
||||
if (!ent || !ent.position() || !ent.hasClass("Structure"))
|
||||
continue;
|
||||
if (!gameState.isPlayerEnemy(ent.owner()))
|
||||
continue;
|
||||
let access = gameState.ai.accessibility.getAccessValue(ent.position());
|
||||
for (let base of gameState.ai.HQ.baseManagers)
|
||||
{
|
||||
if (!base.anchor || !base.anchor.position())
|
||||
continue;
|
||||
if (base.accessIndex !== access)
|
||||
continue;
|
||||
this.overseas = 0;
|
||||
this.rallyPoint = base.anchor.position();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
m.AttackPlan.prototype.waitingForTransport = function()
|
||||
|
@ -2088,6 +2088,10 @@ m.HQ.prototype.Deserialize = function(gameState, data)
|
||||
this.baseManagers.push(newbase);
|
||||
}
|
||||
|
||||
this.navalManager = new m.NavalManager(this.Config);
|
||||
this.navalManager.init(gameState, true);
|
||||
this.navalManager.Deserialize(gameState, data.navalManager);
|
||||
|
||||
this.attackManager = new m.AttackManager(this.Config);
|
||||
this.attackManager.Deserialize(gameState, data.attackManager);
|
||||
this.attackManager.init(gameState);
|
||||
@ -2100,10 +2104,6 @@ m.HQ.prototype.Deserialize = function(gameState, data)
|
||||
this.tradeManager.init(gameState);
|
||||
this.tradeManager.Deserialize(gameState, data.tradeManager);
|
||||
|
||||
this.navalManager = new m.NavalManager(this.Config);
|
||||
this.navalManager.init(gameState, true);
|
||||
this.navalManager.Deserialize(gameState, data.navalManager);
|
||||
|
||||
this.researchManager = new m.ResearchManager(this.Config);
|
||||
this.researchManager.Deserialize(data.researchManager);
|
||||
|
||||
|
@ -134,8 +134,8 @@ m.NavalManager.prototype.init = function(gameState, deserializing)
|
||||
if (!this.landingZones[land])
|
||||
this.landingZones[land] = {};
|
||||
if (!this.landingZones[land][naval])
|
||||
this.landingZones[land][naval] = [];
|
||||
this.landingZones[land][naval].push(i);
|
||||
this.landingZones[land][naval] = new Set();
|
||||
this.landingZones[land][naval].add(i);
|
||||
}
|
||||
// and keep only thoses with enough room around when possible
|
||||
for (let land in this.landingZones)
|
||||
@ -145,27 +145,25 @@ m.NavalManager.prototype.init = function(gameState, deserializing)
|
||||
let landing = this.landingZones[land][sea];
|
||||
let nbaround = {};
|
||||
let nbcut = 0;
|
||||
for (let i = 0; i < landing.length; i++)
|
||||
for (let i of landing)
|
||||
{
|
||||
let j = landing[i];
|
||||
let nb = 0;
|
||||
if (i > 0 && landing[i-1] == j-1)
|
||||
if (landing.has(i-1))
|
||||
nb++;
|
||||
if (i < landing.length-1 && landing[i+1] == j+1)
|
||||
if (landing.has(i+1))
|
||||
nb++;
|
||||
if (landing.indexOf(j+width) != -1)
|
||||
if (landing.has(i+width))
|
||||
nb++;
|
||||
if (landing.indexOf(j-width) != -1)
|
||||
if (landing.has(i-width))
|
||||
nb++;
|
||||
nbaround[j] = nb;
|
||||
nbaround[i] = nb;
|
||||
nbcut = Math.max(nb, nbcut);
|
||||
}
|
||||
nbcut = Math.min(2, nbcut);
|
||||
for (let i = 0; i < landing.length; i++)
|
||||
for (let i of landing)
|
||||
{
|
||||
let j = landing[i];
|
||||
if (nbaround[j] < nbcut)
|
||||
landing.splice(i--, 1);
|
||||
if (nbaround[i] < nbcut)
|
||||
landing.delete(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,17 +144,17 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (template.hasClass("Tower") || template.hasClass("Fortress"))
|
||||
else if (template.hasClass("Tower") || template.hasClass("Fortress") || template.hasClass("ArmyCamp"))
|
||||
{
|
||||
var pos = gameState.ai.HQ.findDefensiveLocation(gameState, template);
|
||||
|
||||
if (pos)
|
||||
return { "x": pos[0], "z": pos[1], "angle": 3*Math.PI/4, "base": pos[2] };
|
||||
else if (!template.hasClass("Fortress") || gameState.civ() === "mace" || gameState.civ() === "maur" ||
|
||||
else if (template.hasClass("Tower") || gameState.civ() === "mace" || gameState.civ() === "maur" ||
|
||||
gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_fortress"), true)
|
||||
+ gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_army_camp"), true) > 0)
|
||||
// if this fortress is our first siege unit builder, just try the standard placement as we want siege units
|
||||
return false;
|
||||
// if this fortress is our first siege unit builder, just try the standard placement as we want siege units
|
||||
}
|
||||
else if (template.hasClass("Market")) // Docks (i.e. NavalMarket) are done before
|
||||
{
|
||||
|
@ -85,6 +85,8 @@ m.TransportPlan.prototype.init = function(gameState)
|
||||
this.units.registerUpdates();
|
||||
this.ships.registerUpdates();
|
||||
this.transportShips.registerUpdates();
|
||||
|
||||
this.boardingRange = 18*18; // TODO compute it from the ship clearance and garrison range
|
||||
};
|
||||
|
||||
// count available slots
|
||||
@ -274,7 +276,7 @@ m.TransportPlan.prototype.onBoarding = function(gameState)
|
||||
else
|
||||
{
|
||||
let distShip = API3.SquareVectorDistance(self.boardingPos[shipId], ship.position());
|
||||
if (time - ship.getMetadata(PlayerID, "timeGarrison") > 8 && distShip > 225)
|
||||
if (time - ship.getMetadata(PlayerID, "timeGarrison") > 8 && distShip > self.boardingRange)
|
||||
{
|
||||
if (!self.nTry[shipId])
|
||||
self.nTry[shipId] = 1;
|
||||
@ -296,7 +298,7 @@ m.TransportPlan.prototype.onBoarding = function(gameState)
|
||||
let newPos = ent.position();
|
||||
if (oldPos[0] === newPos[0] && oldPos[1] === newPos[1])
|
||||
{
|
||||
if (distShip < 225) // looks like we are blocked ... try to go out of this trap
|
||||
if (distShip < self.boardingRange) // looks like we are blocked ... try to go out of this trap
|
||||
{
|
||||
if (!self.nTry[ent.id()])
|
||||
self.nTry[ent.id()] = 1;
|
||||
@ -388,7 +390,7 @@ m.TransportPlan.prototype.getBoardingPos = function(gameState, ship, landIndex,
|
||||
// require a small distance between all ships of the transport plan to avoid path finder problems
|
||||
// this is also used when the ship is blocked and we want to find a new boarding point
|
||||
for (let shipId in this.boardingPos)
|
||||
if (this.boardingPos[shipId] !== undefined && API3.SquareVectorDistance(this.boardingPos[shipId], pos) < 225)
|
||||
if (this.boardingPos[shipId] !== undefined && API3.SquareVectorDistance(this.boardingPos[shipId], pos) < this.boardingRange)
|
||||
dist += 1000000;
|
||||
if (dist > distmin)
|
||||
continue;
|
||||
@ -549,7 +551,7 @@ m.TransportPlan.prototype.onSailing = function(gameState)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dist > 225)
|
||||
if (dist > this.boardingRange)
|
||||
{
|
||||
if (!this.nTry[shipId])
|
||||
this.nTry[shipId] = 1;
|
||||
|
Loading…
Reference in New Issue
Block a user