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