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