1 #include "scoreboard.qh"
3 #include "autocvars.qh"
7 #include "miscfunctions.qh"
9 #include "../common/constants.qh"
10 #include "../common/counting.qh"
11 #include "../common/mapinfo.qh"
12 #include "../common/stats.qh"
13 #include "../common/teams.qh"
14 #include "../common/util.qh"
16 float scoreboard_alpha_bg;
17 float scoreboard_alpha_fg;
18 float scoreboard_highlight;
19 float scoreboard_highlight_alpha;
20 float scoreboard_highlight_alpha_self;
21 float scoreboard_alpha_name;
22 float scoreboard_alpha_name_self;
24 void drawstringright(vector, string, vector, vector, float, float);
25 void drawstringcenter(vector, string, vector, vector, float, float);
27 const float SCOREBOARD_OFFSET = 50;
29 // wrapper to put all possible scores titles through gettext
30 string TranslateScoresLabel(string l)
34 case "bckills": return CTX(_("SCO^bckills"));
35 case "bctime": return CTX(_("SCO^bctime"));
36 case "caps": return CTX(_("SCO^caps"));
37 case "captime": return CTX(_("SCO^captime"));
38 case "deaths": return CTX(_("SCO^deaths"));
39 case "destroyed": return CTX(_("SCO^destroyed"));
40 case "drops": return CTX(_("SCO^drops"));
41 case "faults": return CTX(_("SCO^faults"));
42 case "fckills": return CTX(_("SCO^fckills"));
43 case "goals": return CTX(_("SCO^goals"));
44 case "kckills": return CTX(_("SCO^kckills"));
45 case "kdratio": return CTX(_("SCO^kdratio"));
46 case "k/d": return CTX(_("SCO^k/d"));
47 case "kd": return CTX(_("SCO^kd"));
48 case "kdr": return CTX(_("SCO^kdr"));
49 case "kills": return CTX(_("SCO^kills"));
50 case "laps": return CTX(_("SCO^laps"));
51 case "lives": return CTX(_("SCO^lives"));
52 case "losses": return CTX(_("SCO^losses"));
53 case "name": return CTX(_("SCO^name"));
54 case "sum": return CTX(_("SCO^sum"));
55 case "nick": return CTX(_("SCO^nick"));
56 case "objectives": return CTX(_("SCO^objectives"));
57 case "pickups": return CTX(_("SCO^pickups"));
58 case "ping": return CTX(_("SCO^ping"));
59 case "pl": return CTX(_("SCO^pl"));
60 case "pushes": return CTX(_("SCO^pushes"));
61 case "rank": return CTX(_("SCO^rank"));
62 case "returns": return CTX(_("SCO^returns"));
63 case "revivals": return CTX(_("SCO^revivals"));
64 case "score": return CTX(_("SCO^score"));
65 case "suicides": return CTX(_("SCO^suicides"));
66 case "takes": return CTX(_("SCO^takes"));
67 case "ticks": return CTX(_("SCO^ticks"));
76 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
77 for(i = 0; i < MAX_SCORE; ++i)
79 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
80 if(f == SFL_SORT_PRIO_PRIMARY)
82 if(f == SFL_SORT_PRIO_SECONDARY)
85 if(ps_secondary == -1)
86 ps_secondary = ps_primary;
88 for(i = 0; i < MAX_TEAMSCORE; ++i)
90 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
91 if(f == SFL_SORT_PRIO_PRIMARY)
93 if(f == SFL_SORT_PRIO_SECONDARY)
96 if(ts_secondary == -1)
97 ts_secondary = ts_primary;
102 float SetTeam(entity pl, float Team);
104 void HUD_UpdatePlayerTeams()
111 for(pl = players.sort_next; pl; pl = pl.sort_next)
114 Team = GetPlayerColor(pl.sv_entnum);
115 if(SetTeam(pl, Team))
118 HUD_UpdatePlayerPos(pl);
122 pl = players.sort_next;
127 print(strcat("PNUM: ", ftos(num), "\n"));
132 int HUD_CompareScore(float vl, float vr, int f)
134 if(f & SFL_ZERO_IS_WORST)
136 if(vl == 0 && vr != 0)
138 if(vl != 0 && vr == 0)
142 return IS_INCREASING(f);
144 return IS_DECREASING(f);
148 float HUD_ComparePlayerScores(entity left, entity right)
151 vl = GetPlayerColor(left.sv_entnum);
152 vr = GetPlayerColor(right.sv_entnum);
164 if(vl == NUM_SPECTATOR)
166 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
168 if(!left.gotscores && right.gotscores)
173 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
177 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
182 for(i = 0; i < MAX_SCORE; ++i)
184 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
189 if (left.sv_entnum < right.sv_entnum)
195 void HUD_UpdatePlayerPos(entity player)
197 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
199 SORT_SWAP(player, other);
201 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
203 SORT_SWAP(other, player);
207 float HUD_CompareTeamScores(entity left, entity right)
211 if(left.team == NUM_SPECTATOR)
213 if(right.team == NUM_SPECTATOR)
216 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
220 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
224 for(i = 0; i < MAX_SCORE; ++i)
226 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
231 if (left.team < right.team)
237 void HUD_UpdateTeamPos(entity Team)
239 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
241 SORT_SWAP(Team, other);
243 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
245 SORT_SWAP(other, Team);
251 print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
252 print(_("^3|---------------------------------------------------------------|\n"));
253 print(_("Usage:\n"));
254 print(_("^2scoreboard_columns_set default\n"));
255 print(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
256 print(_("The following field names are recognized (case insensitive):\n"));
257 print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
259 print(_("^3name^7 or ^3nick^7 Name of a player\n"));
260 print(_("^3ping^7 Ping time\n"));
261 print(_("^3pl^7 Packet loss\n"));
262 print(_("^3kills^7 Number of kills\n"));
263 print(_("^3deaths^7 Number of deaths\n"));
264 print(_("^3suicides^7 Number of suicides\n"));
265 print(_("^3frags^7 kills - suicides\n"));
266 print(_("^3kd^7 The kill-death ratio\n"));
267 print(_("^3sum^7 frags - deaths\n"));
268 print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
269 print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
270 print(_("^3captime^7 Time of fastest cap (CTF)\n"));
271 print(_("^3fckills^7 Number of flag carrier kills\n"));
272 print(_("^3returns^7 Number of flag returns\n"));
273 print(_("^3drops^7 Number of flag drops\n"));
274 print(_("^3lives^7 Number of lives (LMS)\n"));
275 print(_("^3rank^7 Player rank\n"));
276 print(_("^3pushes^7 Number of players pushed into void\n"));
277 print(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
278 print(_("^3kckills^7 Number of keys carrier kills\n"));
279 print(_("^3losses^7 Number of times a key was lost\n"));
280 print(_("^3laps^7 Number of laps finished (race/cts)\n"));
281 print(_("^3time^7 Total time raced (race/cts)\n"));
282 print(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
283 print(_("^3ticks^7 Number of ticks (DOM)\n"));
284 print(_("^3takes^7 Number of domination points taken (DOM)\n"));
285 print(_("^3bckills^7 Number of ball carrier kills\n"));
286 print(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
287 print(_("^3score^7 Total score\n\n"));
289 print(_("Before a field you can put a + or - sign, then a comma separated list\n"
290 "of game types, then a slash, to make the field show up only in these\n"
291 "or in all but these game types. You can also specify 'all' as a\n"
292 "field to show all fields available for the current game mode.\n\n"));
294 print(_("The special game type names 'teams' and 'noteams' can be used to\n"
295 "include/exclude ALL teams/noteams game modes.\n\n"));
297 print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
298 print(_("will display name, ping and pl aligned to the left, and the fields\n"
299 "right of the vertical bar aligned to the right.\n"));
300 print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
301 "other gamemodes except DM.\n"));
304 #define HUD_DefaultColumnLayout() \
306 "-teams,race,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,race,ka/suicides +ft,tdm/suicides -race,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
307 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \
308 "+lms/lives +lms/rank " \
309 "+kh/caps +kh/pushes +kh/destroyed " \
310 "?+race/laps ?+race/time ?+race/fastest " \
311 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
314 void Cmd_HUD_SetFields(float argc)
318 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
323 // set up a temporary scoreboard layout
324 // no layout can be properly set up until score_info data haven't been received
325 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
327 scores_label[ps_primary] = strzone("score");
328 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
331 // TODO: re enable with gametype dependant cvars?
332 if(argc < 3) // no arguments provided
333 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
336 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
340 if(argv(2) == "default")
341 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
342 else if(argv(2) == "all")
345 s = "ping pl name |";
346 for(i = 0; i < MAX_SCORE; ++i)
349 if(i != ps_secondary)
350 if(scores_label[i] != "")
351 s = strcat(s, " ", scores_label[i]);
353 if(ps_secondary != ps_primary)
354 s = strcat(s, " ", scores_label[ps_secondary]);
355 s = strcat(s, " ", scores_label[ps_primary]);
356 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
363 hud_fontsize = HUD_GetFontsize("hud_fontsize");
365 draw_beginBoldFont();
366 for(i = 1; i < argc - 1; ++i)
372 if(substring(str, 0, 1) == "?")
375 str = substring(str, 1, strlen(str) - 1);
378 slash = strstrofs(str, "/", 0);
381 pattern = substring(str, 0, slash);
382 str = substring(str, slash + 1, strlen(str) - (slash + 1));
384 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
388 strunzone(hud_title[hud_num_fields]);
389 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
390 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
391 str = strtolower(str);
394 hud_field[hud_num_fields] = SP_PING;
395 } else if(str == "pl") {
396 hud_field[hud_num_fields] = SP_PL;
397 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
398 hud_field[hud_num_fields] = SP_KDRATIO;
399 } else if(str == "sum" || str == "diff" || str == "k-d") {
400 hud_field[hud_num_fields] = SP_SUM;
401 } else if(str == "name" || str == "nick") {
402 hud_field[hud_num_fields] = SP_NAME;
404 } else if(str == "|") {
405 hud_field[hud_num_fields] = SP_SEPARATOR;
408 for(j = 0; j < MAX_SCORE; ++j)
409 if(str == strtolower(scores_label[j]))
410 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
419 printf("^1Error:^7 Unknown score field: '%s'\n", str);
423 hud_field[hud_num_fields] = j;
426 if(j == ps_secondary)
430 if(hud_num_fields >= MAX_HUD_FIELDS)
434 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
436 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
438 if(ps_primary == ps_secondary)
440 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
442 if(hud_num_fields+missing < MAX_HUD_FIELDS)
446 strunzone(hud_title[hud_num_fields]);
447 for(i = hud_num_fields; i > 0; --i)
449 hud_title[i] = hud_title[i-1];
450 hud_size[i] = hud_size[i-1];
451 hud_field[i] = hud_field[i-1];
453 hud_title[0] = strzone(TranslateScoresLabel("name"));
454 hud_field[0] = SP_NAME;
456 print("fixed missing field 'name'\n");
460 strunzone(hud_title[hud_num_fields]);
461 for(i = hud_num_fields; i > 1; --i)
463 hud_title[i] = hud_title[i-1];
464 hud_size[i] = hud_size[i-1];
465 hud_field[i] = hud_field[i-1];
467 hud_title[1] = strzone("|");
468 hud_field[1] = SP_SEPARATOR;
469 hud_size[1] = stringwidth("|", false, hud_fontsize);
471 print("fixed missing field '|'\n");
474 else if(!have_separator)
476 strunzone(hud_title[hud_num_fields]);
477 hud_title[hud_num_fields] = strzone("|");
478 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
479 hud_field[hud_num_fields] = SP_SEPARATOR;
481 print("fixed missing field '|'\n");
485 strunzone(hud_title[hud_num_fields]);
486 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
487 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
488 hud_field[hud_num_fields] = ps_secondary;
490 printf("fixed missing field '%s'\n", scores_label[ps_secondary]);
494 strunzone(hud_title[hud_num_fields]);
495 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
496 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
497 hud_field[hud_num_fields] = ps_primary;
499 printf("fixed missing field '%s'\n", scores_label[ps_primary]);
503 hud_field[hud_num_fields] = SP_END;
508 vector hud_field_rgb;
509 string hud_field_icon0;
510 string hud_field_icon1;
511 string hud_field_icon2;
512 vector hud_field_icon0_rgb;
513 vector hud_field_icon1_rgb;
514 vector hud_field_icon2_rgb;
515 float hud_field_icon0_alpha;
516 float hud_field_icon1_alpha;
517 float hud_field_icon2_alpha;
518 string HUD_GetField(entity pl, int field)
520 float tmp, num, denom;
523 hud_field_rgb = '1 1 1';
524 hud_field_icon0 = "";
525 hud_field_icon1 = "";
526 hud_field_icon2 = "";
527 hud_field_icon0_rgb = '1 1 1';
528 hud_field_icon1_rgb = '1 1 1';
529 hud_field_icon2_rgb = '1 1 1';
530 hud_field_icon0_alpha = 1;
531 hud_field_icon1_alpha = 1;
532 hud_field_icon2_alpha = 1;
537 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
538 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
542 tmp = max(0, min(220, f-80)) / 220;
543 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
549 f = pl.ping_packetloss;
550 tmp = pl.ping_movementloss;
551 if(f == 0 && tmp == 0)
553 str = ftos(ceil(f * 100));
555 str = strcat(str, "~", ftos(ceil(tmp * 100)));
556 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
557 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
561 if(ready_waiting && pl.ready)
563 hud_field_icon0 = "gfx/scoreboard/player_ready";
567 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
569 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
570 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
571 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
572 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
573 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
576 return GetPlayerName(pl.sv_entnum);
579 f = pl.(scores[SP_KILLS]);
580 f -= pl.(scores[SP_SUICIDES]);
584 num = pl.(scores[SP_KILLS]);
585 denom = pl.(scores[SP_DEATHS]);
588 hud_field_rgb = '0 1 0';
589 str = sprintf("%d", num);
590 } else if(num <= 0) {
591 hud_field_rgb = '1 0 0';
592 str = sprintf("%.1f", num/denom);
594 str = sprintf("%.1f", num/denom);
598 f = pl.(scores[SP_KILLS]);
599 f -= pl.(scores[SP_DEATHS]);
602 hud_field_rgb = '0 1 0';
604 hud_field_rgb = '1 1 1';
606 hud_field_rgb = '1 0 0';
611 tmp = pl.(scores[field]);
612 f = scores_flags[field];
613 if(field == ps_primary)
614 hud_field_rgb = '1 1 0';
615 else if(field == ps_secondary)
616 hud_field_rgb = '0 1 1';
618 hud_field_rgb = '1 1 1';
619 return ScoreString(f, tmp);
624 float hud_fixscoreboardcolumnwidth_len;
625 float hud_fixscoreboardcolumnwidth_iconlen;
626 float hud_fixscoreboardcolumnwidth_marginlen;
628 string HUD_FixScoreboardColumnWidth(int i, string str)
632 field = hud_field[i];
634 hud_fixscoreboardcolumnwidth_iconlen = 0;
636 if(hud_field_icon0 != "")
638 sz = draw_getimagesize(hud_field_icon0);
640 if(hud_fixscoreboardcolumnwidth_iconlen < f)
641 hud_fixscoreboardcolumnwidth_iconlen = f;
644 if(hud_field_icon1 != "")
646 sz = draw_getimagesize(hud_field_icon1);
648 if(hud_fixscoreboardcolumnwidth_iconlen < f)
649 hud_fixscoreboardcolumnwidth_iconlen = f;
652 if(hud_field_icon2 != "")
654 sz = draw_getimagesize(hud_field_icon2);
656 if(hud_fixscoreboardcolumnwidth_iconlen < f)
657 hud_fixscoreboardcolumnwidth_iconlen = f;
660 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
662 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
663 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
665 hud_fixscoreboardcolumnwidth_marginlen = 0;
667 if(field == SP_NAME) // name gets all remaining space
671 namesize = sbwidth;// / hud_fontsize_x;
672 for(j = 0; j < hud_num_fields; ++j)
674 if (hud_field[i] != SP_SEPARATOR)
675 namesize -= hud_size[j] + hud_fontsize.x;
676 namesize += hud_fontsize.x;
677 hud_size[i] = namesize;
679 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
680 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
681 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
682 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
685 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
687 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
694 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
697 rgb = Team_ColorRGB(pl.team);
701 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
703 if((rgb == '1 1 1') && (!is_spec)) {
704 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
705 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
706 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
708 vector h_pos = pos - '1 1 0';
709 vector h_size = item_size + '2 0 0';
710 // alternated rows highlighting
712 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
713 else if((scoreboard_highlight) && (!(pl_number % 2)))
714 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
720 for(i = 0; i < hud_num_fields; ++i)
722 field = hud_field[i];
723 if(field == SP_SEPARATOR)
726 if(is_spec && field != SP_NAME && field != SP_PING) {
727 pos.x += hud_size[i] + hud_fontsize.x;
730 str = HUD_GetField(pl, field);
731 str = HUD_FixScoreboardColumnWidth(i, str);
733 pos.x += hud_size[i] + hud_fontsize.x;
735 if(field == SP_NAME) {
736 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
738 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
740 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
742 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
744 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
746 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
749 tmp.x = hud_size[i] + hud_fontsize.x;
750 if(hud_field_icon0 != "")
752 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
754 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
755 if(hud_field_icon1 != "")
757 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
759 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
760 if(hud_field_icon2 != "")
762 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
764 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
767 if(hud_field[i] == SP_SEPARATOR)
770 for(i = hud_num_fields-1; i > 0; --i)
772 field = hud_field[i];
773 if(field == SP_SEPARATOR)
776 if(is_spec && field != SP_NAME && field != SP_PING) {
777 pos.x -= hud_size[i] + hud_fontsize.x;
781 str = HUD_GetField(pl, field);
782 str = HUD_FixScoreboardColumnWidth(i, str);
784 if(field == SP_NAME) {
785 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
787 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
789 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
791 tmp.x = hud_fixscoreboardcolumnwidth_len;
793 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
795 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
799 if(hud_field_icon0 != "")
801 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
803 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
804 if(hud_field_icon1 != "")
806 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
808 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
809 if(hud_field_icon2 != "")
811 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
813 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
814 pos.x -= hud_size[i] + hud_fontsize.x;
819 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
823 * HUD_Scoreboard_MakeTable
825 * Makes a table for a team (for all playing players in DM) and fills it
828 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
830 float body_table_height;
831 vector tmp = '0 0 0', column_dim = '0 0 0';
834 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
836 pos.y += autocvar_scoreboard_border_thickness;
840 tmp.y = 1.25 * hud_fontsize.y;
844 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
846 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
849 tmp.y += autocvar_scoreboard_border_thickness;
850 tmp.y += body_table_height;
851 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
853 // separator header/table
854 pos.y += 1.25 * hud_fontsize.y;
855 tmp.y = autocvar_scoreboard_border_thickness;
856 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
858 pos.y += autocvar_scoreboard_border_thickness;
861 tmp.y = body_table_height;
863 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
865 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
867 // anyway, apply some color
868 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
870 // go back to the top to make alternated columns highlighting and to print the strings
871 pos.y -= 1.25 * hud_fontsize.y;
872 pos.y -= autocvar_scoreboard_border_thickness;
876 if (scoreboard_highlight)
878 column_dim.y = 1.25 * hud_fontsize.y; // header
879 column_dim.y += autocvar_scoreboard_border_thickness;
880 column_dim.y += body_table_height;
883 // print the strings of the columns headers and draw the columns
884 draw_beginBoldFont();
886 for(i = 0; i < hud_num_fields; ++i)
888 if(hud_field[i] == SP_SEPARATOR)
890 column_dim.x = hud_size[i] + hud_fontsize.x;
891 if (scoreboard_highlight)
894 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
896 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
897 pos.x += column_dim.x;
899 if(hud_field[i] == SP_SEPARATOR)
903 for(i = hud_num_fields-1; i > 0; --i)
905 if(hud_field[i] == SP_SEPARATOR)
908 pos.x -= hud_size[i];
910 if (scoreboard_highlight)
914 if (i == hud_num_fields-1)
915 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
917 column_dim.x = hud_size[i] + hud_fontsize.x;
918 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
922 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
923 tmp.x = (hud_size[i] - tmp.x);
924 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
925 pos.x -= hud_fontsize.x;
931 pos.y += 1.25 * hud_fontsize.y; // skip the header
932 pos.y += autocvar_scoreboard_border_thickness;
936 tmp.y = hud_fontsize.y * 1.25;
938 // fill the table and draw the rows
941 for(pl = players.sort_next; pl; pl = pl.sort_next)
943 if(pl.team != tm.team)
945 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
946 pos.y += 1.25 * hud_fontsize.y;
950 for(pl = players.sort_next; pl; pl = pl.sort_next)
952 if(pl.team == NUM_SPECTATOR)
954 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
955 pos.y += 1.25 * hud_fontsize.y;
960 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
961 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
966 float HUD_WouldDrawScoreboard() {
967 if (autocvar__hud_configure)
969 else if (scoreboard_showscores)
971 else if (intermission == 1)
973 else if (intermission == 2)
975 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
977 else if (scoreboard_showscores_force)
982 float average_accuracy;
983 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
986 int weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
988 if(autocvar_scoreboard_accuracy_doublerows)
993 float fontsize = height * 1/3;
994 float weapon_height = height * 2/3;
995 float weapon_width = sbwidth / weapon_cnt;
996 float g_instagib = 0;
998 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
999 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1000 vector tmp = '0 0 0';
1002 tmp.y = height * rows;
1005 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1007 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1008 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1010 // column highlighting
1011 for(i = 0; i < weapon_cnt/rows; ++i)
1014 drawfill(pos + '1 0 0' * weapon_width * rows * i, '0 1 0' * height * rows + '1 0 0' * weapon_width * rows, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
1018 for(i = 0; i < rows; ++i)
1020 drawfill(pos + '0 1 0' * weapon_height + '0 1 0' * height * i, '1 0 0' * sbwidth + '0 1 0' * fontsize, '1 1 1', scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1023 average_accuracy = 0;
1024 float weapons_with_stats;
1025 weapons_with_stats = 0;
1027 pos.x += weapon_width / 2;
1029 if(switchweapon == WEP_VAPORIZER)
1030 g_instagib = 1; // TODO: real detection for instagib?
1033 if(autocvar_scoreboard_accuracy_nocolors)
1036 Accuracy_LoadColors();
1038 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1040 self = get_weaponinfo(i);
1043 if ((i == WEP_VORTEX && g_instagib) || i == WEP_PORTO || (i == WEP_VAPORIZER && !g_instagib) || i == WEP_TUBA) // skip port-o-launch, vortex || vaporizer and tuba
1045 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1048 if(weapon_stats >= 0)
1049 weapon_alpha = scoreboard_alpha_fg;
1051 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1054 drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1056 if(weapon_stats >= 0) {
1057 weapons_with_stats += 1;
1058 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1061 s = sprintf("%d%%", weapon_stats*100);
1064 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1066 if(!autocvar_scoreboard_accuracy_nocolors)
1067 rgb = Accuracy_GetColor(weapon_stats);
1069 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1071 pos.x += weapon_width * rows;
1072 if(rows == 2 && i == 6) {
1078 if(weapons_with_stats)
1079 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1082 pos.x -= weapon_width / 2;
1086 pos.y += 1.25 * hud_fontsize.y;
1090 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1092 pos.x += hud_fontsize.x * 0.25;
1093 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1094 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1095 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1097 pos.y+= hud_fontsize.y;
1102 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1103 float stat_secrets_found, stat_secrets_total;
1104 float stat_monsters_killed, stat_monsters_total;
1108 // get monster stats
1109 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1110 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1112 // get secrets stats
1113 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1114 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1116 // get number of rows
1117 if(stat_secrets_total)
1119 if(stat_monsters_total)
1122 // if no rows, return
1126 // draw table header
1127 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1128 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1131 vector tmp = '0 0 0';
1133 tmp.y = hud_fontsize.y * rows;
1136 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1138 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1139 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1142 if(stat_monsters_total)
1144 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1145 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1149 if(stat_secrets_total)
1151 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1152 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1156 pos.y += 1.25 * hud_fontsize.y;
1161 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1164 RANKINGS_RECEIVED_CNT = 0;
1165 for (i=RANKINGS_CNT-1; i>=0; --i)
1167 ++RANKINGS_RECEIVED_CNT;
1169 if (RANKINGS_RECEIVED_CNT == 0)
1173 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1175 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1176 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1177 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1179 pos.y += hud_fontsize.y;
1180 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1181 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1182 vector tmp = '0 0 0';
1184 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1187 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1189 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1190 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1193 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1200 n = grecordholder[i];
1201 p = count_ordinal(i+1);
1202 if(grecordholder[i] == GetPlayerName(player_localnum))
1203 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1204 else if(!(i % 2) && scoreboard_highlight)
1205 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1206 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1207 drawstring(pos + '3 0 0' * hud_fontsize.y, TIME_ENCODED_TOSTRING(t), '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1208 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1209 pos.y += 1.25 * hud_fontsize.y;
1211 pos.y += autocvar_scoreboard_border_thickness;
1216 float hud_woulddrawscoreboard_prev;
1217 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1218 void HUD_DrawScoreboard()
1220 float hud_woulddrawscoreboard;
1221 hud_woulddrawscoreboard = scoreboard_active;
1222 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1223 hud_woulddrawscoreboard_change = time;
1224 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1227 if(hud_woulddrawscoreboard) {
1228 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1229 if (scoreboard_fadeinspeed)
1230 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1232 scoreboard_fade_alpha = 1;
1235 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1236 if (scoreboard_fadeoutspeed)
1237 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1239 scoreboard_fade_alpha = 0;
1242 if (!scoreboard_fade_alpha)
1245 HUD_UpdatePlayerTeams();
1247 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1248 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1249 scoreboard_highlight = autocvar_scoreboard_highlight;
1250 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1251 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1252 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1253 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1255 vector rgb, pos, tmp;
1259 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1260 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1262 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1263 ymax = (vid_conheight - ymin);
1265 sbwidth = xmax - xmin;
1267 // Initializes position
1273 vector sb_heading_fontsize;
1274 sb_heading_fontsize = hud_fontsize * 2;
1275 draw_beginBoldFont();
1276 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1279 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1281 // Draw the scoreboard
1282 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1286 vector team_score_baseoffset;
1287 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1288 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1290 if(tm.team == NUM_SPECTATOR)
1293 draw_beginBoldFont();
1294 rgb = Team_ColorRGB(tm.team);
1295 str = ftos(tm.(teamscores[ts_primary]));
1296 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1298 if(ts_primary != ts_secondary)
1300 str = ftos(tm.(teamscores[ts_secondary]));
1301 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize) + eY * hud_fontsize.y * 1.5, str, hud_fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1305 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1307 rgb.x = autocvar_scoreboard_color_bg_r;
1308 rgb.y = autocvar_scoreboard_color_bg_g;
1309 rgb.z = autocvar_scoreboard_color_bg_b;
1313 rgb.x = autocvar_scoreboard_color_bg_r;
1314 rgb.y = autocvar_scoreboard_color_bg_g;
1315 rgb.z = autocvar_scoreboard_color_bg_b;
1317 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1319 if(tm.team == NUM_SPECTATOR)
1322 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1326 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1327 if(race_speedaward) {
1328 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1329 pos.y += 1.25 * hud_fontsize.y;
1331 if(race_speedaward_alltimebest) {
1332 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1333 pos.y += 1.25 * hud_fontsize.y;
1335 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1337 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1339 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1341 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1346 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1348 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1355 item_size.x = sbwidth;
1356 item_size.y = hud_fontsize.y * 1.25;
1358 for(pl = players.sort_next; pl; pl = pl.sort_next)
1360 if(pl.team != NUM_SPECTATOR)
1362 pos.y += 1.25 * hud_fontsize.y;
1363 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1369 draw_beginBoldFont();
1370 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1372 pos.y += 1.25 * hud_fontsize.y;
1375 // Print info string
1377 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1378 tl = getstatf(STAT_TIMELIMIT);
1379 fl = getstatf(STAT_FRAGLIMIT);
1380 ll = getstatf(STAT_LEADLIMIT);
1381 if(gametype == MAPINFO_TYPE_LMS)
1384 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1389 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1393 str = strcat(str, _(" or"));
1396 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1397 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1398 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1399 TranslateScoresLabel(teamscores_label[ts_primary])));
1403 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1404 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1405 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1406 TranslateScoresLabel(scores_label[ps_primary])));
1411 if(tl > 0 || fl > 0)
1412 str = strcat(str, _(" or"));
1415 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1416 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1417 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1418 TranslateScoresLabel(teamscores_label[ts_primary])));
1422 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1423 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1424 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1425 TranslateScoresLabel(scores_label[ps_primary])));
1430 pos.y += 1.2 * hud_fontsize.y;
1431 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1433 // print information about respawn status
1434 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1438 if(respawn_time < 0)
1440 // a negative number means we are awaiting respawn, time value is still the same
1441 respawn_time *= -1; // remove mark now that we checked it
1442 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1444 str = sprintf(_("^1Respawning in ^3%s^1..."),
1445 (autocvar_scoreboard_respawntime_decimals ?
1446 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1448 count_seconds(respawn_time - time)
1452 else if(time < respawn_time)
1454 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1455 (autocvar_scoreboard_respawntime_decimals ?
1456 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1458 count_seconds(respawn_time - time)
1462 else if(time >= respawn_time)
1463 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1465 pos.y += 1.2 * hud_fontsize.y;
1466 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1469 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;