Line data Source code
1 : function AlertRaiser() {}
2 :
3 0 : AlertRaiser.prototype.Schema =
4 : "<element name='List' a:help='Classes of entities which are affected by this alert raiser'>" +
5 : "<attribute name='datatype'>" +
6 : "<value>tokens</value>" +
7 : "</attribute>" +
8 : "<text/>" +
9 : "</element>" +
10 : "<element name='RaiseAlertRange'><data type='integer'/></element>" +
11 : "<element name='EndOfAlertRange'><data type='integer'/></element>" +
12 : "<element name='SearchRange'><data type='integer'/></element>";
13 :
14 0 : AlertRaiser.prototype.Init = function()
15 : {
16 : // Store the last time the alert was used so players can't lag the game by raising alerts repeatedly.
17 0 : this.lastTime = 0;
18 : };
19 :
20 0 : AlertRaiser.prototype.UnitFilter = function(unit)
21 : {
22 0 : let cmpIdentity = Engine.QueryInterface(unit, IID_Identity);
23 0 : return cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), this.template.List._string);
24 : };
25 :
26 0 : AlertRaiser.prototype.RaiseAlert = function()
27 : {
28 0 : let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
29 0 : if (cmpTimer.GetTime() == this.lastTime)
30 0 : return;
31 :
32 0 : this.lastTime = cmpTimer.GetTime();
33 0 : PlaySound("alert_raise", this.entity);
34 :
35 0 : let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
36 0 : if (!cmpOwnership || cmpOwnership.GetOwner() == INVALID_PLAYER)
37 0 : return;
38 :
39 0 : let owner = cmpOwnership.GetOwner();
40 0 : let cmpPlayer = QueryOwnerInterface(this.entity);
41 0 : let mutualAllies = cmpPlayer ? cmpPlayer.GetMutualAllies() : [owner];
42 0 : let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
43 :
44 : // Store the number of available garrison spots so that units don't try to garrison in buildings that will be full
45 0 : let reserved = new Map();
46 :
47 0 : let units = cmpRangeManager.ExecuteQuery(this.entity, 0, +this.template.RaiseAlertRange, [owner], IID_UnitAI, true).filter(ent => this.UnitFilter(ent));
48 0 : for (let unit of units)
49 : {
50 0 : let cmpGarrisonable = Engine.QueryInterface(unit, IID_Garrisonable);
51 0 : if (!cmpGarrisonable)
52 0 : continue;
53 :
54 0 : let size = cmpGarrisonable.TotalSize();
55 0 : let cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI);
56 :
57 0 : let holder = cmpRangeManager.ExecuteQuery(unit, 0, +this.template.SearchRange, mutualAllies, IID_GarrisonHolder, true).find(ent => {
58 : // Ignore moving garrison holders
59 0 : if (Engine.QueryInterface(ent, IID_UnitAI))
60 0 : return false;
61 :
62 : // Ensure that the garrison holder is within range of the alert raiser
63 0 : if (+this.template.EndOfAlertRange > 0 && PositionHelper.DistanceBetweenEntities(this.entity, ent) > +this.template.EndOfAlertRange)
64 0 : return false;
65 :
66 0 : if (!cmpUnitAI.CheckTargetVisible(ent))
67 0 : return false;
68 :
69 0 : let cmpGarrisonHolder = Engine.QueryInterface(ent, IID_GarrisonHolder);
70 0 : if (!reserved.has(ent))
71 0 : reserved.set(ent, cmpGarrisonHolder.GetCapacity() - cmpGarrisonHolder.OccupiedSlots());
72 :
73 0 : return cmpGarrisonHolder.IsAllowedToGarrison(unit) && reserved.get(ent) >= size;
74 : });
75 :
76 0 : if (holder)
77 : {
78 0 : reserved.set(holder, reserved.get(holder) - size);
79 0 : cmpUnitAI.Garrison(holder, false, false);
80 : }
81 : else
82 : // If no available spots, stop moving
83 0 : cmpUnitAI.ReplaceOrder("Stop", { "force": true });
84 : }
85 : };
86 :
87 0 : AlertRaiser.prototype.EndOfAlert = function()
88 : {
89 0 : let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
90 0 : if (cmpTimer.GetTime() == this.lastTime)
91 0 : return;
92 :
93 0 : this.lastTime = cmpTimer.GetTime();
94 0 : PlaySound("alert_end", this.entity);
95 :
96 0 : let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
97 0 : if (!cmpOwnership || cmpOwnership.GetOwner() == INVALID_PLAYER)
98 0 : return;
99 :
100 0 : let owner = cmpOwnership.GetOwner();
101 0 : let cmpPlayer = QueryOwnerInterface(this.entity);
102 0 : let mutualAllies = cmpPlayer ? cmpPlayer.GetMutualAllies() : [owner];
103 0 : let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
104 :
105 : // Units that are not garrisoned should go back to work
106 0 : let units = cmpRangeManager.ExecuteQuery(this.entity, 0, +this.template.EndOfAlertRange, [owner], IID_UnitAI, true).filter(ent => this.UnitFilter(ent));
107 0 : for (let unit of units)
108 : {
109 0 : let cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI);
110 0 : if (cmpUnitAI.HasWorkOrders() && cmpUnitAI.ShouldRespondToEndOfAlert())
111 0 : cmpUnitAI.BackToWork();
112 0 : else if (cmpUnitAI.ShouldRespondToEndOfAlert())
113 : // Stop rather than continue to try to garrison
114 0 : cmpUnitAI.ReplaceOrder("Stop", { "force": true });
115 : }
116 :
117 : // Units that are garrisoned should ungarrison and go back to work
118 0 : let holders = cmpRangeManager.ExecuteQuery(this.entity, 0, +this.template.EndOfAlertRange, mutualAllies, IID_GarrisonHolder, true);
119 0 : if (Engine.QueryInterface(this.entity, IID_GarrisonHolder))
120 0 : holders.push(this.entity);
121 :
122 0 : for (let holder of holders)
123 : {
124 0 : if (Engine.QueryInterface(holder, IID_UnitAI))
125 0 : continue;
126 :
127 0 : let cmpGarrisonHolder = Engine.QueryInterface(holder, IID_GarrisonHolder);
128 0 : let units = cmpGarrisonHolder.GetEntities().filter(ent => {
129 0 : let cmpOwner = Engine.QueryInterface(ent, IID_Ownership);
130 0 : return cmpOwner && cmpOwner.GetOwner() == owner && this.UnitFilter(ent);
131 : });
132 :
133 0 : for (let unit of units)
134 0 : if (cmpGarrisonHolder.Unload(unit))
135 : {
136 0 : let cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI);
137 0 : if (cmpUnitAI.HasWorkOrders())
138 0 : cmpUnitAI.BackToWork();
139 : else
140 : // Stop rather than walk to the rally point
141 0 : cmpUnitAI.ReplaceOrder("Stop", { "force": true });
142 : }
143 : }
144 : };
145 :
146 0 : Engine.RegisterComponentType(IID_AlertRaiser, "AlertRaiser", AlertRaiser);
|