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