]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/sbar.qc
r7377 | div0 | 2009-08-07 04:27:06 -0400 (Fri, 07 Aug 2009) | 2 lines
[divverent/nexuiz.git] / data / qcsrc / client / sbar.qc
1 void drawstringright(vector, string, vector, vector, float, float);
2 void drawstringcenter(vector, string, vector, vector, float, float);
3 void Sbar_DrawAccuracyStats();
4 void Sbar_DrawAccuracyStats_Description_Splash(vector);
5 void Sbar_DrawAccuracyStats_Description_Hitscan(vector);
6 float weapon_hits[WEP_COUNT];
7 float weapon_fired[WEP_COUNT];
8 float weapon_number;
9
10 float last_weapon;
11 float weapontime;
12
13 float sbar_alpha_fg;
14 float sbar_alpha_bg;
15 float sbar_color_bg_team;
16 float sbar_border_thickness;
17 float sbar_scoreboard_alpha_bg;
18 float sbar_scoreboard_highlight;
19 float sbar_hudselector;
20 float sbar_hud_accuracy;
21
22 float ps_primary, ps_secondary;
23 float ts_primary, ts_secondary;
24
25 vector sbar, color;
26 vector element_offset = '0 6 0'; // global item offset from the bottom edge
27
28 void CSQC_kh_hud();
29 void CSQC_ctf_hud();
30 void CSQC_nb_hud();
31 void MapVote_Draw();
32 void Sbar_FinaleOverlay()
33 {
34         /*vector pos;
35         pos_x = (vid_conwidth - 1)/2;
36         pos_y = 16;
37         pos_z = 0;*/
38
39         //drawpic(pos, "gfx/finale", '0 0 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
40
41         //drawstring(pos, "END", sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
42         MapVote_Draw();
43 }
44
45 float weaponspace[10];
46 float weapon_first, weapon_last;
47 void Sbar_DrawWeapon_Clear()
48 {
49         float idx;
50         weapon_first = -2;
51         weapon_last = -1;
52         for(idx = 0; idx < 10; ++idx)
53                 weaponspace[idx] = 0;
54         for(idx = 0; idx <= 23; ++idx)
55         {
56                 if(weaponimpulse[idx] >= 0)
57                 {
58                         if(weapon_first < 0)
59                                 weapon_first = idx;
60                         weapon_last = idx;
61                 }
62         }
63 }
64 void Sbar_DrawWeapon(float nr, float fade, float active, float wc)
65 {
66         vector pos, vsize, fill_colour;
67         float value, idx, imp, sp, weapon_hit, weapon_damage, weapon_stats;
68
69         imp = weaponimpulse[nr];
70         weapon_hit = weapon_hits[wc];
71         weapon_damage = weapon_fired[wc];
72         if(imp == 0)
73                 idx = 9;
74         else
75                 idx = imp - 1;
76
77         value = (active) ? 1 : 0.6;
78         color_x = color_y = color_z = value;
79
80         // width = 300, height = 100
81         const float w_width = 25, w_height = 12, w_space = 2, font_size = 8;
82
83         sp = weaponspace[idx] + 1;
84         weaponspace[idx] = sp;
85
86         pos_x = (vid_conwidth + 10 - w_width * 9) * 0.5 + w_width * idx;
87         pos_y = (vid_conheight - w_height * sp) - 38; // move 38 pixels up
88         pos_z = 0;
89         vsize_x = w_width;
90         vsize_y = w_height;
91         vsize_z = 0;
92         if (active)
93                 drawpic(pos, "gfx/hud/sb_ammobg", vsize, color, value * fade * sbar_alpha_fg, DRAWFLAG_NORMAL);
94         drawpic(pos, strcat("gfx/hud/inv_weapon", ftos(nr)), vsize, color, value * fade * sbar_alpha_fg, DRAWFLAG_NORMAL);
95         pos_x += w_space;
96         pos_y += w_space;
97         vsize_x = font_size;
98         vsize_y = font_size;
99         vsize_z = 0;
100         drawstring(pos, ftos(imp), vsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
101
102         // draw the weapon accuracy on the HUD
103         if(sbar_hud_accuracy)
104         {
105                 weapon_stats = rint(100*weapon_hit/weapon_damage);
106                 fill_colour_x = 1 - 0.015 * weapon_stats;
107                 fill_colour_y = 1 - 0.015 * (100 - weapon_stats);
108                 drawstringright(pos + '22 0 0', strcat(ftos(weapon_stats),"%"), '6 6 0', fill_colour, sbar_alpha_fg, 0);
109         }
110 }
111
112 void Sbar_DrawXNum (vector pos, float num, float digits, float lettersize, vector rgb, float highlighted, float stroke, float alpha, float dflags)
113 {
114         float l, i;
115         string str, tmp, l_length;
116         float minus;
117         vector vsize, num_color;
118         alpha = alpha * sbar_alpha_fg;
119
120         vsize_x = vsize_y = lettersize;
121         vsize_z = 0;
122
123         if(num < 0)
124         {
125                 minus = true;
126                 num = -num;
127                 pos_x -= lettersize;
128         } else
129                 minus = false;
130
131         if(digits < 0)
132         {
133                 tmp = ftos(num);
134                 digits = -digits;
135                 str = strcat(substring("0000000000", 0, digits - strlen(tmp)), tmp);
136         } else
137                 str = ftos(num);
138
139         l = strlen(str);
140         l_length = ftos(l);
141
142         if(l > digits)
143         {
144                 str = substring(str, l-digits, 999);
145                 l = strlen(str);
146         } else if(l < digits)
147                 pos_x += (digits-l) * lettersize;
148
149         if (highlighted == 1) {
150                 vector hl_size;
151                 hl_size_x = vsize_x * l + vsize_x * 0.2;
152                 hl_size_y = vsize_y * 1.1;
153                 hl_size_z = 0;
154                 if(minus)
155                         hl_size_x = hl_size_x + vsize_x;
156
157                 vector hl_pos;
158                 hl_pos_x = pos_x - lettersize/10;
159                 hl_pos_y = pos_y - lettersize/20;
160                 hl_pos_z = 0;
161
162                 drawpic(hl_pos, strcat("gfx/hud/sb_highlight_", l_length), hl_size, '1 1 1', sbar_alpha_fg, dflags);
163         }
164
165         if (stroke == 1)
166                 num_color = '1 1 1';
167         else
168                 num_color = rgb;
169
170         if(minus)
171         {
172                 if (stroke == 1)
173                         drawpic(pos, "gfx/hud/num_minus_stroke", vsize, rgb, alpha, dflags);
174                 drawpic(pos, "gfx/hud/num_minus", vsize, num_color, alpha, dflags);
175                 pos_x += lettersize;
176         }
177
178         for(i = 0; i < l; ++i)
179         {
180                 tmp = substring(str, i, 1);
181                 if (stroke == 1)
182                         drawpic(pos, strcat("gfx/hud/num_", tmp, "_stroke"), vsize, rgb, alpha, dflags);
183                 drawpic(pos, strcat("gfx/hud/num_", tmp), vsize, num_color, alpha, dflags);
184                 pos_x += lettersize;
185         }
186 }
187
188 void Sbar_DrawXNum_Colored (vector pos, float x, float lettersize, float alpha)
189 {
190         if(x > 200) {
191                 color_x = 0;
192                 color_y = 1;
193                 color_z = 0;
194         }
195         else if(x > 150) {
196                 color_x = 0.4 - (x-150)*0.02 * 0.4; //red value between 0.4 -> 0
197                 color_y = 0.9 + (x-150)*0.02 * 0.1; // green value between 0.9 -> 1
198                 color_z = 0;
199         }
200         else if(x > 100) {
201                 color_x = 1 - (x-100)*0.02 * 0.6; //red value between 1 -> 0.4
202                 color_y = 1 - (x-100)*0.02 * 0.1; // green value between 1 -> 0.9
203                 color_z = 1 - (x-100)*0.02; // blue value between 1 -> 0
204         }
205         else if(x > 50) {
206                 color_x = 1;
207                 color_y = 1;
208                 color_z = 0.2 + (x-50)*0.02 * 0.8; // blue value between 0.2 -> 1
209         }
210         else if(x > 20) {
211                 color_x = 1;
212                 color_y = (x-20)*90/27/100; // green value between 0 -> 1
213                 color_z = (x-20)*90/27/100 * 0.2; // blue value between 0 -> 0.2
214         }
215         else {
216                 color_x = 1;
217                 color_y = 0;
218                 color_z = 0;
219         }
220         Sbar_DrawXNum(pos, x, 3, lettersize, color, 0, 0, alpha, DRAWFLAG_NORMAL);
221 }
222
223 void Cmd_Sbar_SetFields(float argc);
224 void Sbar_InitScores()
225 {
226         float i, f;
227
228         ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
229         for(i = 0; i < MAX_SCORE; ++i)
230         {
231                 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
232                 if(f == SFL_SORT_PRIO_PRIMARY)
233                         ps_primary = i;
234                 if(f == SFL_SORT_PRIO_SECONDARY)
235                         ps_secondary = i;
236         }
237         if(ps_secondary == -1)
238                 ps_secondary = ps_primary;
239
240         for(i = 0; i < MAX_TEAMSCORE; ++i)
241         {
242                 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
243                 if(f == SFL_SORT_PRIO_PRIMARY)
244                         ts_primary = i;
245                 if(f == SFL_SORT_PRIO_SECONDARY)
246                         ts_secondary = i;
247         }
248         if(ts_secondary == -1)
249                 ts_secondary = ts_primary;
250
251         Cmd_Sbar_SetFields(0);
252 }
253
254 void Sbar_UpdatePlayerPos(entity pl);
255 float SetTeam(entity pl, float Team);
256 //float lastpnum;
257 void Sbar_UpdatePlayerTeams()
258 {
259         float Team;
260         entity pl, tmp;
261         float num;
262
263         num = 0;
264         for(pl = players.sort_next; pl; pl = pl.sort_next)
265         {
266                 num += 1;
267                 Team = GetPlayerColor(pl.sv_entnum);
268                 if(SetTeam(pl, Team))
269                 {
270                         tmp = pl.sort_prev;
271                         Sbar_UpdatePlayerPos(pl);
272                         if(tmp)
273                                 pl = tmp;
274                         else
275                                 pl = players.sort_next;
276                 }
277         }
278         /*
279         if(num != lastpnum)
280                 print(strcat("PNUM: ", ftos(num), "\n"));
281         lastpnum = num;
282         */
283 }
284
285 float Sbar_ComparePlayerScores(entity left, entity right)
286 {
287         float vl, vr;
288         vl = GetPlayerColor(left.sv_entnum);
289         vr = GetPlayerColor(right.sv_entnum);
290
291         if(!left.gotscores)
292                 vl = COLOR_SPECTATOR;
293         if(!right.gotscores)
294                 vr = COLOR_SPECTATOR;
295
296         if(vl > vr)
297                 return true;
298         if(vl < vr)
299                 return false;
300
301         if(vl == COLOR_SPECTATOR)
302         {
303                 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
304                 // no other sorting
305                 if(!left.gotscores && right.gotscores)
306                         return true;
307                 return false;
308         }
309
310         vl = left.scores[ps_primary];
311         vr = right.scores[ps_primary];
312         if(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)
313         {
314                 if(vl == 0 && vr != 0)
315                         return 1;
316                 if(vl != 0 && vr == 0)
317                         return 0;
318         }
319         if(vl > vr)
320                 return IS_INCREASING(scores_flags[ps_primary]);
321         if(vl < vr)
322                 return IS_DECREASING(scores_flags[ps_primary]);
323
324         vl = left.scores[ps_secondary];
325         vr = right.scores[ps_secondary];
326         if(scores_flags[ps_secondary] & SFL_ZERO_IS_WORST)
327         {
328                 if(vl == 0 && vr != 0)
329                         return 1;
330                 if(vl != 0 && vr == 0)
331                         return 0;
332         }
333         if(vl > vr)
334                 return IS_INCREASING(scores_flags[ps_secondary]);
335         if(vl < vr)
336                 return IS_DECREASING(scores_flags[ps_secondary]);
337
338         return false;
339 }
340
341 void Sbar_UpdatePlayerPos(entity player)
342 {
343         for(other = player.sort_next; other && Sbar_ComparePlayerScores(player, other); other = player.sort_next)
344         {
345                 SORT_SWAP(player, other);
346         }
347         for(other = player.sort_prev; other != players && Sbar_ComparePlayerScores(other, player); other = player.sort_prev)
348         {
349                 SORT_SWAP(other, player);
350         }
351 }
352
353 float Sbar_CompareTeamScores(entity left, entity right)
354 {
355         float vl, vr;
356
357         if(left.team == COLOR_SPECTATOR)
358                 return 1;
359         if(right.team == COLOR_SPECTATOR)
360                 return 0;
361
362         vl = left.teamscores[ts_primary];
363         vr = right.teamscores[ts_primary];
364         if(vl > vr)
365                 return IS_INCREASING(teamscores_flags[ts_primary]);
366         if(vl < vr)
367                 return IS_DECREASING(teamscores_flags[ts_primary]);
368
369         vl = left.teamscores[ts_secondary];
370         vr = right.teamscores[ts_secondary];
371         if(vl > vr)
372                 return IS_INCREASING(teamscores_flags[ts_secondary]);
373         if(vl < vr)
374                 return IS_DECREASING(teamscores_flags[ts_secondary]);
375
376         return false;
377 }
378
379 void Sbar_UpdateTeamPos(entity Team)
380 {
381         for(other = Team.sort_next; other && Sbar_CompareTeamScores(Team, other); other = Team.sort_next)
382         {
383                 SORT_SWAP(Team, other);
384         }
385         for(other = Team.sort_prev; other != teams && Sbar_CompareTeamScores(other, Team); other = Team.sort_prev)
386         {
387                 SORT_SWAP(other, Team);
388         }
389 }
390
391 void Cmd_Sbar_Help(float argc)
392 {
393         print("You can modify the scoreboard using the\n");
394         print("^3|---------------------------------------------------------------|\n");
395         print("^1 TO BE DONE\n");
396         print("Usage:\n");
397         print("^2sbar_columns_set default\n");
398         print("^2sbar_columns_set ^7filed1 field2 ...\n");
399         print("The following field names are recognized (case INsensitive):\n");
400         print("You can use a ^3|^7 to start the right-aligned fields.\n");
401
402         print("^3name^7 or ^3nick^7             Name of a player\n");
403         print("^3ping^7                     Ping time\n\n");
404         print("^3kd^7 or ^3kdr^7 or ^3kdratio^7 or ^3k/d\n");
405         print("                         The kill-death ratio\n");
406
407         print("Before a field you can put a + or - sign, then a comma separated list\n");
408         print("of game types, then a slash, to make the field show up only in these\n");
409         print("or in all but these game types.\n");
410
411         print("The special game type names 'teams' and 'noteams' can be used to\n");
412         print("include/exclude ALL teams/noteams game modes.\n");
413
414         local float i;
415         print("Additional columns:\n");
416         for(i = 0; i < MAX_SCORE; ++i)
417         {
418                 if(scores_label[i] != "")
419                         print(strcat(scores_label[i], "\n"));
420         }
421 }
422
423 string Sbar_DefaultColumnLayout()
424 {
425         return strcat( // fteqcc sucks
426                 "ping pl name | ",
427                 "-teams,race,lms/kills -teams,lms/deaths -teams,lms,race/suicides -race,dm,tdm/frags ", // tdm already has this in "score"
428                 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns ",
429                 "+lms/lives +lms/rank ",
430                 "+kh/caps +kh/pushes +kh/destroyed ",
431                 "?+race/laps ?+race/time ?+race/fastest ",
432                 "+as/objectives +nexball/faults +nexball/goals ",
433                 "-lms,race,nexball/score");
434 }
435
436 void Cmd_Sbar_SetFields(float argc)
437 {
438         float i, j, slash;
439         string str, pattern;
440         float digit;
441         float have_name, have_primary, have_secondary, have_separator;
442         float missing;
443
444         // TODO: re enable with gametype dependant cvars?
445         if(argc < 2) // no arguments provided
446                 argc = tokenizebyseparator(strcat("x ", cvar_string("sbar_columns")), " ");
447
448         if(argc < 2)
449                 argc = tokenizebyseparator(strcat("x ", Sbar_DefaultColumnLayout()), " ");
450
451         if(argc == 2)
452         {
453                 if(argv(1) == "default")
454                         argc = tokenizebyseparator(strcat("x ", Sbar_DefaultColumnLayout()), " ");
455                 else if(argv(1) == "all")
456                 {
457                         string s;
458                         s = "ping pl color name |";
459                         for(i = 0; i < MAX_SCORE; ++i)
460                         {
461                                 if(i != ps_primary)
462                                 if(i != ps_secondary)
463                                 if(scores_label[i] != "")
464                                         s = strcat(s, " ", scores_label[i]);
465                         }
466                         if(ps_secondary != ps_primary)
467                                 s = strcat(s, " ", scores_label[ps_secondary]);
468                         s = strcat(s, " ", scores_label[ps_primary]);
469                         argc = tokenizebyseparator(strcat("x ", s), " ");
470                 }
471         }
472
473
474         sbar_num_fields = 0;
475
476         drawfont = sbar_font;
477         digit = stringwidth("0123456789", FALSE) / 10;
478
479         for(i = 0; i < argc - 1; ++i)
480         {
481                 float nocomplain;
482                 str = argv(i+1);
483
484                 nocomplain = FALSE;
485                 if(substring(str, 0, 1) == "?")
486                 {
487                         nocomplain = TRUE;
488                         str = substring(str, 1, strlen(str) - 1);
489                 }
490
491                 slash = strstrofs(str, "/", 0);
492                 if(slash >= 0)
493                 {
494                         pattern = substring(str, 0, slash);
495                         str = substring(str, slash + 1, strlen(str) - (slash + 1));
496
497                         if not(isGametypeInFilter(gametype, teamplay, pattern))
498                                 continue;
499                 }
500
501                 strunzone(sbar_title[sbar_num_fields]);
502                 sbar_title[sbar_num_fields] = strzone(str);
503                 sbar_size[sbar_num_fields] = stringwidth(str, FALSE);
504                 str = strtolower(str);
505
506                 if(str == "ping") {
507                         sbar_field[sbar_num_fields] = SP_PING;
508                 } else if(str == "pl") {
509                         sbar_field[sbar_num_fields] = SP_PL;
510                 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
511                         sbar_field[sbar_num_fields] = SP_KDRATIO;
512                 } else if(str == "name" || str == "nick") {
513                         sbar_field[sbar_num_fields] = SP_NAME;
514                         have_name = 1;
515                 } else if(str == "|") {
516                         sbar_field[sbar_num_fields] = SP_SEPARATOR;
517                         have_separator = 1;
518                 } else {
519                         for(j = 0; j < MAX_SCORE; ++j)
520                                 if(str == strtolower(scores_label[j]))
521                                         goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
522 :notfound
523                         if(str == "frags")
524                         {
525                                 j = SP_FRAGS;
526                         }
527                         else
528                         {
529                                 if not(nocomplain)
530                                         print(strcat("^1Error:^7 Unknown score field: '", str, "'\n"));
531                                 continue;
532                         }
533 :found
534                         sbar_field[sbar_num_fields] = j;
535                         if(j == ps_primary)
536                                 have_primary = 1;
537                         if(j == ps_secondary)
538                                 have_secondary = 1;
539                 }
540                 ++sbar_num_fields;
541                 if(sbar_num_fields >= MAX_SBAR_FIELDS)
542                         break;
543         }
544
545         if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
546                 have_primary = 1;
547         if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
548                 have_secondary = 1;
549         if(ps_primary == ps_secondary)
550                 have_secondary = 1;
551         missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
552
553         if(sbar_num_fields+missing < MAX_SBAR_FIELDS)
554         {
555                 if(!have_name)
556                 {
557                         strunzone(sbar_title[sbar_num_fields]);
558                         for(i = sbar_num_fields; i > 0; --i)
559                         {
560                                 sbar_title[i] = sbar_title[i-1];
561                                 sbar_size[i] = sbar_size[i-1];
562                                 sbar_field[i] = sbar_field[i-1];
563                         }
564                         sbar_title[0] = strzone("name");
565                         sbar_field[0] = SP_NAME;
566                         ++sbar_num_fields;
567                         print("fixed missing field 'name'\n");
568
569                         if(!have_separator)
570                         {
571                                 strunzone(sbar_title[sbar_num_fields]);
572                                 for(i = sbar_num_fields; i > 1; --i)
573                                 {
574                                         sbar_title[i] = sbar_title[i-1];
575                                         sbar_size[i] = sbar_size[i-1];
576                                         sbar_field[i] = sbar_field[i-1];
577                                 }
578                                 sbar_title[1] = strzone("|");
579                                 sbar_field[1] = SP_SEPARATOR;
580                                 sbar_size[1] = stringwidth("|", FALSE);
581                                 ++sbar_num_fields;
582                                 print("fixed missing field '|'\n");
583                         }
584                 }
585                 else if(!have_separator)
586                 {
587                         strunzone(sbar_title[sbar_num_fields]);
588                         sbar_title[sbar_num_fields] = strzone("|");
589                         sbar_size[sbar_num_fields] = stringwidth("|", FALSE);
590                         sbar_field[sbar_num_fields] = SP_SEPARATOR;
591                         ++sbar_num_fields;
592                         print("fixed missing field '|'\n");
593                 }
594                 if(!have_secondary)
595                 {
596                         strunzone(sbar_title[sbar_num_fields]);
597                         sbar_title[sbar_num_fields] = strzone(scores_label[ps_secondary]);
598                         sbar_size[sbar_num_fields] = stringwidth(sbar_title[sbar_num_fields], FALSE);
599                         sbar_field[sbar_num_fields] = ps_secondary;
600                         ++sbar_num_fields;
601                         print("fixed missing field '", scores_label[ps_secondary], "'\n");
602                 }
603                 if(!have_primary)
604                 {
605                         strunzone(sbar_title[sbar_num_fields]);
606                         sbar_title[sbar_num_fields] = strzone(scores_label[ps_primary]);
607                         sbar_size[sbar_num_fields] = stringwidth(sbar_title[sbar_num_fields], FALSE);
608                         sbar_field[sbar_num_fields] = ps_primary;
609                         ++sbar_num_fields;
610                         print("fixed missing field '", scores_label[ps_primary], "'\n");
611                 }
612         }
613
614         sbar_field[sbar_num_fields] = SP_END;
615 }
616
617 // MOVEUP::
618 vector sbar_field_rgb;
619 string sbar_field_icon0;
620 string sbar_field_icon1;
621 string sbar_field_icon2;
622 vector sbar_field_icon0_rgb;
623 vector sbar_field_icon1_rgb;
624 vector sbar_field_icon2_rgb;
625 float sbar_field_icon0_alpha;
626 float sbar_field_icon1_alpha;
627 float sbar_field_icon2_alpha;
628 string Sbar_GetField(entity pl, float field)
629 {
630         float tmp, num, denom, f;
631         string str;
632         sbar_field_rgb = '1 1 1';
633         sbar_field_icon0 = "";
634         sbar_field_icon1 = "";
635         sbar_field_icon2 = "";
636         sbar_field_icon0_rgb = '1 1 1';
637         sbar_field_icon1_rgb = '1 1 1';
638         sbar_field_icon2_rgb = '1 1 1';
639         sbar_field_icon0_alpha = 1;
640         sbar_field_icon1_alpha = 1;
641         sbar_field_icon2_alpha = 1;
642         switch(field)
643         {
644                 case SP_PING:
645                         if not(pl.gotscores)
646                                 return "\x8D\x8D\x8D"; // >>> sign
647                         str = getplayerkey(pl.sv_entnum, "ping");
648                         if(str == "0")
649                                 return "N/A";
650                         tmp = max(0, min(220, stof(str)-80)) / 220;
651                         sbar_field_rgb = '1 1 1' - '0 1 1'*tmp;
652                         return str;
653
654                 case SP_PL:
655                         if not(pl.gotscores)
656                                 return "N/A";
657                         str = getplayerkey(pl.sv_entnum, "pl");
658                         if(str == "0")
659                                 return "";
660                         tmp = bound(0, stof(str), 20) / 20; // 20% is REALLY BAD pl
661                         sbar_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
662                         return str;
663
664                 case SP_NAME:
665                         if(ready_waiting && pl.ready)
666                         {
667                                 sbar_field_icon0 = "gfx/sb_player_ready";
668                         }
669                         else if(!teamplay)
670                         {
671                                 f = stof(getplayerkey(pl.sv_entnum, "colors"));
672                                 {
673                                         sbar_field_icon0 = "gfx/sb_playercolor_base";
674                                         sbar_field_icon1 = "gfx/sb_playercolor_shirt";
675                                         sbar_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
676                                         sbar_field_icon2 = "gfx/sb_playercolor_pants";
677                                         sbar_field_icon2_rgb = colormapPaletteColor(mod(f, 16), 1);
678                                 }
679                         }
680                         return GetPlayerName(pl.sv_entnum);
681
682                 case SP_FRAGS:
683                         f = pl.(scores[SP_KILLS]);
684                         f -= pl.(scores[SP_SUICIDES]);
685                         return ftos(f);
686
687                 case SP_KDRATIO:
688                         num = pl.(scores[SP_KILLS]);
689                         denom = pl.(scores[SP_DEATHS]);
690
691                         if(denom == 0) {
692                                 sbar_field_rgb = '0 1 0';
693                                 str = ftos(num);
694                         } else if(num <= 0) {
695                                 sbar_field_rgb = '1 0 0';
696                                 str = ftos(num/denom);
697                         } else
698                                 str = ftos(num/denom);
699
700                         tmp = strstrofs(str, ".", 0);
701                         if(tmp > 0)
702                                 str = substring(str, 0, tmp+2);
703                         return str;
704
705                 default:
706                         tmp = pl.(scores[field]);
707                         f = scores_flags[field];
708                         if(field == ps_primary)
709                                 sbar_field_rgb = '1 1 0';
710                         else if(field == ps_secondary)
711                                 sbar_field_rgb = '0 1 1';
712                         else
713                                 sbar_field_rgb = '1 1 1';
714                         return ScoreString(f, tmp);
715         }
716         //return "error";
717 }
718
719 float xmin, xmax, ymin, ymax, sbwidth;
720 float sbar_fixscoreboardcolumnwidth_len;
721 float sbar_fixscoreboardcolumnwidth_iconlen;
722 float sbar_fixscoreboardcolumnwidth_marginlen;
723
724 float stringwidth_colors(string s)
725 {
726         return stringwidth(s, TRUE);
727 }
728
729 float stringwidth_nocolors(string s)
730 {
731         return stringwidth(s, FALSE);
732 }
733
734 string Sbar_FixScoreboardColumnWidth(float i, string str)
735 {
736         float field, f;
737         vector sz;
738         field = sbar_field[i];
739
740         sbar_fixscoreboardcolumnwidth_iconlen = 0;
741
742         if(sbar_field_icon0 != "")
743         {
744                 sz = drawgetimagesize(sbar_field_icon0);
745                 f = sz_x / sz_y;
746                 if(sbar_fixscoreboardcolumnwidth_iconlen < f)
747                         sbar_fixscoreboardcolumnwidth_iconlen = f;
748         }
749
750         if(sbar_field_icon1 != "")
751         {
752                 sz = drawgetimagesize(sbar_field_icon1);
753                 f = sz_x / sz_y;
754                 if(sbar_fixscoreboardcolumnwidth_iconlen < f)
755                         sbar_fixscoreboardcolumnwidth_iconlen = f;
756         }
757
758         if(sbar_field_icon2 != "")
759         {
760                 sz = drawgetimagesize(sbar_field_icon2);
761                 f = sz_x / sz_y;
762                 if(sbar_fixscoreboardcolumnwidth_iconlen < f)
763                         sbar_fixscoreboardcolumnwidth_iconlen = f;
764         }
765
766         sbar_fixscoreboardcolumnwidth_iconlen *= sbar_fontsize_y / sbar_fontsize_x; // fix icon aspect
767
768         if(sbar_fixscoreboardcolumnwidth_iconlen != 0)
769                 sbar_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", FALSE);
770         else
771                 sbar_fixscoreboardcolumnwidth_marginlen = 0;
772
773         if(field == SP_NAME) // name gets all remaining space
774         {
775                 float size, j;
776                 size = sbwidth / sbar_fontsize_x;
777                 for(j = 0; j < sbar_num_fields; ++j)
778                         if(j != i)
779                                 if (sbar_field[i] != SP_SEPARATOR)
780                                         size -= sbar_size[j] + 1;
781                 size += 1;
782                 sbar_size[i] = size;
783                 
784                 if (sbar_fixscoreboardcolumnwidth_iconlen != 0)
785                         size -= sbar_fixscoreboardcolumnwidth_marginlen + sbar_fixscoreboardcolumnwidth_iconlen;
786                 str = textShortenToWidth(str, size, stringwidth_colors);
787                 sbar_fixscoreboardcolumnwidth_len = stringwidth(str, TRUE);
788         }
789         else
790                 sbar_fixscoreboardcolumnwidth_len = stringwidth(str, FALSE);
791         
792         f = sbar_fixscoreboardcolumnwidth_len + sbar_fixscoreboardcolumnwidth_marginlen + sbar_fixscoreboardcolumnwidth_iconlen;
793         if(sbar_size[i] < f)
794                 sbar_size[i] = f;
795
796         return str;
797 }
798
799 void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self, float pl_number)
800 {
801         vector tmp;
802         string str;
803         float i, field;
804         float is_spec;
805         is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR);
806
807         // Layout:
808         tmp_x = sbwidth;
809         tmp_y = sbar_fontsize_y * 1.25;
810         tmp_z = 0;
811
812         // alternated rows highlighting
813         if (is_self)
814                 drawfill(pos - '1 1 0', tmp + '2 0 0', '1 1 1', 0.25, DRAWFLAG_NORMAL);
815         else
816         {
817                 if (sbar_scoreboard_highlight)
818                         if(!mod(pl_number,2))
819                                 drawfill(pos - '1 1 0', tmp + '2 0 0', '1 1 1', 0.09, DRAWFLAG_NORMAL);
820         }
821
822         tmp_y = 0;
823
824         for(i = 0; i < sbar_num_fields; ++i)
825         {
826                 field = sbar_field[i];
827                 if(field == SP_SEPARATOR)
828                         break;
829
830                 if(is_spec && field != SP_NAME && field != SP_PING) {
831                         pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
832                         continue;
833                 }
834                 str = Sbar_GetField(pl, field);
835                 str = Sbar_FixScoreboardColumnWidth(i, str);
836
837                 pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
838
839                 if(field == SP_NAME) {
840                         tmp_x = sbar_fontsize_x*(sbar_size[i] - sbar_fixscoreboardcolumnwidth_iconlen - sbar_fixscoreboardcolumnwidth_marginlen) + sbar_fontsize_x;
841                         drawcolorcodedstring(pos - tmp, str, sbar_fontsize, 1, DRAWFLAG_NORMAL);
842                 } else {
843                         tmp_x = sbar_fixscoreboardcolumnwidth_len*sbar_fontsize_x + sbar_fontsize_x;
844                         drawstring(pos - tmp, str, sbar_fontsize, sbar_field_rgb, 1, DRAWFLAG_NORMAL);
845                 }
846
847                 tmp_x = sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
848                 if(sbar_field_icon0 != "")
849                         drawpic(pos - tmp, sbar_field_icon0, '0 1 0' * sbar_fontsize_y + '1 0 0' * sbar_fontsize_x * sbar_fixscoreboardcolumnwidth_iconlen, sbar_field_icon1_rgb, sbar_field_icon0_alpha, DRAWFLAG_NORMAL);
850                 if(sbar_field_icon1 != "")
851                         drawpic(pos - tmp, sbar_field_icon1, '0 1 0' * sbar_fontsize_y + '1 0 0' * sbar_fontsize_x * sbar_fixscoreboardcolumnwidth_iconlen, sbar_field_icon1_rgb, sbar_field_icon1_alpha, DRAWFLAG_NORMAL);
852                 if(sbar_field_icon2 != "")
853                         drawpic(pos - tmp, sbar_field_icon2, '0 1 0' * sbar_fontsize_y + '1 0 0' * sbar_fontsize_x * sbar_fixscoreboardcolumnwidth_iconlen, sbar_field_icon2_rgb, sbar_field_icon2_alpha, DRAWFLAG_NORMAL);
854         }
855
856         if(sbar_field[i] == SP_SEPARATOR)
857         {
858                 pos_x = xmax;
859                 for(i = sbar_num_fields-1; i > 0; --i)
860                 {
861                         field = sbar_field[i];
862                         if(field == SP_SEPARATOR)
863                                 break;
864
865                         if(is_spec && field != SP_NAME && field != SP_PING) {
866                                 pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
867                                 continue;
868                         }
869
870                         str = Sbar_GetField(pl, field);
871                         str = Sbar_FixScoreboardColumnWidth(i, str);
872
873                         if(field == SP_NAME) {
874                                 tmp_x = sbar_fontsize_x*sbar_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
875                                 drawcolorcodedstring(pos - tmp, str, sbar_fontsize, 1, DRAWFLAG_NORMAL);
876                         } else {
877                                 tmp_x = sbar_fontsize_x*sbar_fixscoreboardcolumnwidth_len;
878                                 drawstring(pos - tmp, str, sbar_fontsize, sbar_field_rgb, 1, DRAWFLAG_NORMAL);
879                         }
880
881                         tmp_x = sbar_fontsize_x*sbar_size[i];
882                         if(sbar_field_icon0 != "")
883                                 drawpic(pos - tmp, sbar_field_icon0, '0 1 0' * sbar_fontsize_y + '1 0 0' * sbar_fontsize_x * sbar_fixscoreboardcolumnwidth_iconlen, sbar_field_icon1_rgb, sbar_field_icon0_alpha, DRAWFLAG_NORMAL);
884                         if(sbar_field_icon1 != "")
885                                 drawpic(pos - tmp, sbar_field_icon1, '0 1 0' * sbar_fontsize_y + '1 0 0' * sbar_fontsize_x * sbar_fixscoreboardcolumnwidth_iconlen, sbar_field_icon1_rgb, sbar_field_icon1_alpha, DRAWFLAG_NORMAL);
886                         if(sbar_field_icon2 != "")
887                                 drawpic(pos - tmp, sbar_field_icon2, '0 1 0' * sbar_fontsize_y + '1 0 0' * sbar_fontsize_x * sbar_fixscoreboardcolumnwidth_iconlen, sbar_field_icon2_rgb, sbar_field_icon2_alpha, DRAWFLAG_NORMAL);
888
889                         pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
890                 }
891         }
892 }
893
894 /*
895  * Sbar_Scoreboard_MakeTable
896  *
897  * Makes a table for a team (for all playing players in DM) and fills it
898  */
899
900 vector Sbar_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
901 {
902         float body_table_height, i;
903         vector tmp, column_dim;
904         entity pl;
905
906         body_table_height = 1.25 * sbar_fontsize_y * max(1, tm.team_size); // no player? show 1 empty line
907
908         pos -= '1 1 0';
909
910         tmp_x = sbwidth + 2;
911         tmp_y = 1.25 * sbar_fontsize_y;
912
913         // rounded header
914         drawpic(pos, "gfx/hud/sb_scoreboard_tableheader", tmp, '0.5 0.5 0.5', sbar_scoreboard_alpha_bg, DRAWFLAG_NORMAL);
915
916         // table border
917         tmp_y += sbar_border_thickness;
918         tmp_y += body_table_height;
919         drawborderlines(sbar_border_thickness, pos, tmp, '0 0 0', sbar_scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
920
921         // separator header/table
922         pos_y += 1.25 * sbar_fontsize_y;
923         tmp_y = sbar_border_thickness;
924         drawfill(pos, tmp, '0 0 0', sbar_scoreboard_alpha_bg, DRAWFLAG_NORMAL);
925
926         pos_y += sbar_border_thickness;
927
928         // table background
929         tmp_y = body_table_height;
930         drawpic_tiled(pos, "gfx/hud/sb_scoreboard_bg", bg_size, tmp, rgb * sbar_color_bg_team, sbar_scoreboard_alpha_bg, DRAWFLAG_NORMAL);
931
932         // anyway, apply some color
933         //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
934
935         // go back to the top to make alternated columns highlighting and to print the strings
936         pos_y -= 1.25 * sbar_fontsize_y;
937         pos_y -= sbar_border_thickness;
938
939         pos += '1 1 0';
940
941         if (sbar_scoreboard_highlight)
942         {
943                 column_dim_y = 1.25 * sbar_fontsize_y; // header
944                 column_dim_y += sbar_border_thickness;
945                 column_dim_y += body_table_height;
946         }
947
948         // print the strings of the columns headers and draw the columns
949         for(i = 0; i < sbar_num_fields; ++i)
950         {
951                 if(sbar_field[i] == SP_SEPARATOR)
952                         break;
953                 column_dim_x = sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
954                 if (sbar_scoreboard_highlight)
955                 {
956                         if (mod(i,2))
957                                 drawfill(pos - '0 1 0' - sbar_fontsize_x / 2 * '1 0 0', column_dim, '0 0 0', sbar_scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
958                 }
959                 drawstring(pos, sbar_title[i], sbar_fontsize, rgb, 1, DRAWFLAG_NORMAL);
960                 pos_x += column_dim_x;
961         }
962         if(sbar_field[i] == SP_SEPARATOR)
963         {
964                 pos_x = xmax;
965                 tmp_y = 0;
966                 for(i = sbar_num_fields-1; i > 0; --i)
967                 {
968                         if(sbar_field[i] == SP_SEPARATOR)
969                                 break;
970
971                         pos_x -= sbar_size[i]*sbar_fontsize_x;
972
973                         if (sbar_scoreboard_highlight)
974                         {
975                                 if (!mod(i,2))
976                                 {
977                                         if (i == sbar_num_fields-1)
978                                                 column_dim_x = sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x / 2 + 1;
979                                         else
980                                                 column_dim_x = sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
981                                         drawfill(pos - '0 1 0' - sbar_fontsize_x / 2 * '1 0 0', column_dim, '0 0 0', sbar_scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
982                                 }
983                         }
984
985                         tmp_x = stringwidth(sbar_title[i], FALSE);
986                         tmp_x = (sbar_size[i] - tmp_x) * sbar_fontsize_x;
987                         drawstring(pos + tmp, sbar_title[i], sbar_fontsize, rgb, 1, DRAWFLAG_NORMAL);
988                         pos_x -= sbar_fontsize_x;
989                 }
990         }
991         
992         pos_x = xmin;
993         pos_y += 1.25 * sbar_fontsize_y; // skip the header
994         pos_y += sbar_border_thickness;
995
996         // fill the table and draw the rows
997         i = 0;
998         if (teamplay)
999                 for(pl = players.sort_next; pl; pl = pl.sort_next)
1000                 {
1001                         if(pl.team != tm.team)
1002                                 continue;
1003                         Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), i);
1004                         pos_y += 1.25 * sbar_fontsize_y;
1005                         ++i;
1006                 }
1007         else
1008                 for(pl = players.sort_next; pl; pl = pl.sort_next)
1009                 {
1010                         if(pl.team == COLOR_SPECTATOR)
1011                                 continue;
1012                         Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), i);
1013                         pos_y += 1.25 * sbar_fontsize_y;
1014                         ++i;
1015                 }
1016
1017         pos_y += 1.25 * sbar_fontsize_y; // move to the end of the table
1018         pos_y += 1.25 * sbar_fontsize_y; // move empty row (out of the table)
1019
1020         return pos;
1021 }
1022
1023 float lastpingstime;
1024 float scoreboard_bottom;
1025 void Sbar_DrawScoreboard()
1026 {
1027         vector rgb, pos, tmp, sbar_save;
1028         entity pl, tm;
1029         float specs;
1030         float center_x;
1031
1032         if(time > lastpingstime + 10)
1033         {
1034                 localcmd("pings\n");
1035                 lastpingstime = time;
1036         }
1037
1038         sbwidth = Sbar_GetWidth(6.5 * sbar_fontsize_y);
1039
1040         xmin = 0.5 * (vid_conwidth - sbwidth);
1041         ymin = 60;
1042
1043         xmax = vid_conwidth - xmin;
1044     ymax = vid_conheight - 0.2*vid_conheight;
1045
1046         center_x = xmin + 0.5*sbwidth;
1047
1048         // Initializes position
1049         pos_x = xmin;
1050         pos_y = ymin;
1051         pos_z = 0;
1052
1053         // Heading
1054         drawfont = sbar_bigfont;
1055         drawstringcenter('0 1 0' * ymin, "Scoreboard", '24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1056
1057         pos_y += 24 + 4;
1058         pos_y += sbar_fontsize_y;
1059
1060         drawfont = sbar_font;
1061
1062         sbar_save = sbar;
1063         sbar = '0 0 0';
1064
1065         vector bg_size;
1066         bg_size = drawgetimagesize("gfx/hud/sb_scoreboard_bg");
1067
1068         if(teamplay)
1069         {
1070                 //for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next)
1071                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1072                 {
1073                         if(tm.team == COLOR_SPECTATOR)
1074                                 continue;
1075
1076                         rgb = GetTeamRGB(tm.team);
1077
1078                         Sbar_DrawXNum(sbar + pos - '6.5 0 0' * sbar_fontsize_y + '0 1 0' * sbar_fontsize_y, tm.(teamscores[ts_primary]), 4, sbar_fontsize_y * 1.5, rgb, 0, 1, 1, DRAWFLAG_NORMAL);
1079
1080                         if(ts_primary != ts_secondary)
1081                                 Sbar_DrawXNum(sbar + pos - '4.5 0 0' * sbar_fontsize_y + '0 2.5 0' * sbar_fontsize_y, tm.(teamscores[ts_secondary]), 4, sbar_fontsize_y * 1, rgb, 0, 1, 1, DRAWFLAG_NORMAL);
1082
1083                         pos = Sbar_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1084                 }
1085         }
1086         else
1087         {
1088                 rgb_x = cvar("sbar_color_bg_r");
1089                 rgb_y = cvar("sbar_color_bg_g");
1090                 rgb_z = cvar("sbar_color_bg_b");
1091
1092                 tm = teams.sort_next;
1093
1094                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1095                 {
1096                         if(tm.team == COLOR_SPECTATOR)
1097                                 continue;
1098
1099                         pos = Sbar_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1100                 }
1101         }
1102
1103         tmp = pos + '0 1.5 0' * sbar_fontsize_y;
1104         pos_y += 3 * sbar_fontsize_y;
1105         specs = 0;
1106         for(pl = players.sort_next; pl; pl = pl.sort_next)
1107         {
1108                 if(pl.team != COLOR_SPECTATOR)
1109                         continue;
1110                 Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), specs);
1111                 pos_y += 1.25 * sbar_fontsize_y;
1112                 ++specs;
1113         }
1114
1115         if(specs)
1116                 drawstring(tmp, "Spectators", sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
1117
1118         string str;
1119         float tl, fl, ll;
1120         str = strcat("playing on ^2", shortmapname, "^7");
1121         tl = getstatf(STAT_TIMELIMIT);
1122         fl = getstatf(STAT_FRAGLIMIT);
1123         ll = getstatf(STAT_LEADLIMIT);
1124         if(gametype == GAME_LMS)
1125         {
1126                 if(tl > 0)
1127                         str = strcat(str, " for up to ^1", ftos(tl), " minutes^7");
1128         }
1129         else
1130         {
1131                 if(tl > 0)
1132                         str = strcat(str, " for ^1", ftos(tl), " minutes^7");
1133                 if(fl > 0)
1134                 {
1135                         if(tl > 0)
1136                                 str = strcat(str, " or");
1137                         if(teamplay)
1138                         {
1139                                 str = strcat(str, " until ^3", ScoreString(teamscores_flags[ts_primary], fl));
1140                                 if(teamscores_label[ts_primary] == "score")
1141                                         str = strcat(str, " points^7");
1142                                 else if(teamscores_label[ts_primary] == "fastest")
1143                                         str = strcat(str, " is beaten^7");
1144                                 else
1145                                         str = strcat(str, " ", teamscores_label[ts_primary]);
1146                         }
1147                         else
1148                         {
1149                                 str = strcat(str, " until ^3", ScoreString(scores_flags[ps_primary], fl));
1150                                 if(scores_label[ps_primary] == "score")
1151                                         str = strcat(str, " points^7");
1152                                 else if(scores_label[ps_primary] == "fastest")
1153                                         str = strcat(str, " is beaten^7");
1154                                 else
1155                                         str = strcat(str, " ", scores_label[ps_primary]);
1156                         }
1157                 }
1158                 if(ll > 0)
1159                 {
1160                         if(tl > 0 || fl > 0)
1161                                 str = strcat(str, " or");
1162                         if(teamplay)
1163                         {
1164                                 str = strcat(str, " until a lead of ^3", ScoreString(teamscores_flags[ts_primary], ll));
1165                                 if(teamscores_label[ts_primary] == "score")
1166                                         str = strcat(str, " points^7");
1167                                 else if(teamscores_label[ts_primary] == "fastest")
1168                                         str = strcat(str, " is beaten^7");
1169                                 else
1170                                         str = strcat(str, " ", teamscores_label[ts_primary]);
1171                         }
1172                         else
1173                         {
1174                                 str = strcat(str, " until a lead of ^3", ScoreString(scores_flags[ps_primary], ll));
1175                                 if(scores_label[ps_primary] == "score")
1176                                         str = strcat(str, " points^7");
1177                                 else if(scores_label[ps_primary] == "fastest")
1178                                         str = strcat(str, " is beaten^7");
1179                                 else
1180                                         str = strcat(str, " ", scores_label[ps_primary]);
1181                         }
1182                 }
1183         }
1184
1185
1186         pos_y += 1.2 * sbar_fontsize_y;
1187         drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - sbar_fontsize_x * stringwidth(str, TRUE)), str, sbar_fontsize, 0.8, DRAWFLAG_NORMAL);
1188
1189         sbar = sbar_save;
1190         scoreboard_bottom = pos_y + 2 * sbar_fontsize_y;
1191 }
1192
1193 string MakeRaceString(float cp, float mytime, float histime, float lapdelta, string hisname)
1194 {
1195         string col;
1196         string timestr;
1197         string cpname;
1198         string lapstr;
1199         lapstr = "";
1200
1201         if(histime == 0) // goal hit
1202         {
1203                 if(mytime > 0)
1204                 {
1205                         timestr = strcat("+", ftos_decimals(+mytime, 1));
1206                         col = "^1";
1207                 }
1208                 else if(mytime == 0)
1209                 {
1210                         timestr = "+0.0";
1211                         col = "^3";
1212                 }
1213                 else
1214                 {
1215                         timestr = strcat("-", ftos_decimals(-mytime, 1));
1216                         col = "^2";
1217                 }
1218
1219                 if(lapdelta > 0)
1220                 {
1221                         lapstr = strcat(" (-", ftos(lapdelta), "L)");
1222                         col = "^2";
1223                 }
1224                 else if(lapdelta < 0)
1225                 {
1226                         lapstr = strcat(" (+", ftos(-lapdelta), "L)");
1227                         col = "^1";
1228                 }
1229         }
1230         else if(histime > 0) // anticipation
1231         {
1232                 if(mytime >= histime)
1233                         timestr = strcat("+", ftos_decimals(mytime - histime, 1));
1234                 else
1235                         timestr = mmsss(histime * 10);
1236                 col = "^3";
1237         }
1238         else
1239                 col = "^7";
1240
1241         if(cp == 254)
1242                 cpname = "Start line";
1243         else if(cp == 255)
1244                 cpname = "Finish line";
1245         else if(cp)
1246                 cpname = strcat("Intermediate ", ftos(cp));
1247         else
1248                 cpname = "Finish line";
1249
1250         if(histime < 0)
1251                 return strcat(col, cpname);
1252         else if(hisname == "")
1253                 return strcat(col, cpname, " (", timestr, ")");
1254         else
1255                 return strcat(col, cpname, " (", timestr, " ", strcat(hisname, col, lapstr), ")");
1256 }
1257
1258 void Sbar_Score(float margin)
1259 {
1260         float timelimit, minutes, seconds, timeleft, minutesLeft, secondsLeft, distribution, score, desiredPlayerId;
1261         float racemin, racesec, racemsec;
1262         float distsec, distmsec;
1263         vector sbar_save, score_offset, timer_color, offset, distribution_color, minuspos;
1264         entity tm, pl, me;
1265         sbar_save = sbar;
1266
1267         vector bottomright;
1268         bottomright_x = vid_conwidth;
1269         bottomright_y = vid_conheight;
1270         bottomright_z = 0;
1271
1272         vector topright;
1273         topright_x = vid_conwidth;
1274         topright_y = 0;
1275         topright_z = 0;
1276
1277         //get the ID (could be "me", or the player I'm spectating)
1278         if (spectatee_status > 0)
1279                 desiredPlayerId = spectatee_status - 1;
1280         else
1281                 desiredPlayerId = player_localentnum - 1;
1282         me = playerslots[desiredPlayerId];
1283
1284         sbar_y = vid_conheight - (32+12);
1285         sbar_x -= margin;
1286
1287         offset = '0 0 0';
1288
1289         if (vid_conwidth >= 800) {
1290                 score_offset_x = 196;
1291                 score_offset_y = 36;
1292         }
1293         else { // move the scores if vid_conwidth < 800
1294                 score_offset_x = 196;
1295                 score_offset_y = 84;
1296         }
1297         score_offset_z = 0;
1298
1299         if((scores_flags[ps_primary] & SFL_TIME) && !teamplay)
1300         {
1301                 // me vector := [team/connected frags id]
1302
1303                 pl = players.sort_next;
1304                 if(pl == me)
1305                         pl = pl.sort_next;
1306                 if(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)
1307                         if(pl.scores[ps_primary] == 0)
1308                                 pl = world;
1309
1310                 score = me.(scores[ps_primary]);
1311
1312                 racemin = floor(score/600);
1313                 racesec = floor((score - racemin*600)/10);
1314                 racemsec = score - racemin*600 - racesec*10;
1315
1316                 if (pl && ((!(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)) || score)) {
1317                         // distribution display
1318                         distribution = me.(scores[ps_primary]);
1319                         distribution -= pl.(scores[ps_primary]);
1320
1321                         if (distribution < 10 && distribution > -10)
1322                                 distmsec = fabs(distribution);
1323                         else {
1324                                 distsec = floor(fabs(distribution)/10);
1325                                 distmsec = fabs(distribution) - distsec*10;
1326                         }
1327
1328                         if (distribution < 100 && distribution > -100)
1329                                 minuspos = bottomright - element_offset - score_offset + '130 -6 0' + '16 0 0';
1330                         else if (distribution < 1000 && distribution > -1000)
1331                                 minuspos = bottomright - element_offset - score_offset + '130 -6 0';
1332                         else if (distribution < 10000 && distribution > -10000)
1333                                 minuspos = bottomright - element_offset - score_offset + '130 -6 0' - '16 0 0';
1334                         else
1335                                 minuspos = bottomright - element_offset - score_offset + '130 -6 0' - '32 0 0';
1336
1337                         if (distribution <= 0) {
1338                                 distribution_color = '0 1 0';
1339                                 drawpic(minuspos, "gfx/hud/num_minus", '16 16 0', distribution_color, sbar_alpha_fg, DRAWFLAG_ADDITIVE);
1340                         }
1341                         else {
1342                                 distribution_color = '1 0 0';
1343                                 drawpic(minuspos, "gfx/hud/num_plus", '16 16 0', distribution_color, sbar_alpha_fg, DRAWFLAG_ADDITIVE);
1344                         }
1345
1346                         Sbar_DrawXNum(bottomright - element_offset - score_offset + '180 -6 0', distmsec, 1, 16, distribution_color, 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1347                         Sbar_DrawXNum(bottomright - element_offset - score_offset + '112 -6 0', distsec, 4, 16, distribution_color, 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1348                         drawpic(bottomright - element_offset - score_offset + '170 -6 0', "gfx/hud/num_dot", '16 16 0', distribution_color, sbar_alpha_fg, DRAWFLAG_ADDITIVE);
1349                 }
1350
1351                 // big timer
1352                 if (distribution <= 0 || distribution == score) // draw the highlight background behind the timer if we have the lead
1353                         drawpic(bottomright - element_offset - score_offset + '20 10 0', "gfx/hud/sb_highlight_4", '178 28 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
1354
1355                 Sbar_DrawXNum(bottomright - element_offset - score_offset + '166 10 0', racemsec, 1, 30, '1 1 1', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1356                 Sbar_DrawXNum(bottomright - element_offset - score_offset + '96 10 0', racesec, -2, 30, '1 1 1', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1357                 drawpic(bottomright - element_offset - score_offset + '145 10 0', "gfx/hud/num_dot", '30 30 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_ADDITIVE);
1358
1359                 Sbar_DrawXNum(bottomright - element_offset - score_offset + '24 10 0', racemin, -2, 30, '1 1 1', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1360                 drawpic(bottomright - element_offset - score_offset + '76 8 0', "gfx/hud/num_colon", '30 30 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_ADDITIVE);
1361         }
1362         else {
1363                 if(teamplay)
1364                 {
1365                         // Layout:
1366                         //
1367                         //   team1 team3 team4
1368                         //
1369                         //         TEAM2
1370                         //for(i = 0; i < 4; ++i)
1371
1372                         float max_fragcount;
1373                         max_fragcount = -999;
1374
1375                         for(tm = teams.sort_next; tm; tm = tm.sort_next)
1376                         {
1377                                 if(tm.team == COLOR_SPECTATOR || !tm.team_size) // no players? don't display
1378                                         continue;
1379                                 // -32*4 = -128
1380                                 score = tm.(teamscores[ts_primary]);
1381
1382                                 if (score > max_fragcount)
1383                                         max_fragcount = score;
1384
1385                                 if(tm.team == myteam) {
1386                                         if (max_fragcount == score)
1387                                                 Sbar_DrawXNum(bottomright - element_offset - score_offset, score, 4, 34, GetTeamRGB(tm.team) * 0.8, 1, 1, sbar_alpha_fg, DRAWFLAG_NORMAL);
1388                                         else
1389                                                 Sbar_DrawXNum(bottomright - element_offset - score_offset, score, 4, 34, GetTeamRGB(tm.team) * 0.8, 0, 1, sbar_alpha_fg, DRAWFLAG_NORMAL);
1390                                 }
1391                                 else
1392                                 {
1393                                         if (max_fragcount == score)
1394                                                 Sbar_DrawXNum(bottomright - element_offset - score_offset + '132 -6 0' - offset, score, 4, 16, GetTeamRGB(tm.team) * 0.8, 1, 1, sbar_alpha_fg, DRAWFLAG_NORMAL);
1395                                         else
1396                                                 Sbar_DrawXNum(bottomright - element_offset - score_offset + '132 -6 0' - offset, score, 4, 16, GetTeamRGB(tm.team) * 0.8, 0, 1, sbar_alpha_fg, DRAWFLAG_NORMAL);
1397                                         offset_y -= 16;
1398                                 }
1399                         }
1400                 } else {
1401                         // me vector := [team/connected frags id]
1402
1403                         pl = players.sort_next;
1404                         if(pl == me)
1405                                 pl = pl.sort_next;
1406
1407                         if(pl) {
1408                                 distribution = me.(scores[ps_primary]);
1409                                 distribution -= pl.(scores[ps_primary]);
1410                         } else
1411                                 distribution = 0;
1412
1413                         score = me.(scores[ps_primary]);
1414
1415                         if(distribution >= 0)
1416                         {
1417                                 if (distribution != 0) {
1418                                         // draw a + sign in front of the score
1419                                         if (distribution < 10) { drawpic(bottomright - element_offset - score_offset + '132 -6 0' + '32 0 0', "gfx/hud/num_plus", '16 16 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_ADDITIVE); }
1420                                         else if (distribution < 100) { drawpic(bottomright - element_offset - score_offset + '132 -6 0' + '16 0 0', "gfx/hud/num_plus", '16 16 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_ADDITIVE); }
1421                                         else if (distribution < 1000) { drawpic(bottomright - element_offset - score_offset + '132 -6 0', "gfx/hud/num_plus", '16 16 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_ADDITIVE); }
1422                                 }
1423
1424                                 Sbar_DrawXNum(bottomright - element_offset - score_offset + '132 -6 0', distribution, 4, 16, ' 1 1 1', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1425                                 Sbar_DrawXNum(bottomright - element_offset - score_offset, score, 4, 34, '1 1 1', 1, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1426                         }
1427                         else if(distribution >= -5)
1428                         {
1429                                 Sbar_DrawXNum(bottomright - element_offset - score_offset + '132 -6 0', distribution, 4, 16, ' 1 1 0', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1430                                 Sbar_DrawXNum(bottomright - element_offset - score_offset, score, 4, 34, '1 1 0', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1431                         }
1432                         else {
1433                                 Sbar_DrawXNum(bottomright - element_offset - score_offset + '132 -6 0', distribution, 4, 16, ' 1 0 0', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1434                                 Sbar_DrawXNum(bottomright - element_offset - score_offset, score, 4, 34, '1 0 0', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1435                         }
1436                 }
1437         }
1438
1439         //draw the remaining or elapsed time
1440         timelimit = getstatf(STAT_TIMELIMIT);
1441         vector bgpos;
1442
1443         timeleft = max(0, timelimit * 60 + getstatf(STAT_GAMESTARTTIME) - time);
1444         timeleft = ceil(timeleft);
1445         minutesLeft = floor(timeleft / 60);
1446         secondsLeft = timeleft - minutesLeft*60;
1447
1448         if(minutesLeft >= 5 || warmup_stage || timelimit == 0) //don't use red or yellow in warmup or when there is no timelimit
1449                 timer_color = '1 1 1'; //white
1450         else if(minutesLeft >= 1)
1451                 timer_color = '1 1 0'; //yellow
1452         else
1453                 timer_color = '1 0 0'; //red
1454
1455         if (cvar("sbar_increment_maptime") || timelimit == 0 || warmup_stage) {
1456                 if (time < getstatf(STAT_GAMESTARTTIME)) {
1457                         //while restart is still active, show 00:00
1458                         minutes = seconds = 0;
1459                 } else {
1460                         float elapsedTime;
1461                         elapsedTime = floor(time - getstatf(STAT_GAMESTARTTIME)); //127
1462                         minutes = floor(elapsedTime / 60);
1463                         seconds = elapsedTime - minutes*60;
1464                 }
1465                 if (minutes < 10)
1466                         bgpos_x = topright_x - 54 - 17 - 12;
1467                 else if (minutes < 100) // nudge the timer background left if more digits are drawn
1468                         bgpos_x = topright_x - 72 - 17 - 12;
1469                 else
1470                         bgpos_x = topright_x - 90 - 17 - 12;
1471                 bgpos_y = 0;
1472                 bgpos_z = 0;
1473         } else {
1474                 minutes = minutesLeft;
1475                 seconds = secondsLeft;
1476                 if (minutes == 0)
1477                 bgpos_x = topright_x - 36 - 7 - 12;
1478                 else if (minutes < 10) // nudge the timer background left if more digits are drawn
1479                         bgpos_x = topright_x - 54 - 17 - 12;
1480                 else if (minutes < 100)
1481                         bgpos_x = topright_x - 72 - 17 - 12;
1482                 else
1483                         bgpos_x = topright_x - 90 - 17 - 12;
1484                 bgpos_y = 0;
1485                 bgpos_z = 0;
1486         }
1487
1488         if (cvar("viewsize") <= 100) { // draw timer background when viewsize <= 100
1489                 if (teamplay)
1490                         drawpic(bgpos, "gfx/hud/sb_timerbg", '120 30 0', GetTeamRGB(myteam) * sbar_color_bg_team, sbar_alpha_bg, DRAWFLAG_NORMAL); // timer bg color = myteam color
1491                 else {
1492                         color_x = cvar("sbar_color_bg_r");
1493                         color_y = cvar("sbar_color_bg_g");
1494                         color_z = cvar("sbar_color_bg_b");
1495
1496                         drawpic(bgpos, "gfx/hud/sb_timerbg", '120 30 0', color, sbar_alpha_bg, DRAWFLAG_NORMAL);
1497                 }
1498         }
1499
1500         if(minutesLeft >= 1 || cvar("sbar_increment_maptime") || timelimit == 0) {
1501                 Sbar_DrawXNum(topright - '103 0 0' + '0 2 0', minutes, 3, 18, timer_color, 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1502                 drawpic(topright - '53 0 0' + '0 1 0', "gfx/hud/num_colon", '18 18 0', timer_color, sbar_alpha_fg, DRAWFLAG_NORMAL);
1503         }
1504         Sbar_DrawXNum(topright - '36 0 0' - '3 0 0' + '0 2 0', seconds, -2, 18, timer_color, 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1505
1506         if(gametype == GAME_RACE || gametype == GAME_CTS)
1507         {
1508                 drawfont = sbar_bigfont;
1509                 float a, t;
1510                 vector m;
1511                 string s, forcetime;
1512
1513                 m = '0.5 0 0' * vid_conwidth + '0 0.25 0' * vid_conheight;
1514
1515                 if(race_checkpointtime)
1516                 {
1517                         a = bound(0, 2 - (time - race_checkpointtime), 1);
1518                         s = "";
1519                         forcetime = "";
1520                         if(a > 0) // just hit a checkpoint?
1521                         {
1522                                 if(race_checkpoint != 254)
1523                                 {
1524                                         if(race_time && race_previousbesttime)
1525                                                 s = MakeRaceString(race_checkpoint, race_time / 10 - race_previousbesttime / 10, 0, 0, race_previousbestname);
1526                                         else
1527                                                 s = MakeRaceString(race_checkpoint, 0, -1, 0, race_previousbestname);
1528                                         if(race_time)
1529                                                 forcetime = mmsss(race_time);
1530                                 }
1531                         }
1532                         else
1533                         {
1534                                 if(race_laptime && race_nextbesttime && race_nextcheckpoint != 254)
1535                                 {
1536                                         a = bound(0, 2 - ((race_laptime + race_nextbesttime/10) - (time + race_penaltyaccumulator/10)), 1);
1537                                         if(a > 0) // next one?
1538                                         {
1539                                                 s = MakeRaceString(race_nextcheckpoint, (time + race_penaltyaccumulator/10) - race_laptime, race_nextbesttime / 10, 0, race_nextbestname);
1540                                         }
1541                                 }
1542                         }
1543
1544                         if(s != "" && a > 0)
1545                         {
1546                                 dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1547                                 drawcolorcodedstring(m - '0 16 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', sbar_alpha_fg * a, DRAWFLAG_NORMAL);
1548                         }
1549
1550                         if(race_penaltytime)
1551                         {
1552                                 a = bound(0, 2 - (time - race_penaltyeventtime), 1);
1553                                 if(a > 0)
1554                                 {
1555                                         s = strcat("^1PENALTY: ", ftos_decimals(race_penaltytime * 0.1, 1), " (", race_penaltyreason, ")");
1556                                         dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1557                                         drawcolorcodedstring(m - '0 32 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', sbar_alpha_fg * a, DRAWFLAG_NORMAL);
1558                                 }
1559                         }
1560
1561                         if(forcetime != "")
1562                         {
1563                                 a = bound(0, (time - race_checkpointtime) / 0.5, 1);
1564                                 drawstring_expanding(m - '16 0 0' * stringwidth(forcetime, FALSE), forcetime, '32 32 0', '1 1 1', sbar_alpha_fg, 0, a);
1565                         }
1566                         else
1567                                 a = 1;
1568
1569                         if(race_laptime && race_checkpoint != 255)
1570                         {
1571                                 s = mmsss(10*(time + race_penaltyaccumulator/10 - race_laptime));
1572                                 drawstring(m - '16 0 0' * stringwidth(s, FALSE), s, '32 32 0', '1 1 1', sbar_alpha_fg * a, DRAWFLAG_NORMAL);
1573                         }
1574                 }
1575                 else
1576                 {
1577                         if(race_mycheckpointtime)
1578                         {
1579                                 a = bound(0, 2 - (time - race_mycheckpointtime), 1);
1580                                 s = MakeRaceString(race_mycheckpoint, race_mycheckpointdelta / 10, -!race_mycheckpointenemy, race_mycheckpointlapsdelta, race_mycheckpointenemy);
1581                                 dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1582                                 drawcolorcodedstring(m - '0 16 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', sbar_alpha_fg * a, DRAWFLAG_NORMAL);
1583                         }
1584                         if(race_othercheckpointtime && race_othercheckpointenemy != "")
1585                         {
1586                                 a = bound(0, 2 - (time - race_othercheckpointtime), 1);
1587                                 s = MakeRaceString(race_othercheckpoint, -race_othercheckpointdelta / 10, -!race_othercheckpointenemy, race_othercheckpointlapsdelta, race_othercheckpointenemy);
1588                                 dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1589                                 drawcolorcodedstring(m - '0 0 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', sbar_alpha_fg * a, DRAWFLAG_NORMAL);
1590                         }
1591
1592                         if(race_penaltytime && !race_penaltyaccumulator)
1593                         {
1594                                 t = race_penaltytime * 0.1 + race_penaltyeventtime;
1595                                 a = bound(0, (1 + t - time), 1);
1596                                 if(a > 0)
1597                                 {
1598                                         if(time < t)
1599                                                 s = strcat("^1PENALTY: ", ftos_decimals(t - time, 1), " (", race_penaltyreason, ")");
1600                                         else
1601                                                 s = strcat("^2PENALTY: 0.0 (", race_penaltyreason, ")");
1602                                         dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1603                                         drawcolorcodedstring(m - '0 32 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', sbar_alpha_fg * a, DRAWFLAG_NORMAL);
1604                                 }
1605                         }
1606                 }
1607
1608                 drawfont = sbar_font;
1609         }
1610
1611         sbar = sbar_save;
1612 }
1613
1614 float Sbar_WouldDrawScoreboard ()
1615 {
1616         if (sb_showscores)
1617                 return 1;
1618         else if (intermission == 1)
1619                 return 1;
1620         else if (intermission == 2)
1621                 return 1;
1622         else if (getstati(STAT_HEALTH) <= 0 && cvar("cl_deathscoreboard"))
1623                 return 1;
1624         else if(sb_showscores_force)
1625                 return 1;
1626         return 0;
1627 }
1628
1629 void CSQC_Strength_Timer() {
1630         vector bottom;
1631
1632         bottom_x = vid_conwidth/2;
1633         bottom_y = vid_conheight;
1634         bottom_z = 0;
1635
1636         float stat_items, dt;
1637         stat_items = getstati(STAT_ITEMS);
1638         /*
1639         if not(stat_items & IT_STRENGTH)
1640                 if not(stat_items & IT_INVINCIBLE)
1641                         return;
1642         */
1643
1644         if (getstati(STAT_HEALTH) <= 0)
1645                 return;
1646
1647         vector picsize;
1648         float strength_time, invincibility_time, countdown_fontsize;
1649
1650         picsize = '22 22 0';
1651         countdown_fontsize = 18;
1652
1653         //strength
1654         strength_time = getstatf(STAT_STRENGTH_FINISHED);
1655         invincibility_time = getstatf(STAT_INVINCIBLE_FINISHED);
1656
1657         if (strength_time) {
1658                 dt = strength_time - time;
1659                 if(dt > 0)
1660                 {
1661                         if(dt < 5)
1662                         {
1663                                 drawpic_expanding_two(bottom + '192 -46 0', "gfx/hud/sb_str", picsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_ADDITIVE,
1664                                         bound(0, (ceil(dt) - dt) / 0.5, 1));
1665                         }
1666                         else
1667                         {
1668                                 drawpic(bottom + '192 -46 0', "gfx/hud/sb_str", picsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_ADDITIVE);
1669                         }
1670                         Sbar_DrawXNum(bottom + '152 -44 0', ceil(dt), 2, countdown_fontsize, '1 1 1', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
1671                 }
1672                 else if(dt > -1)
1673                 {
1674                         drawpic_expanding(bottom + '192 -46 0', "gfx/hud/sb_str", picsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_ADDITIVE,
1675                                 bound(0, -dt / 0.5, 1));
1676                 }
1677         }
1678
1679         //invincibility
1680         if (invincibility_time) {
1681                 dt = invincibility_time - time;
1682                 if(dt > 0)
1683                 {
1684                         if(dt < 5)
1685                         {
1686                                 drawpic_expanding_two(bottom + '192 -24 0', "gfx/hud/sb_invinc", picsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_ADDITIVE,
1687                                         bound(0, (ceil(dt) - dt) / 0.5, 1));
1688                         }
1689                         else
1690                         {
1691                                 drawpic(bottom + '192 -24 0', "gfx/hud/sb_invinc", picsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_ADDITIVE);
1692                         }
1693                         Sbar_DrawXNum(bottom + '152 -22 0', ceil(dt), 2, countdown_fontsize, '1 1 1', 0, 0, 1, DRAWFLAG_NORMAL);
1694                 }
1695                 else if(dt > -1)
1696                 {
1697                         drawpic_expanding(bottom + '192 -24 0', "gfx/hud/sb_invinc", picsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_ADDITIVE,
1698                                 bound(0, -dt / 0.5, 1));
1699                 }
1700         }
1701 }
1702
1703 #define CENTERPRINT_MAX_LINES 30
1704 string centerprint_messages[CENTERPRINT_MAX_LINES];
1705 float centerprint_width[CENTERPRINT_MAX_LINES];
1706 vector centerprint_start;
1707 float centerprint_expire;
1708 float centerprint_num;
1709 float centerprint_offset_hint;
1710 vector centerprint_fontsize;
1711
1712 void centerprint(string strMessage)
1713 {
1714         float i, j, n, hcount;
1715         string s;
1716
1717         centerprint_fontsize = Sbar_GetFontsize("scr_centersize");
1718
1719         centerprint_expire = min(centerprint_expire, time); // if any of the returns happens, this message will fade out
1720
1721         if(cvar("scr_centertime") <= 0)
1722                 return;
1723
1724         if(strMessage == "")
1725                 return;
1726
1727         // strip trailing newlines
1728         j = strlen(strMessage) - 1;
1729         while(substring(strMessage, j, 1) == "\n" && j >= 0)
1730                 j = j - 1;
1731         strMessage = substring(strMessage, 0, j + 1);
1732
1733         if(strMessage == "")
1734                 return;
1735
1736         // strip leading newlines and remember them, they are a hint that the message should be lower on the screen
1737         j = 0;
1738         while(substring(strMessage, j, 1) == "\n" && j < strlen(strMessage))
1739                 j = j + 1;
1740         strMessage = substring(strMessage, j, strlen(strMessage) - j);
1741         centerprint_offset_hint = j;
1742
1743         if(strMessage == "")
1744                 return;
1745
1746         // if we get here, we have a message. Initialize its height.
1747         centerprint_num = 0;
1748
1749         n = tokenizebyseparator(strMessage, "\n");
1750         i = hcount = 0;
1751         for(j = 0; j < n; ++j)
1752         {
1753                 getWrappedLine_remaining = argv(j);
1754                 while(getWrappedLine_remaining)
1755                 {
1756                         s = getWrappedLine(vid_conwidth * 0.75 / centerprint_fontsize_x, stringwidth_colors);
1757                         if(centerprint_messages[i])
1758                                 strunzone(centerprint_messages[i]);
1759                         centerprint_messages[i] = strzone(s);
1760                         centerprint_width[i] = stringwidth(s, TRUE);
1761                         ++i;
1762
1763                         // half height for empty lines looks better
1764                         if(s == "")
1765                                 hcount += 0.5;
1766                         else
1767                                 hcount += 1;
1768
1769                         if(i >= CENTERPRINT_MAX_LINES)
1770                                 break;
1771                 }
1772         }
1773
1774         float h, havail;
1775         h = centerprint_fontsize_y*hcount;
1776
1777         havail = vid_conheight;
1778         if(cvar("con_chatpos") < 0)
1779                 havail -= (-cvar("con_chatpos") + cvar("con_chat")) * cvar("con_chatsize"); // avoid overlapping chat
1780         if(havail > vid_conheight - 70)
1781                 havail = vid_conheight - 70; // avoid overlapping HUD
1782
1783         centerprint_start_x = 0;
1784
1785 #if 0
1786         float forbiddenmin, forbiddenmax, allowedmin, allowedmax, preferred;
1787
1788         // here, the centerprint would cover the crosshair. REALLY BAD.
1789         forbiddenmin = vid_conheight * 0.5 - h - 16;
1790         forbiddenmax = vid_conheight * 0.5 + 16;
1791
1792         allowedmin = scoreboard_bottom;
1793         allowedmax = havail - h;
1794         preferred = (havail - h)/2;
1795
1796
1797         // possible orderings (total: 4! / 4 = 6)
1798         //  allowedmin allowedmax forbiddenmin forbiddenmax
1799         //  forbiddenmin forbiddenmax allowedmin allowedmax
1800         if(allowedmax < forbiddenmin || allowedmin > forbiddenmax)
1801         {
1802                 // forbidden doesn't matter in this case
1803                 centerprint_start_y = bound(allowedmin, preferred, allowedmax);
1804         }
1805         //  allowedmin forbiddenmin allowedmax forbiddenmax
1806         else if(allowedmin < forbiddenmin && allowedmax < forbiddenmax)
1807         {
1808                 centerprint_start_y = bound(allowedmin, preferred, forbiddenmin);
1809         }
1810         //  allowedmin forbiddenmin forbiddenmax allowedmax
1811         else if(allowedmin < forbiddenmin)
1812         {
1813                 // make sure the forbidden zone is not covered
1814                 if(preferred > (forbiddenmin + forbiddenmax) * 0.5)
1815                         centerprint_start_y = bound(allowedmin, preferred, forbiddenmin);
1816                 else
1817                         centerprint_start_y = bound(forbiddenmax, preferred, allowedmin);
1818         }
1819         //  forbiddenmin allowedmin allowedmax forbiddenmax
1820         else if(allowedmax < forbiddenmax)
1821         {
1822                 // it's better to leave the allowed zone (overlap with scoreboard) than
1823                 // to cover the forbidden zone (crosshair)
1824                 if(preferred > (forbiddenmin + forbiddenmax) * 0.5)
1825                         centerprint_start_y = forbiddenmax;
1826                 else
1827                         centerprint_start_y = forbiddenmin;
1828         }
1829         //  forbiddenmin allowedmin forbiddenmax allowedmax
1830         else
1831         {
1832                 centerprint_start_y = bound(forbiddenmax, preferred, allowedmax);
1833         }
1834 #else
1835         centerprint_start_y =
1836                 min(
1837                         max(
1838                                 max(scoreboard_bottom, vid_conheight * 0.5 + 16),
1839                                 (havail - h)/2
1840                         ),
1841                         havail - h
1842                 );
1843 #endif
1844
1845         centerprint_num = i;
1846         centerprint_expire = time + cvar("scr_centertime");
1847 }
1848
1849 void Sbar_DrawCenterPrint (void)
1850 {
1851         float i;
1852         vector pos;
1853         string ts;
1854         float a;
1855
1856         //if(time > centerprint_expire)
1857         //      return;
1858
1859         //a = bound(0, 1 - 2 * (time - centerprint_expire), 1);
1860         a = bound(0, 1 - 4 * (time - centerprint_expire), 1);
1861         //sz = 1.2 / (a + 0.2);
1862
1863         if(a <= 0)
1864                 return;
1865
1866         pos = centerprint_start;
1867         for (i=0; i<centerprint_num; i = i + 1)
1868         {
1869                 pos_x = (vid_conwidth - centerprint_fontsize_x * centerprint_width[i]) * 0.5;
1870                 ts = centerprint_messages[i];
1871                 if (ts != "")
1872                 {
1873                         dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1874                         drawcolorcodedstring(pos, ts, centerprint_fontsize, a, DRAWFLAG_NORMAL);
1875                         //  - '0 0.5 0' * (sz - 1) * centerprint_fontsize_x - '0.5 0 0' * (sz - 1) * centerprint_width[i] * centerprint_fontsize_y, centerprint_fontsize * sz
1876                         pos_y = pos_y + centerprint_fontsize_y;
1877                 }
1878                 else
1879                         // half height for empty lines looks better
1880                         pos_y = pos_y + centerprint_fontsize_y * 0.5;
1881         }
1882 }
1883
1884 vector Sbar_DrawNoteLine(vector offset, string s)
1885 {
1886         dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1887         drawcolorcodedstring(
1888                 offset - sbar_fontsize_x * '1 0 0' * stringwidth(s, TRUE),
1889                 s,
1890                 sbar_fontsize,
1891                 sbar_alpha_fg,
1892                 0
1893         );
1894         return offset + sbar_fontsize_y * '0 1 0';
1895 }
1896
1897 void Sbar_DrawPressedKeys(void)
1898 {
1899         vector pos, bgsize;
1900         float pressedkeys;
1901
1902         pos = stov(cvar_string("cl_showpressedkeys_position"));
1903
1904         bgsize = '126 75 0';
1905
1906         pos = '1 0 0' * (vid_conwidth - bgsize_x) * pos_x
1907             + '0 1 0' * (vid_conheight - bgsize_y) * pos_y;
1908         pos -= '-15 -6 0'; // adjust to the origin of these numbers
1909
1910         pressedkeys = getstatf(STAT_PRESSED_KEYS);
1911         drawpic(pos + '-15   -6   0', "gfx/hud/keys/key_bg.tga",           bgsize, '1 1 1', .1, DRAWFLAG_NORMAL);
1912         drawpic(pos + ' 83.5  9   0', ((pressedkeys & KEY_CROUCH) ? "gfx/hud/keys/key_crouch_inv.tga" : "gfx/hud/keys/key_crouch.tga"), ' 24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1913         drawpic(pos + ' 32   -1.5 0', ((pressedkeys & KEY_FORWARD) ? "gfx/hud/keys/key_forward_inv.tga" : "gfx/hud/keys/key_forward.tga"),  ' 32 32 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1914         drawpic(pos + '-11.5  9   0', ((pressedkeys & KEY_JUMP) ? "gfx/hud/keys/key_jump_inv.tga" : "gfx/hud/keys/key_jump.tga"),     ' 24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1915         drawpic(pos + ' -1   32   0', ((pressedkeys & KEY_LEFT) ? "gfx/hud/keys/key_left_inv.tga" : "gfx/hud/keys/key_left.tga"),     ' 32 32 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1916         drawpic(pos + ' 32   32   0', ((pressedkeys & KEY_BACKWARD) ? "gfx/hud/keys/key_backward_inv.tga" : "gfx/hud/keys/key_backward.tga"), ' 32 32 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1917         drawpic(pos + ' 65   32   0', ((pressedkeys & KEY_RIGHT) ? "gfx/hud/keys/key_right_inv.tga" : "gfx/hud/keys/key_right.tga"),    ' 32 32 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1918 }
1919
1920 void Sbar_ShowSpeed(void)
1921 {
1922         vector numsize;
1923         float pos;
1924         string speed;
1925
1926         if (cvar("cl_showspeed_z") == 1)
1927                 speed = ftos(floor(vlen(pmove_vel) + 0.5));
1928         else
1929                 speed = ftos(floor(vlen(pmove_vel - pmove_vel_z * '0 0 1') + 0.5));
1930
1931         pos = cvar("cl_showspeed_position");
1932         numsize_x = numsize_y = cvar("cl_showspeed_size");
1933         pos = (vid_conheight - numsize_y) * pos;
1934
1935         drawstringcenter('1 0 0' + pos * '0 1 0', speed, numsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
1936 }
1937
1938 void Sbar_DrawAccuracyStats()
1939 {
1940         float i, count_hitscan, count_splash, row;  // count is the number of 'colums'
1941         float weapon_hit, weapon_damage, weapon_stats;
1942         float left_border;  // position where the weapons start, the description is in the border
1943         vector fill_colour, fill_size;
1944         vector pos;
1945
1946         float col_margin = 20;  // pixels between the columns
1947         float row_margin = 20;  // pixels between the rows
1948         float top_border;  // position where the first row starts: pixels down the screen
1949
1950         fill_size_x = 5 * sbar_fontsize_x;  // width of the background
1951         fill_size_y = 10 * sbar_fontsize_y;  // height of the background
1952
1953         // 45 pixels is the same as the 'Scoreboard' heading
1954         drawfont = sbar_bigfont;
1955         drawstringcenter('0 45 0', "Weapon Accuracy", 2 * sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
1956         left_border = col_margin + 11 * sbar_fontsize_x;
1957
1958         if(warmup_stage)
1959         {
1960                 if(mod(time, 1) >= 0.4)
1961                         drawstringcenter('0 85 0', "Stats are not tracked during warmup stage", sbar_fontsize, '1 1 0', sbar_alpha_fg, DRAWFLAG_NORMAL);
1962
1963                 return;
1964         }
1965
1966         top_border = 80;
1967         Sbar_DrawAccuracyStats_Description_Hitscan('1 0 0' * col_margin + '0 1 0' * top_border);
1968
1969         top_border = 220;
1970         Sbar_DrawAccuracyStats_Description_Splash('1 0 0' * col_margin + '0 1 0' * top_border);
1971
1972
1973         for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1974         {
1975                 weapon_hit = weapon_hits[i];
1976                 weapon_damage = weapon_fired[i];
1977                 self = get_weaponinfo(i);
1978
1979                 //if ((weapon_number != 42))  // print them all :)
1980                 if ((self.weapon_type == WEP_TYPE_SPLASH) && (weapon_damage))
1981                 {
1982                         top_border = 220;
1983                         weapon_stats = rint(100 * weapon_hit / weapon_damage);
1984
1985                         fill_colour_x = 1 - 0.015 * weapon_stats;
1986                         fill_colour_y = 1 - 0.015 * (100 - weapon_stats);
1987
1988 //                      how the background colour is calculated
1989 //                      %       red     green   red_2           green_2
1990 //                      0       1       0       1 - % * 0.015   1 - (100 - %) * 0.015
1991 //                      10      0.85    0       1 - % * 0.015   1 - (100 - %) * 0.015
1992 //                      20      0.70    0       1 - % * 0.015   1 - (100 - %) * 0.015
1993 //                      30      0.55    0       1 - % * 0.015   1 - (100 - %) * 0.015
1994 //                      40      0.40    0.10    1 - % * 0.015   1 - (100 - %) * 0.015
1995 //                      50      0.25    0.25    1 - % * 0.015   1 - (100 - %) * 0.015
1996 //                      60      0.10    0.40    1 - % * 0.015   1 - (100 - %) * 0.015
1997 //                      70      0       0.55    1 - % * 0.015   1 - (100 - %) * 0.015
1998 //                      80      0       0.70    1 - % * 0.015   1 - (100 - %) * 0.015
1999 //                      90      0       0.85    1 - % * 0.015   1 - (100 - %) * 0.015
2000 //                      100     0       1       1 - % * 0.015   1 - (100 - %) * 0.015
2001
2002                         if ((left_border + count_splash * (fill_size_x + col_margin) + fill_size_x) >= vid_conwidth)
2003                         {
2004                                 count_splash = 0;
2005                                 ++row;
2006                                 Sbar_DrawAccuracyStats_Description_Splash('1 0 0' * col_margin + '0 1 0' * (top_border + row * (fill_size_y + row_margin)));
2007                         }
2008
2009                         pos_x = left_border + count_splash * (fill_size_x + col_margin);
2010                         pos_y = top_border + row * (fill_size_y + row_margin);
2011
2012                         // background
2013                         drawpic(pos, "gfx/hud/sb_accuracy", fill_size , fill_colour, sbar_alpha_bg, DRAWFLAG_NORMAL);
2014                         drawborderlines(sbar_border_thickness, pos, fill_size, '0 0 0', sbar_alpha_bg, DRAWFLAG_NORMAL);
2015
2016                         // the weapon
2017                         drawpic(pos, strcat("gfx/hud/inv_weapon", ftos(i-1)), '1 0.5 0' * fill_size_x , '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2018
2019                         // the amount of shots fired or max damage
2020                         drawstringright(pos + '4.5 0 0' * sbar_fontsize_x + '0 3 0' * sbar_fontsize_y, ftos(weapon_damage), sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2021
2022                         // the amount of hits or actual damage
2023                         drawstringright(pos + '4.5 0 0' * sbar_fontsize_x + '0 5 0' * sbar_fontsize_y, ftos(weapon_hit), sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2024
2025                         // the accuracy
2026                         drawstringright(pos + '4.5 0 0' * sbar_fontsize_x + '0 7 0' * sbar_fontsize_y, strcat(ftos(weapon_stats),"%"), sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2027
2028                         // the amount of shots missed or damage wasted
2029                         drawstringright(pos + '4.5 0 0' * sbar_fontsize_x + '0 9 0' * sbar_fontsize_y, ftos(weapon_damage - weapon_hit), sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2030
2031                         ++count_splash;
2032                 }
2033                 else if ((self.weapon_type == WEP_TYPE_HITSCAN) && (weapon_damage))
2034                 {
2035                         top_border = 80;
2036                         weapon_stats = rint(100 * weapon_hit / weapon_damage);
2037
2038                         fill_colour_x = 1 - 0.015 * weapon_stats;
2039                         fill_colour_y = 1 - 0.015 * (100 - weapon_stats);
2040
2041 //                      how the background colour is calculated
2042 //                      %       red     green   red_2           green_2
2043 //                      0       1       0       1 - % * 0.015   1 - (100 - %) * 0.015
2044 //                      10      0.85    0       1 - % * 0.015   1 - (100 - %) * 0.015
2045 //                      20      0.70    0       1 - % * 0.015   1 - (100 - %) * 0.015
2046 //                      30      0.55    0       1 - % * 0.015   1 - (100 - %) * 0.015
2047 //                      40      0.40    0.10    1 - % * 0.015   1 - (100 - %) * 0.015
2048 //                      50      0.25    0.25    1 - % * 0.015   1 - (100 - %) * 0.015
2049 //                      60      0.10    0.40    1 - % * 0.015   1 - (100 - %) * 0.015
2050 //                      70      0       0.55    1 - % * 0.015   1 - (100 - %) * 0.015
2051 //                      80      0       0.70    1 - % * 0.015   1 - (100 - %) * 0.015
2052 //                      90      0       0.85    1 - % * 0.015   1 - (100 - %) * 0.015
2053 //                      100     0       1       1 - % * 0.015   1 - (100 - %) * 0.015
2054
2055                         if ((left_border + count_hitscan * (fill_size_x + col_margin) + fill_size_x + cvar("stats_right_margin")) >= vid_conwidth)
2056                         {
2057                                 count_hitscan = 0;
2058                                 ++row;
2059                                 Sbar_DrawAccuracyStats_Description_Hitscan('1 0 0' * col_margin + '0 1 0' * (top_border + row * (fill_size_y + row_margin)));
2060                         }
2061
2062                         pos_x = left_border + count_hitscan * (fill_size_x + col_margin);
2063                         pos_y = top_border + row * (fill_size_y + row_margin);
2064
2065                         // background
2066                         drawpic(pos, "gfx/hud/sb_accuracy", fill_size , fill_colour, sbar_alpha_bg, DRAWFLAG_NORMAL);
2067                         drawborderlines(sbar_border_thickness, pos, fill_size, '0 0 0', sbar_alpha_bg, DRAWFLAG_NORMAL);
2068
2069                         // the weapon
2070                         drawpic(pos, strcat("gfx/hud/inv_weapon", ftos(i-1)), '1 0.5 0' * fill_size_x , '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2071
2072                         // the amount of shots fired or max damage
2073                         drawstringright(pos + '4.5 0 0' * sbar_fontsize_x + '0 3 0' * sbar_fontsize_y, ftos(weapon_damage), sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2074
2075                         // the amount of hits or actual damage
2076                         drawstringright(pos + '4.5 0 0' * sbar_fontsize_x + '0 5 0' * sbar_fontsize_y, ftos(weapon_hit), sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2077
2078                         // the accuracy
2079                         drawstringright(pos + '4.5 0 0' * sbar_fontsize_x + '0 7 0' * sbar_fontsize_y, strcat(ftos(weapon_stats),"%"), sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2080
2081                         // the amount of shots missed or damage wasted
2082                         drawstringright(pos + '4.5 0 0' * sbar_fontsize_x + '0 9 0' * sbar_fontsize_y, ftos(weapon_damage - weapon_hit), sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2083
2084                         ++count_hitscan;
2085                 }
2086         }
2087 }
2088
2089 void Sbar_DrawAccuracyStats_Description_Hitscan(vector position)
2090 {
2091         drawfont = sbar_font;
2092          // hitscan stats
2093         drawstring(position + '0 3 0' * sbar_fontsize_y, "Shots fired:", sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2094         drawstring(position + '0 5 0' * sbar_fontsize_y, "Shots hit:", sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2095         drawstring(position + '0 7 0' * sbar_fontsize_y, "Accuracy:", sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2096         drawstring(position + '0 9 0' * sbar_fontsize_y, "Shots missed:", sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2097 }
2098
2099 void Sbar_DrawAccuracyStats_Description_Splash(vector position)
2100 {
2101         //splash stats
2102         drawfont = sbar_font;
2103         drawstring(position + '0 3 0' * sbar_fontsize_y, "Maximum damage:", sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2104         drawstring(position + '0 5 0' * sbar_fontsize_y, "Actual damage:", sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2105         drawstring(position + '0 7 0' * sbar_fontsize_y, "Accuracy:", sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2106         drawstring(position + '0 9 0' * sbar_fontsize_y, "Damage wasted:", sbar_fontsize, '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2107 }
2108
2109 void drawstringright(vector position, string text, vector scale, vector rgb, float alpha, float flag)
2110 {
2111         position_x -= 2 / 3 * strlen(text) * scale_x;
2112         drawstring(position, text, scale, rgb, alpha, flag);
2113 }
2114
2115 void drawstringcenter(vector position, string text, vector scale, vector rgb, float alpha, float flag)
2116 {
2117         position_x = 0.5 * (vid_conwidth - 0.6025 * strlen(text) * scale_x);
2118         drawstring(position, text, scale, rgb, alpha, flag);
2119 }
2120
2121 float GetAmmoStat(float i)
2122 {
2123         switch(i)
2124         {
2125                 case 0: return STAT_SHELLS;
2126                 case 1: return STAT_NAILS;
2127                 case 2: return STAT_ROCKETS;
2128                 case 3: return STAT_CELLS;
2129                 case 4: return STAT_FUEL;
2130                 default: return -1;
2131         }
2132 }
2133
2134 float GetAmmoItemCode(float i)
2135 {
2136         switch(i)
2137         {
2138                 case 0: return IT_SHELLS;
2139                 case 1: return IT_NAILS;
2140                 case 2: return IT_ROCKETS;
2141                 case 3: return IT_CELLS;
2142                 case 4: return IT_FUEL;
2143                 default: return -1;
2144         }
2145 }
2146
2147 string GetAmmoPicture(float i)
2148 {
2149         switch(i)
2150         {
2151                 case 0: return "gfx/hud/sb_shells";
2152                 case 1: return "gfx/hud/sb_bullets";
2153                 case 2: return "gfx/hud/sb_rocket";
2154                 case 3: return "gfx/hud/sb_cells";
2155                 case 4: return "gfx/hud/sb_fuel";
2156                 default: return "";
2157         }
2158 }
2159
2160 void Sbar_Draw (void)
2161 {
2162         // vectors for top right, bottom right, bottom and bottom left corners
2163
2164         vector topright;
2165         vector bottom;
2166         vector bottomright;
2167         vector bottomleft;
2168
2169         topright_x = vid_conwidth;
2170         topright_y = 0;
2171         topright_z = 0;
2172
2173         bottom_x = vid_conwidth/2;
2174         bottom_y = vid_conheight;
2175         bottom_z = 0;
2176
2177         bottomright_x = vid_conwidth;
2178         bottomright_y = vid_conheight;
2179         bottomright_z = 0;
2180
2181         bottomleft_x = 0;
2182         bottomleft_y = vid_conheight;
2183         bottomleft_z = 0;
2184
2185         sbar_alpha_bg = cvar("sbar_alpha_bg");
2186         sbar_border_thickness = bound(0, cvar("sbar_border_thickness"), 5);
2187         sbar_color_bg_team = cvar("sbar_color_bg_team");
2188         sbar_scoreboard_alpha_bg = cvar("sbar_scoreboard_alpha_bg");
2189         sbar_scoreboard_highlight = cvar("sbar_scoreboard_highlight");
2190
2191         float i;
2192         float weapon_stats;
2193         float x, fade;
2194         float stat_items, stat_weapons;
2195
2196         weapon_stats = getstati(STAT_DAMAGE_HITS);
2197         weapon_number = weapon_stats & 63;
2198         weapon_hits[weapon_number] = rint(weapon_stats / 64);
2199
2200         weapon_stats = getstati(STAT_DAMAGE_FIRED);
2201         weapon_number = weapon_stats & 63;
2202         weapon_fired[weapon_number] = rint(weapon_stats / 64);
2203
2204         vector o; o = '1 0 0' * vid_conwidth;
2205         o_y = 28; // move spectator text slightly down to prevent overlapping the timer
2206
2207         string s;
2208
2209         sbar_fontsize = Sbar_GetFontsize("sbar_fontsize");
2210
2211         if(spectatee_status && !intermission)
2212         {
2213                 if(spectatee_status == -1)
2214                         s = "^1Observing";
2215                 else
2216                         s = strcat("^1Spectating ^7", GetPlayerName(spectatee_status - 1));
2217                 o = Sbar_DrawNoteLine(o, s);
2218
2219                 if(spectatee_status == -1)
2220                         s = strcat("^1Press ^3", getcommandkey("primary fire", "+attack"), "^1 to spectate");
2221                 else
2222                         s = strcat("^1Press ^3", getcommandkey("primary fire", "+attack"), "^1 for another player");
2223                 o = Sbar_DrawNoteLine(o, s);
2224
2225                 if(spectatee_status == -1)
2226                         s = strcat("^1Use ^3", getcommandkey("next weapon", "weapnext"), "^1 or ^3", getcommandkey("previous weapon", "weapprev"), "^1 to change the speed");
2227                 else
2228                         s = strcat("^1Press ^3", getcommandkey("secondary fire", "+attack2"), "^1 to observe");
2229                 o = Sbar_DrawNoteLine(o, s);
2230
2231                 s = strcat("^1Press ^3", getcommandkey("server info", "+show_info"), "^1 for gamemode info");
2232                 o = Sbar_DrawNoteLine(o, s);
2233
2234                 if(gametype == GAME_ARENA)
2235                         s = "^1Wait for your turn to join";
2236                 else if(gametype == GAME_LMS)
2237                 {
2238                         entity sk;
2239                         sk = playerslots[player_localentnum - 1];
2240                         if(sk.(scores[ps_primary]) >= 666)
2241                                 s = "^1Match has already begun";
2242                         else if(sk.(scores[ps_primary]) > 0)
2243                                 s = "^1You have no more lives left";
2244                         else
2245                                 s = strcat("^1Press ^3", getcommandkey("jump", "+jump"), "^1 to join");
2246                 }
2247                 else
2248                         s = strcat("^1Press ^3", getcommandkey("jump", "+jump"), "^1 to join");
2249                 o = Sbar_DrawNoteLine(o, s);
2250
2251                 //show restart countdown:
2252                 if (time < getstatf(STAT_GAMESTARTTIME)) {
2253                         float countdown;
2254                         //we need to ceil, otherwise the countdown would be off by .5 when using round()
2255                         countdown = ceil(getstatf(STAT_GAMESTARTTIME) - time);
2256                         s = strcat("^1Game starts in ^3", ftos(countdown), "^1 seconds");
2257                         o = Sbar_DrawNoteLine(o, s);
2258                 }
2259         }
2260         if(warmup_stage && !intermission)
2261         {
2262                 s = "^2Currently in ^1warmup^2 stage!";
2263                 o = Sbar_DrawNoteLine(o, s);
2264         }
2265
2266         // move more important stuff more to the middle so its more visible
2267         o_y = vid_conheight * 0.66;
2268
2269         string blinkcolor;
2270         if(mod(time, 1) >= 0.5)
2271                 blinkcolor = "^1";
2272         else
2273                 blinkcolor = "^3";
2274
2275         if(ready_waiting && !intermission)
2276         {
2277                 if(ready_waiting_for_me)
2278                 {
2279                         if(warmup_stage)
2280                                 s = strcat(blinkcolor, "Press ^3", getcommandkey("ready", "ready"), blinkcolor, " to end warmup");
2281                         else
2282                                 s = strcat(blinkcolor, "Press ^3", getcommandkey("ready", "ready"), blinkcolor, " once you are ready");
2283                 }
2284                 else
2285                 {
2286                         if(warmup_stage)
2287                                 s = strcat("^2Waiting for others to ready up to end warmup...");
2288                         else
2289                                 s = strcat("^2Waiting for others to ready up...");
2290                 }
2291                 o = Sbar_DrawNoteLine(o, s);
2292         }
2293         else if(warmup_stage && !intermission)
2294         {
2295                 s = strcat("^2Press ^3", getcommandkey("ready", "ready"), "^2 to end warmup");
2296                 o = Sbar_DrawNoteLine(o, s);
2297         }
2298         if(vote_waiting)
2299         {
2300                 s = strcat("^2A vote has been called for ^1", vote_called_vote);
2301                 o = Sbar_DrawNoteLine(o, s);
2302
2303                 if(vote_waiting_for_me)
2304                 {
2305                         s = strcat(blinkcolor, "Press ^3", getcommandkey("vote yes", "vyes"), blinkcolor, " to accept");
2306                         o = Sbar_DrawNoteLine(o, s);
2307
2308                         s = strcat(blinkcolor, "Press ^3", getcommandkey("vote no", "vno"), blinkcolor, " to reject");
2309                         o = Sbar_DrawNoteLine(o, s);
2310
2311                         s = strcat(blinkcolor, "Press ^3", getcommandkey("vote abstain", "vabstain"), blinkcolor, " to abstain");
2312                         o = Sbar_DrawNoteLine(o, s);
2313                 }
2314                 else
2315                 {
2316                         s = strcat("^2Waiting for others to vote...");
2317                         o = Sbar_DrawNoteLine(o, s);
2318                 }
2319         }
2320         if(teamplay && !intermission && !spectatee_status)
2321         {
2322                 entity tm;
2323                 float ts_min, ts_max;
2324                 tm = teams.sort_next;
2325                 if (tm)
2326                 {
2327                         for(; tm.sort_next; tm = tm.sort_next)
2328                         {
2329                                 if(!tm.team_size || tm.team == COLOR_SPECTATOR)
2330                                         continue;
2331                                 if(!ts_min) ts_min = tm.team_size;
2332                                 else ts_min = min(ts_min, tm.team_size);
2333                                 if(!ts_max) ts_max = tm.team_size;
2334                                 else ts_max = max(ts_max, tm.team_size);
2335                         }
2336                         if ((ts_max - ts_min) > 1)
2337                         {
2338                                 s = strcat(blinkcolor, "Teamnumbers are unbalanced!");
2339                                 tm = GetTeam(myteam, false);
2340                                 if (tm)
2341                                 if (tm.team != COLOR_SPECTATOR)
2342                                 if (tm.team_size == ts_max)
2343                                         s = strcat(s, " Press ^3", getcommandkey("team menu", "menu_showteamselect"), blinkcolor, " to adjust");
2344
2345                                 o = Sbar_DrawNoteLine(o, s);
2346                         }
2347                 }
2348         }
2349
2350         Sbar_UpdatePlayerTeams();
2351
2352         if (intermission == 2) // map voting screen
2353         {
2354                 if(sb_showscores) {
2355                         Sbar_DrawScoreboard();
2356                         Sbar_Score(16);
2357                 }
2358                 else if(sb_showaccuracy) {
2359                         Sbar_DrawAccuracyStats();
2360                         Sbar_Score(16);
2361                 }
2362                 else
2363                         Sbar_FinaleOverlay();
2364         }
2365         else if (sb_showscores_force || getstati(STAT_HEALTH) <= 0 || intermission == 1)
2366         {
2367                 if(sb_showaccuracy)
2368                         Sbar_DrawAccuracyStats();
2369                 else
2370                         Sbar_DrawScoreboard();
2371                 Sbar_Score(16);
2372         }
2373         else
2374         {
2375                 if (sb_showscores)
2376                         Sbar_DrawScoreboard();
2377                 else if(sb_showaccuracy)
2378                         Sbar_DrawAccuracyStats();
2379
2380                 float armor, health;
2381                 armor = getstati(STAT_ARMOR);
2382                 health = getstati(STAT_HEALTH);
2383
2384                 stat_items = getstati(STAT_ITEMS);
2385                 stat_weapons = getstati(STAT_WEAPONS);
2386
2387                 fade = 3.2 - 2 * (time - weapontime);
2388                 fade = bound(0.7, fade, 1);
2389
2390                 vector bg_size; // hud background size
2391                 bg_size_x = 800;
2392                 if (vid_conwidth > 800) // if conwidth > 800, resize the background image
2393                         bg_size_x = vid_conwidth;
2394                 bg_size_y = 58;
2395                 bg_size_z = 0;
2396
2397                 vector bgoffset; // hud background offset
2398                 bgoffset_x = 0;
2399                 bgoffset_y = 0;
2400                 bgoffset_z = 0;
2401                 if (vid_conwidth < 800) // if conwidth < 800 we need to offset the background image to keep it centered, as it will be scaled up
2402                         bgoffset_x = (vid_conwidth - 800) / 2;
2403
2404                 if (cvar("viewsize") <= 100) {
2405                         if (teamplay)
2406                                 drawpic(bottomleft - '0 58 0' + bgoffset, "gfx/hud/sbar", bg_size, GetTeamRGB(myteam) * sbar_color_bg_team, sbar_alpha_bg, DRAWFLAG_NORMAL); // hud color = myteam color
2407                         else {
2408                                 // allow for custom HUD colors in non-teamgames
2409                                 color_x = cvar("sbar_color_bg_r");
2410                                 color_y = cvar("sbar_color_bg_g");
2411                                 color_z = cvar("sbar_color_bg_b");
2412
2413                                 drawpic(bottomleft - '0 58 0' + bgoffset, "gfx/hud/sbar", bg_size, color, sbar_alpha_bg, DRAWFLAG_NORMAL);
2414                         }
2415                 }
2416
2417                 if(sbar_hudselector == 2) // combined health and armor display
2418                 {
2419                         vector v;
2420                         v = healtharmor_maxdamage(health, armor, armorblockpercent);
2421
2422                         vector num_pos;
2423                         num_pos = bottom - element_offset - '0 22 0' + '-96 0 0';
2424
2425                         x = floor(v_x + 1);
2426
2427                         if(v_z) // fully armored
2428                         {
2429                                 // here, armorideal > armor
2430                                 drawpic(num_pos + '78 -4.5 0', "gfx/hud/sb_health", '32 32 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2431                                 drawpic(num_pos + '108 -4.5 0', "gfx/hud/sb_armor", '20 20 0', '1 1 1', sbar_alpha_fg * armor / v_y, DRAWFLAG_NORMAL);
2432                         }
2433                         else
2434                         {
2435                                 drawpic(num_pos + '108 -4.5 0', "gfx/hud/sb_health", '20 20 0', '1 1 1', sbar_alpha_fg * v_y / armor, DRAWFLAG_NORMAL);
2436                                 drawpic(num_pos + '78 -4.5 0', "gfx/hud/sb_armor", '32 32 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2437                         }
2438                         Sbar_DrawXNum_Colored(num_pos, x, 24, sbar_alpha_fg); // draw the combined health and armor
2439                 }
2440
2441                 else
2442                 {
2443                         vector health_pos, armor_pos;
2444
2445                         if (sbar_hudselector == 0) { // old style layout with armor left of health
2446                                 health_pos = bottom - element_offset - '0 22 0' + '14 0 0';
2447                                 armor_pos = bottom - element_offset - '0 22 0' + '-96 0 0';
2448                         }
2449                         else {
2450                                 health_pos = bottom - element_offset - '0 22 0' + '-96 0 0';
2451                                 armor_pos = bottom - element_offset - '0 22 0' + '14 0 0';
2452                         }
2453
2454                         // armor
2455                         x = armor;
2456                         if (x > 0)
2457                         {
2458                                 drawpic(armor_pos + '78 -4.5 0', "gfx/hud/sb_armor", '32 32 0', '1 1 1', (x+10)/55 * sbar_alpha_fg, DRAWFLAG_NORMAL);
2459                                 if (x > 45)
2460                                         Sbar_DrawXNum_Colored(armor_pos, x, 24, sbar_alpha_fg);
2461                                 else
2462                                         Sbar_DrawXNum_Colored(armor_pos, x, 24, (x+10)/55 * sbar_alpha_fg);
2463                         }
2464
2465                         // health
2466                         x = health;
2467                         drawpic(health_pos + '78 -4.5 0', "gfx/hud/sb_health", '32 32 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2468                         Sbar_DrawXNum_Colored(health_pos, x, 24, sbar_alpha_fg);
2469                 }
2470
2471                 // weapon icons
2472                 x = 1.0;
2473                 Sbar_DrawWeapon_Clear();
2474                 for(i = 1; i <= 24; ++i)
2475                 {
2476                         if(weaponimpulse[i-1] >= 0)
2477                         if(stat_weapons & x)
2478                         {
2479                                 Sbar_DrawWeapon(i-1, fade, (i == activeweapon), i);
2480                         }
2481                         x *= 2;
2482                 }
2483
2484                 // ammo
2485                 float a; // i will be the ammo type (already declared), a will contain how much ammo there is of type i
2486                 vector pos;
2487                 pos_z = 0;
2488
2489                 for (i = 0; i < 4; ++i) {
2490                         a = getstati(GetAmmoStat(i)); // how much ammo do we have of type i?
2491
2492                         if(sbar_currentammo)
2493                         {
2494                                 pos = '0 0 0';
2495                                 if (stat_items & GetAmmoItemCode(i))
2496                                 {
2497                                         if(vid_conwidth >= 800) {
2498                                                 pos_x = 170;
2499                                                 pos_y = -40;
2500                                         }
2501                                         else {
2502                                                 pos_x = vid_conwidth - 110;
2503                                                 pos_y = -128;
2504                                         }
2505
2506                                         drawpic(bottomleft + pos + '0 1.5 0', "gfx/hud/sb_ammobg", '107 29 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2507                                         drawpic(bottomleft + pos + '76 3 0', GetAmmoPicture(i), '24 24 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2508                                         if(a < 10)
2509                                                 Sbar_DrawXNum(bottomleft + pos + '5 5 0', a, 3, 24, '0.7 0 0', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
2510                                         else
2511                                                 Sbar_DrawXNum(bottomleft + pos + '5 5 0', a, 3, 24, '1 1 1', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
2512                                 }
2513                         }
2514                         else
2515                         {
2516                                 if (a > 0) {
2517                                         pos = '0 0 0';
2518                                         if (vid_conwidth >= 800)
2519                                                 switch (i) {
2520                                                         case 0: pos_x = 114; pos_y = -48; break; // shells
2521                                                         case 1: pos_x = 114; pos_y = -26; break; // bullets
2522                                                         case 2: pos_x = 200; pos_y = -48; break; // rockets
2523                                                         case 3: pos_x = 200; pos_y = -26; break; // cells
2524                                                 }
2525                                         else // if vid_conwidth is lower than 800, ammo will overlap with weapon icons and health so we'll move it to the right
2526                                                 switch (i) {
2527                                                         case 0: pos_x = vid_conwidth - 158; pos_y = -150; break; // shells
2528                                                         case 1: pos_x = vid_conwidth - 158; pos_y = -128; break; // bullets
2529                                                         case 2: pos_x = vid_conwidth - 84; pos_y = -150; break; // rockets
2530                                                         case 3: pos_x = vid_conwidth - 84; pos_y = -128; break; // cells
2531                                                 }
2532
2533                                         if (stat_items & GetAmmoItemCode(i))
2534                                                 drawpic(bottomleft + pos + '0 1.5 0', "gfx/hud/sb_ammobg", '80 22 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2535                                         drawpic(bottomleft + pos + '56 3 0', GetAmmoPicture(i), '18 18 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2536                                         if (a < 10) {
2537                                                 if(stat_items & GetAmmoItemCode(i))
2538                                                         Sbar_DrawXNum(bottomleft + pos + '6 4.5 0', a, 3, 16, '0.7 0 0', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
2539                                                 else
2540                                                         Sbar_DrawXNum(bottomleft + pos + '6 4.5 0', a, 3, 16, '0.7 0 0', 0, 0, sbar_alpha_fg * 0.7, DRAWFLAG_NORMAL);
2541                                         } else {
2542                                                 if(stat_items & GetAmmoItemCode(i))
2543                                                         Sbar_DrawXNum(bottomleft + pos + '6 4.5 0', a, 3, 16, '1 1 1', 0, 0, sbar_alpha_fg, DRAWFLAG_NORMAL);
2544                                                 else
2545                                                         Sbar_DrawXNum(bottomleft + pos + '6 4.5 0', a, 3, 16, '0.7 0.7 0.7', 0, 0, sbar_alpha_fg * 0.7, DRAWFLAG_NORMAL);
2546                                         }
2547                                 }
2548                         }
2549                 }
2550
2551                 // fuel ammo
2552                 a = getstati(GetAmmoStat(4)); // how much fuel do we have?
2553
2554                 if (a > 0) { // if we have fuel, draw the amount
2555                         float invincibility_time, dt;
2556                         invincibility_time = getstatf(STAT_INVINCIBLE_FINISHED);
2557                         dt = invincibility_time - time;
2558                         if (dt > 0) { // if the invincibility timer is active, draw fuel ammo elsewhere
2559                                 pos_x = bottom_x + 140;
2560                                 pos_y = bottom_y - 72;
2561                         }
2562                         else { // if the invincibility timer is inactive, draw the fuel ammo there (it's rare to have invincibility + fuel anyway)
2563                                 pos_x = bottom_x + 140;
2564                                 pos_y = bottom_y - 22;
2565                         }
2566                         drawpic(pos - '0 2 0' + '52 0 0', GetAmmoPicture(4), '20 20 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
2567                         if (a > 10)
2568                                 Sbar_DrawXNum(pos, a, 3, 16, '1 1 1', 0, 0, 1, DRAWFLAG_NORMAL);
2569                         else
2570                                 Sbar_DrawXNum(pos, a, 3, 16, '0.7 0 0', 0, 0, 1, DRAWFLAG_NORMAL);
2571                 }
2572
2573                 // draw scores and timer
2574                 Sbar_Score(16);
2575
2576                 //show strength/invincibility ICON and timer:
2577                 CSQC_Strength_Timer();
2578
2579                 if(gametype == GAME_KEYHUNT)
2580                 {
2581                         CSQC_kh_hud();
2582                 } else if(gametype == GAME_CTF)
2583                 {
2584                         CSQC_ctf_hud();
2585                 } else if(gametype == GAME_NEXBALL)
2586                 {
2587                         CSQC_nb_hud();
2588                 }
2589         }
2590 }
2591
2592 // CTF HUD
2593 void CSQC_ctf_hud(void)
2594 {
2595         vector bottomleft, redflag_pos, blueflag_pos;
2596         bottomleft_x = 0;
2597         bottomleft_y = vid_conheight;
2598         bottomleft_z = 0;
2599
2600         float redflag, blueflag;
2601         float stat_items;
2602
2603         stat_items = getstati(STAT_ITEMS);
2604         redflag = (stat_items/IT_RED_FLAG_TAKEN) & 3;
2605         blueflag = (stat_items/IT_BLUE_FLAG_TAKEN) & 3;
2606
2607         if (myteam == COLOR_TEAM1) { // always draw own flag on left
2608                 redflag_pos = bottomleft - element_offset - '-4 36 0';
2609                 blueflag_pos = bottomleft - element_offset - '-68 36 0';
2610         }
2611
2612         else {
2613                 blueflag_pos = bottomleft - element_offset - '-4 36 0';
2614                 redflag_pos = bottomleft - element_offset - '-68 36 0';
2615         }
2616
2617         switch(redflag)
2618         {
2619         case 1: drawpic(redflag_pos, "gfx/hud/sb_flag_red_taken", '48 48 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL); break;
2620         case 2: drawpic(redflag_pos, "gfx/hud/sb_flag_red_lost", '48 48 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL); break;
2621         case 3: drawpic(redflag_pos, "gfx/hud/sb_flag_red_carrying", '48 48 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL); break;
2622         default:
2623                 if(stat_items & IT_CTF_SHIELDED)
2624                         if(myteam == COLOR_TEAM2)
2625                                 drawpic(redflag_pos, "gfx/hud/sb_flag_red_shielded", '48 48 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL); break;
2626         }
2627
2628         switch(blueflag)
2629         {
2630         case 1: drawpic(blueflag_pos, "gfx/hud/sb_flag_blue_taken", '48 48 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL); break;
2631         case 2: drawpic(blueflag_pos, "gfx/hud/sb_flag_blue_lost", '48 48 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL); break;
2632         case 3: drawpic(blueflag_pos, "gfx/hud/sb_flag_blue_carrying", '48 48 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL); break;
2633         default:
2634                 if(stat_items & IT_CTF_SHIELDED)
2635                         if(myteam == COLOR_TEAM1)
2636                                 drawpic(blueflag_pos, "gfx/hud/sb_flag_blue_shielded", '48 48 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL); break;
2637         }
2638 }
2639
2640 // Keyhunt HUD
2641 void CSQC_kh_hud(void)
2642 {
2643         float kh_keys, kh_keys_status, kh_teams_set;
2644         vector red_pos, blue_pos, yellow_pos, pink_pos, kh_size;
2645
2646         vector bottomleft;
2647         bottomleft_x = 0;
2648         bottomleft_y = vid_conheight;
2649         bottomleft_z = 0;
2650
2651         red_pos_x = 6;
2652         red_pos_y = vid_conheight - 35 - 6;
2653         red_pos_z = 0;
2654
2655         blue_pos_x = 6 + (24 * 1);
2656         blue_pos_y = vid_conheight - 35 - 6;
2657         blue_pos_z = 0;
2658
2659         yellow_pos_x = 6 + (24 * 2);
2660         yellow_pos_y = vid_conheight - 35 - 6;
2661         yellow_pos_z = 0;
2662
2663         pink_pos_x = 6 + (24 * 3);
2664         pink_pos_y = vid_conheight - 35 - 6;
2665         pink_pos_z = 0;
2666
2667         kh_keys = getstati(STAT_KH_KEYS);
2668         kh_keys_status = kh_keys / 256;
2669         kh_teams_set = cvar("_teams_available");  // set in keyhunt.qc
2670
2671         kh_size = '22 35 0';
2672
2673         if (kh_keys_status & 1)  // red
2674                 drawpic (red_pos, "gfx/hud/sb_kh_red", kh_size, '1 1 1', 0.3, DRAWFLAG_NORMAL);  // show 30% alpha key
2675         if (kh_keys & 1)
2676                 drawpic (red_pos, "gfx/hud/sb_kh_red", kh_size, '1 1 1', 1.0, DRAWFLAG_NORMAL);  // show solid key 100% alpha
2677
2678         if (kh_keys_status & 2)  // blue
2679                 drawpic (blue_pos, "gfx/hud/sb_kh_blue", kh_size, '1 1 1', 0.3, DRAWFLAG_NORMAL);
2680         if (kh_keys & 2)
2681                 drawpic (blue_pos, "gfx/hud/sb_kh_blue", kh_size, '1 1 1', 1.0, DRAWFLAG_NORMAL);
2682
2683         if (kh_teams_set & 4)  // yellow
2684         {
2685                 if (kh_keys_status & 4)
2686                         drawpic (yellow_pos, "gfx/hud/sb_kh_yellow", kh_size, '1 1 1', 0.3, DRAWFLAG_NORMAL);
2687                 if (kh_keys & 4)
2688                         drawpic (yellow_pos, "gfx/hud/sb_kh_yellow", kh_size, '1 1 1', 1.0, DRAWFLAG_NORMAL);
2689         }
2690
2691         if (kh_teams_set & 8)  // pink
2692         {
2693                 if (kh_keys_status & 8)
2694                         drawpic (pink_pos, "gfx/hud/sb_kh_pink", kh_size, '1 1 1', 0.3, DRAWFLAG_NORMAL);
2695                 if (kh_keys & 8)
2696                         drawpic (pink_pos, "gfx/hud/sb_kh_pink", kh_size, '1 1 1', 1.0, DRAWFLAG_NORMAL);
2697         }
2698 }
2699
2700 //Nexball HUD
2701 #define NBPB_SIZE '96 38 0'
2702 #define NBPB_BT 2                   //thickness
2703 #define NBPB_BRGB '1 1 1'
2704 #define NBPB_BALPH 1                //alpha
2705 #define NBPB_BFLAG DRAWFLAG_NORMAL
2706 #define NBPB_IALPH 0.4
2707 #define NBPB_IFLAG DRAWFLAG_NORMAL
2708 #define NBPB_IRGB '0.7 0.1 0'
2709
2710 void CSQC_nb_hud(void)
2711 {
2712         float stat_items, nb_pb_starttime, dt, p;
2713         vector pos;
2714
2715         stat_items = getstati(STAT_ITEMS);
2716         nb_pb_starttime = getstatf(STAT_NB_METERSTART);
2717
2718         pos_x = 4;
2719         pos_y = vid_conheight - 42;
2720         pos_z = 0;
2721
2722         //Manage the progress bar if any
2723         if (nb_pb_starttime > 0)
2724         {
2725                 vector s;
2726                 dt = mod(time - nb_pb_starttime, nb_pb_period);
2727                 // one period of positive triangle
2728                 p = 2 * dt / nb_pb_period;
2729                 if (p > 1)
2730                         p = 2 - p;
2731
2732                 s = NBPB_SIZE;
2733                 //Draw the filling
2734                 drawfill(pos, p * s_x * '1 0 0' + s_y * '0 1 0', NBPB_IRGB, NBPB_IALPH, NBPB_IFLAG);
2735
2736                 //Draw the box
2737                 s = NBPB_SIZE;
2738                 drawline(NBPB_BT, pos    , pos + '1 0 0' * s_x, NBPB_BRGB, NBPB_BALPH, NBPB_BFLAG);
2739                 drawline(NBPB_BT, pos    , pos + '0 1 0' * s_y, NBPB_BRGB, NBPB_BALPH, NBPB_BFLAG);
2740                 drawline(NBPB_BT, pos + s, pos + '1 0 0' * s_x, NBPB_BRGB, NBPB_BALPH, NBPB_BFLAG);
2741                 drawline(NBPB_BT, pos + s, pos + '0 1 0' * s_y, NBPB_BRGB, NBPB_BALPH, NBPB_BFLAG);
2742         }
2743
2744         pos_x += 12; //horizontal margin to the picture
2745         pos_y += 2; //vertical margin to the picture
2746
2747         if (stat_items & IT_KEY1)
2748                 drawpic(pos, "gfx/hud/sb_nexball_carrying", '80 34 0', '1 1 1', 1, DRAWFLAG_NORMAL);
2749 }