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 (QuickMenu_IsOpened())
966 else if (HUD_Radar_Clickable())
968 else if (scoreboard_showscores)
970 else if (intermission == 1)
972 else if (intermission == 2)
974 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
976 else if (scoreboard_showscores_force)
981 float average_accuracy;
982 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
984 WepSet weapons_stat = WepSet_GetFromStat();
985 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
986 float initial_posx = pos.x;
990 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
992 self = get_weaponinfo(i);
996 weapon_stats = weapon_accuracy[i-WEP_FIRST];
998 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1002 int weapon_cnt = WEP_COUNT - disownedcnt;
1008 if(autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor(WEP_COUNT * 0.5))
1012 int columnns = ceil(weapon_cnt / rows);
1015 float fontsize = height * 1/3;
1016 float weapon_height = height * 2/3;
1017 float weapon_width = sbwidth / columnns / rows;
1019 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1020 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1021 vector tmp = '0 0 0';
1023 tmp.y = height * rows;
1026 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1028 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1029 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1031 // column highlighting
1032 for(i = 0; i < columnns; ++i)
1035 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);
1039 for(i = 0; i < rows; ++i)
1041 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);
1044 average_accuracy = 0;
1045 int weapons_with_stats = 0;
1047 pos.x += weapon_width / 2;
1049 if(autocvar_scoreboard_accuracy_nocolors)
1052 Accuracy_LoadColors();
1054 float oldposx = pos.x;
1058 for(i = WEP_FIRST, column = 0; i <= WEP_LAST; ++i)
1060 self = get_weaponinfo(i);
1063 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1065 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1069 if(weapon_stats >= 0)
1070 weapon_alpha = scoreboard_alpha_fg;
1072 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1075 drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1077 if(weapon_stats >= 0) {
1078 weapons_with_stats += 1;
1079 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1082 s = sprintf("%d%%", weapon_stats*100);
1085 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1087 if(!autocvar_scoreboard_accuracy_nocolors)
1088 rgb = Accuracy_GetColor(weapon_stats);
1090 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1092 tmpos.x += weapon_width * rows;
1093 pos.x += weapon_width * rows;
1094 if(rows == 2 && column == columnns - 1) {
1102 if(weapons_with_stats)
1103 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1106 pos.y += 1.25 * hud_fontsize.y;
1107 pos.x = initial_posx;
1111 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1113 pos.x += hud_fontsize.x * 0.25;
1114 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1115 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1116 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1118 pos.y+= hud_fontsize.y;
1123 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1124 float stat_secrets_found, stat_secrets_total;
1125 float stat_monsters_killed, stat_monsters_total;
1129 // get monster stats
1130 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1131 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1133 // get secrets stats
1134 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1135 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1137 // get number of rows
1138 if(stat_secrets_total)
1140 if(stat_monsters_total)
1143 // if no rows, return
1147 // draw table header
1148 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1149 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1152 vector tmp = '0 0 0';
1154 tmp.y = hud_fontsize.y * rows;
1157 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1159 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1160 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1163 if(stat_monsters_total)
1165 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1166 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1170 if(stat_secrets_total)
1172 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1173 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1177 pos.y += 1.25 * hud_fontsize.y;
1182 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1185 RANKINGS_RECEIVED_CNT = 0;
1186 for (i=RANKINGS_CNT-1; i>=0; --i)
1188 ++RANKINGS_RECEIVED_CNT;
1190 if (RANKINGS_RECEIVED_CNT == 0)
1194 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1196 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1197 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1198 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1200 pos.y += hud_fontsize.y;
1201 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1202 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1203 vector tmp = '0 0 0';
1205 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1208 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1210 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1211 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1214 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1221 n = grecordholder[i];
1222 p = count_ordinal(i+1);
1223 if(grecordholder[i] == GetPlayerName(player_localnum))
1224 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1225 else if(!(i % 2) && scoreboard_highlight)
1226 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1227 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1228 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);
1229 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1230 pos.y += 1.25 * hud_fontsize.y;
1232 pos.y += autocvar_scoreboard_border_thickness;
1237 float hud_woulddrawscoreboard_prev;
1238 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1239 void HUD_DrawScoreboard()
1241 float hud_woulddrawscoreboard;
1242 hud_woulddrawscoreboard = scoreboard_active;
1243 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1244 hud_woulddrawscoreboard_change = time;
1245 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1248 if(hud_woulddrawscoreboard) {
1249 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1250 if (scoreboard_fadeinspeed)
1251 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1253 scoreboard_fade_alpha = 1;
1256 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1257 if (scoreboard_fadeoutspeed)
1258 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1260 scoreboard_fade_alpha = 0;
1263 if (!scoreboard_fade_alpha)
1266 HUD_UpdatePlayerTeams();
1268 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1269 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1270 scoreboard_highlight = autocvar_scoreboard_highlight;
1271 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1272 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1273 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1274 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1276 vector rgb, pos, tmp;
1280 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1281 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1283 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1284 ymax = (vid_conheight - ymin);
1286 sbwidth = xmax - xmin;
1288 // Initializes position
1294 vector sb_heading_fontsize;
1295 sb_heading_fontsize = hud_fontsize * 2;
1296 draw_beginBoldFont();
1297 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1300 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1302 // Draw the scoreboard
1303 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1307 vector team_score_baseoffset;
1308 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1309 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1311 if(tm.team == NUM_SPECTATOR)
1314 draw_beginBoldFont();
1315 rgb = Team_ColorRGB(tm.team);
1316 str = ftos(tm.(teamscores[ts_primary]));
1317 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1319 if(ts_primary != ts_secondary)
1321 str = ftos(tm.(teamscores[ts_secondary]));
1322 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);
1326 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1328 rgb.x = autocvar_scoreboard_color_bg_r;
1329 rgb.y = autocvar_scoreboard_color_bg_g;
1330 rgb.z = autocvar_scoreboard_color_bg_b;
1334 rgb.x = autocvar_scoreboard_color_bg_r;
1335 rgb.y = autocvar_scoreboard_color_bg_g;
1336 rgb.z = autocvar_scoreboard_color_bg_b;
1338 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1340 if(tm.team == NUM_SPECTATOR)
1343 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1347 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1348 if(race_speedaward) {
1349 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1350 pos.y += 1.25 * hud_fontsize.y;
1352 if(race_speedaward_alltimebest) {
1353 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1354 pos.y += 1.25 * hud_fontsize.y;
1356 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1358 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1360 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1362 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1367 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1369 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1376 item_size.x = sbwidth;
1377 item_size.y = hud_fontsize.y * 1.25;
1379 for(pl = players.sort_next; pl; pl = pl.sort_next)
1381 if(pl.team != NUM_SPECTATOR)
1383 pos.y += 1.25 * hud_fontsize.y;
1384 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1390 draw_beginBoldFont();
1391 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1393 pos.y += 1.25 * hud_fontsize.y;
1396 // Print info string
1398 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1399 tl = getstatf(STAT_TIMELIMIT);
1400 fl = getstatf(STAT_FRAGLIMIT);
1401 ll = getstatf(STAT_LEADLIMIT);
1402 if(gametype == MAPINFO_TYPE_LMS)
1405 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1410 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1414 str = strcat(str, _(" or"));
1417 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1418 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1419 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1420 TranslateScoresLabel(teamscores_label[ts_primary])));
1424 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1425 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1426 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1427 TranslateScoresLabel(scores_label[ps_primary])));
1432 if(tl > 0 || fl > 0)
1433 str = strcat(str, _(" or"));
1436 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1437 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1438 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1439 TranslateScoresLabel(teamscores_label[ts_primary])));
1443 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1444 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1445 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1446 TranslateScoresLabel(scores_label[ps_primary])));
1451 pos.y += 1.2 * hud_fontsize.y;
1452 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1454 // print information about respawn status
1455 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1459 if(respawn_time < 0)
1461 // a negative number means we are awaiting respawn, time value is still the same
1462 respawn_time *= -1; // remove mark now that we checked it
1463 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1465 str = sprintf(_("^1Respawning in ^3%s^1..."),
1466 (autocvar_scoreboard_respawntime_decimals ?
1467 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1469 count_seconds(respawn_time - time)
1473 else if(time < respawn_time)
1475 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1476 (autocvar_scoreboard_respawntime_decimals ?
1477 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1479 count_seconds(respawn_time - time)
1483 else if(time >= respawn_time)
1484 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1486 pos.y += 1.2 * hud_fontsize.y;
1487 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1490 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;