1 #include "scoreboard.qh"
6 #include "../common/constants.qh"
7 #include "../common/mapinfo.qh"
8 #include "../common/minigames/cl_minigames.qh"
9 #include "../common/stats.qh"
10 #include "../common/teams.qh"
11 #include "../common/util.qh"
13 float scoreboard_alpha_bg;
14 float scoreboard_alpha_fg;
15 float scoreboard_highlight;
16 float scoreboard_highlight_alpha;
17 float scoreboard_highlight_alpha_self;
18 float scoreboard_alpha_name;
19 float scoreboard_alpha_name_self;
21 void drawstringright(vector, string, vector, vector, float, float);
22 void drawstringcenter(vector, string, vector, vector, float, float);
24 const float SCOREBOARD_OFFSET = 50;
26 // wrapper to put all possible scores titles through gettext
27 string TranslateScoresLabel(string l)
31 case "bckills": return CTX(_("SCO^bckills"));
32 case "bctime": return CTX(_("SCO^bctime"));
33 case "caps": return CTX(_("SCO^caps"));
34 case "captime": return CTX(_("SCO^captime"));
35 case "deaths": return CTX(_("SCO^deaths"));
36 case "destroyed": return CTX(_("SCO^destroyed"));
37 case "drops": return CTX(_("SCO^drops"));
38 case "faults": return CTX(_("SCO^faults"));
39 case "fckills": return CTX(_("SCO^fckills"));
40 case "goals": return CTX(_("SCO^goals"));
41 case "kckills": return CTX(_("SCO^kckills"));
42 case "kdratio": return CTX(_("SCO^kdratio"));
43 case "k/d": return CTX(_("SCO^k/d"));
44 case "kd": return CTX(_("SCO^kd"));
45 case "kdr": return CTX(_("SCO^kdr"));
46 case "kills": return CTX(_("SCO^kills"));
47 case "laps": return CTX(_("SCO^laps"));
48 case "lives": return CTX(_("SCO^lives"));
49 case "losses": return CTX(_("SCO^losses"));
50 case "name": return CTX(_("SCO^name"));
51 case "sum": return CTX(_("SCO^sum"));
52 case "nick": return CTX(_("SCO^nick"));
53 case "objectives": return CTX(_("SCO^objectives"));
54 case "pickups": return CTX(_("SCO^pickups"));
55 case "ping": return CTX(_("SCO^ping"));
56 case "pl": return CTX(_("SCO^pl"));
57 case "pushes": return CTX(_("SCO^pushes"));
58 case "rank": return CTX(_("SCO^rank"));
59 case "returns": return CTX(_("SCO^returns"));
60 case "revivals": return CTX(_("SCO^revivals"));
61 case "score": return CTX(_("SCO^score"));
62 case "suicides": return CTX(_("SCO^suicides"));
63 case "takes": return CTX(_("SCO^takes"));
64 case "ticks": return CTX(_("SCO^ticks"));
73 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
74 for(i = 0; i < MAX_SCORE; ++i)
76 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
77 if(f == SFL_SORT_PRIO_PRIMARY)
79 if(f == SFL_SORT_PRIO_SECONDARY)
82 if(ps_secondary == -1)
83 ps_secondary = ps_primary;
85 for(i = 0; i < MAX_TEAMSCORE; ++i)
87 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
88 if(f == SFL_SORT_PRIO_PRIMARY)
90 if(f == SFL_SORT_PRIO_SECONDARY)
93 if(ts_secondary == -1)
94 ts_secondary = ts_primary;
99 float SetTeam(entity pl, float Team);
101 void HUD_UpdatePlayerTeams()
108 for(pl = players.sort_next; pl; pl = pl.sort_next)
111 Team = GetPlayerColor(pl.sv_entnum);
112 if(SetTeam(pl, Team))
115 HUD_UpdatePlayerPos(pl);
119 pl = players.sort_next;
124 print(strcat("PNUM: ", ftos(num), "\n"));
129 int HUD_CompareScore(float vl, float vr, int f)
131 if(f & SFL_ZERO_IS_WORST)
133 if(vl == 0 && vr != 0)
135 if(vl != 0 && vr == 0)
139 return IS_INCREASING(f);
141 return IS_DECREASING(f);
145 float HUD_ComparePlayerScores(entity left, entity right)
148 vl = GetPlayerColor(left.sv_entnum);
149 vr = GetPlayerColor(right.sv_entnum);
161 if(vl == NUM_SPECTATOR)
163 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
165 if(!left.gotscores && right.gotscores)
170 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
174 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
179 for(i = 0; i < MAX_SCORE; ++i)
181 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
186 if (left.sv_entnum < right.sv_entnum)
192 void HUD_UpdatePlayerPos(entity player)
194 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
196 SORT_SWAP(player, other);
198 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
200 SORT_SWAP(other, player);
204 float HUD_CompareTeamScores(entity left, entity right)
208 if(left.team == NUM_SPECTATOR)
210 if(right.team == NUM_SPECTATOR)
213 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
217 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
221 for(i = 0; i < MAX_SCORE; ++i)
223 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
228 if (left.team < right.team)
234 void HUD_UpdateTeamPos(entity Team)
236 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
238 SORT_SWAP(Team, other);
240 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
242 SORT_SWAP(other, Team);
248 print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
249 print(_("^3|---------------------------------------------------------------|\n"));
250 print(_("Usage:\n"));
251 print(_("^2scoreboard_columns_set default\n"));
252 print(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
253 print(_("The following field names are recognized (case insensitive):\n"));
254 print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
256 print(_("^3name^7 or ^3nick^7 Name of a player\n"));
257 print(_("^3ping^7 Ping time\n"));
258 print(_("^3pl^7 Packet loss\n"));
259 print(_("^3kills^7 Number of kills\n"));
260 print(_("^3deaths^7 Number of deaths\n"));
261 print(_("^3suicides^7 Number of suicides\n"));
262 print(_("^3frags^7 kills - suicides\n"));
263 print(_("^3kd^7 The kill-death ratio\n"));
264 print(_("^3sum^7 frags - deaths\n"));
265 print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
266 print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
267 print(_("^3captime^7 Time of fastest cap (CTF)\n"));
268 print(_("^3fckills^7 Number of flag carrier kills\n"));
269 print(_("^3returns^7 Number of flag returns\n"));
270 print(_("^3drops^7 Number of flag drops\n"));
271 print(_("^3lives^7 Number of lives (LMS)\n"));
272 print(_("^3rank^7 Player rank\n"));
273 print(_("^3pushes^7 Number of players pushed into void\n"));
274 print(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
275 print(_("^3kckills^7 Number of keys carrier kills\n"));
276 print(_("^3losses^7 Number of times a key was lost\n"));
277 print(_("^3laps^7 Number of laps finished (race/cts)\n"));
278 print(_("^3time^7 Total time raced (race/cts)\n"));
279 print(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
280 print(_("^3ticks^7 Number of ticks (DOM)\n"));
281 print(_("^3takes^7 Number of domination points taken (DOM)\n"));
282 print(_("^3bckills^7 Number of ball carrier kills\n"));
283 print(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
284 print(_("^3score^7 Total score\n\n"));
286 print(_("Before a field you can put a + or - sign, then a comma separated list\n"
287 "of game types, then a slash, to make the field show up only in these\n"
288 "or in all but these game types. You can also specify 'all' as a\n"
289 "field to show all fields available for the current game mode.\n\n"));
291 print(_("The special game type names 'teams' and 'noteams' can be used to\n"
292 "include/exclude ALL teams/noteams game modes.\n\n"));
294 print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
295 print(_("will display name, ping and pl aligned to the left, and the fields\n"
296 "right of the vertical bar aligned to the right.\n"));
297 print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
298 "other gamemodes except DM.\n"));
301 #define HUD_DefaultColumnLayout() \
303 "-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" */ \
304 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \
305 "+lms/lives +lms/rank " \
306 "+kh/caps +kh/pushes +kh/destroyed " \
307 "?+race/laps ?+race/time ?+race/fastest " \
308 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
311 void Cmd_HUD_SetFields(float argc)
315 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
320 // set up a temporary scoreboard layout
321 // no layout can be properly set up until score_info data haven't been received
322 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
324 scores_label[ps_primary] = strzone("score");
325 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
328 // TODO: re enable with gametype dependant cvars?
329 if(argc < 3) // no arguments provided
330 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
333 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
337 if(argv(2) == "default")
338 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
339 else if(argv(2) == "all")
342 s = "ping pl name |";
343 for(i = 0; i < MAX_SCORE; ++i)
346 if(i != ps_secondary)
347 if(scores_label[i] != "")
348 s = strcat(s, " ", scores_label[i]);
350 if(ps_secondary != ps_primary)
351 s = strcat(s, " ", scores_label[ps_secondary]);
352 s = strcat(s, " ", scores_label[ps_primary]);
353 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
360 hud_fontsize = HUD_GetFontsize("hud_fontsize");
362 for(i = 1; i < argc - 1; ++i)
368 if(substring(str, 0, 1) == "?")
371 str = substring(str, 1, strlen(str) - 1);
374 slash = strstrofs(str, "/", 0);
377 pattern = substring(str, 0, slash);
378 str = substring(str, slash + 1, strlen(str) - (slash + 1));
380 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
384 strunzone(hud_title[hud_num_fields]);
385 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
386 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
387 str = strtolower(str);
390 hud_field[hud_num_fields] = SP_PING;
391 } else if(str == "pl") {
392 hud_field[hud_num_fields] = SP_PL;
393 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
394 hud_field[hud_num_fields] = SP_KDRATIO;
395 } else if(str == "sum" || str == "diff" || str == "k-d") {
396 hud_field[hud_num_fields] = SP_SUM;
397 } else if(str == "name" || str == "nick") {
398 hud_field[hud_num_fields] = SP_NAME;
400 } else if(str == "|") {
401 hud_field[hud_num_fields] = SP_SEPARATOR;
404 for(j = 0; j < MAX_SCORE; ++j)
405 if(str == strtolower(scores_label[j]))
406 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
415 printf("^1Error:^7 Unknown score field: '%s'\n", str);
419 hud_field[hud_num_fields] = j;
422 if(j == ps_secondary)
426 if(hud_num_fields >= MAX_HUD_FIELDS)
430 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
432 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
434 if(ps_primary == ps_secondary)
436 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
438 if(hud_num_fields+missing < MAX_HUD_FIELDS)
442 strunzone(hud_title[hud_num_fields]);
443 for(i = hud_num_fields; i > 0; --i)
445 hud_title[i] = hud_title[i-1];
446 hud_size[i] = hud_size[i-1];
447 hud_field[i] = hud_field[i-1];
449 hud_title[0] = strzone(TranslateScoresLabel("name"));
450 hud_field[0] = SP_NAME;
452 print("fixed missing field 'name'\n");
456 strunzone(hud_title[hud_num_fields]);
457 for(i = hud_num_fields; i > 1; --i)
459 hud_title[i] = hud_title[i-1];
460 hud_size[i] = hud_size[i-1];
461 hud_field[i] = hud_field[i-1];
463 hud_title[1] = strzone("|");
464 hud_field[1] = SP_SEPARATOR;
465 hud_size[1] = stringwidth("|", false, hud_fontsize);
467 print("fixed missing field '|'\n");
470 else if(!have_separator)
472 strunzone(hud_title[hud_num_fields]);
473 hud_title[hud_num_fields] = strzone("|");
474 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
475 hud_field[hud_num_fields] = SP_SEPARATOR;
477 print("fixed missing field '|'\n");
481 strunzone(hud_title[hud_num_fields]);
482 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
483 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
484 hud_field[hud_num_fields] = ps_secondary;
486 printf("fixed missing field '%s'\n", scores_label[ps_secondary]);
490 strunzone(hud_title[hud_num_fields]);
491 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
492 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
493 hud_field[hud_num_fields] = ps_primary;
495 printf("fixed missing field '%s'\n", scores_label[ps_primary]);
499 hud_field[hud_num_fields] = SP_END;
503 vector hud_field_rgb;
504 string hud_field_icon0;
505 string hud_field_icon1;
506 string hud_field_icon2;
507 vector hud_field_icon0_rgb;
508 vector hud_field_icon1_rgb;
509 vector hud_field_icon2_rgb;
510 float hud_field_icon0_alpha;
511 float hud_field_icon1_alpha;
512 float hud_field_icon2_alpha;
513 string HUD_GetField(entity pl, int field)
515 float tmp, num, denom;
518 hud_field_rgb = '1 1 1';
519 hud_field_icon0 = "";
520 hud_field_icon1 = "";
521 hud_field_icon2 = "";
522 hud_field_icon0_rgb = '1 1 1';
523 hud_field_icon1_rgb = '1 1 1';
524 hud_field_icon2_rgb = '1 1 1';
525 hud_field_icon0_alpha = 1;
526 hud_field_icon1_alpha = 1;
527 hud_field_icon2_alpha = 1;
532 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
533 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
537 tmp = max(0, min(220, f-80)) / 220;
538 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
544 f = pl.ping_packetloss;
545 tmp = pl.ping_movementloss;
546 if(f == 0 && tmp == 0)
548 str = ftos(ceil(f * 100));
550 str = strcat(str, "~", ftos(ceil(tmp * 100)));
551 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
552 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
556 if(ready_waiting && pl.ready)
558 hud_field_icon0 = "gfx/scoreboard/player_ready";
562 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
564 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
565 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
566 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
567 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
568 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
571 return GetPlayerName(pl.sv_entnum);
574 f = pl.(scores[SP_KILLS]);
575 f -= pl.(scores[SP_SUICIDES]);
579 num = pl.(scores[SP_KILLS]);
580 denom = pl.(scores[SP_DEATHS]);
583 hud_field_rgb = '0 1 0';
584 str = sprintf("%d", num);
585 } else if(num <= 0) {
586 hud_field_rgb = '1 0 0';
587 str = sprintf("%.1f", num/denom);
589 str = sprintf("%.1f", num/denom);
593 f = pl.(scores[SP_KILLS]);
594 f -= pl.(scores[SP_DEATHS]);
597 hud_field_rgb = '0 1 0';
599 hud_field_rgb = '1 1 1';
601 hud_field_rgb = '1 0 0';
606 tmp = pl.(scores[field]);
607 f = scores_flags[field];
608 if(field == ps_primary)
609 hud_field_rgb = '1 1 0';
610 else if(field == ps_secondary)
611 hud_field_rgb = '0 1 1';
613 hud_field_rgb = '1 1 1';
614 return ScoreString(f, tmp);
619 float hud_fixscoreboardcolumnwidth_len;
620 float hud_fixscoreboardcolumnwidth_iconlen;
621 float hud_fixscoreboardcolumnwidth_marginlen;
623 string HUD_FixScoreboardColumnWidth(int i, string str)
627 field = hud_field[i];
629 hud_fixscoreboardcolumnwidth_iconlen = 0;
631 if(hud_field_icon0 != "")
633 sz = draw_getimagesize(hud_field_icon0);
635 if(hud_fixscoreboardcolumnwidth_iconlen < f)
636 hud_fixscoreboardcolumnwidth_iconlen = f;
639 if(hud_field_icon1 != "")
641 sz = draw_getimagesize(hud_field_icon1);
643 if(hud_fixscoreboardcolumnwidth_iconlen < f)
644 hud_fixscoreboardcolumnwidth_iconlen = f;
647 if(hud_field_icon2 != "")
649 sz = draw_getimagesize(hud_field_icon2);
651 if(hud_fixscoreboardcolumnwidth_iconlen < f)
652 hud_fixscoreboardcolumnwidth_iconlen = f;
655 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
657 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
658 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
660 hud_fixscoreboardcolumnwidth_marginlen = 0;
662 if(field == SP_NAME) // name gets all remaining space
666 namesize = sbwidth;// / hud_fontsize_x;
667 for(j = 0; j < hud_num_fields; ++j)
669 if (hud_field[i] != SP_SEPARATOR)
670 namesize -= hud_size[j] + hud_fontsize.x;
671 namesize += hud_fontsize.x;
672 hud_size[i] = namesize;
674 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
675 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
676 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
677 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
680 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
682 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
689 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
692 rgb = Team_ColorRGB(pl.team);
696 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
698 if((rgb == '1 1 1') && (!is_spec)) {
699 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
700 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
701 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
703 vector h_pos = pos - '1 1 0';
704 vector h_size = item_size + '2 0 0';
705 // alternated rows highlighting
707 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
708 else if((scoreboard_highlight) && (!(pl_number % 2)))
709 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
715 for(i = 0; i < hud_num_fields; ++i)
717 field = hud_field[i];
718 if(field == SP_SEPARATOR)
721 if(is_spec && field != SP_NAME && field != SP_PING) {
722 pos.x += hud_size[i] + hud_fontsize.x;
725 str = HUD_GetField(pl, field);
726 str = HUD_FixScoreboardColumnWidth(i, str);
728 pos.x += hud_size[i] + hud_fontsize.x;
730 if(field == SP_NAME) {
731 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
733 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
735 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
737 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
739 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
741 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
744 tmp.x = hud_size[i] + hud_fontsize.x;
745 if(hud_field_icon0 != "")
747 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);
749 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);
750 if(hud_field_icon1 != "")
752 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);
754 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);
755 if(hud_field_icon2 != "")
757 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);
759 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);
762 if(hud_field[i] == SP_SEPARATOR)
765 for(i = hud_num_fields-1; i > 0; --i)
767 field = hud_field[i];
768 if(field == SP_SEPARATOR)
771 if(is_spec && field != SP_NAME && field != SP_PING) {
772 pos.x -= hud_size[i] + hud_fontsize.x;
776 str = HUD_GetField(pl, field);
777 str = HUD_FixScoreboardColumnWidth(i, str);
779 if(field == SP_NAME) {
780 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
782 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
784 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
786 tmp.x = hud_fixscoreboardcolumnwidth_len;
788 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
790 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
794 if(hud_field_icon0 != "")
796 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);
798 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);
799 if(hud_field_icon1 != "")
801 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);
803 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);
804 if(hud_field_icon2 != "")
806 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);
808 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);
809 pos.x -= hud_size[i] + hud_fontsize.x;
814 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
818 * HUD_Scoreboard_MakeTable
820 * Makes a table for a team (for all playing players in DM) and fills it
823 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
825 float body_table_height;
826 vector tmp = '0 0 0', column_dim = '0 0 0';
829 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
831 pos.y += autocvar_scoreboard_border_thickness;
835 tmp.y = 1.25 * hud_fontsize.y;
839 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
841 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
844 tmp.y += autocvar_scoreboard_border_thickness;
845 tmp.y += body_table_height;
846 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
848 // separator header/table
849 pos.y += 1.25 * hud_fontsize.y;
850 tmp.y = autocvar_scoreboard_border_thickness;
851 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
853 pos.y += autocvar_scoreboard_border_thickness;
856 tmp.y = body_table_height;
858 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
860 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
862 // anyway, apply some color
863 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
865 // go back to the top to make alternated columns highlighting and to print the strings
866 pos.y -= 1.25 * hud_fontsize.y;
867 pos.y -= autocvar_scoreboard_border_thickness;
871 if (scoreboard_highlight)
873 column_dim.y = 1.25 * hud_fontsize.y; // header
874 column_dim.y += autocvar_scoreboard_border_thickness;
875 column_dim.y += body_table_height;
878 // print the strings of the columns headers and draw the columns
880 for(i = 0; i < hud_num_fields; ++i)
882 if(hud_field[i] == SP_SEPARATOR)
884 column_dim.x = hud_size[i] + hud_fontsize.x;
885 if (scoreboard_highlight)
888 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
890 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
891 pos.x += column_dim.x;
893 if(hud_field[i] == SP_SEPARATOR)
897 for(i = hud_num_fields-1; i > 0; --i)
899 if(hud_field[i] == SP_SEPARATOR)
902 pos.x -= hud_size[i];
904 if (scoreboard_highlight)
908 if (i == hud_num_fields-1)
909 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
911 column_dim.x = hud_size[i] + hud_fontsize.x;
912 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
916 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
917 tmp.x = (hud_size[i] - tmp.x);
918 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
919 pos.x -= hud_fontsize.x;
924 pos.y += 1.25 * hud_fontsize.y; // skip the header
925 pos.y += autocvar_scoreboard_border_thickness;
929 tmp.y = hud_fontsize.y * 1.25;
931 // fill the table and draw the rows
934 for(pl = players.sort_next; pl; pl = pl.sort_next)
936 if(pl.team != tm.team)
938 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
939 pos.y += 1.25 * hud_fontsize.y;
943 for(pl = players.sort_next; pl; pl = pl.sort_next)
945 if(pl.team == NUM_SPECTATOR)
947 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
948 pos.y += 1.25 * hud_fontsize.y;
953 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
954 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
959 float HUD_WouldDrawScoreboard() {
960 if (autocvar__hud_configure)
962 else if (QuickMenu_IsOpened())
964 else if (HUD_Radar_Clickable())
966 else if (scoreboard_showscores)
968 else if (intermission == 1)
970 else if (intermission == 2)
972 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
974 else if (scoreboard_showscores_force)
979 float average_accuracy;
980 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
982 WepSet weapons_stat = WepSet_GetFromStat();
983 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
984 float initial_posx = pos.x;
988 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
990 self = get_weaponinfo(i);
994 weapon_stats = weapon_accuracy[i-WEP_FIRST];
996 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1000 int weapon_cnt = WEP_COUNT - disownedcnt;
1006 if(autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor(WEP_COUNT * 0.5))
1010 int columnns = ceil(weapon_cnt / rows);
1013 float fontsize = height * 1/3;
1014 float weapon_height = height * 2/3;
1015 float weapon_width = sbwidth / columnns / rows;
1017 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1018 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1019 vector tmp = '0 0 0';
1021 tmp.y = height * rows;
1024 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1026 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1027 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1029 // column highlighting
1030 for(i = 0; i < columnns; ++i)
1033 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);
1037 for(i = 0; i < rows; ++i)
1039 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);
1042 average_accuracy = 0;
1043 int weapons_with_stats = 0;
1045 pos.x += weapon_width / 2;
1047 if(autocvar_scoreboard_accuracy_nocolors)
1050 Accuracy_LoadColors();
1052 float oldposx = pos.x;
1056 for(i = WEP_FIRST, column = 0; i <= WEP_LAST; ++i)
1058 self = get_weaponinfo(i);
1061 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1063 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1067 if(weapon_stats >= 0)
1068 weapon_alpha = scoreboard_alpha_fg;
1070 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1073 drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1075 if(weapon_stats >= 0) {
1076 weapons_with_stats += 1;
1077 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1080 s = sprintf("%d%%", weapon_stats*100);
1083 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1085 if(!autocvar_scoreboard_accuracy_nocolors)
1086 rgb = Accuracy_GetColor(weapon_stats);
1088 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1090 tmpos.x += weapon_width * rows;
1091 pos.x += weapon_width * rows;
1092 if(rows == 2 && column == columnns - 1) {
1100 if(weapons_with_stats)
1101 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1104 pos.y += 1.25 * hud_fontsize.y;
1105 pos.x = initial_posx;
1109 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1111 pos.x += hud_fontsize.x * 0.25;
1112 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1113 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1114 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1116 pos.y+= hud_fontsize.y;
1121 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1122 float stat_secrets_found, stat_secrets_total;
1123 float stat_monsters_killed, stat_monsters_total;
1127 // get monster stats
1128 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1129 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1131 // get secrets stats
1132 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1133 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1135 // get number of rows
1136 if(stat_secrets_total)
1138 if(stat_monsters_total)
1141 // if no rows, return
1145 // draw table header
1146 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1147 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1150 vector tmp = '0 0 0';
1152 tmp.y = hud_fontsize.y * rows;
1155 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1157 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1158 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1161 if(stat_monsters_total)
1163 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1164 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1168 if(stat_secrets_total)
1170 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1171 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1175 pos.y += 1.25 * hud_fontsize.y;
1180 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1183 RANKINGS_RECEIVED_CNT = 0;
1184 for (i=RANKINGS_CNT-1; i>=0; --i)
1186 ++RANKINGS_RECEIVED_CNT;
1188 if (RANKINGS_RECEIVED_CNT == 0)
1192 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1194 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1195 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1196 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1198 pos.y += hud_fontsize.y;
1199 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1200 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1201 vector tmp = '0 0 0';
1203 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1206 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1208 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1209 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1212 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1219 n = grecordholder[i];
1220 p = count_ordinal(i+1);
1221 if(grecordholder[i] == GetPlayerName(player_localnum))
1222 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1223 else if(!(i % 2) && scoreboard_highlight)
1224 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1225 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1226 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);
1227 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1228 pos.y += 1.25 * hud_fontsize.y;
1230 pos.y += autocvar_scoreboard_border_thickness;
1235 float hud_woulddrawscoreboard_prev;
1236 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1237 void HUD_DrawScoreboard()
1239 float hud_woulddrawscoreboard;
1240 hud_woulddrawscoreboard = scoreboard_active;
1241 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1242 hud_woulddrawscoreboard_change = time;
1243 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1246 if(hud_woulddrawscoreboard) {
1247 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1248 if (scoreboard_fadeinspeed)
1249 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1251 scoreboard_fade_alpha = 1;
1254 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1255 if (scoreboard_fadeoutspeed)
1256 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1258 scoreboard_fade_alpha = 0;
1261 if (!scoreboard_fade_alpha)
1264 HUD_UpdatePlayerTeams();
1266 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1267 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1268 scoreboard_highlight = autocvar_scoreboard_highlight;
1269 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1270 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1271 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1272 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1274 vector rgb, pos, tmp;
1278 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1279 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1281 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1282 ymax = (vid_conheight - ymin);
1284 sbwidth = xmax - xmin;
1286 // Initializes position
1292 vector sb_heading_fontsize;
1293 sb_heading_fontsize = hud_fontsize * 2;
1294 draw_beginBoldFont();
1295 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1298 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1300 // Draw the scoreboard
1301 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1305 vector team_score_baseoffset;
1306 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1307 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1309 if(tm.team == NUM_SPECTATOR)
1312 draw_beginBoldFont();
1313 rgb = Team_ColorRGB(tm.team);
1314 str = ftos(tm.(teamscores[ts_primary]));
1315 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1317 if(ts_primary != ts_secondary)
1319 str = ftos(tm.(teamscores[ts_secondary]));
1320 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);
1324 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1326 rgb.x = autocvar_scoreboard_color_bg_r;
1327 rgb.y = autocvar_scoreboard_color_bg_g;
1328 rgb.z = autocvar_scoreboard_color_bg_b;
1332 rgb.x = autocvar_scoreboard_color_bg_r;
1333 rgb.y = autocvar_scoreboard_color_bg_g;
1334 rgb.z = autocvar_scoreboard_color_bg_b;
1336 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1338 if(tm.team == NUM_SPECTATOR)
1341 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1345 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1346 if(race_speedaward) {
1347 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1348 pos.y += 1.25 * hud_fontsize.y;
1350 if(race_speedaward_alltimebest) {
1351 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1352 pos.y += 1.25 * hud_fontsize.y;
1354 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1356 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1358 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1360 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1365 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1367 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1374 item_size.x = sbwidth;
1375 item_size.y = hud_fontsize.y * 1.25;
1377 for(pl = players.sort_next; pl; pl = pl.sort_next)
1379 if(pl.team != NUM_SPECTATOR)
1381 pos.y += 1.25 * hud_fontsize.y;
1382 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1388 draw_beginBoldFont();
1389 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1391 pos.y += 1.25 * hud_fontsize.y;
1394 // Print info string
1396 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1397 tl = getstatf(STAT_TIMELIMIT);
1398 fl = getstatf(STAT_FRAGLIMIT);
1399 ll = getstatf(STAT_LEADLIMIT);
1400 if(gametype == MAPINFO_TYPE_LMS)
1403 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1408 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1412 str = strcat(str, _(" or"));
1415 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
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 ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
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 if(tl > 0 || fl > 0)
1431 str = strcat(str, _(" or"));
1434 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1435 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1436 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1437 TranslateScoresLabel(teamscores_label[ts_primary])));
1441 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1442 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1443 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1444 TranslateScoresLabel(scores_label[ps_primary])));
1449 pos.y += 1.2 * hud_fontsize.y;
1450 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1452 // print information about respawn status
1453 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1457 if(respawn_time < 0)
1459 // a negative number means we are awaiting respawn, time value is still the same
1460 respawn_time *= -1; // remove mark now that we checked it
1461 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1463 str = sprintf(_("^1Respawning in ^3%s^1..."),
1464 (autocvar_scoreboard_respawntime_decimals ?
1465 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1467 count_seconds(respawn_time - time)
1471 else if(time < respawn_time)
1473 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1474 (autocvar_scoreboard_respawntime_decimals ?
1475 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1477 count_seconds(respawn_time - time)
1481 else if(time >= respawn_time)
1482 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1484 pos.y += 1.2 * hud_fontsize.y;
1485 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1488 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;