Line data Source code
1 0 : const g_NaturalColor = "255 255 255 255"; // pure white
2 :
3 : function StatusBars() {}
4 :
5 0 : StatusBars.prototype.Schema =
6 : "<element name='BarWidth'>" +
7 : "<data type='decimal'/>" +
8 : "</element>" +
9 : "<element name='BarHeight' a:help='Height of a normal sized bar. Some bars are scaled accordingly.'>" +
10 : "<data type='decimal'/>" +
11 : "</element>" +
12 : "<element name='HeightOffset'>" +
13 : "<data type='decimal'/>" +
14 : "</element>";
15 :
16 : /**
17 : * For every sprite, the code will call their "Add" method when regenerating
18 : * the sprites. Every sprite adder should return the height it needs.
19 : *
20 : * Modders who need extra sprites can just modify this array, and
21 : * provide the right methods.
22 : */
23 0 : StatusBars.prototype.Sprites = [
24 : "ExperienceBar",
25 : "PackBar",
26 : "UpgradeBar",
27 : "ResourceSupplyBar",
28 : "CaptureBar",
29 : "HealthBar",
30 : "AuraIcons",
31 : "RankIcon"
32 : ];
33 :
34 0 : StatusBars.prototype.Init = function()
35 : {
36 0 : this.enabled = false;
37 0 : this.showRank = false;
38 0 : this.showExperience = false;
39 :
40 : // Whether the status bars used the player colors anywhere (e.g. in the capture bar)
41 0 : this.usedPlayerColors = false;
42 :
43 0 : this.auraSources = new Map();
44 : };
45 :
46 : /**
47 : * Don't serialise this.enabled since it's modified by the GUI.
48 : */
49 0 : StatusBars.prototype.Serialize = function()
50 : {
51 0 : return { "auraSources": this.auraSources };
52 : };
53 :
54 0 : StatusBars.prototype.Deserialize = function(data)
55 : {
56 0 : this.Init();
57 0 : this.auraSources = data.auraSources;
58 : };
59 :
60 0 : StatusBars.prototype.SetEnabled = function(enabled, showRank, showExperience)
61 : {
62 : // Quick return if no change
63 0 : if (enabled == this.enabled && showRank == this.showRank && showExperience == this.showExperience)
64 0 : return;
65 :
66 0 : this.enabled = enabled;
67 0 : this.showRank = showRank;
68 0 : this.showExperience = showExperience;
69 :
70 : // Update the displayed sprites
71 0 : this.RegenerateSprites();
72 : };
73 :
74 0 : StatusBars.prototype.AddAuraSource = function(source, auraName)
75 : {
76 0 : if (this.auraSources.has(source))
77 0 : this.auraSources.get(source).push(auraName);
78 : else
79 0 : this.auraSources.set(source, [auraName]);
80 0 : this.RegenerateSprites();
81 : };
82 :
83 0 : StatusBars.prototype.RemoveAuraSource = function(source, auraName)
84 : {
85 0 : let names = this.auraSources.get(source);
86 0 : names.splice(names.indexOf(auraName), 1);
87 0 : this.RegenerateSprites();
88 : };
89 :
90 0 : StatusBars.prototype.OnHealthChanged = function(msg)
91 : {
92 0 : if (this.enabled)
93 0 : this.RegenerateSprites();
94 : };
95 :
96 0 : StatusBars.prototype.OnCapturePointsChanged = function(msg)
97 : {
98 0 : if (this.enabled)
99 0 : this.RegenerateSprites();
100 : };
101 :
102 0 : StatusBars.prototype.OnResourceSupplyChanged = function(msg)
103 : {
104 0 : if (this.enabled)
105 0 : this.RegenerateSprites();
106 : };
107 :
108 0 : StatusBars.prototype.OnPackProgressUpdate = function(msg)
109 : {
110 0 : if (this.enabled)
111 0 : this.RegenerateSprites();
112 : };
113 :
114 0 : StatusBars.prototype.OnUpgradeProgressUpdate = function(msg)
115 : {
116 0 : if (this.enabled)
117 0 : this.RegenerateSprites();
118 : };
119 :
120 0 : StatusBars.prototype.OnExperienceChanged = function()
121 : {
122 0 : if (this.enabled)
123 0 : this.RegenerateSprites();
124 : };
125 :
126 0 : StatusBars.prototype.UpdateColor = function()
127 : {
128 0 : if (this.usedPlayerColors)
129 0 : this.RegenerateSprites();
130 : };
131 :
132 0 : StatusBars.prototype.OnPlayerColorChanged = function(msg)
133 : {
134 0 : if (this.enabled)
135 0 : this.RegenerateSprites();
136 : };
137 :
138 0 : StatusBars.prototype.RegenerateSprites = function()
139 : {
140 0 : let cmpOverlayRenderer = Engine.QueryInterface(this.entity, IID_OverlayRenderer);
141 0 : cmpOverlayRenderer.Reset();
142 :
143 0 : let yoffset = 0;
144 0 : for (let sprite of this.Sprites)
145 0 : yoffset += this["Add" + sprite](cmpOverlayRenderer, yoffset);
146 : };
147 :
148 : // Internal helper functions
149 : /**
150 : * Generic piece of code to add a bar.
151 : */
152 0 : StatusBars.prototype.AddBar = function(cmpOverlayRenderer, yoffset, type, amount, heightMultiplier = 1)
153 : {
154 : // Size of health bar (in world-space units)
155 0 : let width = +this.template.BarWidth;
156 0 : let height = +this.template.BarHeight * heightMultiplier;
157 :
158 : // World-space offset from the unit's position
159 0 : let offset = { "x": 0, "y": +this.template.HeightOffset, "z": 0 };
160 :
161 : // background
162 0 : cmpOverlayRenderer.AddSprite(
163 : "art/textures/ui/session/icons/" + type + "_bg.png",
164 : { "x": -width / 2, "y": yoffset },
165 : { "x": width / 2, "y": height + yoffset },
166 : offset,
167 : g_NaturalColor
168 : );
169 :
170 : // foreground
171 0 : cmpOverlayRenderer.AddSprite(
172 : "art/textures/ui/session/icons/" + type + "_fg.png",
173 : { "x": -width / 2, "y": yoffset },
174 : { "x": width * (amount - 0.5), "y": height + yoffset },
175 : offset,
176 : g_NaturalColor
177 : );
178 :
179 0 : return height * 1.2;
180 : };
181 :
182 0 : StatusBars.prototype.AddExperienceBar = function(cmpOverlayRenderer, yoffset)
183 : {
184 0 : if (!this.enabled || !this.showExperience)
185 0 : return 0;
186 :
187 0 : let cmpPromotion = Engine.QueryInterface(this.entity, IID_Promotion);
188 0 : if (!cmpPromotion || !cmpPromotion.GetCurrentXp() || !cmpPromotion.GetRequiredXp())
189 0 : return 0;
190 :
191 0 : return this.AddBar(cmpOverlayRenderer, yoffset, "pack", cmpPromotion.GetCurrentXp() / cmpPromotion.GetRequiredXp(), 2/3);
192 : };
193 :
194 0 : StatusBars.prototype.AddPackBar = function(cmpOverlayRenderer, yoffset)
195 : {
196 0 : if (!this.enabled)
197 0 : return 0;
198 :
199 0 : let cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
200 0 : if (!cmpPack || !cmpPack.IsPacking())
201 0 : return 0;
202 :
203 0 : return this.AddBar(cmpOverlayRenderer, yoffset, "pack", cmpPack.GetProgress());
204 : };
205 :
206 0 : StatusBars.prototype.AddUpgradeBar = function(cmpOverlayRenderer, yoffset)
207 : {
208 0 : if (!this.enabled)
209 0 : return 0;
210 :
211 0 : let cmpUpgrade = Engine.QueryInterface(this.entity, IID_Upgrade);
212 0 : if (!cmpUpgrade || !cmpUpgrade.IsUpgrading())
213 0 : return 0;
214 :
215 0 : return this.AddBar(cmpOverlayRenderer, yoffset, "upgrade", cmpUpgrade.GetProgress());
216 : };
217 :
218 0 : StatusBars.prototype.AddHealthBar = function(cmpOverlayRenderer, yoffset)
219 : {
220 0 : if (!this.enabled)
221 0 : return 0;
222 :
223 0 : let cmpHealth = QueryMiragedInterface(this.entity, IID_Health);
224 0 : if (!cmpHealth || cmpHealth.GetHitpoints() <= 0)
225 0 : return 0;
226 :
227 0 : return this.AddBar(cmpOverlayRenderer, yoffset, "health", cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints());
228 : };
229 :
230 0 : StatusBars.prototype.AddResourceSupplyBar = function(cmpOverlayRenderer, yoffset)
231 : {
232 0 : if (!this.enabled)
233 0 : return 0;
234 :
235 0 : let cmpResourceSupply = QueryMiragedInterface(this.entity, IID_ResourceSupply);
236 0 : if (!cmpResourceSupply)
237 0 : return 0;
238 0 : let value = cmpResourceSupply.IsInfinite() ? 1 : cmpResourceSupply.GetCurrentAmount() / cmpResourceSupply.GetMaxAmount();
239 0 : return this.AddBar(cmpOverlayRenderer, yoffset, "supply", value);
240 : };
241 :
242 0 : StatusBars.prototype.AddCaptureBar = function(cmpOverlayRenderer, yoffset)
243 : {
244 0 : if (!this.enabled)
245 0 : return 0;
246 :
247 0 : let cmpCapturable = QueryMiragedInterface(this.entity, IID_Capturable);
248 0 : if (!cmpCapturable)
249 0 : return 0;
250 :
251 0 : let cmpOwnership = QueryMiragedInterface(this.entity, IID_Ownership);
252 0 : if (!cmpOwnership)
253 0 : return 0;
254 :
255 0 : let owner = cmpOwnership.GetOwner();
256 0 : if (owner == INVALID_PLAYER)
257 0 : return 0;
258 :
259 0 : this.usedPlayerColors = true;
260 0 : let capturePoints = cmpCapturable.GetCapturePoints();
261 :
262 : // Size of health bar (in world-space units)
263 0 : let width = +this.template.BarWidth;
264 0 : let height = +this.template.BarHeight;
265 :
266 : // World-space offset from the unit's position
267 0 : let offset = { "x": 0, "y": +this.template.HeightOffset, "z": 0 };
268 :
269 0 : let setCaptureBarPart = function(playerID, startSize)
270 : {
271 0 : let c = QueryPlayerIDInterface(playerID).GetDisplayedColor();
272 0 : let strColor = (c.r * 255) + " " + (c.g * 255) + " " + (c.b * 255) + " 255";
273 0 : let size = width * capturePoints[playerID] / cmpCapturable.GetMaxCapturePoints();
274 :
275 0 : cmpOverlayRenderer.AddSprite(
276 : "art/textures/ui/session/icons/capture_bar.png",
277 : { "x": startSize, "y": yoffset },
278 : { "x": startSize + size, "y": height + yoffset },
279 : offset,
280 : strColor
281 : );
282 :
283 0 : return size + startSize;
284 : };
285 :
286 : // First handle the owner's points, to keep those points on the left for clarity
287 0 : let size = setCaptureBarPart(owner, -width / 2);
288 0 : for (let i in capturePoints)
289 0 : if (i != owner && capturePoints[i] > 0)
290 0 : size = setCaptureBarPart(i, size);
291 :
292 0 : return height * 1.2;
293 : };
294 :
295 0 : StatusBars.prototype.AddAuraIcons = function(cmpOverlayRenderer, yoffset)
296 : {
297 0 : let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
298 0 : let sources = cmpGuiInterface.GetEntitiesWithStatusBars().filter(e => this.auraSources.has(e) && this.auraSources.get(e).length);
299 :
300 0 : if (!sources.length)
301 0 : return 0;
302 :
303 0 : let iconSet = new Set();
304 0 : for (let ent of sources)
305 : {
306 0 : let cmpAuras = Engine.QueryInterface(ent, IID_Auras);
307 0 : if (!cmpAuras) // probably the ent just died
308 0 : continue;
309 0 : for (let name of this.auraSources.get(ent))
310 0 : iconSet.add(cmpAuras.GetOverlayIcon(name));
311 : }
312 :
313 : // World-space offset from the unit's position
314 0 : let offset = { "x": 0, "y": +this.template.HeightOffset + yoffset, "z": 0 };
315 :
316 0 : let iconSize = +this.template.BarWidth / 2;
317 0 : let xoffset = -iconSize * (iconSet.size - 1) * 0.6;
318 0 : for (let icon of iconSet)
319 : {
320 0 : cmpOverlayRenderer.AddSprite(
321 : icon,
322 : { "x": xoffset - iconSize / 2, "y": yoffset },
323 : { "x": xoffset + iconSize / 2, "y": iconSize + yoffset },
324 : offset,
325 : g_NaturalColor
326 : );
327 0 : xoffset += iconSize * 1.2;
328 : }
329 :
330 0 : return iconSize + this.template.BarHeight / 2;
331 : };
332 :
333 0 : StatusBars.prototype.AddRankIcon = function(cmpOverlayRenderer, yoffset)
334 : {
335 0 : if (!this.enabled || !this.showRank)
336 0 : return 0;
337 :
338 0 : let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
339 0 : if (!cmpIdentity || !cmpIdentity.GetRank())
340 0 : return 0;
341 :
342 0 : let iconSize = +this.template.BarWidth / 2;
343 0 : cmpOverlayRenderer.AddSprite(
344 : "art/textures/ui/session/icons/ranks/" + cmpIdentity.GetRank() + ".png",
345 : { "x": -iconSize / 2, "y": yoffset },
346 : { "x": iconSize / 2, "y": iconSize + yoffset },
347 : { "x": 0, "y": +this.template.HeightOffset + 0.1, "z": 0 },
348 : g_NaturalColor);
349 :
350 0 : return iconSize + this.template.BarHeight / 2;
351 : };
352 :
353 0 : Engine.RegisterComponentType(IID_StatusBars, "StatusBars", StatusBars);
|