0ad/binaries/data/mods/public/simulation/components/EntityLimits.js
2015-01-24 12:03:31 +00:00

252 lines
8.0 KiB
JavaScript

function EntityLimits() {}
EntityLimits.prototype.Schema =
"<a:help>Specifies per category limits on number of entities (buildings or units) that can be created for each player</a:help>" +
"<a:example>" +
"<Limits>" +
"<DefenseTower>25</DefenseTower>" +
"<Fortress>10</Fortress>" +
"<Wonder>1</Wonder>" +
"<Hero>1</Hero>" +
"<Apadana>1</Apadana>" +
"<Monument>5</Monument>" +
"</Limits>" +
"<LimitChangers>" +
"<Monument>" +
"<CivilCentre>2</CivilCentre>" +
"</Monument>" +
"</LimitChangers>" +
"<LimitRemovers>" +
"<CivilCentre>" +
"<RequiredTechs datatype=\"tokens\">town_phase</RequiredTechs>" +
"</CivilCentre>" +
"</LimitRemovers>" +
"</a:example>" +
"<element name='Limits'>" +
"<zeroOrMore>" +
"<element a:help='Specifies a category of building/unit on which to apply this limit. See BuildRestrictions/TrainingRestrictions for possible categories'>" +
"<anyName />" +
"<data type='integer'/>" +
"</element>" +
"</zeroOrMore>" +
"</element>" +
"<element name='LimitChangers'>" +
"<zeroOrMore>" +
"<element a:help='Specifies a category of building/unit on which to apply this limit. See BuildRestrictions/TrainingRestrictions for possible categories'>" +
"<anyName />" +
"<zeroOrMore>" +
"<element a:help='Specifies the class that changes the entity limit'>" +
"<anyName />" +
"<data type='integer'/>" +
"</element>" +
"</zeroOrMore>" +
"</element>" +
"</zeroOrMore>" +
"</element>" +
"<element name='LimitRemovers'>" +
"<zeroOrMore>" +
"<element a:help='Specifies a category of building/unit on which to remove this limit. The limit will be removed if all the followings requirements are satisfied'>" +
"<anyName />" +
"<oneOrMore>" +
"<element a:help='Possible requirements are: RequiredTechs and RequiredClasses'>" +
"<anyName />" +
"<attribute name='datatype'>" +
"<value>tokens</value>" +
"</attribute>" +
"<text/>" +
"</element>" +
"</oneOrMore>" +
"</element>" +
"</zeroOrMore>" +
"</element>";
const TRAINING = "training";
const BUILD = "build";
EntityLimits.prototype.Init = function()
{
this.limit = {};
this.count = {}; // counts entities which change the limit of the given category
this.changers = {};
this.removers = {};
this.classCount = {}; // counts entities with the given class, used in the limit removal
this.removedLimit = {};
for (var category in this.template.Limits)
{
this.limit[category] = +this.template.Limits[category];
this.count[category] = 0;
if (category in this.template.LimitChangers)
{
this.changers[category] = {};
for (var c in this.template.LimitChangers[category])
this.changers[category][c] = +this.template.LimitChangers[category][c];
}
if (category in this.template.LimitRemovers)
{
this.removedLimit[category] = this.limit[category]; // keep a copy of removeable limits for possible restoration
this.removers[category] = {};
for (var c in this.template.LimitRemovers[category])
{
this.removers[category][c] = this.template.LimitRemovers[category][c]._string.split(/\s+/);
if (c === "RequiredClasses")
for (var cls of this.removers[category][c])
this.classCount[cls] = 0;
}
}
}
};
EntityLimits.prototype.ChangeCount = function(category, value)
{
if (this.count[category] !== undefined)
this.count[category] += value;
};
EntityLimits.prototype.GetLimits = function()
{
return this.limit;
};
EntityLimits.prototype.GetCounts = function()
{
return this.count;
};
EntityLimits.prototype.GetLimitChangers = function()
{
return this.changers;
};
EntityLimits.prototype.UpdateLimitsFromTech = function(tech)
{
for (var category in this.removers)
if ("RequiredTechs" in this.removers[category] && this.removers[category]["RequiredTechs"].indexOf(tech) !== -1)
this.removers[category]["RequiredTechs"].splice(this.removers[category]["RequiredTechs"].indexOf(tech), 1);
this.UpdateLimitRemoval();
};
EntityLimits.prototype.UpdateLimitRemoval = function()
{
for (var category in this.removers)
{
var nolimit = true;
if ("RequiredTechs" in this.removers[category])
nolimit = !this.removers[category]["RequiredTechs"].length;
if (nolimit && "RequiredClasses" in this.removers[category])
for (var cls of this.removers[category]["RequiredClasses"])
nolimit = nolimit && this.classCount[cls] > 0;
if (nolimit && this.limit[category] !== undefined)
this.limit[category] = undefined;
else if (!nolimit && this.limit[category] === undefined)
this.limit[category] = this.removedLimit[category];
}
};
EntityLimits.prototype.AllowedToCreate = function(limitType, category, count)
{
// Allow unspecified categories and those with no limit
if (this.count[category] === undefined || this.limit[category] === undefined)
return true;
if (this.count[category] + count > this.limit[category])
{
var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
var notification = {
"players": [cmpPlayer.GetPlayerID()],
"translateMessage": true,
"translateParameters": ["category"],
"parameters": {"category": category, "limit": this.limit[category]},
};
if (limitType == BUILD)
notification.message = markForTranslation("%(category)s build limit of %(limit)s reached");
else if (limitType == TRAINING)
notification.message = markForTranslation("%(category)s training limit of %(limit)s reached");
else
{
warn("EntityLimits.js: Unknown LimitType " + limitType)
notification.message = markForTranslation("%(category)s limit of %(limit)s reached");
}
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGUIInterface.PushNotification(notification);
return false;
}
return true;
}
EntityLimits.prototype.AllowedToBuild = function(category)
{
// We pass count 0 as the creation of the building has already taken place and
// the ownership has been set (triggering OnGlobalOwnershipChanged)
return this.AllowedToCreate(BUILD, category, 0);
};
EntityLimits.prototype.AllowedToTrain = function(category, count)
{
return this.AllowedToCreate(TRAINING, category, count);
};
EntityLimits.prototype.OnGlobalOwnershipChanged = function(msg)
{
// check if we are adding or removing an entity from this player
var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
if (!cmpPlayer)
{
error("EntityLimits component is defined on a non-player entity");
return;
}
if (msg.from == cmpPlayer.GetPlayerID())
var modifier = -1;
else if (msg.to == cmpPlayer.GetPlayerID())
var modifier = 1;
else
return;
// Update entity counts
var category = null;
var cmpBuildRestrictions = Engine.QueryInterface(msg.entity, IID_BuildRestrictions);
if (cmpBuildRestrictions)
category = cmpBuildRestrictions.GetCategory();
var cmpTrainingRestrictions = Engine.QueryInterface(msg.entity, IID_TrainingRestrictions);
if (cmpTrainingRestrictions)
category = cmpTrainingRestrictions.GetCategory();
if (category)
this.ChangeCount(category, modifier);
// Update entity limits
var cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity);
if (!cmpIdentity)
return;
// foundations shouldn't change the entity limits until they're completed
var cmpFoundation = Engine.QueryInterface(msg.entity, IID_Foundation);
if (cmpFoundation)
return;
var classes = cmpIdentity.GetClassesList();
for (var category in this.changers)
for (var c in this.changers[category])
if (classes.indexOf(c) >= 0)
{
if (this.limit[category] != undefined)
this.limit[category] += modifier * this.changers[category][c];
if (this.removedLimit[category] != undefined) // update removed limits in case we want to restore it
this.removedLimit[category] += modifier * this.changers[category][c];
}
for (var category in this.removers)
if ("RequiredClasses" in this.removers[category])
for (var cls of this.removers[category]["RequiredClasses"])
if (classes.indexOf(cls) !== -1)
this.classCount[cls] += modifier;
this.UpdateLimitRemoval();
};
Engine.RegisterComponentType(IID_EntityLimits, "EntityLimits", EntityLimits);