0ad/binaries/data/mods/public/simulation/components/Upgrade.js
elexis 52f311da2b Merge resource agnostic branch by s0600204, fixes #3934.
Remove all occurances of hardcoded resources in the simulation, GUI and
AI code by
specifying resources as JSON files in a new simulation subdirectory and
accessing them through a globally defined prototype.

This was SVN commit r18964.
2016-11-19 14:29:45 +00:00

308 lines
8 KiB
JavaScript

function Upgrade() {}
const UPGRADING_PROGRESS_INTERVAL = 250;
Upgrade.prototype.Schema =
"<oneOrMore>" +
"<element>" +
"<anyName />" +
"<interleave>" +
"<element name='Entity' a:help='Entity to upgrade to'>" +
"<text/>" +
"</element>" +
"<optional>" +
"<element name='Icon' a:help='Icon to show in the GUI'>" +
"<text/>" +
"</element>" +
"</optional>" +
"<optional>" +
"<element name='Tooltip' a:help='This will be added to the tooltip to help the player choose why to upgrade.'>" +
"<text/>" +
"</element>" +
"</optional>" +
"<optional>" +
"<element name='Time' a:help='Time required to upgrade this entity, in seconds'>" +
"<data type='nonNegativeInteger'/>" +
"</element>" +
"</optional>" +
"<optional>" +
"<element name='Cost' a:help='Resource cost to upgrade this unit'>" +
"<oneOrMore>" +
"<choice>" +
Resources.BuildSchema("nonNegativeInteger") +
"</choice>" +
"</oneOrMore>" +
"</element>" +
"</optional>" +
"<optional>" +
"<element name='RequiredTechnology' a:help='Define what technology is required for this upgrade'>" +
"<choice>" +
"<text/>" +
"<empty/>" +
"</choice>" +
"</element>" +
"</optional>" +
"<optional>" +
"<element name='CheckPlacementRestrictions' a:help='Upgrading will check for placement restrictions (nb:GUI only)'><empty/></element>" +
"</optional>" +
"</interleave>" +
"</element>" +
"</oneOrMore>";
Upgrade.prototype.Init = function()
{
this.upgrading = false;
this.completed = false;
this.elapsedTime = 0;
this.timer = undefined;
this.upgradeTemplates = {};
for (let choice in this.template)
{
let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
let name = this.template[choice].Entity;
if (cmpIdentity)
name = name.replace(/\{civ\}/g, cmpIdentity.GetCiv());
if (this.upgradeTemplates.name)
warn("Upgrade Component: entity " + this.entity + " has two upgrades to the same entity, only the last will be used.");
this.upgradeTemplates[name] = choice;
}
};
// This will also deal with the "OnDestroy" case.
Upgrade.prototype.OnOwnershipChanged = function(msg)
{
if (!this.completed)
this.CancelUpgrade(msg.from);
if (msg.to !== -1)
this.owner = msg.to;
};
Upgrade.prototype.ChangeUpgradedEntityCount = function(amount)
{
if (!this.IsUpgrading())
return;
let cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
let template = cmpTempMan.GetTemplate(this.upgrading);
let category;
if (template.TrainingRestrictions)
category = template.TrainingRestrictions.Category;
else if (template.BuildRestrictions)
category = template.BuildRestrictions.Category;
if (!category)
return;
let cmpEntityLimits = QueryPlayerIDInterface(this.owner, IID_EntityLimits);
cmpEntityLimits.ChangeCount(category, amount);
};
Upgrade.prototype.CanUpgradeTo = function(template)
{
return this.upgradeTemplates[template] !== undefined;
};
Upgrade.prototype.GetUpgrades = function()
{
let ret = [];
let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
for (let option in this.template)
{
let choice = this.template[option];
let entType = choice.Entity;
if (cmpIdentity)
entType = entType.replace(/\{civ\}/g, cmpIdentity.GetCiv());
let hasCosts;
let cost = {};
if (choice.Cost)
{
hasCosts = true;
for (let type in choice.Cost)
cost[type] = ApplyValueModificationsToTemplate("Upgrade/Cost/"+type, +choice.Cost[type], this.owner, entType);
}
if (choice.Time)
{
hasCosts = true;
let cmpPlayer = QueryPlayerIDInterface(this.owner, IID_Player);
cost.time = ApplyValueModificationsToTemplate("Upgrade/Time", +choice.Time, this.owner, entType) *
cmpPlayer.GetCheatTimeMultiplier();
}
ret.push({
"entity": entType,
"icon": choice.Icon || undefined,
"cost": hasCosts ? cost : undefined,
"tooltip": choice.Tooltip || undefined,
"requiredTechnology": this.GetRequiredTechnology(option),
});
}
return ret;
};
Upgrade.prototype.CancelTimer = function()
{
if (!this.timer)
return;
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.CancelTimer(this.timer);
this.timer = undefined;
};
Upgrade.prototype.IsUpgrading = function()
{
return !!this.upgrading;
};
Upgrade.prototype.GetUpgradingTo = function()
{
return this.upgrading;
};
Upgrade.prototype.WillCheckPlacementRestrictions = function(template)
{
if (!this.upgradeTemplates[template])
return undefined;
// is undefined by default so use X in Y
return "CheckPlacementRestrictions" in this.template[this.upgradeTemplates[template]];
};
Upgrade.prototype.GetRequiredTechnology = function(templateArg)
{
let choice = this.upgradeTemplates[templateArg] || templateArg;
if (this.template[choice].RequiredTechnology)
return this.template[choice].RequiredTechnology;
if (!("RequiredTechnology" in this.template[choice]))
return undefined;
let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
let entType = this.template[choice].Entity;
if (cmpIdentity)
entType = entType.replace(/\{civ\}/g, cmpIdentity.GetCiv());
let template = cmpTemplateManager.GetTemplate(entType);
return template.Identity.RequiredTechnology || undefined;
};
Upgrade.prototype.GetResourceCosts = function(template)
{
if (!this.upgradeTemplates[template])
return undefined;
let choice = this.upgradeTemplates[template];
if (!this.template[choice].Cost)
return {};
let costs = {};
for (let r in this.template[choice].Cost)
costs[r] = ApplyValueModificationsToEntity("Upgrade/Cost/"+r, +this.template[choice].Cost[r], this.entity);
return costs;
};
Upgrade.prototype.Upgrade = function(template)
{
if (this.IsUpgrading() || !this.upgradeTemplates[template])
return false;
let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
if (!cmpPlayer.TrySubtractResources(this.GetResourceCosts(template)))
return false;
this.upgrading = template;
// Prevent cheating
this.ChangeUpgradedEntityCount(1);
if (this.GetUpgradeTime(template) !== 0)
{
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
this.timer = cmpTimer.SetInterval(this.entity, IID_Upgrade, "UpgradeProgress", 0, UPGRADING_PROGRESS_INTERVAL, { "upgrading": template });
}
else
this.UpgradeProgress();
return true;
};
Upgrade.prototype.CancelUpgrade = function(owner)
{
if (!this.IsUpgrading())
return;
let cmpPlayer = QueryPlayerIDInterface(owner, IID_Player);
if (cmpPlayer)
cmpPlayer.AddResources(this.GetResourceCosts(this.upgrading));
this.ChangeUpgradedEntityCount(-1);
this.upgrading = false;
this.CancelTimer();
this.SetElapsedTime(0);
};
Upgrade.prototype.GetUpgradeTime = function(templateArg)
{
let template = this.upgrading || templateArg;
let choice = this.upgradeTemplates[template];
if (!choice)
return undefined;
if (!this.template[choice].Time)
return 0;
let cmpPlayer = QueryPlayerIDInterface(this.owner, IID_Player);
return ApplyValueModificationsToEntity("Upgrade/Time", +this.template[choice].Time, this.entity) *
cmpPlayer.GetCheatTimeMultiplier();
};
Upgrade.prototype.GetElapsedTime = function()
{
return this.elapsedTime;
};
Upgrade.prototype.GetProgress = function()
{
if (!this.IsUpgrading())
return undefined;
return this.GetUpgradeTime() == 0 ? 1 : Math.min(this.elapsedTime / 1000.0 / this.GetUpgradeTime(), 1.0);
};
Upgrade.prototype.SetElapsedTime = function(time)
{
this.elapsedTime = time;
};
Upgrade.prototype.UpgradeProgress = function(data, lateness)
{
if (this.elapsedTime/1000.0 < this.GetUpgradeTime())
{
this.SetElapsedTime(this.GetElapsedTime() + UPGRADING_PROGRESS_INTERVAL + lateness);
return;
}
this.CancelTimer();
this.completed = true;
this.ChangeUpgradedEntityCount(-1);
let newEntity = ChangeEntityTemplate(this.entity, this.upgrading);
if (newEntity)
PlaySound("upgraded", newEntity);
};
Engine.RegisterComponentType(IID_Upgrade, "Upgrade", Upgrade);