8c012c5d73d6290ff33020d9238c12e51aea731c
[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         vector dX, dY;
18         vector width, height;
19         vector bW, bH;
20         //pic = draw_UseSkinFor(pic);
21         width = eX * theSize_x;
22         height = eY * theSize_y;
23         if(theSize_x <= theBorderSize_x * 2)
24         {
25                 // not wide enough... draw just left and right then
26                 bW = eX * (0.25 * theSize_x / (theBorderSize_x * 2));
27                 if(theSize_y <= theBorderSize_y * 2)
28                 {
29                         // not high enough... draw just corners
30                         bH = eY * (0.25 * theSize_y / (theBorderSize_y * 2));
31                         drawsubpic(theOrigin,                 width * 0.5 + height * 0.5, pic, '0 0 0',           bW + bH, theColor, theAlpha, 0);
32                         drawsubpic(theOrigin + width   * 0.5, width * 0.5 + height * 0.5, pic, eX - bW,           bW + bH, theColor, theAlpha, 0);
33                         drawsubpic(theOrigin + height  * 0.5, width * 0.5 + height * 0.5, pic, eY - bH,           bW + bH, theColor, theAlpha, 0);
34                         drawsubpic(theOrigin + theSize * 0.5, width * 0.5 + height * 0.5, pic, eX + eY - bW - bH, bW + bH, theColor, theAlpha, 0);
35                 }
36                 else
37                 {
38                         dY = theBorderSize_x * eY;
39                         drawsubpic(theOrigin,                             width * 0.5          +     dY, pic, '0 0    0',           '0 0.25 0' + bW, theColor, theAlpha, 0);
40                         drawsubpic(theOrigin + width * 0.5,               width * 0.5          +     dY, pic, '0 0    0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0);
41                         drawsubpic(theOrigin                        + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0',           '0 0.5  0' + bW, theColor, theAlpha, 0);
42                         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);
43                         drawsubpic(theOrigin               + height - dY, width * 0.5          +     dY, pic, '0 0.75 0',           '0 0.25 0' + bW, theColor, theAlpha, 0);
44                         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);
45                 }
46         }
47         else
48         {
49                 if(theSize_y <= theBorderSize_y * 2)
50                 {
51                         // not high enough... draw just top and bottom then
52                         bH = eY * (0.25 * theSize_y / (theBorderSize_y * 2));
53                         dX = theBorderSize_x * eX;
54                         drawsubpic(theOrigin,                                         dX + height * 0.5, pic, '0    0 0',           '0.25 0 0' + bH, theColor, theAlpha, 0);
55                         drawsubpic(theOrigin + dX,                        width - 2 * dX + height * 0.5, pic, '0.25 0 0',           '0.5  0 0' + bH, theColor, theAlpha, 0);
56                         drawsubpic(theOrigin + width - dX,                            dX + height * 0.5, pic, '0.75 0 0',           '0.25 0 0' + bH, theColor, theAlpha, 0);
57                         drawsubpic(theOrigin              + height * 0.5,             dX + height * 0.5, pic, '0    0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0);
58                         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);
59                         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);
60                 }
61                 else
62                 {
63                         dX = theBorderSize_x * eX;
64                         dY = theBorderSize_x * eY;
65                         drawsubpic(theOrigin,                                        dX          +     dY, pic, '0    0    0', '0.25 0.25 0', theColor, theAlpha, 0);
66                         drawsubpic(theOrigin                  + dX,      width - 2 * dX          +     dY, pic, '0.25 0    0', '0.5  0.25 0', theColor, theAlpha, 0);
67                         drawsubpic(theOrigin          + width - dX,                  dX          +     dY, pic, '0.75 0    0', '0.25 0.25 0', theColor, theAlpha, 0);
68                         drawsubpic(theOrigin          + dY,                          dX + height - 2 * dY, pic, '0    0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
69                         drawsubpic(theOrigin          + dY         + dX, width - 2 * dX + height - 2 * dY, pic, '0.25 0.25 0', '0.5  0.5  0', theColor, theAlpha, 0);
70                         drawsubpic(theOrigin          + dY + width - dX,             dX + height - 2 * dY, pic, '0.75 0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
71                         drawsubpic(theOrigin + height - dY,                          dX          +     dY, pic, '0    0.75 0', '0.25 0.25 0', theColor, theAlpha, 0);
72                         drawsubpic(theOrigin + height - dY         + dX, width - 2 * dX          +     dY, pic, '0.25 0.75 0', '0.5  0.25 0', theColor, theAlpha, 0);
73                         drawsubpic(theOrigin + height - dY + width - dX,             dX          +     dY, pic, '0.75 0.75 0', '0.25 0.25 0', theColor, theAlpha, 0);
74                 }
75         }
76 }
77
78 // draw HUD element with image from gfx/hud/hud_skin/foo.tga if it exists, otherwise gfx/hud/default/foo.tga
79 void drawpic_skin(vector pos, string pic, vector sz, vector color, float alpha, float drawflag) {
80         drawpic(pos, strcat("gfx/hud/", hud_skin, "/", pic), sz, color, alpha, drawflag);
81 }
82
83 // return HUD background color
84 vector HUD_GetBgColor()
85 {
86         vector color;
87         if (teamplay)
88                 GetTeamRGB(myteam) * hud_color_bg_team;
89         else {
90                 // allow custom HUD colors in non-teamgames
91                 color_x = cvar("hud_color_bg_r");
92                 color_y = cvar("hud_color_bg_g");
93                 color_z = cvar("hud_color_bg_b");
94         }
95         return color;
96 }
97
98 // return accuracy text color
99 vector HUD_AccuracyColor(float accuracy)
100 {
101         vector rgb;
102         float yellow_accuracy = cvar("hud_accuracy_yellow"); // value at which this function returns yellow
103         if(accuracy >= 100) {
104                 rgb_x = 0;
105                 rgb_y = 1;
106         }
107         else if(accuracy > yellow_accuracy) {
108                 rgb_x = 1 - (accuracy-yellow_accuracy)/(100-yellow_accuracy); // red value between 1 -> 0
109                 rgb_y = 1;
110         }
111         else {
112                 rgb_x = 1;
113                 rgb_y = accuracy/yellow_accuracy; // green value between 0 -> 1
114         }
115         rgb_z = 0;
116         return rgb;
117 }
118
119 // draw number in the XSCALE font
120 void HUD_DrawXNum (vector pos, float num, float digits, float showsign, float lettersize, vector rgb, float highlighted, float stroke, float alpha, float dflags)
121 {
122         float l, i;
123         string str, tmp, l_length;
124         float minus, plus;
125         vector vsize, num_color;
126
127         vsize_x = vsize_y = lettersize;
128         vsize_z = 0;
129
130         // showsign 1: always prefix with minus sign (useful in race distribution display)
131         // showsign 2: always prefix with plus sign (useful in race distribution display)
132         // showsign 3: prefix with minus sign if negative, plus sign if positive (useful in score distribution display)
133
134         if((showsign == 2 && num >= 0) || (num > 0 && showsign == 3))
135         {
136                 plus = true;
137                 pos_x -= lettersize;
138         } else
139                 plus = false;
140
141         if(num < 0 || (num < 0 && showsign == 3) || (showsign == 1 && num <= 0))
142         {
143                 minus = true;
144                 num = -num;
145                 pos_x -= lettersize;
146         } else
147                 minus = false;
148
149         if(digits < 0)
150         {
151                 tmp = ftos(num);
152                 digits = -digits;
153                 str = strcat(substring("0000000000", 0, digits - strlen(tmp)), tmp);
154         } else
155                 str = ftos(num);
156
157         l = strlen(str);
158         l_length = ftos(l);
159
160         if(l > digits)
161         {
162                 str = substring(str, l-digits, 999);
163                 l = strlen(str);
164         } else if(l < digits)
165                 pos_x += (digits-l) * lettersize;
166
167         if (highlighted == 1) {
168                 vector hl_size;
169                 hl_size_x = vsize_x * l + vsize_x * 0.2;
170                 hl_size_y = vsize_y * 1.1;
171                 hl_size_z = 0;
172                 if(minus)
173                         hl_size_x = hl_size_x + vsize_x;
174
175                 vector hl_pos;
176                 hl_pos_x = pos_x - lettersize/10;
177                 hl_pos_y = pos_y - lettersize/20;
178                 hl_pos_z = 0;
179
180                 drawpic_skin(hl_pos, strcat("highlight_", l_length), hl_size, '1 1 1', alpha, dflags);
181         }
182
183         if (stroke == 1)
184                 num_color = '1 1 1';
185         else
186                 num_color = rgb;
187
188         if(minus)
189         {
190                 if (stroke == 1)
191                         drawpic_skin(pos, "num_minus_stroke", vsize, rgb, alpha, dflags);
192                 drawpic_skin(pos, "num_minus", vsize, num_color, alpha, dflags);
193                 pos_x += lettersize;
194         } else if(plus)
195         {
196                 if (stroke == 1)
197                         drawpic_skin(pos, "num_plus_stroke", vsize, rgb, alpha, dflags);
198                 drawpic_skin(pos, "num_plus", vsize, num_color, alpha, dflags);
199                 pos_x += lettersize;
200         }
201
202         for(i = 0; i < l; ++i)
203         {
204                 tmp = substring(str, i, 1);
205                 if (stroke == 1)
206                         drawpic_skin(pos, strcat("num_", tmp, "_stroke"), vsize, rgb, alpha, dflags);
207                 drawpic_skin(pos, strcat("num_", tmp), vsize, num_color, alpha, dflags);
208                 pos_x += lettersize;
209         }
210 }
211
212 // color the number differently based on how big it is (used in the health/armor panel)
213 void HUD_DrawXNum_Colored (vector pos, float x, float digits, float lettersize, float alpha)
214 {
215         vector color;
216         if(x > 200) {
217                 color_x = 0;
218                 color_y = 1;
219                 color_z = 0;
220         }
221         else if(x > 150) {
222                 color_x = 0.4 - (x-150)*0.02 * 0.4; //red value between 0.4 -> 0
223                 color_y = 0.9 + (x-150)*0.02 * 0.1; // green value between 0.9 -> 1
224                 color_z = 0;
225         }
226         else if(x > 100) {
227                 color_x = 1 - (x-100)*0.02 * 0.6; //red value between 1 -> 0.4
228                 color_y = 1 - (x-100)*0.02 * 0.1; // green value between 1 -> 0.9
229                 color_z = 1 - (x-100)*0.02; // blue value between 1 -> 0
230         }
231         else if(x > 50) {
232                 color_x = 1;
233                 color_y = 1;
234                 color_z = 0.2 + (x-50)*0.02 * 0.8; // blue value between 0.2 -> 1
235         }
236         else if(x > 20) {
237                 color_x = 1;
238                 color_y = (x-20)*90/27/100; // green value between 0 -> 1
239                 color_z = (x-20)*90/27/100 * 0.2; // blue value between 0 -> 0.2
240         }
241         else {
242                 color_x = 1;
243                 color_y = 0;
244                 color_z = 0;
245         }
246         HUD_DrawXNum(pos, x, digits, 0, lettersize, color, 0, 0, alpha, DRAWFLAG_NORMAL);
247 }
248
249 float stringwidth_colors(string s, vector theSize)
250 {
251         return stringwidth(s, TRUE, theSize);
252 }
253
254 float stringwidth_nocolors(string s, vector theSize)
255 {
256         return stringwidth(s, FALSE, theSize);
257 }
258
259 #define CENTERPRINT_MAX_LINES 30
260 string centerprint_messages[CENTERPRINT_MAX_LINES];
261 float centerprint_width[CENTERPRINT_MAX_LINES];
262 vector centerprint_start;
263 float centerprint_expire;
264 float centerprint_num;
265 float centerprint_offset_hint;
266 vector centerprint_fontsize;
267
268 void centerprint(string strMessage)
269 {
270         float i, j, n, hcount;
271         string s;
272
273         centerprint_fontsize = HUD_GetFontsize("scr_centersize");
274
275         centerprint_expire = min(centerprint_expire, time); // if any of the returns happens, this message will fade out
276
277         if(cvar("scr_centertime") <= 0)
278                 return;
279
280         if(strMessage == "")
281                 return;
282
283         // strip trailing newlines
284         j = strlen(strMessage) - 1;
285         while(substring(strMessage, j, 1) == "\n" && j >= 0)
286                 j = j - 1;
287         strMessage = substring(strMessage, 0, j + 1);
288
289         if(strMessage == "")
290                 return;
291
292         // strip leading newlines and remember them, they are a hint that the message should be lower on the screen
293         j = 0;
294         while(substring(strMessage, j, 1) == "\n" && j < strlen(strMessage))
295                 j = j + 1;
296         strMessage = substring(strMessage, j, strlen(strMessage) - j);
297         centerprint_offset_hint = j;
298
299         if(strMessage == "")
300                 return;
301
302         // if we get here, we have a message. Initialize its height.
303         centerprint_num = 0;
304
305         n = tokenizebyseparator(strMessage, "\n");
306         i = hcount = 0;
307         for(j = 0; j < n; ++j)
308         {
309                 getWrappedLine_remaining = argv(j);
310                 while(getWrappedLine_remaining)
311                 {
312                         s = getWrappedLine(vid_conwidth * 0.75, centerprint_fontsize, stringwidth_colors);
313                         if(centerprint_messages[i])
314                                 strunzone(centerprint_messages[i]);
315                         centerprint_messages[i] = strzone(s);
316                         centerprint_width[i] = stringwidth(s, TRUE, centerprint_fontsize);
317                         ++i;
318
319                         // half height for empty lines looks better
320                         if(s == "")
321                                 hcount += 0.5;
322                         else
323                                 hcount += 1;
324
325                         if(i >= CENTERPRINT_MAX_LINES)
326                                 break;
327                 }
328         }
329
330         float h, havail;
331         h = centerprint_fontsize_y*hcount;
332
333         havail = vid_conheight;
334         if(cvar("con_chatpos") < 0)
335                 havail -= (-cvar("con_chatpos") + cvar("con_chat")) * cvar("con_chatsize"); // avoid overlapping chat
336         if(havail > vid_conheight - 70)
337                 havail = vid_conheight - 70; // avoid overlapping HUD
338
339         centerprint_start_x = 0;
340
341 #if 0
342         float forbiddenmin, forbiddenmax, allowedmin, allowedmax, preferred;
343
344         // here, the centerprint would cover the crosshair. REALLY BAD.
345         forbiddenmin = vid_conheight * 0.5 - h - 16;
346         forbiddenmax = vid_conheight * 0.5 + 16;
347
348         allowedmin = scoreboard_bottom;
349         allowedmax = havail - h;
350         preferred = (havail - h)/2;
351
352
353         // possible orderings (total: 4! / 4 = 6)
354         //  allowedmin allowedmax forbiddenmin forbiddenmax
355         //  forbiddenmin forbiddenmax allowedmin allowedmax
356         if(allowedmax < forbiddenmin || allowedmin > forbiddenmax)
357         {
358                 // forbidden doesn't matter in this case
359                 centerprint_start_y = bound(allowedmin, preferred, allowedmax);
360         }
361         //  allowedmin forbiddenmin allowedmax forbiddenmax
362         else if(allowedmin < forbiddenmin && allowedmax < forbiddenmax)
363         {
364                 centerprint_start_y = bound(allowedmin, preferred, forbiddenmin);
365         }
366         //  allowedmin forbiddenmin forbiddenmax allowedmax
367         else if(allowedmin < forbiddenmin)
368         {
369                 // make sure the forbidden zone is not covered
370                 if(preferred > (forbiddenmin + forbiddenmax) * 0.5)
371                         centerprint_start_y = bound(allowedmin, preferred, forbiddenmin);
372                 else
373                         centerprint_start_y = bound(forbiddenmax, preferred, allowedmin);
374         }
375         //  forbiddenmin allowedmin allowedmax forbiddenmax
376         else if(allowedmax < forbiddenmax)
377         {
378                 // it's better to leave the allowed zone (overlap with scoreboard) than
379                 // to cover the forbidden zone (crosshair)
380                 if(preferred > (forbiddenmin + forbiddenmax) * 0.5)
381                         centerprint_start_y = forbiddenmax;
382                 else
383                         centerprint_start_y = forbiddenmin;
384         }
385         //  forbiddenmin allowedmin forbiddenmax allowedmax
386         else
387         {
388                 centerprint_start_y = bound(forbiddenmax, preferred, allowedmax);
389         }
390 #else
391         centerprint_start_y =
392                 min(
393                         max(
394                                 max(scoreboard_bottom, vid_conheight * 0.5 + 16),
395                                 (havail - h)/2
396                         ),
397                         havail - h
398                 );
399 #endif
400
401         centerprint_num = i;
402         centerprint_expire = time + cvar("scr_centertime");
403 }
404
405 void HUD_DrawCenterPrint (void)
406 {
407         float i;
408         vector pos;
409         string ts;
410         float a;
411
412         //if(time > centerprint_expire)
413         //      return;
414
415         //a = bound(0, 1 - 2 * (time - centerprint_expire), 1);
416         a = bound(0, 1 - 4 * (time - centerprint_expire), 1);
417         //sz = 1.2 / (a + 0.2);
418
419         if(a <= 0)
420                 return;
421
422         pos = centerprint_start;
423         for (i=0; i<centerprint_num; i = i + 1)
424         {
425                 pos_x = (vid_conwidth - centerprint_width[i]) * 0.5;
426                 ts = centerprint_messages[i];
427                 if (ts != "")
428                 {
429                         dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
430                         drawcolorcodedstring(pos, ts, centerprint_fontsize, a, DRAWFLAG_NORMAL);
431                         //  - '0 0.5 0' * (sz - 1) * centerprint_fontsize_x - '0.5 0 0' * (sz - 1) * centerprint_width[i] * centerprint_fontsize_y, centerprint_fontsize * sz
432                         pos_y = pos_y + centerprint_fontsize_y;
433                 }
434                 else
435                         // half height for empty lines looks better
436                         pos_y = pos_y + centerprint_fontsize_y * 0.5;
437         }
438 }
439
440 void drawstringright(vector position, string text, vector scale, vector rgb, float alpha, float flag)
441 {
442         position_x -= 2 / 3 * strlen(text) * scale_x;
443         drawstring(position, text, scale, rgb, alpha, flag);
444 }
445
446 void drawstringcenter(vector position, string text, vector scale, vector rgb, float alpha, float flag)
447 {
448         position_x = 0.5 * (vid_conwidth - 0.6025 * strlen(text) * scale_x);
449         drawstring(position, text, scale, rgb, alpha, flag);
450 }
451
452 // return the string of the given race place
453 string race_PlaceName(float pos) {
454         if(pos == 1)
455                 return "1st";
456         else if(pos == 2)
457                 return "2nd";
458         else if(pos == 3)
459                 return "3rd";
460         else
461                 return strcat(ftos(pos), "th");
462 }
463
464 // return the string of the onscreen race timer
465 string MakeRaceString(float cp, float mytime, float histime, float lapdelta, string hisname)
466 {
467         string col;
468         string timestr;
469         string cpname;
470         string lapstr;
471         lapstr = "";
472
473         if(histime == 0) // goal hit
474         {
475                 if(mytime > 0)
476                 {
477                         timestr = strcat("+", ftos_decimals(+mytime, TIME_DECIMALS));
478                         col = "^1";
479                 }
480                 else if(mytime == 0)
481                 {
482                         timestr = "+0.0";
483                         col = "^3";
484                 }
485                 else
486                 {
487                         timestr = strcat("-", ftos_decimals(-mytime, TIME_DECIMALS));
488                         col = "^2";
489                 }
490
491                 if(lapdelta > 0)
492                 {
493                         lapstr = strcat(" (-", ftos(lapdelta), "L)");
494                         col = "^2";
495                 }
496                 else if(lapdelta < 0)
497                 {
498                         lapstr = strcat(" (+", ftos(-lapdelta), "L)");
499                         col = "^1";
500                 }
501         }
502         else if(histime > 0) // anticipation
503         {
504                 if(mytime >= histime)
505                         timestr = strcat("+", ftos_decimals(mytime - histime, TIME_DECIMALS));
506                 else
507                         timestr = TIME_ENCODED_TOSTRING(TIME_ENCODE(histime));
508                 col = "^3";
509         }
510         else
511                 col = "^7";
512
513         if(cp == 254)
514                 cpname = "Start line";
515         else if(cp == 255)
516                 cpname = "Finish line";
517         else if(cp)
518                 cpname = strcat("Intermediate ", ftos(cp));
519         else
520                 cpname = "Finish line";
521
522         if(histime < 0)
523                 return strcat(col, cpname);
524         else if(hisname == "")
525                 return strcat(col, cpname, " (", timestr, ")");
526         else
527                 return strcat(col, cpname, " (", timestr, " ", strcat(hisname, col, lapstr), ")");
528 }
529
530 // Check if the given name already exist in race rankings? In that case, where? (otherwise return 0)
531 float race_CheckName(string net_name) {
532         float i;
533         for (i=RANKINGS_CNT-1;i>=0;--i)
534                 if(grecordholder[i] == net_name)
535                         return i+1;
536         return 0;
537 }
538
539 /*
540 ==================
541 HUD panels
542 ==================
543 */
544
545 string HUD_Panel_GetName(float id)
546 {
547         switch(id) {
548                 case 0: return "weaponicons"; break;
549                 case 1: return "inventory"; break;
550                 case 2: return "powerups"; break;
551                 case 3: return "healtharmor"; break;
552                 case 4: return "notify"; break;
553                 case 5: return "timer"; break;
554                 case 6: return "radar"; break;
555                 case 7: return "score"; break;
556                 case 8: return "racetimer"; break;
557                 case 9: return "vote"; break;
558                 case 10: return "pressedkeys"; break;
559                 default: return "";
560         }
561 }
562
563 // Save the config
564 void HUD_Panel_ExportCfg(string cfgname)
565 {
566         float fh;
567         fh = fopen(strcat("hud_", hud_skin, "_", cfgname, ".cfg"), FILE_WRITE);
568         if(fh >= 0)
569         {
570                 float i;
571                 for (i = 0; i < panel_cnt; ++i)
572                 {
573                         fputs(fh, strcat("seta hud_", HUD_Panel_GetName(i), " ", ftos(cvar(strcat("hud_", HUD_Panel_GetName(i))))));
574                         fputs(fh, strcat("seta hud_", HUD_Panel_GetName(i), "_pos \"", cvar_string(strcat("hud_", HUD_Panel_GetName(i), "_pos")), "\""));
575                         fputs(fh, strcat("seta hud_", HUD_Panel_GetName(i), "_size \"", cvar_string(strcat("hud_", HUD_Panel_GetName(i), "_size")), "\""));
576                         fputs(fh, strcat("seta hud_", HUD_Panel_GetName(i), "_bg \"", cvar_string(strcat("hud_", HUD_Panel_GetName(i), "_bg")), "\""));
577                         fputs(fh, strcat("seta hud_", HUD_Panel_GetName(i), "_bg_color \"", cvar_string(strcat("hud_", HUD_Panel_GetName(i), "_bg_color")), "\""));
578                         fputs(fh, strcat("seta hud_", HUD_Panel_GetName(i), "_bg_alpha ", ftos(cvar(strcat("hud_", HUD_Panel_GetName(i), "_bg_alpha")))));
579                         fputs(fh, strcat("seta hud_", HUD_Panel_GetName(i), "_bg_border ", ftos(cvar(strcat("hud_", HUD_Panel_GetName(i), "_bg_border")))));
580                         fputs(fh, "");
581                 }
582
583                 print("^2Successfully exported to hud_", hud_skin, "_", cfgname, ".cfg");
584         }
585         fclose(fh);
586 }
587
588 vector HUD_Panel_CheckLimitSize(float id, vector mySize)
589 {
590         switch(id) {
591                 case 0: 
592                         mySize_x = max(mySize_y * (1/10), mySize_x); // at least 1/10 * height
593                         mySize_y = max(mySize_x * (1/26), mySize_y); // at least 1/26 * width
594                         break;
595                 case 1: 
596                         mySize_x = max(mySize_y * 0.7, mySize_x); // at least 0.7 * height
597                         break;
598                 case 2: 
599                         mySize_x = max(mySize_y * 1.5, mySize_x); // at least 2 * height
600                         break;
601                 case 3: 
602                         mySize_x = max(mySize_y * 2, mySize_x); // at least 2 * height
603                         break;
604                 case 5: 
605                         mySize_x = max(mySize_y * 2, mySize_x); // at least 2 * height
606                         break;
607                 case 7: 
608                         mySize_y = (1/4) * mySize_x; // 1/4 * width
609                         break;
610                 case 8: 
611                         mySize_y = (1/4) * mySize_x; // 1/4 * width
612                         break;
613                 case 9: 
614                         mySize_y = (1/4) * mySize_x; // 1/4 * width
615                         break;
616                 case 10: 
617                         mySize_y = 0.5898 * mySize_x; // 0.5898 * width, reason: bg has weird dimensions...
618                         break;
619         }
620         return mySize;
621 }
622
623 // return active status of panel
624 float HUD_Panel_CheckActive(float id)
625 {
626         if (cvar_or(strcat("hud_", HUD_Panel_GetName(id)), 1))
627                 return 1;
628         return 0;
629 }
630
631 // return size of given panel
632 vector HUD_Panel_GetSize(float id)
633 {
634         vector mySize;
635         mySize = stov(cvar_string(strcat("hud_", HUD_Panel_GetName(id), "_size")));
636
637         mySize = eX * mySize_x * vid_conwidth + eY * mySize_y * vid_conheight;
638
639         return mySize;
640 }
641
642 // return pos of given panel
643 vector HUD_Panel_GetPos(float id)
644 {
645         vector pos;
646         pos = stov(cvar_string(strcat("hud_", HUD_Panel_GetName(id), "_pos")));
647
648         pos = eX * pos_x * vid_conwidth + eY * pos_y * vid_conheight;
649
650         if (pos_x < 0)
651                 pos_x = vid_conwidth + pos_x;
652         if (pos_y < 0)
653                 pos_y = vid_conheight + pos_y;
654         return pos;
655 }
656
657 float HUD_Panel_GetBorder(float id)
658 {
659         float border;
660         border = cvar(strcat("hud_", HUD_Panel_GetName(id), "_bg_border"));
661         if(!border)
662                 border = cvar("hud_bg_border");
663         return border;
664 }
665
666 // draw the background/borders
667 void HUD_Panel_DrawBg(float id, vector pos, vector mySize)
668 {
669         if(!hud_configure && cvar_string(strcat("hud_", HUD_Panel_GetName(id), "_bg")) == "0")
670                 return;
671
672         string bg;
673         bg = cvar_string(strcat("hud_", HUD_Panel_GetName(id), "_bg"));
674         if(bg == "")
675                 bg = cvar_string("hud_bg");
676
677         if(bg != "0")
678         {
679                 float border;
680                 border = HUD_Panel_GetBorder(id);
681
682                 vector color;
683                 if(cvar_string(strcat("hud_", HUD_Panel_GetName(id), "_bg_color")) != "")
684                         color = stov(cvar_string(strcat("hud_", HUD_Panel_GetName(id), "_bg_color")));
685                 else
686                         color = stov(cvar_string("hud_bg_color"));
687
688                 float alpha;
689                 alpha = cvar(strcat("hud_", HUD_Panel_GetName(id), "_bg_alpha"));
690                 if(!alpha)
691                         alpha = cvar("hud_bg_alpha");
692                 if(hud_configure)
693                         alpha = max(cvar("hud_configure_bg_minalpha"), alpha);
694
695                 draw_BorderPicture(pos - '1 1 0' * border, strcat("gfx/hud/", hud_skin, "/", bg), mySize + '1 1 0' * 2 * border, color, alpha, '1 1 0' * (border/BORDER_MULTIPLIER));
696         }
697 }
698
699 vector HUD_Panel_GetProgressBarColor(string item)
700 {
701         return stov(cvar_string(strcat("hud_progressbar_", item, "_color")));
702 }
703
704 float resizeCorner; // 1 = topleft, 2 = topright, 3 = bottomleft, 4 = bottomright
705 // check if resize will result in panel being moved into another panel. If so, return snapped vector, otherwise return the given vector
706 vector HUD_Panel_CheckResize(float id, vector myPos, vector mySize)
707 {
708         float i;
709
710         vector myTarget;
711         myTarget = mySize;
712
713         vector targPos;
714         vector targSize;
715         vector myCenter;
716         vector targCenter;
717
718         for (i = 0; i < panel_cnt; ++i) {
719                 if(i == id || !HUD_Panel_CheckActive(i))
720                         continue;
721
722                 targPos = HUD_Panel_GetPos(i);
723                 targSize = HUD_Panel_GetSize(i);
724
725                 targPos = HUD_Panel_GetPos(i) - '1 1 0' * HUD_Panel_GetBorder(id);
726                 targSize = HUD_Panel_GetSize(i) + '2 2 0' * HUD_Panel_GetBorder(id);
727
728                 if(myPos_y + mySize_y < targPos_y)
729                         continue;
730                 if(myPos_y > targPos_y + targSize_y)
731                         continue;
732
733                 if(myPos_x + mySize_x < targPos_x)
734                         continue;
735                 if(myPos_x > targPos_x + targSize_x)
736                         continue;
737
738                 // OK, there IS a collision.
739
740                 myCenter_x = myPos_x + 0.5 * mySize_x;
741                 myCenter_y = myPos_y + 0.5 * mySize_y;
742
743                 targCenter_x = targPos_x + 0.5 * targSize_x;
744                 targCenter_y = targPos_y + 0.5 * targSize_y;
745
746                 float k, y;
747                 if(myCenter_x < targCenter_x && myCenter_y < targCenter_y && resizeCorner != 1) // top left (of target panel)
748                 {
749                         if(myPos_x + mySize_x - targPos_x < myPos_y + mySize_y - targPos_y) // push it to the side
750                                 myTarget_x = targPos_x - myPos_x;
751                         else // push it upwards
752                                 myTarget_y = targPos_y - myPos_y;
753                 }
754                 else if(myCenter_x > targCenter_x && myCenter_y < targCenter_y && resizeCorner != 2) // top right
755                 {
756                         if(targPos_x + targSize_x - myPos_x < myPos_y + mySize_y - targPos_y) // push it to the side
757                                 myTarget_x = targPos_x + targSize_x;
758                         else // push it upwards
759                                 myTarget_y = targPos_y - myPos_y;
760                 }
761                 else if(myCenter_x < targCenter_x && myCenter_y > targCenter_y) // bottom left
762                 {
763                         if(myPos_x + mySize_x - targPos_x < targPos_y + targSize_y - myPos_y) // push it to the side
764                                 myTarget_x = targPos_x - myPos_x;
765                         else // push it downwards
766                                 myTarget_y = targPos_y + targSize_y;
767                 }
768                 else if(myCenter_x > targCenter_x && myCenter_y > targCenter_y) // bottom right
769                 {
770                         if(targPos_x + targSize_x - myPos_x < targPos_y + targSize_y - myPos_y) // push it to the side
771                                 myTarget_x = targPos_x + targSize_x;
772                         else // push it downwards
773                                 myTarget_y = targPos_y + targSize_y;
774                 }
775         }
776
777         return myTarget;
778 }
779
780 // TODO: checkResize
781 float HUD_Panel_SetSize(float id, vector mySize)
782 {
783         float didntresize;
784
785         vector oldSize;
786         oldSize = stov(cvar_string(strcat("hud_", HUD_Panel_GetName(id), "_size")));
787
788         vector myPos;
789         myPos = HUD_Panel_GetPos(id);
790
791         // check for collisions
792         if(cvar("hud_configure_checkcollisions"))
793                 mySize = HUD_Panel_CheckResize(id, HUD_Panel_GetPos(id), mySize);
794
795         mySize_x = bound(0.025 * vid_conwidth, mySize_x, vid_conwidth - myPos_x);
796         mySize_y = bound(0.025 * vid_conheight, mySize_y, vid_conheight - myPos_y);
797
798         // cap against panel's own limits
799         mySize = HUD_Panel_CheckLimitSize(id, mySize);
800
801         //mySize_x = bound(0.025 * vid_conwidth, mySize_x, vid_conwidth);
802         //mySize_y = bound(0.025 * vid_conheight, mySize_y, vid_conheight);
803
804         // TODO: is this needed?
805         // this is to check if (and how) SetPos should be called
806         if(mySize_x == oldSize_x && mySize_y == oldSize_y)
807                 didntresize = 3; // didnt resize either
808         else if(mySize_x == oldSize_x && mySize_y != oldSize_y)
809                 didntresize = 2; // resized Y
810         else if(mySize_y == oldSize_y && mySize_x != oldSize_x)
811                 didntresize = 1; // resized X
812
813         string s;
814         s = strcat(ftos(mySize_x/vid_conwidth), " ", ftos(mySize_y/vid_conheight));
815         cvar_set(strcat("hud_", HUD_Panel_GetName(id), "_size"), s);
816         return didntresize;
817 }
818
819 // check if move will result in panel being moved into another panel. If so, return snapped vector, otherwise return the given vector
820 vector HUD_Panel_CheckMove(float id, vector myPos, vector mySize)
821 {
822         float i;
823
824         vector myTarget;
825         myTarget = myPos;
826
827         vector targPos;
828         vector targSize;
829         vector myCenter;
830         vector targCenter;
831
832         for (i = 0; i < panel_cnt; ++i) {
833                 if(i == id || !HUD_Panel_CheckActive(i))
834                         continue;
835
836                 targPos = HUD_Panel_GetPos(i) - '1 1 0' * HUD_Panel_GetBorder(id);
837                 targSize = HUD_Panel_GetSize(i) + '2 2 0' * HUD_Panel_GetBorder(id);
838
839                 if(myPos_y + mySize_y < targPos_y)
840                         continue;
841                 if(myPos_y > targPos_y + targSize_y)
842                         continue;
843
844                 if(myPos_x + mySize_x < targPos_x)
845                         continue;
846                 if(myPos_x > targPos_x + targSize_x)
847                         continue;
848
849                 // OK, there IS a collision.
850
851                 myCenter_x = myPos_x + 0.5 * mySize_x;
852                 myCenter_y = myPos_y + 0.5 * mySize_y;
853
854                 targCenter_x = targPos_x + 0.5 * targSize_x;
855                 targCenter_y = targPos_y + 0.5 * targSize_y;
856
857                 float k, y;
858                 if(myCenter_x < targCenter_x && myCenter_y < targCenter_y) // top left (of the target panel)
859                 {
860                         if(myPos_x + mySize_x - targPos_x < myPos_y + mySize_y - targPos_y) // push it to the side
861                                 myTarget_x = targPos_x - mySize_x;
862                         else // push it upwards
863                                 myTarget_y = targPos_y - mySize_y;
864                 }
865                 else if(myCenter_x > targCenter_x && myCenter_y < targCenter_y) // top right
866                 {
867                         if(targPos_x + targSize_x - myPos_x < myPos_y + mySize_y - targPos_y) // push it to the side
868                                 myTarget_x = targPos_x + targSize_x;
869                         else // push it upwards
870                                 myTarget_y = targPos_y - mySize_y;
871                 }
872                 else if(myCenter_x < targCenter_x && myCenter_y > targCenter_y) // bottom left
873                 {
874                         if(myPos_x + mySize_x - targPos_x < targPos_y + targSize_y - myPos_y) // push it to the side
875                                 myTarget_x = targPos_x - mySize_x;
876                         else // push it downwards
877                                 myTarget_y = targPos_y + targSize_y;
878                 }
879                 else if(myCenter_x > targCenter_x && myCenter_y > targCenter_y) // bottom right
880                 {
881                         if(targPos_x + targSize_x - myPos_x < targPos_y + targSize_y - myPos_y) // push it to the side
882                                 myTarget_x = targPos_x + targSize_x;
883                         else // push it downwards
884                                 myTarget_y = targPos_y + targSize_y;
885                 }
886         }
887
888         return myTarget;
889 }
890
891 void HUD_Panel_SetPos(float id, vector pos, float didntresize)
892 {
893         vector oldPos;
894         oldPos = HUD_Panel_GetPos(id);
895         if(didntresize == 2)
896                 pos_x = oldPos_x;
897         else if(didntresize == 1)
898                 pos_y = oldPos_y;
899         else if(didntresize == 3)
900                 return;
901
902         vector mySize;
903         mySize = HUD_Panel_GetSize(id);
904
905         if(cvar("hud_configure_checkcollisions"))
906                 pos = HUD_Panel_CheckMove(id, pos, mySize);
907
908         pos_x = bound(0, pos_x, vid_conwidth - mySize_x);
909         pos_y = bound(0, pos_y, vid_conheight - mySize_y);
910
911         if (pos_x + 0.5 * mySize_x > 0.5 * vid_conwidth)
912                 pos_x = pos_x - vid_conwidth;
913         if (pos_y + 0.5 * mySize_y > 0.5 * vid_conheight)
914                 pos_y = pos_y - vid_conheight;
915
916         string s;
917         s = strcat(ftos(pos_x/vid_conwidth), " ", ftos(pos_y/vid_conheight));
918
919         cvar_set(strcat("hud_", HUD_Panel_GetName(id), "_pos"), s);
920 }
921
922 float mouseClicked;
923 float prevMouseClicked; // previous state
924 float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
925 {
926         prevMouseClicked = mouseClicked;
927         if(nPrimary == K_MOUSE1)
928         {
929                 if(bInputType == 0) { // key pressed
930                         mouseClicked = 1;
931                         return true;
932                 }
933                 if(bInputType == 1) {// key released
934                         mouseClicked = 0;
935                         return true;
936                 }
937         }
938         return false;
939 }
940
941 vector mousepos, mouseprevpos;
942 vector panel_click_distance; // mouse cursor distance from the top left corner of the panel (saved only upon a click)
943 vector panel_click_pos; // panel pos (saved only upon a click)
944 vector panel_click_size; // panel size (saved only upon a click)
945 float highlightedPanel;
946 float highlightedAction; // 0 = nothing, 1 = move, 2 = resize
947 void HUD_Panel_Mouse()
948 {
949         if(mouseClicked == 0) {
950                 highlightedPanel = -1;
951                 highlightedAction = 0;
952         }
953
954         mousepos = mousepos + getmousepos();
955
956         mousepos_x = bound(0, mousepos_x, vid_conwidth);
957         mousepos_y = bound(0, mousepos_y, vid_conheight);
958
959         drawpic_skin(mousepos, "gfx/menu/default/cursor.tga", '32 32 0', '1 1 1', 1, hud_alpha_fg);
960
961         if(mouseClicked)
962         {
963                 float i, border;
964                 vector panelPos;
965                 vector panelSize;
966
967                 for(i = 0; i <= panel_cnt; ++i)
968                 {
969                         panelPos = HUD_Panel_GetPos(i);
970                         panelSize = HUD_Panel_GetSize(i);
971                         border = HUD_Panel_GetBorder(i);
972                         if(prevMouseClicked == 0) {
973                                 // move
974                                 if(mousepos_x >= panelPos_x && mousepos_y >= panelPos_y && mousepos_x <= panelPos_x + panelSize_x && mousepos_y <= panelPos_y + panelSize_y)
975                                 {
976                                         highlightedPanel = i;
977                                         highlightedAction = 1;
978                                 }
979                                 // resize from topleft border
980                                 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)
981                                 {
982                                         highlightedPanel = i;
983                                         highlightedAction = 2;
984                                         resizeCorner = 1;
985                                 }
986                                 // resize from topright border
987                                 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)
988                                 {
989                                         highlightedPanel = i;
990                                         highlightedAction = 2;
991                                         resizeCorner = 2;
992                                 }
993                                 // resize from bottomleft border
994                                 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)
995                                 {
996                                         highlightedPanel = i;
997                                         highlightedAction = 2;
998                                         resizeCorner = 3;
999                                 }
1000                                 // resize from bottomright border
1001                                 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)
1002                                 {
1003                                         highlightedPanel = i;
1004                                         highlightedAction = 2;
1005                                         resizeCorner = 4;
1006                                 }
1007                         }
1008
1009                         if(highlightedPanel == i)
1010                         {
1011                                 if(prevMouseClicked == 0)
1012                                 {
1013                                         panel_click_distance = mousepos - panelPos;
1014                                         panel_click_pos = panelPos;
1015                                         panel_click_size = panelSize;
1016                                 }
1017
1018                                 if(highlightedAction == 1)
1019                                         HUD_Panel_SetPos(i, mousepos - panel_click_distance, 0);
1020                                 else if(highlightedAction == 2)
1021                                 {
1022                                         float didntresize; // panel too big/too small, dont resize (also has to affect moving)
1023                                         if(resizeCorner == 1) {
1024                                                 didntresize = HUD_Panel_SetSize(i, panelSize + mouseprevpos - mousepos);
1025                                                 HUD_Panel_SetPos(i, mousepos - panel_click_distance, didntresize);
1026                                         }
1027                                         if(resizeCorner == 2) {
1028                                                 didntresize = HUD_Panel_SetSize(i, eY * panel_click_size_y + eX * (mousepos_x - panelPos_x - (panel_click_distance_x - panel_click_size_x))
1029                                                                 + eY * (panel_click_distance_y + (panel_click_pos_y - mousepos_y)));
1030                                                 HUD_Panel_SetPos(i, eX * panelPos_x + eY * (mousepos_y - panel_click_distance_y), didntresize);
1031                                         }
1032                                         if(resizeCorner == 3) {
1033                                                 didntresize = HUD_Panel_SetSize(i, panelSize + eX * (mouseprevpos_x - mousepos_x) + eY * (mousepos_y - mouseprevpos_y));
1034                                                 HUD_Panel_SetPos(i, eX * (mousepos_x - panel_click_distance_x) + eY * panelPos_y, didntresize);
1035                                         }
1036                                         if(resizeCorner == 4) {
1037                                                 HUD_Panel_SetSize(i, mousepos - panelPos - (panel_click_distance - panel_click_size));
1038                                         }
1039                                 }
1040                         }
1041                 }
1042         }
1043         mouseprevpos = mousepos;
1044         prevMouseClicked = mouseClicked;
1045 }
1046
1047 /*void HUD_DrawDockEdge(float id, vector p1, vector p2, float target)
1048 {
1049         vector pos;
1050         vector size;
1051
1052         pos =
1053 */ // maybe one day, since this will be quite complicated
1054
1055 // Weapon icons (#0)
1056 //
1057
1058 float weaponspace[10];
1059 void HUD_WeaponIcons_Clear()
1060 {
1061         float idx;
1062         for(idx = 0; idx < 10; ++idx)
1063                 weaponspace[idx] = 0;
1064 }
1065
1066 entity weaponorder[WEP_MAXCOUNT];
1067
1068 void weaponorder_swap(float i, float j, entity pass)
1069 {
1070         entity h;
1071         h = weaponorder[i];
1072         weaponorder[i] = weaponorder[j];
1073         weaponorder[j] = h;
1074 }
1075
1076 float weaponorder_cmp(float i, float j, entity pass)
1077 {
1078         float d, ii, ij;
1079         d = mod(weaponorder[i].impulse + 9, 10) - mod(weaponorder[j].impulse + 9, 10);
1080         if(d)
1081                 return d;
1082         d = weaponorder[i].weapon - weaponorder[j].weapon;
1083         return d;
1084 }
1085
1086 void HUD_WeaponIcons()
1087 {
1088         float alpha, height, accuracybar_height, stat_weapons; // "constants"
1089         vector pos, mySize, mysize, mypos, accuracy_color;
1090         float i, id, fade, weapon_stats, weapon_hit, weapon_damage, weapon_cnt; // variables
1091
1092         pos = HUD_Panel_GetPos(0);
1093         mySize = HUD_Panel_GetSize(0);
1094         accuracybar_height = cvar_or("hud_weaponicons_accuracybar_height", 3);
1095
1096         stat_weapons = getstati(STAT_WEAPONS);
1097         for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1098         {
1099                 self = get_weaponinfo(i);
1100                 if(self.weapons && (self.impulse >= 0) && (stat_weapons & self.weapons) || hud_configure)
1101                 {
1102                         weaponorder[weapon_cnt] = self;
1103                         ++weapon_cnt;
1104                 }
1105         }
1106         heapsort(weapon_cnt, weaponorder_swap, weaponorder_cmp, world);
1107
1108         HUD_Panel_DrawBg(0, pos, mySize);
1109
1110         // hits
1111         weapon_stats = getstati(STAT_DAMAGE_HITS);
1112         weapon_number = weapon_stats & 63;
1113         weapon_hits[weapon_number-WEP_FIRST] = floor(weapon_stats / 64);
1114         // fired
1115         weapon_stats = getstati(STAT_DAMAGE_FIRED);
1116         weapon_number = weapon_stats & 63;
1117         weapon_fired[weapon_number-WEP_FIRST] = floor(weapon_stats / 64);
1118
1119         if(cvar_or("hud_weaponicons_fade", 1))
1120         {
1121                 fade = 3.2 - 2 * (time - weapontime);
1122                 fade = bound(0.7, fade, 1);
1123         }
1124         else
1125                 fade = 1;
1126
1127         HUD_WeaponIcons_Clear();
1128
1129         float rows, columns;
1130         rows = mySize_y/mySize_x;
1131         rows = bound(1, floor((sqrt(4 * (2/1) * rows * WEP_COUNT + rows * rows) + rows + 0.5) / 2), WEP_COUNT);
1132         //                               ^^^ weapon icon aspect goes here
1133
1134         columns = ceil(WEP_COUNT/rows);
1135         float row, column;
1136         for(i = 0; i < weapon_cnt; ++i)
1137         {
1138                 self = weaponorder[i];
1139                 if((self.weapons && (self.impulse >= 0) && (stat_weapons & self.weapons)) || hud_configure)
1140                 {
1141                         id = self.impulse;
1142
1143                         alpha = (self.weapon == activeweapon) ? 1 : 0.6;
1144
1145                         weapon_hit = weapon_hits[self.weapon-WEP_FIRST];
1146                         weapon_damage = weapon_fired[self.weapon-WEP_FIRST];
1147
1148                         if(self.weapon == activeweapon)
1149                                 drawpic_skin(pos + eX * column * mySize_x*(1/columns) + eY * row * mySize_y*(1/rows), "ammobg", eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows), '1 1 1', fade * hud_alpha_fg, DRAWFLAG_NORMAL);
1150                         drawpic_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 * hud_alpha_fg, DRAWFLAG_NORMAL);
1151
1152                         if(cvar_or("hud_weaponicons_number", 1))
1153                                 drawstring(pos + eX * column * mySize_x*(1/columns) + eY * row * mySize_y*(1/rows), ftos(id), '1 1 0' * 0.5 * mySize_y*(1/rows), '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
1154
1155                         // draw the weapon accuracy on the HUD
1156                         if(hud_accuracy_hud && !(gametype == GAME_RACE || gametype == GAME_CTS))
1157                         {
1158                                 if(weapon_damage)
1159                                         weapon_stats = floor(100 * weapon_hit / weapon_damage);
1160
1161                                 accuracy_color = HUD_AccuracyColor(weapon_stats);
1162                                 if(weapon_damage)
1163                                         drawpic_skin(pos + eX * column * mySize_x*(1/columns) + eY * row * mySize_y*(1/rows) - '2 0 0' + eY * (mySize_y/rows - accuracybar_height), "accuracy_bar.tga", eX * mySize_x*(1/columns) + eY * accuracybar_height, accuracy_color, hud_alpha_fg, DRAWFLAG_NORMAL);
1164                         }
1165                 }
1166
1167                 ++row;
1168                 if(row >= rows)
1169                 {
1170                         row = 0;
1171                         ++column;
1172                 }
1173         }
1174
1175 }
1176
1177 // Inventory (#1)
1178 //
1179
1180 float GetAmmoStat(float i)
1181 {
1182         switch(i)
1183         {
1184                 case 0: return STAT_SHELLS;
1185                 case 1: return STAT_NAILS;
1186                 case 2: return STAT_ROCKETS;
1187                 case 3: return STAT_CELLS;
1188                 case 4: return STAT_FUEL;
1189                 default: return -1;
1190         }
1191 }
1192
1193 float GetAmmoItemCode(float i)
1194 {
1195         switch(i)
1196         {
1197                 case 0: return IT_SHELLS;
1198                 case 1: return IT_NAILS;
1199                 case 2: return IT_ROCKETS;
1200                 case 3: return IT_CELLS;
1201                 case 4: return IT_FUEL;
1202                 default: return -1;
1203         }
1204 }
1205
1206 string GetAmmoPicture(float i)
1207 {
1208         switch(i)
1209         {
1210                 case 0: return "shells";
1211                 case 1: return "bullets";
1212                 case 2: return "rocket";
1213                 case 3: return "cells";
1214                 case 4: return "fuel";
1215                 default: return "";
1216         }
1217 }
1218
1219 void HUD_Inventory()
1220 {
1221         float i;
1222         float stat_items;
1223
1224         vector pos, mySize, mysize, mypos;
1225         pos = HUD_Panel_GetPos(1);
1226         mySize = HUD_Panel_GetSize(1);
1227
1228         HUD_Panel_DrawBg(1, pos, mySize);
1229
1230         // ammo
1231         stat_items = getstati(STAT_ITEMS);
1232         for (i = 0; i < 4; ++i) {
1233                 float a;
1234                 a = getstati(GetAmmoStat(i)); // how much ammo do we have of type i?
1235                 if(hud_configure)
1236                         a = 100;
1237
1238                 if(cvar("hud_inventory_onlycurrent")) {
1239                         if (stat_items & GetAmmoItemCode(i)) {
1240                                 drawpic_skin(pos, "ammobg", mySize, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
1241                                 drawpic_skin(pos + eY * 0.05 * mySize_y, GetAmmoPicture(i), '1 1 0' * 0.8 * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
1242                                 if(a < 10)
1243                                         HUD_DrawXNum(pos + eX * 0.8 * mySize_y + eY * 0.25 * mySize_y, a, strlen(ftos(a)), 0, 0.5 * mySize_y, '0.7 0 0', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1244                                 else
1245                                         HUD_DrawXNum(pos + eX * 0.8 * mySize_y + eY * 0.25 * mySize_y, a, strlen(ftos(a)), 0, 0.5 * mySize_y, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1246                         }
1247                 } else {
1248                         if (a > 0) {
1249                                 if(mySize_x/mySize_y >= 10) { // arrange horizontally
1250                                         switch (i) {
1251                                                 case 0: mypos_x = pos_x;                        mypos_y = pos_y;                        break; // shells
1252                                                 case 1: mypos_x = pos_x + 0.25 * mySize_x;      mypos_y = pos_y;                        break; // bullets
1253                                                 case 2: mypos_x = pos_x + 0.5  * mySize_x;      mypos_y = pos_y;                        break; // rockets
1254                                                 case 3: mypos_x = pos_x + 0.75 * mySize_x;      mypos_y = pos_y;                        break; // cells
1255                                         }
1256                                         mysize_x = 0.25 * mySize_x;
1257                                         mysize_y = mySize_y;
1258                                 } else if(mySize_x/mySize_y >= 2.5) { // arrange in a 2x2 grid
1259                                         switch (i) {
1260                                                 case 0: mypos_x = pos_x + 0.5 * mySize_x;       mypos_y = pos_y + 0.5 * mySize_y;       break; // shells
1261                                                 case 1: mypos_x = pos_x + 0.5 * mySize_x;       mypos_y = pos_y;                        break; // bullets
1262                                                 case 2: mypos_x = pos_x;                        mypos_y = pos_y + 0.5 * mySize_y;       break; // rockets
1263                                                 case 3: mypos_x = pos_x;                        mypos_y = pos_y;                        break; // cells
1264                                         }
1265                                         mysize_x = 0.5 * mySize_x;
1266                                         mysize_y = 0.5 * mySize_y;
1267                                 } else { // arrange vertically
1268                                         switch (i) {
1269                                                 case 0: mypos_x = pos_x;                        mypos_y = pos_y;                        break; // shells
1270                                                 case 1: mypos_x = pos_x;                        mypos_y = pos_y + 0.25 * mySize_y;      break; // bullets
1271                                                 case 2: mypos_x = pos_x;                        mypos_y = pos_y + 0.5  * mySize_y;      break; // rockets
1272                                                 case 3: mypos_x = pos_x;                        mypos_y = pos_y + 0.75 * mySize_y;      break; // cells
1273                                         }
1274                                         mysize_x = mySize_x;
1275                                         mysize_y = 0.25 * mySize_y;
1276                                 }
1277
1278                                 if (stat_items & GetAmmoItemCode(i))
1279                                         drawpic_skin(mypos, "ammobg", mysize, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
1280                                 drawpic_skin(mypos + eY * 0.05 * mysize_y, GetAmmoPicture(i), '1 1 0' * 0.8 * mysize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
1281                                 if (a < 10) {
1282                                         if(stat_items & GetAmmoItemCode(i))
1283                                                 HUD_DrawXNum(mypos + eX * 0.8 * mysize_y + eY * 0.25 * mysize_y, a, strlen(ftos(a)), 0, 0.5 * mysize_y, '0.7 0 0', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1284                                         else
1285                                                 HUD_DrawXNum(mypos + eX * 0.8 * mysize_y + eY * 0.25 * mysize_y, a, strlen(ftos(a)), 0, 0.5 * mysize_y, '0.7 0 0', 0, 0, hud_alpha_fg * 0.7, DRAWFLAG_NORMAL);
1286                                 } else {
1287                                         if(stat_items & GetAmmoItemCode(i))
1288                                                 HUD_DrawXNum(mypos + eX * 0.8 * mysize_y + eY * 0.25 * mysize_y, a, strlen(ftos(a)), 0, 0.5 * mysize_y, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1289                                         else
1290                                                 HUD_DrawXNum(mypos + eX * 0.8 * mysize_y + eY * 0.25 * mysize_y, a, strlen(ftos(a)), 0, 0.5 * mysize_y, '0.7 0.7 0.7', 0, 0, hud_alpha_fg * 0.7, DRAWFLAG_NORMAL);
1291                                 }
1292                         }
1293                 }
1294         }
1295 }
1296
1297
1298 // Powerups (#2)
1299 float shield_maxtime;
1300 float strength_maxtime;
1301 void HUD_Powerups() {
1302         float stat_items;
1303         stat_items = getstati(STAT_ITEMS);
1304
1305         if(!hud_configure)
1306         {
1307                 if not(stat_items & IT_STRENGTH)
1308                         if not(stat_items & IT_INVINCIBLE)
1309                                 return;
1310
1311                 if (getstati(STAT_HEALTH) <= 0)
1312                         return;
1313         }
1314
1315         vector pos, mySize;
1316         pos = HUD_Panel_GetPos(2);
1317         mySize = HUD_Panel_GetSize(2);
1318
1319         HUD_Panel_DrawBg(2, pos, mySize);
1320
1321         float strength_time, shield_time;
1322
1323         strength_time = bound(0, ceil(getstatf(STAT_STRENGTH_FINISHED) - time), 99);
1324         shield_time = bound(0, ceil(getstatf(STAT_INVINCIBLE_FINISHED) - time), 99);
1325
1326         if(hud_configure)
1327         {
1328                 strength_time = 15;
1329                 shield_time = 27;
1330         }
1331
1332         string leftname, rightname;
1333         float leftcnt, rightcnt;
1334         float leftalpha, rightalpha;
1335         if (cvar(strcat("hud_", HUD_Panel_GetName(2), "_flip"))) {
1336                 leftname = "strength";
1337                 leftcnt = strength_time;
1338
1339                 rightname = "shield";
1340                 rightcnt = shield_time;
1341         } else {
1342                 leftname = "shield";
1343                 leftcnt = shield_time;
1344
1345                 rightname = "strength";
1346                 rightcnt = strength_time;
1347         }
1348         leftalpha = bound(0, leftcnt, 1);
1349         rightalpha = bound(0, rightcnt, 1);
1350
1351         float len;
1352         if (mySize_x/mySize_y > 5)
1353         {
1354                 if(leftcnt)
1355                 {
1356                         len = strlen(ftos(leftcnt));
1357
1358                         drawpic_skin(pos, "statusbar", eX * 0.5 * mySize_x * min(1, leftcnt/30) + eY * mySize_y, HUD_Panel_GetProgressBarColor(leftname), cvar("hud_progressbar_alpha"), DRAWFLAG_NORMAL);
1359                         drawpic_skin(pos, strcat("", leftname), '1 1 0' * mySize_y, '1 1 1', leftalpha * hud_alpha_fg, DRAWFLAG_NORMAL);
1360                         HUD_DrawXNum(pos + eX * mySize_y + eY * 0.25 * mySize_y, leftcnt, len, 0, 0.5 * mySize_y, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1361                 }
1362
1363                 if(rightcnt)
1364                 {
1365                         drawpic_skin(pos + eX * mySize_x - eX * 0.5 * mySize_x * min(1, rightcnt/30), "statusbar", eX * 0.5 * mySize_x * min(1, rightcnt/30) + eY * mySize_y, HUD_Panel_GetProgressBarColor(rightname), cvar("hud_progressbar_alpha"), DRAWFLAG_NORMAL);
1366                         drawpic_skin(pos + eX * mySize_x - eX * mySize_y, strcat("", rightname), '1 1 0' * mySize_y, '1 1 1', rightalpha * hud_alpha_fg, DRAWFLAG_NORMAL);
1367                         HUD_DrawXNum(pos + eX * mySize_x - eX * 2.5 * mySize_y + eY * 0.25 * mySize_y, rightcnt, 3, 0, 0.5 * mySize_y, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1368                 }
1369         }
1370         else if (mySize_x/mySize_y > 3.2)
1371         {
1372                 if(leftcnt)
1373                 {
1374                         len = strlen(ftos(leftcnt));
1375
1376                         drawpic_skin(pos + eY * mySize_y - eY * mySize_y * min(1, leftcnt/30), "statusbar", eX * 0.5 * mySize_x + eY * mySize_y * min(1, leftcnt/30), HUD_Panel_GetProgressBarColor(leftname), cvar("hud_progressbar_alpha"), DRAWFLAG_NORMAL);
1377                         drawpic_skin(pos + eX * 0.4 * mySize_y, strcat("", leftname), '0.7 0.7 0' * mySize_y, '1 1 1', leftalpha * hud_alpha_fg, DRAWFLAG_NORMAL);
1378                         HUD_DrawXNum(pos + eX * ((3-len)/2) * 0.5 * mySize_y + eY * 0.55 * mySize_y, leftcnt, len, 0, 0.5 * mySize_y, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1379                 }
1380
1381                 if(rightcnt)
1382                 {
1383                         len = strlen(ftos(rightcnt));
1384                         
1385                         drawpic_skin(pos + eX * 0.5 * mySize_x + eY * mySize_y - eY * mySize_y * min(1, rightcnt/30), "statusbar", eX * 0.5 * mySize_x + eY * mySize_y * min(1, rightcnt/30), HUD_Panel_GetProgressBarColor(rightname), cvar("hud_progressbar_alpha"), DRAWFLAG_NORMAL);
1386                         drawpic_skin(pos + eX * mySize_x - eX * 1.1 * mySize_y, strcat("", rightname), '0.7 0.7 0' * mySize_y, '1 1 1', rightalpha * hud_alpha_fg, DRAWFLAG_NORMAL);
1387                         HUD_DrawXNum(pos + eX * mySize_x - eX * len * 0.5 * mySize_y - eX * ((3-len)/2) * 0.5 * mySize_y + eY * 0.55 * mySize_y, rightcnt, len, 0, 0.5 * mySize_y, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1388                 }
1389         }
1390         else
1391         {
1392                 if(leftcnt)
1393                 {
1394                         len = strlen(ftos(leftcnt));
1395
1396                         drawpic_skin(pos, "statusbar", eX * mySize_x * min(1, leftcnt/30) + eY * 0.5 * mySize_y, HUD_Panel_GetProgressBarColor(leftname), cvar("hud_progressbar_alpha"), DRAWFLAG_NORMAL);
1397                         drawpic_skin(pos, strcat("", leftname), '0.5 0.5 0' * mySize_y, '1 1 1', leftalpha * hud_alpha_fg, DRAWFLAG_NORMAL);
1398                         HUD_DrawXNum(pos + eX * 0.5 * mySize_y, leftcnt, len, 0, 0.5 * mySize_y, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1399                 }
1400
1401                 if(rightcnt)
1402                 {
1403                         len = strlen(ftos(rightcnt));
1404
1405                         drawpic_skin(pos + eY * 0.5 * mySize_y, "statusbar", eX * mySize_x * min(1, rightcnt/30) + eY * 0.5 * mySize_y, HUD_Panel_GetProgressBarColor(rightname), cvar("hud_progressbar_alpha"), DRAWFLAG_NORMAL);
1406                         drawpic_skin(pos + eY * 0.5 * mySize_y, strcat("", rightname), '0.5 0.5 0' * mySize_y, '1 1 1', rightalpha * hud_alpha_fg, DRAWFLAG_NORMAL);
1407                         HUD_DrawXNum(pos + eX * 0.5 * mySize_y + eY * 0.5 * mySize_y, rightcnt, len, 0, 0.5 * mySize_y, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1408                 }
1409         }
1410
1411         /* TODO: add expand!
1412         //strength
1413         if (strength_time) {
1414                 dt = strength_time - time;
1415                 if(dt > 0)
1416                 {
1417                         if(dt < 5)
1418                         {
1419                                 drawpic_expanding_two(pos, "str", '1 1 0' * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_ADDITIVE,
1420                                         bound(0, (ceil(dt) - dt) / 0.5, 1));
1421                         }
1422                         else
1423                         {
1424                                 drawpic_skin(pos, "str", '1 1 0' * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_ADDITIVE);
1425                         }
1426                         HUD_DrawXNum(pos - '40 -2 0', ceil(dt), 2, 0, countdown_fontsize, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1427                 }
1428                 else if(dt > -1)
1429                 {
1430                         drawpic_expanding(pos, "str", '1 1 0' * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_ADDITIVE,
1431                                 bound(0, -dt / 0.5, 1));
1432                 }
1433         }
1434
1435         //invincibility
1436         if (invincibility_time) {
1437                 dt = invincibility_time - time;
1438                 if(dt > 0)
1439                 {
1440                         if(dt < 5)
1441                         {
1442                                 drawpic_expanding_two(pos - '0 -22 0', "invinc", '1 1 0' * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_ADDITIVE,
1443                                         bound(0, (ceil(dt) - dt) / 0.5, 1));
1444                         }
1445                         else
1446                         {
1447                                 drawpic_skin(pos - '0 -22 0', "invinc", '1 1 0' * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_ADDITIVE);
1448                         }
1449                         HUD_DrawXNum(pos - '40 -24 0', ceil(dt), 2, 0, countdown_fontsize, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1450                 }
1451                 else if(dt > -1)
1452                 {
1453                         drawpic_expanding(pos - '0 -22 0', "invinc", '1 1 0' * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_ADDITIVE,
1454                                 bound(0, -dt / 0.5, 1));
1455                 }
1456         }
1457         */
1458 }
1459
1460 // Health/armor (#3)
1461 //
1462 void HUD_HealthArmor(void)
1463 {
1464         vector pos, mySize;
1465         pos = HUD_Panel_GetPos(3);
1466         mySize = HUD_Panel_GetSize(3);
1467
1468         HUD_Panel_DrawBg(3, pos, mySize);
1469
1470         float armor, health, x;
1471         armor = getstati(STAT_ARMOR);
1472         health = getstati(STAT_HEALTH);
1473         if(hud_configure)
1474         {
1475                 armor = 150;
1476                 health = 100;
1477         }
1478
1479         if(health <= 0)
1480                 return;
1481
1482         float len;
1483
1484         // TODO!
1485         if(hud_hudselector == 2) // combined health and armor display
1486         {
1487                 vector v;
1488                 v = healtharmor_maxdamage(health, armor, armorblockpercent);
1489
1490                 vector num_pos;
1491                 num_pos = - '96 28 0';
1492
1493                 x = floor(v_x + 1);
1494
1495                 if(v_z) // fully armored
1496                 {
1497                         // here, armorideal > armor
1498                         drawpic_skin(num_pos + '78 -4.5 0', "health", '32 32 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
1499                         drawpic_skin(num_pos + '108 -4.5 0', "armor", '20 20 0', '1 1 1', hud_alpha_fg * armor / v_y, DRAWFLAG_NORMAL);
1500                 }
1501                 else
1502                 {
1503                         drawpic_skin(num_pos + '108 -4.5 0', "health", '20 20 0', '1 1 1', hud_alpha_fg * v_y / armor, DRAWFLAG_NORMAL);
1504                         drawpic_skin(num_pos + '78 -4.5 0', "armor", '32 32 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
1505                 }
1506                 HUD_DrawXNum_Colored(num_pos, x, 3, 24, hud_alpha_fg); // draw the combined health and armor
1507         }
1508
1509         else
1510         {
1511                 string leftname, rightname;
1512                 float leftcnt, rightcnt;
1513                 float leftactive, rightactive;
1514                 float leftalpha, rightalpha;
1515                 // TODO!
1516                 if (cvar(strcat("hud_", HUD_Panel_GetName(3), "_flip"))) { // old style layout with armor left/top of health
1517                         leftname = "armor";
1518                         leftcnt = armor;
1519                         if(leftcnt)
1520                                 leftactive = 1;
1521                         leftalpha = min((armor+10)/55, 1);
1522
1523                         rightname = "health";
1524                         rightcnt = health;
1525                         rightactive = 1;
1526                         rightalpha = 1;
1527                 } else {
1528                         leftname = "health";
1529                         leftcnt = health;
1530                         leftactive = 1;
1531                         leftalpha = 1;
1532
1533                         rightname = "armor";
1534                         rightcnt = armor;
1535                         if(rightcnt)
1536                                 rightactive = 1;
1537                         rightalpha = min((armor+10)/55, 1);
1538                 }
1539
1540                 float fuel;
1541                 fuel = getstati(GetAmmoStat(4)); // how much fuel do we have?
1542
1543                 if (mySize_x/mySize_y > 5)
1544                 {
1545                         if(leftactive)
1546                         {
1547                                 len = strlen(ftos(leftcnt));
1548
1549                                 drawpic_skin(pos, "statusbar", eX * 0.5 * mySize_x * min(1, leftcnt/200) + eY * mySize_y, HUD_Panel_GetProgressBarColor(leftname), cvar("hud_progressbar_alpha"), DRAWFLAG_NORMAL);
1550                                 drawpic_skin(pos, strcat("", leftname), '1 1 0' * mySize_y, '1 1 1', leftalpha * hud_alpha_fg, DRAWFLAG_NORMAL);
1551                                 HUD_DrawXNum_Colored(pos + eX * mySize_y + eY * 0.25 * mySize_y, leftcnt, len, 0.5 * mySize_y, hud_alpha_fg);
1552                         }
1553
1554                         if(rightactive)
1555                         {
1556                                 drawpic_skin(pos + eX * mySize_x - eX * 0.5 * mySize_x * min(1, rightcnt/200), "statusbar", eX * 0.5 * mySize_x * min(1, rightcnt/200) + eY * mySize_y, HUD_Panel_GetProgressBarColor(rightname), cvar("hud_progressbar_alpha"), DRAWFLAG_NORMAL);
1557                                 drawpic_skin(pos + eX * mySize_x - eX * mySize_y, strcat("", rightname), '1 1 0' * mySize_y, '1 1 1', rightalpha * hud_alpha_fg, DRAWFLAG_NORMAL);
1558                                 HUD_DrawXNum_Colored(pos + eX * mySize_x - eX * 2.5 * mySize_y + eY * 0.25 * mySize_y, rightcnt, 3, 0.5 * mySize_y, hud_alpha_fg);
1559                         }
1560
1561                         if(fuel)
1562                                 drawpic_skin(pos, "statusbar", eX * mySize_x * min(1, fuel/100) + eY * 0.2 * mySize_y, HUD_Panel_GetProgressBarColor("fuel"), hud_alpha_fg * 0.8, DRAWFLAG_NORMAL);
1563                 }
1564                 else if (mySize_x/mySize_y > 3.2)
1565                 {
1566                         if(leftactive)
1567                         {
1568                                 len = strlen(ftos(leftcnt));
1569
1570                                 drawpic_skin(pos + eY * mySize_y - eY * mySize_y * min(1, leftcnt/200), "statusbar", eX * 0.5 * mySize_x + eY * mySize_y * min(1, leftcnt/200), HUD_Panel_GetProgressBarColor(leftname), cvar("hud_progressbar_alpha"), DRAWFLAG_NORMAL);
1571                                 drawpic_skin(pos + eX * 0.4 * mySize_y, strcat("", leftname), '0.7 0.7 0' * mySize_y, '1 1 1', leftalpha * hud_alpha_fg, DRAWFLAG_NORMAL);
1572                                 HUD_DrawXNum_Colored(pos + eX * ((3-len)/2) * 0.5 * mySize_y + eY * 0.55 * mySize_y, leftcnt, len, 0.5 * mySize_y, hud_alpha_fg);
1573                         }
1574
1575                         if(rightactive)
1576                         {
1577                                 len = strlen(ftos(rightcnt));
1578                                 
1579                                 drawpic_skin(pos + eX * 0.5 * mySize_x + eY * mySize_y - eY * mySize_y * min(1, rightcnt/200), "statusbar", eX * 0.5 * mySize_x + eY * mySize_y * min(1, rightcnt/200), HUD_Panel_GetProgressBarColor(rightname), cvar("hud_progressbar_alpha"), DRAWFLAG_NORMAL);
1580                                 drawpic_skin(pos + eX * mySize_x - eX * 1.1 * mySize_y, strcat("", rightname), '0.7 0.7 0' * mySize_y, '1 1 1', rightalpha * hud_alpha_fg, DRAWFLAG_NORMAL);
1581                                 HUD_DrawXNum_Colored(pos + eX * mySize_x - eX * len * 0.5 * mySize_y - eX * ((3-len)/2) * 0.5 * mySize_y + eY * 0.55 * mySize_y, rightcnt, len, 0.5 * mySize_y, hud_alpha_fg);
1582                         }
1583
1584                         if(fuel)
1585                                 drawpic_skin(pos, "statusbar", eX * mySize_x * min(1, fuel/100) + eY * 0.15 * mySize_y, HUD_Panel_GetProgressBarColor("fuel"), hud_alpha_fg * 0.8, DRAWFLAG_NORMAL);
1586                 }
1587                 else
1588                 {
1589                         if(leftactive)
1590                         {
1591                                 len = strlen(ftos(leftcnt));
1592
1593                                 drawpic_skin(pos, "statusbar", eX * mySize_x * min(1, leftcnt/200) + eY * 0.5 * mySize_y, HUD_Panel_GetProgressBarColor(leftname), cvar("hud_progressbar_alpha"), DRAWFLAG_NORMAL);
1594                                 drawpic_skin(pos, strcat("", leftname), '0.5 0.5 0' * mySize_y, '1 1 1', leftalpha * hud_alpha_fg, DRAWFLAG_NORMAL);
1595                                 HUD_DrawXNum_Colored(pos + eX * 0.5 * mySize_y, leftcnt, len, 0.5 * mySize_y, hud_alpha_fg);
1596                         }
1597
1598                         if(rightactive)
1599                         {
1600                                 len = strlen(ftos(rightcnt));
1601
1602                                 drawpic_skin(pos + eY * 0.5 * mySize_y, "statusbar", eX * mySize_x * min(1, rightcnt/200) + eY * 0.5 * mySize_y, HUD_Panel_GetProgressBarColor(rightname), cvar("hud_progressbar_alpha"), DRAWFLAG_NORMAL);
1603                                 drawpic_skin(pos + eY * 0.5 * mySize_y, strcat("", rightname), '0.5 0.5 0' * mySize_y, '1 1 1', rightalpha * hud_alpha_fg, DRAWFLAG_NORMAL);
1604                                 HUD_DrawXNum_Colored(pos + eX * 0.5 * mySize_y + eY * 0.5 * mySize_y, rightcnt, len, 0.5 * mySize_y, hud_alpha_fg);
1605                         }
1606
1607                         if(fuel)
1608                                 drawpic_skin(pos, "statusbar", eX * mySize_x * min(1, fuel/100) + eY * 0.1 * mySize_y, HUD_Panel_GetProgressBarColor("fuel"), hud_alpha_fg * 0.8, DRAWFLAG_NORMAL);
1609                 }
1610         }
1611 }
1612
1613 // Score (#7)
1614 //
1615 void HUD_Score()
1616 {
1617         vector pos, mySize;
1618         pos = HUD_Panel_GetPos(7);
1619         mySize = HUD_Panel_GetSize(7);
1620
1621         HUD_Panel_DrawBg(7, pos, mySize);
1622
1623         float score, distribution, leader;
1624         float score_len, distr_len;
1625         vector score_pos, secondary_score_pos, distribution_color;
1626         entity tm, pl, me;
1627         me = (spectatee_status > 0) ? playerslots[spectatee_status - 1] : playerslots[player_localentnum - 1];
1628
1629         if (!teamplay) { // non-teamgames
1630                 // me vector := [team/connected frags id]
1631                 pl = players.sort_next;
1632                 if(pl == me)
1633                         pl = pl.sort_next;
1634
1635                 if(hud_configure)
1636                         distribution = 42;
1637                 else if(pl)
1638                         distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
1639                 else
1640                         distribution = 0;
1641
1642                 score = me.(scores[ps_primary]);
1643                 if(hud_configure)
1644                         score = 123;
1645
1646                 if(distribution >= 5) {
1647                         distribution_color = eY;
1648                         leader = 1;
1649                 } else if(distribution >= 0) {
1650                         distribution_color = '1 1 1';
1651                         leader = 1;
1652                 } else if(distribution >= -5)
1653                         distribution_color = '1 1 0';
1654                 else
1655                         distribution_color = eX;
1656
1657                 score_len = strlen(ftos(score));
1658                 distr_len = strlen(ftos(distribution));
1659
1660                 HUD_DrawXNum(pos + eX * mySize_x - eX * 3 * 0.33 * mySize_y, distribution, 3, 3, 0.33 * mySize_y, distribution_color, 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1661                 if (leader)
1662                         drawpic_skin(pos + eX * mySize_x - eX * score_len * mySize_y - eX * 3 * 0.33 * mySize_y, strcat("highlight_", ftos(score_len)), eX * score_len * mySize_y + eY * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
1663                 HUD_DrawXNum(pos + eX * mySize_x - eX * 3 * mySize_y - eX * 3 * 0.33 * mySize_y, score, 3, 0, mySize_y, distribution_color, 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1664         } else { // teamgames
1665                 float max_fragcount;
1666                 max_fragcount = -99;
1667
1668                 float teamnum;
1669                 for(tm = teams.sort_next; tm; tm = tm.sort_next) {
1670                         if(tm.team == COLOR_SPECTATOR || (!tm.team_size && !hud_configure)) // no players? don't display
1671                                 continue;
1672                         score = tm.(teamscores[ts_primary]);
1673                         if(hud_configure)
1674                                 score = 123;
1675                         leader = 0;
1676                         
1677                         score_len = strlen(ftos(score));
1678
1679                         if (score > max_fragcount)
1680                                 max_fragcount = score;
1681
1682                         if(tm.team == myteam) {
1683                                 if (max_fragcount == score)
1684                                         leader = 1;
1685                                 if (leader)
1686                                         drawpic_skin(pos + eX * mySize_x - eX * score_len * mySize_y - eX * 3 * 0.33 * mySize_y, strcat("highlight_", ftos(score_len)), eX * score_len * mySize_y + eY * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
1687                                 HUD_DrawXNum(pos + eX * mySize_x - eX * 3 * mySize_y - eX * 3 * 0.33 * mySize_y, score, 3, 0, mySize_y, GetTeamRGB(tm.team) * 0.8, 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1688                         } else {
1689                                 if (max_fragcount == score)
1690                                         leader = 1;
1691                                 if (leader)
1692                                         drawpic_skin(pos + eX * mySize_x - eX * 0.33 * score_len * mySize_y + eY * 0.33 * mySize_y * teamnum, strcat("highlight_", ftos(score_len)), eX * 0.33 * score_len * mySize_y + eY * 0.33 * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
1693                                 HUD_DrawXNum(pos + eX * mySize_x - eX * 3 * 0.33 * mySize_y + eY * 0.33 * mySize_y * teamnum, score, 3, 0, 0.33 * mySize_y, GetTeamRGB(tm.team) * 0.8, 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
1694                                 teamnum += 1;
1695                         }
1696                 }
1697         }
1698 }
1699
1700 // Race timer (#8)
1701 //
1702 void HUD_RaceTimer (void) {
1703         vector pos, mySize;
1704         pos = HUD_Panel_GetPos(8);
1705         mySize = HUD_Panel_GetSize(8);
1706
1707         HUD_Panel_DrawBg(8, pos, mySize);
1708
1709         drawfont = hud_bigfont;
1710         float a, t;
1711         string s, forcetime;
1712
1713         if(hud_configure)
1714         {
1715                 s = "0:13:37";
1716                 drawstring(pos + eX * 0.5 * mySize_x - '0.5 0 0' * stringwidth(s, FALSE, '0.75 0.75 0' * mySize_y), s, '0.75 0.75 0' * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
1717                 s = "^1Intermediate 1 (+15.42)";
1718                 drawcolorcodedstring(pos + eX * 0.5 * mySize_x - '0.5 0 0' * stringwidth(s, TRUE, '1 1 0' * 0.25 * mySize_y) + eY * 0.75 * mySize_y, s, '1 1 0' * 0.25 * mySize_y, hud_alpha_fg, DRAWFLAG_NORMAL);
1719         }
1720         else if(race_checkpointtime)
1721         {
1722                 a = bound(0, 2 - (time - race_checkpointtime), 1);
1723                 s = "";
1724                 forcetime = "";
1725                 if(a > 0) // just hit a checkpoint?
1726                 {
1727                         if(race_checkpoint != 254)
1728                         {
1729                                 if(race_time && race_previousbesttime)
1730                                         s = MakeRaceString(race_checkpoint, TIME_DECODE(race_time) - TIME_DECODE(race_previousbesttime), 0, 0, race_previousbestname);
1731                                 else
1732                                         s = MakeRaceString(race_checkpoint, 0, -1, 0, race_previousbestname);
1733                                 if(race_time)
1734                                         forcetime = TIME_ENCODED_TOSTRING(race_time);
1735                         }
1736                 }
1737                 else
1738                 {
1739                         if(race_laptime && race_nextbesttime && race_nextcheckpoint != 254)
1740                         {
1741                                 a = bound(0, 2 - ((race_laptime + TIME_DECODE(race_nextbesttime)) - (time + TIME_DECODE(race_penaltyaccumulator))), 1);
1742                                 if(a > 0) // next one?
1743                                 {
1744                                         s = MakeRaceString(race_nextcheckpoint, (time + TIME_DECODE(race_penaltyaccumulator)) - race_laptime, TIME_DECODE(race_nextbesttime), 0, race_nextbestname);
1745                                 }
1746                         }
1747                 }
1748
1749                 if(s != "" && a > 0)
1750                 {
1751                         dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1752                         //drawcolorcodedstring(m - '0 16 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', hud_alpha_fg * a, DRAWFLAG_NORMAL);
1753                         drawcolorcodedstring(pos + eX * 0.5 * mySize_x - '0.5 0 0' * stringwidth(s, TRUE, '1 1 0' * 0.25 * mySize_y) + eY * 0.75 * mySize_y, s, '1 1 0' * 0.25 * mySize_y, hud_alpha_fg * a, DRAWFLAG_NORMAL);
1754                 }
1755
1756                 if(race_penaltytime)
1757                 {
1758                         a = bound(0, 2 - (time - race_penaltyeventtime), 1);
1759                         if(a > 0)
1760                         {
1761                                 s = strcat("^1PENALTY: ", ftos_decimals(race_penaltytime * 0.1, 1), " (", race_penaltyreason, ")");
1762                                 dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1763                                 //drawcolorcodedstring(m - '0 32 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', hud_alpha_fg * a, DRAWFLAG_NORMAL);
1764                                 drawcolorcodedstring(pos - '0 32 0' - '0.5 0 0' * stringwidth(s, TRUE, '16 16 0'), s, '16 16 0', hud_alpha_fg * a, DRAWFLAG_NORMAL);
1765                         }
1766                 }
1767
1768                 if(forcetime != "")
1769                 {
1770                         a = bound(0, (time - race_checkpointtime) / 0.5, 1);
1771                         //drawstring_expanding(m - '16 0 0' * stringwidth(forcetime, FALSE), forcetime, '32 32 0', '1 1 1', hud_alpha_fg, 0, a);
1772                         drawstring_expanding(pos + eX * 0.5 * mySize_x - '0.5 0 0' * stringwidth(forcetime, FALSE, '1 1 0' * 0.75 * mySize_y), forcetime, '1 1 0' * 0.75 * mySize_y, '1 1 1', hud_alpha_fg, 0, a);
1773                 }
1774                 else
1775                         a = 1;
1776
1777                 if(race_laptime && race_checkpoint != 255)
1778                 {
1779                         s = TIME_ENCODED_TOSTRING(TIME_ENCODE(time + TIME_DECODE(race_penaltyaccumulator) - race_laptime));
1780                         //drawstring(m - '16 0 0' * stringwidth(s, FALSE), s, '32 32 0', '1 1 1', hud_alpha_fg * a, DRAWFLAG_NORMAL);
1781                         drawstring(pos + eX * 0.5 * mySize_x - '0.5 0 0' * stringwidth(s, FALSE, '0.75 0.75 0' * mySize_y), s, '0.75 0.75 0' * mySize_y, '1 1 1', hud_alpha_fg * a, DRAWFLAG_NORMAL);
1782                 }
1783         }
1784         else
1785         {
1786                 if(race_mycheckpointtime)
1787                 {
1788                         a = bound(0, 2 - (time - race_mycheckpointtime), 1);
1789                         s = MakeRaceString(race_mycheckpoint, TIME_DECODE(race_mycheckpointdelta), -!race_mycheckpointenemy, race_mycheckpointlapsdelta, race_mycheckpointenemy);
1790                         dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1791                         //drawcolorcodedstring(m - '0 16 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', hud_alpha_fg * a, DRAWFLAG_NORMAL);
1792                         drawcolorcodedstring(pos - '0 16 0' - '0.5 0 0' * stringwidth(s, TRUE, '16 16 0'), s, '16 16 0', hud_alpha_fg * a, DRAWFLAG_NORMAL);
1793                 }
1794                 if(race_othercheckpointtime && race_othercheckpointenemy != "")
1795                 {
1796                         a = bound(0, 2 - (time - race_othercheckpointtime), 1);
1797                         s = MakeRaceString(race_othercheckpoint, -TIME_DECODE(race_othercheckpointdelta), -!race_othercheckpointenemy, race_othercheckpointlapsdelta, race_othercheckpointenemy);
1798                         dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1799                         //drawcolorcodedstring(m - '0 0 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', hud_alpha_fg * a, DRAWFLAG_NORMAL);
1800                         drawcolorcodedstring(pos - '0 0 0' - '0.5 0 0' * stringwidth(s, TRUE, '16 16 0'), s, '16 16 0', hud_alpha_fg * a, DRAWFLAG_NORMAL);
1801                 }
1802
1803                 if(race_penaltytime && !race_penaltyaccumulator)
1804                 {
1805                         t = race_penaltytime * 0.1 + race_penaltyeventtime;
1806                         a = bound(0, (1 + t - time), 1);
1807                         if(a > 0)
1808                         {
1809                                 if(time < t)
1810                                         s = strcat("^1PENALTY: ", ftos_decimals(t - time, 1), " (", race_penaltyreason, ")");
1811                                 else
1812                                         s = strcat("^2PENALTY: 0.0 (", race_penaltyreason, ")");
1813                                 dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1814                                 //drawcolorcodedstring(m - '0 32 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', hud_alpha_fg * a, DRAWFLAG_NORMAL);
1815                                 drawcolorcodedstring(pos - '0 32 0' - '0.5 0 0' * stringwidth(s, TRUE, '16 16 0'), s, '16 16 0', hud_alpha_fg * a, DRAWFLAG_NORMAL);
1816                         }
1817                 }
1818         }
1819
1820         drawfont = hud_font;
1821 }
1822
1823 // Notification area (#4)
1824 void HUD_Notify (void)
1825 {
1826         vector pos, mySize;
1827         pos = HUD_Panel_GetPos(4);
1828         mySize = HUD_Panel_GetSize(4);
1829
1830         HUD_Panel_DrawBg(4, pos, mySize);
1831
1832         string s;
1833         entity tm;
1834         if(spectatee_status && !intermission)
1835         {
1836                 drawfont = hud_bigfont;
1837                 if(spectatee_status == -1)
1838                         s = "^1Observing";
1839                 else
1840                         s = GetPlayerName(spectatee_status - 1);
1841                 // spectated player name between HUD and chat area, aligned to the left
1842                 pos_x = 0;
1843                 pos_y = - 50 - hud_fontsize_spec_y;
1844                 s = textShortenToWidth(s, vid_conwidth/2.5, hud_fontsize_spec, stringwidth_colors);
1845                 drawcolorcodedstring(pos, s, hud_fontsize_spec, hud_alpha_fg, DRAWFLAG_NORMAL);
1846                 drawfont = hud_font;
1847
1848                 // spectator text in the upper right corner
1849                 if(spectatee_status == -1)
1850                         s = strcat("^1Press ^3", getcommandkey("primary fire", "+attack"), "^1 to spectate");
1851                 else
1852                         s = strcat("^1Press ^3", getcommandkey("primary fire", "+attack"), "^1 for another player");
1853
1854                 if(spectatee_status == -1)
1855                         s = strcat("^1Use ^3", getcommandkey("next weapon", "weapnext"), "^1 or ^3", getcommandkey("previous weapon", "weapprev"), "^1 to change the speed");
1856                 else
1857                         s = strcat("^1Press ^3", getcommandkey("secondary fire", "+attack2"), "^1 to observe");
1858
1859                 s = strcat("^1Press ^3", getcommandkey("server info", "+show_info"), "^1 for gamemode info");
1860
1861                 if(gametype == GAME_ARENA)
1862                         s = "^1Wait for your turn to join";
1863                 else if(gametype == GAME_LMS)
1864                 {
1865                         entity sk;
1866                         sk = playerslots[player_localentnum - 1];
1867                         if(sk.(scores[ps_primary]) >= 666)
1868                                 s = "^1Match has already begun";
1869                         else if(sk.(scores[ps_primary]) > 0)
1870                                 s = "^1You have no more lives left";
1871                         else
1872                                 s = strcat("^1Press ^3", getcommandkey("jump", "+jump"), "^1 to join");
1873                 }
1874                 else
1875                         s = strcat("^1Press ^3", getcommandkey("jump", "+jump"), "^1 to join");
1876
1877                 //show restart countdown:
1878                 if (time < getstatf(STAT_GAMESTARTTIME)) {
1879                         float countdown;
1880                         //we need to ceil, otherwise the countdown would be off by .5 when using round()
1881                         countdown = ceil(getstatf(STAT_GAMESTARTTIME) - time);
1882                         s = strcat("^1Game starts in ^3", ftos(countdown), "^1 seconds");
1883                 }
1884         }
1885         if(warmup_stage && !intermission)
1886         {
1887                 s = "^2Currently in ^1warmup^2 stage!";
1888         }
1889
1890         // move more important stuff more to the middle so its more visible
1891
1892         string blinkcolor;
1893         if(mod(time, 1) >= 0.5)
1894                 blinkcolor = "^1";
1895         else
1896                 blinkcolor = "^3";
1897
1898         if(ready_waiting && !intermission && !spectatee_status)
1899         {
1900                 if(ready_waiting_for_me)
1901                 {
1902                         if(warmup_stage)
1903                                 s = strcat(blinkcolor, "Press ^3", getcommandkey("ready", "ready"), blinkcolor, " to end warmup");
1904                         else
1905                                 s = strcat(blinkcolor, "Press ^3", getcommandkey("ready", "ready"), blinkcolor, " once you are ready");
1906                 }
1907                 else
1908                 {
1909                         if(warmup_stage)
1910                                 s = strcat("^2Waiting for others to ready up to end warmup...");
1911                         else
1912                                 s = strcat("^2Waiting for others to ready up...");
1913                 }
1914         }
1915         else if(warmup_stage && !intermission && !spectatee_status)
1916         {
1917                 s = strcat("^2Press ^3", getcommandkey("ready", "ready"), "^2 to end warmup");
1918         }
1919
1920         if(teamplay && !intermission && !spectatee_status && gametype != GAME_CA && teamnagger)
1921         {
1922                 float ts_min, ts_max;
1923                 tm = teams.sort_next;
1924                 if (tm)
1925                 {
1926                         for(; tm.sort_next; tm = tm.sort_next)
1927                         {
1928                                 if(!tm.team_size || tm.team == COLOR_SPECTATOR)
1929                                         continue;
1930                                 if(!ts_min) ts_min = tm.team_size;
1931                                 else ts_min = min(ts_min, tm.team_size);
1932                                 if(!ts_max) ts_max = tm.team_size;
1933                                 else ts_max = max(ts_max, tm.team_size);
1934                         }
1935                         if ((ts_max - ts_min) > 1)
1936                         {
1937                                 s = strcat(blinkcolor, "Teamnumbers are unbalanced!");
1938                                 tm = GetTeam(myteam, false);
1939                                 if (tm)
1940                                 if (tm.team != COLOR_SPECTATOR)
1941                                 if (tm.team_size == ts_max)
1942                                         s = strcat(s, " Press ^3", getcommandkey("team menu", "menu_showteamselect"), blinkcolor, " to adjust");
1943
1944                         }
1945                 }
1946         }
1947 }
1948
1949 // Vote window (#9)
1950 float vote_yescount;
1951 float vote_nocount;
1952 float vote_needed;
1953 float vote_highlighted; // currently selected vote
1954
1955 float vote_active; // is there an active vote?
1956 float vote_prev; // previous state of vote_active to check for a change
1957 float vote_alpha;
1958 float vote_change; // "time" when vote_active changed
1959
1960 void HUD_VoteWindow(void) 
1961 {
1962         vector pos, mySize;
1963         pos = HUD_Panel_GetPos(9);
1964         mySize = HUD_Panel_GetSize(9);
1965
1966         string s;
1967         float a;
1968         if(vote_active != vote_prev) {
1969                 vote_change = time;
1970                 vote_prev = bound(0, vote_active, 1);
1971         }
1972
1973         if(vote_active)
1974                 vote_alpha = bound(0, (time - vote_change) * 2, 1);
1975         else
1976                 vote_alpha = bound(0, 1 - (time - vote_change) * 2, 1);
1977
1978         if(hud_configure)
1979         {
1980                 vote_alpha = 1;
1981                 vote_yescount = 3;
1982                 vote_nocount = 2;
1983                 vote_needed = 4;
1984         }
1985
1986         HUD_Panel_DrawBg(9, pos, mySize);
1987
1988         if(vote_alpha) {
1989                 a = vote_alpha * bound(cvar_or("hud_vote_alreadyvoted_alpha", 0.75), 1 - vote_highlighted, 1);
1990
1991                 drawpic_skin(pos, "voteprogress_back", mySize, HUD_GetBgColor(), a * hud_alpha_bg, DRAWFLAG_NORMAL);
1992
1993                 s = "A vote has been called for: ";
1994                 drawstring(pos + '0.5 0 0' * mySize_x + '0 0.1 0' * mySize_y - eX * stringwidth(s, FALSE, '1 1 0' * 0.5 * mySize_y*(1/5)), s, '1 1 0' * mySize_y*(1/5), '1 1 1', a * hud_alpha_fg, DRAWFLAG_NORMAL);
1995                 s = textShortenToWidth(vote_called_vote, mySize_x * 0.96, '10 0 0', stringwidth_colors);
1996                 if(hud_configure)
1997                         s = "Configure the HUD";
1998                 drawcolorcodedstring(pos + '0.52 0 0' * mySize_x + '0 0.3 0' * mySize_y - eX * stringwidth(s, FALSE, '1 1 0' * 0.5 * mySize_y*(1/6)), s, '1 1 0' * mySize_y*(1/6), a * hud_alpha_fg, DRAWFLAG_NORMAL);
1999
2000                 // print the yes/no counts
2001                 s = strcat("Yes: ", ftos(vote_yescount));
2002                 drawstring(pos + '0 0.6 0' * mySize_y + '0.02 0 0' * mySize_x, s, '1 1 0' * mySize_y*(1/6) , eY, a * hud_alpha_fg, DRAWFLAG_NORMAL);
2003                 s = strcat("No: ", ftos(vote_nocount));
2004                 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 * hud_alpha_fg, DRAWFLAG_NORMAL);
2005
2006                 // draw the progress bars
2007                 drawsetcliparea(pos_x, pos_y, mySize_x * 0.5 * (vote_yescount/vote_needed), mySize_y);
2008                 drawpic_skin(pos, "voteprogress_prog", mySize, eY, a * hud_alpha_fg, DRAWFLAG_NORMAL);
2009
2010                 drawsetcliparea(pos_x + mySize_x - mySize_x * 0.5 * (vote_nocount/vote_needed), pos_y, mySize_x * 0.5, mySize_y);
2011                 drawpic_skin(pos, "voteprogress_prog", mySize, eX, a * hud_alpha_fg, DRAWFLAG_NORMAL);
2012
2013                 // draw the highlights
2014                 if(vote_highlighted == 1) {
2015                         drawsetcliparea(pos_x, pos_y, mySize_x * 0.5, mySize_y);
2016                         drawpic_skin(pos, "voteprogress_voted", mySize, eY, a * hud_alpha_fg, DRAWFLAG_NORMAL);
2017                 }
2018                 else if(vote_highlighted == 2) {
2019                         drawsetcliparea(pos_x + 0.5 * mySize_x, pos_y, mySize_x * 0.5, mySize_y);
2020                         drawpic_skin(pos, "voteprogress_voted", mySize, eX, a * hud_alpha_fg, DRAWFLAG_NORMAL);
2021                 }
2022
2023                 drawresetcliparea();
2024         }
2025         else if(!vote_active) {
2026                 vote_highlighted = 0;
2027         }
2028 }
2029
2030 // Awards system
2031 float race_status_time;
2032 float race_status_prev;
2033 string race_status_name_prev;
2034 void HUD_DrawRaceStatus(vector pos)
2035 {
2036         if (race_status != race_status_prev || race_status_name != race_status_name_prev) {
2037                 race_status_time = time + 5;
2038                 race_status_prev = race_status;
2039                 if (race_status_name_prev)
2040                         strunzone(race_status_name_prev);
2041                 race_status_name_prev = strzone(race_status_name);
2042         }
2043
2044         float a;
2045         a = bound(0, race_status_time - time, 1);
2046
2047         string s;
2048         s = textShortenToWidth(race_status_name, 120, '10 10 0', stringwidth_colors);
2049
2050         float rank;
2051         if(race_status > 0)
2052                 rank = race_CheckName(race_status_name);
2053         string rankname;
2054         rankname = race_PlaceName(rank);
2055
2056         if(race_status == 0)
2057                 drawpic_skin(pos, "race_newfail", '80 80 0', '1 1 1', hud_alpha_fg * a, DRAWFLAG_NORMAL);
2058         else if(race_status == 1) {
2059                 drawpic_skin(pos, "race_newtime", '80 80 0', '1 1 1', hud_alpha_fg * a, DRAWFLAG_NORMAL);
2060                 drawcolorcodedstring(pos + '40 80 0' - eX * stringwidth(s, TRUE, '5 0 0'), s, '10 10 0', hud_alpha_fg * a, DRAWFLAG_NORMAL);
2061                 drawstring(pos + '40 20 0' - eX * stringwidth(rankname, TRUE, '7 0 0'), rankname, '14 14 0', '1 1 1', hud_alpha_fg * a, DRAWFLAG_NORMAL);
2062         } else if(race_status == 2) {
2063                 if(race_status_name == GetPlayerName(player_localentnum -1) || !race_myrank || race_myrank < rank)
2064                         drawpic_skin(pos, "race_newrankgreen", '80 80 0', '1 1 1', hud_alpha_fg * a, DRAWFLAG_NORMAL);
2065                 else
2066                         drawpic_skin(pos, "race_newrankyellow", '80 80 0', '1 1 1', hud_alpha_fg * a, DRAWFLAG_NORMAL);
2067                 drawcolorcodedstring(pos + '40 80 0' - eX * stringwidth(s, TRUE, '5 0 0'), s, '10 10 0', hud_alpha_fg * a, DRAWFLAG_NORMAL);
2068                 drawstring(pos + '40 20 0' - eX * stringwidth(rankname, TRUE, '7 0 0'), rankname, '14 14 0', '1 1 1', hud_alpha_fg * a, DRAWFLAG_NORMAL);
2069         } else if(race_status == 3) {
2070                 drawpic_skin(pos, "race_newrecordserver", '80 80 0', '1 1 1', hud_alpha_fg * a, DRAWFLAG_NORMAL);
2071                 drawcolorcodedstring(pos + '40 80 0' - eX * stringwidth(s, TRUE, '5 0 0'), s, '10 10 0', hud_alpha_fg * a, DRAWFLAG_NORMAL);
2072                 drawstring(pos + '40 20 0' - eX * stringwidth(rankname, TRUE, '7 0 0'), rankname, '14 14 0', '1 1 1', hud_alpha_fg * a, DRAWFLAG_NORMAL);
2073         }
2074
2075         if (race_status_time - time <= 0) {
2076                 race_status_prev = -1;
2077                 race_status = -1;
2078                 if(race_status_name)
2079                         strunzone(race_status_name);
2080                 race_status_name = string_null;
2081                 if(race_status_name_prev)
2082                         strunzone(race_status_name_prev);
2083                 race_status_name_prev = string_null;
2084         }
2085 }
2086
2087 // CTF HUD modicon section
2088 float redflag_prevframe, blueflag_prevframe; // status during previous frame
2089 float redflag_prevstatus, blueflag_prevstatus; // last remembered status
2090 float redflag_statuschange_time, blueflag_statuschange_time; // time when the status changed
2091
2092 void CSQC_ctf_hudreset(void)
2093 {
2094         redflag_prevstatus = blueflag_prevstatus = redflag_prevframe = blueflag_prevframe = redflag_statuschange_time = blueflag_statuschange_time = 0;
2095 }
2096
2097 void CSQC_ctf_hud(void)
2098 {
2099         vector bottomleft, redflag_pos, blueflag_pos, sz;
2100         float f; // every function should have that
2101         bottomleft_y = vid_conheight;
2102         bottomleft_z = 0;
2103
2104         float redflag, blueflag; // current status
2105         float redflag_statuschange_elapsedtime, blueflag_statuschange_elapsedtime; // time since the status changed
2106         float stat_items;
2107
2108         stat_items = getstati(STAT_ITEMS);
2109         redflag = (stat_items/IT_RED_FLAG_TAKEN) & 3;
2110         blueflag = (stat_items/IT_BLUE_FLAG_TAKEN) & 3;
2111
2112         // when status CHANGES, set old status into prevstatus and current status into status
2113         if (redflag != redflag_prevframe)
2114         {
2115                 redflag_statuschange_time = time;
2116                 redflag_prevstatus = redflag_prevframe;
2117                 redflag_prevframe = redflag;
2118         }
2119
2120         if (blueflag != blueflag_prevframe)
2121         {
2122                 blueflag_statuschange_time = time;
2123                 blueflag_prevstatus = blueflag_prevframe;
2124                 blueflag_prevframe = blueflag;
2125         }
2126
2127         redflag_statuschange_elapsedtime = time - redflag_statuschange_time;
2128         blueflag_statuschange_elapsedtime = time - blueflag_statuschange_time;
2129
2130         float BLINK_FACTOR = 0.15;
2131         float BLINK_BASE = 0.85;
2132         // note:
2133         //   RMS = sqrt(BLINK_BASE^2 + 0.5 * BLINK_FACTOR^2)
2134         // thus
2135         //   BLINK_BASE = sqrt(RMS^2 - 0.5 * BLINK_FACTOR^2)
2136         // ensure RMS == 1
2137         float BLINK_FREQ = 5; // circle frequency, = 2*pi*frequency in hertz
2138
2139         string red_icon, red_icon_prevstatus;
2140         float red_alpha, red_alpha_prevstatus;
2141         red_alpha = red_alpha_prevstatus = 1;
2142         switch(redflag) {
2143                 case 1: red_icon = "flag_red_taken"; break;
2144                 case 2: red_icon = "flag_red_lost"; break;
2145                 case 3: red_icon = "flag_red_carrying"; red_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
2146                 default:
2147                         if((stat_items & IT_CTF_SHIELDED) && (myteam == COLOR_TEAM2))
2148                                 red_icon = "flag_red_shielded";
2149                         else
2150                                 red_icon = string_null;
2151                         break;
2152         }
2153         switch(redflag_prevstatus) {
2154                 case 1: red_icon_prevstatus = "flag_red_taken"; break;
2155                 case 2: red_icon_prevstatus = "flag_red_lost"; break;
2156                 case 3: red_icon_prevstatus = "flag_red_carrying"; red_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
2157                 default:
2158                         if(redflag == 3)
2159                                 red_icon_prevstatus = "flag_red_carrying"; // make it more visible
2160                         else if((stat_items & IT_CTF_SHIELDED) && (myteam == COLOR_TEAM2))
2161                                 red_icon_prevstatus = "flag_red_shielded";
2162                         else
2163                                 red_icon_prevstatus = string_null;
2164                         break;
2165         }
2166
2167         string blue_icon, blue_icon_prevstatus;
2168         float blue_alpha, blue_alpha_prevstatus;
2169         blue_alpha = blue_alpha_prevstatus = 1;
2170         switch(blueflag) {
2171                 case 1: blue_icon = "flag_blue_taken"; break;
2172                 case 2: blue_icon = "flag_blue_lost"; break;
2173                 case 3: blue_icon = "flag_blue_carrying"; blue_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
2174                 default:
2175                         if((stat_items & IT_CTF_SHIELDED) && (myteam == COLOR_TEAM1))
2176                                 blue_icon = "flag_blue_shielded";
2177                         else
2178                                 blue_icon = string_null;
2179                         break;
2180         }
2181         switch(blueflag_prevstatus) {
2182                 case 1: blue_icon_prevstatus = "flag_blue_taken"; break;
2183                 case 2: blue_icon_prevstatus = "flag_blue_lost"; break;
2184                 case 3: blue_icon_prevstatus = "flag_blue_carrying"; blue_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
2185                 default:
2186                         if(blueflag == 3)
2187                                 blue_icon_prevstatus = "flag_blue_carrying"; // make it more visible
2188                         else if((stat_items & IT_CTF_SHIELDED) && (myteam == COLOR_TEAM1))
2189                                 blue_icon_prevstatus = "flag_blue_shielded";
2190                         else
2191                                 blue_icon_prevstatus = string_null;
2192                         break;
2193         }
2194
2195         if (myteam == COLOR_TEAM1) { // always draw own flag on left
2196                 redflag_pos = bottomleft - '-4 50 0';
2197                 blueflag_pos = bottomleft - '-62 50 0';
2198         } else {
2199                 blueflag_pos = bottomleft - '-4 50 0';
2200                 redflag_pos = bottomleft - '-62 50 0';
2201         }
2202
2203         sz = '52 52 0';
2204
2205         f = bound(0, redflag_statuschange_elapsedtime*2, 1);
2206         if(red_icon_prevstatus && f < 1)
2207                 drawpic_expanding(redflag_pos, red_icon_prevstatus, sz, '1 1 1', hud_alpha_fg * red_alpha_prevstatus, DRAWFLAG_NORMAL, f);
2208         if(red_icon)
2209                 drawpic_skin(redflag_pos, red_icon, sz, '1 1 1', hud_alpha_fg * red_alpha * f, DRAWFLAG_NORMAL);
2210
2211         f = bound(0, blueflag_statuschange_elapsedtime*2, 1);
2212         if(blue_icon_prevstatus && f < 1)
2213                 drawpic_expanding(blueflag_pos, blue_icon_prevstatus, sz, '1 1 1', hud_alpha_fg * blue_alpha_prevstatus, DRAWFLAG_NORMAL, f);
2214         if(blue_icon)
2215                 drawpic_skin(blueflag_pos, blue_icon, sz, '1 1 1', hud_alpha_fg * blue_alpha * f, DRAWFLAG_NORMAL);
2216 }
2217
2218 /*void HUD_Mod_Race (void) {
2219         if((scores_flags[ps_primary] & SFL_TIME) && !teamplay) { // race/cts record display on HUD
2220                 pl = players.sort_next;
2221                 if(pl == me)
2222                         pl = pl.sort_next;
2223                 if(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)
2224                         if(pl.scores[ps_primary] == 0)
2225                                 pl = world;
2226
2227                 score = me.(scores[ps_primary]);
2228
2229                 float racemin, racesec, racemsec;
2230                 float distsec, distmsec, minusplus;
2231
2232                 racemin = floor(score/(60 * TIME_FACTOR));
2233                 racesec = floor((score - racemin*(60 * TIME_FACTOR))/TIME_FACTOR);
2234                 racemsec = score - racemin*60*TIME_FACTOR - racesec*TIME_FACTOR;
2235
2236                 if (pl && ((!(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)) || score)) {
2237                         // distribution display
2238                         distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
2239
2240                         if (distribution < TIME_FACTOR && distribution > -TIME_FACTOR)
2241                                 distmsec = fabs(distribution);
2242                         else {
2243                                 distsec = floor(fabs(distribution)/TIME_FACTOR);
2244                                 distmsec = fabs(distribution) - distsec*TIME_FACTOR;
2245                                 if (distribution < 0)
2246                                         distsec = -distsec;
2247                         }
2248
2249                         if (distribution <= 0) {
2250                                 distribution_color = eY;
2251                                 minusplus = 1; // minusplus 1: always prefix with minus sign
2252                         }
2253                         else {
2254                                 distribution_color = eX;
2255                                 minusplus = 2; // minusplus 1: always prefix with plus sign
2256                         }
2257                         HUD_DrawXNum(bottomright - '0 48 0' - '16 0 0' * TIME_DECIMALS, distmsec, -TIME_DECIMALS, 0, 16, distribution_color, 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
2258                         HUD_DrawXNum(bottomright - '68 48 0' - '16 0 0' * TIME_DECIMALS, distsec, 4, minusplus, 16, distribution_color, 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
2259                         drawpic_skin(bottomright - '10 48 0' - '16 0 0' * TIME_DECIMALS, "num_dot", '16 16 0', distribution_color, hud_alpha_fg, DRAWFLAG_ADDITIVE);
2260                 }
2261                 // race record display
2262                 if (distribution <= 0 || distribution == score) // draw the highlight background behind the timer if we have the lead
2263                         drawpic_skin(bottomright - '0 32 0' - '32 0 0' * (4 + TIME_DECIMALS), "highlight_4", '0 28 0' + '32 0 0' * (4 + TIME_DECIMALS), '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2264
2265                 HUD_DrawXNum(bottomright - '0 32 0' - TIME_DECIMALS * '30 0 0', racemsec, -TIME_DECIMALS, 0, 30, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
2266                 HUD_DrawXNum(bottomright - '0 32 0' - TIME_DECIMALS * '30 0 0'  - '66 0 0', racesec, -2, 0, 30, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
2267                 drawpic_skin(bottomright - '0 32 0' - TIME_DECIMALS * '30 0 0' - '18 0 0', "num_dot", '30 30 0', '1 1 1', hud_alpha_fg, DRAWFLAG_ADDITIVE);
2268
2269                 HUD_DrawXNum(bottomright - '0 32 0' - TIME_DECIMALS * '30 0 0' - '132 0 0', racemin, -2, 0, 30, '1 1 1', 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
2270                 drawpic_skin(bottomright - '0 32 0' - TIME_DECIMALS * '30 0 0' - '84 0 0', "num_colon", '30 30 0', '1 1 1', hud_alpha_fg, DRAWFLAG_ADDITIVE);
2271         }
2272 }*/
2273
2274 // Keyhunt HUD modicon section
2275 float kh_runheretime;
2276
2277 void CSQC_kh_hudreset(void)
2278 {
2279         kh_runheretime = 0;
2280 }
2281
2282 void CSQC_kh_hud(void)
2283 {
2284         float kh_keys;
2285         float keyteam;
2286         float a, aa;
2287         vector p, pa, kh_size, kh_asize;
2288
2289         p_x = 6;
2290         p_y = vid_conheight - 34 - 3;
2291         p_z = 0;
2292
2293         kh_keys = getstati(STAT_KH_KEYS);
2294
2295         kh_size = '19 34 0';
2296         kh_asize = '19 10 0';
2297         pa = p + '0 -10 0';
2298
2299         float i, key;
2300
2301         float keycount;
2302         keycount = 0;
2303         for(i = 0; i < 4; ++i)
2304         {
2305                 key = floor(kh_keys / pow(32, i)) & 31;
2306                 keyteam = key - 1;
2307                 if(keyteam == 30 && keycount <= 4)
2308                         keycount += 4;
2309                 if(keyteam == myteam || keyteam == -1 || keyteam == 30)
2310                         keycount += 1;
2311         }
2312         // this yields 8 exactly if "RUN HERE" shows
2313
2314         if(keycount == 8)
2315         {
2316                 if(!kh_runheretime)
2317                         kh_runheretime = time;
2318                 pa_y -= fabs(sin((time - kh_runheretime) * 3.5)) * 6; // make the arrows jump in case of RUN HERE
2319         }
2320         else
2321                 kh_runheretime = 0;
2322
2323         for(i = 0; i < 4; ++i)
2324         {
2325                 key = floor(kh_keys / pow(32, i)) & 31;
2326                 keyteam = key - 1;
2327                 switch(keyteam)
2328                 {
2329                         case 30: // my key
2330                                 keyteam = myteam;
2331                                 a = 1;
2332                                 aa = 1;
2333                                 break;
2334                         case -1: // no key
2335                                 a = 0;
2336                                 aa = 0;
2337                                 break;
2338                         default: // owned or dropped
2339                                 a = 0.2;
2340                                 aa = 0.5;
2341                                 break;
2342                 }
2343                 a = a * hud_alpha_fg;
2344                 aa = aa * hud_alpha_fg;
2345                 if(a > 0)
2346                 {
2347                         switch(keyteam)
2348                         {
2349                                 case COLOR_TEAM1:
2350                                         drawpic (pa, "kh_redarrow", kh_asize, '1 1 1', aa, DRAWFLAG_NORMAL);  // show 30% alpha key
2351                                         break;
2352                                 case COLOR_TEAM2:
2353                                         drawpic (pa, "kh_bluearrow", kh_asize, '1 1 1', aa, DRAWFLAG_NORMAL);  // show 30% alpha key
2354                                         break;
2355                                 case COLOR_TEAM3:
2356                                         drawpic (pa, "kh_yellowarrow", kh_asize, '1 1 1', aa, DRAWFLAG_NORMAL);  // show 30% alpha key
2357                                         break;
2358                                 case COLOR_TEAM4:
2359                                         drawpic (pa, "kh_pinkarrow", kh_asize, '1 1 1', aa, DRAWFLAG_NORMAL);  // show 30% alpha key
2360                                         break;
2361                                 default:
2362                                         break;
2363                         }
2364                         switch(i) // YAY! switch(i) inside a for loop for i. DailyWTF, here we come!
2365                         {
2366                                 case 0:
2367                                         drawpic (p, "kh_red", kh_size, '1 1 1', a, DRAWFLAG_NORMAL);  // show 30% alpha key
2368                                         break;
2369                                 case 1:
2370                                         drawpic (p, "kh_blue", kh_size, '1 1 1', a, DRAWFLAG_NORMAL);  // show 30% alpha key
2371                                         break;
2372                                 case 2:
2373                                         drawpic (p, "kh_yellow", kh_size, '1 1 1', a, DRAWFLAG_NORMAL);  // show 30% alpha key
2374                                         break;
2375                                 case 3:
2376                                         drawpic (p, "kh_pink", kh_size, '1 1 1', a, DRAWFLAG_NORMAL);  // show 30% alpha key
2377                                         break;
2378                         }
2379                 }
2380                 p_x += 24;
2381                 pa_x += 24;
2382         }
2383 }
2384
2385 // Nexball HUD modicon section
2386 #define NBPB_SIZE '96 38 0'
2387 #define NBPB_BT 2                   //thickness
2388 #define NBPB_BRGB '1 1 1'
2389 #define NBPB_BALPH 1                //alpha
2390 #define NBPB_BFLAG DRAWFLAG_NORMAL
2391 #define NBPB_IALPH 0.4
2392 #define NBPB_IFLAG DRAWFLAG_NORMAL
2393 #define NBPB_IRGB '0.7 0.1 0'
2394
2395 void CSQC_nb_hud(void)
2396 {
2397         float stat_items, nb_pb_starttime, dt, p;
2398         vector pos;
2399
2400         stat_items = getstati(STAT_ITEMS);
2401         nb_pb_starttime = getstatf(STAT_NB_METERSTART);
2402
2403         pos_x = 4;
2404         pos_y = vid_conheight - 42;
2405         pos_z = 0;
2406
2407         //Manage the progress bar if any
2408         if (nb_pb_starttime > 0)
2409         {
2410                 vector s;
2411                 dt = mod(time - nb_pb_starttime, nb_pb_period);
2412                 // one period of positive triangle
2413                 p = 2 * dt / nb_pb_period;
2414                 if (p > 1)
2415                         p = 2 - p;
2416
2417                 s = NBPB_SIZE;
2418                 //Draw the filling
2419                 drawfill(pos, p * s_x * eX + s_y * eY, NBPB_IRGB, NBPB_IALPH, NBPB_IFLAG);
2420
2421                 //Draw the box
2422                 s = NBPB_SIZE;
2423                 drawline(NBPB_BT, pos    , pos + eX * s_x, NBPB_BRGB, NBPB_BALPH, NBPB_BFLAG);
2424                 drawline(NBPB_BT, pos    , pos + eY * s_y, NBPB_BRGB, NBPB_BALPH, NBPB_BFLAG);
2425                 drawline(NBPB_BT, pos + s, pos + eX * s_x, NBPB_BRGB, NBPB_BALPH, NBPB_BFLAG);
2426                 drawline(NBPB_BT, pos + s, pos + eY * s_y, NBPB_BRGB, NBPB_BALPH, NBPB_BFLAG);
2427         }
2428
2429         pos_x += 12; //horizontal margin to the picture
2430         pos_y += 2; //vertical margin to the picture
2431
2432         if (stat_items & IT_KEY1)
2433                 drawpic_skin(pos, "nexball_carrying", '80 34 0', '1 1 1', 1, DRAWFLAG_NORMAL);
2434 }
2435
2436 // Race/CTS HUD modicon section
2437 float crecordtime_prev; // last remembered crecordtime
2438 float crecordtime_change_time; // time when crecordtime last changed
2439 float srecordtime_prev; // last remembered srecordtime
2440 float srecordtime_change_time; // time when srecordtime last changed
2441 void CSQC_race_hud(void)
2442 {
2443         entity me;
2444         me = playerslots[player_localentnum - 1];
2445         float t, score;
2446         float f; // yet another function has this
2447         score = me.(scores[ps_primary]);
2448
2449         if not((scores_flags[ps_primary] & SFL_TIME) && !teamplay) // race/cts record display on HUD
2450                 return; // no records in the actual race
2451
2452         drawfont = hud_bigfont;
2453         vector pos;
2454         pos_x = 2;
2455         pos_y = vid_conheight - 48;
2456
2457         // clientside personal record
2458         string rr;
2459         if(gametype == GAME_CTS)
2460                 rr = CTS_RECORD;
2461         else
2462                 rr = RACE_RECORD;
2463         t = stof(db_get(ClientProgsDB, strcat(shortmapname, rr, "time")));
2464
2465         if(score && (score < t || !t)) {
2466                 db_put(ClientProgsDB, strcat(shortmapname, rr, "time"), ftos(score));
2467                 if(cvar("cl_autodemo_delete_keeprecords"))
2468                 {
2469                         f = cvar("cl_autodemo_delete");
2470                         f &~= 1;
2471                         cvar_set("cl_autodemo_delete", ftos(f)); // don't delete demo with new record!
2472                 }
2473         }
2474
2475         if(t != crecordtime_prev) {
2476                 crecordtime_prev = t;
2477                 crecordtime_change_time = time;
2478         }
2479         f = time - crecordtime_change_time;
2480
2481         if (f > 1) {
2482                 drawstring(pos, "Personal best ", '10 10 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2483                 drawstring(pos + '0 10 0', TIME_ENCODED_TOSTRING(t),'14 14 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2484         } else {
2485                 drawstring(pos, "Personal best ", '10 10 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2486                 drawstring(pos + '0 10 0', TIME_ENCODED_TOSTRING(t),'14 14 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2487                 drawstring_expanding(pos, "Personal best ", '10 10 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL, f);
2488                 drawstring_expanding(pos + '0 10 0', TIME_ENCODED_TOSTRING(t),'14 14 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL, f);
2489         }
2490
2491         // server record
2492         pos_y += 26;
2493         t = race_server_record;
2494         if(t != srecordtime_prev) {
2495                 srecordtime_prev = t;
2496                 srecordtime_change_time = time;
2497         }
2498         f = time - srecordtime_change_time;
2499
2500         if (f > 1) {
2501                 drawstring(pos, "Server best ", '10 10 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2502                 drawstring(pos + '0 10 0', TIME_ENCODED_TOSTRING(t),'14 14 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2503         } else {
2504                 drawstring(pos, "Server best ", '10 10 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2505                 drawstring(pos + '0 10 0', TIME_ENCODED_TOSTRING(t),'14 14 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2506                 drawstring_expanding(pos, "Server best ", '10 10 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL, f);
2507                 drawstring_expanding(pos + '0 10 0', TIME_ENCODED_TOSTRING(t),'14 14 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL, f);
2508         }
2509         drawfont = hud_font;
2510 }
2511
2512 // Timer (#5)
2513 //
2514 void HUD_Timer()
2515 {
2516         vector pos, mySize;
2517         pos = HUD_Panel_GetPos(5);
2518         mySize = HUD_Panel_GetSize(5);
2519
2520         HUD_Panel_DrawBg(5, pos, mySize);
2521
2522         float timelimit, elapsedTime, minutes, seconds, timeleft, minutesLeft, secondsLeft;
2523
2524         timelimit = getstatf(STAT_TIMELIMIT);
2525
2526         HUD_DrawRaceStatus(pos + '0 30 0');
2527
2528         timeleft = max(0, timelimit * 60 + getstatf(STAT_GAMESTARTTIME) - time);
2529         timeleft = ceil(timeleft);
2530         minutesLeft = floor(timeleft / 60);
2531         secondsLeft = timeleft - minutesLeft*60;
2532
2533         vector timer_color;
2534         if(minutesLeft >= 5 || warmup_stage || timelimit == 0) //don't use red or yellow in warmup or when there is no timelimit
2535                 timer_color = '1 1 1'; //white
2536         else if(minutesLeft >= 1)
2537                 timer_color = '1 1 0'; //yellow
2538         else
2539                 timer_color = '1 0 0'; //red
2540
2541         if (cvar("hud_timer_increment") || timelimit == 0 || warmup_stage) {
2542                 if (time < getstatf(STAT_GAMESTARTTIME)) {
2543                         //while restart is still active, show 00:00
2544                         minutes = seconds = 0;
2545                 } else {
2546                         elapsedTime = floor(time - getstatf(STAT_GAMESTARTTIME)); //127
2547                         minutes = floor(elapsedTime / 60);
2548                         seconds = elapsedTime - minutes*60;
2549                 }
2550         } else {
2551                 minutes = minutesLeft;
2552                 seconds = secondsLeft;
2553         }
2554
2555         if(mySize_x/mySize_y > 5.1)
2556         {
2557                 if(minutes > 999)
2558                         seconds = 99;
2559                 minutes = min(minutes, 999);
2560                 if(minutesLeft >= 1 || cvar("hud_timer_increment") || timelimit == 0 || warmup_stage) {
2561                         if(minutes < 100)
2562                                 drawpic_skin(pos + eX * mySize_x - eX * 5.1 * mySize_y, "timer", '1 1 0' * mySize_y, timer_color, hud_alpha_fg, DRAWFLAG_NORMAL);
2563                         HUD_DrawXNum(pos + eX * mySize_x - eX * 5.1 * mySize_y, minutes, 3, 0, mySize_y, timer_color, 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
2564                         drawpic_skin(pos + eX * mySize_x - eX * 2.57 * mySize_y, "num_colon", '1 1 0' * mySize_y, timer_color, hud_alpha_fg, DRAWFLAG_NORMAL);
2565                 }
2566                 HUD_DrawXNum(pos + eX * mySize_x - eX * 2 * mySize_y, seconds, -2, 0, mySize_y, timer_color, 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
2567         }
2568         else
2569         {
2570                 if(minutes > 99)
2571                         seconds = 99;
2572                 minutes = min(minutes, 99);
2573                 if(minutesLeft >= 1 || cvar("hud_timer_increment") || timelimit == 0 || warmup_stage) {
2574                         if(minutes < 100)
2575                                 drawpic_skin(pos + eX * 0.5 * mySize_x - eX * 0.5 * 0.5 * mySize_y, "timer", '0.5 0.5 0' * mySize_y, timer_color, hud_alpha_fg, DRAWFLAG_NORMAL);
2576                         HUD_DrawXNum(pos + eX * 0.5 * mySize_x - eX * mySize_y + eY * 0.5 * mySize_y, minutes, -2, 0, 0.5 * mySize_y, timer_color, 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
2577                         drawpic_skin(pos + eX * 0.5 * mySize_x - eX * 0.5 * 0.5 * mySize_y + eY * 0.5 * mySize_y, "num_colon", '0.5 0.5 0' * mySize_y, timer_color, hud_alpha_fg, DRAWFLAG_NORMAL);
2578                 }
2579                 HUD_DrawXNum(pos + eX * 0.51 * mySize_x + eY * 0.5 * mySize_y, seconds, -2, 0, 0.5 * mySize_y, timer_color, 0, 0, hud_alpha_fg, DRAWFLAG_NORMAL);
2580         }
2581 }
2582
2583 // Radar (#6)
2584 //
2585
2586 void HUD_Radar(void)
2587 {
2588         vector pos, mySize;
2589         pos = HUD_Panel_GetPos(6);
2590         mySize = HUD_Panel_GetSize(6);
2591
2592         HUD_Panel_DrawBg(6, pos, mySize);
2593
2594         local float color1, color2; // color already declared as a global in hud.qc
2595         local vector rgb;
2596         local entity tm;
2597         float scale2d, normalsize, bigsize;
2598         float f;
2599
2600         teamradar_origin2d = pos + 0.5 * mySize; // TODO: stupid compat, should be removed
2601         teamradar_size2d = mySize;
2602
2603         if(minimapname == "" && !ons_showmap)
2604                 return;
2605
2606         teamradar_loadcvars();
2607
2608         switch(cl_teamradar_zoommode)
2609         {
2610                 default:
2611                 case 0:
2612                         f = current_zoomfraction;
2613                         break;
2614                 case 1:
2615                         f = 1 - current_zoomfraction;
2616                         break;
2617                 case 2:
2618                         f = 0;
2619                         break;
2620                 case 3:
2621                         f = 1;
2622                         break;
2623         }
2624
2625         switch(cl_teamradar_rotation)
2626         {
2627                 case 0:
2628                         teamradar_angle = view_angles_y - 90;
2629                         break;
2630                 default:
2631                         teamradar_angle = 90 * cl_teamradar_rotation;
2632                         break;
2633         }
2634
2635         scale2d = vlen_maxnorm2d(mi_picmax - mi_picmin);
2636         teamradar_size2d = mySize;
2637
2638         teamradar_extraclip_mins = teamradar_extraclip_maxs = '0 0 0'; // we always center
2639
2640         // pixels per world qu to match the teamradar_size2d_x range in the longest dimension
2641         if(cl_teamradar_rotation == 0)
2642         {
2643                 // max-min distance must fit the radar in any rotation
2644                 bigsize = vlen_minnorm2d(teamradar_size2d) * scale2d / (1.05 * vlen2d(mi_max - mi_min));
2645         }
2646         else
2647         {
2648                 vector c0, c1, c2, c3, span;
2649                 c0 = rotate(mi_min, teamradar_angle * DEG2RAD);
2650                 c1 = rotate(mi_max, teamradar_angle * DEG2RAD);
2651                 c2 = rotate('1 0 0' * mi_min_x + '0 1 0' * mi_max_y, teamradar_angle * DEG2RAD);
2652                 c3 = rotate('1 0 0' * mi_max_x + '0 1 0' * mi_min_y, teamradar_angle * DEG2RAD);
2653                 span = '0 0 0';
2654                 span_x = max4(c0_x, c1_x, c2_x, c3_x) - min4(c0_x, c1_x, c2_x, c3_x);
2655                 span_y = max4(c0_y, c1_y, c2_y, c3_y) - min4(c0_y, c1_y, c2_y, c3_y);
2656
2657                 // max-min distance must fit the radar in x=x, y=y
2658                 bigsize = min(
2659                         teamradar_size2d_x * scale2d / (1.05 * span_x),
2660                         teamradar_size2d_y * scale2d / (1.05 * span_y)
2661                 );
2662         }
2663
2664         normalsize = vlen_maxnorm2d(teamradar_size2d) * scale2d / cl_teamradar_scale;
2665         if(bigsize > normalsize)
2666                 normalsize = bigsize;
2667
2668         teamradar_size =
2669                   f * bigsize
2670                 + (1 - f) * normalsize;
2671         teamradar_origin3d_in_texcoord = teamradar_3dcoord_to_texcoord(
2672                   f * (mi_min + mi_max) * 0.5
2673                 + (1 - f) * view_origin);
2674
2675         color1 = GetPlayerColor(player_localentnum-1);
2676         rgb = GetTeamRGB(color1);
2677
2678         drawsetcliparea(
2679                 pos_x,
2680                 pos_y,
2681                 mySize_x,
2682                 mySize_y
2683         );
2684
2685         draw_teamradar_background(cl_teamradar_background_alpha, cl_teamradar_foreground_alpha);
2686
2687         if(ons_showmap)
2688         {
2689                 drawresetcliparea();
2690
2691                 vector frame_origin, frame_size;
2692                 frame_origin = frame_size = '0 0 0';
2693
2694                 frame_origin_x = pos_x - teamradar_size2d_x * 0.55859375; // matches the picture
2695                 frame_origin_y = pos_y - teamradar_size2d_y * 0.55859375; // matches the picture
2696                 frame_size_x = pos_x * 1.1171875; // matches the picture
2697                 frame_size_y = pos_y * 1.1171875; // matches the picture
2698                 drawpic_skin(frame_origin, "gfx/ons-frame.tga", frame_size, '1 1 1', hud_alpha_fg, 0);
2699                 drawpic_skin(frame_origin, "gfx/ons-frame-team.tga", frame_size, rgb, hud_alpha_fg, 0);
2700
2701                 drawsetcliparea(
2702                         pos_x - teamradar_size2d_x * 0.5,
2703                         pos_y - teamradar_size2d_y * 0.5,
2704                         teamradar_size2d_x,
2705                         teamradar_size2d_y
2706                 );
2707         }
2708
2709         for(tm = world; (tm = find(tm, classname, "radarlink")); )
2710                 draw_teamradar_link(tm.origin, tm.velocity, tm.team);
2711         for(tm = world; (tm = findflags(tm, teamradar_icon, 0xFFFFFF)); )
2712                 draw_teamradar_icon(tm.origin, tm.teamradar_icon, tm, tm.teamradar_color, hud_alpha_fg);
2713         for(tm = world; (tm = find(tm, classname, "entcs_receiver")); )
2714         {
2715                 color2 = GetPlayerColor(tm.sv_entnum);
2716                 //if(color == COLOR_SPECTATOR || color == color2)
2717                         draw_teamradar_player(tm.origin, tm.angles, GetTeamRGB(color2));
2718         }
2719         draw_teamradar_player(view_origin, view_angles, '1 1 1');
2720
2721         drawresetcliparea();
2722 };
2723
2724 /*
2725 ==================
2726 Main HUD system
2727 ==================
2728 */
2729
2730 void HUD_DrawPressedKeys(void)
2731 {
2732         vector pos, mySize;
2733         pos = HUD_Panel_GetPos(10);
2734         mySize = HUD_Panel_GetSize(10);
2735
2736         HUD_Panel_DrawBg(10, pos, mySize);
2737
2738         float pressedkeys;
2739
2740         pressedkeys = getstatf(STAT_PRESSED_KEYS);
2741         drawpic_skin(pos, "keys/key_bg.tga",           mySize, '1 1 1', 0.1 * hud_alpha_fg, DRAWFLAG_NORMAL);
2742         drawpic_skin(pos + eX * mySize_x - eX * 0.22 * mySize_x +       eY * 0.195 * mySize_y, ((pressedkeys & KEY_CROUCH) ? "keys/key_crouch_inv.tga" : "keys/key_crouch.tga"),        '1 1 0' * (1/3) * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2743         drawpic_skin(pos + eX * 0.5 * mySize_x - eX * 0.23 * mySize_y + eY * 0.040 * mySize_y, ((pressedkeys & KEY_FORWARD) ? "keys/key_forward_inv.tga" : "keys/key_forward.tga"),     '1 1 0' * 0.46 * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2744         drawpic_skin(pos + eX * 0.023 * mySize_x +                      eY * 0.195 * mySize_y, ((pressedkeys & KEY_JUMP) ? "keys/key_jump_inv.tga" : "keys/key_jump.tga"),              '1 1 0' * (1/3) * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2745         drawpic_skin(pos + eX * 0.1 * mySize_x +                        eY * 0.486 * mySize_y, ((pressedkeys & KEY_LEFT) ? "keys/key_left_inv.tga" : "keys/key_left.tga"),              '1 1 0' * 0.46 * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2746         drawpic_skin(pos + eX * 0.5 * mySize_x - eX * 0.23 * mySize_y + eY * 0.486 * mySize_y, ((pressedkeys & KEY_BACKWARD) ? "keys/key_backward_inv.tga" : "keys/key_backward.tga"),  '1 1 0' * 0.46 * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2747         drawpic_skin(pos + eX * mySize_x - eX * 0.372 * mySize_x +      eY * 0.486 * mySize_y, ((pressedkeys & KEY_RIGHT) ? "keys/key_right_inv.tga" : "keys/key_right.tga"),           '1 1 0' * 0.46 * mySize_y, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2748 }
2749
2750 void HUD_ShowSpeed(void)
2751 {
2752         vector numsize;
2753         float pos, conversion_factor;
2754         string speed, zspeed, unit;
2755
2756         switch(cvar("cl_showspeed_unit"))
2757         {
2758                 default:
2759                 case 0:
2760                         unit = "";
2761                         conversion_factor = 1.0;
2762                         break;
2763                 case 1:
2764                         unit = " qu/s";
2765                         conversion_factor = 1.0;
2766                         break;
2767                 case 2:
2768                         unit = " m/s";
2769                         conversion_factor = 0.0254;
2770                         break;
2771                 case 3:
2772                         unit = " km/h";
2773                         conversion_factor = 0.0254 * 3.6;
2774                         break;
2775                 case 4:
2776                         unit = " mph";
2777                         conversion_factor = 0.0254 * 3.6 * 0.6213711922;
2778                         break;
2779                 case 5:
2780                         unit = " knots";
2781                         conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
2782                         break;
2783         }
2784
2785         speed = strcat(ftos(floor( vlen(pmove_vel - pmove_vel_z * '0 0 1') * conversion_factor + 0.5 )), unit);
2786
2787         numsize_x = numsize_y = cvar("cl_showspeed_size");
2788         pos = (vid_conheight - numsize_y) * cvar("cl_showspeed_position");
2789
2790         drawfont = hud_bigfont;
2791         drawstringcenter(eX + pos * eY, speed, numsize, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2792
2793         if (cvar("cl_showspeed_z") == 1) {
2794                 zspeed = strcat(ftos(fabs(floor( pmove_vel_z * conversion_factor + 0.5 ))), unit);
2795                 drawstringcenter(eX + pos * eY + numsize_y * eY, zspeed, numsize * 0.5, '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
2796         }
2797
2798         drawfont = hud_font;
2799 }
2800
2801 vector acc_prevspeed;
2802 float acc_prevtime;
2803 float acc_avg;
2804
2805 void HUD_ShowAcceleration(void)
2806 {
2807         float acceleration, sz, scale, alpha, f;
2808         vector pos, top, rgb;
2809         top_x = vid_conwidth/2;
2810         top_y = 0;
2811
2812         f = time - acc_prevtime;
2813         if(cvar("cl_showacceleration_z"))
2814                 acceleration = (vlen(pmove_vel) - vlen(acc_prevspeed)) * (1 / f);
2815         else
2816                 acceleration = (vlen(pmove_vel - '0 0 1' * pmove_vel_z) - vlen(acc_prevspeed - '0 0 1' * acc_prevspeed_z)) * (1 / f);
2817         acc_prevspeed = pmove_vel;
2818         acc_prevtime = time;
2819
2820         f = bound(0, f * 10, 1);
2821         acc_avg = acc_avg * (1 - f) + acceleration * f;
2822         acceleration = acc_avg / getstatf(STAT_MOVEVARS_MAXSPEED);
2823
2824         pos = top - sz/2 * eY + (cvar("cl_showacceleration_position") * vid_conheight) * eY;
2825
2826         sz = cvar("cl_showacceleration_size");
2827         scale = cvar("cl_showacceleration_scale");
2828         alpha = cvar("cl_showacceleration_alpha");
2829         if (cvar("cl_showacceleration_color_custom"))
2830                 rgb = stov(cvar_string("cl_showacceleration_color"));
2831         else {
2832                 rgb = '1 1 1';
2833                 if (acceleration < 0) {
2834                         rgb = '1 .5 .5' - '0 .5 .5' * bound(0, -acceleration * 0.2, 1);
2835                 } else if (acceleration > 0) {
2836                         rgb = '.5 1 .5' - '.5 0 .5' * bound(0, +acceleration * 0.2, 1);
2837                 }
2838         }
2839
2840         if (acceleration > 0)
2841                 drawpic_skin(pos, "statusbar", acceleration * scale * '40 0 0' + sz * eY, rgb, alpha * hud_alpha_fg, DRAWFLAG_NORMAL);
2842         else if (acceleration < 0)
2843                 drawpic_skin(pos + acceleration * scale * '40 0 0', "statusbar", -acceleration * scale * '40 0 0' + sz * eY, rgb, alpha * hud_alpha_fg, DRAWFLAG_NORMAL);
2844 }
2845
2846 void HUD_Reset (void)
2847 {
2848         // reset gametype specific icons
2849         if(gametype == GAME_KEYHUNT)
2850                 CSQC_kh_hudreset();
2851         else if(gametype == GAME_CTF)
2852                 CSQC_ctf_hudreset();
2853 }
2854
2855 void HUD_Main (void)
2856 {
2857         hud_alpha_bg = cvar_or("hud_bg_alpha", 0.8) * (1 - cvar("_menu_alpha"));
2858         hud_border_thickness = bound(0, cvar("hud_border_thickness"), 5);
2859         hud_accuracy_border_thickness = bound(0, cvar_or("hud_accuracy_border_thickness", 1), 5);
2860         hud_color_bg_team = cvar("hud_color_bg_team");
2861
2862         hud_fontsize = HUD_GetFontsize("hud_fontsize");
2863         hud_fontsize_spec = HUD_GetFontsize("hud_fontsize_spec");
2864
2865         hud_configure = cvar("_hud_configure");
2866         hud_skin = cvar_string("hud_skin");
2867
2868         // Drawing stuff
2869         if(cvar("hud_dock"))
2870                 drawpic_skin('0 0 0', "dock", eX * vid_conwidth + eY * vid_conheight, stov(cvar_string("hud_dock_color")), cvar("hud_dock_alpha"), DRAWFLAG_NORMAL);
2871
2872         if(HUD_Panel_CheckActive(0))
2873                 HUD_WeaponIcons();
2874         if(HUD_Panel_CheckActive(1))
2875                 HUD_Inventory();
2876         if(HUD_Panel_CheckActive(2))
2877                 HUD_Powerups();
2878         if(HUD_Panel_CheckActive(3))
2879                 HUD_HealthArmor();
2880         if(HUD_Panel_CheckActive(4))
2881                 HUD_Notify();
2882         if(HUD_Panel_CheckActive(5))
2883                 HUD_Timer();
2884         // TODO hud'ify
2885         if(HUD_Panel_CheckActive(6))
2886                 if(ons_showmap || cvar_string("cl_teamradar") != "0" && (cvar("cl_teamradar") == 2 || teamplay))
2887                         HUD_Radar();
2888         if(HUD_Panel_CheckActive(7))
2889                 HUD_Score();
2890         if(HUD_Panel_CheckActive(8))
2891                 if(gametype == GAME_RACE || gametype == GAME_CTS || hud_configure)
2892                         HUD_RaceTimer();
2893         if(HUD_Panel_CheckActive(9))
2894                 HUD_VoteWindow();
2895         // TODO hud'ify