1 #include "scoreboard.qh"
7 #include "../common/constants.qh"
8 #include "../common/mapinfo.qh"
9 #include "../common/minigames/cl_minigames.qh"
10 #include "../common/stats.qh"
11 #include "../common/teams.qh"
12 #include "../common/util.qh"
14 float scoreboard_alpha_bg;
15 float scoreboard_alpha_fg;
16 float scoreboard_highlight;
17 float scoreboard_highlight_alpha;
18 float scoreboard_highlight_alpha_self;
19 float scoreboard_alpha_name;
20 float scoreboard_alpha_name_self;
22 void drawstringright(vector, string, vector, vector, float, float);
23 void drawstringcenter(vector, string, vector, vector, float, float);
25 const float SCOREBOARD_OFFSET = 50;
27 // wrapper to put all possible scores titles through gettext
28 string TranslateScoresLabel(string l)
32 case "bckills": return CTX(_("SCO^bckills"));
33 case "bctime": return CTX(_("SCO^bctime"));
34 case "caps": return CTX(_("SCO^caps"));
35 case "captime": return CTX(_("SCO^captime"));
36 case "deaths": return CTX(_("SCO^deaths"));
37 case "destroyed": return CTX(_("SCO^destroyed"));
38 case "drops": return CTX(_("SCO^drops"));
39 case "faults": return CTX(_("SCO^faults"));
40 case "fckills": return CTX(_("SCO^fckills"));
41 case "goals": return CTX(_("SCO^goals"));
42 case "kckills": return CTX(_("SCO^kckills"));
43 case "kdratio": return CTX(_("SCO^kdratio"));
44 case "k/d": return CTX(_("SCO^k/d"));
45 case "kd": return CTX(_("SCO^kd"));
46 case "kdr": return CTX(_("SCO^kdr"));
47 case "kills": return CTX(_("SCO^kills"));
48 case "laps": return CTX(_("SCO^laps"));
49 case "lives": return CTX(_("SCO^lives"));
50 case "losses": return CTX(_("SCO^losses"));
51 case "name": return CTX(_("SCO^name"));
52 case "sum": return CTX(_("SCO^sum"));
53 case "nick": return CTX(_("SCO^nick"));
54 case "objectives": return CTX(_("SCO^objectives"));
55 case "pickups": return CTX(_("SCO^pickups"));
56 case "ping": return CTX(_("SCO^ping"));
57 case "pl": return CTX(_("SCO^pl"));
58 case "pushes": return CTX(_("SCO^pushes"));
59 case "rank": return CTX(_("SCO^rank"));
60 case "returns": return CTX(_("SCO^returns"));
61 case "revivals": return CTX(_("SCO^revivals"));
62 case "score": return CTX(_("SCO^score"));
63 case "suicides": return CTX(_("SCO^suicides"));
64 case "takes": return CTX(_("SCO^takes"));
65 case "ticks": return CTX(_("SCO^ticks"));
74 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
75 for(i = 0; i < MAX_SCORE; ++i)
77 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
78 if(f == SFL_SORT_PRIO_PRIMARY)
80 if(f == SFL_SORT_PRIO_SECONDARY)
83 if(ps_secondary == -1)
84 ps_secondary = ps_primary;
86 for(i = 0; i < MAX_TEAMSCORE; ++i)
88 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
89 if(f == SFL_SORT_PRIO_PRIMARY)
91 if(f == SFL_SORT_PRIO_SECONDARY)
94 if(ts_secondary == -1)
95 ts_secondary = ts_primary;
100 float SetTeam(entity pl, float Team);
102 void HUD_UpdatePlayerTeams()
109 for(pl = players.sort_next; pl; pl = pl.sort_next)
112 Team = GetPlayerColor(pl.sv_entnum);
113 if(SetTeam(pl, Team))
116 HUD_UpdatePlayerPos(pl);
120 pl = players.sort_next;
125 print(strcat("PNUM: ", ftos(num), "\n"));
130 int HUD_CompareScore(float vl, float vr, int f)
132 if(f & SFL_ZERO_IS_WORST)
134 if(vl == 0 && vr != 0)
136 if(vl != 0 && vr == 0)
140 return IS_INCREASING(f);
142 return IS_DECREASING(f);
146 float HUD_ComparePlayerScores(entity left, entity right)
149 vl = GetPlayerColor(left.sv_entnum);
150 vr = GetPlayerColor(right.sv_entnum);
162 if(vl == NUM_SPECTATOR)
164 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
166 if(!left.gotscores && right.gotscores)
171 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
175 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
180 for(i = 0; i < MAX_SCORE; ++i)
182 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
187 if (left.sv_entnum < right.sv_entnum)
193 void HUD_UpdatePlayerPos(entity player)
195 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
197 SORT_SWAP(player, other);
199 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
201 SORT_SWAP(other, player);
205 float HUD_CompareTeamScores(entity left, entity right)
209 if(left.team == NUM_SPECTATOR)
211 if(right.team == NUM_SPECTATOR)
214 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
218 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
222 for(i = 0; i < MAX_SCORE; ++i)
224 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
229 if (left.team < right.team)
235 void HUD_UpdateTeamPos(entity Team)
237 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
239 SORT_SWAP(Team, other);
241 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
243 SORT_SWAP(other, Team);
249 print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
250 print(_("^3|---------------------------------------------------------------|\n"));
251 print(_("Usage:\n"));
252 print(_("^2scoreboard_columns_set default\n"));
253 print(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
254 print(_("The following field names are recognized (case insensitive):\n"));
255 print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
257 print(_("^3name^7 or ^3nick^7 Name of a player\n"));
258 print(_("^3ping^7 Ping time\n"));
259 print(_("^3pl^7 Packet loss\n"));
260 print(_("^3kills^7 Number of kills\n"));
261 print(_("^3deaths^7 Number of deaths\n"));
262 print(_("^3suicides^7 Number of suicides\n"));
263 print(_("^3frags^7 kills - suicides\n"));
264 print(_("^3kd^7 The kill-death ratio\n"));
265 print(_("^3sum^7 frags - deaths\n"));
266 print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
267 print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
268 print(_("^3captime^7 Time of fastest cap (CTF)\n"));
269 print(_("^3fckills^7 Number of flag carrier kills\n"));
270 print(_("^3returns^7 Number of flag returns\n"));
271 print(_("^3drops^7 Number of flag drops\n"));
272 print(_("^3lives^7 Number of lives (LMS)\n"));
273 print(_("^3rank^7 Player rank\n"));
274 print(_("^3pushes^7 Number of players pushed into void\n"));
275 print(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
276 print(_("^3kckills^7 Number of keys carrier kills\n"));
277 print(_("^3losses^7 Number of times a key was lost\n"));
278 print(_("^3laps^7 Number of laps finished (race/cts)\n"));
279 print(_("^3time^7 Total time raced (race/cts)\n"));
280 print(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
281 print(_("^3ticks^7 Number of ticks (DOM)\n"));
282 print(_("^3takes^7 Number of domination points taken (DOM)\n"));
283 print(_("^3bckills^7 Number of ball carrier kills\n"));
284 print(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
285 print(_("^3score^7 Total score\n\n"));
287 print(_("Before a field you can put a + or - sign, then a comma separated list\n"
288 "of game types, then a slash, to make the field show up only in these\n"
289 "or in all but these game types. You can also specify 'all' as a\n"
290 "field to show all fields available for the current game mode.\n\n"));
292 print(_("The special game type names 'teams' and 'noteams' can be used to\n"
293 "include/exclude ALL teams/noteams game modes.\n\n"));
295 print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
296 print(_("will display name, ping and pl aligned to the left, and the fields\n"
297 "right of the vertical bar aligned to the right.\n"));
298 print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
299 "other gamemodes except DM.\n"));
302 #define HUD_DefaultColumnLayout() \
304 "-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" */ \
305 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \
306 "+lms/lives +lms/rank " \
307 "+kh/caps +kh/pushes +kh/destroyed " \
308 "?+race/laps ?+race/time ?+race/fastest " \
309 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
312 void Cmd_HUD_SetFields(float argc)
316 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
321 // set up a temporary scoreboard layout
322 // no layout can be properly set up until score_info data haven't been received
323 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
325 scores_label[ps_primary] = strzone("score");
326 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
329 // TODO: re enable with gametype dependant cvars?
330 if(argc < 3) // no arguments provided
331 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
334 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
338 if(argv(2) == "default")
339 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
340 else if(argv(2) == "all")
343 s = "ping pl name |";
344 for(i = 0; i < MAX_SCORE; ++i)
347 if(i != ps_secondary)
348 if(scores_label[i] != "")
349 s = strcat(s, " ", scores_label[i]);
351 if(ps_secondary != ps_primary)
352 s = strcat(s, " ", scores_label[ps_secondary]);
353 s = strcat(s, " ", scores_label[ps_primary]);
354 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
361 hud_fontsize = HUD_GetFontsize("hud_fontsize");
363 for(i = 1; i < argc - 1; ++i)
369 if(substring(str, 0, 1) == "?")
372 str = substring(str, 1, strlen(str) - 1);
375 slash = strstrofs(str, "/", 0);
378 pattern = substring(str, 0, slash);
379 str = substring(str, slash + 1, strlen(str) - (slash + 1));
381 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
385 strunzone(hud_title[hud_num_fields]);
386 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
387 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
388 str = strtolower(str);
391 hud_field[hud_num_fields] = SP_PING;
392 } else if(str == "pl") {
393 hud_field[hud_num_fields] = SP_PL;
394 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
395 hud_field[hud_num_fields] = SP_KDRATIO;
396 } else if(str == "sum" || str == "diff" || str == "k-d") {
397 hud_field[hud_num_fields] = SP_SUM;
398 } else if(str == "name" || str == "nick") {
399 hud_field[hud_num_fields] = SP_NAME;
401 } else if(str == "|") {
402 hud_field[hud_num_fields] = SP_SEPARATOR;
405 for(j = 0; j < MAX_SCORE; ++j)
406 if(str == strtolower(scores_label[j]))
407 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
416 printf("^1Error:^7 Unknown score field: '%s'\n", str);
420 hud_field[hud_num_fields] = j;
423 if(j == ps_secondary)
427 if(hud_num_fields >= MAX_HUD_FIELDS)
431 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
433 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
435 if(ps_primary == ps_secondary)
437 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
439 if(hud_num_fields+missing < MAX_HUD_FIELDS)
443 strunzone(hud_title[hud_num_fields]);
444 for(i = hud_num_fields; i > 0; --i)
446 hud_title[i] = hud_title[i-1];
447 hud_size[i] = hud_size[i-1];
448 hud_field[i] = hud_field[i-1];
450 hud_title[0] = strzone(TranslateScoresLabel("name"));
451 hud_field[0] = SP_NAME;
453 print("fixed missing field 'name'\n");
457 strunzone(hud_title[hud_num_fields]);
458 for(i = hud_num_fields; i > 1; --i)
460 hud_title[i] = hud_title[i-1];
461 hud_size[i] = hud_size[i-1];
462 hud_field[i] = hud_field[i-1];
464 hud_title[1] = strzone("|");
465 hud_field[1] = SP_SEPARATOR;
466 hud_size[1] = stringwidth("|", false, hud_fontsize);
468 print("fixed missing field '|'\n");
471 else if(!have_separator)
473 strunzone(hud_title[hud_num_fields]);
474 hud_title[hud_num_fields] = strzone("|");
475 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
476 hud_field[hud_num_fields] = SP_SEPARATOR;
478 print("fixed missing field '|'\n");
482 strunzone(hud_title[hud_num_fields]);
483 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
484 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
485 hud_field[hud_num_fields] = ps_secondary;
487 printf("fixed missing field '%s'\n", scores_label[ps_secondary]);
491 strunzone(hud_title[hud_num_fields]);
492 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
493 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
494 hud_field[hud_num_fields] = ps_primary;
496 printf("fixed missing field '%s'\n", scores_label[ps_primary]);
500 hud_field[hud_num_fields] = SP_END;
504 vector hud_field_rgb;
505 string hud_field_icon0;
506 string hud_field_icon1;
507 string hud_field_icon2;
508 vector hud_field_icon0_rgb;
509 vector hud_field_icon1_rgb;
510 vector hud_field_icon2_rgb;
511 float hud_field_icon0_alpha;
512 float hud_field_icon1_alpha;
513 float hud_field_icon2_alpha;
514 string HUD_GetField(entity pl, int field)
516 float tmp, num, denom;
519 hud_field_rgb = '1 1 1';
520 hud_field_icon0 = "";
521 hud_field_icon1 = "";
522 hud_field_icon2 = "";
523 hud_field_icon0_rgb = '1 1 1';
524 hud_field_icon1_rgb = '1 1 1';
525 hud_field_icon2_rgb = '1 1 1';
526 hud_field_icon0_alpha = 1;
527 hud_field_icon1_alpha = 1;
528 hud_field_icon2_alpha = 1;
533 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
534 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
538 tmp = max(0, min(220, f-80)) / 220;
539 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
545 f = pl.ping_packetloss;
546 tmp = pl.ping_movementloss;
547 if(f == 0 && tmp == 0)
549 str = ftos(ceil(f * 100));
551 str = strcat(str, "~", ftos(ceil(tmp * 100)));
552 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
553 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
557 if(ready_waiting && pl.ready)
559 hud_field_icon0 = "gfx/scoreboard/player_ready";
563 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
565 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
566 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
567 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
568 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
569 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
572 return GetPlayerName(pl.sv_entnum);
575 f = pl.(scores[SP_KILLS]);
576 f -= pl.(scores[SP_SUICIDES]);
580 num = pl.(scores[SP_KILLS]);
581 denom = pl.(scores[SP_DEATHS]);
584 hud_field_rgb = '0 1 0';
585 str = sprintf("%d", num);
586 } else if(num <= 0) {
587 hud_field_rgb = '1 0 0';
588 str = sprintf("%.1f", num/denom);
590 str = sprintf("%.1f", num/denom);
594 f = pl.(scores[SP_KILLS]);
595 f -= pl.(scores[SP_DEATHS]);
598 hud_field_rgb = '0 1 0';
600 hud_field_rgb = '1 1 1';
602 hud_field_rgb = '1 0 0';
607 tmp = pl.(scores[field]);
608 f = scores_flags[field];
609 if(field == ps_primary)
610 hud_field_rgb = '1 1 0';
611 else if(field == ps_secondary)
612 hud_field_rgb = '0 1 1';
614 hud_field_rgb = '1 1 1';
615 return ScoreString(f, tmp);
620 float hud_fixscoreboardcolumnwidth_len;
621 float hud_fixscoreboardcolumnwidth_iconlen;
622 float hud_fixscoreboardcolumnwidth_marginlen;
624 string HUD_FixScoreboardColumnWidth(int i, string str)
628 field = hud_field[i];
630 hud_fixscoreboardcolumnwidth_iconlen = 0;
632 if(hud_field_icon0 != "")
634 sz = draw_getimagesize(hud_field_icon0);
636 if(hud_fixscoreboardcolumnwidth_iconlen < f)
637 hud_fixscoreboardcolumnwidth_iconlen = f;
640 if(hud_field_icon1 != "")
642 sz = draw_getimagesize(hud_field_icon1);
644 if(hud_fixscoreboardcolumnwidth_iconlen < f)
645 hud_fixscoreboardcolumnwidth_iconlen = f;
648 if(hud_field_icon2 != "")
650 sz = draw_getimagesize(hud_field_icon2);
652 if(hud_fixscoreboardcolumnwidth_iconlen < f)
653 hud_fixscoreboardcolumnwidth_iconlen = f;
656 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
658 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
659 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
661 hud_fixscoreboardcolumnwidth_marginlen = 0;
663 if(field == SP_NAME) // name gets all remaining space
667 namesize = sbwidth;// / hud_fontsize_x;
668 for(j = 0; j < hud_num_fields; ++j)
670 if (hud_field[i] != SP_SEPARATOR)
671 namesize -= hud_size[j] + hud_fontsize.x;
672 namesize += hud_fontsize.x;
673 hud_size[i] = namesize;
675 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
676 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
677 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
678 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
681 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
683 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
690 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
693 rgb = Team_ColorRGB(pl.team);
697 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
699 if((rgb == '1 1 1') && (!is_spec)) {
700 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
701 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
702 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
704 vector h_pos = pos - '1 1 0';
705 vector h_size = item_size + '2 0 0';
706 // alternated rows highlighting
708 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
709 else if((scoreboard_highlight) && (!(pl_number % 2)))
710 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
716 for(i = 0; i < hud_num_fields; ++i)
718 field = hud_field[i];
719 if(field == SP_SEPARATOR)
722 if(is_spec && field != SP_NAME && field != SP_PING) {
723 pos.x += hud_size[i] + hud_fontsize.x;
726 str = HUD_GetField(pl, field);
727 str = HUD_FixScoreboardColumnWidth(i, str);
729 pos.x += hud_size[i] + hud_fontsize.x;
731 if(field == SP_NAME) {
732 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
734 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
736 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
738 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
740 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
742 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
745 tmp.x = hud_size[i] + hud_fontsize.x;
746 if(hud_field_icon0 != "")
748 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);
750 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);
751 if(hud_field_icon1 != "")
753 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);
755 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);
756 if(hud_field_icon2 != "")
758 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);
760 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);
763 if(hud_field[i] == SP_SEPARATOR)
766 for(i = hud_num_fields-1; i > 0; --i)
768 field = hud_field[i];
769 if(field == SP_SEPARATOR)
772 if(is_spec && field != SP_NAME && field != SP_PING) {
773 pos.x -= hud_size[i] + hud_fontsize.x;
777 str = HUD_GetField(pl, field);
778 str = HUD_FixScoreboardColumnWidth(i, str);
780 if(field == SP_NAME) {
781 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
783 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
785 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
787 tmp.x = hud_fixscoreboardcolumnwidth_len;
789 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
791 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
795 if(hud_field_icon0 != "")
797 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);
799 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);
800 if(hud_field_icon1 != "")
802 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);
804 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);
805 if(hud_field_icon2 != "")
807 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);
809 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);
810 pos.x -= hud_size[i] + hud_fontsize.x;
815 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
819 * HUD_Scoreboard_MakeTable
821 * Makes a table for a team (for all playing players in DM) and fills it
824 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
826 float body_table_height;
827 vector tmp = '0 0 0', column_dim = '0 0 0';
830 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
832 pos.y += autocvar_scoreboard_border_thickness;
836 tmp.y = 1.25 * hud_fontsize.y;
840 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
842 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
845 tmp.y += autocvar_scoreboard_border_thickness;
846 tmp.y += body_table_height;
847 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
849 // separator header/table
850 pos.y += 1.25 * hud_fontsize.y;
851 tmp.y = autocvar_scoreboard_border_thickness;
852 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
854 pos.y += autocvar_scoreboard_border_thickness;
857 tmp.y = body_table_height;
859 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
861 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
863 // anyway, apply some color
864 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
866 // go back to the top to make alternated columns highlighting and to print the strings
867 pos.y -= 1.25 * hud_fontsize.y;
868 pos.y -= autocvar_scoreboard_border_thickness;
872 if (scoreboard_highlight)
874 column_dim.y = 1.25 * hud_fontsize.y; // header
875 column_dim.y += autocvar_scoreboard_border_thickness;
876 column_dim.y += body_table_height;
879 // print the strings of the columns headers and draw the columns
881 for(i = 0; i < hud_num_fields; ++i)
883 if(hud_field[i] == SP_SEPARATOR)
885 column_dim.x = hud_size[i] + hud_fontsize.x;
886 if (scoreboard_highlight)
889 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
891 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
892 pos.x += column_dim.x;
894 if(hud_field[i] == SP_SEPARATOR)
898 for(i = hud_num_fields-1; i > 0; --i)
900 if(hud_field[i] == SP_SEPARATOR)
903 pos.x -= hud_size[i];
905 if (scoreboard_highlight)
909 if (i == hud_num_fields-1)
910 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
912 column_dim.x = hud_size[i] + hud_fontsize.x;
913 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
917 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
918 tmp.x = (hud_size[i] - tmp.x);
919 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
920 pos.x -= hud_fontsize.x;
925 pos.y += 1.25 * hud_fontsize.y; // skip the header
926 pos.y += autocvar_scoreboard_border_thickness;
930 tmp.y = hud_fontsize.y * 1.25;
932 // fill the table and draw the rows
935 for(pl = players.sort_next; pl; pl = pl.sort_next)
937 if(pl.team != tm.team)
939 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
940 pos.y += 1.25 * hud_fontsize.y;
944 for(pl = players.sort_next; pl; pl = pl.sort_next)
946 if(pl.team == NUM_SPECTATOR)
948 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
949 pos.y += 1.25 * hud_fontsize.y;
954 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
955 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
960 float HUD_WouldDrawScoreboard() {
961 if (autocvar__hud_configure)
963 else if (HUD_Radar_Clickable())
965 else if (scoreboard_showscores)
967 else if (intermission == 1)
969 else if (intermission == 2)
971 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
973 else if (scoreboard_showscores_force)
978 float average_accuracy;
979 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
981 WepSet weapons_stat = WepSet_GetFromStat();
982 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
983 float initial_posx = pos.x;
987 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
989 self = get_weaponinfo(i);
993 weapon_stats = weapon_accuracy[i-WEP_FIRST];
995 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
999 int weapon_cnt = WEP_COUNT - disownedcnt;
1005 if(autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor(WEP_COUNT * 0.5))
1009 int columnns = ceil(weapon_cnt / rows);
1012 float fontsize = height * 1/3;
1013 float weapon_height = height * 2/3;
1014 float weapon_width = sbwidth / columnns / rows;
1016 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1017 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1018 vector tmp = '0 0 0';
1020 tmp.y = height * rows;
1023 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1025 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1026 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1028 // column highlighting
1029 for(i = 0; i < columnns; ++i)
1032 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);
1036 for(i = 0; i < rows; ++i)
1038 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);
1041 average_accuracy = 0;
1042 int weapons_with_stats = 0;
1044 pos.x += weapon_width / 2;
1046 if(autocvar_scoreboard_accuracy_nocolors)
1049 Accuracy_LoadColors();
1051 float oldposx = pos.x;
1055 for(i = WEP_FIRST, column = 0; i <= WEP_LAST; ++i)
1057 self = get_weaponinfo(i);
1060 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1062 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1066 if(weapon_stats >= 0)
1067 weapon_alpha = scoreboard_alpha_fg;
1069 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1072 drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1074 if(weapon_stats >= 0) {
1075 weapons_with_stats += 1;
1076 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1079 s = sprintf("%d%%", weapon_stats*100);
1082 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1084 if(!autocvar_scoreboard_accuracy_nocolors)
1085 rgb = Accuracy_GetColor(weapon_stats);
1087 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1089 tmpos.x += weapon_width * rows;
1090 pos.x += weapon_width * rows;
1091 if(rows == 2 && column == columnns - 1) {
1099 if(weapons_with_stats)
1100 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1103 pos.y += 1.25 * hud_fontsize.y;
1104 pos.x = initial_posx;
1108 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1110 pos.x += hud_fontsize.x * 0.25;
1111 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1112 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1113 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1115 pos.y+= hud_fontsize.y;
1120 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1121 float stat_secrets_found, stat_secrets_total;
1122 float stat_monsters_killed, stat_monsters_total;
1126 // get monster stats
1127 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1128 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1130 // get secrets stats
1131 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1132 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1134 // get number of rows
1135 if(stat_secrets_total)
1137 if(stat_monsters_total)
1140 // if no rows, return
1144 // draw table header
1145 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1146 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1149 vector tmp = '0 0 0';
1151 tmp.y = hud_fontsize.y * rows;
1154 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1156 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1157 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1160 if(stat_monsters_total)
1162 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1163 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1167 if(stat_secrets_total)
1169 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1170 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1174 pos.y += 1.25 * hud_fontsize.y;
1179 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1182 RANKINGS_RECEIVED_CNT = 0;
1183 for (i=RANKINGS_CNT-1; i>=0; --i)
1185 ++RANKINGS_RECEIVED_CNT;
1187 if (RANKINGS_RECEIVED_CNT == 0)
1191 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1193 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1194 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1195 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1197 pos.y += hud_fontsize.y;
1198 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1199 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1200 vector tmp = '0 0 0';
1202 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1205 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1207 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1208 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1211 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1218 n = grecordholder[i];
1219 p = count_ordinal(i+1);
1220 if(grecordholder[i] == GetPlayerName(player_localnum))
1221 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1222 else if(!(i % 2) && scoreboard_highlight)
1223 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1224 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1225 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);
1226 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1227 pos.y += 1.25 * hud_fontsize.y;
1229 pos.y += autocvar_scoreboard_border_thickness;
1234 float hud_woulddrawscoreboard_prev;
1235 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1236 void HUD_DrawScoreboard()
1238 float hud_woulddrawscoreboard;
1239 hud_woulddrawscoreboard = scoreboard_active;
1240 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1241 hud_woulddrawscoreboard_change = time;
1242 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1245 if(hud_woulddrawscoreboard) {
1246 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1247 if (scoreboard_fadeinspeed)
1248 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1250 scoreboard_fade_alpha = 1;
1253 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1254 if (scoreboard_fadeoutspeed)
1255 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1257 scoreboard_fade_alpha = 0;
1260 if (!scoreboard_fade_alpha)
1263 HUD_UpdatePlayerTeams();
1265 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1266 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1267 scoreboard_highlight = autocvar_scoreboard_highlight;
1268 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1269 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1270 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1271 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1273 vector rgb, pos, tmp;
1277 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1278 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1280 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1281 ymax = (vid_conheight - ymin);
1283 sbwidth = xmax - xmin;
1285 // Initializes position
1291 vector sb_heading_fontsize;
1292 sb_heading_fontsize = hud_fontsize * 2;
1293 draw_beginBoldFont();
1294 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1297 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1299 // Draw the scoreboard
1300 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1304 vector team_score_baseoffset;
1305 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1306 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1308 if(tm.team == NUM_SPECTATOR)
1311 draw_beginBoldFont();
1312 rgb = Team_ColorRGB(tm.team);
1313 str = ftos(tm.(teamscores[ts_primary]));
1314 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1316 if(ts_primary != ts_secondary)
1318 str = ftos(tm.(teamscores[ts_secondary]));
1319 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);
1323 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1325 rgb.x = autocvar_scoreboard_color_bg_r;
1326 rgb.y = autocvar_scoreboard_color_bg_g;
1327 rgb.z = autocvar_scoreboard_color_bg_b;
1331 rgb.x = autocvar_scoreboard_color_bg_r;
1332 rgb.y = autocvar_scoreboard_color_bg_g;
1333 rgb.z = autocvar_scoreboard_color_bg_b;
1335 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1337 if(tm.team == NUM_SPECTATOR)
1340 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1344 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1345 if(race_speedaward) {
1346 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1347 pos.y += 1.25 * hud_fontsize.y;
1349 if(race_speedaward_alltimebest) {
1350 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1351 pos.y += 1.25 * hud_fontsize.y;
1353 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1355 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1357 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1359 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1364 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1366 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1373 item_size.x = sbwidth;
1374 item_size.y = hud_fontsize.y * 1.25;
1376 for(pl = players.sort_next; pl; pl = pl.sort_next)
1378 if(pl.team != NUM_SPECTATOR)
1380 pos.y += 1.25 * hud_fontsize.y;
1381 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1387 draw_beginBoldFont();
1388 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1390 pos.y += 1.25 * hud_fontsize.y;
1393 // Print info string
1395 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1396 tl = getstatf(STAT_TIMELIMIT);
1397 fl = getstatf(STAT_FRAGLIMIT);
1398 ll = getstatf(STAT_LEADLIMIT);
1399 if(gametype == MAPINFO_TYPE_LMS)
1402 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1407 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1411 str = strcat(str, _(" or"));
1414 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1415 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1416 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1417 TranslateScoresLabel(teamscores_label[ts_primary])));
1421 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1422 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1423 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1424 TranslateScoresLabel(scores_label[ps_primary])));
1429 if(tl > 0 || fl > 0)
1430 str = strcat(str, _(" or"));
1433 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1434 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1435 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1436 TranslateScoresLabel(teamscores_label[ts_primary])));
1440 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1441 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1442 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1443 TranslateScoresLabel(scores_label[ps_primary])));
1448 pos.y += 1.2 * hud_fontsize.y;
1449 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1451 // print information about respawn status
1452 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1456 if(respawn_time < 0)
1458 // a negative number means we are awaiting respawn, time value is still the same
1459 respawn_time *= -1; // remove mark now that we checked it
1460 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1462 str = sprintf(_("^1Respawning in ^3%s^1..."),
1463 (autocvar_scoreboard_respawntime_decimals ?
1464 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1466 count_seconds(respawn_time - time)
1470 else if(time < respawn_time)
1472 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1473 (autocvar_scoreboard_respawntime_decimals ?
1474 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1476 count_seconds(respawn_time - time)
1480 else if(time >= respawn_time)
1481 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1483 pos.y += 1.2 * hud_fontsize.y;
1484 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1487 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;