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 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
249 LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
250 LOG_INFO(_("Usage:\n"));
251 LOG_INFO(_("^2scoreboard_columns_set default\n"));
252 LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
253 LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
254 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
256 LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n"));
257 LOG_INFO(_("^3ping^7 Ping time\n"));
258 LOG_INFO(_("^3pl^7 Packet loss\n"));
259 LOG_INFO(_("^3kills^7 Number of kills\n"));
260 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
261 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
262 LOG_INFO(_("^3frags^7 kills - suicides\n"));
263 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
264 LOG_INFO(_("^3sum^7 frags - deaths\n"));
265 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
266 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
267 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
268 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
269 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
270 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
271 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
272 LOG_INFO(_("^3rank^7 Player rank\n"));
273 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
274 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
275 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
276 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
277 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
278 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
279 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
280 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
281 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
282 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
283 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
284 LOG_INFO(_("^3score^7 Total score\n\n"));
286 LOG_INFO(_("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 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
292 "include/exclude ALL teams/noteams game modes.\n\n"));
294 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
295 LOG_INFO(_("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 LOG_INFO(_("'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 LOG_INFOF("^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 LOG_INFO("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 LOG_INFO("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 LOG_INFO("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 LOG_INFOF("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 LOG_INFOF("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 (HUD_Radar_Clickable())
964 else if (scoreboard_showscores)
966 else if (intermission == 1)
968 else if (intermission == 2)
970 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
972 else if (scoreboard_showscores_force)
977 float average_accuracy;
978 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
980 WepSet weapons_stat = WepSet_GetFromStat();
981 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
982 float initial_posx = pos.x;
986 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
988 self = get_weaponinfo(i);
992 weapon_stats = weapon_accuracy[i-WEP_FIRST];
994 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
998 int weapon_cnt = WEP_COUNT - disownedcnt;
1004 if(autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor(WEP_COUNT * 0.5))
1008 int columnns = ceil(weapon_cnt / rows);
1011 float fontsize = height * 1/3;
1012 float weapon_height = height * 2/3;
1013 float weapon_width = sbwidth / columnns / rows;
1015 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1016 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1017 vector tmp = '0 0 0';
1019 tmp.y = height * rows;
1022 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1024 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1025 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1027 // column highlighting
1028 for(i = 0; i < columnns; ++i)
1031 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);
1035 for(i = 0; i < rows; ++i)
1037 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);
1040 average_accuracy = 0;
1041 int weapons_with_stats = 0;
1043 pos.x += weapon_width / 2;
1045 if(autocvar_scoreboard_accuracy_nocolors)
1048 Accuracy_LoadColors();
1050 float oldposx = pos.x;
1054 for(i = WEP_FIRST, column = 0; i <= WEP_LAST; ++i)
1056 self = get_weaponinfo(i);
1059 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1061 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1065 if(weapon_stats >= 0)
1066 weapon_alpha = scoreboard_alpha_fg;
1068 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1071 drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1073 if(weapon_stats >= 0) {
1074 weapons_with_stats += 1;
1075 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1078 s = sprintf("%d%%", weapon_stats*100);
1081 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1083 if(!autocvar_scoreboard_accuracy_nocolors)
1084 rgb = Accuracy_GetColor(weapon_stats);
1086 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1088 tmpos.x += weapon_width * rows;
1089 pos.x += weapon_width * rows;
1090 if(rows == 2 && column == columnns - 1) {
1098 if(weapons_with_stats)
1099 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1102 pos.y += 1.25 * hud_fontsize.y;
1103 pos.x = initial_posx;
1107 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1109 pos.x += hud_fontsize.x * 0.25;
1110 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1111 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1112 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1114 pos.y+= hud_fontsize.y;
1119 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1120 float stat_secrets_found, stat_secrets_total;
1121 float stat_monsters_killed, stat_monsters_total;
1125 // get monster stats
1126 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1127 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1129 // get secrets stats
1130 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1131 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1133 // get number of rows
1134 if(stat_secrets_total)
1136 if(stat_monsters_total)
1139 // if no rows, return
1143 // draw table header
1144 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1145 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1148 vector tmp = '0 0 0';
1150 tmp.y = hud_fontsize.y * rows;
1153 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1155 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1156 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1159 if(stat_monsters_total)
1161 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1162 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1166 if(stat_secrets_total)
1168 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1169 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1173 pos.y += 1.25 * hud_fontsize.y;
1178 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1181 RANKINGS_RECEIVED_CNT = 0;
1182 for (i=RANKINGS_CNT-1; i>=0; --i)
1184 ++RANKINGS_RECEIVED_CNT;
1186 if (RANKINGS_RECEIVED_CNT == 0)
1190 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1192 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1193 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1194 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1196 pos.y += hud_fontsize.y;
1197 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1198 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1199 vector tmp = '0 0 0';
1201 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1204 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1206 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1207 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1210 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1217 n = grecordholder[i];
1218 p = count_ordinal(i+1);
1219 if(grecordholder[i] == GetPlayerName(player_localnum))
1220 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1221 else if(!(i % 2) && scoreboard_highlight)
1222 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1223 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1224 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);
1225 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1226 pos.y += 1.25 * hud_fontsize.y;
1228 pos.y += autocvar_scoreboard_border_thickness;
1233 float hud_woulddrawscoreboard_prev;
1234 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1235 void HUD_DrawScoreboard()
1237 float hud_woulddrawscoreboard;
1238 hud_woulddrawscoreboard = scoreboard_active;
1239 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1240 hud_woulddrawscoreboard_change = time;
1241 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1244 if(hud_woulddrawscoreboard) {
1245 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1246 if (scoreboard_fadeinspeed)
1247 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1249 scoreboard_fade_alpha = 1;
1252 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1253 if (scoreboard_fadeoutspeed)
1254 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1256 scoreboard_fade_alpha = 0;
1259 if (!scoreboard_fade_alpha)
1262 HUD_UpdatePlayerTeams();
1264 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1265 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1266 scoreboard_highlight = autocvar_scoreboard_highlight;
1267 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1268 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1269 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1270 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1272 vector rgb, pos, tmp;
1276 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1277 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1279 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1280 ymax = (vid_conheight - ymin);
1282 sbwidth = xmax - xmin;
1284 // Initializes position
1290 vector sb_heading_fontsize;
1291 sb_heading_fontsize = hud_fontsize * 2;
1292 draw_beginBoldFont();
1293 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1296 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1298 // Draw the scoreboard
1299 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1303 vector team_score_baseoffset;
1304 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1305 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1307 if(tm.team == NUM_SPECTATOR)
1310 draw_beginBoldFont();
1311 rgb = Team_ColorRGB(tm.team);
1312 str = ftos(tm.(teamscores[ts_primary]));
1313 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1315 if(ts_primary != ts_secondary)
1317 str = ftos(tm.(teamscores[ts_secondary]));
1318 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);
1322 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1324 rgb.x = autocvar_scoreboard_color_bg_r;
1325 rgb.y = autocvar_scoreboard_color_bg_g;
1326 rgb.z = autocvar_scoreboard_color_bg_b;
1330 rgb.x = autocvar_scoreboard_color_bg_r;
1331 rgb.y = autocvar_scoreboard_color_bg_g;
1332 rgb.z = autocvar_scoreboard_color_bg_b;
1334 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1336 if(tm.team == NUM_SPECTATOR)
1339 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1343 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1344 if(race_speedaward) {
1345 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1346 pos.y += 1.25 * hud_fontsize.y;
1348 if(race_speedaward_alltimebest) {
1349 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1350 pos.y += 1.25 * hud_fontsize.y;
1352 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1354 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1356 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1358 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1363 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1365 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1372 item_size.x = sbwidth;
1373 item_size.y = hud_fontsize.y * 1.25;
1375 for(pl = players.sort_next; pl; pl = pl.sort_next)
1377 if(pl.team != NUM_SPECTATOR)
1379 pos.y += 1.25 * hud_fontsize.y;
1380 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1386 draw_beginBoldFont();
1387 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1389 pos.y += 1.25 * hud_fontsize.y;
1392 // Print info string
1394 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1395 tl = getstatf(STAT_TIMELIMIT);
1396 fl = getstatf(STAT_FRAGLIMIT);
1397 ll = getstatf(STAT_LEADLIMIT);
1398 if(gametype == MAPINFO_TYPE_LMS)
1401 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1406 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1410 str = strcat(str, _(" or"));
1413 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1414 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1415 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1416 TranslateScoresLabel(teamscores_label[ts_primary])));
1420 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1421 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1422 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1423 TranslateScoresLabel(scores_label[ps_primary])));
1428 if(tl > 0 || fl > 0)
1429 str = strcat(str, _(" or"));
1432 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1433 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1434 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1435 TranslateScoresLabel(teamscores_label[ts_primary])));
1439 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1440 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1441 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1442 TranslateScoresLabel(scores_label[ps_primary])));
1447 pos.y += 1.2 * hud_fontsize.y;
1448 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1450 // print information about respawn status
1451 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1455 if(respawn_time < 0)
1457 // a negative number means we are awaiting respawn, time value is still the same
1458 respawn_time *= -1; // remove mark now that we checked it
1459 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1461 str = sprintf(_("^1Respawning in ^3%s^1..."),
1462 (autocvar_scoreboard_respawntime_decimals ?
1463 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1465 count_seconds(respawn_time - time)
1469 else if(time < respawn_time)
1471 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1472 (autocvar_scoreboard_respawntime_decimals ?
1473 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1475 count_seconds(respawn_time - time)
1479 else if(time >= respawn_time)
1480 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1482 pos.y += 1.2 * hud_fontsize.y;
1483 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1486 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;