1 #include "scoreboard.qh"
7 #include "../common/constants.qh"
8 #include "../common/counting.qh"
9 #include "../common/mapinfo.qh"
10 #include "../common/minigames/cl_minigames.qh"
11 #include "../common/stats.qh"
12 #include "../common/teams.qh"
13 #include "../common/util.qh"
15 float scoreboard_alpha_bg;
16 float scoreboard_alpha_fg;
17 float scoreboard_highlight;
18 float scoreboard_highlight_alpha;
19 float scoreboard_highlight_alpha_self;
20 float scoreboard_alpha_name;
21 float scoreboard_alpha_name_self;
23 void drawstringright(vector, string, vector, vector, float, float);
24 void drawstringcenter(vector, string, vector, vector, float, float);
26 const float SCOREBOARD_OFFSET = 50;
28 // wrapper to put all possible scores titles through gettext
29 string TranslateScoresLabel(string l)
33 case "bckills": return CTX(_("SCO^bckills"));
34 case "bctime": return CTX(_("SCO^bctime"));
35 case "caps": return CTX(_("SCO^caps"));
36 case "captime": return CTX(_("SCO^captime"));
37 case "deaths": return CTX(_("SCO^deaths"));
38 case "destroyed": return CTX(_("SCO^destroyed"));
39 case "drops": return CTX(_("SCO^drops"));
40 case "faults": return CTX(_("SCO^faults"));
41 case "fckills": return CTX(_("SCO^fckills"));
42 case "goals": return CTX(_("SCO^goals"));
43 case "kckills": return CTX(_("SCO^kckills"));
44 case "kdratio": return CTX(_("SCO^kdratio"));
45 case "k/d": return CTX(_("SCO^k/d"));
46 case "kd": return CTX(_("SCO^kd"));
47 case "kdr": return CTX(_("SCO^kdr"));
48 case "kills": return CTX(_("SCO^kills"));
49 case "laps": return CTX(_("SCO^laps"));
50 case "lives": return CTX(_("SCO^lives"));
51 case "losses": return CTX(_("SCO^losses"));
52 case "name": return CTX(_("SCO^name"));
53 case "sum": return CTX(_("SCO^sum"));
54 case "nick": return CTX(_("SCO^nick"));
55 case "objectives": return CTX(_("SCO^objectives"));
56 case "pickups": return CTX(_("SCO^pickups"));
57 case "ping": return CTX(_("SCO^ping"));
58 case "pl": return CTX(_("SCO^pl"));
59 case "pushes": return CTX(_("SCO^pushes"));
60 case "rank": return CTX(_("SCO^rank"));
61 case "returns": return CTX(_("SCO^returns"));
62 case "revivals": return CTX(_("SCO^revivals"));
63 case "score": return CTX(_("SCO^score"));
64 case "suicides": return CTX(_("SCO^suicides"));
65 case "takes": return CTX(_("SCO^takes"));
66 case "ticks": return CTX(_("SCO^ticks"));
75 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
76 for(i = 0; i < MAX_SCORE; ++i)
78 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
79 if(f == SFL_SORT_PRIO_PRIMARY)
81 if(f == SFL_SORT_PRIO_SECONDARY)
84 if(ps_secondary == -1)
85 ps_secondary = ps_primary;
87 for(i = 0; i < MAX_TEAMSCORE; ++i)
89 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
90 if(f == SFL_SORT_PRIO_PRIMARY)
92 if(f == SFL_SORT_PRIO_SECONDARY)
95 if(ts_secondary == -1)
96 ts_secondary = ts_primary;
101 float SetTeam(entity pl, float Team);
103 void HUD_UpdatePlayerTeams()
110 for(pl = players.sort_next; pl; pl = pl.sort_next)
113 Team = GetPlayerColor(pl.sv_entnum);
114 if(SetTeam(pl, Team))
117 HUD_UpdatePlayerPos(pl);
121 pl = players.sort_next;
126 print(strcat("PNUM: ", ftos(num), "\n"));
131 int HUD_CompareScore(float vl, float vr, int f)
133 if(f & SFL_ZERO_IS_WORST)
135 if(vl == 0 && vr != 0)
137 if(vl != 0 && vr == 0)
141 return IS_INCREASING(f);
143 return IS_DECREASING(f);
147 float HUD_ComparePlayerScores(entity left, entity right)
150 vl = GetPlayerColor(left.sv_entnum);
151 vr = GetPlayerColor(right.sv_entnum);
163 if(vl == NUM_SPECTATOR)
165 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
167 if(!left.gotscores && right.gotscores)
172 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
176 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
181 for(i = 0; i < MAX_SCORE; ++i)
183 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
188 if (left.sv_entnum < right.sv_entnum)
194 void HUD_UpdatePlayerPos(entity player)
196 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
198 SORT_SWAP(player, other);
200 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
202 SORT_SWAP(other, player);
206 float HUD_CompareTeamScores(entity left, entity right)
210 if(left.team == NUM_SPECTATOR)
212 if(right.team == NUM_SPECTATOR)
215 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
219 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
223 for(i = 0; i < MAX_SCORE; ++i)
225 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
230 if (left.team < right.team)
236 void HUD_UpdateTeamPos(entity Team)
238 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
240 SORT_SWAP(Team, other);
242 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
244 SORT_SWAP(other, Team);
250 print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
251 print(_("^3|---------------------------------------------------------------|\n"));
252 print(_("Usage:\n"));
253 print(_("^2scoreboard_columns_set default\n"));
254 print(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
255 print(_("The following field names are recognized (case insensitive):\n"));
256 print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
258 print(_("^3name^7 or ^3nick^7 Name of a player\n"));
259 print(_("^3ping^7 Ping time\n"));
260 print(_("^3pl^7 Packet loss\n"));
261 print(_("^3kills^7 Number of kills\n"));
262 print(_("^3deaths^7 Number of deaths\n"));
263 print(_("^3suicides^7 Number of suicides\n"));
264 print(_("^3frags^7 kills - suicides\n"));
265 print(_("^3kd^7 The kill-death ratio\n"));
266 print(_("^3sum^7 frags - deaths\n"));
267 print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
268 print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
269 print(_("^3captime^7 Time of fastest cap (CTF)\n"));
270 print(_("^3fckills^7 Number of flag carrier kills\n"));
271 print(_("^3returns^7 Number of flag returns\n"));
272 print(_("^3drops^7 Number of flag drops\n"));
273 print(_("^3lives^7 Number of lives (LMS)\n"));
274 print(_("^3rank^7 Player rank\n"));
275 print(_("^3pushes^7 Number of players pushed into void\n"));
276 print(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
277 print(_("^3kckills^7 Number of keys carrier kills\n"));
278 print(_("^3losses^7 Number of times a key was lost\n"));
279 print(_("^3laps^7 Number of laps finished (race/cts)\n"));
280 print(_("^3time^7 Total time raced (race/cts)\n"));
281 print(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
282 print(_("^3ticks^7 Number of ticks (DOM)\n"));
283 print(_("^3takes^7 Number of domination points taken (DOM)\n"));
284 print(_("^3bckills^7 Number of ball carrier kills\n"));
285 print(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
286 print(_("^3score^7 Total score\n\n"));
288 print(_("Before a field you can put a + or - sign, then a comma separated list\n"
289 "of game types, then a slash, to make the field show up only in these\n"
290 "or in all but these game types. You can also specify 'all' as a\n"
291 "field to show all fields available for the current game mode.\n\n"));
293 print(_("The special game type names 'teams' and 'noteams' can be used to\n"
294 "include/exclude ALL teams/noteams game modes.\n\n"));
296 print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
297 print(_("will display name, ping and pl aligned to the left, and the fields\n"
298 "right of the vertical bar aligned to the right.\n"));
299 print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
300 "other gamemodes except DM.\n"));
303 #define HUD_DefaultColumnLayout() \
305 "-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" */ \
306 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \
307 "+lms/lives +lms/rank " \
308 "+kh/caps +kh/pushes +kh/destroyed " \
309 "?+race/laps ?+race/time ?+race/fastest " \
310 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
313 void Cmd_HUD_SetFields(float argc)
317 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
322 // set up a temporary scoreboard layout
323 // no layout can be properly set up until score_info data haven't been received
324 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
326 scores_label[ps_primary] = strzone("score");
327 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
330 // TODO: re enable with gametype dependant cvars?
331 if(argc < 3) // no arguments provided
332 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
335 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
339 if(argv(2) == "default")
340 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
341 else if(argv(2) == "all")
344 s = "ping pl name |";
345 for(i = 0; i < MAX_SCORE; ++i)
348 if(i != ps_secondary)
349 if(scores_label[i] != "")
350 s = strcat(s, " ", scores_label[i]);
352 if(ps_secondary != ps_primary)
353 s = strcat(s, " ", scores_label[ps_secondary]);
354 s = strcat(s, " ", scores_label[ps_primary]);
355 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
362 hud_fontsize = HUD_GetFontsize("hud_fontsize");
364 for(i = 1; i < argc - 1; ++i)
370 if(substring(str, 0, 1) == "?")
373 str = substring(str, 1, strlen(str) - 1);
376 slash = strstrofs(str, "/", 0);
379 pattern = substring(str, 0, slash);
380 str = substring(str, slash + 1, strlen(str) - (slash + 1));
382 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
386 strunzone(hud_title[hud_num_fields]);
387 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
388 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
389 str = strtolower(str);
392 hud_field[hud_num_fields] = SP_PING;
393 } else if(str == "pl") {
394 hud_field[hud_num_fields] = SP_PL;
395 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
396 hud_field[hud_num_fields] = SP_KDRATIO;
397 } else if(str == "sum" || str == "diff" || str == "k-d") {
398 hud_field[hud_num_fields] = SP_SUM;
399 } else if(str == "name" || str == "nick") {
400 hud_field[hud_num_fields] = SP_NAME;
402 } else if(str == "|") {
403 hud_field[hud_num_fields] = SP_SEPARATOR;
406 for(j = 0; j < MAX_SCORE; ++j)
407 if(str == strtolower(scores_label[j]))
408 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
417 printf("^1Error:^7 Unknown score field: '%s'\n", str);
421 hud_field[hud_num_fields] = j;
424 if(j == ps_secondary)
428 if(hud_num_fields >= MAX_HUD_FIELDS)
432 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
434 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
436 if(ps_primary == ps_secondary)
438 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
440 if(hud_num_fields+missing < MAX_HUD_FIELDS)
444 strunzone(hud_title[hud_num_fields]);
445 for(i = hud_num_fields; i > 0; --i)
447 hud_title[i] = hud_title[i-1];
448 hud_size[i] = hud_size[i-1];
449 hud_field[i] = hud_field[i-1];
451 hud_title[0] = strzone(TranslateScoresLabel("name"));
452 hud_field[0] = SP_NAME;
454 print("fixed missing field 'name'\n");
458 strunzone(hud_title[hud_num_fields]);
459 for(i = hud_num_fields; i > 1; --i)
461 hud_title[i] = hud_title[i-1];
462 hud_size[i] = hud_size[i-1];
463 hud_field[i] = hud_field[i-1];
465 hud_title[1] = strzone("|");
466 hud_field[1] = SP_SEPARATOR;
467 hud_size[1] = stringwidth("|", false, hud_fontsize);
469 print("fixed missing field '|'\n");
472 else if(!have_separator)
474 strunzone(hud_title[hud_num_fields]);
475 hud_title[hud_num_fields] = strzone("|");
476 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
477 hud_field[hud_num_fields] = SP_SEPARATOR;
479 print("fixed missing field '|'\n");
483 strunzone(hud_title[hud_num_fields]);
484 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
485 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
486 hud_field[hud_num_fields] = ps_secondary;
488 printf("fixed missing field '%s'\n", scores_label[ps_secondary]);
492 strunzone(hud_title[hud_num_fields]);
493 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
494 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
495 hud_field[hud_num_fields] = ps_primary;
497 printf("fixed missing field '%s'\n", scores_label[ps_primary]);
501 hud_field[hud_num_fields] = SP_END;
505 vector hud_field_rgb;
506 string hud_field_icon0;
507 string hud_field_icon1;
508 string hud_field_icon2;
509 vector hud_field_icon0_rgb;
510 vector hud_field_icon1_rgb;
511 vector hud_field_icon2_rgb;
512 float hud_field_icon0_alpha;
513 float hud_field_icon1_alpha;
514 float hud_field_icon2_alpha;
515 string HUD_GetField(entity pl, int field)
517 float tmp, num, denom;
520 hud_field_rgb = '1 1 1';
521 hud_field_icon0 = "";
522 hud_field_icon1 = "";
523 hud_field_icon2 = "";
524 hud_field_icon0_rgb = '1 1 1';
525 hud_field_icon1_rgb = '1 1 1';
526 hud_field_icon2_rgb = '1 1 1';
527 hud_field_icon0_alpha = 1;
528 hud_field_icon1_alpha = 1;
529 hud_field_icon2_alpha = 1;
534 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
535 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
539 tmp = max(0, min(220, f-80)) / 220;
540 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
546 f = pl.ping_packetloss;
547 tmp = pl.ping_movementloss;
548 if(f == 0 && tmp == 0)
550 str = ftos(ceil(f * 100));
552 str = strcat(str, "~", ftos(ceil(tmp * 100)));
553 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
554 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
558 if(ready_waiting && pl.ready)
560 hud_field_icon0 = "gfx/scoreboard/player_ready";
564 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
566 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
567 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
568 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
569 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
570 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
573 return GetPlayerName(pl.sv_entnum);
576 f = pl.(scores[SP_KILLS]);
577 f -= pl.(scores[SP_SUICIDES]);
581 num = pl.(scores[SP_KILLS]);
582 denom = pl.(scores[SP_DEATHS]);
585 hud_field_rgb = '0 1 0';
586 str = sprintf("%d", num);
587 } else if(num <= 0) {
588 hud_field_rgb = '1 0 0';
589 str = sprintf("%.1f", num/denom);
591 str = sprintf("%.1f", num/denom);
595 f = pl.(scores[SP_KILLS]);
596 f -= pl.(scores[SP_DEATHS]);
599 hud_field_rgb = '0 1 0';
601 hud_field_rgb = '1 1 1';
603 hud_field_rgb = '1 0 0';
608 tmp = pl.(scores[field]);
609 f = scores_flags[field];
610 if(field == ps_primary)
611 hud_field_rgb = '1 1 0';
612 else if(field == ps_secondary)
613 hud_field_rgb = '0 1 1';
615 hud_field_rgb = '1 1 1';
616 return ScoreString(f, tmp);
621 float hud_fixscoreboardcolumnwidth_len;
622 float hud_fixscoreboardcolumnwidth_iconlen;
623 float hud_fixscoreboardcolumnwidth_marginlen;
625 string HUD_FixScoreboardColumnWidth(int i, string str)
629 field = hud_field[i];
631 hud_fixscoreboardcolumnwidth_iconlen = 0;
633 if(hud_field_icon0 != "")
635 sz = draw_getimagesize(hud_field_icon0);
637 if(hud_fixscoreboardcolumnwidth_iconlen < f)
638 hud_fixscoreboardcolumnwidth_iconlen = f;
641 if(hud_field_icon1 != "")
643 sz = draw_getimagesize(hud_field_icon1);
645 if(hud_fixscoreboardcolumnwidth_iconlen < f)
646 hud_fixscoreboardcolumnwidth_iconlen = f;
649 if(hud_field_icon2 != "")
651 sz = draw_getimagesize(hud_field_icon2);
653 if(hud_fixscoreboardcolumnwidth_iconlen < f)
654 hud_fixscoreboardcolumnwidth_iconlen = f;
657 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
659 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
660 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
662 hud_fixscoreboardcolumnwidth_marginlen = 0;
664 if(field == SP_NAME) // name gets all remaining space
668 namesize = sbwidth;// / hud_fontsize_x;
669 for(j = 0; j < hud_num_fields; ++j)
671 if (hud_field[i] != SP_SEPARATOR)
672 namesize -= hud_size[j] + hud_fontsize.x;
673 namesize += hud_fontsize.x;
674 hud_size[i] = namesize;
676 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
677 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
678 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
679 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
682 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
684 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
691 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
694 rgb = Team_ColorRGB(pl.team);
698 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
700 if((rgb == '1 1 1') && (!is_spec)) {
701 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
702 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
703 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
705 vector h_pos = pos - '1 1 0';
706 vector h_size = item_size + '2 0 0';
707 // alternated rows highlighting
709 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
710 else if((scoreboard_highlight) && (!(pl_number % 2)))
711 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
717 for(i = 0; i < hud_num_fields; ++i)
719 field = hud_field[i];
720 if(field == SP_SEPARATOR)
723 if(is_spec && field != SP_NAME && field != SP_PING) {
724 pos.x += hud_size[i] + hud_fontsize.x;
727 str = HUD_GetField(pl, field);
728 str = HUD_FixScoreboardColumnWidth(i, str);
730 pos.x += hud_size[i] + hud_fontsize.x;
732 if(field == SP_NAME) {
733 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
735 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
737 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
739 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
741 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
743 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
746 tmp.x = hud_size[i] + hud_fontsize.x;
747 if(hud_field_icon0 != "")
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_self, DRAWFLAG_NORMAL);
751 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);
752 if(hud_field_icon1 != "")
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_self, DRAWFLAG_NORMAL);
756 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);
757 if(hud_field_icon2 != "")
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_self, DRAWFLAG_NORMAL);
761 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);
764 if(hud_field[i] == SP_SEPARATOR)
767 for(i = hud_num_fields-1; i > 0; --i)
769 field = hud_field[i];
770 if(field == SP_SEPARATOR)
773 if(is_spec && field != SP_NAME && field != SP_PING) {
774 pos.x -= hud_size[i] + hud_fontsize.x;
778 str = HUD_GetField(pl, field);
779 str = HUD_FixScoreboardColumnWidth(i, str);
781 if(field == SP_NAME) {
782 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
784 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
786 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
788 tmp.x = hud_fixscoreboardcolumnwidth_len;
790 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
792 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
796 if(hud_field_icon0 != "")
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_self, DRAWFLAG_NORMAL);
800 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);
801 if(hud_field_icon1 != "")
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_self, DRAWFLAG_NORMAL);
805 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);
806 if(hud_field_icon2 != "")
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_self, DRAWFLAG_NORMAL);
810 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);
811 pos.x -= hud_size[i] + hud_fontsize.x;
816 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
820 * HUD_Scoreboard_MakeTable
822 * Makes a table for a team (for all playing players in DM) and fills it
825 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
827 float body_table_height;
828 vector tmp = '0 0 0', column_dim = '0 0 0';
831 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
833 pos.y += autocvar_scoreboard_border_thickness;
837 tmp.y = 1.25 * hud_fontsize.y;
841 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
843 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
846 tmp.y += autocvar_scoreboard_border_thickness;
847 tmp.y += body_table_height;
848 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
850 // separator header/table
851 pos.y += 1.25 * hud_fontsize.y;
852 tmp.y = autocvar_scoreboard_border_thickness;
853 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
855 pos.y += autocvar_scoreboard_border_thickness;
858 tmp.y = body_table_height;
860 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
862 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
864 // anyway, apply some color
865 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
867 // go back to the top to make alternated columns highlighting and to print the strings
868 pos.y -= 1.25 * hud_fontsize.y;
869 pos.y -= autocvar_scoreboard_border_thickness;
873 if (scoreboard_highlight)
875 column_dim.y = 1.25 * hud_fontsize.y; // header
876 column_dim.y += autocvar_scoreboard_border_thickness;
877 column_dim.y += body_table_height;
880 // print the strings of the columns headers and draw the columns
882 for(i = 0; i < hud_num_fields; ++i)
884 if(hud_field[i] == SP_SEPARATOR)
886 column_dim.x = hud_size[i] + hud_fontsize.x;
887 if (scoreboard_highlight)
890 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
892 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
893 pos.x += column_dim.x;
895 if(hud_field[i] == SP_SEPARATOR)
899 for(i = hud_num_fields-1; i > 0; --i)
901 if(hud_field[i] == SP_SEPARATOR)
904 pos.x -= hud_size[i];
906 if (scoreboard_highlight)
910 if (i == hud_num_fields-1)
911 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
913 column_dim.x = hud_size[i] + hud_fontsize.x;
914 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
918 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
919 tmp.x = (hud_size[i] - tmp.x);
920 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
921 pos.x -= hud_fontsize.x;
926 pos.y += 1.25 * hud_fontsize.y; // skip the header
927 pos.y += autocvar_scoreboard_border_thickness;
931 tmp.y = hud_fontsize.y * 1.25;
933 // fill the table and draw the rows
936 for(pl = players.sort_next; pl; pl = pl.sort_next)
938 if(pl.team != tm.team)
940 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
941 pos.y += 1.25 * hud_fontsize.y;
945 for(pl = players.sort_next; pl; pl = pl.sort_next)
947 if(pl.team == NUM_SPECTATOR)
949 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
950 pos.y += 1.25 * hud_fontsize.y;
955 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
956 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
961 float HUD_WouldDrawScoreboard() {
962 if (autocvar__hud_configure)
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;