0ad/binaries/data/mods/public/simulation/components/EntityLimits.js
Angen e07f12bea6 Clean up tower classes and related changes.
Classes:
The Defensive class is made visible (cf. Civic, Economic, Military).
The Fortress class is made visible (because of build restrictions).
The Tower class defined in template_structure_defensive_tower.xml is now
visible.
The unused GarrisonTower class is removed.
The DefenseTower class is deprecated in favour of one unique class in
each of the *_tower.xml children templates:
*_artillery.xml → ArtilleryTower
*_bolt.xml → BoltTower
*_sentry.xml → SentryTower
*_stone.xml → StoneTower
template_structure_defensive_wall_tower.xml does not inherit from
*_tower and therefore has a WallTower class instead of a Tower class.
Updated the Identity.js file accordingly.
Updated the Petra AI files accordingly (DefenseTower → Tower).
Tower build restrictions are changed to simply Tower.
Technologies:
tower_armour.json affects all towers, but not outposts (unchanged).
tower_decay.json and tower_vision.json affect only outposts (unchanged).
tower_crenellations.json, tower_murderholes.json, tower_range.json, and
tower_watch.json affect now only the Sentry and Stone towers, instead of
all towers. This alters gameplay for the Artillery and Bolt tower, but
those shouldn't have been benefitting from arrow tower technologies
anyway.
Standardized the tooltips per the style guide.
Updated the <Identity/Tooltip> in the tower templates.
Inserted <BatchTimeModifier>1.0</BatchTimeModifier> in
template_structure.xml, where the <ProductionQueue> is defined, and
removed the <BatchTimeModifier> from templates that can't train any
units.

Patch by: Nescio
Reviewed by: Angen
Differential Revision: https://code.wildfiregames.com/D2549
This was SVN commit r23853.
2020-07-19 11:45:00 +00:00

276 lines
8.9 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>" +
"<Apadana>1</Apadana>" +
"<Fortress>10</Fortress>" +
"<Hero>1</Hero>" +
"<Monument>5</Monument>" +
"<Tower>25</Tower>" +
"<Wonder>1</Wonder>" +
"</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);
};
/**
* @param {number} ent - id of the entity which would be replaced.
* @param {string} template - name of the new template.
* @return {boolean} - whether we can replace ent.
*/
EntityLimits.prototype.AllowedToReplace = function(ent, template)
{
let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
let templateFrom = cmpTemplateManager.GetTemplate(cmpTemplateManager.GetCurrentTemplateName(ent));
let templateTo = cmpTemplateManager.GetTemplate(template);
if (templateTo.TrainingRestrictions)
{
let category = templateTo.TrainingRestrictions.Category;
return this.AllowedToCreate(TRAINING, category, templateFrom.TrainingRestrictions && templateFrom.TrainingRestrictions.Category == category ? 0 : 1);
}
if (templateTo.BuildRestrictions)
{
let category = templateTo.BuildRestrictions.Category;
return this.AllowedToCreate(BUILD, category, templateFrom.BuildRestrictions && templateFrom.BuildRestrictions.Category == category ? 0 : 1);
}
return true;
};
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);