0ad/binaries/data/mods/public/simulation/components/AlertRaiser.js
temple 4eb9582e63 Town bell - use matching classes, add a market alert
Replace the alert level with matching classes.
Women respond to bells at the civic center, farmstead, and storehouse.
Trade carts respond to the bell at the market.
Allow garrisoning in allied buildings.
Adapt the GUI to the new behavior.

Differential Revision: https://code.wildfiregames.com/D937
Reviewed by: causative
This was SVN commit r21343.
2018-02-24 04:35:26 +00:00

141 lines
5.1 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).filter(ent => this.UnitFilter(ent));
for (let unit of units)
{
let cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI);
let holder = cmpRangeManager.ExecuteQuery(unit, 0, +this.template.SearchRange, mutualAllies, IID_GarrisonHolder).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 && 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.GetGarrisonedEntitiesCount());
return cmpGarrisonHolder.IsAllowedToGarrison(unit) && reserved.get(ent);
});
if (holder)
{
reserved.set(holder, reserved.get(holder) - 1);
cmpUnitAI.ReplaceOrder("Garrison", { "target": holder, "force": true });
}
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).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);
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.PerformEject([unit], false))
{
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);