petra: continue splitting of attack function in smaller functions, + some cleanup

This was SVN commit r18273.
This commit is contained in:
mimo 2016-05-31 21:04:13 +00:00
parent 246c758085
commit 7060ca888c
7 changed files with 244 additions and 232 deletions

View file

@ -209,7 +209,7 @@ m.Army.prototype.removeOwn = function (gameState, id, Entity)
if (this.assignedTo[id] !== 0)
{
var temp = this.assignedAgainst[this.assignedTo[id]];
let temp = this.assignedAgainst[this.assignedTo[id]];
if (temp)
temp.splice(temp.indexOf(id), 1);
}
@ -235,13 +235,13 @@ m.Army.prototype.removeOwn = function (gameState, id, Entity)
// TODO be sure that all units in the transport need the cancelation
/* if (!ent.position()) // this unit must still be in a transport plan ... try to cancel it
{
var planID = ent.getMetadata(PlayerID, "transport");
let planID = ent.getMetadata(PlayerID, "transport");
// no plans must mean that the unit was in a ship which was destroyed, so do nothing
if (planID)
{
if (gameState.ai.Config.debug > 0)
warn("ent from army still in transport plan: plan " + planID + " canceled");
var plan = gameState.ai.HQ.navalManager.getPlan(planID);
let plan = gameState.ai.HQ.navalManager.getPlan(planID);
if (plan && !plan.canceled)
plan.cancelTransport(gameState);
}

View file

@ -92,7 +92,7 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, data)
this.maxCompletingTime = 0;
// priority of the queues we'll create.
var priority = 70;
let priority = 70;
// 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.
@ -158,7 +158,7 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, data)
}
// Put some randomness on the attack size
var variation = 0.8 + 0.4*Math.random();
let variation = 0.8 + 0.4*Math.random();
// and lower priority and smaller sizes for easier difficulty levels
if (this.Config.difficulty < 2)
{
@ -311,7 +311,7 @@ m.AttackPlan.prototype.forceStart = function()
}
};
/** Adds a build order. If resetQueue is true, this will reset the queue.*/
/** Adds a build order. If resetQueue is true, this will reset the queue. */
m.AttackPlan.prototype.addBuildOrder = function(gameState, name, unitStats, resetQueue)
{
if (!this.isStarted())
@ -349,7 +349,7 @@ m.AttackPlan.prototype.addSiegeUnits = function(gameState)
return true;
};
/** Three returns possible: 1 is "keep going", 0 is "failed plan", 2 is "start" */
/** Three returns possible: 1 is "keep going", 0 is "failed plan", 2 is "start". */
m.AttackPlan.prototype.updatePreparation = function(gameState)
{
// the completing step is used to return resources and regroup the units
@ -466,8 +466,8 @@ m.AttackPlan.prototype.updatePreparation = function(gameState)
Engine.PostCommand(PlayerID, {"type": "attack-request", "source": PlayerID, "target": this.targetPlayer});
}
var rallyPoint = this.rallyPoint;
var rallyIndex = gameState.ai.accessibility.getAccessValue(rallyPoint);
let rallyPoint = this.rallyPoint;
let rallyIndex = gameState.ai.accessibility.getAccessValue(rallyPoint);
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
@ -585,8 +585,8 @@ m.AttackPlan.prototype.trainMoreUnits = function(gameState)
m.AttackPlan.prototype.assignUnits = function(gameState)
{
var plan = this.name;
var added = false;
let plan = this.name;
let added = false;
// If we can not build units, assign all available except those affected to allied defense to the current attack
if (!this.canBuildUnits)
{
@ -695,7 +695,7 @@ m.AttackPlan.prototype.assignUnits = function(gameState)
return added;
};
/** Reassign one (at each turn) Cav unit to fasten raid preparation */
/** Reassign one (at each turn) Cav unit to fasten raid preparation. */
m.AttackPlan.prototype.reassignCavUnit = function(gameState)
{
let found;
@ -1176,16 +1176,15 @@ m.AttackPlan.prototype.update = function(gameState, events)
Engine.ProfileStart("Update Attack");
this.position = this.unitCollection.getCentrePosition();
var IDs = this.unitCollection.toIdArray();
var self = this;
let self = this;
// we are transporting our units, let's wait
// TODO instead of state "arrived", made a state "walking" with a new path
if (this.state === "transporting")
this.UpdateTransporting(gameState, events, IDs);
this.UpdateTransporting(gameState, events);
if (this.state === "walking" && !this.UpdateWalking(gameState, events, IDs))
if (this.state === "walking" && !this.UpdateWalking(gameState, events))
{
Engine.ProfileStop();
return 0;
@ -1214,95 +1213,22 @@ m.AttackPlan.prototype.update = function(gameState, events)
// basic state of attacking.
if (this.state === "")
{
// First update the target position in case it's a unit (and check if it has garrisoned)
if (this.target && this.target.hasClass("Unit"))
// First update the target and/or its position if needed
if (!this.UpdateTarget(gameState, events))
{
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 === undefined)
{
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
if (this.targetPlayer === undefined)
{
Engine.ProfileStop();
return false;
}
if (this.target && this.target.owner() !== this.targetPlayer)
this.target = undefined;
}
if (this.target && this.target.owner() === 0 && this.targetPlayer !== 0) // this enemy has resigned
this.target = undefined;
if (!this.target || !gameState.getEntityById(this.target.id()))
{
if (this.Config.debug > 1)
API3.warn("Seems like our target has been destroyed. Switching.");
this.target = this.getNearestTarget(gameState, this.position, true);
if (!this.target)
{
// Check if we could help any current attack
let attackManager = gameState.ai.HQ.attackManager;
let accessIndex = gameState.ai.accessibility.getAccessValue(this.position);
for (let attackType in attackManager.startedAttacks)
{
if (this.target)
break;
for (let attack of attackManager.startedAttacks[attackType])
{
if (attack.name === this.name)
continue;
if (!attack.target || !gameState.getEntityById(attack.target.id()))
continue;
if (accessIndex !== gameState.ai.accessibility.getAccessValue(attack.targetPos))
continue;
if (attack.target.owner() === 0 && attack.targetPlayer !== 0) // looks like it has resigned
continue;
this.target = attack.target;
this.targetPlayer = attack.targetPlayer;
break;
}
}
// If not, let's look for another enemy
if (!this.target)
{
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
if (this.targetPlayer !== undefined)
this.target = this.getNearestTarget(gameState, this.position, true);
if (!this.target)
{
if (this.Config.debug > 1)
API3.warn("No new target found. Remaining units " + this.unitCollection.length);
Engine.ProfileStop();
return false;
}
}
if (this.Config.debug > 1)
API3.warn("We will help one of our other attacks");
}
this.targetPos = this.target.position();
Engine.ProfileStop();
return false;
}
var time = gameState.ai.elapsedTime;
let time = gameState.ai.elapsedTime;
for (let evt of events.Attacked)
{
if (IDs.indexOf(evt.target) == -1)
if (!this.unitCollection.hasEntId(evt.target))
continue;
let attacker = gameState.getEntityById(evt.attacker);
if (!attacker || !attacker.position() || !attacker.hasClass("Unit"))
continue;
var ourUnit = gameState.getEntityById(evt.target);
let ourUnit = gameState.getEntityById(evt.target);
if (this.isSiegeUnit(gameState, ourUnit))
{ // if our siege units are attacked, we'll send some units to deal with enemies.
let collec = this.unitCollection.filter(API3.Filters.not(API3.Filters.byClass("Siege"))).filterNearest(ourUnit.position(), 5);
@ -1358,8 +1284,8 @@ m.AttackPlan.prototype.update = function(gameState, events)
}
}
var enemyUnits = gameState.getEnemyUnits(this.targetPlayer);
var enemyStructures = gameState.getEnemyStructures(this.targetPlayer);
let enemyUnits = gameState.getEnemyUnits(this.targetPlayer);
let enemyStructures = gameState.getEnemyStructures(this.targetPlayer);
// Count the number of times an enemy is targeted, to prevent all units to follow the same target
let unitTargets = {};
@ -1390,8 +1316,8 @@ m.AttackPlan.prototype.update = function(gameState, events)
if (unitTargets[target] > 0)
veto[target] = true;
var targetClassesUnit;
var targetClassesSiege;
let targetClassesUnit;
let targetClassesSiege;
if (this.type === "Rush")
targetClassesUnit = {"attack": ["Unit", "Structure"], "avoid": ["Palisade", "StoneWall", "Tower", "Fortress"], "vetoEntities": veto};
else
@ -1419,7 +1345,7 @@ m.AttackPlan.prototype.update = function(gameState, events)
this.unitCollUpdateArray = this.unitCollection.toIdArray();
// Let's check a few units each time we update (currently 10) except when attack starts
var lgth = (this.unitCollUpdateArray.length < 15 || this.startingAttack) ? this.unitCollUpdateArray.length : 10;
let lgth = (this.unitCollUpdateArray.length < 15 || this.startingAttack) ? this.unitCollUpdateArray.length : 10;
for (let check = 0; check < lgth; check++)
{
let ent = gameState.getEntityById(this.unitCollUpdateArray[check]);
@ -1687,7 +1613,7 @@ m.AttackPlan.prototype.update = function(gameState, events)
return this.unitCollection.length;
};
m.AttackPlan.prototype.UpdateTransporting = function(gameState, events, IDs)
m.AttackPlan.prototype.UpdateTransporting = function(gameState, events)
{
let done = true;
for (let ent of this.unitCollection.values())
@ -1711,7 +1637,7 @@ m.AttackPlan.prototype.UpdateTransporting = function(gameState, events, IDs)
// if we are attacked while waiting the rest of the army, retaliate
for (let evt of events.Attacked)
{
if (IDs.indexOf(evt.target) == -1)
if (!this.unitCollection.hasEntId(evt.target))
continue;
let attacker = gameState.getEntityById(evt.attacker);
if (!attacker || !gameState.getEntityById(evt.target))
@ -1728,7 +1654,7 @@ m.AttackPlan.prototype.UpdateTransporting = function(gameState, events, IDs)
}
};
m.AttackPlan.prototype.UpdateWalking = function(gameState, events, IDs)
m.AttackPlan.prototype.UpdateWalking = function(gameState, events)
{
// we're marching towards the target
// Let's check if any of our unit has been attacked.
@ -1738,7 +1664,7 @@ m.AttackPlan.prototype.UpdateWalking = function(gameState, events, IDs)
let attackedUnitNB = 0;
for (let evt of events.Attacked)
{
if (IDs.indexOf(evt.target) === -1)
if (!this.unitCollection.hasEntId(evt.target))
continue;
let attacker = gameState.getEntityById(evt.attacker);
if (attacker && (attacker.owner() !== 0 || this.targetPlayer === 0))
@ -1803,9 +1729,9 @@ m.AttackPlan.prototype.UpdateWalking = function(gameState, events, IDs)
API3.warn("Attack Plan " + this.type + " " + this.name + " has met walls and gives up.");
return false;
}
else
//this.unitCollection.move(this.path[0][0], this.path[0][1]);
this.unitCollection.moveIndiv(this.path[0][0], this.path[0][1]);
//this.unitCollection.move(this.path[0][0], this.path[0][1]);
this.unitCollection.moveIndiv(this.path[0][0], this.path[0][1]);
}
// check if our units are close enough from the next waypoint.
@ -1833,6 +1759,87 @@ m.AttackPlan.prototype.UpdateWalking = function(gameState, events, IDs)
return true;
};
m.AttackPlan.prototype.UpdateTarget = function(gameState, events)
{
// 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 === undefined)
{
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
if (this.targetPlayer === undefined)
return false;
if (this.target && this.target.owner() !== this.targetPlayer)
this.target = undefined;
}
if (this.target && this.target.owner() === 0 && this.targetPlayer !== 0) // this enemy has resigned
this.target = undefined;
if (!this.target || !gameState.getEntityById(this.target.id()))
{
if (this.Config.debug > 1)
API3.warn("Seems like our target has been destroyed. Switching.");
this.target = this.getNearestTarget(gameState, this.position, true);
if (!this.target)
{
// Check if we could help any current attack
let attackManager = gameState.ai.HQ.attackManager;
let accessIndex = gameState.ai.accessibility.getAccessValue(this.position);
for (let attackType in attackManager.startedAttacks)
{
for (let attack of attackManager.startedAttacks[attackType])
{
if (attack.name === this.name)
continue;
if (!attack.target || !gameState.getEntityById(attack.target.id()))
continue;
if (accessIndex !== gameState.ai.accessibility.getAccessValue(attack.targetPos))
continue;
if (attack.target.owner() === 0 && attack.targetPlayer !== 0) // looks like it has resigned
continue;
this.target = attack.target;
this.targetPlayer = attack.targetPlayer;
this.targetPos = this.target.position();
return true;
}
}
// If not, let's look for another enemy
if (!this.target)
{
this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this);
if (this.targetPlayer !== undefined)
this.target = this.getNearestTarget(gameState, this.position, true);
if (!this.target)
{
if (this.Config.debug > 1)
API3.warn("No new target found. Remaining units " + this.unitCollection.length);
return false;
}
}
if (this.Config.debug > 1)
API3.warn("We will help one of our other attacks");
}
this.targetPos = this.target.position();
}
return true;
};
/** reset any units */
m.AttackPlan.prototype.Abort = function(gameState)
{
@ -1885,7 +1892,7 @@ m.AttackPlan.prototype.checkEvents = function(gameState, events)
for (let evt of events.OwnershipChanged) // capture event
if (this.target && this.target.id() == evt.entity && gameState.isPlayerAlly(evt.to))
this.target = undefined;
this.target = undefined;
for (let evt of events.PlayerDefeated)
{

View file

@ -77,7 +77,6 @@ m.DefenseManager.prototype.getArmy = function(partOfArmy)
return undefined;
};
// TODO: this algorithm needs to be improved, sorta.
m.DefenseManager.prototype.isDangerous = function(gameState, entity)
{
if (!entity.position())
@ -165,11 +164,10 @@ m.DefenseManager.prototype.isDangerous = function(gameState, entity)
return false;
};
m.DefenseManager.prototype.checkEnemyUnits = function(gameState)
{
const nbPlayers = gameState.sharedScript.playersData.length;
var i = gameState.ai.playedTurn % nbPlayers;
let i = gameState.ai.playedTurn % nbPlayers;
this.attackingUnits[i] = undefined;
if (i === PlayerID)
@ -346,9 +344,8 @@ m.DefenseManager.prototype.assignDefenders = function(gameState)
if (this.armies.length === 0)
return;
var armiesNeeding = [];
// Okay, let's add defenders
// TODO: this is dumb.
let armiesNeeding = [];
// let's add defenders
for (let army of this.armies)
{
let needsDef = army.needsDefenders(gameState);
@ -364,7 +361,7 @@ m.DefenseManager.prototype.assignDefenders = function(gameState)
return;
// let's get our potential units
var potentialDefenders = [];
let potentialDefenders = [];
gameState.getOwnUnits().forEach(function(ent) {
if (!ent.position())
return;
@ -380,7 +377,7 @@ m.DefenseManager.prototype.assignDefenders = function(gameState)
return;
if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1)
{
var subrole = ent.getMetadata(PlayerID, "subrole");
let subrole = ent.getMetadata(PlayerID, "subrole");
if (subrole && (subrole === "completing" || subrole === "walking" || subrole === "attacking"))
return;
}
@ -423,7 +420,7 @@ m.DefenseManager.prototype.assignDefenders = function(gameState)
if (!armiesNeeding.length)
return;
// If shortage of defenders, produce infantry garrisoned in nearest civil centre
var armiesPos = [];
let armiesPos = [];
for (let a = 0; a < armiesNeeding.length; ++a)
armiesPos.push(armiesNeeding[a].army.foePosition);
gameState.ai.HQ.trainEmergencyUnits(gameState, armiesPos);
@ -441,9 +438,11 @@ m.DefenseManager.prototype.abortArmy = function(gameState, army)
}
};
// If our defense structures are attacked, garrison soldiers inside when possible
// and if a support unit is attacked and has less than 55% health, garrison it inside the nearest healing structure
// and if a ranged siege unit (not used for defense) is attacked, garrison it in the nearest fortress
/**
* If our defense structures are attacked, garrison soldiers inside when possible
* and if a support unit is attacked and has less than 55% health, garrison it inside the nearest healing structure
* and if a ranged siege unit (not used for defense) is attacked, garrison it in the nearest fortress
*/
m.DefenseManager.prototype.checkEvents = function(gameState, events)
{
// must be called every turn for all armies
@ -553,10 +552,10 @@ m.DefenseManager.prototype.garrisonRangedUnitsInside = function(gameState, targe
if (dist >= range*range)
return;
}
var index = gameState.ai.accessibility.getAccessValue(target.position());
var garrisonManager = gameState.ai.HQ.garrisonManager;
var garrisonArrowClasses = target.getGarrisonArrowClasses();
var units = gameState.getOwnUnits().filter(function (ent) { return MatchesClassList(garrisonArrowClasses, ent.classes()); }).filterNearest(target.position());
let index = gameState.ai.accessibility.getAccessValue(target.position());
let garrisonManager = gameState.ai.HQ.garrisonManager;
let garrisonArrowClasses = target.getGarrisonArrowClasses();
let units = gameState.getOwnUnits().filter(ent => MatchesClassList(garrisonArrowClasses, ent.classes())).filterNearest(target.position());
for (let ent of units.values())
{
if (garrisonManager.numberOfGarrisonedUnits(target) >= minGarrison)
@ -569,7 +568,7 @@ m.DefenseManager.prototype.garrisonRangedUnitsInside = function(gameState, targe
continue;
if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1)
{
var subrole = ent.getMetadata(PlayerID, "subrole");
let subrole = ent.getMetadata(PlayerID, "subrole");
if (subrole && (subrole === "completing" || subrole === "walking" || subrole === "attacking"))
continue;
}
@ -579,7 +578,7 @@ m.DefenseManager.prototype.garrisonRangedUnitsInside = function(gameState, targe
}
};
// garrison a attacked siege ranged unit inside the nearest fortress
/** garrison a attacked siege ranged unit inside the nearest fortress */
m.DefenseManager.prototype.garrisonSiegeUnit = function(gameState, unit)
{
let distmin = Math.min();
@ -593,7 +592,7 @@ m.DefenseManager.prototype.garrisonSiegeUnit = function(gameState, unit)
return;
if (ent.hitpoints() < ent.garrisonEjectHealth() * ent.maxHitpoints())
return;
var entAccess = ent.getMetadata(PlayerID, "access");
let entAccess = ent.getMetadata(PlayerID, "access");
if (!entAccess)
{
entAccess = gameState.ai.accessibility.getAccessValue(ent.position());
@ -611,7 +610,7 @@ m.DefenseManager.prototype.garrisonSiegeUnit = function(gameState, unit)
garrisonManager.garrison(gameState, unit, nearest, "protection");
};
// garrison a hurt unit inside the nearest healing structure
/** garrison a hurt unit inside the nearest healing structure */
m.DefenseManager.prototype.garrisonUnitForHealing = function(gameState, unit)
{
let distmin = Math.min();

View file

@ -15,14 +15,16 @@ m.DiplomacyManager = function(Config)
this.nextTributeRequest.set("all", 240);
};
// Check if any allied needs help (tribute) and sent it if we have enough resource
// or ask for a tribute if we are in need and one ally can help
/**
* Check if any allied needs help (tribute) and sent it if we have enough resource
* or ask for a tribute if we are in need and one ally can help
*/
m.DiplomacyManager.prototype.tributes = function(gameState)
{
this.nextTributeUpdate = gameState.ai.elapsedTime + 30;
var totalResources = gameState.getResources();
var availableResources = gameState.ai.queueManager.getAvailableResources(gameState);
var mostNeeded;
let totalResources = gameState.getResources();
let availableResources = gameState.ai.queueManager.getAvailableResources(gameState);
let mostNeeded;
for (let i = 1; i < gameState.sharedScript.playersData.length; ++i)
{
if (i === PlayerID || !gameState.isPlayerAlly(i) || gameState.ai.HQ.attackManager.defeated[i])

View file

@ -1,11 +1,11 @@
var PETRA = function(m)
{
// returns some sort of DPS * health factor. If you specify a class, it'll use the modifiers against that class too.
/** returns some sort of DPS * health factor. If you specify a class, it'll use the modifiers against that class too. */
m.getMaxStrength = function(ent, againstClass)
{
var strength = 0.0;
var attackTypes = ent.attackTypes();
let strength = 0;
let attackTypes = ent.attackTypes();
if (!attackTypes)
return strength;
@ -58,7 +58,7 @@ m.getMaxStrength = function(ent, againstClass)
}
}
var armourStrength = ent.armourStrengths();
let armourStrength = ent.armourStrengths();
for (let str in armourStrength)
{
let val = parseFloat(armourStrength[str]);
@ -81,24 +81,24 @@ m.getMaxStrength = function(ent, againstClass)
return strength * ent.maxHitpoints() / 100.0;
};
// Decide if we should try to capture or destroy
/** Decide if we should try to capture or destroy */
m.allowCapture = function(ent, target)
{
return !target.hasClass("Siege") || !ent.hasClass("Melee") ||
!target.isGarrisonHolder() || !target.garrisoned().length;
};
// Makes the worker deposit the currently carried resources at the closest accessible dropsite
/** Makes the worker deposit the currently carried resources at the closest accessible dropsite */
m.returnResources = function(gameState, ent)
{
if (!ent.resourceCarrying() || !ent.resourceCarrying().length || !ent.position())
return false;
var resource = ent.resourceCarrying()[0].type;
let resource = ent.resourceCarrying()[0].type;
var closestDropsite;
var distmin = Math.min();
var access = gameState.ai.accessibility.getAccessValue(ent.position());
let closestDropsite;
let distmin = Math.min();
let access = gameState.ai.accessibility.getAccessValue(ent.position());
gameState.getOwnDropsites(resource).forEach(function(dropsite) {
if (!dropsite.position() || dropsite.getMetadata(PlayerID, "access") !== access)
return;
@ -115,13 +115,13 @@ m.returnResources = function(gameState, ent)
return true;
};
// is supply full taking into account gatherers affected during this turn
/** is supply full taking into account gatherers affected during this turn */
m.IsSupplyFull = function(gameState, ent)
{
if (ent.isFull() === true)
return true;
var turnCache = gameState.ai.HQ.turnCache;
var count = ent.resourceSupplyNumGatherers();
let turnCache = gameState.ai.HQ.turnCache;
let count = ent.resourceSupplyNumGatherers();
if (turnCache.resourceGatherer && turnCache.resourceGatherer[ent.id()])
count += turnCache.resourceGatherer[ent.id()];
if (count >= ent.maxGatherers())
@ -134,7 +134,7 @@ m.IsSupplyFull = function(gameState, ent)
*/
m.getBestBase = function(gameState, ent)
{
var pos = ent.position();
let pos = ent.position();
if (!pos)
{
let holder = m.getHolder(gameState, ent);
@ -146,9 +146,9 @@ m.getBestBase = function(gameState, ent)
}
pos = holder.position();
}
var distmin = Math.min();
var bestbase;
var accessIndex = gameState.ai.accessibility.getAccessValue(pos);
let distmin = Math.min();
let bestbase;
let accessIndex = gameState.ai.accessibility.getAccessValue(pos);
for (let base of gameState.ai.HQ.baseManagers)
{
if (!base.anchor)

View file

@ -22,7 +22,7 @@ m.HQ.prototype.gameAnalysis = function(gameState)
nobase.init(gameState);
nobase.accessIndex = 0;
this.baseManagers.push(nobase); // baseManagers[0] will deal with unit/structure without base
var ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre"));
let ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre"));
for (let cc of ccEnts.values())
{
let newbase = new m.BaseManager(gameState, this.Config);
@ -133,10 +133,10 @@ m.HQ.prototype.assignStartingEntities = function(gameState)
*/
m.HQ.prototype.regionAnalysis = function(gameState)
{
var accessibility = gameState.ai.accessibility;
let accessibility = gameState.ai.accessibility;
let landIndex;
let seaIndex;
var ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre"));
let ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre"));
for (let cc of ccEnts.values())
{
let land = accessibility.getAccessValue(cc.position());
@ -170,11 +170,11 @@ m.HQ.prototype.regionAnalysis = function(gameState)
return false;
}
var passabilityMap = gameState.getMap();
var totalSize = passabilityMap.width * passabilityMap.width;
var minLandSize = Math.floor(0.1*totalSize);
var minWaterSize = Math.floor(0.2*totalSize);
var cellArea = passabilityMap.cellSize * passabilityMap.cellSize;
let passabilityMap = gameState.getMap();
let totalSize = passabilityMap.width * passabilityMap.width;
let minLandSize = Math.floor(0.1*totalSize);
let minWaterSize = Math.floor(0.2*totalSize);
let cellArea = passabilityMap.cellSize * passabilityMap.cellSize;
for (let i = 0; i < accessibility.regionSize.length; ++i)
{
if (landIndex && i == landIndex)
@ -227,8 +227,8 @@ m.HQ.prototype.regionAnalysis = function(gameState)
*/
m.HQ.prototype.structureAnalysis = function(gameState)
{
var civref = gameState.playerData.civ;
var civ = civref in this.Config.buildings.base ? civref : 'default';
let civref = gameState.playerData.civ;
let civ = civref in this.Config.buildings.base ? civref : 'default';
this.bBase = [];
for (let base of this.Config.buildings.base[civ])
this.bBase.push(gameState.applyCiv(base));
@ -246,13 +246,13 @@ m.HQ.prototype.structureAnalysis = function(gameState)
*/
m.HQ.prototype.buildFirstBase = function(gameState)
{
var total = gameState.getResources();
var template = gameState.applyCiv("structures/{civ}_civil_centre");
let total = gameState.getResources();
let template = gameState.applyCiv("structures/{civ}_civil_centre");
if (gameState.isDisabledTemplates(template))
return;
template = gameState.getTemplate(template);
var goal = "civil_centre";
var docks = gameState.getOwnStructures().filter(API3.Filters.byClass("Dock"));
let goal = "civil_centre";
let docks = gameState.getOwnStructures().filter(API3.Filters.byClass("Dock"));
if (!total.canAfford(new API3.Resources(template.cost())) && !docks.hasEntities())
{
// not enough resource to build a cc, try with a dock to accumulate resources if none yet
@ -325,22 +325,22 @@ m.HQ.prototype.buildFirstBase = function(gameState)
*/
m.HQ.prototype.dispatchUnits = function(gameState)
{
var allycc = gameState.getExclusiveAllyEntities().filter(API3.Filters.byClass("CivCentre")).toEntityArray();
let allycc = gameState.getExclusiveAllyEntities().filter(API3.Filters.byClass("CivCentre")).toEntityArray();
if (allycc.length)
{
if (this.Config.debug > 1)
API3.warn(" We have allied cc " + allycc.length + " and " + gameState.getOwnUnits().length + " units ");
var units = gameState.getOwnUnits();
var num = Math.max(Math.min(Math.round(0.08*(1+this.Config.personality.cooperative)*units.length), 20), 5);
var num1 = Math.floor(num / 2);
var num2 = num1;
let units = gameState.getOwnUnits();
let num = Math.max(Math.min(Math.round(0.08*(1+this.Config.personality.cooperative)*units.length), 20), 5);
let num1 = Math.floor(num / 2);
let num2 = num1;
// first pass to affect ranged infantry
units.filter(API3.Filters.byClassesAnd(["Infantry", "Ranged"])).forEach(function (ent) {
if (!num || !num1)
return;
if (ent.getMetadata(PlayerID, "allied"))
return;
var access = gameState.ai.accessibility.getAccessValue(ent.position());
let access = gameState.ai.accessibility.getAccessValue(ent.position());
for (let cc of allycc)
{
if (!cc.position())
@ -410,8 +410,8 @@ m.HQ.prototype.configFirstBase = function(gameState)
if (this.baseManagers.length < 2)
return;
var startingSize = 0;
var startingLand = [];
let startingSize = 0;
let startingLand = [];
for (let region in this.landRegions)
{
for (let base of this.baseManagers)
@ -423,7 +423,7 @@ m.HQ.prototype.configFirstBase = function(gameState)
break;
}
}
var cell = gameState.getMap().cellSize;
let cell = gameState.getMap().cellSize;
startingSize = startingSize * cell * cell;
if (this.Config.debug > 1)
API3.warn("starting size " + startingSize + "(cut at 24000 for fish pushing)");
@ -441,8 +441,8 @@ m.HQ.prototype.configFirstBase = function(gameState)
}
// - count the available wood resource, and react accordingly
var startingFood = gameState.getResources().food;
var check = {};
let startingFood = gameState.getResources().food;
let check = {};
for (let proxim of ["nearby", "medium", "faraway"])
{
for (let base of this.baseManagers)
@ -467,7 +467,7 @@ m.HQ.prototype.configFirstBase = function(gameState)
this.needFarm = true;
}
// - count the available wood resource, and allow rushes only if enough (we should otherwise favor expansion)
var startingWood = gameState.getResources().wood;
let startingWood = gameState.getResources().wood;
check = {};
for (let proxim of ["nearby", "medium", "faraway"])
{
@ -501,7 +501,7 @@ m.HQ.prototype.configFirstBase = function(gameState)
}
// immediatly build a wood dropsite if possible.
var template = gameState.applyCiv("structures/{civ}_storehouse");
let template = gameState.applyCiv("structures/{civ}_storehouse");
if (!gameState.getOwnEntitiesByClass("Storehouse", true).hasEntities() && this.canBuild(gameState, template))
{
let newDP = this.baseManagers[1].findBestDropsiteLocation(gameState, "wood");

View file

@ -33,26 +33,26 @@ m.TradeManager.prototype.assignTrader = function(ent)
this.traders.updateEnt(ent);
};
// TODO take trader ships into account
m.TradeManager.prototype.trainMoreTraders = function(gameState, queues)
{
if (!this.tradeRoute || queues.trader.hasQueuedUnits())
return;
var numTraders = this.traders.length;
var numSeaTraders = this.traders.filter(API3.Filters.byClass("Ship")).length;
var numLandTraders = numTraders - numSeaTraders;
let numTraders = this.traders.length;
let numSeaTraders = this.traders.filter(API3.Filters.byClass("Ship")).length;
let numLandTraders = numTraders - numSeaTraders;
// add traders already in training
gameState.getOwnTrainingFacilities().forEach(function(ent) {
ent.trainingQueue().forEach(function(item) {
for (let item of ent.trainingQueue())
{
if (!item.metadata || !item.metadata.role || item.metadata.role !== "trader")
return;
continue;
numTraders += item.count;
if (item.metadata.sea !== undefined)
numSeaTraders += item.count;
else
numLandTraders += item.count;
});
}
});
if (numTraders >= this.targetNumTraders &&
((!this.tradeRoute.sea && numLandTraders >= Math.floor(this.targetNumTraders/2)) ||
@ -118,8 +118,8 @@ m.TradeManager.prototype.updateTrader = function(gameState, ent)
return;
Engine.ProfileStart("Trade Manager");
var access = ent.hasClass("Ship") ? ent.getMetadata(PlayerID, "sea") : gameState.ai.accessibility.getAccessValue(ent.position());
var route = this.checkRoutes(gameState, access);
let access = ent.hasClass("Ship") ? ent.getMetadata(PlayerID, "sea") : gameState.ai.accessibility.getAccessValue(ent.position());
let route = this.checkRoutes(gameState, access);
if (!route)
{
// TODO try to garrison land trader inside merchant ship when only sea routes available
@ -129,7 +129,7 @@ m.TradeManager.prototype.updateTrader = function(gameState, ent)
return;
}
var nearerSource = true;
let nearerSource = true;
if (API3.SquareVectorDistance(route.target.position(), ent.position()) < API3.SquareVectorDistance(route.source.position(), ent.position()))
nearerSource = false;
@ -153,13 +153,13 @@ m.TradeManager.prototype.updateTrader = function(gameState, ent)
m.TradeManager.prototype.setTradingGoods = function(gameState)
{
var tradingGoods = { "food": 0, "wood": 0, "stone": 0, "metal": 0 };
let tradingGoods = { "food": 0, "wood": 0, "stone": 0, "metal": 0 };
// first, try to anticipate future needs
var stocks = gameState.ai.HQ.getTotalResourceLevel(gameState);
var mostNeeded = gameState.ai.HQ.pickMostNeededResources(gameState);
var remaining = 100;
var targetNum = this.Config.Economy.targetNumTraders;
for (var type in stocks)
let stocks = gameState.ai.HQ.getTotalResourceLevel(gameState);
let mostNeeded = gameState.ai.HQ.pickMostNeededResources(gameState);
let remaining = 100;
let targetNum = this.Config.Economy.targetNumTraders;
for (let type in stocks)
{
if (type == "food")
continue;
@ -185,8 +185,8 @@ m.TradeManager.prototype.setTradingGoods = function(gameState)
// then add what is needed now
var mainNeed = Math.floor(remaining * 70 / 100);
var nextNeed = remaining - mainNeed;
let mainNeed = Math.floor(remaining * 70 / 100);
let nextNeed = remaining - mainNeed;
tradingGoods[mostNeeded[0].type] += mainNeed;
if (mostNeeded[1].wanted > 0)
@ -198,33 +198,35 @@ m.TradeManager.prototype.setTradingGoods = function(gameState)
API3.warn(" trading goods set to " + uneval(tradingGoods));
};
// Try to barter unneeded resources for needed resources.
// only once per turn because the info doesn't update between a turn and fixing isn't worth it.
/**
* Try to barter unneeded resources for needed resources.
* only once per turn because the info is not updated within a turn
*/
m.TradeManager.prototype.performBarter = function(gameState)
{
var barterers = gameState.getOwnEntitiesByClass("BarterMarket", true).filter(API3.Filters.isBuilt()).toEntityArray();
let barterers = gameState.getOwnEntitiesByClass("BarterMarket", true).filter(API3.Filters.isBuilt()).toEntityArray();
if (barterers.length === 0)
return false;
// Available resources after account substraction
var available = gameState.ai.queueManager.getAvailableResources(gameState);
var needs = gameState.ai.queueManager.currentNeeds(gameState);
let available = gameState.ai.queueManager.getAvailableResources(gameState);
let needs = gameState.ai.queueManager.currentNeeds(gameState);
var rates = gameState.ai.HQ.GetCurrentGatherRates(gameState);
let rates = gameState.ai.HQ.GetCurrentGatherRates(gameState);
var prices = gameState.getBarterPrices();
let prices = gameState.getBarterPrices();
// calculates conversion rates
var getBarterRate = function (prices,buy,sell) { return Math.round(100 * prices.sell[sell] / prices.buy[buy]); };
let getBarterRate = function (prices,buy,sell) { return Math.round(100 * prices.sell[sell] / prices.buy[buy]); };
// loop through each missing resource checking if we could barter and help finishing a queue quickly.
for (var buy of needs.types)
for (let buy of needs.types)
{
if (needs[buy] === 0 || needs[buy] < rates[buy]*30) // check if our rate allows to gather it fast enough
continue;
// pick the best resource to barter.
var bestToSell = undefined;
var bestRate = 0;
let bestToSell = undefined;
let bestRate = 0;
for (let sell of needs.types)
{
if (sell === buy)
@ -273,8 +275,8 @@ m.TradeManager.prototype.performBarter = function(gameState)
// now do contingency bartering, selling food to buy finite resources (and annoy our ennemies by increasing prices)
if (available.food < 1000 || needs.food > 0)
return false;
var bestToBuy;
var bestChoice = 0;
let bestToBuy;
let bestChoice = 0;
for (let buy of needs.types)
{
if (buy === "food")
@ -371,12 +373,14 @@ m.TradeManager.prototype.checkEvents = function(gameState, events)
return false;
};
// fills the best trade route in this.tradeRoute and the best potential route in this.potentialTradeRoute
// If an index is given, it returns the best route with this index or the best land route if index is a land index
/**
* fills the best trade route in this.tradeRoute and the best potential route in this.potentialTradeRoute
* If an index is given, it returns the best route with this index or the best land route if index is a land index
*/
m.TradeManager.prototype.checkRoutes = function(gameState, accessIndex)
{
var market1 = gameState.updatingCollection("OwnMarkets", API3.Filters.byClass("Market"), gameState.getOwnStructures()).toEntityArray();
var market2 = gameState.updatingCollection("ExclusiveAllyMarkets", API3.Filters.byClass("Market"), gameState.getExclusiveAllyEntities()).toEntityArray();
let market1 = gameState.updatingCollection("OwnMarkets", API3.Filters.byClass("Market"), gameState.getOwnStructures()).toEntityArray();
let market2 = gameState.updatingCollection("ExclusiveAllyMarkets", API3.Filters.byClass("Market"), gameState.getExclusiveAllyEntities()).toEntityArray();
if (market1.length + market2.length < 2) // We have to wait ... markets will be built soon
{
this.tradeRoute = undefined;
@ -386,29 +390,29 @@ m.TradeManager.prototype.checkRoutes = function(gameState, accessIndex)
if (!market2.length)
market2 = market1;
var candidate = { "gain": 0 };
var potential = { "gain": 0 };
var bestIndex = { "gain": 0 };
var bestLand = { "gain": 0 };
let candidate = { "gain": 0 };
let potential = { "gain": 0 };
let bestIndex = { "gain": 0 };
let bestLand = { "gain": 0 };
let traderTemplatesGains = gameState.getTraderTemplatesGains();
for (var m1 of market1)
for (let m1 of market1)
{
if (!m1.position())
continue;
var access1 = gameState.ai.accessibility.getAccessValue(m1.position());
var sea1 = m1.hasClass("NavalMarket") ? gameState.ai.HQ.navalManager.getDockIndex(gameState, m1, true) : undefined;
for (var m2 of market2)
let access1 = gameState.ai.accessibility.getAccessValue(m1.position());
let sea1 = m1.hasClass("NavalMarket") ? gameState.ai.HQ.navalManager.getDockIndex(gameState, m1, true) : undefined;
for (let m2 of market2)
{
if (m1.id() === m2.id())
continue;
if (!m2.position())
continue;
var access2 = gameState.ai.accessibility.getAccessValue(m2.position());
var sea2 = m2.hasClass("NavalMarket") ? gameState.ai.HQ.navalManager.getDockIndex(gameState, m2, true) : undefined;
var land = access1 == access2 ? access1 : undefined;
var sea = (sea1 && sea1 == sea2) ? sea1 : undefined;
let access2 = gameState.ai.accessibility.getAccessValue(m2.position());
let sea2 = m2.hasClass("NavalMarket") ? gameState.ai.HQ.navalManager.getDockIndex(gameState, m2, true) : undefined;
let land = access1 == access2 ? access1 : undefined;
let sea = (sea1 && sea1 == sea2) ? sea1 : undefined;
if (!land && !sea)
continue;
let gainMultiplier;
@ -499,10 +503,10 @@ m.TradeManager.prototype.checkRoutes = function(gameState, accessIndex)
return true;
};
// Called when a market was built or destroyed, and checks if trader orders should be changed
/** Called when a market was built or destroyed, and checks if trader orders should be changed */
m.TradeManager.prototype.checkTrader = function(gameState, ent)
{
var presentRoute = ent.getMetadata(PlayerID, "route");
let presentRoute = ent.getMetadata(PlayerID, "route");
if (!presentRoute)
return;
@ -513,8 +517,8 @@ m.TradeManager.prototype.checkTrader = function(gameState, ent)
return;
}
var access = ent.hasClass("Ship") ? ent.getMetadata(PlayerID, "sea") : gameState.ai.accessibility.getAccessValue(ent.position());
var possibleRoute = this.checkRoutes(gameState, access);
let access = ent.hasClass("Ship") ? ent.getMetadata(PlayerID, "sea") : gameState.ai.accessibility.getAccessValue(ent.position());
let possibleRoute = this.checkRoutes(gameState, access);
// Warning: presentRoute is from metadata, so contains entity ids
if (!possibleRoute ||
(possibleRoute.source.id() != presentRoute.source && possibleRoute.source.id() != presentRoute.target) ||
@ -535,11 +539,11 @@ m.TradeManager.prototype.prospectForNewMarket = function(gameState, queues)
if (!gameState.updatingCollection("OwnMarkets", API3.Filters.byClass("Market"), gameState.getOwnStructures()).length &&
!gameState.updatingCollection("ExclusiveAllyMarkets", API3.Filters.byClass("Market"), gameState.getExclusiveAllyEntities()).length)
return;
var template = gameState.getTemplate(gameState.applyCiv("structures/{civ}_market"));
let template = gameState.getTemplate(gameState.applyCiv("structures/{civ}_market"));
if (!template)
return;
this.checkRoutes(gameState);
var marketPos = gameState.ai.HQ.findMarketLocation(gameState, template);
let marketPos = gameState.ai.HQ.findMarketLocation(gameState, template);
if (!marketPos || marketPos[3] === 0) // marketPos[3] is the expected gain
{ // no position found
gameState.ai.HQ.stopBuild(gameState, "structures/{civ}_market");
@ -582,7 +586,7 @@ m.TradeManager.prototype.update = function(gameState, events, queues)
if (this.routeProspection)
this.prospectForNewMarket(gameState, queues);
var self = this;
let self = this;
if (this.checkEvents(gameState, events)) // true if one market was built or destroyed
{