]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/hud.qc
c799ad550d2f8acce515a2b1b54f8b791eb3efbb
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / hud.qc
1 #include "scoreboard.qh"
2 #include "teamradar.qh"
3 #include "../common/buffs.qh"
4 #include "../common/counting.qh"
5 #include "../common/mapinfo.qh"
6 #include "../common/nades.qh"
7 #include "../server/t_items.qh"
8
9 /*
10 ==================
11 Misc HUD functions
12 ==================
13 */
14
15 // a border picture is a texture containing nine parts:
16 //   1/4 width: left part
17 //   1/2 width: middle part (stretched)
18 //   1/4 width: right part
19 // divided into
20 //   1/4 height: top part
21 //   1/2 height: middle part (stretched)
22 //   1/4 height: bottom part
23 void draw_BorderPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha, vector theBorderSize)
24 {
25     if (theBorderSize.x < 0 && theBorderSize.y < 0) // draw whole image as it is
26     {
27                 drawpic(theOrigin, pic, theSize, theColor, theAlpha, 0);
28                 return;
29     }
30         if (theBorderSize.x == 0 && theBorderSize.y == 0) // no border
31         {
32                 // draw only the central part
33                 drawsubpic(theOrigin, theSize, pic, '0.25 0.25 0', '0.5 0.5 0', theColor, theAlpha, 0);
34                 return;
35         }
36
37         vector dX, dY;
38         vector width, height;
39         vector bW, bH;
40         //pic = draw_UseSkinFor(pic);
41         width = eX * theSize.x;
42         height = eY * theSize.y;
43         if(theSize.x <= theBorderSize.x * 2)
44         {
45                 // not wide enough... draw just left and right then
46                 bW = eX * (0.25 * theSize.x / (theBorderSize.x * 2));
47                 if(theSize.y <= theBorderSize.y * 2)
48                 {
49                         // not high enough... draw just corners
50                         bH = eY * (0.25 * theSize.y / (theBorderSize.y * 2));
51                         drawsubpic(theOrigin,                 width * 0.5 + height * 0.5, pic, '0 0 0',           bW + bH, theColor, theAlpha, 0);
52                         drawsubpic(theOrigin + width   * 0.5, width * 0.5 + height * 0.5, pic, eX - bW,           bW + bH, theColor, theAlpha, 0);
53                         drawsubpic(theOrigin + height  * 0.5, width * 0.5 + height * 0.5, pic, eY - bH,           bW + bH, theColor, theAlpha, 0);
54                         drawsubpic(theOrigin + theSize * 0.5, width * 0.5 + height * 0.5, pic, eX + eY - bW - bH, bW + bH, theColor, theAlpha, 0);
55                 }
56                 else
57                 {
58                         dY = theBorderSize.x * eY;
59                         drawsubpic(theOrigin,                             width * 0.5          +     dY, pic, '0 0    0',           '0 0.25 0' + bW, theColor, theAlpha, 0);
60                         drawsubpic(theOrigin + width * 0.5,               width * 0.5          +     dY, pic, '0 0    0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0);
61                         drawsubpic(theOrigin                        + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0',           '0 0.5  0' + bW, theColor, theAlpha, 0);
62                         drawsubpic(theOrigin + width * 0.5          + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0' + eX - bW, '0 0.5  0' + bW, theColor, theAlpha, 0);
63                         drawsubpic(theOrigin               + height - dY, width * 0.5          +     dY, pic, '0 0.75 0',           '0 0.25 0' + bW, theColor, theAlpha, 0);
64                         drawsubpic(theOrigin + width * 0.5 + height - dY, width * 0.5          +     dY, pic, '0 0.75 0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0);
65                 }
66         }
67         else
68         {
69                 if(theSize.y <= theBorderSize.y * 2)
70                 {
71                         // not high enough... draw just top and bottom then
72                         bH = eY * (0.25 * theSize.y / (theBorderSize.y * 2));
73                         dX = theBorderSize.x * eX;
74                         drawsubpic(theOrigin,                                         dX + height * 0.5, pic, '0    0 0',           '0.25 0 0' + bH, theColor, theAlpha, 0);
75                         drawsubpic(theOrigin + dX,                        width - 2 * dX + height * 0.5, pic, '0.25 0 0',           '0.5  0 0' + bH, theColor, theAlpha, 0);
76                         drawsubpic(theOrigin + width - dX,                            dX + height * 0.5, pic, '0.75 0 0',           '0.25 0 0' + bH, theColor, theAlpha, 0);
77                         drawsubpic(theOrigin              + height * 0.5,             dX + height * 0.5, pic, '0    0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0);
78                         drawsubpic(theOrigin + dX         + height * 0.5, width - 2 * dX + height * 0.5, pic, '0.25 0 0' + eY - bH, '0.5  0 0' + bH, theColor, theAlpha, 0);
79                         drawsubpic(theOrigin + width - dX + height * 0.5,             dX + height * 0.5, pic, '0.75 0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0);
80                 }
81                 else
82                 {
83                         dX = theBorderSize.x * eX;
84                         dY = theBorderSize.x * eY;
85                         drawsubpic(theOrigin,                                        dX          +     dY, pic, '0    0    0', '0.25 0.25 0', theColor, theAlpha, 0);
86                         drawsubpic(theOrigin                  + dX,      width - 2 * dX          +     dY, pic, '0.25 0    0', '0.5  0.25 0', theColor, theAlpha, 0);
87                         drawsubpic(theOrigin          + width - dX,                  dX          +     dY, pic, '0.75 0    0', '0.25 0.25 0', theColor, theAlpha, 0);
88                         drawsubpic(theOrigin          + dY,                          dX + height - 2 * dY, pic, '0    0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
89                         drawsubpic(theOrigin          + dY         + dX, width - 2 * dX + height - 2 * dY, pic, '0.25 0.25 0', '0.5  0.5  0', theColor, theAlpha, 0);
90                         drawsubpic(theOrigin          + dY + width - dX,             dX + height - 2 * dY, pic, '0.75 0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
91                         drawsubpic(theOrigin + height - dY,                          dX          +     dY, pic, '0    0.75 0', '0.25 0.25 0', theColor, theAlpha, 0);
92                         drawsubpic(theOrigin + height - dY         + dX, width - 2 * dX          +     dY, pic, '0.25 0.75 0', '0.5  0.25 0', theColor, theAlpha, 0);
93                         drawsubpic(theOrigin + height - dY + width - dX,             dX          +     dY, pic, '0.75 0.75 0', '0.25 0.25 0', theColor, theAlpha, 0);
94                 }
95         }
96 }
97
98 vector HUD_Get_Num_Color (float x, float maxvalue)
99 {
100         float blinkingamt;
101         vector color;
102         if(x >= maxvalue) {
103                 color.x = sin(2*M_PI*time);
104                 color.y = 1;
105                 color.z = sin(2*M_PI*time);
106         }
107         else if(x > maxvalue * 0.75) {
108                 color.x = 0.4 - (x-150)*0.02 * 0.4; //red value between 0.4 -> 0
109                 color.y = 0.9 + (x-150)*0.02 * 0.1; // green value between 0.9 -> 1
110                 color.z = 0;
111         }
112         else if(x > maxvalue * 0.5) {
113                 color.x = 1 - (x-100)*0.02 * 0.6; //red value between 1 -> 0.4
114                 color.y = 1 - (x-100)*0.02 * 0.1; // green value between 1 -> 0.9
115                 color.z = 1 - (x-100)*0.02; // blue value between 1 -> 0
116         }
117         else if(x > maxvalue * 0.25) {
118                 color.x = 1;
119                 color.y = 1;
120                 color.z = 0.2 + (x-50)*0.02 * 0.8; // blue value between 0.2 -> 1
121         }
122         else if(x > maxvalue * 0.1) {
123                 color.x = 1;
124                 color.y = (x-20)*90/27/100; // green value between 0 -> 1
125                 color.z = (x-20)*90/27/100 * 0.2; // blue value between 0 -> 0.2
126         }
127         else {
128                 color.x = 1;
129                 color.y = 0;
130                 color.z = 0;
131         }
132
133         blinkingamt = (1 - x/maxvalue/0.25);
134         if(blinkingamt > 0)
135         {
136                 color.x = color.x - color.x * blinkingamt * sin(2*M_PI*time);
137                 color.y = color.y - color.y * blinkingamt * sin(2*M_PI*time);
138                 color.z = color.z - color.z * blinkingamt * sin(2*M_PI*time);
139         }
140         return color;
141 }
142
143 float HUD_GetRowCount(float item_count, vector size, float item_aspect)
144 {
145         float aspect = size_y / size_x;
146         return bound(1, floor((sqrt(4 * item_aspect * aspect * item_count + aspect * aspect) + aspect + 0.5) / 2), item_count);
147 }
148
149 float HUD_GetColumnCount(float item_count, vector size, float item_aspect)
150 {
151         float aspect = size_x / size_y;
152         return bound(1, floor((sqrt(4 * item_aspect * aspect * item_count + aspect * aspect) + aspect + 0.5) / 2), item_count);
153 }
154
155 float stringwidth_colors(string s, vector theSize)
156 {
157         return stringwidth(s, true, theSize);
158 }
159
160 float stringwidth_nocolors(string s, vector theSize)
161 {
162         return stringwidth(s, false, theSize);
163 }
164
165 void drawstringright(vector position, string text, vector theScale, vector rgb, float theAlpha, float flag)
166 {
167         position.x -= 2 / 3 * strlen(text) * theScale.x;
168         drawstring(position, text, theScale, rgb, theAlpha, flag);
169 }
170
171 void drawstringcenter(vector position, string text, vector theScale, vector rgb, float theAlpha, float flag)
172 {
173         position.x = 0.5 * (vid_conwidth - 0.6025 * strlen(text) * theScale.x);
174         drawstring(position, text, theScale, rgb, theAlpha, flag);
175 }
176
177 // return the string of the onscreen race timer
178 string MakeRaceString(float cp, float mytime, float histime, float lapdelta, string hisname)
179 {
180         string col;
181         string timestr;
182         string cpname;
183         string lapstr;
184         lapstr = "";
185
186         if(histime == 0) // goal hit
187         {
188                 if(mytime > 0)
189                 {
190                         timestr = strcat("+", ftos_decimals(+mytime, TIME_DECIMALS));
191                         col = "^1";
192                 }
193                 else if(mytime == 0)
194                 {
195                         timestr = "+0.0";
196                         col = "^3";
197                 }
198                 else
199                 {
200                         timestr = strcat("-", ftos_decimals(-mytime, TIME_DECIMALS));
201                         col = "^2";
202                 }
203
204                 if(lapdelta > 0)
205                 {
206                         lapstr = sprintf(_(" (-%dL)"), lapdelta);
207                         col = "^2";
208                 }
209                 else if(lapdelta < 0)
210                 {
211                         lapstr = sprintf(_(" (+%dL)"), -lapdelta);
212                         col = "^1";
213                 }
214         }
215         else if(histime > 0) // anticipation
216         {
217                 if(mytime >= histime)
218                         timestr = strcat("+", ftos_decimals(mytime - histime, TIME_DECIMALS));
219                 else
220                         timestr = TIME_ENCODED_TOSTRING(TIME_ENCODE(histime));
221                 col = "^3";
222         }
223         else
224         {
225                 col = "^7";
226                 timestr = "";
227         }
228
229         if(cp == 254)
230                 cpname = _("Start line");
231         else if(cp == 255)
232                 cpname = _("Finish line");
233         else if(cp)
234                 cpname = sprintf(_("Intermediate %d"), cp);
235         else
236                 cpname = _("Finish line");
237
238         if(histime < 0)
239                 return strcat(col, cpname);
240         else if(hisname == "")
241                 return strcat(col, sprintf("%s (%s)", cpname, timestr));
242         else
243                 return strcat(col, sprintf("%s (%s %s)", cpname, timestr, strcat(hisname, col, lapstr)));
244 }
245
246 // Check if the given name already exist in race rankings? In that case, where? (otherwise return 0)
247 float race_CheckName(string net_name) {
248         float i;
249         for (i=RANKINGS_CNT-1;i>=0;--i)
250                 if(grecordholder[i] == net_name)
251                         return i+1;
252         return 0;
253 }
254
255 float GetPlayerColorForce(int i)
256 {
257         if(!teamplay)
258                 return 0;
259         else
260                 return stof(getplayerkeyvalue(i, "colors")) & 15;
261 }
262
263 float GetPlayerColor(int i)
264 {
265         if(!playerslots[i].gotscores) // unconnected
266                 return NUM_SPECTATOR;
267         else if(stof(getplayerkeyvalue(i, "frags")) == FRAGS_SPECTATOR)
268                 return NUM_SPECTATOR;
269         else
270                 return GetPlayerColorForce(i);
271 }
272
273 string GetPlayerName(int i)
274 {
275         return ColorTranslateRGB(getplayerkeyvalue(i, "name"));
276 }
277
278
279 /*
280 ==================
281 HUD panels
282 ==================
283 */
284
285 // draw the background/borders
286 #define HUD_Panel_DrawBg(theAlpha) do {                                                                                                                                                         \
287         if(panel.current_panel_bg != "0" && panel.current_panel_bg != "")                                                                                               \
288                 draw_BorderPicture(panel_pos - '1 1 0' * panel_bg_border, panel.current_panel_bg, panel_size + '1 1 0' * 2 * panel_bg_border, panel_bg_color, panel_bg_alpha * theAlpha, '1 1 0' * (panel_bg_border/BORDER_MULTIPLIER));\
289 } while(0)
290
291 //basically the same code of draw_ButtonPicture and draw_VertButtonPicture for the menu
292 void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, float vertical, float baralign, vector theColor, float theAlpha, float drawflag)
293 {
294         if(!length_ratio || !theAlpha)
295                 return;
296         if(length_ratio > 1)
297                 length_ratio = 1;
298         if (baralign == 3)
299         {
300                 if(length_ratio < -1)
301                         length_ratio = -1;
302         }
303         else if(length_ratio < 0)
304                 return;
305
306         vector square;
307         vector width, height;
308         if(vertical) {
309                 pic = strcat(hud_skin_path, "/", pic, "_vertical");
310                 if(precache_pic(pic) == "") {
311                         pic = "gfx/hud/default/progressbar_vertical";
312                 }
313
314         if (baralign == 1) // bottom align
315                         theOrigin.y += (1 - length_ratio) * theSize.y;
316         else if (baralign == 2) // center align
317             theOrigin.y += 0.5 * (1 - length_ratio) * theSize.y;
318         else if (baralign == 3) // center align, positive values down, negative up
319                 {
320                         theSize.y *= 0.5;
321                         if (length_ratio > 0)
322                                 theOrigin.y += theSize.y;
323                         else
324                         {
325                                 theOrigin.y += (1 + length_ratio) * theSize.y;
326                                 length_ratio = -length_ratio;
327                         }
328                 }
329                 theSize.y *= length_ratio;
330
331                 vector bH;
332                 width = eX * theSize.x;
333                 height = eY * theSize.y;
334                 if(theSize.y <= theSize.x * 2)
335                 {
336                         // button not high enough
337                         // draw just upper and lower part then
338                         square = eY * theSize.y * 0.5;
339                         bH = eY * (0.25 * theSize.y / (theSize.x * 2));
340                         drawsubpic(theOrigin,          square + width, pic, '0 0 0', eX + bH, theColor, theAlpha, drawflag);
341                         drawsubpic(theOrigin + square, square + width, pic, eY - bH, eX + bH, theColor, theAlpha, drawflag);
342                 }
343                 else
344                 {
345                         square = eY * theSize.x;
346                         drawsubpic(theOrigin,                   width   +     square, pic, '0 0    0', '1 0.25 0', theColor, theAlpha, drawflag);
347                         drawsubpic(theOrigin +          square, theSize - 2 * square, pic, '0 0.25 0', '1 0.5  0', theColor, theAlpha, drawflag);
348                         drawsubpic(theOrigin + height - square, width   +     square, pic, '0 0.75 0', '1 0.25 0', theColor, theAlpha, drawflag);
349                 }
350         } else {
351                 pic = strcat(hud_skin_path, "/", pic);
352                 if(precache_pic(pic) == "") {
353                         pic = "gfx/hud/default/progressbar";
354                 }
355
356                 if (baralign == 1) // right align
357                         theOrigin.x += (1 - length_ratio) * theSize.x;
358         else if (baralign == 2) // center align
359             theOrigin.x += 0.5 * (1 - length_ratio) * theSize.x;
360         else if (baralign == 3) // center align, positive values on the right, negative on the left
361                 {
362                         theSize.x *= 0.5;
363                         if (length_ratio > 0)
364                                 theOrigin.x += theSize.x;
365                         else
366                         {
367                                 theOrigin.x += (1 + length_ratio) * theSize.x;
368                                 length_ratio = -length_ratio;
369                         }
370                 }
371                 theSize.x *= length_ratio;
372
373                 vector bW;
374                 width = eX * theSize.x;
375                 height = eY * theSize.y;
376                 if(theSize.x <= theSize.y * 2)
377                 {
378                         // button not wide enough
379                         // draw just left and right part then
380                         square = eX * theSize.x * 0.5;
381                         bW = eX * (0.25 * theSize.x / (theSize.y * 2));
382                         drawsubpic(theOrigin,          square + height, pic, '0 0 0', eY + bW, theColor, theAlpha, drawflag);
383                         drawsubpic(theOrigin + square, square + height, pic, eX - bW, eY + bW, theColor, theAlpha, drawflag);
384                 }
385                 else
386                 {
387                         square = eX * theSize.y;
388                         drawsubpic(theOrigin,                  height  +     square, pic, '0    0 0', '0.25 1 0', theColor, theAlpha, drawflag);
389                         drawsubpic(theOrigin +         square, theSize - 2 * square, pic, '0.25 0 0', '0.5  1 0', theColor, theAlpha, drawflag);
390                         drawsubpic(theOrigin + width - square, height  +     square, pic, '0.75 0 0', '0.25 1 0', theColor, theAlpha, drawflag);
391                 }
392         }
393 }
394
395 void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theAlpha, float drawflag)
396 {
397         if(!theAlpha)
398                 return;
399
400         string pic;
401         pic = strcat(hud_skin_path, "/num_leading");
402         if(precache_pic(pic) == "") {
403                 pic = "gfx/hud/default/num_leading";
404         }
405
406         drawsubpic(pos, eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0 0 0', '0.25 1 0', color, theAlpha, drawflag);
407         if(mySize.x/mySize.y > 2)
408                 drawsubpic(pos + eX * mySize.y, eX * (mySize.x - 2 * mySize.y) + eY * mySize.y, pic, '0.25 0 0', '0.5 1 0', color, theAlpha, drawflag);
409         drawsubpic(pos + eX * mySize.x - eX * min(mySize.x * 0.5, mySize.y), eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0.75 0 0', '0.25 1 0', color, theAlpha, drawflag);
410 }
411
412 // Weapon icons (#0)
413 //
414 entity weaponorder[WEP_MAXCOUNT];
415 void weaponorder_swap(int i, int j, entity pass)
416 {
417         entity h = weaponorder[i];
418         weaponorder[i] = weaponorder[j];
419         weaponorder[j] = h;
420 }
421
422 string weaponorder_cmp_str;
423 float weaponorder_cmp(float i, float j, entity pass)
424 {
425         float ai, aj;
426         ai = strstrofs(weaponorder_cmp_str, sprintf(" %d ", weaponorder[i].weapon), 0);
427         aj = strstrofs(weaponorder_cmp_str, sprintf(" %d ", weaponorder[j].weapon), 0);
428         return aj - ai; // the string is in REVERSE order (higher prio at the right is what we want, but higher prio first is the string)
429 }
430
431 void HUD_Weapons(void)
432 {
433         // declarations
434         WepSet weapons_stat = WepSet_GetFromStat();
435         float i, f, a;
436         float screen_ar;
437         vector center = '0 0 0';
438         float weapon_count, weapon_id;
439         float row, column, rows = 0, columns = 0;
440         bool vertical_order = true;
441         float aspect = autocvar_hud_panel_weapons_aspect;
442
443         float timeout = autocvar_hud_panel_weapons_timeout;
444         float timein_effect_length = autocvar_hud_panel_weapons_timeout_speed_in; //? 0.375 : 0);
445         float timeout_effect_length = autocvar_hud_panel_weapons_timeout_speed_out; //? 0.75 : 0);
446
447         vector barsize = '0 0 0', baroffset = '0 0 0';
448         vector ammo_color = '1 0 1';
449         float ammo_alpha = 1;
450
451         float when = max(1, autocvar_hud_panel_weapons_complainbubble_time);
452         float fadetime = max(0, autocvar_hud_panel_weapons_complainbubble_fadetime);
453
454         vector weapon_pos, weapon_size = '0 0 0';
455         vector color;
456
457         // check to see if we want to continue
458         if(hud != HUD_NORMAL) { return; }
459
460         if(!autocvar__hud_configure)
461         {
462                 if((!autocvar_hud_panel_weapons) || (spectatee_status == -1))
463                         return;
464                 if(timeout && time >= weapontime + timeout + timeout_effect_length)
465                 if(autocvar_hud_panel_weapons_timeout_effect == 3 || (autocvar_hud_panel_weapons_timeout_effect == 1 && !(autocvar_hud_panel_weapons_timeout_fadebgmin + autocvar_hud_panel_weapons_timeout_fadefgmin)))
466                 {
467                         weaponprevtime = time;
468                         return;
469                 }
470         }
471
472         // update generic hud functions
473         HUD_Panel_UpdateCvars();
474
475         draw_beginBoldFont();
476
477         // figure out weapon order (how the weapons are sorted) // TODO make this configurable
478         if(weaponorder_bypriority != autocvar_cl_weaponpriority || !weaponorder[0])
479         {
480                 int weapon_cnt;
481                 if(weaponorder_bypriority)
482                         strunzone(weaponorder_bypriority);
483                 if(weaponorder_byimpulse)
484                         strunzone(weaponorder_byimpulse);
485
486                 weaponorder_bypriority = strzone(autocvar_cl_weaponpriority);
487                 weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(W_FixWeaponOrder_ForceComplete(W_NumberWeaponOrder(weaponorder_bypriority))));
488                 weaponorder_cmp_str = strcat(" ", weaponorder_byimpulse, " ");
489
490                 weapon_cnt = 0;
491                 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
492                 {
493                         self = get_weaponinfo(i);
494                         if(self.impulse >= 0)
495                         {
496                                 weaponorder[weapon_cnt] = self;
497                                 ++weapon_cnt;
498                         }
499                 }
500                 for(i = weapon_cnt; i < WEP_MAXCOUNT; ++i)
501                         weaponorder[i] = world;
502                 heapsort(weapon_cnt, weaponorder_swap, weaponorder_cmp, world);
503
504                 weaponorder_cmp_str = string_null;
505         }
506
507         if(!autocvar_hud_panel_weapons_complainbubble || autocvar__hud_configure || time - complain_weapon_time >= when + fadetime)
508                 complain_weapon = 0;
509
510         if(autocvar__hud_configure)
511         {
512                 if(!weapons_stat)
513                         for(i = WEP_FIRST; i <= WEP_LAST; i += floor((WEP_LAST-WEP_FIRST)/5))
514                                 weapons_stat |= WepSet_FromWeapon(i);
515         }
516
517         // determine which weapons are going to be shown
518         if (autocvar_hud_panel_weapons_onlyowned)
519         {
520                 if(autocvar__hud_configure)
521                 {
522                         if(menu_enabled != 2)
523                                 HUD_Panel_DrawBg(1); // also draw the bg of the entire panel
524                 }
525
526                 // do we own this weapon?
527                 weapon_count = 0;
528                 for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i)
529                         if((weapons_stat & WepSet_FromWeapon(weaponorder[i].weapon)) || (weaponorder[i].weapon == complain_weapon))
530                                 ++weapon_count;
531
532                 // might as well commit suicide now, no reason to live ;)
533                 if (weapon_count == 0)
534                 {
535                         draw_endBoldFont();
536                         return;
537                 }
538
539                 vector old_panel_size = panel_size;
540                 vector padded_panel_size = panel_size - '2 2 0' * panel_bg_padding;
541
542                 // get the all-weapons layout
543                 if(padded_panel_size.x / padded_panel_size.y < aspect)
544                 {
545                         columns = HUD_GetColumnCount(WEP_COUNT, padded_panel_size, aspect);
546                         rows = ceil(WEP_COUNT/columns);
547                 }
548                 else
549                 {
550                         rows = HUD_GetRowCount(WEP_COUNT, padded_panel_size, aspect);
551                         columns = ceil(WEP_COUNT/rows);
552                 }
553                 weapon_size.x = padded_panel_size.x / columns;
554                 weapon_size.y = padded_panel_size.y / rows;
555
556                 // reduce table trying to keep the original table proportions as much as possible
557                 vertical_order = (columns >= rows);
558                 if(vertical_order)
559                 {
560                         rows = ceil(sqrt(weapon_count / (columns / rows)));
561                         columns = ceil(weapon_count / rows);
562                 }
563                 else
564                 {
565                         columns = ceil(sqrt(weapon_count / (rows / columns)));
566                         rows = ceil(weapon_count / columns);
567                 }
568
569                 // NOTE: although weapons should aways look the same even if onlyowned is enabled,
570                 // we enlarge them a bit when possible to better match the desired aspect ratio
571                 if(padded_panel_size.y > padded_panel_size.x)
572                 {
573                         weapon_size.y = min(padded_panel_size.y / rows, weapon_size.x / aspect);
574                         weapon_size.x = min(padded_panel_size.x / columns, aspect * weapon_size.y);
575                 }
576                 else
577                 {
578                         weapon_size.x = min(padded_panel_size.x / columns, aspect * weapon_size.y);
579                         weapon_size.y = min(padded_panel_size.y / rows, weapon_size.x / aspect);
580                 }
581
582                 // reduce size of the panel
583                 panel_size.x = columns * weapon_size.x;
584                 panel_size.y = rows * weapon_size.y;
585                 panel_size += '2 2 0' * panel_bg_padding;
586
587                 // center the resized panel, or snap it to the screen edge when close enough
588                 if(panel_pos.x > vid_conwidth * 0.001)
589                         if(panel_pos.x + old_panel_size.x > vid_conwidth * 0.999)
590                                 panel_pos.x += old_panel_size.x - panel_size.x;
591                         else
592                                 panel_pos.x += (old_panel_size.x - panel_size.x) / 2;
593
594                 if(panel_pos.y > vid_conheight * 0.001)
595                         if(panel_pos.y + old_panel_size.y > vid_conheight * 0.999)
596                                 panel_pos.y += old_panel_size.y - panel_size.y;
597                         else
598                                 panel_pos.y += (old_panel_size.y - panel_size.y) / 2;
599         }
600         else
601                 weapon_count = WEP_COUNT;
602
603         // animation for fading in/out the panel respectively when not in use
604         if(!autocvar__hud_configure)
605         {
606                 if (timeout && time >= weapontime + timeout) // apply timeout effect if needed
607                 {
608                         f = bound(0, (time - (weapontime + timeout)) / timeout_effect_length, 1);
609
610                         // fade the panel alpha
611                         if(autocvar_hud_panel_weapons_timeout_effect == 1)
612                         {
613                                 panel_bg_alpha *= (autocvar_hud_panel_weapons_timeout_fadebgmin * f + (1 - f));
614                                 panel_fg_alpha *= (autocvar_hud_panel_weapons_timeout_fadefgmin * f + (1 - f));
615                         }
616                         else if(autocvar_hud_panel_weapons_timeout_effect == 3)
617                         {
618                                 panel_bg_alpha *= (1 - f);
619                                 panel_fg_alpha *= (1 - f);
620                         }
621
622                         // move the panel off the screen
623                         if (autocvar_hud_panel_weapons_timeout_effect == 2 || autocvar_hud_panel_weapons_timeout_effect == 3)
624                         {
625                                 f *= f; // for a cooler movement
626                                 center.x = panel_pos.x + panel_size.x/2;
627                                 center.y = panel_pos.y + panel_size.y/2;
628                                 screen_ar = vid_conwidth/vid_conheight;
629                                 if (center.x/center.y < screen_ar) //bottom left
630                                 {
631                                         if ((vid_conwidth - center.x)/center.y < screen_ar) //bottom
632                                                 panel_pos.y += f * (vid_conheight - panel_pos.y);
633                                         else //left
634                                                 panel_pos.x -= f * (panel_pos.x + panel_size.x);
635                                 }
636                                 else //top right
637                                 {
638                                         if ((vid_conwidth - center.x)/center.y < screen_ar) //right
639                                                 panel_pos.x += f * (vid_conwidth - panel_pos.x);
640                                         else //top
641                                                 panel_pos.y -= f * (panel_pos.y + panel_size.y);
642                                 }
643                                 if(f == 1)
644                                         center.x = -1; // mark the panel as off screen
645                         }
646                         weaponprevtime = time - (1 - f) * timein_effect_length;
647                 }
648                 else if (timeout && time < weaponprevtime + timein_effect_length) // apply timein effect if needed
649                 {
650                         f = bound(0, (time - weaponprevtime) / timein_effect_length, 1);
651
652                         // fade the panel alpha
653                         if(autocvar_hud_panel_weapons_timeout_effect == 1)
654                         {
655                                 panel_bg_alpha *= (autocvar_hud_panel_weapons_timeout_fadebgmin * (1 - f) + f);
656                                 panel_fg_alpha *= (autocvar_hud_panel_weapons_timeout_fadefgmin * (1 - f) + f);
657                         }
658                         else if(autocvar_hud_panel_weapons_timeout_effect == 3)
659                         {
660                                 panel_bg_alpha *= (f);
661                                 panel_fg_alpha *= (f);
662                         }
663
664                         // move the panel back on screen
665                         if (autocvar_hud_panel_weapons_timeout_effect == 2 || autocvar_hud_panel_weapons_timeout_effect == 3)
666                         {
667                                 f *= f; // for a cooler movement
668                                 f = 1 - f;
669                                 center.x = panel_pos.x + panel_size.x/2;
670                                 center.y = panel_pos.y + panel_size.y/2;
671                                 screen_ar = vid_conwidth/vid_conheight;
672                                 if (center.x/center.y < screen_ar) //bottom left
673                                 {
674                                         if ((vid_conwidth - center.x)/center.y < screen_ar) //bottom
675                                                 panel_pos.y += f * (vid_conheight - panel_pos.y);
676                                         else //left
677                                                 panel_pos.x -= f * (panel_pos.x + panel_size.x);
678                                 }
679                                 else //top right
680                                 {
681                                         if ((vid_conwidth - center.x)/center.y < screen_ar) //right
682                                                 panel_pos.x += f * (vid_conwidth - panel_pos.x);
683                                         else //top
684                                                 panel_pos.y -= f * (panel_pos.y + panel_size.y);
685                                 }
686                         }
687                 }
688         }
689
690         // draw the background, then change the virtual size of it to better fit other items inside
691         HUD_Panel_DrawBg(1);
692
693         if(center.x == -1)
694         {
695                 draw_endBoldFont();
696                 return;
697         }
698
699         if(panel_bg_padding)
700         {
701                 panel_pos += '1 1 0' * panel_bg_padding;
702                 panel_size -= '2 2 0' * panel_bg_padding;
703         }
704
705         // after the sizing and animations are done, update the other values
706
707         if(!rows) // if rows is > 0 onlyowned code has already updated these vars
708         {
709                 if(panel_size.x / panel_size.y < aspect)
710                 {
711                         columns = HUD_GetColumnCount(WEP_COUNT, panel_size, aspect);
712                         rows = ceil(WEP_COUNT/columns);
713                 }
714                 else
715                 {
716                         rows = HUD_GetRowCount(WEP_COUNT, panel_size, aspect);
717                         columns = ceil(WEP_COUNT/rows);
718                 }
719                 weapon_size = eX * panel_size.x*(1/columns) + eY * panel_size.y*(1/rows);
720                 vertical_order = (columns >= rows);
721         }
722
723         // calculate position/size for visual bar displaying ammount of ammo status
724         if (autocvar_hud_panel_weapons_ammo)
725         {
726                 ammo_color = stov(autocvar_hud_panel_weapons_ammo_color);
727                 ammo_alpha = panel_fg_alpha * autocvar_hud_panel_weapons_ammo_alpha;
728
729                 if(weapon_size.x/weapon_size.y > aspect)
730                 {
731                         barsize.x = aspect * weapon_size.y;
732                         barsize.y = weapon_size.y;
733                         baroffset.x = (weapon_size.x - barsize.x) / 2;
734                 }
735                 else
736                 {
737                         barsize.y = 1/aspect * weapon_size.x;
738                         barsize.x = weapon_size.x;
739                         baroffset.y = (weapon_size.y - barsize.y) / 2;
740                 }
741         }
742         if(autocvar_hud_panel_weapons_accuracy)
743                 Accuracy_LoadColors();
744
745         // draw items
746         row = column = 0;
747         vector label_size = '1 1 0' * min(weapon_size.x, weapon_size.y) * bound(0, autocvar_hud_panel_weapons_label_scale, 1);
748         for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i)
749         {
750                 // retrieve information about the current weapon to be drawn
751                 self = weaponorder[i];
752                 weapon_id = self.impulse;
753
754                 // skip if this weapon doesn't exist
755                 if(!self || weapon_id < 0) { continue; }
756
757                 // skip this weapon if we don't own it (and onlyowned is enabled)-- or if weapons_complainbubble is showing for this weapon
758                 if(autocvar_hud_panel_weapons_onlyowned)
759                 if (!((weapons_stat & WepSet_FromWeapon(self.weapon)) || (self.weapon == complain_weapon)))
760                         continue;
761
762                 // figure out the drawing position of weapon
763                 weapon_pos = (panel_pos
764                         + eX * column * weapon_size.x
765                         + eY * row * weapon_size.y);
766
767                 // draw background behind currently selected weapon
768                 if(self.weapon == switchweapon)
769                         drawpic_aspect_skin(weapon_pos, "weapon_current_bg", weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
770
771                 // draw the weapon accuracy
772                 if(autocvar_hud_panel_weapons_accuracy)
773                 {
774                         float panel_weapon_accuracy = weapon_accuracy[self.weapon-WEP_FIRST];
775                         if(panel_weapon_accuracy >= 0)
776                         {
777                                 color = Accuracy_GetColor(panel_weapon_accuracy);
778                                 drawpic_aspect_skin(weapon_pos, "weapon_accuracy", weapon_size, color, panel_fg_alpha, DRAWFLAG_NORMAL);
779                         }
780                 }
781
782                 // drawing all the weapon items
783                 if(weapons_stat & WepSet_FromWeapon(self.weapon))
784                 {
785                         // draw the weapon image
786                         drawpic_aspect_skin(weapon_pos, self.model2, weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
787
788                         // draw weapon label string
789                         switch(autocvar_hud_panel_weapons_label)
790                         {
791                                 case 1: // weapon number
792                                         drawstring(weapon_pos, ftos(weapon_id), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
793                                         break;
794
795                                 case 2: // bind
796                                         drawstring(weapon_pos, getcommandkey(ftos(weapon_id), strcat("weapon_group_", ftos(weapon_id))), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
797                                         break;
798
799                                 case 3: // weapon name
800                                         drawstring(weapon_pos, strtolower(self.message), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
801                                         break;
802
803                                 default: // nothing
804                                         break;
805                         }
806
807                         // draw ammo status bar
808                         if(autocvar_hud_panel_weapons_ammo && (self.ammo_field != ammo_none))
809                         {
810                                 float ammo_full;
811                                 a = getstati(GetAmmoStat(self.ammo_field)); // how much ammo do we have?
812
813                                 if(a > 0)
814                                 {
815                                         switch(self.ammo_field)
816                                         {
817                                                 case ammo_shells:  ammo_full = autocvar_hud_panel_weapons_ammo_full_shells;  break;
818                                                 case ammo_nails:   ammo_full = autocvar_hud_panel_weapons_ammo_full_nails;   break;
819                                                 case ammo_rockets: ammo_full = autocvar_hud_panel_weapons_ammo_full_rockets; break;
820                                                 case ammo_cells:   ammo_full = autocvar_hud_panel_weapons_ammo_full_cells;   break;
821                                                 case ammo_plasma:  ammo_full = autocvar_hud_panel_weapons_ammo_full_plasma;  break;
822                                                 case ammo_fuel:    ammo_full = autocvar_hud_panel_weapons_ammo_full_fuel;    break;
823                                                 default: ammo_full = 60;
824                                         }
825
826                                         drawsetcliparea(
827                                                 weapon_pos.x + baroffset.x,
828                                                 weapon_pos.y + baroffset.y,
829                                                 barsize.x * bound(0, a/ammo_full, 1),
830                                                 barsize.y
831                                         );
832
833                                         drawpic_aspect_skin(
834                                                 weapon_pos,
835                                                 "weapon_ammo",
836                                                 weapon_size,
837                                                 ammo_color,
838                                                 ammo_alpha,
839                                                 DRAWFLAG_NORMAL
840                                         );
841
842                                         drawresetcliparea();
843                                 }
844                         }
845                 }
846                 else // draw a "ghost weapon icon" if you don't have the weapon
847                 {
848                         drawpic_aspect_skin(weapon_pos, self.model2, weapon_size, '0 0 0', panel_fg_alpha * 0.5, DRAWFLAG_NORMAL);
849                 }
850
851                 // draw the complain message
852                 if(self.weapon == complain_weapon)
853                 {
854                         if(fadetime)
855                                 a = ((complain_weapon_time + when > time) ? 1 : bound(0, (complain_weapon_time + when + fadetime - time) / fadetime, 1));
856                         else
857                                 a = ((complain_weapon_time + when > time) ? 1 : 0);
858
859                         string s;
860                         if(complain_weapon_type == 0) {
861                                 s = _("Out of ammo");
862                                 color = stov(autocvar_hud_panel_weapons_complainbubble_color_outofammo);
863                         }
864                         else if(complain_weapon_type == 1) {
865                                 s = _("Don't have");
866                                 color = stov(autocvar_hud_panel_weapons_complainbubble_color_donthave);
867                         }
868                         else {
869                                 s = _("Unavailable");
870                                 color = stov(autocvar_hud_panel_weapons_complainbubble_color_unavailable);
871                         }
872                         float padding = autocvar_hud_panel_weapons_complainbubble_padding;
873                         drawpic_aspect_skin(weapon_pos + '1 1 0' * padding, "weapon_complainbubble", weapon_size - '2 2 0' * padding, color, a * panel_fg_alpha, DRAWFLAG_NORMAL);
874                         drawstring_aspect(weapon_pos + '1 1 0' * padding, s, weapon_size - '2 2 0' * padding, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
875                 }
876
877                 /// debug
878                 //drawfill(weapon_pos + '1 1 0', weapon_size - '2 2 0', '1 1 1', panel_fg_alpha * 0.2, DRAWFLAG_NORMAL);
879                 //drawstring(weapon_pos, ftos(i + 1), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
880
881                 // continue with new position for the next weapon
882                 if(vertical_order)
883                 {
884                         ++column;
885                         if(column >= columns)
886                         {
887                                 column = 0;
888                                 ++row;
889                         }
890                 }
891                 else
892                 {
893                         ++row;
894                         if(row >= rows)
895                         {
896                                 row = 0;
897                                 ++column;
898                         }
899                 }
900         }
901
902         draw_endBoldFont();
903 }
904
905 // Ammo (#1)
906 void DrawNadeScoreBar(vector myPos, vector mySize, vector color)
907 {
908
909         HUD_Panel_DrawProgressBar(
910                 myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x,
911                 mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x,
912                 autocvar_hud_panel_ammo_progressbar_name,
913                 getstatf(STAT_NADE_BONUS_SCORE), 0, 0, color,
914                 autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
915
916 }
917
918 void DrawAmmoNades(vector myPos, vector mySize, float draw_expanding, float expand_time)
919 {
920         float theAlpha = 1, a, b;
921         vector nade_color, picpos, numpos;
922
923         nade_color = Nade_Color(getstati(STAT_NADE_BONUS_TYPE));
924
925         a = getstatf(STAT_NADE_BONUS);
926         b = getstatf(STAT_NADE_BONUS_SCORE);
927
928         if(autocvar_hud_panel_ammo_iconalign)
929         {
930                 numpos = myPos;
931                 picpos = myPos + eX * 2 * mySize.y;
932         }
933         else
934         {
935                 numpos = myPos + eX * mySize.y;
936                 picpos = myPos;
937         }
938
939         DrawNadeScoreBar(myPos, mySize, nade_color);
940
941         if(b > 0 || a > 0)
942         {
943                 if(autocvar_hud_panel_ammo_text)
944                         drawstring_aspect(numpos, ftos(a), eX * (2/3) * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
945
946                 if(draw_expanding)
947                         drawpic_aspect_skin_expanding(picpos, "nade_nbg", '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, expand_time);
948
949                 drawpic_aspect_skin(picpos, "nade_bg" , '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
950                 drawpic_aspect_skin(picpos, "nade_nbg" , '1 1 0' * mySize.y, nade_color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
951         }
952 }
953
954 void DrawAmmoItem(vector myPos, vector mySize, .float ammoType, float isCurrent, float isInfinite)
955 {
956         if(ammoType == ammo_none)
957                 return;
958
959         // Initialize variables
960
961         float ammo;
962         if(autocvar__hud_configure)
963         {
964                 isCurrent = (ammoType == ammo_rockets); // Rockets always current
965                 ammo = 60;
966         }
967         else
968                 ammo = getstati(GetAmmoStat(ammoType));
969
970         if(!isCurrent)
971         {
972                 float scale = bound(0, autocvar_hud_panel_ammo_noncurrent_scale, 1);
973                 myPos = myPos + (mySize - mySize * scale) * 0.5;
974                 mySize = mySize * scale;
975         }
976
977         vector iconPos, textPos;
978         if(autocvar_hud_panel_ammo_iconalign)
979         {
980                 iconPos = myPos + eX * 2 * mySize.y;
981                 textPos = myPos;
982         }
983         else
984         {
985                 iconPos = myPos;
986                 textPos = myPos + eX * mySize.y;
987         }
988
989         float isShadowed = (ammo <= 0 && !isCurrent && !isInfinite);
990
991         vector iconColor = isShadowed ? '0 0 0' : '1 1 1';
992         vector textColor;
993         if(isInfinite)
994                 textColor = '0.2 0.95 0';
995         else if(isShadowed)
996                 textColor = '0 0 0';
997         else if(ammo < 10)
998                 textColor = '0.8 0.04 0';
999         else
1000                 textColor = '1 1 1';
1001
1002         float alpha;
1003         if(isCurrent)
1004                 alpha = panel_fg_alpha;
1005         else if(isShadowed)
1006                 alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_ammo_noncurrent_alpha, 1) * 0.5;
1007         else
1008                 alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_ammo_noncurrent_alpha, 1);
1009
1010         string text = isInfinite ? "\xE2\x88\x9E" : ftos(ammo); // Use infinity symbol (U+221E)
1011
1012         // Draw item
1013
1014         if(isCurrent)
1015                 drawpic_aspect_skin(myPos, "ammo_current_bg", mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1016
1017         if(ammo > 0 && autocvar_hud_panel_ammo_progressbar)
1018                 HUD_Panel_DrawProgressBar(myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, autocvar_hud_panel_ammo_progressbar_name, ammo/autocvar_hud_panel_ammo_maxammo, 0, 0, textColor, autocvar_hud_progressbar_alpha * alpha, DRAWFLAG_NORMAL);
1019
1020         if(autocvar_hud_panel_ammo_text)
1021                 drawstring_aspect(textPos, text, eX * (2/3) * mySize.x + eY * mySize.y, textColor, alpha, DRAWFLAG_NORMAL);
1022
1023         drawpic_aspect_skin(iconPos, GetAmmoPicture(ammoType), '1 1 0' * mySize.y, iconColor, alpha, DRAWFLAG_NORMAL);
1024 }
1025
1026 float nade_prevstatus;
1027 float nade_prevframe;
1028 float nade_statuschange_time;
1029 void HUD_Ammo(void)
1030 {
1031         if(hud != HUD_NORMAL) return;
1032         if(!autocvar__hud_configure)
1033         {
1034                 if(!autocvar_hud_panel_ammo) return;
1035                 if(spectatee_status == -1) return;
1036         }
1037
1038         HUD_Panel_UpdateCvars();
1039
1040         draw_beginBoldFont();
1041
1042         vector pos, mySize;
1043         pos = panel_pos;
1044         mySize = panel_size;
1045
1046         HUD_Panel_DrawBg(1);
1047         if(panel_bg_padding)
1048         {
1049                 pos += '1 1 0' * panel_bg_padding;
1050                 mySize -= '2 2 0' * panel_bg_padding;
1051         }
1052
1053         float rows = 0, columns, row, column;
1054         float nade_cnt = getstatf(STAT_NADE_BONUS), nade_score = getstatf(STAT_NADE_BONUS_SCORE);
1055         float draw_nades = (nade_cnt > 0 || nade_score > 0), nade_statuschange_elapsedtime;
1056         float total_ammo_count;
1057
1058         vector ammo_size;
1059         if (autocvar_hud_panel_ammo_onlycurrent)
1060                 total_ammo_count = 1;
1061         else
1062                 total_ammo_count = AMMO_COUNT;
1063
1064         if(draw_nades)
1065         {
1066                 ++total_ammo_count;
1067                 if (nade_cnt != nade_prevframe)
1068                 {
1069                         nade_statuschange_time = time;
1070                         nade_prevstatus = nade_prevframe;
1071                         nade_prevframe = nade_cnt;
1072                 }
1073         }
1074         else
1075                 nade_prevstatus = nade_prevframe = nade_statuschange_time = 0;
1076
1077         rows = HUD_GetRowCount(total_ammo_count, mySize, 3);
1078         columns = ceil((total_ammo_count)/rows);
1079         ammo_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
1080
1081         vector offset = '0 0 0'; // fteqcc sucks
1082         float newSize;
1083         if(ammo_size.x/ammo_size.y > 3)
1084         {
1085                 newSize = 3 * ammo_size.y;
1086                 offset.x = ammo_size.x - newSize;
1087                 pos.x += offset.x/2;
1088                 ammo_size.x = newSize;
1089         }
1090         else
1091         {
1092                 newSize = 1/3 * ammo_size.x;
1093                 offset.y = ammo_size.y - newSize;
1094                 pos.y += offset.y/2;
1095                 ammo_size.y = newSize;
1096         }
1097
1098         float i;
1099         float infinite_ammo = (getstati(STAT_ITEMS, 0, 24) & IT_UNLIMITED_WEAPON_AMMO);
1100         row = column = 0;
1101         if(autocvar_hud_panel_ammo_onlycurrent)
1102         {
1103                 if(autocvar__hud_configure)
1104                 {
1105                         DrawAmmoItem(pos, ammo_size, ammo_rockets, true, false);
1106                 }
1107                 else
1108                 {
1109                         DrawAmmoItem(
1110                                 pos,
1111                                 ammo_size,
1112                                 (get_weaponinfo(switchweapon)).ammo_field,
1113                                 true,
1114                                 infinite_ammo
1115                         );
1116                 }
1117
1118                 ++row;
1119                 if(row >= rows)
1120                 {
1121                         row = 0;
1122                         column = column + 1;
1123                 }
1124         }
1125         else
1126         {
1127                 .float ammotype;
1128                 row = column = 0;
1129                 for(i = 0; i < AMMO_COUNT; ++i)
1130                 {
1131                         ammotype = GetAmmoFieldFromNum(i);
1132                         DrawAmmoItem(
1133                                 pos + eX * column * (ammo_size.x + offset.x) + eY * row * (ammo_size.y + offset.y),
1134                                 ammo_size,
1135                                 ammotype,
1136                                 ((get_weaponinfo(switchweapon)).ammo_field == ammotype),
1137                                 infinite_ammo
1138                         );
1139
1140                         ++row;
1141                         if(row >= rows)
1142                         {
1143                                 row = 0;
1144                                 column = column + 1;
1145                         }
1146                 }
1147         }
1148
1149         if (draw_nades)
1150         {
1151                 nade_statuschange_elapsedtime = time - nade_statuschange_time;
1152
1153                 float f = bound(0, nade_statuschange_elapsedtime*2, 1);
1154
1155                 DrawAmmoNades(pos + eX * column * (ammo_size.x + offset.x) + eY * row * (ammo_size.y + offset.y), ammo_size, nade_prevstatus < nade_cnt && nade_cnt != 0 && f < 1, f);
1156         }
1157
1158         draw_endBoldFont();
1159 }
1160
1161 void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, float vertical, float icon_right_align, vector color, float theAlpha, float fadelerp)
1162 {
1163         vector newPos = '0 0 0', newSize = '0 0 0';
1164         vector picpos, numpos;
1165
1166         if (vertical)
1167         {
1168                 if(mySize.y/mySize.x > 2)
1169                 {
1170                         newSize.y = 2 * mySize.x;
1171                         newSize.x = mySize.x;
1172
1173                         newPos.y = myPos.y + (mySize.y - newSize.y) / 2;
1174                         newPos.x = myPos.x;
1175                 }
1176                 else
1177                 {
1178                         newSize.x = 1/2 * mySize.y;
1179                         newSize.y = mySize.y;
1180
1181                         newPos.x = myPos.x + (mySize.x - newSize.x) / 2;
1182                         newPos.y = myPos.y;
1183                 }
1184
1185                 if(icon_right_align)
1186                 {
1187                         numpos = newPos;
1188                         picpos = newPos + eY * newSize.x;
1189                 }
1190                 else
1191                 {
1192                         picpos = newPos;
1193                         numpos = newPos + eY * newSize.x;
1194                 }
1195
1196                 newSize.y /= 2;
1197                 drawpic_aspect_skin(picpos, icon, newSize, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
1198                 // make number smaller than icon, it looks better
1199                 // reduce only y to draw numbers with different number of digits with the same y size
1200                 numpos.y += newSize.y * ((1 - 0.7) / 2);
1201                 newSize.y *= 0.7;
1202                 drawstring_aspect(numpos, ftos(x), newSize, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
1203                 return;
1204         }
1205
1206         if(mySize.x/mySize.y > 3)
1207         {
1208                 newSize.x = 3 * mySize.y;
1209                 newSize.y = mySize.y;
1210
1211                 newPos.x = myPos.x + (mySize.x - newSize.x) / 2;
1212                 newPos.y = myPos.y;
1213         }
1214         else
1215         {
1216                 newSize.y = 1/3 * mySize.x;
1217                 newSize.x = mySize.x;
1218
1219                 newPos.y = myPos.y + (mySize.y - newSize.y) / 2;
1220                 newPos.x = myPos.x;
1221         }
1222
1223         if(icon_right_align) // right align
1224         {
1225                 numpos = newPos;
1226                 picpos = newPos + eX * 2 * newSize.y;
1227         }
1228         else // left align
1229         {
1230                 numpos = newPos + eX * newSize.y;
1231                 picpos = newPos;
1232         }
1233
1234         // NOTE: newSize_x is always equal to 3 * mySize_y so we can use
1235         // '2 1 0' * newSize_y instead of eX * (2/3) * newSize_x + eY * newSize_y
1236         drawstring_aspect_expanding(numpos, ftos(x), '2 1 0' * newSize.y, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
1237         drawpic_aspect_skin_expanding(picpos, icon, '1 1 0' * newSize.y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
1238 }
1239
1240 void DrawNumIcon(vector myPos, vector mySize, float x, string icon, float vertical, float icon_right_align, vector color, float theAlpha)
1241 {
1242         DrawNumIcon_expanding(myPos, mySize, x, icon, vertical, icon_right_align, color, theAlpha, 0);
1243 }
1244
1245 // Powerups (#2)
1246 //
1247 void HUD_Powerups(void)
1248 {
1249         float strength_time, shield_time, superweapons_time;
1250         if(!autocvar__hud_configure)
1251         {
1252                 if(!autocvar_hud_panel_powerups) return;
1253                 if(spectatee_status == -1) return;
1254                 if(!(getstati(STAT_ITEMS, 0, 24) & (IT_STRENGTH | IT_INVINCIBLE | IT_SUPERWEAPON))) return;
1255                 if (getstati(STAT_HEALTH) <= 0) return;
1256
1257                 strength_time = bound(0, getstatf(STAT_STRENGTH_FINISHED) - time, 99);
1258                 shield_time = bound(0, getstatf(STAT_INVINCIBLE_FINISHED) - time, 99);
1259                 superweapons_time = bound(0, getstatf(STAT_SUPERWEAPONS_FINISHED) - time, 99);
1260
1261                 if (getstati(STAT_ITEMS, 0, 24) & IT_UNLIMITED_SUPERWEAPONS)
1262                         superweapons_time = 99; // force max
1263
1264                 // prevent stuff to show up on mismatch that will be fixed next frame
1265                 if (!(getstati(STAT_ITEMS, 0, 24) & IT_SUPERWEAPON))
1266                         superweapons_time = 0;
1267         }
1268         else
1269         {
1270                 strength_time = 15;
1271                 shield_time = 27;
1272                 superweapons_time = 13;
1273         }
1274
1275         HUD_Panel_UpdateCvars();
1276
1277         draw_beginBoldFont();
1278
1279         vector pos, mySize;
1280         pos = panel_pos;
1281         mySize = panel_size;
1282
1283         HUD_Panel_DrawBg(bound(0, max(strength_time, shield_time, superweapons_time), 1));
1284         if(panel_bg_padding)
1285         {
1286                 pos += '1 1 0' * panel_bg_padding;
1287                 mySize -= '2 2 0' * panel_bg_padding;
1288         }
1289
1290         float panel_ar = mySize.x/mySize.y;
1291         float is_vertical = (panel_ar < 1);
1292         vector shield_offset = '0 0 0', strength_offset = '0 0 0', superweapons_offset = '0 0 0';
1293
1294         float superweapons_is = -1;
1295
1296         if(superweapons_time)
1297         {
1298                 if(strength_time)
1299                 {
1300                         if(shield_time)
1301                                 superweapons_is = 0;
1302                         else
1303                                 superweapons_is = 2;
1304                 }
1305                 else
1306                 {
1307                         if(shield_time)
1308                                 superweapons_is = 1;
1309                         else
1310                                 superweapons_is = 2;
1311                 }
1312         }
1313
1314         // FIXME handle superweapons here
1315         if(superweapons_is == 0)
1316         {
1317                 if (panel_ar >= 4 || (panel_ar >= 1/4 && panel_ar < 1))
1318                 {
1319                         mySize.x *= (1.0 / 3.0);
1320                         superweapons_offset.x = mySize.x;
1321                         if (autocvar_hud_panel_powerups_flip)
1322                                 shield_offset.x = 2*mySize.x;
1323                         else
1324                                 strength_offset.x = 2*mySize.x;
1325                 }
1326                 else
1327                 {
1328                         mySize.y *= (1.0 / 3.0);
1329                         superweapons_offset.y = mySize.y;
1330                         if (autocvar_hud_panel_powerups_flip)
1331                                 shield_offset.y = 2*mySize.y;
1332                         else
1333                                 strength_offset.y = 2*mySize.y;
1334                 }
1335         }
1336         else
1337         {
1338                 if (panel_ar >= 4 || (panel_ar >= 1/4 && panel_ar < 1))
1339                 {
1340                         mySize.x *= 0.5;
1341                         if (autocvar_hud_panel_powerups_flip)
1342                                 shield_offset.x = mySize.x;
1343                         else
1344                                 strength_offset.x = mySize.x;
1345                 }
1346                 else
1347                 {
1348                         mySize.y *= 0.5;
1349                         if (autocvar_hud_panel_powerups_flip)
1350                                 shield_offset.y = mySize.y;
1351                         else
1352                                 strength_offset.y = mySize.y;
1353                 }
1354         }
1355
1356         float shield_baralign, strength_baralign, superweapons_baralign;
1357         float shield_iconalign, strength_iconalign, superweapons_iconalign;
1358
1359         if (autocvar_hud_panel_powerups_flip)
1360         {
1361                 strength_baralign = (autocvar_hud_panel_powerups_baralign == 2 || autocvar_hud_panel_powerups_baralign == 1);
1362                 shield_baralign = (autocvar_hud_panel_powerups_baralign == 3 || autocvar_hud_panel_powerups_baralign == 1);
1363                 strength_iconalign = (autocvar_hud_panel_powerups_iconalign == 2 || autocvar_hud_panel_powerups_iconalign == 1);
1364                 shield_iconalign = (autocvar_hud_panel_powerups_iconalign == 3 || autocvar_hud_panel_powerups_iconalign == 1);
1365         }
1366         else
1367         {
1368                 shield_baralign = (autocvar_hud_panel_powerups_baralign == 2 || autocvar_hud_panel_powerups_baralign == 1);
1369                 strength_baralign = (autocvar_hud_panel_powerups_baralign == 3 || autocvar_hud_panel_powerups_baralign == 1);
1370                 shield_iconalign = (autocvar_hud_panel_powerups_iconalign == 2 || autocvar_hud_panel_powerups_iconalign == 1);
1371                 strength_iconalign = (autocvar_hud_panel_powerups_iconalign == 3 || autocvar_hud_panel_powerups_iconalign == 1);
1372         }
1373
1374         if(superweapons_is == 0)
1375         {
1376                 superweapons_iconalign = strength_iconalign;
1377                 superweapons_baralign = 2;
1378         }
1379         else if(superweapons_is == 1)
1380         {
1381                 superweapons_offset = strength_offset;
1382                 superweapons_iconalign = strength_iconalign;
1383                 superweapons_baralign = strength_baralign;
1384         }
1385         else // if(superweapons_is == 2)
1386         {
1387                 superweapons_offset = shield_offset;
1388                 superweapons_iconalign = shield_iconalign;
1389                 superweapons_baralign = shield_baralign;
1390         }
1391
1392         if(shield_time)
1393         {
1394                 const float maxshield = 30;
1395                 float shield = ceil(shield_time);
1396                 if(autocvar_hud_panel_powerups_progressbar)
1397                         HUD_Panel_DrawProgressBar(pos + shield_offset, mySize, autocvar_hud_panel_powerups_progressbar_shield, shield/maxshield, is_vertical, shield_baralign, autocvar_hud_progressbar_shield_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
1398                 if(autocvar_hud_panel_powerups_text)
1399                 {
1400                         if(shield > 1)
1401                                 DrawNumIcon(pos + shield_offset, mySize, shield, "shield", is_vertical, shield_iconalign, '1 1 1', 1);
1402                         if(shield <= 5)
1403                                 DrawNumIcon_expanding(pos + shield_offset, mySize, shield, "shield", is_vertical, shield_iconalign, '1 1 1', 1, bound(0, (shield - shield_time) / 0.5, 1));
1404                 }
1405         }
1406
1407         if(strength_time)
1408         {
1409                 const float maxstrength = 30;
1410                 float strength = ceil(strength_time);
1411                 if(autocvar_hud_panel_powerups_progressbar)
1412                         HUD_Panel_DrawProgressBar(pos + strength_offset, mySize, autocvar_hud_panel_powerups_progressbar_strength, strength/maxstrength, is_vertical, strength_baralign, autocvar_hud_progressbar_strength_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
1413                 if(autocvar_hud_panel_powerups_text)
1414                 {
1415                         if(strength > 1)
1416                                 DrawNumIcon(pos + strength_offset, mySize, strength, "strength", is_vertical, strength_iconalign, '1 1 1', 1);
1417                         if(strength <= 5)
1418                                 DrawNumIcon_expanding(pos + strength_offset, mySize, strength, "strength", is_vertical, strength_iconalign, '1 1 1', 1, bound(0, (strength - strength_time) / 0.5, 1));
1419                 }
1420         }
1421
1422         if(superweapons_time)
1423         {
1424                 const float maxsuperweapons = 30;
1425                 float superweapons = ceil(superweapons_time);
1426                 if(autocvar_hud_panel_powerups_progressbar)
1427                         HUD_Panel_DrawProgressBar(pos + superweapons_offset, mySize, autocvar_hud_panel_powerups_progressbar_superweapons, superweapons/maxsuperweapons, is_vertical, superweapons_baralign, autocvar_hud_progressbar_superweapons_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
1428                 if(autocvar_hud_panel_powerups_text)
1429                 {
1430                         if(superweapons > 1)
1431                                 DrawNumIcon(pos + superweapons_offset, mySize, superweapons, "superweapons", is_vertical, superweapons_iconalign, '1 1 1', 1);
1432                         if(superweapons <= 5)
1433                                 DrawNumIcon_expanding(pos + superweapons_offset, mySize, superweapons, "superweapons", is_vertical, superweapons_iconalign, '1 1 1', 1, bound(0, (superweapons - superweapons_time) / 0.5, 1));
1434                 }
1435         }
1436
1437         draw_endBoldFont();
1438 }
1439
1440 // Health/armor (#3)
1441 //
1442
1443 // prev_* vars contain the health/armor at the previous FRAME
1444 // set to -1 when player is dead or was not playing
1445 float prev_health, prev_armor;
1446 float health_damagetime, armor_damagetime;
1447 float health_beforedamage, armor_beforedamage;
1448 // old_p_* vars keep track of previous values when smoothing value changes of the progressbar
1449 float old_p_health, old_p_armor;
1450 float old_p_healthtime, old_p_armortime;
1451 // prev_p_* vars contain the health/armor progressbar value at the previous FRAME
1452 // set to -1 to forcedly stop effects when we switch spectated player (e.g. from playerX: 70h to playerY: 50h)
1453 float prev_p_health, prev_p_armor;
1454
1455 void HUD_HealthArmor(void)
1456 {
1457         float armor, health, fuel;
1458         if(!autocvar__hud_configure)
1459         {
1460                 if(!autocvar_hud_panel_healtharmor) return;
1461                 if(hud != HUD_NORMAL) return;
1462                 if(spectatee_status == -1) return;
1463
1464                 health = getstati(STAT_HEALTH);
1465                 if(health <= 0)
1466                 {
1467                         prev_health = -1;
1468                         return;
1469                 }
1470                 armor = getstati(STAT_ARMOR);
1471
1472                 // code to check for spectatee_status changes is in Ent_ClientData()
1473                 // prev_p_health and prev_health can be set to -1 there
1474
1475                 if (prev_p_health == -1)
1476                 {
1477                         // no effect
1478                         health_beforedamage = 0;
1479                         armor_beforedamage = 0;
1480                         health_damagetime = 0;
1481                         armor_damagetime = 0;
1482                         prev_health = health;
1483                         prev_armor = armor;
1484                         old_p_health = health;
1485                         old_p_armor = armor;
1486                         prev_p_health = health;
1487                         prev_p_armor = armor;
1488                 }
1489                 else if (prev_health == -1)
1490                 {
1491                         //start the load effect
1492                         health_damagetime = 0;
1493                         armor_damagetime = 0;
1494                         prev_health = 0;
1495                         prev_armor = 0;
1496                 }
1497                 fuel = getstati(STAT_FUEL);
1498         }
1499         else
1500         {
1501                 health = 150;
1502                 armor = 75;
1503                 fuel = 20;
1504         }
1505
1506         HUD_Panel_UpdateCvars();
1507         vector pos, mySize;
1508         pos = panel_pos;
1509         mySize = panel_size;
1510
1511         HUD_Panel_DrawBg(1);
1512         if(panel_bg_padding)
1513         {
1514                 pos += '1 1 0' * panel_bg_padding;
1515                 mySize -= '2 2 0' * panel_bg_padding;
1516         }
1517
1518         float baralign = autocvar_hud_panel_healtharmor_baralign;
1519         float iconalign = autocvar_hud_panel_healtharmor_iconalign;
1520
1521     float maxhealth = autocvar_hud_panel_healtharmor_maxhealth;
1522     float maxarmor = autocvar_hud_panel_healtharmor_maxarmor;
1523         if(autocvar_hud_panel_healtharmor == 2) // combined health and armor display
1524         {
1525                 vector v;
1526                 v = healtharmor_maxdamage(health, armor, armorblockpercent, DEATH_WEAPON);
1527
1528                 float x;
1529                 x = floor(v.x + 1);
1530
1531         float maxtotal = maxhealth + maxarmor;
1532                 string biggercount;
1533                 if(v.z) // NOT fully armored
1534                 {
1535                         biggercount = "health";
1536                         if(autocvar_hud_panel_healtharmor_progressbar)
1537                                 HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_health, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
1538                         if(armor)
1539             if(autocvar_hud_panel_healtharmor_text)
1540                                 drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "armor", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha * armor / health, DRAWFLAG_NORMAL);
1541                 }
1542                 else
1543                 {
1544                         biggercount = "armor";
1545                         if(autocvar_hud_panel_healtharmor_progressbar)
1546                                 HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
1547                         if(health)
1548             if(autocvar_hud_panel_healtharmor_text)
1549                                 drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "health", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1550                 }
1551         if(autocvar_hud_panel_healtharmor_text)
1552                         DrawNumIcon(pos, mySize, x, biggercount, 0, iconalign, HUD_Get_Num_Color(x, maxtotal), 1);
1553
1554                 if(fuel)
1555                         HUD_Panel_DrawProgressBar(pos, eX * mySize.x + eY * 0.2 * mySize.y, "progressbar", fuel/100, 0, (baralign == 1 || baralign == 3), autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL);
1556         }
1557         else
1558         {
1559                 float panel_ar = mySize.x/mySize.y;
1560                 float is_vertical = (panel_ar < 1);
1561                 vector health_offset = '0 0 0', armor_offset = '0 0 0';
1562                 if (panel_ar >= 4 || (panel_ar >= 1/4 && panel_ar < 1))
1563                 {
1564                         mySize.x *= 0.5;
1565                         if (autocvar_hud_panel_healtharmor_flip)
1566                                 health_offset.x = mySize.x;
1567                         else
1568                                 armor_offset.x = mySize.x;
1569                 }
1570                 else
1571                 {
1572                         mySize.y *= 0.5;
1573                         if (autocvar_hud_panel_healtharmor_flip)
1574                                 health_offset.y = mySize.y;
1575                         else
1576                                 armor_offset.y = mySize.y;
1577                 }
1578
1579                 float health_baralign, armor_baralign, fuel_baralign;
1580                 float health_iconalign, armor_iconalign;
1581                 if (autocvar_hud_panel_healtharmor_flip)
1582                 {
1583                         armor_baralign = (autocvar_hud_panel_healtharmor_baralign == 2 || autocvar_hud_panel_healtharmor_baralign == 1);
1584                         health_baralign = (autocvar_hud_panel_healtharmor_baralign == 3 || autocvar_hud_panel_healtharmor_baralign == 1);
1585                         fuel_baralign = health_baralign;
1586                         armor_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 2 || autocvar_hud_panel_healtharmor_iconalign == 1);
1587                         health_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 3 || autocvar_hud_panel_healtharmor_iconalign == 1);
1588                 }
1589                 else
1590                 {
1591                         health_baralign = (autocvar_hud_panel_healtharmor_baralign == 2 || autocvar_hud_panel_healtharmor_baralign == 1);
1592                         armor_baralign = (autocvar_hud_panel_healtharmor_baralign == 3 || autocvar_hud_panel_healtharmor_baralign == 1);
1593                         fuel_baralign = armor_baralign;
1594                         health_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 2 || autocvar_hud_panel_healtharmor_iconalign == 1);
1595                         armor_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 3 || autocvar_hud_panel_healtharmor_iconalign == 1);
1596                 }
1597
1598                 //if(health)
1599                 {
1600                         if(autocvar_hud_panel_healtharmor_progressbar)
1601                         {
1602                                 float p_health, pain_health_alpha;
1603                                 p_health = health;
1604                                 pain_health_alpha = 1;
1605                                 if (autocvar_hud_panel_healtharmor_progressbar_gfx)
1606                                 {
1607                                         if (autocvar_hud_panel_healtharmor_progressbar_gfx_smooth > 0)
1608                                         {
1609                                                 if (fabs(prev_health - health) >= autocvar_hud_panel_healtharmor_progressbar_gfx_smooth)
1610                                                 {
1611                                                         if (time - old_p_healthtime < 1)
1612                                                                 old_p_health = prev_p_health;
1613                                                         else
1614                                                                 old_p_health = prev_health;
1615                                                         old_p_healthtime = time;
1616                                                 }
1617                                                 if (time - old_p_healthtime < 1)
1618                                                 {
1619                                                         p_health += (old_p_health - health) * (1 - (time - old_p_healthtime));
1620                                                         prev_p_health = p_health;
1621                                                 }
1622                                         }
1623                                         if (autocvar_hud_panel_healtharmor_progressbar_gfx_damage > 0)
1624                                         {
1625                                                 if (prev_health - health >= autocvar_hud_panel_healtharmor_progressbar_gfx_damage)
1626                                                 {
1627                                                         if (time - health_damagetime >= 1)
1628                                                                 health_beforedamage = prev_health;
1629                                                         health_damagetime = time;
1630                                                 }
1631                                                 if (time - health_damagetime < 1)
1632                                                 {
1633                                                         float health_damagealpha = 1 - (time - health_damagetime)*(time - health_damagetime);
1634                                                         HUD_Panel_DrawProgressBar(pos + health_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_health, health_beforedamage/maxhealth, is_vertical, health_baralign, autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * health_damagealpha, DRAWFLAG_NORMAL);
1635                                                 }
1636                                         }
1637                                         prev_health = health;
1638
1639                                         if (health <= autocvar_hud_panel_healtharmor_progressbar_gfx_lowhealth)
1640                                         {
1641                                                 float BLINK_FACTOR = 0.15;
1642                                                 float BLINK_BASE = 0.85;
1643                                                 float BLINK_FREQ = 9;
1644                                                 pain_health_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ);
1645                                         }
1646                                 }
1647                                 HUD_Panel_DrawProgressBar(pos + health_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_health, p_health/maxhealth, is_vertical, health_baralign, autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * pain_health_alpha, DRAWFLAG_NORMAL);
1648                         }
1649                         if(autocvar_hud_panel_healtharmor_text)
1650                                 DrawNumIcon(pos + health_offset, mySize, health, "health", is_vertical, health_iconalign, HUD_Get_Num_Color(health, maxhealth), 1);
1651                 }
1652
1653                 if(armor)
1654                 {
1655                         if(autocvar_hud_panel_healtharmor_progressbar)
1656                         {
1657                                 float p_armor;
1658                                 p_armor = armor;
1659                                 if (autocvar_hud_panel_healtharmor_progressbar_gfx)
1660                                 {
1661                                         if (autocvar_hud_panel_healtharmor_progressbar_gfx_smooth > 0)
1662                                         {
1663                                                 if (fabs(prev_armor - armor) >= autocvar_hud_panel_healtharmor_progressbar_gfx_smooth)
1664                                                 {
1665                                                         if (time - old_p_armortime < 1)
1666                                                                 old_p_armor = prev_p_armor;
1667                                                         else
1668                                                                 old_p_armor = prev_armor;
1669                                                         old_p_armortime = time;
1670                                                 }
1671                                                 if (time - old_p_armortime < 1)
1672                                                 {
1673                                                         p_armor += (old_p_armor - armor) * (1 - (time - old_p_armortime));
1674                                                         prev_p_armor = p_armor;
1675                                                 }
1676                                         }
1677                                         if (autocvar_hud_panel_healtharmor_progressbar_gfx_damage > 0)
1678                                         {
1679                                                 if (prev_armor - armor >= autocvar_hud_panel_healtharmor_progressbar_gfx_damage)
1680                                                 {
1681                                                         if (time - armor_damagetime >= 1)
1682                                                                 armor_beforedamage = prev_armor;
1683                                                         armor_damagetime = time;
1684                                                 }
1685                                                 if (time - armor_damagetime < 1)
1686                                                 {
1687                                                         float armor_damagealpha = 1 - (time - armor_damagetime)*(time - armor_damagetime);
1688                                                         HUD_Panel_DrawProgressBar(pos + armor_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, armor_beforedamage/maxarmor, is_vertical, armor_baralign, autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * armor_damagealpha, DRAWFLAG_NORMAL);
1689                                                 }
1690                                         }
1691                                         prev_armor = armor;
1692                                 }
1693                                 HUD_Panel_DrawProgressBar(pos + armor_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, p_armor/maxarmor, is_vertical, armor_baralign, autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
1694                         }
1695                         if(autocvar_hud_panel_healtharmor_text)
1696                                 DrawNumIcon(pos + armor_offset, mySize, armor, "armor", is_vertical, armor_iconalign, HUD_Get_Num_Color(armor, maxarmor), 1);
1697                 }
1698
1699                 if(fuel)
1700                 {
1701                         if (is_vertical)
1702                                 mySize.x *= 0.2 / 2; //if vertical always halve x to not cover too much numbers with 3 digits
1703                         else
1704                                 mySize.y *= 0.2;
1705                         if (panel_ar >= 4)
1706                                 mySize.x *= 2; //restore full panel size
1707                         else if (panel_ar < 1/4)
1708                                 mySize.y *= 2; //restore full panel size
1709                         HUD_Panel_DrawProgressBar(pos, mySize, "progressbar", fuel/100, is_vertical, fuel_baralign, autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL);
1710                 }
1711         }
1712 }
1713
1714 // Notification area (#4)
1715 //
1716
1717 void HUD_Notify_Push(string icon, string attacker, string victim)
1718 {
1719         if (icon == "")
1720                 return;
1721
1722         ++notify_count;
1723         --notify_index;
1724
1725         if (notify_index == -1)
1726                 notify_index = NOTIFY_MAX_ENTRIES-1;
1727
1728         // Free old strings
1729         if (notify_attackers[notify_index])
1730                 strunzone(notify_attackers[notify_index]);
1731
1732         if (notify_victims[notify_index])
1733                 strunzone(notify_victims[notify_index]);
1734
1735         if (notify_icons[notify_index])
1736                 strunzone(notify_icons[notify_index]);
1737
1738         // Allocate new strings
1739         if (victim != "")
1740         {
1741                 notify_attackers[notify_index] = strzone(attacker);
1742                 notify_victims[notify_index] = strzone(victim);
1743         }
1744         else
1745         {
1746                 // In case of a notification without a victim, the attacker
1747                 // is displayed on the victim's side. Instead of special
1748                 // treatment later on, we can simply switch them here.
1749                 notify_attackers[notify_index] = string_null;
1750                 notify_victims[notify_index] = strzone(attacker);
1751         }
1752
1753         notify_icons[notify_index] = strzone(icon);
1754         notify_times[notify_index] = time;
1755 }
1756
1757 void HUD_Notify(void)
1758 {
1759         if (!autocvar__hud_configure)
1760                 if (!autocvar_hud_panel_notify)
1761                         return;
1762
1763         HUD_Panel_UpdateCvars();
1764         HUD_Panel_DrawBg(1);
1765
1766         if (!autocvar__hud_configure)
1767                 if (notify_count == 0)
1768                         return;
1769
1770         vector pos, size;
1771         pos  = panel_pos;
1772         size = panel_size;
1773
1774         if (panel_bg_padding)
1775         {
1776                 pos  += '1 1 0' * panel_bg_padding;
1777                 size -= '2 2 0' * panel_bg_padding;
1778         }
1779
1780         float fade_start = max(0, autocvar_hud_panel_notify_time);
1781         float fade_time = max(0, autocvar_hud_panel_notify_fadetime);
1782         float icon_aspect = max(1, autocvar_hud_panel_notify_icon_aspect);
1783
1784         float entry_count = bound(1, floor(NOTIFY_MAX_ENTRIES * size.y / size.x), NOTIFY_MAX_ENTRIES);
1785         float entry_height = size.y / entry_count;
1786
1787         float panel_width_half = size.x * 0.5;
1788         float icon_width_half = entry_height * icon_aspect / 2;
1789         float name_maxwidth = panel_width_half - icon_width_half - size.x * NOTIFY_ICON_MARGIN;
1790
1791         vector font_size = '0.5 0.5 0' * entry_height * autocvar_hud_panel_notify_fontsize;
1792         vector icon_size = (eX * icon_aspect + eY) * entry_height;
1793         vector icon_left = eX * (panel_width_half - icon_width_half);
1794         vector attacker_right = eX * name_maxwidth;
1795         vector victim_left = eX * (size.x - name_maxwidth);
1796
1797         vector attacker_pos, victim_pos, icon_pos;
1798         string attacker, victim, icon;
1799         float i, j, count, step, limit, alpha;
1800
1801         if (autocvar_hud_panel_notify_flip)
1802         {
1803                 // Order items from the top down
1804                 i = 0;
1805                 step = +1;
1806                 limit = entry_count;
1807         }
1808         else
1809         {
1810                 // Order items from the bottom up
1811                 i = entry_count - 1;
1812                 step = -1;
1813                 limit = -1;
1814         }
1815
1816         for (j = notify_index, count = 0; i != limit; i += step, ++j, ++count)
1817         {
1818                 if(autocvar__hud_configure)
1819                 {
1820                         attacker = sprintf(_("Player %d"), count + 1);
1821                         victim = sprintf(_("Player %d"), count + 2);
1822                         icon = get_weaponinfo(min(WEP_FIRST + count * 2, WEP_LAST)).model2;
1823                         alpha = bound(0, 1.2 - count / entry_count, 1);
1824                 }
1825                 else
1826                 {
1827                         if (j == NOTIFY_MAX_ENTRIES)
1828                                 j = 0;
1829
1830                         if (notify_times[j] + fade_start > time)
1831                                 alpha = 1;
1832                         else if (fade_time != 0)
1833                         {
1834                                 alpha = bound(0, (notify_times[j] + fade_start + fade_time - time) / fade_time, 1);
1835                                 if (alpha == 0)
1836                                         break;
1837                         }
1838                         else
1839                                 break;
1840
1841                         attacker = notify_attackers[j];
1842                         victim = notify_victims[j];
1843                         icon = notify_icons[j];
1844                 }
1845
1846                 if (icon != "" && victim != "")
1847                 {
1848                         vector name_top = eY * (i * entry_height + 0.5 * (entry_height - font_size.y));
1849
1850                         icon_pos = pos + icon_left + eY * i * entry_height;
1851                         drawpic_aspect_skin(icon_pos, icon, icon_size, '1 1 1', panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
1852
1853                         victim = textShortenToWidth(victim, name_maxwidth, font_size, stringwidth_colors);
1854                         victim_pos = pos + victim_left + name_top;
1855                         drawcolorcodedstring(victim_pos, victim, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
1856
1857                         if (attacker != "")
1858                         {
1859                                 attacker = textShortenToWidth(attacker, name_maxwidth, font_size, stringwidth_colors);
1860                                 attacker_pos = pos + attacker_right - eX * stringwidth(attacker, true, font_size) + name_top;
1861                                 drawcolorcodedstring(attacker_pos, attacker, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
1862                         }
1863                 }
1864         }
1865
1866         notify_count = count;
1867 }
1868
1869 // Timer (#5)
1870 //
1871 // TODO: macro
1872 string seconds_tostring(float sec)
1873 {
1874         float minutes;
1875         minutes = floor(sec / 60);
1876
1877         sec -= minutes * 60;
1878         return sprintf("%d:%02d", minutes, sec);
1879 }
1880
1881 void HUD_Timer(void)
1882 {
1883         if(!autocvar__hud_configure)
1884         {
1885                 if(!autocvar_hud_panel_timer) return;
1886         }
1887
1888         HUD_Panel_UpdateCvars();
1889
1890         draw_beginBoldFont();
1891
1892         vector pos, mySize;
1893         pos = panel_pos;
1894         mySize = panel_size;
1895
1896         HUD_Panel_DrawBg(1);
1897         if(panel_bg_padding)
1898         {
1899                 pos += '1 1 0' * panel_bg_padding;
1900                 mySize -= '2 2 0' * panel_bg_padding;
1901         }
1902
1903         string timer;
1904         float timelimit, elapsedTime, timeleft, minutesLeft;
1905
1906         timelimit = getstatf(STAT_TIMELIMIT);
1907
1908         timeleft = max(0, timelimit * 60 + getstatf(STAT_GAMESTARTTIME) - time);
1909         timeleft = ceil(timeleft);
1910
1911         minutesLeft = floor(timeleft / 60);
1912
1913         vector timer_color;
1914         if(minutesLeft >= 5 || warmup_stage || timelimit == 0) //don't use red or yellow in warmup or when there is no timelimit
1915                 timer_color = '1 1 1'; //white
1916         else if(minutesLeft >= 1)
1917                 timer_color = '1 1 0'; //yellow
1918         else
1919                 timer_color = '1 0 0'; //red
1920
1921         if (autocvar_hud_panel_timer_increment || timelimit == 0 || warmup_stage) {
1922                 if (time < getstatf(STAT_GAMESTARTTIME)) {
1923                         //while restart is still active, show 00:00
1924                         timer = seconds_tostring(0);
1925                 } else {
1926                         elapsedTime = floor(time - getstatf(STAT_GAMESTARTTIME)); //127
1927                         timer = seconds_tostring(elapsedTime);
1928                 }
1929         } else {
1930                 timer = seconds_tostring(timeleft);
1931         }
1932
1933         drawstring_aspect(pos, timer, mySize, timer_color, panel_fg_alpha, DRAWFLAG_NORMAL);
1934
1935         draw_endBoldFont();
1936 }
1937
1938 // Radar (#6)
1939 //
1940 void HUD_Radar(void)
1941 {
1942         if (!autocvar__hud_configure)
1943         {
1944                 if (hud_panel_radar_maximized)
1945                 {
1946                         if (!hud_draw_maximized) return;
1947                 }
1948                 else
1949                 {
1950                         if (autocvar_hud_panel_radar == 0) return;
1951                         if (autocvar_hud_panel_radar != 2 && !teamplay) return;
1952                         if(radar_panel_modified)
1953                         {
1954                                 panel.update_time = time; // forces reload of panel attributes
1955                                 radar_panel_modified = false;
1956                         }
1957                 }
1958         }
1959
1960         HUD_Panel_UpdateCvars();
1961
1962         float f = 0;
1963
1964         if (hud_panel_radar_maximized && !autocvar__hud_configure)
1965         {
1966                 panel_size = autocvar_hud_panel_radar_maximized_size;
1967                 panel_size.x = bound(0.2, panel_size.x, 1) * vid_conwidth;
1968                 panel_size.y = bound(0.2, panel_size.y, 1) * vid_conheight;
1969                 panel_pos.x = (vid_conwidth - panel_size.x) / 2;
1970                 panel_pos.y = (vid_conheight - panel_size.y) / 2;
1971
1972                 string panel_bg;
1973                 panel_bg = strcat(hud_skin_path, "/border_default"); // always use the default border when maximized
1974                 if(precache_pic(panel_bg) == "")
1975                         panel_bg = "gfx/hud/default/border_default"; // fallback
1976                 if(!radar_panel_modified && panel_bg != panel.current_panel_bg)
1977                         radar_panel_modified = true;
1978                 if(panel.current_panel_bg)
1979                         strunzone(panel.current_panel_bg);
1980                 panel.current_panel_bg = strzone(panel_bg);
1981
1982                 switch(hud_panel_radar_maximized_zoommode)
1983                 {
1984                         default:
1985                         case 0:
1986                                 f = current_zoomfraction;
1987                                 break;
1988                         case 1:
1989                                 f = 1 - current_zoomfraction;
1990                                 break;
1991                         case 2:
1992                                 f = 0;
1993                                 break;
1994                         case 3:
1995                                 f = 1;
1996                                 break;
1997                 }
1998
1999                 switch(hud_panel_radar_maximized_rotation)
2000                 {
2001                         case 0:
2002                                 teamradar_angle = view_angles.y - 90;
2003                                 break;
2004                         default:
2005                                 teamradar_angle = 90 * hud_panel_radar_maximized_rotation;
2006                                 break;
2007                 }
2008         }
2009         if (!hud_panel_radar_maximized && !autocvar__hud_configure)
2010         {
2011                 switch(hud_panel_radar_zoommode)
2012                 {
2013                         default:
2014                         case 0:
2015                                 f = current_zoomfraction;
2016                                 break;
2017                         case 1:
2018                                 f = 1 - current_zoomfraction;
2019                                 break;
2020                         case 2:
2021                                 f = 0;
2022                                 break;
2023                         case 3:
2024                                 f = 1;
2025                                 break;
2026                 }
2027
2028                 switch(hud_panel_radar_rotation)
2029                 {
2030                         case 0:
2031                                 teamradar_angle = view_angles.y - 90;
2032                                 break;
2033                         default:
2034                                 teamradar_angle = 90 * hud_panel_radar_rotation;
2035                                 break;
2036                 }
2037         }
2038
2039         vector pos, mySize;
2040         pos = panel_pos;
2041         mySize = panel_size;
2042
2043         HUD_Panel_DrawBg(1);
2044         if(panel_bg_padding)
2045         {
2046                 pos += '1 1 0' * panel_bg_padding;
2047                 mySize -= '2 2 0' * panel_bg_padding;
2048         }
2049
2050         float color2;
2051         entity tm;
2052         float scale2d, normalsize, bigsize;
2053
2054         teamradar_origin2d = pos + 0.5 * mySize;
2055         teamradar_size2d = mySize;
2056
2057         if(minimapname == "")
2058                 return;
2059
2060         teamradar_loadcvars();
2061
2062         scale2d = vlen_maxnorm2d(mi_picmax - mi_picmin);
2063         teamradar_size2d = mySize;
2064
2065         teamradar_extraclip_mins = teamradar_extraclip_maxs = '0 0 0'; // we always center
2066
2067         // pixels per world qu to match the teamradar_size2d_x range in the longest dimension
2068         if((hud_panel_radar_rotation == 0 && !hud_panel_radar_maximized) || (hud_panel_radar_maximized_rotation == 0 && hud_panel_radar_maximized))
2069         {
2070                 // max-min distance must fit the radar in any rotation
2071                 bigsize = vlen_minnorm2d(teamradar_size2d) * scale2d / (1.05 * vlen2d(mi_scale));
2072         }
2073         else
2074         {
2075                 vector c0, c1, c2, c3, span;
2076                 c0 = rotate(mi_min, teamradar_angle * DEG2RAD);
2077                 c1 = rotate(mi_max, teamradar_angle * DEG2RAD);
2078                 c2 = rotate('1 0 0' * mi_min.x + '0 1 0' * mi_max.y, teamradar_angle * DEG2RAD);
2079                 c3 = rotate('1 0 0' * mi_max.x + '0 1 0' * mi_min.y, teamradar_angle * DEG2RAD);
2080                 span = '0 0 0';
2081                 span.x = max(c0_x, c1_x, c2_x, c3_x) - min(c0_x, c1_x, c2_x, c3_x);
2082                 span.y = max(c0_y, c1_y, c2_y, c3_y) - min(c0_y, c1_y, c2_y, c3_y);
2083
2084                 // max-min distance must fit the radar in x=x, y=y
2085                 bigsize = min(
2086                         teamradar_size2d.x * scale2d / (1.05 * span.x),
2087                         teamradar_size2d.y * scale2d / (1.05 * span.y)
2088                 );
2089         }
2090
2091         normalsize = vlen_maxnorm2d(teamradar_size2d) * scale2d / hud_panel_radar_scale;
2092         if(bigsize > normalsize)
2093                 normalsize = bigsize;
2094
2095         teamradar_size =
2096                   f * bigsize
2097                 + (1 - f) * normalsize;
2098         teamradar_origin3d_in_texcoord = teamradar_3dcoord_to_texcoord(
2099                   f * mi_center
2100                 + (1 - f) * view_origin);
2101
2102         drawsetcliparea(
2103                 pos.x,
2104                 pos.y,
2105                 mySize.x,
2106                 mySize.y
2107         );
2108
2109         draw_teamradar_background(hud_panel_radar_foreground_alpha);
2110
2111         for(tm = world; (tm = find(tm, classname, "radarlink")); )
2112                 draw_teamradar_link(tm.origin, tm.velocity, tm.team);
2113         for(tm = world; (tm = findflags(tm, teamradar_icon, 0xFFFFFF)); )
2114                 draw_teamradar_icon(tm.origin, tm.teamradar_icon, tm, tm.teamradar_color, panel_fg_alpha);
2115         for(tm = world; (tm = find(tm, classname, "entcs_receiver")); )
2116         {
2117                 color2 = GetPlayerColor(tm.sv_entnum);
2118                 //if(color == NUM_SPECTATOR || color == color2)
2119                         draw_teamradar_player(tm.origin, tm.angles, Team_ColorRGB(color2));
2120         }
2121         draw_teamradar_player(view_origin, view_angles, '1 1 1');
2122
2123         drawresetcliparea();
2124 }
2125
2126 // Score (#7)
2127 //
2128 void HUD_UpdatePlayerTeams();
2129 void HUD_Score_Rankings(vector pos, vector mySize, entity me)
2130 {
2131         float score;
2132         entity tm = world, pl;
2133         float SCOREPANEL_MAX_ENTRIES = 6;
2134         float SCOREPANEL_ASPECTRATIO = 2;
2135         float entries = bound(1, floor(SCOREPANEL_MAX_ENTRIES * mySize.y/mySize.x * SCOREPANEL_ASPECTRATIO), SCOREPANEL_MAX_ENTRIES);
2136         vector fontsize = '1 1 0' * (mySize.y/entries);
2137
2138         vector rgb, score_color;
2139         rgb = '1 1 1';
2140         score_color = '1 1 1';
2141
2142         float name_size = mySize.x*0.75;
2143         float spacing_size = mySize.x*0.04;
2144         const float highlight_alpha = 0.2;
2145         float i = 0, me_printed = 0, first_pl = 0;
2146         string s;
2147         if (autocvar__hud_configure)
2148         {
2149                 float players_per_team = 0;
2150                 if (team_count)
2151                 {
2152                         // show team scores in the first line
2153                         float score_size = mySize.x / team_count;
2154                         players_per_team = max(2, ceil((entries - 1) / team_count));
2155                         for(i=0; i<team_count; ++i) {
2156                                 if (i == floor((entries - 2) / players_per_team) || (entries == 1 && i == 0))
2157                                         HUD_Panel_DrawHighlight(pos + eX * score_size * i, eX * score_size + eY * fontsize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2158                                 drawstring_aspect(pos + eX * score_size * i, ftos(175 - 23*i), eX * score_size + eY * fontsize.y, Team_ColorRGB(ColorByTeam(i)) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
2159                         }
2160                         first_pl = 1;
2161                         pos.y += fontsize.y;
2162                 }
2163                 score = 10 + SCOREPANEL_MAX_ENTRIES * 3;
2164                 for (i=first_pl; i<entries; ++i)
2165                 {
2166                         //simulate my score is lower than all displayed players,
2167                         //so that I don't appear at all showing pure rankings.
2168                         //This is to better show the difference between the 2 ranking views
2169                         if (i == entries-1 && autocvar_hud_panel_score_rankings == 1)
2170                         {
2171                                 rgb = '1 1 0';
2172                                 drawfill(pos, eX * mySize.x + eY * fontsize.y, rgb, highlight_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
2173                                 s = GetPlayerName(player_localnum);
2174                                 score = 7;
2175                         }
2176                         else
2177                         {
2178                                 s = sprintf(_("Player %d"), i + 1 - first_pl);
2179                                 score -= 3;
2180                         }
2181
2182                         if (team_count)
2183                                 score_color = Team_ColorRGB(ColorByTeam(floor((i - first_pl) / players_per_team))) * 0.8;
2184                         s = textShortenToWidth(s, name_size, fontsize, stringwidth_colors);
2185                         drawcolorcodedstring(pos + eX * (name_size - stringwidth(s, true, fontsize)), s, fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2186                         drawstring(pos + eX * (name_size + spacing_size), ftos(score), fontsize, score_color, panel_fg_alpha, DRAWFLAG_NORMAL);
2187                         pos.y += fontsize.y;
2188                 }
2189                 return;
2190         }
2191
2192         if (!scoreboard_fade_alpha) // the scoreboard too calls HUD_UpdatePlayerTeams
2193                 HUD_UpdatePlayerTeams();
2194         if (team_count)
2195         {
2196                 // show team scores in the first line
2197                 float score_size = mySize.x / team_count;
2198                 for(tm = teams.sort_next; tm; tm = tm.sort_next) {
2199                         if(tm.team == NUM_SPECTATOR)
2200                                 continue;
2201                         if (tm.team == myteam)
2202                                 drawfill(pos + eX * score_size * i, eX * score_size + eY * fontsize.y, '1 1 1', highlight_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
2203                         drawstring_aspect(pos + eX * score_size * i, ftos(tm.(teamscores[ts_primary])), eX * score_size + eY * fontsize.y, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
2204                         ++i;
2205                 }
2206                 first_pl = 1;
2207                 pos.y += fontsize.y;
2208                 tm = teams.sort_next;
2209         }
2210         i = first_pl;
2211
2212         do
2213         for (pl = players.sort_next; pl && i<entries; pl = pl.sort_next)
2214         {
2215                 if ((team_count && pl.team != tm.team) || pl.team == NUM_SPECTATOR)
2216                         continue;
2217
2218                 if (i == entries-1 && !me_printed && pl != me)
2219                 if (autocvar_hud_panel_score_rankings == 1 && spectatee_status != -1)
2220                 {
2221                         for (pl = me.sort_next; pl; pl = pl.sort_next)
2222                                 if (pl.team != NUM_SPECTATOR)
2223                                         break;
2224
2225                         if (pl)
2226                                 rgb = '1 1 0'; //not last but not among the leading players: yellow
2227                         else
2228                                 rgb = '1 0 0'; //last: red
2229                         pl = me;
2230                 }
2231
2232                 if (pl == me)
2233                 {
2234                         if (i == first_pl)
2235                                 rgb = '0 1 0'; //first: green
2236                         me_printed = 1;
2237                         drawfill(pos, eX * mySize.x + eY * fontsize.y, rgb, highlight_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
2238                 }
2239                 if (team_count)
2240                         score_color = Team_ColorRGB(pl.team) * 0.8;
2241                 s = textShortenToWidth(GetPlayerName(pl.sv_entnum), name_size, fontsize, stringwidth_colors);
2242                 drawcolorcodedstring(pos + eX * (name_size - stringwidth(s, true, fontsize)), s, fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2243                 drawstring(pos + eX * (name_size + spacing_size), ftos(pl.(scores[ps_primary])), fontsize, score_color, panel_fg_alpha, DRAWFLAG_NORMAL);
2244                 pos.y += fontsize.y;
2245                 ++i;
2246         }
2247         while (i<entries && team_count && (tm = tm.sort_next) && (tm.team != NUM_SPECTATOR || (tm = tm.sort_next)));
2248 }
2249
2250 void HUD_Score(void)
2251 {
2252         if(!autocvar__hud_configure)
2253         {
2254                 if(!autocvar_hud_panel_score) return;
2255                 if(spectatee_status == -1 && (gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
2256         }
2257
2258         HUD_Panel_UpdateCvars();
2259         vector pos, mySize;
2260         pos = panel_pos;
2261         mySize = panel_size;
2262
2263         HUD_Panel_DrawBg(1);
2264         if(panel_bg_padding)
2265         {
2266                 pos += '1 1 0' * panel_bg_padding;
2267                 mySize -= '2 2 0' * panel_bg_padding;
2268         }
2269
2270         float score, distribution = 0;
2271         string sign;
2272         vector distribution_color;
2273         entity tm, pl, me;
2274
2275         me = playerslots[player_localentnum - 1];
2276
2277         if((scores_flags[ps_primary] & SFL_TIME) && !teamplay) { // race/cts record display on HUD
2278                 string timer, distrtimer;
2279
2280                 pl = players.sort_next;
2281                 if(pl == me)
2282                         pl = pl.sort_next;
2283                 if(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)
2284                         if(pl.scores[ps_primary] == 0)
2285                                 pl = world;
2286
2287                 score = me.(scores[ps_primary]);
2288                 timer = TIME_ENCODED_TOSTRING(score);
2289
2290                 draw_beginBoldFont();
2291                 if (pl && ((!(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)) || score)) {
2292                         // distribution display
2293                         distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
2294
2295                         distrtimer = ftos_decimals(fabs(distribution/pow(10, TIME_DECIMALS)), TIME_DECIMALS);
2296
2297                         if (distribution <= 0) {
2298                                 distribution_color = '0 1 0';
2299                                 sign = "-";
2300                         }
2301                         else {
2302                                 distribution_color = '1 0 0';
2303                                 sign = "+";
2304                         }
2305                         drawstring_aspect(pos + eX * 0.75 * mySize.x, strcat(sign, distrtimer), eX * 0.25 * mySize.x + eY * (1/3) * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL);
2306                 }
2307                 // race record display
2308                 if (distribution <= 0)
2309                         HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2310                 drawstring_aspect(pos, timer, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2311                 draw_endBoldFont();
2312         } else if (!teamplay) { // non-teamgames
2313                 if ((spectatee_status == -1 && !autocvar__hud_configure) || autocvar_hud_panel_score_rankings)
2314                 {
2315                         HUD_Score_Rankings(pos, mySize, me);
2316                         return;
2317                 }
2318                 // me vector := [team/connected frags id]
2319                 pl = players.sort_next;
2320                 if(pl == me)
2321                         pl = pl.sort_next;
2322
2323                 if(autocvar__hud_configure)
2324                         distribution = 42;
2325                 else if(pl)
2326                         distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
2327                 else
2328                         distribution = 0;
2329
2330                 score = me.(scores[ps_primary]);
2331                 if(autocvar__hud_configure)
2332                         score = 123;
2333
2334                 if(distribution >= 5)
2335                         distribution_color = eY;
2336                 else if(distribution >= 0)
2337                         distribution_color = '1 1 1';
2338                 else if(distribution >= -5)
2339                         distribution_color = '1 1 0';
2340                 else
2341                         distribution_color = eX;
2342
2343                 string distribution_str;
2344                 distribution_str = ftos(distribution);
2345                 draw_beginBoldFont();
2346                 if (distribution >= 0)
2347                 {
2348                         if (distribution > 0)
2349                                 distribution_str = strcat("+", distribution_str);
2350                         HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2351                 }
2352                 drawstring_aspect(pos, ftos(score), eX * 0.75 * mySize.x + eY * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL);
2353                 drawstring_aspect(pos + eX * 0.75 * mySize.x, distribution_str, eX * 0.25 * mySize.x + eY * (1/3) * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL);
2354                 draw_endBoldFont();
2355         } else { // teamgames
2356                 float row, column, rows = 0, columns = 0;
2357                 vector offset = '0 0 0';
2358                 vector score_pos, score_size; //for scores other than myteam
2359                 if(autocvar_hud_panel_score_rankings)
2360                 {
2361                         HUD_Score_Rankings(pos, mySize, me);
2362                         return;
2363                 }
2364                 if(spectatee_status == -1)
2365                 {
2366                         rows = HUD_GetRowCount(team_count, mySize, 3);
2367                         columns = ceil(team_count/rows);
2368                         score_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
2369
2370                         float newSize;
2371                         if(score_size.x/score_size.y > 3)
2372                         {
2373                                 newSize = 3 * score_size.y;
2374                                 offset.x = score_size.x - newSize;
2375                                 pos.x += offset.x/2;
2376                                 score_size.x = newSize;
2377                         }
2378                         else
2379                         {
2380                                 newSize = 1/3 * score_size.x;
2381                                 offset.y = score_size.y - newSize;
2382                                 pos.y += offset.y/2;
2383                                 score_size.y = newSize;
2384                         }
2385                 }
2386                 else
2387                         score_size = eX * mySize.x*(1/4) + eY * mySize.y*(1/3);
2388
2389                 float max_fragcount;
2390                 max_fragcount = -99;
2391                 draw_beginBoldFont();
2392                 row = column = 0;
2393                 for(tm = teams.sort_next; tm; tm = tm.sort_next) {
2394                         if(tm.team == NUM_SPECTATOR)
2395                                 continue;
2396                         score = tm.(teamscores[ts_primary]);
2397                         if(autocvar__hud_configure)
2398                                 score = 123;
2399
2400                         if (score > max_fragcount)
2401                                 max_fragcount = score;
2402
2403                         if (spectatee_status == -1)
2404                         {
2405                                 score_pos = pos + eX * column * (score_size.x + offset.x) + eY * row * (score_size.y + offset.y);
2406                                 if (max_fragcount == score)
2407                                         HUD_Panel_DrawHighlight(score_pos, score_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2408                                 drawstring_aspect(score_pos, ftos(score), score_size, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
2409                                 ++row;
2410                                 if(row >= rows)
2411                                 {
2412                                         row = 0;
2413                                         ++column;
2414                                 }
2415                         }
2416                         else if(tm.team == myteam) {
2417                                 if (max_fragcount == score)
2418                                         HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2419                                 drawstring_aspect(pos, ftos(score), eX * 0.75 * mySize.x + eY * mySize.y, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
2420                         } else {
2421                                 if (max_fragcount == score)
2422                                         HUD_Panel_DrawHighlight(pos + eX * 0.75 * mySize.x + eY * (1/3) * rows * mySize.y, score_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2423                                 drawstring_aspect(pos + eX * 0.75 * mySize.x + eY * (1/3) * rows * mySize.y, ftos(score), score_size, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
2424                                 ++rows;
2425                         }
2426                 }
2427                 draw_endBoldFont();
2428         }
2429 }
2430
2431 // Race timer (#8)
2432 //
2433 void HUD_RaceTimer (void)
2434 {
2435         if(!autocvar__hud_configure)
2436         {
2437                 if(!autocvar_hud_panel_racetimer) return;
2438                 if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
2439                 if(spectatee_status == -1) return;
2440         }
2441
2442         HUD_Panel_UpdateCvars();
2443
2444         draw_beginBoldFont();
2445
2446         vector pos, mySize;
2447         pos = panel_pos;
2448         mySize = panel_size;
2449
2450         HUD_Panel_DrawBg(1);
2451         if(panel_bg_padding)
2452         {
2453                 pos += '1 1 0' * panel_bg_padding;
2454                 mySize -= '2 2 0' * panel_bg_padding;
2455         }
2456
2457         // always force 4:1 aspect
2458         vector newSize = '0 0 0';
2459         if(mySize.x/mySize.y > 4)
2460         {
2461                 newSize.x = 4 * mySize.y;
2462                 newSize.y = mySize.y;
2463
2464                 pos.x = pos.x + (mySize.x - newSize.x) / 2;
2465         }
2466         else
2467         {
2468                 newSize.y = 1/4 * mySize.x;
2469                 newSize.x = mySize.x;
2470
2471                 pos.y = pos.y + (mySize.y - newSize.y) / 2;
2472         }
2473         mySize = newSize;
2474
2475         float a, t;
2476         string s, forcetime;
2477
2478         if(autocvar__hud_configure)
2479         {
2480                 s = "0:13:37";
2481                 drawstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, false, '0.60 0.60 0' * mySize.y), s, '0.60 0.60 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2482                 s = _("^1Intermediate 1 (+15.42)");
2483                 drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.20 * mySize.y) + eY * 0.60 * mySize.y, s, '1 1 0' * 0.20 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
2484                 s = sprintf(_("^1PENALTY: %.1f (%s)"), 2, "missing a checkpoint");
2485                 drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.20 * mySize.y) + eY * 0.80 * mySize.y, s, '1 1 0' * 0.20 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
2486         }
2487         else if(race_checkpointtime)
2488         {
2489                 a = bound(0, 2 - (time - race_checkpointtime), 1);
2490                 s = "";
2491                 forcetime = "";
2492                 if(a > 0) // just hit a checkpoint?
2493                 {
2494                         if(race_checkpoint != 254)
2495                         {
2496                                 if(race_time && race_previousbesttime)
2497                                         s = MakeRaceString(race_checkpoint, TIME_DECODE(race_time) - TIME_DECODE(race_previousbesttime), 0, 0, race_previousbestname);
2498                                 else
2499                                         s = MakeRaceString(race_checkpoint, 0, -1, 0, race_previousbestname);
2500                                 if(race_time)
2501                                         forcetime = TIME_ENCODED_TOSTRING(race_time);
2502                         }
2503                 }
2504                 else
2505                 {
2506                         if(race_laptime && race_nextbesttime && race_nextcheckpoint != 254)
2507                         {
2508                                 a = bound(0, 2 - ((race_laptime + TIME_DECODE(race_nextbesttime)) - (time + TIME_DECODE(race_penaltyaccumulator))), 1);
2509                                 if(a > 0) // next one?
2510                                 {
2511                                         s = MakeRaceString(race_nextcheckpoint, (time + TIME_DECODE(race_penaltyaccumulator)) - race_laptime, TIME_DECODE(race_nextbesttime), 0, race_nextbestname);
2512                                 }
2513                         }
2514                 }
2515
2516                 if(s != "" && a > 0)
2517                 {
2518                         drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
2519                 }
2520
2521                 if(race_penaltytime)
2522                 {
2523                         a = bound(0, 2 - (time - race_penaltyeventtime), 1);
2524                         if(a > 0)
2525                         {
2526                                 s = sprintf(_("^1PENALTY: %.1f (%s)"), race_penaltytime * 0.1, race_penaltyreason);
2527                                 drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.8 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
2528                         }
2529                 }
2530
2531                 if(forcetime != "")
2532                 {
2533                         a = bound(0, (time - race_checkpointtime) / 0.5, 1);
2534                         drawstring_expanding(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(forcetime, false, '1 1 0' * 0.6 * mySize.y), forcetime, '1 1 0' * 0.6 * mySize.y, '1 1 1', panel_fg_alpha, 0, a);
2535                 }
2536                 else
2537                         a = 1;
2538
2539                 if(race_laptime && race_checkpoint != 255)
2540                 {
2541                         s = TIME_ENCODED_TOSTRING(TIME_ENCODE(time + TIME_DECODE(race_penaltyaccumulator) - race_laptime));
2542                         drawstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, false, '0.6 0.6 0' * mySize.y), s, '0.6 0.6 0' * mySize.y, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
2543                 }
2544         }
2545         else
2546         {
2547                 if(race_mycheckpointtime)
2548                 {
2549                         a = bound(0, 2 - (time - race_mycheckpointtime), 1);
2550                         s = MakeRaceString(race_mycheckpoint, TIME_DECODE(race_mycheckpointdelta), -(race_mycheckpointenemy == ""), race_mycheckpointlapsdelta, race_mycheckpointenemy);
2551                         drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
2552                 }
2553                 if(race_othercheckpointtime && race_othercheckpointenemy != "")
2554                 {
2555                         a = bound(0, 2 - (time - race_othercheckpointtime), 1);
2556                         s = MakeRaceString(race_othercheckpoint, -TIME_DECODE(race_othercheckpointdelta), -(race_othercheckpointenemy == ""), race_othercheckpointlapsdelta, race_othercheckpointenemy);
2557                         drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
2558                 }
2559
2560                 if(race_penaltytime && !race_penaltyaccumulator)
2561                 {
2562                         t = race_penaltytime * 0.1 + race_penaltyeventtime;
2563                         a = bound(0, (1 + t - time), 1);
2564                         if(a > 0)
2565                         {
2566                                 if(time < t)
2567                                         s = sprintf(_("^1PENALTY: %.1f (%s)"), (t - time) * 0.1, race_penaltyreason);
2568                                 else
2569                                         s = sprintf(_("^2PENALTY: %.1f (%s)"), 0, race_penaltyreason);
2570                                 drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
2571                         }
2572                 }
2573         }
2574
2575         draw_endBoldFont();
2576 }
2577
2578 // Vote window (#9)
2579 //
2580 float vote_yescount;
2581 float vote_nocount;
2582 float vote_needed;
2583 float vote_highlighted; // currently selected vote
2584
2585 float vote_active; // is there an active vote?
2586 float vote_prev; // previous state of vote_active to check for a change
2587 float vote_alpha;
2588 float vote_change; // "time" when vote_active changed
2589
2590 void HUD_Vote(void)
2591 {
2592         if(autocvar_cl_allow_uid2name == -1 && (gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || (serverflags & SERVERFLAG_PLAYERSTATS)))
2593         {
2594                 vote_active = 1;
2595                 if (autocvar__hud_configure)
2596                 {
2597                         vote_yescount = 0;
2598                         vote_nocount = 0;
2599                         print(_("^1You must answer before entering hud configure mode\n"));
2600                         cvar_set("_hud_configure", "0");
2601                 }
2602                 if(vote_called_vote)
2603                         strunzone(vote_called_vote);
2604                 vote_called_vote = strzone(_("^2Name ^7instead of \"^1Anonymous player^7\" in stats"));
2605                 uid2name_dialog = 1;
2606         }
2607
2608         if(!autocvar__hud_configure)
2609         {
2610                 if(!autocvar_hud_panel_vote) return;
2611
2612                 panel_fg_alpha = autocvar_hud_panel_fg_alpha;
2613                 panel_bg_alpha_str = autocvar_hud_panel_vote_bg_alpha;
2614
2615                 if(panel_bg_alpha_str == "") {
2616                         panel_bg_alpha_str = ftos(autocvar_hud_panel_bg_alpha);
2617                 }
2618                 panel_bg_alpha = stof(panel_bg_alpha_str);
2619         }
2620         else
2621         {
2622                 vote_yescount = 3;
2623                 vote_nocount = 2;
2624                 vote_needed = 4;
2625         }
2626
2627         string s;
2628         float a;
2629         if(vote_active != vote_prev) {
2630                 vote_change = time;
2631                 vote_prev = vote_active;
2632         }
2633
2634         if(vote_active || autocvar__hud_configure)
2635                 vote_alpha = bound(0, (time - vote_change) * 2, 1);
2636         else
2637                 vote_alpha = bound(0, 1 - (time - vote_change) * 2, 1);
2638
2639         if(!vote_alpha)
2640                 return;
2641
2642         HUD_Panel_UpdateCvars();
2643
2644         if(uid2name_dialog)
2645         {
2646                 panel_pos = eX * 0.3 * vid_conwidth + eY * 0.1 * vid_conheight;
2647                 panel_size = eX * 0.4 * vid_conwidth + eY * 0.3 * vid_conheight;
2648         }
2649
2650     // these must be below above block
2651         vector pos, mySize;
2652         pos = panel_pos;
2653         mySize = panel_size;
2654
2655         a = vote_alpha * (vote_highlighted ? autocvar_hud_panel_vote_alreadyvoted_alpha : 1);
2656         HUD_Panel_DrawBg(a);
2657         a = panel_fg_alpha * a;
2658
2659         if(panel_bg_padding)
2660         {
2661                 pos += '1 1 0' * panel_bg_padding;
2662                 mySize -= '2 2 0' * panel_bg_padding;
2663         }
2664
2665         // always force 3:1 aspect
2666         vector newSize = '0 0 0';
2667         if(mySize.x/mySize.y > 3)
2668         {
2669                 newSize.x = 3 * mySize.y;
2670                 newSize.y = mySize.y;
2671
2672                 pos.x = pos.x + (mySize.x - newSize.x) / 2;
2673         }
2674         else
2675         {
2676                 newSize.y = 1/3 * mySize.x;
2677                 newSize.x = mySize.x;
2678
2679                 pos.y = pos.y + (mySize.y - newSize.y) / 2;
2680         }
2681         mySize = newSize;
2682
2683         s = _("A vote has been called for:");
2684         if(uid2name_dialog)
2685                 s = _("Allow servers to store and display your name?");
2686         drawstring_aspect(pos, s, eX * mySize.x + eY * (2/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
2687         s = textShortenToWidth(vote_called_vote, mySize.x, '1 1 0' * mySize.y * (1/8), stringwidth_colors);
2688         if(autocvar__hud_configure)
2689                 s = _("^1Configure the HUD");
2690         drawcolorcodedstring_aspect(pos + eY * (2/8) * mySize.y, s, eX * mySize.x + eY * (1.75/8) * mySize.y, a, DRAWFLAG_NORMAL);
2691
2692         // print the yes/no counts
2693     s = sprintf(_("Yes (%s): %d"), getcommandkey("vyes", "vyes"), vote_yescount);
2694         drawstring_aspect(pos + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '0 1 0', a, DRAWFLAG_NORMAL);
2695     s = sprintf(_("No (%s): %d"), getcommandkey("vno", "vno"), vote_nocount);
2696         drawstring_aspect(pos + eX * 0.5 * mySize.x + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '1 0 0', a, DRAWFLAG_NORMAL);
2697
2698         // draw the progress bar backgrounds
2699         drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_back", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
2700
2701         // draw the highlights
2702         if(vote_highlighted == 1) {
2703                 drawsetcliparea(pos.x, pos.y, mySize.x * 0.5, mySize.y);
2704                 drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
2705         }
2706         else if(vote_highlighted == -1) {
2707                 drawsetcliparea(pos.x + 0.5 * mySize.x, pos.y, mySize.x * 0.5, mySize.y);
2708                 drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
2709         }
2710
2711         // draw the progress bars
2712         if(vote_yescount && vote_needed)
2713         {
2714                 drawsetcliparea(pos.x, pos.y, mySize.x * 0.5 * (vote_yescount/vote_needed), mySize.y);
2715                 drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
2716         }
2717
2718         if(vote_nocount && vote_needed)
2719         {
2720                 drawsetcliparea(pos.x + mySize.x - mySize.x * 0.5 * (vote_nocount/vote_needed), pos.y, mySize.x * 0.5, mySize.y);
2721                 drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
2722         }
2723
2724         drawresetcliparea();
2725 }
2726
2727 // Mod icons panel (#10)
2728 //
2729
2730 float mod_active; // is there any active mod icon?
2731
2732 void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, int layout, float i)
2733 {
2734         float stat = -1;
2735         string pic = "";
2736         vector color = '0 0 0';
2737         switch(i)
2738         {
2739                 case 0:
2740                         stat = getstati(STAT_REDALIVE);
2741                         pic = "player_red.tga";
2742                         color = '1 0 0';
2743                         break;
2744                 case 1:
2745                         stat = getstati(STAT_BLUEALIVE);
2746                         pic = "player_blue.tga";
2747                         color = '0 0 1';
2748                         break;
2749                 case 2:
2750                         stat = getstati(STAT_YELLOWALIVE);
2751                         pic = "player_yellow.tga";
2752                         color = '1 1 0';
2753                         break;
2754                 default:
2755                 case 3:
2756                         stat = getstati(STAT_PINKALIVE);
2757                         pic = "player_pink.tga";
2758                         color = '1 0 1';
2759                         break;
2760         }
2761
2762         if(mySize.x/mySize.y > aspect_ratio)
2763         {
2764                 i = aspect_ratio * mySize.y;
2765                 myPos.x = myPos.x + (mySize.x - i) / 2;
2766                 mySize.x = i;
2767         }
2768         else
2769         {
2770                 i = 1/aspect_ratio * mySize.x;
2771                 myPos.y = myPos.y + (mySize.y - i) / 2;
2772                 mySize.y = i;
2773         }
2774
2775         if(layout)
2776         {
2777                 drawpic_aspect_skin(myPos, pic, eX * 0.7 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2778                 drawstring_aspect(myPos + eX * 0.7 * mySize.x, ftos(stat), eX * 0.3 * mySize.x + eY * mySize.y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
2779         }
2780         else
2781                 drawstring_aspect(myPos, ftos(stat), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
2782 }
2783
2784 // Clan Arena and Freeze Tag HUD modicons
2785 void HUD_Mod_CA(vector myPos, vector mySize)
2786 {
2787         mod_active = 1; // required in each mod function that always shows something
2788
2789         int layout;
2790         if(gametype == MAPINFO_TYPE_CA)
2791                 layout = autocvar_hud_panel_modicons_ca_layout;
2792         else //if(gametype == MAPINFO_TYPE_FREEZETAG)
2793                 layout = autocvar_hud_panel_modicons_freezetag_layout;
2794         float rows, columns, aspect_ratio;
2795         aspect_ratio = (layout) ? 2 : 1;
2796         rows = HUD_GetRowCount(team_count, mySize, aspect_ratio);
2797         columns = ceil(team_count/rows);
2798
2799         int i;
2800         float row = 0, column = 0;
2801         vector pos, itemSize;
2802         itemSize = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
2803         for(i=0; i<team_count; ++i)
2804         {
2805                 pos = myPos + eX * column * itemSize.x + eY * row * itemSize.y;
2806
2807                 DrawCAItem(pos, itemSize, aspect_ratio, layout, i);
2808
2809                 ++row;
2810                 if(row >= rows)
2811                 {
2812                         row = 0;
2813                         ++column;
2814                 }
2815         }
2816 }
2817
2818 // CTF HUD modicon section
2819 float redflag_prevframe, blueflag_prevframe; // status during previous frame
2820 float redflag_prevstatus, blueflag_prevstatus; // last remembered status
2821 float redflag_statuschange_time, blueflag_statuschange_time; // time when the status changed
2822
2823 void HUD_Mod_CTF_Reset(void)
2824 {
2825         redflag_prevstatus = blueflag_prevstatus = redflag_prevframe = blueflag_prevframe = redflag_statuschange_time = blueflag_statuschange_time = 0;
2826 }
2827
2828 void HUD_Mod_CTF(vector pos, vector mySize)
2829 {
2830         vector redflag_pos, blueflag_pos;
2831         vector flag_size;
2832         float f; // every function should have that
2833
2834         float redflag, blueflag; // current status
2835         float redflag_statuschange_elapsedtime, blueflag_statuschange_elapsedtime; // time since the status changed
2836         float stat_items;
2837
2838         stat_items = getstati(STAT_ITEMS, 0, 24);
2839         redflag = (stat_items/IT_RED_FLAG_TAKEN) & 3;
2840         blueflag = (stat_items/IT_BLUE_FLAG_TAKEN) & 3;
2841
2842         if(redflag || blueflag)
2843                 mod_active = 1;
2844         else
2845                 mod_active = 0;
2846
2847         if(autocvar__hud_configure)
2848         {
2849                 redflag = 1;
2850                 blueflag = 2;
2851         }
2852
2853         // when status CHANGES, set old status into prevstatus and current status into status
2854         if (redflag != redflag_prevframe)
2855         {
2856                 redflag_statuschange_time = time;
2857                 redflag_prevstatus = redflag_prevframe;
2858                 redflag_prevframe = redflag;
2859         }
2860
2861         if (blueflag != blueflag_prevframe)
2862         {
2863                 blueflag_statuschange_time = time;
2864                 blueflag_prevstatus = blueflag_prevframe;
2865                 blueflag_prevframe = blueflag;
2866         }
2867
2868         redflag_statuschange_elapsedtime = time - redflag_statuschange_time;
2869         blueflag_statuschange_elapsedtime = time - blueflag_statuschange_time;
2870
2871         float BLINK_FACTOR = 0.15;
2872         float BLINK_BASE = 0.85;
2873         // note:
2874         //   RMS = sqrt(BLINK_BASE^2 + 0.5 * BLINK_FACTOR^2)
2875         // thus
2876         //   BLINK_BASE = sqrt(RMS^2 - 0.5 * BLINK_FACTOR^2)
2877         // ensure RMS == 1
2878         float BLINK_FREQ = 5; // circle frequency, = 2*pi*frequency in hertz
2879
2880         string red_icon, red_icon_prevstatus;
2881         float red_alpha, red_alpha_prevstatus;
2882         red_alpha = red_alpha_prevstatus = 1;
2883         switch(redflag) {
2884                 case 1: red_icon = "flag_red_taken"; break;
2885                 case 2: red_icon = "flag_red_lost"; break;
2886                 case 3: red_icon = "flag_red_carrying"; red_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
2887                 default:
2888                         if((stat_items & IT_CTF_SHIELDED) && (myteam == NUM_TEAM_2))
2889                                 red_icon = "flag_red_shielded";
2890                         else
2891                                 red_icon = string_null;
2892                         break;
2893         }
2894         switch(redflag_prevstatus) {
2895                 case 1: red_icon_prevstatus = "flag_red_taken"; break;
2896                 case 2: red_icon_prevstatus = "flag_red_lost"; break;
2897                 case 3: red_icon_prevstatus = "flag_red_carrying"; red_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
2898                 default:
2899                         if(redflag == 3)
2900                                 red_icon_prevstatus = "flag_red_carrying"; // make it more visible
2901                         else if((stat_items & IT_CTF_SHIELDED) && (myteam == NUM_TEAM_2))
2902                                 red_icon_prevstatus = "flag_red_shielded";
2903                         else
2904                                 red_icon_prevstatus = string_null;
2905                         break;
2906         }
2907
2908         string blue_icon, blue_icon_prevstatus;
2909         float blue_alpha, blue_alpha_prevstatus;
2910         blue_alpha = blue_alpha_prevstatus = 1;
2911         switch(blueflag) {
2912                 case 1: blue_icon = "flag_blue_taken"; break;
2913                 case 2: blue_icon = "flag_blue_lost"; break;
2914                 case 3: blue_icon = "flag_blue_carrying"; blue_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
2915                 default:
2916                         if((stat_items & IT_CTF_SHIELDED) && (myteam == NUM_TEAM_1))
2917                                 blue_icon = "flag_blue_shielded";
2918                         else
2919                                 blue_icon = string_null;
2920                         break;
2921         }
2922         switch(blueflag_prevstatus) {
2923                 case 1: blue_icon_prevstatus = "flag_blue_taken"; break;
2924                 case 2: blue_icon_prevstatus = "flag_blue_lost"; break;
2925                 case 3: blue_icon_prevstatus = "flag_blue_carrying"; blue_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
2926                 default:
2927                         if(blueflag == 3)
2928                                 blue_icon_prevstatus = "flag_blue_carrying"; // make it more visible
2929                         else if((stat_items & IT_CTF_SHIELDED) && (myteam == NUM_TEAM_1))
2930                                 blue_icon_prevstatus = "flag_blue_shielded";
2931                         else
2932                                 blue_icon_prevstatus = string_null;
2933                         break;
2934         }
2935
2936         if(mySize.x > mySize.y) {
2937                 if (myteam == NUM_TEAM_1) { // always draw own flag on left
2938                         redflag_pos = pos;
2939                         blueflag_pos = pos + eX * 0.5 * mySize.x;
2940                 } else {
2941                         blueflag_pos = pos;
2942                         redflag_pos = pos + eX * 0.5 * mySize.x;
2943                 }
2944                 flag_size = eX * 0.5 * mySize.x + eY * mySize.y;
2945         } else {
2946                 if (myteam == NUM_TEAM_1) { // always draw own flag on left
2947                         redflag_pos = pos;
2948                         blueflag_pos = pos + eY * 0.5 * mySize.y;
2949                 } else {
2950                         blueflag_pos = pos;
2951                         redflag_pos = pos + eY * 0.5 * mySize.y;
2952                 }
2953                 flag_size = eY * 0.5 * mySize.y + eX * mySize.x;
2954         }
2955
2956         f = bound(0, redflag_statuschange_elapsedtime*2, 1);
2957         if(red_icon_prevstatus && f < 1)
2958                 drawpic_aspect_skin_expanding(redflag_pos, red_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * red_alpha_prevstatus, DRAWFLAG_NORMAL, f);
2959         if(red_icon)
2960                 drawpic_aspect_skin(redflag_pos, red_icon, flag_size, '1 1 1', panel_fg_alpha * red_alpha * f, DRAWFLAG_NORMAL);
2961
2962         f = bound(0, blueflag_statuschange_elapsedtime*2, 1);
2963         if(blue_icon_prevstatus && f < 1)
2964                 drawpic_aspect_skin_expanding(blueflag_pos, blue_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * blue_alpha_prevstatus, DRAWFLAG_NORMAL, f);
2965         if(blue_icon)
2966                 drawpic_aspect_skin(blueflag_pos, blue_icon, flag_size, '1 1 1', panel_fg_alpha * blue_alpha * f, DRAWFLAG_NORMAL);
2967 }
2968
2969 // Keyhunt HUD modicon section
2970 vector KH_SLOTS[4];
2971
2972 void HUD_Mod_KH(vector pos, vector mySize)
2973 {
2974         mod_active = 1; // keyhunt should never hide the mod icons panel
2975
2976         // Read current state
2977
2978         float state = getstati(STAT_KH_KEYS);
2979         float i, key_state;
2980         float all_keys, team1_keys, team2_keys, team3_keys, team4_keys, dropped_keys, carrying_keys;
2981         all_keys = team1_keys = team2_keys = team3_keys = team4_keys = dropped_keys = carrying_keys = 0;
2982
2983         for(i = 0; i < 4; ++i)
2984         {
2985                 key_state = (bitshift(state, i * -5) & 31) - 1;
2986
2987                 if(key_state == -1)
2988                         continue;
2989
2990                 if(key_state == 30)
2991                 {
2992                         ++carrying_keys;
2993                         key_state = myteam;
2994                 }
2995
2996                 switch(key_state)
2997                 {
2998                         case NUM_TEAM_1: ++team1_keys; break;
2999                         case NUM_TEAM_2: ++team2_keys; break;
3000                         case NUM_TEAM_3: ++team3_keys; break;
3001                         case NUM_TEAM_4: ++team4_keys; break;
3002                         case 29: ++dropped_keys; break;
3003                 }
3004
3005                 ++all_keys;
3006         }
3007
3008         // Calculate slot measurements
3009
3010         vector slot_size;
3011
3012         if(all_keys == 4 && mySize.x * 0.5 < mySize.y && mySize.y * 0.5 < mySize.x)
3013         {
3014                 // Quadratic arrangement
3015                 slot_size = eX * mySize.x * 0.5 + eY * mySize.y * 0.5;
3016                 KH_SLOTS[0] = pos;
3017                 KH_SLOTS[1] = pos + eX * slot_size.x;
3018                 KH_SLOTS[2] = pos + eY * slot_size.y;
3019                 KH_SLOTS[3] = pos + eX * slot_size.x + eY * slot_size.y;
3020         }
3021         else
3022         {
3023                 if(mySize.x > mySize.y)
3024                 {
3025                         // Horizontal arrangement
3026                         slot_size = eX * mySize.x / all_keys + eY * mySize.y;
3027                         for(i = 0; i < all_keys; ++i)
3028                                 KH_SLOTS[i] = pos + eX * slot_size.x * i;
3029                 }
3030                 else
3031                 {
3032                         // Vertical arrangement
3033                         slot_size = eX * mySize.x + eY * mySize.y / all_keys;
3034                         for(i = 0; i < all_keys; ++i)
3035                                 KH_SLOTS[i] = pos + eY * slot_size.y * i;
3036                 }
3037         }
3038
3039         // Make icons blink in case of RUN HERE
3040
3041         float blink = 0.6 + sin(2*M_PI*time) / 2.5; // Oscillate between 0.2 and 1
3042         float alpha;
3043         alpha = 1;
3044
3045         if(carrying_keys)
3046                 switch(myteam)
3047                 {
3048                         case NUM_TEAM_1: if(team1_keys == all_keys) alpha = blink; break;
3049                         case NUM_TEAM_2: if(team2_keys == all_keys) alpha = blink; break;
3050                         case NUM_TEAM_3: if(team3_keys == all_keys) alpha = blink; break;
3051                         case NUM_TEAM_4: if(team4_keys == all_keys) alpha = blink; break;
3052                 }
3053
3054         // Draw icons
3055
3056         i = 0;
3057
3058         while(team1_keys--)
3059                 if(myteam == NUM_TEAM_1 && carrying_keys)
3060                 {
3061                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_red_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
3062                         --carrying_keys;
3063                 }
3064                 else
3065                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_red_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
3066
3067         while(team2_keys--)
3068                 if(myteam == NUM_TEAM_2 && carrying_keys)
3069                 {
3070                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_blue_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
3071                         --carrying_keys;
3072                 }
3073                 else
3074                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_blue_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
3075
3076         while(team3_keys--)
3077                 if(myteam == NUM_TEAM_3 && carrying_keys)
3078                 {
3079                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_yellow_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
3080                         --carrying_keys;
3081                 }
3082                 else
3083                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_yellow_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
3084
3085         while(team4_keys--)
3086                 if(myteam == NUM_TEAM_4 && carrying_keys)
3087                 {
3088                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_pink_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
3089                         --carrying_keys;
3090                 }
3091                 else
3092                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_pink_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
3093
3094         while(dropped_keys--)
3095                 drawpic_aspect_skin(KH_SLOTS[i++], "kh_dropped", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
3096 }
3097
3098 // Keepaway HUD mod icon
3099 float kaball_prevstatus; // last remembered status
3100 float kaball_statuschange_time; // time when the status changed
3101
3102 // we don't need to reset for keepaway since it immediately
3103 // autocorrects prevstatus as to if the player has the ball or not
3104
3105 void HUD_Mod_Keepaway(vector pos, vector mySize)
3106 {
3107         mod_active = 1; // keepaway should always show the mod HUD
3108
3109         float BLINK_FACTOR = 0.15;
3110         float BLINK_BASE = 0.85;
3111         float BLINK_FREQ = 5;
3112         float kaball_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ);
3113
3114         float stat_items = getstati(STAT_ITEMS, 0, 24);
3115         float kaball = (stat_items/IT_KEY1) & 1;
3116
3117         if(kaball != kaball_prevstatus)
3118         {
3119                 kaball_statuschange_time = time;
3120                 kaball_prevstatus = kaball;
3121         }
3122
3123         vector kaball_pos, kaball_size;
3124
3125         if(mySize.x > mySize.y) {
3126                 kaball_pos = pos + eX * 0.25 * mySize.x;
3127                 kaball_size = eX * 0.5 * mySize.x + eY * mySize.y;
3128         } else {
3129                 kaball_pos = pos + eY * 0.25 * mySize.y;
3130                 kaball_size = eY * 0.5 * mySize.y + eX * mySize.x;
3131         }
3132
3133         float kaball_statuschange_elapsedtime = time - kaball_statuschange_time;
3134         float f = bound(0, kaball_statuschange_elapsedtime*2, 1);
3135
3136         if(kaball_prevstatus && f < 1)
3137                 drawpic_aspect_skin_expanding(kaball_pos, "keepawayball_carrying", kaball_size, '1 1 1', panel_fg_alpha * kaball_alpha, DRAWFLAG_NORMAL, f);
3138
3139         if(kaball)
3140                 drawpic_aspect_skin(pos, "keepawayball_carrying", eX * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha * kaball_alpha * f, DRAWFLAG_NORMAL);
3141 }
3142
3143
3144 // Nexball HUD mod icon
3145 void HUD_Mod_NexBall(vector pos, vector mySize)
3146 {
3147         float stat_items, nb_pb_starttime, dt, p;
3148
3149         stat_items = getstati(STAT_ITEMS, 0, 24);
3150         nb_pb_starttime = getstatf(STAT_NB_METERSTART);
3151
3152         if (stat_items & IT_KEY1)
3153                 mod_active = 1;
3154         else
3155                 mod_active = 0;
3156
3157         //Manage the progress bar if any
3158         if (nb_pb_starttime > 0)
3159         {
3160                 dt = (time - nb_pb_starttime) % nb_pb_period;
3161                 // one period of positive triangle
3162                 p = 2 * dt / nb_pb_period;
3163                 if (p > 1)
3164                         p = 2 - p;
3165
3166                 HUD_Panel_DrawProgressBar(pos, mySize, "progressbar", p, (mySize.x <= mySize.y), 0, autocvar_hud_progressbar_nexball_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
3167         }
3168
3169         if (stat_items & IT_KEY1)
3170                 drawpic_aspect_skin(pos, "nexball_carrying", eX * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3171 }
3172
3173 // Race/CTS HUD mod icons
3174 float crecordtime_prev; // last remembered crecordtime
3175 float crecordtime_change_time; // time when crecordtime last changed
3176 float srecordtime_prev; // last remembered srecordtime
3177 float srecordtime_change_time; // time when srecordtime last changed
3178
3179 float race_status_time;
3180 float race_status_prev;
3181 string race_status_name_prev;
3182 void HUD_Mod_Race(vector pos, vector mySize)
3183 {
3184         mod_active = 1; // race should never hide the mod icons panel
3185         entity me;
3186         me = playerslots[player_localnum];
3187         float t, score;
3188         float f; // yet another function has this
3189         score = me.(scores[ps_primary]);
3190
3191         if(!(scores_flags[ps_primary] & SFL_TIME) || teamplay) // race/cts record display on HUD
3192                 return; // no records in the actual race
3193
3194         // clientside personal record
3195         string rr;
3196         if(gametype == MAPINFO_TYPE_CTS)
3197                 rr = CTS_RECORD;
3198         else
3199                 rr = RACE_RECORD;
3200         t = stof(db_get(ClientProgsDB, strcat(shortmapname, rr, "time")));
3201
3202         if(score && (score < t || !t)) {
3203                 db_put(ClientProgsDB, strcat(shortmapname, rr, "time"), ftos(score));
3204                 if(autocvar_cl_autodemo_delete_keeprecords)
3205                 {
3206                         f = autocvar_cl_autodemo_delete;
3207                         f &= ~1;
3208                         cvar_set("cl_autodemo_delete", ftos(f)); // don't delete demo with new record!
3209                 }
3210         }
3211
3212         if(t != crecordtime_prev) {
3213                 crecordtime_prev = t;
3214                 crecordtime_change_time = time;
3215         }
3216
3217         vector textPos, medalPos;
3218         float squareSize;
3219         if(mySize.x > mySize.y) {
3220                 // text on left side
3221                 squareSize = min(mySize.y, mySize.x/2);
3222                 textPos = pos + eX * 0.5 * max(0, mySize.x/2 - squareSize) + eY * 0.5 * (mySize.y - squareSize);
3223                 medalPos = pos + eX * 0.5 * max(0, mySize.x/2 - squareSize) + eX * 0.5 * mySize.x + eY * 0.5 * (mySize.y - squareSize);
3224         } else {
3225                 // text on top
3226                 squareSize = min(mySize.x, mySize.y/2);
3227                 textPos = pos + eY * 0.5 * max(0, mySize.y/2 - squareSize) + eX * 0.5 * (mySize.x - squareSize);
3228                 medalPos = pos + eY * 0.5 * max(0, mySize.y/2 - squareSize) + eY * 0.5 * mySize.y + eX * 0.5 * (mySize.x - squareSize);
3229         }
3230
3231         f = time - crecordtime_change_time;
3232
3233         if (f > 1) {
3234                 drawstring_aspect(textPos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3235                 drawstring_aspect(textPos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3236         } else {
3237                 drawstring_aspect(textPos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3238                 drawstring_aspect(textPos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3239                 drawstring_aspect_expanding(pos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
3240                 drawstring_aspect_expanding(pos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
3241         }
3242
3243         // server record
3244         t = race_server_record;
3245         if(t != srecordtime_prev) {
3246                 srecordtime_prev = t;
3247                 srecordtime_change_time = time;
3248         }
3249         f = time - srecordtime_change_time;
3250
3251         if (f > 1) {
3252                 drawstring_aspect(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3253                 drawstring_aspect(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3254         } else {
3255                 drawstring_aspect(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3256                 drawstring_aspect(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3257                 drawstring_aspect_expanding(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
3258                 drawstring_aspect_expanding(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
3259         }
3260
3261         if (race_status != race_status_prev || race_status_name != race_status_name_prev) {
3262                 race_status_time = time + 5;
3263                 race_status_prev = race_status;
3264                 if (race_status_name_prev)
3265                         strunzone(race_status_name_prev);
3266                 race_status_name_prev = strzone(race_status_name);
3267         }
3268
3269         // race "awards"
3270         float a;
3271         a = bound(0, race_status_time - time, 1);
3272
3273         string s;
3274         s = textShortenToWidth(race_status_name, squareSize, '1 1 0' * 0.1 * squareSize, stringwidth_colors);
3275
3276         float rank;
3277         if(race_status > 0)
3278                 rank = race_CheckName(race_status_name);
3279         else
3280                 rank = 0;
3281         string rankname;
3282         rankname = count_ordinal(rank);
3283
3284         vector namepos;
3285         namepos = medalPos + '0 0.8 0' * squareSize;
3286         vector rankpos;
3287         rankpos = medalPos + '0 0.15 0' * squareSize;
3288
3289         if(race_status == 0)
3290                 drawpic_aspect_skin(medalPos, "race_newfail", '1 1 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
3291         else if(race_status == 1) {
3292                 drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newtime", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
3293                 drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
3294                 drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
3295         } else if(race_status == 2) {
3296                 if(race_status_name == GetPlayerName(player_localnum) || !race_myrank || race_myrank < rank)
3297                         drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankgreen", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
3298                 else
3299                         drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankyellow", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
3300                 drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
3301                 drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
3302         } else if(race_status == 3) {
3303                 drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrecordserver", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
3304                 drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
3305                 drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
3306         }
3307
3308         if (race_status_time - time <= 0) {
3309                 race_status_prev = -1;
3310                 race_status = -1;
3311                 if(race_status_name)
3312                         strunzone(race_status_name);
3313                 race_status_name = string_null;
3314                 if(race_status_name_prev)
3315                         strunzone(race_status_name_prev);
3316                 race_status_name_prev = string_null;
3317         }
3318 }
3319
3320 void DrawDomItem(vector myPos, vector mySize, float aspect_ratio, int layout, float i)
3321 {
3322         float stat = -1;
3323         string pic = "";
3324         vector color = '0 0 0';
3325         switch(i)
3326         {
3327                 case 0:
3328                         stat = getstatf(STAT_DOM_PPS_RED);
3329                         pic = "dom_icon_red";
3330                         color = '1 0 0';
3331                         break;
3332                 case 1:
3333                         stat = getstatf(STAT_DOM_PPS_BLUE);
3334                         pic = "dom_icon_blue";
3335                         color = '0 0 1';
3336                         break;
3337                 case 2:
3338                         stat = getstatf(STAT_DOM_PPS_YELLOW);
3339                         pic = "dom_icon_yellow";
3340                         color = '1 1 0';
3341                         break;
3342                 default:
3343                 case 3:
3344                         stat = getstatf(STAT_DOM_PPS_PINK);
3345                         pic = "dom_icon_pink";
3346                         color = '1 0 1';
3347                         break;
3348         }
3349         float pps_ratio = stat / getstatf(STAT_DOM_TOTAL_PPS);
3350
3351         if(mySize.x/mySize.y > aspect_ratio)
3352         {
3353                 i = aspect_ratio * mySize.y;
3354                 myPos.x = myPos.x + (mySize.x - i) / 2;
3355                 mySize.x = i;
3356         }
3357         else
3358         {
3359                 i = 1/aspect_ratio * mySize.x;
3360                 myPos.y = myPos.y + (mySize.y - i) / 2;
3361                 mySize.y = i;
3362         }
3363
3364         if (layout) // show text too
3365         {
3366                 //draw the text
3367                 color *= 0.5 + pps_ratio * (1 - 0.5); // half saturated color at min, full saturated at max
3368                 if (layout == 2) // average pps
3369                         drawstring_aspect(myPos + eX * mySize.y, ftos_decimals(stat, 2), eX * (2/3) * mySize.x + eY * mySize.y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
3370                 else // percentage of average pps
3371                         drawstring_aspect(myPos + eX * mySize.y, strcat( ftos(floor(pps_ratio*100 + 0.5)), "%" ), eX * (2/3) * mySize.x + eY * mySize.y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
3372         }
3373
3374         //draw the icon
3375         drawpic_aspect_skin(myPos, pic, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3376         if (stat > 0)
3377         {
3378                 drawsetcliparea(myPos.x, myPos.y + mySize.y * (1 - pps_ratio), mySize.y, mySize.y * pps_ratio);
3379                 drawpic_aspect_skin(myPos, strcat(pic, "-highlighted"), '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3380                 drawresetcliparea();
3381         }
3382 }
3383
3384 void HUD_Mod_Dom(vector myPos, vector mySize)
3385 {
3386         mod_active = 1; // required in each mod function that always shows something
3387
3388         int layout = autocvar_hud_panel_modicons_dom_layout;
3389         float rows, columns, aspect_ratio;
3390         aspect_ratio = (layout) ? 3 : 1;
3391         rows = HUD_GetRowCount(team_count, mySize, aspect_ratio);
3392         columns = ceil(team_count/rows);
3393
3394         int i;
3395         float row = 0, column = 0;
3396         vector pos, itemSize;
3397         itemSize = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
3398         for(i=0; i<team_count; ++i)
3399         {
3400                 pos = myPos + eX * column * itemSize.x + eY * row * itemSize.y;
3401
3402                 DrawDomItem(pos, itemSize, aspect_ratio, layout, i);
3403
3404                 ++row;
3405                 if(row >= rows)
3406                 {
3407                         row = 0;
3408                         ++column;
3409                 }
3410         }
3411 }
3412
3413 void HUD_ModIcons_SetFunc()
3414 {
3415         switch(gametype)
3416         {
3417                 case MAPINFO_TYPE_KEYHUNT:              HUD_ModIcons_GameType = HUD_Mod_KH; break;
3418                 case MAPINFO_TYPE_CTF:                  HUD_ModIcons_GameType = HUD_Mod_CTF; break;
3419                 case MAPINFO_TYPE_NEXBALL:              HUD_ModIcons_GameType = HUD_Mod_NexBall; break;
3420                 case MAPINFO_TYPE_CTS:
3421                 case MAPINFO_TYPE_RACE:         HUD_ModIcons_GameType = HUD_Mod_Race; break;
3422                 case MAPINFO_TYPE_CA:
3423                 case MAPINFO_TYPE_FREEZETAG:    HUD_ModIcons_GameType = HUD_Mod_CA; break;
3424                 case MAPINFO_TYPE_DOMINATION:   HUD_ModIcons_GameType = HUD_Mod_Dom; break;
3425                 case MAPINFO_TYPE_KEEPAWAY:     HUD_ModIcons_GameType = HUD_Mod_Keepaway; break;
3426         }
3427 }
3428
3429 float mod_prev; // previous state of mod_active to check for a change
3430 float mod_alpha;
3431 float mod_change; // "time" when mod_active changed
3432
3433 void HUD_ModIcons(void)
3434 {
3435         if(!autocvar__hud_configure)
3436         {
3437                 if(!autocvar_hud_panel_modicons) return;
3438                 if(!HUD_ModIcons_GameType) return;
3439         }
3440
3441         HUD_Panel_UpdateCvars();
3442
3443         draw_beginBoldFont();
3444
3445         if(mod_active != mod_prev) {
3446                 mod_change = time;
3447                 mod_prev = mod_active;
3448         }
3449
3450         if(mod_active || autocvar__hud_configure)
3451                 mod_alpha = bound(0, (time - mod_change) * 2, 1);
3452         else
3453                 mod_alpha = bound(0, 1 - (time - mod_change) * 2, 1);
3454
3455         if(mod_alpha)
3456                 HUD_Panel_DrawBg(mod_alpha);
3457
3458         if(panel_bg_padding)
3459         {
3460                 panel_pos += '1 1 0' * panel_bg_padding;
3461                 panel_size -= '2 2 0' * panel_bg_padding;
3462         }
3463
3464         if(autocvar__hud_configure)
3465                 HUD_Mod_CTF(panel_pos, panel_size);
3466         else
3467                 HUD_ModIcons_GameType(panel_pos, panel_size);
3468
3469         draw_endBoldFont();
3470 }
3471
3472 // Draw pressed keys (#11)
3473 //
3474 void HUD_PressedKeys(void)
3475 {
3476         if(!autocvar__hud_configure)
3477         {
3478                 if(!autocvar_hud_panel_pressedkeys) return;
3479                 if(spectatee_status <= 0 && autocvar_hud_panel_pressedkeys < 2) return;
3480         }
3481
3482         HUD_Panel_UpdateCvars();
3483         vector pos, mySize;
3484         pos = panel_pos;
3485         mySize = panel_size;
3486
3487         HUD_Panel_DrawBg(1);
3488         if(panel_bg_padding)
3489         {
3490                 pos += '1 1 0' * panel_bg_padding;
3491                 mySize -= '2 2 0' * panel_bg_padding;
3492         }
3493
3494         // force custom aspect
3495         float aspect = autocvar_hud_panel_pressedkeys_aspect;
3496         if(aspect)
3497         {
3498                 vector newSize = '0 0 0';
3499                 if(mySize.x/mySize.y > aspect)
3500                 {
3501                         newSize.x = aspect * mySize.y;
3502                         newSize.y = mySize.y;
3503
3504                         pos.x = pos.x + (mySize.x - newSize.x) / 2;
3505                 }
3506                 else
3507                 {
3508                         newSize.y = 1/aspect * mySize.x;
3509                         newSize.x = mySize.x;
3510
3511                         pos.y = pos.y + (mySize.y - newSize.y) / 2;
3512                 }
3513                 mySize = newSize;
3514         }
3515
3516         vector keysize;
3517         keysize = eX * mySize.x * (1/3.0) + eY * mySize.y * (1/(3.0 - !autocvar_hud_panel_pressedkeys_attack));
3518         float pressedkeys;
3519         pressedkeys = getstatf(STAT_PRESSED_KEYS);
3520
3521         if(autocvar_hud_panel_pressedkeys_attack)
3522         {
3523                 drawpic_aspect_skin(pos + eX * keysize.x * 0.5, ((pressedkeys & KEY_ATCK) ? "key_atck_inv.tga" : "key_atck.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3524                 drawpic_aspect_skin(pos + eX * keysize.x * 1.5, ((pressedkeys & KEY_ATCK2) ? "key_atck_inv.tga" : "key_atck.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3525                 pos.y += keysize.y;
3526         }
3527
3528         drawpic_aspect_skin(pos, ((pressedkeys & KEY_CROUCH) ? "key_crouch_inv.tga" : "key_crouch.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3529         drawpic_aspect_skin(pos + eX * keysize.x, ((pressedkeys & KEY_FORWARD) ? "key_forward_inv.tga" : "key_forward.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3530         drawpic_aspect_skin(pos + eX * keysize.x * 2, ((pressedkeys & KEY_JUMP) ? "key_jump_inv.tga" : "key_jump.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3531         pos.y += keysize.y;
3532         drawpic_aspect_skin(pos, ((pressedkeys & KEY_LEFT) ? "key_left_inv.tga" : "key_left.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3533         drawpic_aspect_skin(pos + eX * keysize.x, ((pressedkeys & KEY_BACKWARD) ? "key_backward_inv.tga" : "key_backward.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3534         drawpic_aspect_skin(pos + eX * keysize.x * 2, ((pressedkeys & KEY_RIGHT) ? "key_right_inv.tga" : "key_right.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
3535 }
3536
3537 // Handle chat as a panel (#12)
3538 //
3539 void HUD_Chat(void)
3540 {
3541         if(!autocvar__hud_configure)
3542         {
3543                 if (!autocvar_hud_panel_chat)
3544                 {
3545                         if (!autocvar_con_chatrect)
3546                                 cvar_set("con_chatrect", "0");
3547                         return;
3548                 }
3549                 if(autocvar__con_chat_maximized)
3550                 {
3551                         if(!hud_draw_maximized) return;
3552                 }
3553                 else if(chat_panel_modified)
3554                 {
3555                         panel.update_time = time; // forces reload of panel attributes
3556                         chat_panel_modified = false;
3557                 }
3558         }
3559
3560         HUD_Panel_UpdateCvars();
3561
3562         if(autocvar__con_chat_maximized && !autocvar__hud_configure) // draw at full screen height if maximized
3563         {
3564                 panel_pos.y = panel_bg_border;
3565                 panel_size.y = vid_conheight - panel_bg_border * 2;
3566                 if(panel.current_panel_bg == "0") // force a border when maximized
3567                 {
3568                         string panel_bg;
3569                         panel_bg = strcat(hud_skin_path, "/border_default");
3570                         if(precache_pic(panel_bg) == "")
3571                                 panel_bg = "gfx/hud/default/border_default";
3572                         if(panel.current_panel_bg)
3573                                 strunzone(panel.current_panel_bg);
3574                         panel.current_panel_bg = strzone(panel_bg);
3575                         chat_panel_modified = true;
3576                 }
3577                 panel_bg_alpha = max(0.75, panel_bg_alpha); // force an theAlpha of at least 0.75
3578         }
3579
3580         vector pos, mySize;
3581         pos = panel_pos;
3582         mySize = panel_size;
3583
3584         HUD_Panel_DrawBg(1);
3585
3586         if(panel_bg_padding)
3587         {
3588                 pos += '1 1 0' * panel_bg_padding;
3589                 mySize -= '2 2 0' * panel_bg_padding;
3590         }
3591
3592         if (!autocvar_con_chatrect)
3593                 cvar_set("con_chatrect", "1");
3594
3595         cvar_set("con_chatrect_x", ftos(pos.x/vid_conwidth));
3596         cvar_set("con_chatrect_y", ftos(pos.y/vid_conheight));
3597
3598         cvar_set("con_chatwidth", ftos(mySize.x/vid_conwidth));
3599         cvar_set("con_chat", ftos(floor(mySize.y/autocvar_con_chatsize - 0.5)));
3600
3601         if(autocvar__hud_configure)
3602         {
3603                 vector chatsize;
3604                 chatsize = '1 1 0' * autocvar_con_chatsize;
3605                 cvar_set("con_chatrect_x", "9001"); // over 9000, we'll fake it instead for more control over theAlpha and such
3606                 float i, a;
3607                 for(i = 0; i < autocvar_con_chat; ++i)
3608                 {
3609                         if(i == autocvar_con_chat - 1)
3610                                 a = panel_fg_alpha;
3611                         else
3612                                 a = panel_fg_alpha * floor(((i + 1) * 7 + autocvar_con_chattime)/45);
3613                         drawcolorcodedstring(pos, textShortenToWidth(_("^3Player^7: This is the chat area."), mySize.x, chatsize, stringwidth_colors), chatsize, a, DRAWFLAG_NORMAL);
3614                         pos.y += chatsize.y;
3615                 }
3616         }
3617 }
3618
3619 // Engine info panel (#13)
3620 //
3621 float prevfps;
3622 float prevfps_time;
3623 float framecounter;
3624
3625 float frametimeavg;
3626 float frametimeavg1; // 1 frame ago
3627 float frametimeavg2; // 2 frames ago
3628 void HUD_EngineInfo(void)
3629 {
3630         if(!autocvar__hud_configure)
3631         {
3632                 if(!autocvar_hud_panel_engineinfo) return;
3633         }
3634
3635         HUD_Panel_UpdateCvars();
3636         vector pos, mySize;
3637         pos = panel_pos;
3638         mySize = panel_size;
3639
3640         HUD_Panel_DrawBg(1);
3641         if(panel_bg_padding)
3642         {
3643                 pos += '1 1 0' * panel_bg_padding;
3644                 mySize -= '2 2 0' * panel_bg_padding;
3645         }
3646
3647         float currentTime = gettime(GETTIME_REALTIME);
3648         if(cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage"))
3649         {
3650                 float currentframetime = currentTime - prevfps_time;
3651                 frametimeavg = (frametimeavg + frametimeavg1 + frametimeavg2 + currentframetime)/4; // average three frametimes into framecounter for slightly more stable fps readings :P
3652                 frametimeavg2 = frametimeavg1;
3653                 frametimeavg1 = frametimeavg;
3654
3655                 float weight;
3656                 weight = cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage_new_weight");
3657                 if(currentframetime > 0.0001) // filter out insane values which sometimes seem to occur and throw off the average? If you are getting 10,000 fps or more, then you don't need a framerate counter.
3658                 {
3659                         if(fabs(prevfps - (1/frametimeavg)) > prevfps * cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage_instantupdate_change_threshold")) // if there was a big jump in fps, just force prevfps at current (1/currentframetime) to make big updates instant
3660                                 prevfps = (1/currentframetime);
3661                         prevfps = (1 - weight) * prevfps + weight * (1/frametimeavg); // framecounter just used so there's no need for a new variable, think of it as "frametime average"
3662                 }
3663                 prevfps_time = currentTime;
3664         }
3665         else
3666         {
3667                 framecounter += 1;
3668                 if(currentTime - prevfps_time > autocvar_hud_panel_engineinfo_framecounter_time)
3669                 {
3670                         prevfps = framecounter/(currentTime - prevfps_time);
3671                         framecounter = 0;
3672                         prevfps_time = currentTime;
3673                 }
3674         }
3675
3676         vector color;
3677         color = HUD_Get_Num_Color (prevfps, 100);
3678         drawstring_aspect(pos, sprintf(_("FPS: %.*f"), autocvar_hud_panel_engineinfo_framecounter_decimals, prevfps), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
3679 }
3680
3681 // Info messages panel (#14)
3682 //
3683 #define drawInfoMessage(s) do {                                                                                                                                                                         \
3684         if(autocvar_hud_panel_infomessages_flip)                                                                                                                                                \
3685                 o.x = pos.x + mySize.x - stringwidth(s, true, fontsize);                                                                                                        \
3686         drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL);                                                                                                               \
3687         o.y += fontsize.y;                                                                                                                                                                                              \
3688 } while(0)
3689 void HUD_InfoMessages(void)
3690 {
3691         if(!autocvar__hud_configure)
3692         {
3693                 if(!autocvar_hud_panel_infomessages) return;
3694         }
3695
3696         HUD_Panel_UpdateCvars();
3697         vector pos, mySize;
3698         pos = panel_pos;
3699         mySize = panel_size;
3700
3701         HUD_Panel_DrawBg(1);
3702         if(panel_bg_padding)
3703         {
3704                 pos += '1 1 0' * panel_bg_padding;
3705                 mySize -= '2 2 0' * panel_bg_padding;
3706         }
3707
3708         // always force 5:1 aspect
3709         vector newSize = '0 0 0';
3710         if(mySize.x/mySize.y > 5)
3711         {
3712                 newSize.x = 5 * mySize.y;
3713                 newSize.y = mySize.y;
3714
3715                 pos.x = pos.x + (mySize.x - newSize.x) / 2;
3716         }
3717         else
3718         {
3719                 newSize.y = 1/5 * mySize.x;
3720                 newSize.x = mySize.x;
3721
3722                 pos.y = pos.y + (mySize.y - newSize.y) / 2;
3723         }
3724
3725         mySize = newSize;
3726         entity tm;
3727         vector o;
3728         o = pos;
3729
3730         vector fontsize;
3731         fontsize = '0.20 0.20 0' * mySize.y;
3732
3733         float a;
3734         a = panel_fg_alpha;
3735
3736         string s;
3737         if(!autocvar__hud_configure)
3738         {
3739                 if(spectatee_status && !intermission)
3740                 {
3741                         a = 1;
3742                         if(spectatee_status == -1)
3743                                 s = _("^1Observing");
3744                         else
3745                                 s = sprintf(_("^1Spectating: ^7%s"), GetPlayerName(player_localentnum - 1));
3746                         drawInfoMessage(s);
3747
3748                         if(spectatee_status == -1)
3749                                 s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey("primary fire", "+fire"));
3750                         else
3751                                 s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
3752                         drawInfoMessage(s);
3753
3754                         if(spectatee_status == -1)
3755                                 s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
3756                         else
3757                                 s = sprintf(_("^1Press ^3%s^1 to observe"), getcommandkey("secondary fire", "+fire2"));
3758                         drawInfoMessage(s);
3759
3760                         s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey("server info", "+show_info"));
3761                         drawInfoMessage(s);
3762
3763                         if(gametype == MAPINFO_TYPE_LMS)
3764                         {
3765                                 entity sk;
3766                                 sk = playerslots[player_localnum];
3767                                 if(sk.(scores[ps_primary]) >= 666)
3768                                         s = _("^1Match has already begun");
3769                                 else if(sk.(scores[ps_primary]) > 0)
3770                                         s = _("^1You have no more lives left");
3771                                 else
3772                                         s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
3773                         }
3774                         else
3775                                 s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
3776                         drawInfoMessage(s);
3777
3778                         //show restart countdown:
3779                         if (time < getstatf(STAT_GAMESTARTTIME)) {
3780                                 float countdown;
3781                                 //we need to ceil, otherwise the countdown would be off by .5 when using round()
3782                                 countdown = ceil(getstatf(STAT_GAMESTARTTIME) - time);
3783                                 s = sprintf(_("^1Game starts in ^3%d^1 seconds"), countdown);
3784                                 drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL);
3785                                 o.y += fontsize.y;
3786                         }
3787                 }
3788                 if(warmup_stage && !intermission)
3789                 {
3790                         s = _("^2Currently in ^1warmup^2 stage!");
3791                         drawInfoMessage(s);
3792                 }
3793
3794                 string blinkcolor;
3795                 if(time % 1 >= 0.5)
3796                         blinkcolor = "^1";
3797                 else
3798                         blinkcolor = "^3";
3799
3800                 if(ready_waiting && !intermission && !spectatee_status)
3801                 {
3802                         if(ready_waiting_for_me)
3803                         {
3804                                 if(warmup_stage)
3805                                         s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
3806                                 else
3807                                         s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
3808                         }
3809                         else
3810                         {
3811                                 if(warmup_stage)
3812                                         s = _("^2Waiting for others to ready up to end warmup...");
3813                                 else
3814                                         s = _("^2Waiting for others to ready up...");
3815                         }
3816                         drawInfoMessage(s);
3817                 }
3818                 else if(warmup_stage && !intermission && !spectatee_status)
3819                 {
3820                         s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey("ready", "ready"));
3821                         drawInfoMessage(s);
3822                 }
3823
3824                 if(teamplay && !intermission && !spectatee_status && gametype != MAPINFO_TYPE_CA && teamnagger)
3825                 {
3826                         float ts_min = 0, ts_max = 0;
3827                         tm = teams.sort_next;
3828                         if (tm)
3829                         {
3830                                 for (; tm.sort_next; tm = tm.sort_next)
3831                                 {
3832                                         if(!tm.team_size || tm.team == NUM_SPECTATOR)
3833                                                 continue;
3834                                         if(!ts_min) ts_min = tm.team_size;
3835                                         else ts_min = min(ts_min, tm.team_size);
3836                                         if(!ts_max) ts_max = tm.team_size;
3837                                         else ts_max = max(ts_max, tm.team_size);
3838                                 }
3839                                 if ((ts_max - ts_min) > 1)
3840                                 {
3841                                         s = strcat(blinkcolor, _("Teamnumbers are unbalanced!"));
3842                                         tm = GetTeam(myteam, false);
3843                                         if (tm)
3844                                         if (tm.team != NUM_SPECTATOR)
3845                                         if (tm.team_size == ts_max)
3846                                                 s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey("team menu", "menu_showteamselect"), blinkcolor));
3847                                         drawInfoMessage(s);
3848                                 }
3849                         }
3850                 }
3851         }
3852         else
3853         {
3854                 s = _("^7Press ^3ESC ^7to show HUD options.");
3855                 drawInfoMessage(s);
3856                 s = _("^3Doubleclick ^7a panel for panel-specific options.");
3857                 drawInfoMessage(s);
3858                 s = _("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and");
3859                 drawInfoMessage(s);
3860                 s = _("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments.");
3861                 drawInfoMessage(s);
3862         }
3863 }
3864
3865 // Physics panel (#15)
3866 //
3867 vector acc_prevspeed;
3868 float acc_prevtime, acc_avg, top_speed, top_speed_time;
3869 void HUD_Physics(void)
3870 {
3871         if(!autocvar__hud_configure)
3872         {
3873                 if(!autocvar_hud_panel_physics) return;
3874                 if(spectatee_status == -1 && (autocvar_hud_panel_physics == 1 || autocvar_hud_panel_physics == 3)) return;
3875                 if(autocvar_hud_panel_physics == 3 && !(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
3876         }
3877
3878         HUD_Panel_UpdateCvars();
3879
3880         draw_beginBoldFont();
3881
3882         HUD_Panel_DrawBg(1);
3883         if(panel_bg_padding)
3884         {
3885                 panel_pos += '1 1 0' * panel_bg_padding;
3886                 panel_size -= '2 2 0' * panel_bg_padding;
3887         }
3888
3889         float acceleration_progressbar_scale = 0;
3890         if(autocvar_hud_panel_physics_progressbar && autocvar_hud_panel_physics_acceleration_progressbar_scale > 1)
3891                 acceleration_progressbar_scale = autocvar_hud_panel_physics_acceleration_progressbar_scale;
3892
3893         float text_scale;
3894         if (autocvar_hud_panel_physics_text_scale <= 0)
3895                 text_scale = 1;
3896         else
3897                 text_scale = min(autocvar_hud_panel_physics_text_scale, 1);
3898
3899         //compute speed
3900         float speed, conversion_factor;
3901         string unit;
3902
3903         switch(autocvar_hud_panel_physics_speed_unit)
3904         {
3905                 default:
3906                 case 1:
3907                         unit = _(" qu/s");
3908                         conversion_factor = 1.0;
3909                         break;
3910                 case 2:
3911                         unit = _(" m/s");
3912                         conversion_factor = 0.0254;
3913                         break;
3914                 case 3:
3915                         unit = _(" km/h");
3916                         conversion_factor = 0.0254 * 3.6;
3917                         break;
3918                 case 4:
3919                         unit = _(" mph");
3920                         conversion_factor = 0.0254 * 3.6 * 0.6213711922;
3921                         break;
3922                 case 5:
3923                         unit = _(" knots");
3924                         conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
3925                         break;
3926         }
3927
3928         vector vel = (csqcplayer ? csqcplayer.velocity : pmove_vel);
3929
3930         float max_speed = floor( autocvar_hud_panel_physics_speed_max * conversion_factor + 0.5 );
3931         if (autocvar__hud_configure)
3932                 speed = floor( max_speed * 0.65 + 0.5 );
3933         else if(autocvar_hud_panel_physics_speed_vertical)
3934                 speed = floor( vlen(vel) * conversion_factor + 0.5 );
3935         else
3936                 speed = floor( vlen(vel - vel.z * '0 0 1') * conversion_factor + 0.5 );
3937
3938         //compute acceleration
3939         float acceleration, f;
3940         if (autocvar__hud_configure)
3941                 acceleration = autocvar_hud_panel_physics_acceleration_max * 0.3;
3942         else
3943         {
3944                 // 1 m/s = 0.0254 qu/s; 1 g = 9.80665 m/s^2
3945                 f = time - acc_prevtime;
3946                 if(autocvar_hud_panel_physics_acceleration_vertical)
3947                         acceleration = (vlen(vel) - vlen(acc_prevspeed));
3948                 else
3949                         acceleration = (vlen(vel - '0 0 1' * vel.z) - vlen(acc_prevspeed - '0 0 1' * acc_prevspeed.z));
3950
3951                 acceleration = acceleration * (1 / max(0.0001, f)) * (0.0254 / 9.80665);
3952
3953                 acc_prevspeed = vel;
3954                 acc_prevtime = time;
3955
3956                 f = bound(0, f * 10, 1);
3957                 acc_avg = acc_avg * (1 - f) + acceleration * f;
3958         }
3959
3960         //compute layout
3961         float panel_ar = panel_size.x/panel_size.y;
3962         vector speed_offset = '0 0 0', acceleration_offset = '0 0 0';
3963         if (panel_ar >= 5 && !acceleration_progressbar_scale)
3964         {
3965                 panel_size.x *= 0.5;
3966                 if (autocvar_hud_panel_physics_flip)
3967                         speed_offset.x = panel_size.x;
3968                 else
3969                         acceleration_offset.x = panel_size.x;
3970         }
3971         else
3972         {
3973                 panel_size.y *= 0.5;
3974                 if (autocvar_hud_panel_physics_flip)
3975                         speed_offset.y = panel_size.y;
3976                 else
3977                         acceleration_offset.y = panel_size.y;
3978         }
3979         float speed_baralign, acceleration_baralign;
3980         if (autocvar_hud_panel_physics_baralign == 1)
3981                 acceleration_baralign = speed_baralign = 1;
3982     else if(autocvar_hud_panel_physics_baralign == 4)
3983                 acceleration_baralign = speed_baralign = 2;
3984         else if (autocvar_hud_panel_physics_flip)
3985         {
3986                 acceleration_baralign = (autocvar_hud_panel_physics_baralign == 2);
3987                 speed_baralign = (autocvar_hud_panel_physics_baralign == 3);
3988         }
3989         else
3990         {
3991                 speed_baralign = (autocvar_hud_panel_physics_baralign == 2);
3992                 acceleration_baralign = (autocvar_hud_panel_physics_baralign == 3);
3993         }
3994         if (autocvar_hud_panel_physics_acceleration_progressbar_mode == 0)
3995                 acceleration_baralign = 3; //override hud_panel_physics_baralign value for acceleration
3996
3997         //draw speed
3998         if(speed)
3999         if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 2)
4000                 HUD_Panel_DrawProgressBar(panel_pos + speed_offset, panel_size, "progressbar", speed/max_speed, 0, speed_baralign, autocvar_hud_progressbar_speed_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
4001         vector tmp_offset = '0 0 0', tmp_size = '0 0 0';
4002         if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 2)
4003         {
4004                 tmp_size.x = panel_size.x * 0.75;
4005                 tmp_size.y = panel_size.y * text_scale;
4006                 if (speed_baralign)
4007                         tmp_offset.x = panel_size.x - tmp_size.x;
4008                 //else
4009                         //tmp_offset_x = 0;
4010                 tmp_offset.y = (panel_size.y - tmp_size.y) / 2;
4011                 drawstring_aspect(panel_pos + speed_offset + tmp_offset, ftos(speed), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
4012
4013                 //draw speed unit
4014                 if (speed_baralign)
4015                         tmp_offset.x = 0;
4016                 else
4017                         tmp_offset.x = tmp_size.x;
4018                 if (autocvar_hud_panel_physics_speed_unit_show)
4019                 {
4020                         //tmp_offset_y = 0;
4021                         tmp_size.x = panel_size.x * (1 - 0.75);
4022                         tmp_size.y = panel_size.y * 0.4 * text_scale;
4023                         tmp_offset.y = (panel_size.y * 0.4 - tmp_size.y) / 2;
4024                         drawstring_aspect(panel_pos + speed_offset + tmp_offset, unit, tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
4025                 }
4026         }
4027
4028         //compute and draw top speed
4029         if (autocvar_hud_panel_physics_topspeed)
4030         if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 2)
4031         {
4032                 if (autocvar__hud_configure)
4033                 {
4034                         top_speed = floor( max_speed * 0.75 + 0.5 );
4035                         f = 1;
4036                 }
4037                 else
4038                 {
4039                         if (speed >= top_speed)
4040                         {
4041                                 top_speed = speed;
4042                                 top_speed_time = time;
4043                         }
4044                         if (top_speed != 0)
4045                         {
4046                                 f = max(1, autocvar_hud_panel_physics_topspeed_time);
4047                                 // divide by f to make it start from 1
4048                                 f = cos( ((time - top_speed_time) / f) * PI/2 );
4049                         }
4050             else //hide top speed 0, it would be stupid
4051                                 f = 0;
4052                 }
4053                 if (f > 0)
4054                 {
4055                         //top speed progressbar peak
4056                         if(speed < top_speed)
4057                         if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 2)
4058                         {
4059                                 float peak_offsetX;
4060                                 vector peak_size = '0 0 0';
4061                                 if (speed_baralign == 0)
4062                                         peak_offsetX = min(top_speed, max_speed)/max_speed * panel_size.x;
4063                 else if (speed_baralign == 1)
4064                                         peak_offsetX = (1 - min(top_speed, max_speed)/max_speed) * panel_size.x;
4065                 else // if (speed_baralign == 2)
4066                     peak_offsetX = min(top_speed, max_speed)/max_speed * panel_size.x * 0.5;
4067                                 peak_size.x = floor(panel_size.x * 0.01 + 1.5);
4068                 peak_size.y = panel_size.y;
4069                 if (speed_baralign == 2) // draw two peaks, on both sides
4070                 {
4071                     drawfill(panel_pos + speed_offset + eX * (0.5 * panel_size.x + peak_offsetX - peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
4072                     drawfill(panel_pos + speed_offset + eX * (0.5 * panel_size.x - peak_offsetX + peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
4073                 }
4074                 else
4075                     drawfill(panel_pos + speed_offset + eX * (peak_offsetX - peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
4076                         }
4077
4078                         //top speed
4079                         tmp_offset.y = panel_size.y * 0.4;
4080                         tmp_size.x = panel_size.x * (1 - 0.75);
4081                         tmp_size.y = (panel_size.y - tmp_offset.y) * text_scale;
4082                         tmp_offset.y += (panel_size.y - tmp_offset.y - tmp_size.y) / 2;
4083                         drawstring_aspect(panel_pos + speed_offset + tmp_offset, ftos(top_speed), tmp_size, '1 0 0', f * panel_fg_alpha, DRAWFLAG_NORMAL);
4084                 }
4085                 else
4086                         top_speed = 0;
4087         }
4088
4089         //draw acceleration
4090         if(acceleration)
4091         if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 3)
4092         {
4093                 vector progressbar_color;
4094                 if(acceleration < 0)
4095                         progressbar_color = autocvar_hud_progressbar_acceleration_neg_color;
4096                 else
4097                         progressbar_color = autocvar_hud_progressbar_acceleration_color;
4098
4099                 f = acceleration/autocvar_hud_panel_physics_acceleration_max;
4100                 if (autocvar_hud_panel_physics_acceleration_progressbar_nonlinear)
4101                         f = sqrt(f);
4102
4103                 if (acceleration_progressbar_scale) // allow progressbar to go out of panel bounds
4104                 {
4105                         tmp_size = acceleration_progressbar_scale * panel_size.x * eX + panel_size.y * eY;
4106
4107                         if (acceleration_baralign == 1)
4108                                 tmp_offset.x = panel_size.x - tmp_size.x;
4109                         else if (acceleration_baralign == 2 || acceleration_baralign == 3)
4110                                 tmp_offset.x = (panel_size.x - tmp_size.x) / 2;
4111                         else
4112                                 tmp_offset.x = 0;
4113                         tmp_offset.y = 0;
4114                 }
4115                 else
4116                 {
4117                         tmp_size = panel_size;
4118                         tmp_offset = '0 0 0';
4119                 }
4120
4121                 HUD_Panel_DrawProgressBar(panel_pos + acceleration_offset + tmp_offset, tmp_size, "accelbar", f, 0, acceleration_baralign, progressbar_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
4122         }
4123         tmp_size.x = panel_size.x;
4124         tmp_size.y = panel_size.y * text_scale;
4125         tmp_offset.x = 0;
4126         tmp_offset.y = (panel_size.y - tmp_size.y) / 2;
4127         if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 3)
4128                 drawstring_aspect(panel_pos + acceleration_offset + tmp_offset, strcat(ftos_decimals(acceleration, 2), "g"), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
4129
4130         draw_endBoldFont();
4131 }
4132
4133 // CenterPrint (#16)
4134 //
4135 const float CENTERPRINT_MAX_MSGS = 10;
4136 const float CENTERPRINT_MAX_ENTRIES = 50;
4137 const float CENTERPRINT_SPACING = 0.7;
4138 float cpm_index;
4139 string centerprint_messages[CENTERPRINT_MAX_MSGS];
4140 float centerprint_msgID[CENTERPRINT_MAX_MSGS];
4141 float centerprint_time[CENTERPRINT_MAX_MSGS];
4142 float centerprint_expire_time[CENTERPRINT_MAX_MSGS];
4143 float centerprint_countdown_num[CENTERPRINT_MAX_MSGS];
4144 float centerprint_showing;
4145
4146 void centerprint_generic(float new_id, string strMessage, float duration, float countdown_num)
4147 {
4148         //printf("centerprint_generic(%d, '%s^7', %d, %d);\n", new_id, strMessage, duration, countdown_num);
4149         float i, j;
4150
4151         if(strMessage == "" && new_id == 0)
4152                 return;
4153
4154         // strip trailing newlines
4155         j = strlen(strMessage) - 1;
4156         while(substring(strMessage, j, 1) == "\n" && j >= 0)
4157                 --j;
4158         if (j < strlen(strMessage) - 1)
4159                 strMessage = substring(strMessage, 0, j + 1);
4160
4161         if(strMessage == "" && new_id == 0)
4162                 return;
4163
4164         // strip leading newlines
4165         j = 0;
4166         while(substring(strMessage, j, 1) == "\n" && j < strlen(strMessage))
4167                 ++j;
4168         if (j > 0)
4169                 strMessage = substring(strMessage, j, strlen(strMessage) - j);
4170
4171         if(strMessage == "" && new_id == 0)
4172                 return;
4173
4174         if (!centerprint_showing)
4175                 centerprint_showing = true;
4176
4177         for (i=0, j=cpm_index; i<CENTERPRINT_MAX_MSGS; ++i, ++j)
4178         {
4179                 if (j == CENTERPRINT_MAX_MSGS)
4180                         j = 0;
4181                 if (new_id && new_id == centerprint_msgID[j])
4182                 {
4183                         if (strMessage == "" && centerprint_messages[j] != "" && centerprint_countdown_num[j] == 0)
4184                         {
4185                                 // fade out the current msg (duration and countdown_num are ignored)
4186                                 centerprint_time[j] = min(5, autocvar_hud_panel_centerprint_fade_out);
4187                                 if (centerprint_expire_time[j] > time + min(5, autocvar_hud_panel_centerprint_fade_out) || centerprint_expire_time[j] < time)
4188                                         centerprint_expire_time[j] = time + min(5, autocvar_hud_panel_centerprint_fade_out);
4189                                 return;
4190                         }
4191                         break; // found a msg with the same id, at position j
4192                 }
4193         }
4194
4195         if (i == CENTERPRINT_MAX_MSGS)
4196         {
4197                 // a msg with the same id was not found, add the msg at the next position
4198                 --cpm_index;
4199                 if (cpm_index == -1)
4200                         cpm_index = CENTERPRINT_MAX_MSGS - 1;
4201                 j = cpm_index;
4202         }
4203         if(centerprint_messages[j])
4204                 strunzone(centerprint_messages[j]);
4205         centerprint_messages[j] = strzone(strMessage);
4206         centerprint_msgID[j] = new_id;
4207         if (duration < 0)
4208         {
4209                 centerprint_time[j] = -1;
4210                 centerprint_expire_time[j] = time;
4211         }
4212         else
4213         {
4214                 if(duration == 0)
4215                         duration = max(1, autocvar_hud_panel_centerprint_time);
4216                 centerprint_time[j] = duration;
4217                 centerprint_expire_time[j] = time + duration;
4218         }
4219         centerprint_countdown_num[j] = countdown_num;
4220 }
4221
4222 void centerprint_hud(string strMessage)
4223 {
4224         centerprint_generic(0, strMessage, autocvar_hud_panel_centerprint_time, 0);
4225 }
4226
4227 void reset_centerprint_messages(void)
4228 {
4229         float i;
4230         for (i=0; i<CENTERPRINT_MAX_MSGS; ++i)
4231         {
4232                 centerprint_expire_time[i] = 0;
4233                 centerprint_time[i] = 1;
4234                 centerprint_msgID[i] = 0;
4235                 if(centerprint_messages[i])
4236                         strunzone(centerprint_messages[i]);
4237                 centerprint_messages[i] = string_null;
4238         }
4239 }
4240 float hud_configure_cp_generation_time;
4241 void HUD_CenterPrint (void)
4242 {
4243         if(!autocvar__hud_configure)
4244         {
4245                 if(!autocvar_hud_panel_centerprint) return;
4246
4247                 if (hud_configure_prev && hud_configure_prev != -1)
4248                         reset_centerprint_messages();
4249         }
4250         else
4251         {
4252                 if (!hud_configure_prev)
4253                         reset_centerprint_messages();
4254                 if (time > hud_configure_cp_generation_time)
4255                 {
4256                         float r;
4257                         r = random();
4258                         if (r > 0.75)
4259                                 centerprint_generic(floor(r*1000), strcat(sprintf("^3Countdown message at time %s", seconds_tostring(time)), ", seconds left: ^COUNT"), 1, 10);
4260                         else if (r > 0.5)
4261                                 centerprint_generic(0, sprintf("^1Multiline message at time %s that\n^1lasts longer than normal", seconds_tostring(time)), 20, 0);
4262                         else
4263                                 centerprint_hud(sprintf("Message at time %s", seconds_tostring(time)));
4264                         hud_configure_cp_generation_time = time + 1 + random()*4;
4265                 }
4266         }
4267
4268         // this panel fades only when the menu does
4269         float hud_fade_alpha_save = 0;
4270         if(scoreboard_fade_alpha)
4271         {
4272                 hud_fade_alpha_save = hud_fade_alpha;
4273                 hud_fade_alpha = 1 - autocvar__menu_alpha;
4274         }
4275         HUD_Panel_UpdateCvars();
4276
4277         if(scoreboard_fade_alpha)
4278         {
4279                 hud_fade_alpha = hud_fade_alpha_save;
4280
4281                 // move the panel below the scoreboard
4282                 if (scoreboard_bottom >= 0.96 * vid_conheight)
4283                         return;
4284                 vector target_pos;
4285
4286                 target_pos = eY * scoreboard_bottom + eX * 0.5 * (vid_conwidth - panel_size.x);
4287
4288                 if(target_pos.y > panel_pos.y)
4289                 {
4290                         panel_pos = panel_pos + (target_pos - panel_pos) * sqrt(scoreboard_fade_alpha);
4291                         panel_size.y = min(panel_size.y, vid_conheight - scoreboard_bottom);
4292                 }
4293         }
4294
4295         HUD_Panel_DrawBg(1);
4296
4297         if (!centerprint_showing)
4298                 return;
4299
4300         if(panel_bg_padding)
4301         {
4302                 panel_pos += '1 1 0' * panel_bg_padding;
4303                 panel_size -= '2 2 0' * panel_bg_padding;
4304         }
4305
4306         float entries, height;
4307         vector fontsize;
4308         // entries = bound(1, floor(CENTERPRINT_MAX_ENTRIES * 4 * panel_size_y/panel_size_x), CENTERPRINT_MAX_ENTRIES);
4309         // height = panel_size_y/entries;
4310         // fontsize = '1 1 0' * height;
4311         height = vid_conheight/50 * autocvar_hud_panel_centerprint_fontscale;
4312         fontsize = '1 1 0' * height;
4313         entries = bound(1, floor(panel_size.y/height), CENTERPRINT_MAX_ENTRIES);
4314
4315         float i, j, k, n, g;
4316         float a, sz, align, current_msg_posY = 0, msg_size;
4317         vector pos;
4318         string ts;
4319         float all_messages_expired = true;
4320
4321         pos = panel_pos;
4322         if (autocvar_hud_panel_centerprint_flip)
4323                 pos.y += panel_size.y;
4324         align = bound(0, autocvar_hud_panel_centerprint_align, 1);
4325         for (g=0, i=0, j=cpm_index; i<CENTERPRINT_MAX_MSGS; ++i, ++j)
4326         {
4327                 if (j == CENTERPRINT_MAX_MSGS)
4328                         j = 0;
4329                 if (centerprint_expire_time[j] <= time)
4330                 {
4331                         if (centerprint_countdown_num[j] && centerprint_time[j] > 0)
4332                         {
4333                                 centerprint_countdown_num[j] = centerprint_countdown_num[j] - 1;
4334                                 if (centerprint_countdown_num[j] == 0)
4335                                         continue;
4336                                 centerprint_expire_time[j] = centerprint_expire_time[j] + centerprint_time[j];
4337                         }
4338                         else if(centerprint_time[j] != -1)
4339                                 continue;
4340                 }
4341
4342                 all_messages_expired = false;
4343
4344                 // fade the centerprint_hud in/out
4345                 if(centerprint_time[j] < 0)  // Expired but forced. Expire time is the fade-in time.
4346                         a = (time - centerprint_expire_time[j]) / max(0.0001, autocvar_hud_panel_centerprint_fade_in);
4347                 else if(centerprint_expire_time[j] - autocvar_hud_panel_centerprint_fade_out > time)  // Regularily printed. Not fading out yet.
4348                         a = (time - (centerprint_expire_time[j] - centerprint_time[j])) / max(0.0001, autocvar_hud_panel_centerprint_fade_in);
4349                 else // Expiring soon, so fade it out.
4350                         a = (centerprint_expire_time[j] - time) / max(0.0001, autocvar_hud_panel_centerprint_fade_out);
4351
4352                 // while counting down show it anyway in order to hold the current message position
4353                 if (a <= 0.5/255.0 && centerprint_countdown_num[j] == 0)  // Guaranteed invisible - don't show.
4354                         continue;
4355                 if (a > 1)
4356                         a = 1;
4357
4358                 // set the size from fading in/out before subsequent fading
4359                 sz = autocvar_hud_panel_centerprint_fade_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_minfontsize);
4360
4361                 // also fade it based on positioning
4362                 if(autocvar_hud_panel_centerprint_fade_subsequent)
4363                 {
4364                         a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passone_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passone))), 1); // pass one: all messages after the first have half theAlpha
4365                         a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passtwo_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passtwo))), 1); // pass two: after that, gradually lower theAlpha even more for each message
4366                 }
4367                 a *= panel_fg_alpha;
4368
4369                 // finally set the size based on the new theAlpha from subsequent fading
4370                 sz = sz * (autocvar_hud_panel_centerprint_fade_subsequent_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_subsequent_minfontsize));
4371                 drawfontscale = sz * '1 1 0';
4372
4373                 if (centerprint_countdown_num[j])
4374                         n = tokenizebyseparator(strreplace("^COUNT", count_seconds(centerprint_countdown_num[j]), centerprint_messages[j]), "\n");
4375                 else
4376                         n = tokenizebyseparator(centerprint_messages[j], "\n");
4377
4378                 if (autocvar_hud_panel_centerprint_flip)
4379                 {
4380                         // check if the message can be entirely shown
4381                         for(k = 0; k < n; ++k)
4382                         {
4383                                 getWrappedLine_remaining = argv(k);
4384                                 while(getWrappedLine_remaining)
4385                                 {
4386                                         ts = getWrappedLine(panel_size.x * sz, fontsize, stringwidth_colors);
4387                                         if (ts != "")
4388                                                 pos.y -= fontsize.y;
4389                                         else
4390                                                 pos.y -= fontsize.y * CENTERPRINT_SPACING/2;
4391                                 }
4392                         }
4393                         current_msg_posY = pos.y; // save starting pos (first line) of the current message
4394                 }
4395
4396                 msg_size = pos.y;
4397                 for(k = 0; k < n; ++k)
4398                 {
4399                         getWrappedLine_remaining = argv(k);
4400                         while(getWrappedLine_remaining)
4401                         {
4402                                 ts = getWrappedLine(panel_size.x * sz, fontsize, stringwidth_colors);
4403                                 if (ts != "")
4404                                 {
4405                                         if (align)
4406                                                 pos.x = panel_pos.x + (panel_size.x - stringwidth(ts, true, fontsize)) * align;
4407                                         if (a > 0.5/255.0)  // Otherwise guaranteed invisible - don't show. This is checked a second time after some multiplications with other factors were done so temporary changes of these cannot cause flicker.
4408                                                 drawcolorcodedstring(pos + eY * 0.5 * (1 - sz) * fontsize.y, ts, fontsize, a, DRAWFLAG_NORMAL);
4409                                         pos.y += fontsize.y;
4410                                 }
4411                                 else
4412                                         pos.y += fontsize.y * CENTERPRINT_SPACING/2;
4413                         }
4414                 }
4415
4416                 ++g; // move next position number up
4417
4418                 msg_size = pos.y - msg_size;
4419                 if (autocvar_hud_panel_centerprint_flip)
4420                 {
4421                         pos.y = current_msg_posY - CENTERPRINT_SPACING * fontsize.y;
4422                         if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages
4423                                 pos.y += (msg_size + CENTERPRINT_SPACING * fontsize.y) * (1 - sqrt(sz));
4424
4425                         if (pos.y < panel_pos.y) // check if the next message can be shown
4426                         {
4427                                 drawfontscale = '1 1 0';
4428                                 return;
4429                         }
4430                 }
4431                 else
4432                 {
4433                         pos.y += CENTERPRINT_SPACING * fontsize.y;
4434                         if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages
4435                                 pos.y -= (msg_size + CENTERPRINT_SPACING * fontsize.y) * (1 - sqrt(sz));
4436
4437                         if(pos.y > panel_pos.y + panel_size.y - fontsize.y) // check if the next message can be shown
4438                         {
4439                                 drawfontscale = '1 1 0';
4440                                 return;
4441                         }
4442                 }
4443         }
4444         drawfontscale = '1 1 0';
4445         if (all_messages_expired)
4446         {
4447                 centerprint_showing = false;
4448                 reset_centerprint_messages();
4449         }
4450 }
4451
4452 // Buffs (#18)
4453 //
4454 void HUD_Buffs(void)
4455 {
4456         float buffs = getstati(STAT_BUFFS, 0, 24);
4457         if(!autocvar__hud_configure)
4458         {
4459                 if(!autocvar_hud_panel_buffs) return;
4460                 if(spectatee_status == -1) return;
4461                 if(getstati(STAT_HEALTH) <= 0) return;
4462                 if(!buffs) return;
4463         }
4464         else
4465         {
4466                 buffs = Buff_Type_first.items; // force first buff
4467         }
4468
4469         float b = 0; // counter to tell other functions that we have buffs
4470         entity e;
4471         string s = "";
4472         for(e = Buff_Type_first; e; e = e.enemy) if(buffs & e.items)
4473         {
4474                 ++b;
4475                 string o = strcat(rgb_to_hexcolor(Buff_Color(e.items)), Buff_PrettyName(e.items));
4476                 if(s == "")
4477                         s = o;
4478                 else
4479                         s = strcat(s, " ", o);
4480         }
4481
4482         HUD_Panel_UpdateCvars();
4483
4484         draw_beginBoldFont();
4485
4486         vector pos, mySize;
4487         pos = panel_pos;
4488         mySize = panel_size;
4489
4490         HUD_Panel_DrawBg(bound(0, b, 1));
4491         if(panel_bg_padding)
4492         {
4493                 pos += '1 1 0' * panel_bg_padding;
4494                 mySize -= '2 2 0' * panel_bg_padding;
4495         }
4496
4497         //float panel_ar = mySize_x/mySize_y;
4498         //float is_vertical = (panel_ar < 1);
4499         //float buff_iconalign = autocvar_hud_panel_buffs_iconalign;
4500         vector buff_offset = '0 0 0';
4501
4502         for(e = Buff_Type_first; e; e = e.enemy) if(buffs & e.items)
4503         {
4504                 //DrawNumIcon(pos + buff_offset, mySize, shield, "shield", is_vertical, buff_iconalign, '1 1 1', 1);
4505                 drawcolorcodedstring_aspect(pos + buff_offset, s, mySize, panel_fg_alpha * 0.5, DRAWFLAG_NORMAL);
4506         }
4507
4508         draw_endBoldFont();
4509 }
4510
4511
4512 /*
4513 ==================
4514 Main HUD system
4515 ==================
4516 */
4517
4518 void HUD_Reset (void)
4519 {
4520         // reset gametype specific icons
4521         if(gametype == MAPINFO_TYPE_CTF)
4522                 HUD_Mod_CTF_Reset();
4523 }
4524
4525 void HUD_Main (void)
4526 {
4527         int i;
4528         // global hud theAlpha fade
4529         if(menu_enabled == 1)
4530                 hud_fade_alpha = 1;
4531         else
4532                 hud_fade_alpha = (1 - autocvar__menu_alpha);
4533
4534         if(scoreboard_fade_alpha)
4535                 hud_fade_alpha = (1 - scoreboard_fade_alpha);
4536
4537         HUD_Configure_Frame();
4538
4539         if(intermission == 2) // no hud during mapvote
4540                 hud_fade_alpha = 0;
4541
4542         // panels that we want to be active together with the scoreboard
4543         // they must fade only when the menu does
4544         if(scoreboard_fade_alpha == 1)
4545         {
4546                 (panel = HUD_PANEL(CENTERPRINT)).panel_draw();
4547                 return;
4548         }
4549
4550         if(!autocvar__hud_configure && !hud_fade_alpha)
4551                 return;
4552
4553         // Drawing stuff
4554         if (hud_skin_prev != autocvar_hud_skin)
4555         {
4556                 if (hud_skin_path)
4557                         strunzone(hud_skin_path);
4558                 hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin));
4559                 if (hud_skin_prev)
4560                         strunzone(hud_skin_prev);
4561                 hud_skin_prev = strzone(autocvar_hud_skin);
4562         }
4563
4564     current_player = player_localentnum;
4565
4566         // draw the dock
4567         if(autocvar_hud_dock != "" && autocvar_hud_dock != "0")
4568         {
4569                 float f;
4570                 vector color;
4571                 float hud_dock_color_team = autocvar_hud_dock_color_team;
4572                 if((teamplay) && hud_dock_color_team) {
4573                         if(autocvar__hud_configure && myteam == NUM_SPECTATOR)
4574                                 color = '1 0 0' * hud_dock_color_team;
4575                         else
4576                                 color = myteamcolors * hud_dock_color_team;
4577                 }
4578                 else if(autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && hud_dock_color_team) {
4579                         color = '1 0 0' * hud_dock_color_team;
4580                 }
4581                 else
4582                 {
4583                         string hud_dock_color = autocvar_hud_dock_color;
4584                         if(hud_dock_color == "shirt") {
4585                                 f = stof(getplayerkeyvalue(current_player - 1, "colors"));
4586                                 color = colormapPaletteColor(floor(f / 16), 0);
4587                         }
4588                         else if(hud_dock_color == "pants") {
4589                                 f = stof(getplayerkeyvalue(current_player - 1, "colors"));
4590                                 color = colormapPaletteColor(f % 16, 1);
4591                         }
4592                         else
4593                                 color = stov(hud_dock_color);
4594                 }
4595
4596                 string pic;
4597                 pic = strcat(hud_skin_path, "/", autocvar_hud_dock);
4598                 if(precache_pic(pic) == "") {
4599                         pic = strcat(hud_skin_path, "/dock_medium");
4600                         if(precache_pic(pic) == "") {
4601                                 pic = "gfx/hud/default/dock_medium";
4602                         }
4603                 }
4604                 drawpic('0 0 0', pic, eX * vid_conwidth + eY * vid_conheight, color, autocvar_hud_dock_alpha * hud_fade_alpha, DRAWFLAG_NORMAL); // no aspect ratio forcing on dock...
4605         }
4606
4607         // cache the panel order into the panel_order array
4608         if(autocvar__hud_panelorder != hud_panelorder_prev) {
4609                 for(i = 0; i < HUD_PANEL_NUM; ++i)
4610                         panel_order[i] = -1;
4611                 string s = "";
4612                 float p_num, warning = false;
4613                 float argc = tokenize_console(autocvar__hud_panelorder);
4614                 if (argc > HUD_PANEL_NUM)
4615                         warning = true;
4616                 //first detect wrong/missing panel numbers
4617                 for(i = 0; i < HUD_PANEL_NUM; ++i) {
4618                         p_num = stof(argv(i));
4619                         if (p_num >= 0 && p_num < HUD_PANEL_NUM) { //correct panel number?
4620                                 if (panel_order[p_num] == -1) //found for the first time?
4621                                         s = strcat(s, ftos(p_num), " ");
4622                                 panel_order[p_num] = 1; //mark as found
4623                         }
4624                         else
4625                                 warning = true;
4626                 }
4627                 for(i = 0; i < HUD_PANEL_NUM; ++i) {
4628                         if (panel_order[i] == -1) {
4629                                 warning = true;
4630                                 s = strcat(s, ftos(i), " "); //add missing panel number
4631                         }
4632                 }
4633                 if (warning)
4634                         dprint("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n");
4635
4636                 cvar_set("_hud_panelorder", s);
4637                 if(hud_panelorder_prev)
4638                         strunzone(hud_panelorder_prev);
4639                 hud_panelorder_prev = strzone(s);
4640
4641                 //now properly set panel_order
4642                 tokenize_console(s);
4643                 for(i = 0; i < HUD_PANEL_NUM; ++i) {
4644                         panel_order[i] = stof(argv(i));
4645                 }
4646         }
4647
4648         hud_draw_maximized = 0;
4649         // draw panels in order specified by panel_order array
4650         for(i = HUD_PANEL_NUM - 1; i >= 0; --i)
4651                 (panel = hud_panel[panel_order[i]]).panel_draw();
4652
4653         hud_draw_maximized = 1; // panels that may be maximized must check this var
4654         // draw maximized panels on top
4655         if(hud_panel_radar_maximized)
4656                 (panel = HUD_PANEL(RADAR)).panel_draw();
4657         if(autocvar__con_chat_maximized)
4658                 (panel = HUD_PANEL(CHAT)).panel_draw();
4659
4660         HUD_Configure_PostDraw();
4661
4662         hud_configure_prev = autocvar__hud_configure;
4663 }