0ad/binaries/data/mods/public/simulation/components/Builder.js
wowgetoffyourcellphone 5268cb62a6
Some checks failed
checkrefs / lfscheck (push) Has been cancelled
checkrefs / checkrefs (push) Has been cancelled
lint / cppcheck (push) Has been cancelled
lint / copyright (push) Has been cancelled
lint / jenkinsfiles (push) Has been cancelled
pre-commit / build (push) Has been cancelled
Persians -> Achaemenids
Persians -> Achaemenids
Pers -> Achae

Addressing Checkrefs

Internal Achaemenid and Persian references

Reported by @Stan`

Update unit_tables.py
2026-05-02 02:50:39 +02:00

208 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/achae/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&apos;s owner, while the string \"{native}\" will be automatically replaced by the unit&apos;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 [];
const cmpPlayer = QueryOwnerInterface(this.entity);
if (!cmpPlayer)
return [];
string = ApplyValueModificationsToEntity("Builder/Entities/_string", string, this.entity);
const 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+/);
const disabledTemplates = cmpPlayer.GetDisabledTemplates();
const cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
return entities.filter(ent => !disabledTemplates[ent] && cmpTemplateManager.TemplateExists(ent));
};
Builder.prototype.GetRange = function()
{
let max = 2;
const 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)
{
const cmpFoundation = QueryMiragedInterface(target, IID_Foundation);
const cmpRepairable = QueryMiragedInterface(target, IID_Repairable);
if (!cmpFoundation && (!cmpRepairable || !cmpRepairable.IsRepairable()))
return false;
const 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;
const cmpBuilderList = QueryBuilderListInterface(target);
if (cmpBuilderList)
cmpBuilderList.AddBuilder(this.entity);
const cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
if (cmpVisual)
cmpVisual.SelectAnimation("build", false, 1.0);
this.target = target;
this.callerIID = callerIID;
const 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;
const cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.CancelTimer(this.timer);
delete this.timer;
const cmpBuilderList = QueryBuilderListInterface(this.target);
if (cmpBuilderList)
cmpBuilderList.RemoveBuilder(this.entity);
delete this.target;
const 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.
const callerIID = this.callerIID;
delete this.callerIID;
if (reason && callerIID)
{
const 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);
const cmpFoundation = Engine.QueryInterface(this.target, IID_Foundation);
if (cmpFoundation)
{
cmpFoundation.Build(this.entity, this.GetRate());
return;
}
const 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)
{
const range = this.GetRange();
const 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.
const cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
if (cmpPlayer)
Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).SetSelectionDirty(cmpPlayer.GetPlayerID());
};
Engine.RegisterComponentType(IID_Builder, "Builder", Builder);