mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-07-20 16:27:26 +00:00
ea72437739
Since the players/civs already have cmpIdentity, use it. This forces civs to have corresponding XML in the `special/players/` folder. Also moves the files from `special/player/` to `special/players/` consistent with other folders. And moves the generic `player.xml` one level up. Differential revision: https://code.wildfiregames.com/D4473 Help and comments by: @Stan, @wraitii This was SVN commit r26298.
209 lines
6.2 KiB
JavaScript
209 lines
6.2 KiB
JavaScript
function Builder() {}
|
|
|
|
Builder.prototype.Schema =
|
|
"<a:help>Allows the unit to construct and repair buildings.</a:help>" +
|
|
"<a:example>" +
|
|
"<Rate>1.0</Rate>" +
|
|
"<Entities datatype='tokens'>" +
|
|
"\n structures/{civ}/barracks\n structures/{native}/civil_centre\n structures/pers/apadana\n " +
|
|
"</Entities>" +
|
|
"</a:example>" +
|
|
"<element name='Rate' a:help='Construction speed multiplier (1.0 is normal speed, higher values are faster).'>" +
|
|
"<ref name='positiveDecimal'/>" +
|
|
"</element>" +
|
|
"<element name='Entities' a:help='Space-separated list of entity template names that this unit can build. The special string \"{civ}\" will be automatically replaced by the civ code of the unit's owner, while the string \"{native}\" will be automatically replaced by the unit's civ code. This element can also be empty, in which case no new foundations may be placed by the unit, but they can still repair existing buildings.'>" +
|
|
"<attribute name='datatype'>" +
|
|
"<value>tokens</value>" +
|
|
"</attribute>" +
|
|
"<text/>" +
|
|
"</element>";
|
|
|
|
/*
|
|
* Build interval and repeat time, in ms.
|
|
*/
|
|
Builder.prototype.BUILD_INTERVAL = 1000;
|
|
|
|
Builder.prototype.Init = function()
|
|
{
|
|
};
|
|
|
|
Builder.prototype.GetEntitiesList = function()
|
|
{
|
|
let string = this.template.Entities._string;
|
|
if (!string)
|
|
return [];
|
|
|
|
let cmpPlayer = QueryOwnerInterface(this.entity);
|
|
if (!cmpPlayer)
|
|
return [];
|
|
|
|
string = ApplyValueModificationsToEntity("Builder/Entities/_string", string, this.entity);
|
|
|
|
let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
|
|
if (cmpIdentity)
|
|
string = string.replace(/\{native\}/g, cmpIdentity.GetCiv());
|
|
|
|
const entities = string.replace(/\{civ\}/g, QueryOwnerInterface(this.entity, IID_Identity).GetCiv()).split(/\s+/);
|
|
|
|
let disabledTemplates = cmpPlayer.GetDisabledTemplates();
|
|
|
|
let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
|
|
|
return entities.filter(ent => !disabledTemplates[ent] && cmpTemplateManager.TemplateExists(ent));
|
|
};
|
|
|
|
Builder.prototype.GetRange = function()
|
|
{
|
|
let max = 2;
|
|
let cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
|
|
if (cmpObstruction)
|
|
max += cmpObstruction.GetSize();
|
|
|
|
return { "max": max, "min": 0 };
|
|
};
|
|
|
|
Builder.prototype.GetRate = function()
|
|
{
|
|
return ApplyValueModificationsToEntity("Builder/Rate", +this.template.Rate, this.entity);
|
|
};
|
|
|
|
/**
|
|
* @param {number} target - The target to check.
|
|
* @return {boolean} - Whether we can build/repair the given target.
|
|
*/
|
|
Builder.prototype.CanRepair = function(target)
|
|
{
|
|
let cmpFoundation = QueryMiragedInterface(target, IID_Foundation);
|
|
let cmpRepairable = QueryMiragedInterface(target, IID_Repairable);
|
|
if (!cmpFoundation && (!cmpRepairable || !cmpRepairable.IsRepairable()))
|
|
return false;
|
|
|
|
let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
|
return cmpOwnership && IsOwnedByAllyOfPlayer(cmpOwnership.GetOwner(), target);
|
|
};
|
|
|
|
/**
|
|
* @param {number} target - The target to repair.
|
|
* @param {number} callerIID - The IID to notify on specific events.
|
|
* @return {boolean} - Whether we started repairing.
|
|
*/
|
|
Builder.prototype.StartRepairing = function(target, callerIID)
|
|
{
|
|
if (this.target)
|
|
this.StopRepairing();
|
|
|
|
if (!this.CanRepair(target))
|
|
return false;
|
|
|
|
let cmpBuilderList = QueryBuilderListInterface(target);
|
|
if (cmpBuilderList)
|
|
cmpBuilderList.AddBuilder(this.entity);
|
|
|
|
let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
|
|
if (cmpVisual)
|
|
cmpVisual.SelectAnimation("build", false, 1.0);
|
|
|
|
this.target = target;
|
|
this.callerIID = callerIID;
|
|
|
|
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
|
this.timer = cmpTimer.SetInterval(this.entity, IID_Builder, "PerformBuilding", this.BUILD_INTERVAL, this.BUILD_INTERVAL, null);
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* @param {string} reason - The reason why we stopped repairing.
|
|
*/
|
|
Builder.prototype.StopRepairing = function(reason)
|
|
{
|
|
if (!this.target)
|
|
return;
|
|
|
|
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
|
cmpTimer.CancelTimer(this.timer);
|
|
delete this.timer;
|
|
|
|
let cmpBuilderList = QueryBuilderListInterface(this.target);
|
|
if (cmpBuilderList)
|
|
cmpBuilderList.RemoveBuilder(this.entity);
|
|
|
|
delete this.target;
|
|
|
|
let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
|
|
if (cmpVisual)
|
|
cmpVisual.SelectAnimation("idle", false, 1.0);
|
|
|
|
// The callerIID component may start again,
|
|
// replacing the callerIID, hence save that.
|
|
let callerIID = this.callerIID;
|
|
delete this.callerIID;
|
|
|
|
if (reason && callerIID)
|
|
{
|
|
let component = Engine.QueryInterface(this.entity, callerIID);
|
|
if (component)
|
|
component.ProcessMessage(reason, null);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Repair our target entity.
|
|
* @params - data and lateness are unused.
|
|
*/
|
|
Builder.prototype.PerformBuilding = function(data, lateness)
|
|
{
|
|
if (!this.CanRepair(this.target))
|
|
{
|
|
this.StopRepairing("TargetInvalidated");
|
|
return;
|
|
}
|
|
|
|
if (!this.IsTargetInRange(this.target))
|
|
{
|
|
this.StopRepairing("OutOfRange");
|
|
return;
|
|
}
|
|
|
|
// ToDo: Enable entities to keep facing a target.
|
|
Engine.QueryInterface(this.entity, IID_UnitAI)?.FaceTowardsTarget(this.target);
|
|
|
|
let cmpFoundation = Engine.QueryInterface(this.target, IID_Foundation);
|
|
if (cmpFoundation)
|
|
{
|
|
cmpFoundation.Build(this.entity, this.GetRate());
|
|
return;
|
|
}
|
|
|
|
let cmpRepairable = Engine.QueryInterface(this.target, IID_Repairable);
|
|
if (cmpRepairable)
|
|
{
|
|
cmpRepairable.Repair(this.entity, this.GetRate());
|
|
return;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {number} - The entity ID of the target to check.
|
|
* @return {boolean} - Whether this entity is in range of its target.
|
|
*/
|
|
Builder.prototype.IsTargetInRange = function(target)
|
|
{
|
|
let range = this.GetRange();
|
|
let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
|
|
return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, range.max, false);
|
|
};
|
|
|
|
Builder.prototype.OnValueModification = function(msg)
|
|
{
|
|
if (msg.component != "Builder" || !msg.valueNames.some(name => name.endsWith('_string')))
|
|
return;
|
|
|
|
// Token changes may require selection updates.
|
|
let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
|
|
if (cmpPlayer)
|
|
Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).SetSelectionDirty(cmpPlayer.GetPlayerID());
|
|
};
|
|
|
|
Engine.RegisterComponentType(IID_Builder, "Builder", Builder);
|