0ad/binaries/data/mods/public/simulation/ai/aegis/enemy-watcher.js
Yves 3362c591f5 Moves AI players to one global using the module pattern.
This avoids wrapping overhead that would otherwise be required because
multiple globals per compartment aren't supported anymore in newer
versions of SpiderMonkey.

Check the ticket for a detailed explanation.

Closes #2322
Refs #2241
Refs #1886

This was SVN commit r14441.
2013-12-30 10:04:59 +00:00

223 lines
8.0 KiB
JavaScript
Executable File

var AEGIS = function(m)
{
/*
* A class that keeps track of enemy buildings, units, and pretty much anything I can think of (still a LOT TODO here)
* Only watches one enemy, you'll need one per enemy.
* Note: pretty much unused in the current version.
*/
m.enemyWatcher = function(gameState, playerToWatch) {
this.watched = playerToWatch;
// using global entity collections, shared by any AI that knows the name of this.
var filter = API3.Filters.and(API3.Filters.byClass("Structure"), API3.Filters.byOwner(this.watched));
this.enemyBuildings = gameState.updatingGlobalCollection("player-" +this.watched + "-structures", filter);
filter = API3.Filters.and(API3.Filters.byClass("Unit"), API3.Filters.byOwner(this.watched));
this.enemyUnits = gameState.updatingGlobalCollection("player-" +this.watched + "-units", filter);
filter = API3.Filters.and(API3.Filters.byClass("Worker"), API3.Filters.byOwner(this.watched));
this.enemyCivilians = gameState.updatingGlobalCollection("player-" +this.watched + "-civilians", filter);
filter = API3.Filters.and(API3.Filters.byClassesOr(["CitizenSoldier", "Hero", "Champion", "Siege"]), API3.Filters.byOwner(this.watched));
this.enemySoldiers = gameState.updatingGlobalCollection("player-" +this.watched + "-soldiers", filter);
filter = API3.Filters.and(API3.Filters.byClass("Worker"), API3.Filters.byOwner(this.watched));
this.enemyCivilians = gameState.getEnemyEntities().filter(filter);
this.enemyCivilians.registerUpdates();
filter = API3.Filters.and(API3.Filters.byClassesOr(["CitizenSoldier", "Hero", "Champion", "Siege"]), API3.Filters.byOwner(this.watched));
this.enemySoldiers = gameState.getEnemyEntities().filter(filter);
this.enemySoldiers.registerUpdates();
// entity collections too.
this.armies = {};
this.enemyBuildingClass = {};
this.totalNBofArmies = 0;
// this is an array of integers, refering to "this.armies[ XX ]"
this.dangerousArmies = [];
};
m.enemyWatcher.prototype.getAllEnemySoldiers = function() {
return this.enemySoldiers;
};
m.enemyWatcher.prototype.getAllEnemyBuildings = function() {
return this.enemyBuildings;
};
m.enemyWatcher.prototype.getEnemyBuildings = function(gameState, specialClass, OneTime) {
var filter = API3.Filters.byClass(specialClass);
if (OneTime && gameState.getGEC("player-" +this.watched + "-structures-" +specialClass))
return gameState.getGEC("player-" +this.watched + "-structures-" +specialClass);
else if (OneTime)
return this.enemyBuildings.filter(filter);
return gameState.updatingGlobalCollection("player-" +this.watched + "-structures-" +specialClass, filter, gameState.getGEC("player-" +this.watched + "-structures"));
};
m.enemyWatcher.prototype.getDangerousArmies = function() {
var toreturn = {};
for (var i in this.dangerousArmies)
toreturn[this.dangerousArmies[i]] = this.armies[this.dangerousArmies[i]];
return toreturn;
};
m.enemyWatcher.prototype.getSafeArmies = function() {
var toreturn = {};
for (var i in this.armies)
if (this.dangerousArmies.indexOf(i) == -1)
toreturn[i] = this.armies[i];
return toreturn;
};
m.enemyWatcher.prototype.resetDangerousArmies = function() {
this.dangerousArmies = [];
};
m.enemyWatcher.prototype.setAsDangerous = function(armyID) {
if (this.dangerousArmies.indexOf(armyID) === -1)
this.dangerousArmies.push(armyID);
};
m.enemyWatcher.prototype.isDangerous = function(armyID) {
if (this.dangerousArmies.indexOf(armyID) === -1)
return false;
return true;
};
// returns [id, army]
m.enemyWatcher.prototype.getArmyFromMember = function(memberID) {
for (var i in this.armies) {
if (this.armies[i].toIdArray().indexOf(memberID) !== -1)
return [i,this.armies[i]];
}
return undefined;
};
m.enemyWatcher.prototype.isPartOfDangerousArmy = function(memberID) {
var armyID = this.getArmyFromMember(memberID)[0];
if (this.isDangerous(armyID))
return true;
return false;
};
m.enemyWatcher.prototype.cleanDebug = function() {
for (var armyID in this.armies) {
var army = this.armies[armyID];
m.debug ("Army " +armyID);
m.debug (army.length +" members, centered around " +army.getCentrePosition());
}
}
// this will monitor any unmonitored soldier.
m.enemyWatcher.prototype.detectArmies = function(gameState){
//this.cleanDebug();
var self = this;
if (gameState.ai.playedTurn % 4 === 0) {
Engine.ProfileStart("Looking for new soldiers");
// let's loop through unmonitored enemy soldiers
this.unmonitoredEnemySoldiers.forEach( function (enemy) { //}){
if (enemy.position() === undefined)
return;
// this was an unmonitored unit, we do not know any army associated with it. We assign it a new army (we'll merge later if needed)
enemy.setMetadata(PlayerID, "monitored","true");
var armyID = gameState.player + "" + self.totalNBofArmies;
self.totalNBofArmies++,
enemy.setMetadata(PlayerID, "EnemyWatcherArmy",armyID);
var filter = API3.Filters.byMetadata(PlayerID, "EnemyWatcherArmy",armyID);
var army = self.enemySoldiers.filter(filter);
self.armies[armyID] = army;
self.armies[armyID].registerUpdates();
self.armies[armyID].length;
});
Engine.ProfileStop();
} else if (gameState.ai.playedTurn % 16 === 3) {
Engine.ProfileStart("Merging");
this.mergeArmies(); // calls "scrap empty armies"
Engine.ProfileStop();
} else if (gameState.ai.playedTurn % 16 === 7) {
Engine.ProfileStart("Splitting");
this.splitArmies(gameState);
Engine.ProfileStop();
}
};
// this will merge any two army who are too close together. The distance for "army" is fairly big.
// note: this doesn't actually merge two entity collections... It simply changes the unit metadatas, and will clear the empty entity collection
m.enemyWatcher.prototype.mergeArmies = function(){
for (var army in this.armies) {
var firstArmy = this.armies[army];
if (firstArmy.length !== 0) {
var firstAPos = firstArmy.getApproximatePosition(4);
for (var otherArmy in this.armies) {
if (otherArmy !== army && this.armies[otherArmy].length !== 0) {
var secondArmy = this.armies[otherArmy];
// we're not self merging, so we check if the two armies are close together
if (m.inRange(firstAPos,secondArmy.getApproximatePosition(4), 4000 ) ) {
// okay so we merge the two together
// if the other one was dangerous and we weren't, we're now.
if (this.dangerousArmies.indexOf(otherArmy) !== -1 && this.dangerousArmies.indexOf(army) === -1)
this.dangerousArmies.push(army);
secondArmy.forEach( function(ent) {
ent.setMetadata(PlayerID, "EnemyWatcherArmy",army);
});
}
}
}
}
}
this.ScrapEmptyArmies();
};
m.enemyWatcher.prototype.ScrapEmptyArmies = function(){
var removelist = [];
for (var army in this.armies) {
if (this.armies[army].length === 0) {
removelist.push(army);
// if the army was dangerous, we remove it from the list
if (this.dangerousArmies.indexOf(army) !== -1)
this.dangerousArmies.splice(this.dangerousArmies.indexOf(army),1);
}
}
for each (var toRemove in removelist) {
delete this.armies[toRemove];
}
};
// splits any unit too far from the centerposition
m.enemyWatcher.prototype.splitArmies = function(gameState){
var self = this;
var map = m.createTerritoryMap(gameState);
for (var armyID in this.armies) {
var army = this.armies[armyID];
var centre = army.getApproximatePosition(4);
if (map.getOwner(centre) === gameState.player)
continue;
army.forEach( function (enemy) {
if (enemy.position() === undefined)
return;
if (!m.inRange(enemy.position(),centre, 3500) ) {
var newArmyID = gameState.player + "" + self.totalNBofArmies;
if (self.dangerousArmies.indexOf(armyID) !== -1)
self.dangerousArmies.push(newArmyID);
self.totalNBofArmies++,
enemy.setMetadata(PlayerID, "EnemyWatcherArmy",newArmyID);
var filter = API3.Filters.byMetadata(PlayerID, "EnemyWatcherArmy",newArmyID);
var newArmy = self.enemySoldiers.filter(filter);
self.armies[newArmyID] = newArmy;
self.armies[newArmyID].registerUpdates();
self.armies[newArmyID].length;
}
});
}
};
return m;
}(AEGIS);