1 #include "scoreboard.qh"
7 #include "../common/constants.qh"
8 #include "../common/counting.qh"
9 #include "../common/mapinfo.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 " \
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 draw_beginBoldFont();
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;
506 vector hud_field_rgb;
507 string hud_field_icon0;
508 string hud_field_icon1;
509 string hud_field_icon2;
510 vector hud_field_icon0_rgb;
511 vector hud_field_icon1_rgb;
512 vector hud_field_icon2_rgb;
513 float hud_field_icon0_alpha;
514 float hud_field_icon1_alpha;
515 float hud_field_icon2_alpha;
516 string HUD_GetField(entity pl, int field)
518 float tmp, num, denom;
521 hud_field_rgb = '1 1 1';
522 hud_field_icon0 = "";
523 hud_field_icon1 = "";
524 hud_field_icon2 = "";
525 hud_field_icon0_rgb = '1 1 1';
526 hud_field_icon1_rgb = '1 1 1';
527 hud_field_icon2_rgb = '1 1 1';
528 hud_field_icon0_alpha = 1;
529 hud_field_icon1_alpha = 1;
530 hud_field_icon2_alpha = 1;
535 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
536 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
540 tmp = max(0, min(220, f-80)) / 220;
541 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
547 f = pl.ping_packetloss;
548 tmp = pl.ping_movementloss;
549 if(f == 0 && tmp == 0)
551 str = ftos(ceil(f * 100));
553 str = strcat(str, "~", ftos(ceil(tmp * 100)));
554 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
555 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
559 if(ready_waiting && pl.ready)
561 hud_field_icon0 = "gfx/scoreboard/player_ready";
565 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
567 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
568 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
569 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
570 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
571 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
574 return GetPlayerName(pl.sv_entnum);
577 f = pl.(scores[SP_KILLS]);
578 f -= pl.(scores[SP_SUICIDES]);
582 num = pl.(scores[SP_KILLS]);
583 denom = pl.(scores[SP_DEATHS]);
586 hud_field_rgb = '0 1 0';
587 str = sprintf("%d", num);
588 } else if(num <= 0) {
589 hud_field_rgb = '1 0 0';
590 str = sprintf("%.1f", num/denom);
592 str = sprintf("%.1f", num/denom);
596 f = pl.(scores[SP_KILLS]);
597 f -= pl.(scores[SP_DEATHS]);
600 hud_field_rgb = '0 1 0';
602 hud_field_rgb = '1 1 1';
604 hud_field_rgb = '1 0 0';
609 tmp = pl.(scores[field]);
610 f = scores_flags[field];
611 if(field == ps_primary)
612 hud_field_rgb = '1 1 0';
613 else if(field == ps_secondary)
614 hud_field_rgb = '0 1 1';
616 hud_field_rgb = '1 1 1';
617 return ScoreString(f, tmp);
622 float hud_fixscoreboardcolumnwidth_len;
623 float hud_fixscoreboardcolumnwidth_iconlen;
624 float hud_fixscoreboardcolumnwidth_marginlen;
626 string HUD_FixScoreboardColumnWidth(int i, string str)
630 field = hud_field[i];
632 hud_fixscoreboardcolumnwidth_iconlen = 0;
634 if(hud_field_icon0 != "")
636 sz = draw_getimagesize(hud_field_icon0);
638 if(hud_fixscoreboardcolumnwidth_iconlen < f)
639 hud_fixscoreboardcolumnwidth_iconlen = f;
642 if(hud_field_icon1 != "")
644 sz = draw_getimagesize(hud_field_icon1);
646 if(hud_fixscoreboardcolumnwidth_iconlen < f)
647 hud_fixscoreboardcolumnwidth_iconlen = f;
650 if(hud_field_icon2 != "")
652 sz = draw_getimagesize(hud_field_icon2);
654 if(hud_fixscoreboardcolumnwidth_iconlen < f)
655 hud_fixscoreboardcolumnwidth_iconlen = f;
658 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
660 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
661 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
663 hud_fixscoreboardcolumnwidth_marginlen = 0;
665 if(field == SP_NAME) // name gets all remaining space
669 namesize = sbwidth;// / hud_fontsize_x;
670 for(j = 0; j < hud_num_fields; ++j)
672 if (hud_field[i] != SP_SEPARATOR)
673 namesize -= hud_size[j] + hud_fontsize.x;
674 namesize += hud_fontsize.x;
675 hud_size[i] = namesize;
677 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
678 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
679 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
680 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
683 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
685 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
692 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
695 rgb = Team_ColorRGB(pl.team);
699 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
701 if((rgb == '1 1 1') && (!is_spec)) {
702 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
703 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
704 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
706 vector h_pos = pos - '1 1 0';
707 vector h_size = item_size + '2 0 0';
708 // alternated rows highlighting
710 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
711 else if((scoreboard_highlight) && (!(pl_number % 2)))
712 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
718 for(i = 0; i < hud_num_fields; ++i)
720 field = hud_field[i];
721 if(field == SP_SEPARATOR)
724 if(is_spec && field != SP_NAME && field != SP_PING) {
725 pos.x += hud_size[i] + hud_fontsize.x;
728 str = HUD_GetField(pl, field);
729 str = HUD_FixScoreboardColumnWidth(i, str);
731 pos.x += hud_size[i] + hud_fontsize.x;
733 if(field == SP_NAME) {
734 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
736 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
738 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
740 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
742 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
744 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
747 tmp.x = hud_size[i] + hud_fontsize.x;
748 if(hud_field_icon0 != "")
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_self, DRAWFLAG_NORMAL);
752 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);
753 if(hud_field_icon1 != "")
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_self, DRAWFLAG_NORMAL);
757 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);
758 if(hud_field_icon2 != "")
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_self, DRAWFLAG_NORMAL);
762 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);
765 if(hud_field[i] == SP_SEPARATOR)
768 for(i = hud_num_fields-1; i > 0; --i)
770 field = hud_field[i];
771 if(field == SP_SEPARATOR)
774 if(is_spec && field != SP_NAME && field != SP_PING) {
775 pos.x -= hud_size[i] + hud_fontsize.x;
779 str = HUD_GetField(pl, field);
780 str = HUD_FixScoreboardColumnWidth(i, str);
782 if(field == SP_NAME) {
783 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
785 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
787 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
789 tmp.x = hud_fixscoreboardcolumnwidth_len;
791 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
793 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
797 if(hud_field_icon0 != "")
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_self, DRAWFLAG_NORMAL);
801 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);
802 if(hud_field_icon1 != "")
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_self, DRAWFLAG_NORMAL);
806 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);
807 if(hud_field_icon2 != "")
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_self, DRAWFLAG_NORMAL);
811 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);
812 pos.x -= hud_size[i] + hud_fontsize.x;
817 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
821 * HUD_Scoreboard_MakeTable
823 * Makes a table for a team (for all playing players in DM) and fills it
826 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
828 float body_table_height;
829 vector tmp = '0 0 0', column_dim = '0 0 0';
832 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
834 pos.y += autocvar_scoreboard_border_thickness;
838 tmp.y = 1.25 * hud_fontsize.y;
842 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
844 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
847 tmp.y += autocvar_scoreboard_border_thickness;
848 tmp.y += body_table_height;
849 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
851 // separator header/table
852 pos.y += 1.25 * hud_fontsize.y;
853 tmp.y = autocvar_scoreboard_border_thickness;
854 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
856 pos.y += autocvar_scoreboard_border_thickness;
859 tmp.y = body_table_height;
861 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
863 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
865 // anyway, apply some color
866 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
868 // go back to the top to make alternated columns highlighting and to print the strings
869 pos.y -= 1.25 * hud_fontsize.y;
870 pos.y -= autocvar_scoreboard_border_thickness;
874 if (scoreboard_highlight)
876 column_dim.y = 1.25 * hud_fontsize.y; // header
877 column_dim.y += autocvar_scoreboard_border_thickness;
878 column_dim.y += body_table_height;
881 // print the strings of the columns headers and draw the columns
882 draw_beginBoldFont();
884 for(i = 0; i < hud_num_fields; ++i)
886 if(hud_field[i] == SP_SEPARATOR)
888 column_dim.x = hud_size[i] + hud_fontsize.x;
889 if (scoreboard_highlight)
892 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
894 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
895 pos.x += column_dim.x;
897 if(hud_field[i] == SP_SEPARATOR)
901 for(i = hud_num_fields-1; i > 0; --i)
903 if(hud_field[i] == SP_SEPARATOR)
906 pos.x -= hud_size[i];
908 if (scoreboard_highlight)
912 if (i == hud_num_fields-1)
913 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
915 column_dim.x = hud_size[i] + hud_fontsize.x;
916 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
920 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
921 tmp.x = (hud_size[i] - tmp.x);
922 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
923 pos.x -= hud_fontsize.x;
929 pos.y += 1.25 * hud_fontsize.y; // skip the header
930 pos.y += autocvar_scoreboard_border_thickness;
934 tmp.y = hud_fontsize.y * 1.25;
936 // fill the table and draw the rows
939 for(pl = players.sort_next; pl; pl = pl.sort_next)
941 if(pl.team != tm.team)
943 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
944 pos.y += 1.25 * hud_fontsize.y;
948 for(pl = players.sort_next; pl; pl = pl.sort_next)
950 if(pl.team == NUM_SPECTATOR)
952 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
953 pos.y += 1.25 * hud_fontsize.y;
958 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
959 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
964 float HUD_WouldDrawScoreboard() {
965 if (autocvar__hud_configure)
967 else if (QuickMenu_IsOpened())
969 else if (scoreboard_showscores)
971 else if (intermission == 1)
973 else if (intermission == 2)
975 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
977 else if (scoreboard_showscores_force)
982 float average_accuracy;
983 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
986 int weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
988 if(autocvar_scoreboard_accuracy_doublerows)
993 float fontsize = height * 1/3;
994 float weapon_height = height * 2/3;
995 float weapon_width = sbwidth / weapon_cnt;
996 float g_instagib = 0;
998 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
999 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1000 vector tmp = '0 0 0';
1002 tmp.y = height * rows;
1005 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1007 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1008 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1010 // column highlighting
1011 for(i = 0; i < weapon_cnt/rows; ++i)
1014 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);
1018 for(i = 0; i < rows; ++i)
1020 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);
1023 average_accuracy = 0;
1024 float weapons_with_stats;
1025 weapons_with_stats = 0;
1027 pos.x += weapon_width / 2;
1029 if(switchweapon == WEP_VAPORIZER)
1030 g_instagib = 1; // TODO: real detection for instagib?
1033 if(autocvar_scoreboard_accuracy_nocolors)
1036 Accuracy_LoadColors();
1038 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1040 self = get_weaponinfo(i);
1043 if ((i == WEP_VORTEX && g_instagib) || i == WEP_PORTO || (i == WEP_VAPORIZER && !g_instagib) || i == WEP_TUBA) // skip port-o-launch, vortex || vaporizer and tuba
1045 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1048 if(weapon_stats >= 0)
1049 weapon_alpha = scoreboard_alpha_fg;
1051 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1054 drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1056 if(weapon_stats >= 0) {
1057 weapons_with_stats += 1;
1058 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1061 s = sprintf("%d%%", weapon_stats*100);
1064 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1066 if(!autocvar_scoreboard_accuracy_nocolors)
1067 rgb = Accuracy_GetColor(weapon_stats);
1069 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1071 pos.x += weapon_width * rows;
1072 if(rows == 2 && i == 6) {
1078 if(weapons_with_stats)
1079 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1082 pos.x -= weapon_width / 2;
1086 pos.y += 1.25 * hud_fontsize.y;
1090 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1092 pos.x += hud_fontsize.x * 0.25;
1093 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1094 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1095 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1097 pos.y+= hud_fontsize.y;
1102 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1103 float stat_secrets_found, stat_secrets_total;
1104 float stat_monsters_killed, stat_monsters_total;
1108 // get monster stats
1109 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1110 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1112 // get secrets stats
1113 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1114 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1116 // get number of rows
1117 if(stat_secrets_total)
1119 if(stat_monsters_total)
1122 // if no rows, return
1126 // draw table header
1127 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1128 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1131 vector tmp = '0 0 0';
1133 tmp.y = hud_fontsize.y * rows;
1136 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1138 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1139 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1142 if(stat_monsters_total)
1144 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1145 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1149 if(stat_secrets_total)
1151 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1152 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1156 pos.y += 1.25 * hud_fontsize.y;
1161 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1164 RANKINGS_RECEIVED_CNT = 0;
1165 for (i=RANKINGS_CNT-1; i>=0; --i)
1167 ++RANKINGS_RECEIVED_CNT;
1169 if (RANKINGS_RECEIVED_CNT == 0)
1173 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1175 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1176 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1177 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1179 pos.y += hud_fontsize.y;
1180 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1181 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1182 vector tmp = '0 0 0';
1184 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1187 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1189 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1190 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1193 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1200 n = grecordholder[i];
1201 p = count_ordinal(i+1);
1202 if(grecordholder[i] == GetPlayerName(player_localnum))
1203 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1204 else if(!(i % 2) && scoreboard_highlight)
1205 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1206 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1207 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);
1208 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1209 pos.y += 1.25 * hud_fontsize.y;
1211 pos.y += autocvar_scoreboard_border_thickness;
1216 float hud_woulddrawscoreboard_prev;
1217 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1218 void HUD_DrawScoreboard()
1220 float hud_woulddrawscoreboard;
1221 hud_woulddrawscoreboard = scoreboard_active;
1222 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1223 hud_woulddrawscoreboard_change = time;
1224 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1227 if(hud_woulddrawscoreboard) {
1228 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1229 if (scoreboard_fadeinspeed)
1230 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1232 scoreboard_fade_alpha = 1;
1235 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1236 if (scoreboard_fadeoutspeed)
1237 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1239 scoreboard_fade_alpha = 0;
1242 if (!scoreboard_fade_alpha)
1245 HUD_UpdatePlayerTeams();
1247 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1248 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1249 scoreboard_highlight = autocvar_scoreboard_highlight;
1250 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1251 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1252 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1253 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1255 vector rgb, pos, tmp;
1259 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1260 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1262 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1263 ymax = (vid_conheight - ymin);
1265 sbwidth = xmax - xmin;
1267 // Initializes position
1273 vector sb_heading_fontsize;
1274 sb_heading_fontsize = hud_fontsize * 2;
1275 draw_beginBoldFont();
1276 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1279 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1281 // Draw the scoreboard
1282 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1286 vector team_score_baseoffset;
1287 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1288 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1290 if(tm.team == NUM_SPECTATOR)
1293 draw_beginBoldFont();
1294 rgb = Team_ColorRGB(tm.team);
1295 str = ftos(tm.(teamscores[ts_primary]));
1296 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1298 if(ts_primary != ts_secondary)
1300 str = ftos(tm.(teamscores[ts_secondary]));
1301 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);
1305 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1307 rgb.x = autocvar_scoreboard_color_bg_r;
1308 rgb.y = autocvar_scoreboard_color_bg_g;
1309 rgb.z = autocvar_scoreboard_color_bg_b;
1313 rgb.x = autocvar_scoreboard_color_bg_r;
1314 rgb.y = autocvar_scoreboard_color_bg_g;
1315 rgb.z = autocvar_scoreboard_color_bg_b;
1317 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1319 if(tm.team == NUM_SPECTATOR)
1322 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1326 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1327 if(race_speedaward) {
1328 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1329 pos.y += 1.25 * hud_fontsize.y;
1331 if(race_speedaward_alltimebest) {
1332 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1333 pos.y += 1.25 * hud_fontsize.y;
1335 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1337 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1339 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1341 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1346 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1348 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1355 item_size.x = sbwidth;
1356 item_size.y = hud_fontsize.y * 1.25;
1358 for(pl = players.sort_next; pl; pl = pl.sort_next)
1360 if(pl.team != NUM_SPECTATOR)
1362 pos.y += 1.25 * hud_fontsize.y;
1363 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1369 draw_beginBoldFont();
1370 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1372 pos.y += 1.25 * hud_fontsize.y;
1375 // Print info string
1377 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1378 tl = getstatf(STAT_TIMELIMIT);
1379 fl = getstatf(STAT_FRAGLIMIT);
1380 ll = getstatf(STAT_LEADLIMIT);
1381 if(gametype == MAPINFO_TYPE_LMS)
1384 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1389 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1393 str = strcat(str, _(" or"));
1396 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1397 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1398 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1399 TranslateScoresLabel(teamscores_label[ts_primary])));
1403 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1404 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1405 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1406 TranslateScoresLabel(scores_label[ps_primary])));
1411 if(tl > 0 || fl > 0)
1412 str = strcat(str, _(" or"));
1415 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1416 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1417 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1418 TranslateScoresLabel(teamscores_label[ts_primary])));
1422 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1423 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1424 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1425 TranslateScoresLabel(scores_label[ps_primary])));
1430 pos.y += 1.2 * hud_fontsize.y;
1431 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1433 // print information about respawn status
1434 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1438 if(respawn_time < 0)
1440 // a negative number means we are awaiting respawn, time value is still the same
1441 respawn_time *= -1; // remove mark now that we checked it
1442 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1444 str = sprintf(_("^1Respawning in ^3%s^1..."),
1445 (autocvar_scoreboard_respawntime_decimals ?
1446 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1448 count_seconds(respawn_time - time)
1452 else if(time < respawn_time)
1454 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1455 (autocvar_scoreboard_respawntime_decimals ?
1456 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1458 count_seconds(respawn_time - time)
1462 else if(time >= respawn_time)
1463 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1465 pos.y += 1.2 * hud_fontsize.y;
1466 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1469 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;