0ad/binaries/data/mods/public/simulation/ai/petra/navalManager.js
2014-03-24 22:33:50 +00:00

291 lines
8.8 KiB
JavaScript

var PETRA = function(m)
{
/* Naval Manager
Will deal with anything ships.
-Basically trade over water (with fleets and goals commissioned by the economy manager)
-Defense over water (commissioned by the defense manager)
-subtask being patrols, escort, naval superiority.
-Transport of units over water (a few units).
-Scouting, ultimately.
Also deals with handling docks, making sure we have access and stuffs like that.
Does not build them though, that's for the base manager to handle.
*/
m.NavalManager = function() {
// accessibility zones for which we have a dock.
// Connexion is described as [landindex] = [seaIndexes];
// technically they also exist for sea zones but I don't care.
this.landZoneDocked = [];
// list of seas I have a dock on.
this.accessibleSeas = [];
// ship subCollections. Also exist for land zones, idem, not caring.
this.seaShips = [];
this.seaTpShips = [];
this.seaWarships = [];
// wanted NB per zone.
this.wantedTpShips = [];
this.wantedWarships = [];
this.transportPlans = [];
this.askedPlans = [];
};
// More initialisation for stuff that needs the gameState
m.NavalManager.prototype.init = function(gameState, queues) {
// finished docks
this.docks = gameState.getOwnStructures().filter(API3.Filters.and(API3.Filters.byClass("Dock"), API3.Filters.not(API3.Filters.isFoundation())));
this.docks.allowQuickIter();
this.docks.registerUpdates();
this.ships = gameState.getOwnEntities().filter(API3.Filters.byClass("Ship"));
// note: those two can overlap (some transport ships are warships too and vice-versa).
this.tpShips = this.ships.filter(API3.Filters.byCanGarrison());
this.warships = this.ships.filter(API3.Filters.byClass("Warship"));
this.ships.registerUpdates();
this.tpShips.registerUpdates();
this.warships.registerUpdates();
for (var i = 0; i < gameState.ai.accessibility.regionSize.length; ++i)
{
if (gameState.ai.accessibility.regionType[i] !== "water")
{
// push dummies
this.seaShips.push(new API3.EntityCollection(gameState.sharedScript));
this.seaTpShips.push(new API3.EntityCollection(gameState.sharedScript));
this.seaWarships.push(new API3.EntityCollection(gameState.sharedScript));
this.wantedTpShips.push(0);
this.wantedWarships.push(0);
} else {
var collec = this.ships.filter(API3.Filters.byStaticMetadata(PlayerID, "sea", i));
collec.registerUpdates();
this.seaShips.push(collec);
collec = this.tpShips.filter(API3.Filters.byStaticMetadata(PlayerID, "sea", i));
collec.registerUpdates();
this.seaTpShips.push(collec);
var collec = this.warships.filter(API3.Filters.byStaticMetadata(PlayerID, "sea", i));
collec.registerUpdates();
this.seaWarships.push(collec);
this.wantedTpShips.push(1);
this.wantedWarships.push(1);
}
this.landZoneDocked.push([]);
}
};
m.NavalManager.prototype.getUnconnectedSeas = function (gameState, region) {
var seas = gameState.ai.accessibility.regionLinks[region]
if (seas.length === 0)
return [];
for (var i = 0; i < seas.length; ++i)
{
if (this.landZoneDocked[region].indexOf(seas[i]) !== -1)
seas.splice(i--,1);
}
return seas;
};
// returns true if there is a path from A to B and we have docks.
m.NavalManager.prototype.canReach = function (gameState, regionA, regionB) {
var path = gameState.ai.accessibility.getTrajectToIndex(regionA, regionB);
if (!path)
{
return false;
}
for (var i = 0; i < path.length - 1; ++i)
{
if (gameState.ai.accessibility.regionType[path[i]] == "land")
if (this.accessibleSeas.indexOf(path[i+1]) === -1)
{
m.debug ("cannot reach because of " + path[i+1]);
return false; // we wn't be able to board on that sea
}
}
return true;
};
m.NavalManager.prototype.checkEvents = function (gameState, queues, events) {
var evts = events["ConstructionFinished"];
// TODO: probably check stuffs like a base destruction.
for (var i in evts)
{
var evt = evts[i];
if (evt && evt.newentity)
{
var entity = gameState.getEntityById(evt.newentity);
if (entity && entity.hasClass("Dock") && entity.isOwn(PlayerID))
{
// okay we have a dock whose construction is finished.
// let's assign it to us.
var pos = entity.position();
var li = gameState.ai.accessibility.getAccessValue(pos);
var ni = entity.getMetadata(PlayerID, "sea");
if (this.landZoneDocked[li].indexOf(ni) === -1)
this.landZoneDocked[li].push(ni);
if (this.accessibleSeas.indexOf(ni) === -1)
this.accessibleSeas.push(ni);
}
}
}
};
m.NavalManager.prototype.addPlan = function(plan) {
this.transportPlans.push(plan);
};
// will create a plan at the end of the turn.
// many units can call this separately and end up in the same plan
// which can be useful.
m.NavalManager.prototype.askForTransport = function(entity, startPos, endPos) {
this.askedPlans.push([entity, startPos, endPos]);
};
// creates aforementionned plans
m.NavalManager.prototype.createPlans = function(gameState) {
var startID = {};
for (var i in this.askedPlans)
{
var plan = this.askedPlans[i];
var startIndex = gameState.ai.accessibility.getAccessValue(plan[1]);
var endIndex = gameState.ai.accessibility.getAccessValue(plan[2]);
if (startIndex === 1 || endIndex === -1)
continue;
if (!startID[startIndex])
{
startID[startIndex] = {};
startID[startIndex][endIndex] = { "dest" : plan[2], "units": [plan[0]]};
}
else if (!startID[startIndex][endIndex])
startID[startIndex][endIndex] = { "dest" : plan[2], "units": [plan[0]]};
else
startID[startIndex][endIndex].units.push(plan[0]);
}
for (var i in startID)
for (var k in startID[i])
{
var tpPlan = new m.TransportPlan(gameState, startID[i][k].units, startID[i][k].dest, false)
this.transportPlans.push (tpPlan);
}
};
// TODO: work on this.
m.NavalManager.prototype.maintainFleet = function(gameState, queues, events) {
// check if we have enough transport ships.
// check per region.
for (var i = 0; i < this.seaShips.length; ++i)
{
var tpNb = gameState.countOwnQueuedEntitiesWithMetadata("sea", i);
if (this.accessibleSeas.indexOf(i) !== -1 && this.seaTpShips[i].length < this.wantedTpShips[i]
&& tpNb + queues.ships.length() === 0 && gameState.getTemplate(gameState.applyCiv("units/{civ}_ship_bireme")).available(gameState))
{
// TODO: check our dock can build the wanted ship types, for Carthage.
queues.ships.addItem(new m.TrainingPlan(gameState, "units/{civ}_ship_bireme", { "sea" : i }, 1, 1 ));
}
}
};
// bumps up the number of ships we want if we need more.
m.NavalManager.prototype.checkLevels = function(gameState, queues) {
if (queues.ships.length() !== 0)
return;
for (var i = 0; i < this.transportPlans.length; ++i)
{
var plan = this.transportPlans[i];
if (plan.needTpShips())
{
var zone = plan.neededShipsZone();
if (zone && gameState.countOwnQueuedEntitiesWithMetadata("sea", zone) > 0)
continue;
if (zone && this.wantedTpShips[i] === 0)
this.wantedTpShips[i]++;
else if (zone && plan.allAtOnce)
this.wantedTpShips[i]++;
}
}
};
// assigns free ships to plans that need some
m.NavalManager.prototype.assignToPlans = function(gameState, queues, events) {
for (var i = 0; i < this.transportPlans.length; ++i)
{
var plan = this.transportPlans[i];
if (plan.needTpShips())
{
// assign one per go.
var zone = plan.neededShipsZone();
if (zone)
{
for each (var ship in this.seaTpShips[zone]._entities)
{
if (!ship.getMetadata(PlayerID, "tpplan"))
{
m.debug ("Assigning ship " + ship.id() + " to plan" + plan.ID);
plan.assignShip(gameState, ship);
return true;
}
}
}
}
}
return false;
};
m.NavalManager.prototype.checkActivePlan = function(ID) {
for (var i = 0; i < this.transportPlans.length; ++i)
if (this.transportPlans[i].ID === ID)
return true;
return false;
};
// Some functions are run every turn
// Others once in a while
m.NavalManager.prototype.update = function(gameState, queues, events) {
Engine.ProfileStart("Naval Manager update");
this.checkEvents(gameState, queues, events);
if (gameState.ai.playedTurn % 10 === 0)
{
this.maintainFleet(gameState, queues, events);
this.checkLevels(gameState, queues);
}
for (var i = 0; i < this.transportPlans.length; ++i)
if (!this.transportPlans[i].carryOn(gameState, this))
{
// whatever the reason, this plan needs to be ended
// it could be that it's finished though.
var seaZone = this.transportPlans[i].neededShipsZone();
var rallyPos = [];
this.docks.forEach(function (dock) {
if (dock.getMetadata(PlayerID,"sea") == seaZone)
rallyPos = dock.position();
});
this.transportPlans[i].ships.move(rallyPos);
this.transportPlans[i].releaseAll(gameState);
this.transportPlans.splice(i,1);
--i;
}
this.assignToPlans(gameState, queues, events);
if (gameState.ai.playedTurn % 10 === 2)
{
this.createPlans(gameState);
this.askedPlans = [];
}
Engine.ProfileStop();
};
return m;
}(PETRA);