forked from mirrors/0ad
0a2bd6a5a4
The common-api already used a coding pattern which was called modules. That is replaced with native js-modules. Refs: #8081
195 lines
5.6 KiB
JavaScript
195 lines
5.6 KiB
JavaScript
import * as filters from "simulation/ai/common-api/filters.js";
|
|
import { warn as aiWarn } from "simulation/ai/common-api/utils.js";
|
|
|
|
/**
|
|
* One task of this manager is to cache the list of structures we have builders for,
|
|
* to avoid having to loop on all entities each time.
|
|
* It also takes care of the structures we can't currently build and should not try to build endlessly.
|
|
*/
|
|
|
|
export function BuildManager()
|
|
{
|
|
// List of buildings we have builders for, with number of possible builders.
|
|
this.builderCounters = new Map();
|
|
// List of buildings we can't currently build (because no room, no builder or whatever),
|
|
// with time we should wait before trying again to build it.
|
|
this.unbuildables = new Map();
|
|
}
|
|
|
|
/** Initialization at start of game */
|
|
BuildManager.prototype.init = function(gameState)
|
|
{
|
|
const civ = gameState.getPlayerCiv();
|
|
for (const ent of gameState.getOwnUnits().values())
|
|
this.incrementBuilderCounters(civ, ent, 1);
|
|
};
|
|
|
|
BuildManager.prototype.incrementBuilderCounters = function(civ, ent, increment)
|
|
{
|
|
for (const buildable of ent.buildableEntities(civ))
|
|
{
|
|
if (this.builderCounters.has(buildable))
|
|
{
|
|
const count = this.builderCounters.get(buildable) + increment;
|
|
if (count < 0)
|
|
{
|
|
aiWarn(" Petra error in incrementBuilderCounters for " + buildable +
|
|
" with count < 0");
|
|
continue;
|
|
}
|
|
this.builderCounters.set(buildable, count);
|
|
}
|
|
else if (increment > 0)
|
|
this.builderCounters.set(buildable, increment);
|
|
else
|
|
aiWarn(" Petra error in incrementBuilderCounters for " + buildable + " not yet set");
|
|
}
|
|
};
|
|
|
|
/** Update the builders counters */
|
|
BuildManager.prototype.checkEvents = function(gameState, events)
|
|
{
|
|
this.elapsedTime = gameState.ai.elapsedTime;
|
|
const civ = gameState.getPlayerCiv();
|
|
|
|
for (const evt of events.Create)
|
|
{
|
|
if (events.Destroy.some(e => e.entity == evt.entity))
|
|
continue;
|
|
const ent = gameState.getEntityById(evt.entity);
|
|
if (ent && ent.isOwn(PlayerID) && ent.hasClass("Unit"))
|
|
this.incrementBuilderCounters(civ, ent, 1);
|
|
}
|
|
|
|
for (const evt of events.Destroy)
|
|
{
|
|
if (events.Create.some(e => e.entity == evt.entity) || !evt.entityObj)
|
|
continue;
|
|
const ent = evt.entityObj;
|
|
if (ent && ent.isOwn(PlayerID) && ent.hasClass("Unit"))
|
|
this.incrementBuilderCounters(civ, ent, -1);
|
|
}
|
|
|
|
for (const evt of events.OwnershipChanged) // capture events
|
|
{
|
|
let increment;
|
|
if (evt.from == PlayerID)
|
|
increment = -1;
|
|
else if (evt.to == PlayerID)
|
|
increment = 1;
|
|
else
|
|
continue;
|
|
const ent = gameState.getEntityById(evt.entity);
|
|
if (ent && ent.hasClass("Unit"))
|
|
this.incrementBuilderCounters(civ, ent, increment);
|
|
}
|
|
|
|
for (const evt of events.ValueModification)
|
|
{
|
|
if (evt.component != "Builder" ||
|
|
!evt.valueNames.some(val => val.startsWith("Builder/Entities/")))
|
|
continue;
|
|
|
|
// Unfortunately there really is not an easy way to determine the changes
|
|
// at this stage, so we simply have to dump the cache.
|
|
this.builderCounters = new Map();
|
|
|
|
for (const ent of gameState.getOwnUnits().values())
|
|
this.incrementBuilderCounters(civ, ent, 1);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the buildable structures passing a filter.
|
|
*/
|
|
BuildManager.prototype.findStructuresByFilter = function(gameState, filter)
|
|
{
|
|
const result = [];
|
|
for (const [templateName, count] of this.builderCounters)
|
|
{
|
|
if (!count || gameState.isTemplateDisabled(templateName))
|
|
continue;
|
|
const template = gameState.getTemplate(templateName);
|
|
if (!template || !template.available(gameState))
|
|
continue;
|
|
if (filter.func(template))
|
|
result.push(templateName);
|
|
}
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* Get the first buildable structure with a given class
|
|
* TODO when several available, choose the best one
|
|
*/
|
|
BuildManager.prototype.findStructureWithClass = function(gameState, classes)
|
|
{
|
|
return this.findStructuresByFilter(gameState, filters.byClasses(classes))[0];
|
|
};
|
|
|
|
BuildManager.prototype.hasBuilder = function(template)
|
|
{
|
|
const numBuilders = this.builderCounters.get(template);
|
|
return numBuilders && numBuilders > 0;
|
|
};
|
|
|
|
BuildManager.prototype.isUnbuildable = function(gameState, template)
|
|
{
|
|
return this.unbuildables.has(template) && this.unbuildables.get(template).time > gameState.ai.elapsedTime;
|
|
};
|
|
|
|
BuildManager.prototype.setBuildable = function(template)
|
|
{
|
|
if (this.unbuildables.has(template))
|
|
this.unbuildables.delete(template);
|
|
};
|
|
|
|
/** Time is the duration in second that we will wait before checking again if it is buildable */
|
|
BuildManager.prototype.setUnbuildable = function(gameState, template, time = 90, reason = "room")
|
|
{
|
|
if (!this.unbuildables.has(template))
|
|
this.unbuildables.set(template, { "reason": reason, "time": gameState.ai.elapsedTime + time });
|
|
else
|
|
{
|
|
const unbuildable = this.unbuildables.get(template);
|
|
if (unbuildable.time < gameState.ai.elapsedTime + time)
|
|
{
|
|
unbuildable.reason = reason;
|
|
unbuildable.time = gameState.ai.elapsedTime + time;
|
|
}
|
|
}
|
|
};
|
|
|
|
/** Return the number of unbuildables due to missing room */
|
|
BuildManager.prototype.numberMissingRoom = function(gameState)
|
|
{
|
|
let num = 0;
|
|
for (const unbuildable of this.unbuildables.values())
|
|
if (unbuildable.reason == "room" && unbuildable.time > gameState.ai.elapsedTime)
|
|
++num;
|
|
return num;
|
|
};
|
|
|
|
/** Reset the unbuildables due to missing room */
|
|
BuildManager.prototype.resetMissingRoom = function(gameState)
|
|
{
|
|
for (const [key, unbuildable] of this.unbuildables)
|
|
if (unbuildable.reason == "room")
|
|
this.unbuildables.delete(key);
|
|
};
|
|
|
|
BuildManager.prototype.Serialize = function()
|
|
{
|
|
return {
|
|
"builderCounters": this.builderCounters,
|
|
"unbuildables": this.unbuildables
|
|
};
|
|
};
|
|
|
|
BuildManager.prototype.Deserialize = function(data)
|
|
{
|
|
for (const key in data)
|
|
this[key] = data[key];
|
|
};
|