Files
0ad/binaries/data/mods/public/simulation/components/AlertRaiser.js
T
Freagarach fb498d97d9 Fix raising alert.
After 21e866fcf0.
AllertRaiser replaced the order instead of just calling the required
function.
While at it, check for the garrisonable size when looking for buildings.

Differential revision: D3789
Tested by: @Langbart
This was SVN commit r25183.
2021-04-02 17:01:59 +00:00

147 lines
5.2 KiB
JavaScript

function AlertRaiser() {}
AlertRaiser.prototype.Schema =
"<element name='List' a:help='Classes of entities which are affected by this alert raiser'>" +
"<attribute name='datatype'>" +
"<value>tokens</value>" +
"</attribute>" +
"<text/>" +
"</element>" +
"<element name='RaiseAlertRange'><data type='integer'/></element>" +
"<element name='EndOfAlertRange'><data type='integer'/></element>" +
"<element name='SearchRange'><data type='integer'/></element>";
AlertRaiser.prototype.Init = function()
{
// Store the last time the alert was used so players can't lag the game by raising alerts repeatedly.
this.lastTime = 0;
};
AlertRaiser.prototype.UnitFilter = function(unit)
{
let cmpIdentity = Engine.QueryInterface(unit, IID_Identity);
return cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), this.template.List._string);
};
AlertRaiser.prototype.RaiseAlert = function()
{
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
if (cmpTimer.GetTime() == this.lastTime)
return;
this.lastTime = cmpTimer.GetTime();
PlaySound("alert_raise", this.entity);
let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
if (!cmpOwnership || cmpOwnership.GetOwner() == INVALID_PLAYER)
return;
let owner = cmpOwnership.GetOwner();
let cmpPlayer = QueryOwnerInterface(this.entity);
let mutualAllies = cmpPlayer ? cmpPlayer.GetMutualAllies() : [owner];
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
// Store the number of available garrison spots so that units don't try to garrison in buildings that will be full
let reserved = new Map();
let units = cmpRangeManager.ExecuteQuery(this.entity, 0, +this.template.RaiseAlertRange, [owner], IID_UnitAI, true).filter(ent => this.UnitFilter(ent));
for (let unit of units)
{
let cmpGarrisonable = Engine.QueryInterface(unit, IID_Garrisonable);
if (!cmpGarrisonable)
continue;
let size = cmpGarrisonable.TotalSize();
let cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI);
let holder = cmpRangeManager.ExecuteQuery(unit, 0, +this.template.SearchRange, mutualAllies, IID_GarrisonHolder, true).find(ent => {
// Ignore moving garrison holders
if (Engine.QueryInterface(ent, IID_UnitAI))
return false;
// Ensure that the garrison holder is within range of the alert raiser
if (+this.template.EndOfAlertRange > 0 && PositionHelper.DistanceBetweenEntities(this.entity, ent) > +this.template.EndOfAlertRange)
return false;
if (!cmpUnitAI.CheckTargetVisible(ent))
return false;
let cmpGarrisonHolder = Engine.QueryInterface(ent, IID_GarrisonHolder);
if (!reserved.has(ent))
reserved.set(ent, cmpGarrisonHolder.GetCapacity() - cmpGarrisonHolder.OccupiedSlots());
return cmpGarrisonHolder.IsAllowedToGarrison(unit) && reserved.get(ent) >= size;
});
if (holder)
{
reserved.set(holder, reserved.get(holder) - size);
cmpUnitAI.Garrison(holder, false, false);
}
else
// If no available spots, stop moving
cmpUnitAI.ReplaceOrder("Stop", { "force": true });
}
};
AlertRaiser.prototype.EndOfAlert = function()
{
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
if (cmpTimer.GetTime() == this.lastTime)
return;
this.lastTime = cmpTimer.GetTime();
PlaySound("alert_end", this.entity);
let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
if (!cmpOwnership || cmpOwnership.GetOwner() == INVALID_PLAYER)
return;
let owner = cmpOwnership.GetOwner();
let cmpPlayer = QueryOwnerInterface(this.entity);
let mutualAllies = cmpPlayer ? cmpPlayer.GetMutualAllies() : [owner];
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
// Units that are not garrisoned should go back to work
let units = cmpRangeManager.ExecuteQuery(this.entity, 0, +this.template.EndOfAlertRange, [owner], IID_UnitAI, true).filter(ent => this.UnitFilter(ent));
for (let unit of units)
{
let cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI);
if (cmpUnitAI.HasWorkOrders() && cmpUnitAI.ShouldRespondToEndOfAlert())
cmpUnitAI.BackToWork();
else if (cmpUnitAI.ShouldRespondToEndOfAlert())
// Stop rather than continue to try to garrison
cmpUnitAI.ReplaceOrder("Stop", { "force": true });
}
// Units that are garrisoned should ungarrison and go back to work
let holders = cmpRangeManager.ExecuteQuery(this.entity, 0, +this.template.EndOfAlertRange, mutualAllies, IID_GarrisonHolder, true);
if (Engine.QueryInterface(this.entity, IID_GarrisonHolder))
holders.push(this.entity);
for (let holder of holders)
{
if (Engine.QueryInterface(holder, IID_UnitAI))
continue;
let cmpGarrisonHolder = Engine.QueryInterface(holder, IID_GarrisonHolder);
let units = cmpGarrisonHolder.GetEntities().filter(ent => {
let cmpOwner = Engine.QueryInterface(ent, IID_Ownership);
return cmpOwner && cmpOwner.GetOwner() == owner && this.UnitFilter(ent);
});
for (let unit of units)
if (cmpGarrisonHolder.Unload(unit))
{
let cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI);
if (cmpUnitAI.HasWorkOrders())
cmpUnitAI.BackToWork();
else
// Stop rather than walk to the rally point
cmpUnitAI.ReplaceOrder("Stop", { "force": true });
}
}
};
Engine.RegisterComponentType(IID_AlertRaiser, "AlertRaiser", AlertRaiser);