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