function AlertRaiser() {} AlertRaiser.prototype.Schema = "" + ""; AlertRaiser.prototype.Init = function() { this.level = 0; // Remember the units ordered to garrison this.garrisonedUnits = []; this.walkingUnits = []; // Remember production buildings under alert this.prodBuildings = []; }; AlertRaiser.prototype.GetLevel = function() { return this.level; }; AlertRaiser.prototype.HasRaisedAlert = function() { return this.level > 0; }; AlertRaiser.prototype.CanIncreaseLevel = function() { return this.template.MaximumLevel > this.level; }; AlertRaiser.prototype.SoundAlert = function() { var alertString = "alert" + this.level; PlaySound(alertString, this.entity); }; AlertRaiser.prototype.UpdateUnits = function(units) { var level = this.GetLevel(); for each (var unit in units) { var cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI); if (!cmpUnitAI || !cmpUnitAI.ReactsToAlert(level)) continue; cmpUnitAI.ReplaceOrder("Alert", {"raiser": this.entity, "force": true}); this.walkingUnits.push(unit); } }; AlertRaiser.prototype.IncreaseAlertLevel = function() { if (!this.CanIncreaseLevel()) return false; this.level++; this.SoundAlert(); // Find buildings/units owned by this unit's player var players = []; var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); if (cmpOwnership) players = [cmpOwnership.GetOwner()]; // Select production buildings to put "under alert", including the raiser itself if possible var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); var level = this.GetLevel(); var buildings = cmpRangeManager.ExecuteQuery(this.entity, 0, this.template.Range, players, IID_ProductionQueue); if (Engine.QueryInterface(this.entity, IID_ProductionQueue)) buildings.push(this.entity); for each (var building in buildings) { var cmpProductionQueue = Engine.QueryInterface(building, IID_ProductionQueue); cmpProductionQueue.PutUnderAlert(this.entity); this.prodBuildings.push(building); } // Select units to put under alert, according to their reaction to this level var level = this.GetLevel(); var units = cmpRangeManager.ExecuteQuery(this.entity, 0, this.template.Range, players, IID_UnitAI).filter(ent => { var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); return !cmpUnitAI.IsUnderAlert() && cmpUnitAI.ReactsToAlert(level); }); for each (var unit in units) { var cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI); cmpUnitAI.ReplaceOrder("Alert", {"raiser": this.entity, "force": true}); this.walkingUnits.push(unit); } return true; }; AlertRaiser.prototype.OnUnitGarrisonedAfterAlert = function(msg) { this.garrisonedUnits.push({"holder": msg.holder, "unit": msg.unit}); var index = this.walkingUnits.indexOf(msg.unit); if (index != -1) this.walkingUnits.splice(index, 1); }; AlertRaiser.prototype.EndOfAlert = function() { this.level = 0; this.SoundAlert(); // First, handle units not yet garrisoned for each (var unit in this.walkingUnits) { var cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI); if (!cmpUnitAI) continue; cmpUnitAI.ResetAlert(); if (cmpUnitAI.HasWorkOrders()) cmpUnitAI.BackToWork(); else cmpUnitAI.ReplaceOrder("Stop", undefined); } this.walkingUnits = []; // Then, eject garrisoned units for each (var slot in this.garrisonedUnits) { var cmpGarrisonHolder = Engine.QueryInterface(slot.holder, IID_GarrisonHolder); var cmpUnitAI = Engine.QueryInterface(slot.unit, IID_UnitAI); if (!cmpUnitAI) continue; // If the garrison building was destroyed, the unit is already ejected if (!cmpGarrisonHolder || cmpGarrisonHolder.PerformEject([slot.unit], true)) { cmpUnitAI.ResetAlert(); if (cmpUnitAI.HasWorkOrders()) cmpUnitAI.BackToWork(); } } this.garrisonedUnits = []; // Finally, reset production buildings state for each (var building in this.prodBuildings) { var cmpProductionQueue = Engine.QueryInterface(building, IID_ProductionQueue); if (cmpProductionQueue) cmpProductionQueue.ResetAlert(); } this.prodBuildings = []; return true; }; Engine.RegisterComponentType(IID_AlertRaiser, "AlertRaiser", AlertRaiser);