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