1 #include "scoreboard.qh"
3 #include "autocvars.qh"
7 #include "miscfunctions.qh"
10 #include "../common/constants.qh"
11 #include "../common/counting.qh"
12 #include "../common/mapinfo.qh"
13 #include "../common/stats.qh"
14 #include "../common/teams.qh"
15 #include "../common/util.qh"
17 float scoreboard_alpha_bg;
18 float scoreboard_alpha_fg;
19 float scoreboard_highlight;
20 float scoreboard_highlight_alpha;
21 float scoreboard_highlight_alpha_self;
22 float scoreboard_alpha_name;
23 float scoreboard_alpha_name_self;
25 void drawstringright(vector, string, vector, vector, float, float);
26 void drawstringcenter(vector, string, vector, vector, float, float);
28 const float SCOREBOARD_OFFSET = 50;
30 // wrapper to put all possible scores titles through gettext
31 string TranslateScoresLabel(string l)
35 case "bckills": return CTX(_("SCO^bckills"));
36 case "bctime": return CTX(_("SCO^bctime"));
37 case "caps": return CTX(_("SCO^caps"));
38 case "captime": return CTX(_("SCO^captime"));
39 case "deaths": return CTX(_("SCO^deaths"));
40 case "destroyed": return CTX(_("SCO^destroyed"));
41 case "drops": return CTX(_("SCO^drops"));
42 case "faults": return CTX(_("SCO^faults"));
43 case "fckills": return CTX(_("SCO^fckills"));
44 case "goals": return CTX(_("SCO^goals"));
45 case "kckills": return CTX(_("SCO^kckills"));
46 case "kdratio": return CTX(_("SCO^kdratio"));
47 case "k/d": return CTX(_("SCO^k/d"));
48 case "kd": return CTX(_("SCO^kd"));
49 case "kdr": return CTX(_("SCO^kdr"));
50 case "kills": return CTX(_("SCO^kills"));
51 case "laps": return CTX(_("SCO^laps"));
52 case "lives": return CTX(_("SCO^lives"));
53 case "losses": return CTX(_("SCO^losses"));
54 case "name": return CTX(_("SCO^name"));
55 case "sum": return CTX(_("SCO^sum"));
56 case "nick": return CTX(_("SCO^nick"));
57 case "objectives": return CTX(_("SCO^objectives"));
58 case "pickups": return CTX(_("SCO^pickups"));
59 case "ping": return CTX(_("SCO^ping"));
60 case "pl": return CTX(_("SCO^pl"));
61 case "pushes": return CTX(_("SCO^pushes"));
62 case "rank": return CTX(_("SCO^rank"));
63 case "returns": return CTX(_("SCO^returns"));
64 case "revivals": return CTX(_("SCO^revivals"));
65 case "score": return CTX(_("SCO^score"));
66 case "suicides": return CTX(_("SCO^suicides"));
67 case "takes": return CTX(_("SCO^takes"));
68 case "ticks": return CTX(_("SCO^ticks"));
77 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
78 for(i = 0; i < MAX_SCORE; ++i)
80 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
81 if(f == SFL_SORT_PRIO_PRIMARY)
83 if(f == SFL_SORT_PRIO_SECONDARY)
86 if(ps_secondary == -1)
87 ps_secondary = ps_primary;
89 for(i = 0; i < MAX_TEAMSCORE; ++i)
91 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
92 if(f == SFL_SORT_PRIO_PRIMARY)
94 if(f == SFL_SORT_PRIO_SECONDARY)
97 if(ts_secondary == -1)
98 ts_secondary = ts_primary;
100 Cmd_HUD_SetFields(0);
103 float SetTeam(entity pl, float Team);
105 void HUD_UpdatePlayerTeams()
112 for(pl = players.sort_next; pl; pl = pl.sort_next)
115 Team = GetPlayerColor(pl.sv_entnum);
116 if(SetTeam(pl, Team))
119 HUD_UpdatePlayerPos(pl);
123 pl = players.sort_next;
128 print(strcat("PNUM: ", ftos(num), "\n"));
133 int HUD_CompareScore(float vl, float vr, int f)
135 if(f & SFL_ZERO_IS_WORST)
137 if(vl == 0 && vr != 0)
139 if(vl != 0 && vr == 0)
143 return IS_INCREASING(f);
145 return IS_DECREASING(f);
149 float HUD_ComparePlayerScores(entity left, entity right)
152 vl = GetPlayerColor(left.sv_entnum);
153 vr = GetPlayerColor(right.sv_entnum);
165 if(vl == NUM_SPECTATOR)
167 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
169 if(!left.gotscores && right.gotscores)
174 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
178 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
183 for(i = 0; i < MAX_SCORE; ++i)
185 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
190 if (left.sv_entnum < right.sv_entnum)
196 void HUD_UpdatePlayerPos(entity player)
198 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
200 SORT_SWAP(player, other);
202 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
204 SORT_SWAP(other, player);
208 float HUD_CompareTeamScores(entity left, entity right)
212 if(left.team == NUM_SPECTATOR)
214 if(right.team == NUM_SPECTATOR)
217 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
221 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
225 for(i = 0; i < MAX_SCORE; ++i)
227 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
232 if (left.team < right.team)
238 void HUD_UpdateTeamPos(entity Team)
240 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
242 SORT_SWAP(Team, other);
244 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
246 SORT_SWAP(other, Team);
252 print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
253 print(_("^3|---------------------------------------------------------------|\n"));
254 print(_("Usage:\n"));
255 print(_("^2scoreboard_columns_set default\n"));
256 print(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
257 print(_("The following field names are recognized (case insensitive):\n"));
258 print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
260 print(_("^3name^7 or ^3nick^7 Name of a player\n"));
261 print(_("^3ping^7 Ping time\n"));
262 print(_("^3pl^7 Packet loss\n"));
263 print(_("^3kills^7 Number of kills\n"));
264 print(_("^3deaths^7 Number of deaths\n"));
265 print(_("^3suicides^7 Number of suicides\n"));
266 print(_("^3frags^7 kills - suicides\n"));
267 print(_("^3kd^7 The kill-death ratio\n"));
268 print(_("^3sum^7 frags - deaths\n"));
269 print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
270 print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
271 print(_("^3captime^7 Time of fastest cap (CTF)\n"));
272 print(_("^3fckills^7 Number of flag carrier kills\n"));
273 print(_("^3returns^7 Number of flag returns\n"));
274 print(_("^3drops^7 Number of flag drops\n"));
275 print(_("^3lives^7 Number of lives (LMS)\n"));
276 print(_("^3rank^7 Player rank\n"));
277 print(_("^3pushes^7 Number of players pushed into void\n"));
278 print(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
279 print(_("^3kckills^7 Number of keys carrier kills\n"));
280 print(_("^3losses^7 Number of times a key was lost\n"));
281 print(_("^3laps^7 Number of laps finished (race/cts)\n"));
282 print(_("^3time^7 Total time raced (race/cts)\n"));
283 print(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
284 print(_("^3ticks^7 Number of ticks (DOM)\n"));
285 print(_("^3takes^7 Number of domination points taken (DOM)\n"));
286 print(_("^3bckills^7 Number of ball carrier kills\n"));
287 print(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
288 print(_("^3score^7 Total score\n\n"));
290 print(_("Before a field you can put a + or - sign, then a comma separated list\n"
291 "of game types, then a slash, to make the field show up only in these\n"
292 "or in all but these game types. You can also specify 'all' as a\n"
293 "field to show all fields available for the current game mode.\n\n"));
295 print(_("The special game type names 'teams' and 'noteams' can be used to\n"
296 "include/exclude ALL teams/noteams game modes.\n\n"));
298 print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
299 print(_("will display name, ping and pl aligned to the left, and the fields\n"
300 "right of the vertical bar aligned to the right.\n"));
301 print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
302 "other gamemodes except DM.\n"));
305 #define HUD_DefaultColumnLayout() \
307 "-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" */ \
308 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \
309 "+lms/lives +lms/rank " \
310 "+kh/caps +kh/pushes +kh/destroyed " \
311 "?+race/laps ?+race/time ?+race/fastest " \
312 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
315 void Cmd_HUD_SetFields(float argc)
319 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
324 // set up a temporary scoreboard layout
325 // no layout can be properly set up until score_info data haven't been received
326 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
328 scores_label[ps_primary] = strzone("score");
329 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
332 // TODO: re enable with gametype dependant cvars?
333 if(argc < 3) // no arguments provided
334 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
337 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
341 if(argv(2) == "default")
342 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
343 else if(argv(2) == "all")
346 s = "ping pl name |";
347 for(i = 0; i < MAX_SCORE; ++i)
350 if(i != ps_secondary)
351 if(scores_label[i] != "")
352 s = strcat(s, " ", scores_label[i]);
354 if(ps_secondary != ps_primary)
355 s = strcat(s, " ", scores_label[ps_secondary]);
356 s = strcat(s, " ", scores_label[ps_primary]);
357 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
364 hud_fontsize = HUD_GetFontsize("hud_fontsize");
366 draw_beginBoldFont();
367 for(i = 1; i < argc - 1; ++i)
373 if(substring(str, 0, 1) == "?")
376 str = substring(str, 1, strlen(str) - 1);
379 slash = strstrofs(str, "/", 0);
382 pattern = substring(str, 0, slash);
383 str = substring(str, slash + 1, strlen(str) - (slash + 1));
385 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
389 strunzone(hud_title[hud_num_fields]);
390 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
391 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
392 str = strtolower(str);
395 hud_field[hud_num_fields] = SP_PING;
396 } else if(str == "pl") {
397 hud_field[hud_num_fields] = SP_PL;
398 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
399 hud_field[hud_num_fields] = SP_KDRATIO;
400 } else if(str == "sum" || str == "diff" || str == "k-d") {
401 hud_field[hud_num_fields] = SP_SUM;
402 } else if(str == "name" || str == "nick") {
403 hud_field[hud_num_fields] = SP_NAME;
405 } else if(str == "|") {
406 hud_field[hud_num_fields] = SP_SEPARATOR;
409 for(j = 0; j < MAX_SCORE; ++j)
410 if(str == strtolower(scores_label[j]))
411 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
420 printf("^1Error:^7 Unknown score field: '%s'\n", str);
424 hud_field[hud_num_fields] = j;
427 if(j == ps_secondary)
431 if(hud_num_fields >= MAX_HUD_FIELDS)
435 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
437 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
439 if(ps_primary == ps_secondary)
441 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
443 if(hud_num_fields+missing < MAX_HUD_FIELDS)
447 strunzone(hud_title[hud_num_fields]);
448 for(i = hud_num_fields; i > 0; --i)
450 hud_title[i] = hud_title[i-1];
451 hud_size[i] = hud_size[i-1];
452 hud_field[i] = hud_field[i-1];
454 hud_title[0] = strzone(TranslateScoresLabel("name"));
455 hud_field[0] = SP_NAME;
457 print("fixed missing field 'name'\n");
461 strunzone(hud_title[hud_num_fields]);
462 for(i = hud_num_fields; i > 1; --i)
464 hud_title[i] = hud_title[i-1];
465 hud_size[i] = hud_size[i-1];
466 hud_field[i] = hud_field[i-1];
468 hud_title[1] = strzone("|");
469 hud_field[1] = SP_SEPARATOR;
470 hud_size[1] = stringwidth("|", false, hud_fontsize);
472 print("fixed missing field '|'\n");
475 else if(!have_separator)
477 strunzone(hud_title[hud_num_fields]);
478 hud_title[hud_num_fields] = strzone("|");
479 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
480 hud_field[hud_num_fields] = SP_SEPARATOR;
482 print("fixed missing field '|'\n");
486 strunzone(hud_title[hud_num_fields]);
487 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
488 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
489 hud_field[hud_num_fields] = ps_secondary;
491 printf("fixed missing field '%s'\n", scores_label[ps_secondary]);
495 strunzone(hud_title[hud_num_fields]);
496 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
497 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
498 hud_field[hud_num_fields] = ps_primary;
500 printf("fixed missing field '%s'\n", scores_label[ps_primary]);
504 hud_field[hud_num_fields] = SP_END;
509 vector hud_field_rgb;
510 string hud_field_icon0;
511 string hud_field_icon1;
512 string hud_field_icon2;
513 vector hud_field_icon0_rgb;
514 vector hud_field_icon1_rgb;
515 vector hud_field_icon2_rgb;
516 float hud_field_icon0_alpha;
517 float hud_field_icon1_alpha;
518 float hud_field_icon2_alpha;
519 string HUD_GetField(entity pl, int field)
521 float tmp, num, denom;
524 hud_field_rgb = '1 1 1';
525 hud_field_icon0 = "";
526 hud_field_icon1 = "";
527 hud_field_icon2 = "";
528 hud_field_icon0_rgb = '1 1 1';
529 hud_field_icon1_rgb = '1 1 1';
530 hud_field_icon2_rgb = '1 1 1';
531 hud_field_icon0_alpha = 1;
532 hud_field_icon1_alpha = 1;
533 hud_field_icon2_alpha = 1;
538 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
539 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
543 tmp = max(0, min(220, f-80)) / 220;
544 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
550 f = pl.ping_packetloss;
551 tmp = pl.ping_movementloss;
552 if(f == 0 && tmp == 0)
554 str = ftos(ceil(f * 100));
556 str = strcat(str, "~", ftos(ceil(tmp * 100)));
557 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
558 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
562 if(ready_waiting && pl.ready)
564 hud_field_icon0 = "gfx/scoreboard/player_ready";
568 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
570 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
571 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
572 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
573 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
574 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
577 return GetPlayerName(pl.sv_entnum);
580 f = pl.(scores[SP_KILLS]);
581 f -= pl.(scores[SP_SUICIDES]);
585 num = pl.(scores[SP_KILLS]);
586 denom = pl.(scores[SP_DEATHS]);
589 hud_field_rgb = '0 1 0';
590 str = sprintf("%d", num);
591 } else if(num <= 0) {
592 hud_field_rgb = '1 0 0';
593 str = sprintf("%.1f", num/denom);
595 str = sprintf("%.1f", num/denom);
599 f = pl.(scores[SP_KILLS]);
600 f -= pl.(scores[SP_DEATHS]);
603 hud_field_rgb = '0 1 0';
605 hud_field_rgb = '1 1 1';
607 hud_field_rgb = '1 0 0';
612 tmp = pl.(scores[field]);
613 f = scores_flags[field];
614 if(field == ps_primary)
615 hud_field_rgb = '1 1 0';
616 else if(field == ps_secondary)
617 hud_field_rgb = '0 1 1';
619 hud_field_rgb = '1 1 1';
620 return ScoreString(f, tmp);
625 float hud_fixscoreboardcolumnwidth_len;
626 float hud_fixscoreboardcolumnwidth_iconlen;
627 float hud_fixscoreboardcolumnwidth_marginlen;
629 string HUD_FixScoreboardColumnWidth(int i, string str)
633 field = hud_field[i];
635 hud_fixscoreboardcolumnwidth_iconlen = 0;
637 if(hud_field_icon0 != "")
639 sz = draw_getimagesize(hud_field_icon0);
641 if(hud_fixscoreboardcolumnwidth_iconlen < f)
642 hud_fixscoreboardcolumnwidth_iconlen = f;
645 if(hud_field_icon1 != "")
647 sz = draw_getimagesize(hud_field_icon1);
649 if(hud_fixscoreboardcolumnwidth_iconlen < f)
650 hud_fixscoreboardcolumnwidth_iconlen = f;
653 if(hud_field_icon2 != "")
655 sz = draw_getimagesize(hud_field_icon2);
657 if(hud_fixscoreboardcolumnwidth_iconlen < f)
658 hud_fixscoreboardcolumnwidth_iconlen = f;
661 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
663 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
664 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
666 hud_fixscoreboardcolumnwidth_marginlen = 0;
668 if(field == SP_NAME) // name gets all remaining space
672 namesize = sbwidth;// / hud_fontsize_x;
673 for(j = 0; j < hud_num_fields; ++j)
675 if (hud_field[i] != SP_SEPARATOR)
676 namesize -= hud_size[j] + hud_fontsize.x;
677 namesize += hud_fontsize.x;
678 hud_size[i] = namesize;
680 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
681 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
682 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
683 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
686 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
688 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
695 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
698 rgb = Team_ColorRGB(pl.team);
702 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
704 if((rgb == '1 1 1') && (!is_spec)) {
705 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
706 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
707 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
709 vector h_pos = pos - '1 1 0';
710 vector h_size = item_size + '2 0 0';
711 // alternated rows highlighting
713 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
714 else if((scoreboard_highlight) && (!(pl_number % 2)))
715 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
721 for(i = 0; i < hud_num_fields; ++i)
723 field = hud_field[i];
724 if(field == SP_SEPARATOR)
727 if(is_spec && field != SP_NAME && field != SP_PING) {
728 pos.x += hud_size[i] + hud_fontsize.x;
731 str = HUD_GetField(pl, field);
732 str = HUD_FixScoreboardColumnWidth(i, str);
734 pos.x += hud_size[i] + hud_fontsize.x;
736 if(field == SP_NAME) {
737 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
739 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
741 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
743 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
745 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
747 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
750 tmp.x = hud_size[i] + hud_fontsize.x;
751 if(hud_field_icon0 != "")
753 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);
755 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);
756 if(hud_field_icon1 != "")
758 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);
760 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);
761 if(hud_field_icon2 != "")
763 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);
765 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);
768 if(hud_field[i] == SP_SEPARATOR)
771 for(i = hud_num_fields-1; i > 0; --i)
773 field = hud_field[i];
774 if(field == SP_SEPARATOR)
777 if(is_spec && field != SP_NAME && field != SP_PING) {
778 pos.x -= hud_size[i] + hud_fontsize.x;
782 str = HUD_GetField(pl, field);
783 str = HUD_FixScoreboardColumnWidth(i, str);
785 if(field == SP_NAME) {
786 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
788 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
790 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
792 tmp.x = hud_fixscoreboardcolumnwidth_len;
794 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
796 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
800 if(hud_field_icon0 != "")
802 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);
804 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);
805 if(hud_field_icon1 != "")
807 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);
809 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);
810 if(hud_field_icon2 != "")
812 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);
814 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);
815 pos.x -= hud_size[i] + hud_fontsize.x;
820 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
824 * HUD_Scoreboard_MakeTable
826 * Makes a table for a team (for all playing players in DM) and fills it
829 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
831 float body_table_height;
832 vector tmp = '0 0 0', column_dim = '0 0 0';
835 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
837 pos.y += autocvar_scoreboard_border_thickness;
841 tmp.y = 1.25 * hud_fontsize.y;
845 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
847 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
850 tmp.y += autocvar_scoreboard_border_thickness;
851 tmp.y += body_table_height;
852 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
854 // separator header/table
855 pos.y += 1.25 * hud_fontsize.y;
856 tmp.y = autocvar_scoreboard_border_thickness;
857 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
859 pos.y += autocvar_scoreboard_border_thickness;
862 tmp.y = body_table_height;
864 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
866 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
868 // anyway, apply some color
869 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
871 // go back to the top to make alternated columns highlighting and to print the strings
872 pos.y -= 1.25 * hud_fontsize.y;
873 pos.y -= autocvar_scoreboard_border_thickness;
877 if (scoreboard_highlight)
879 column_dim.y = 1.25 * hud_fontsize.y; // header
880 column_dim.y += autocvar_scoreboard_border_thickness;
881 column_dim.y += body_table_height;
884 // print the strings of the columns headers and draw the columns
885 draw_beginBoldFont();
887 for(i = 0; i < hud_num_fields; ++i)
889 if(hud_field[i] == SP_SEPARATOR)
891 column_dim.x = hud_size[i] + hud_fontsize.x;
892 if (scoreboard_highlight)
895 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
897 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
898 pos.x += column_dim.x;
900 if(hud_field[i] == SP_SEPARATOR)
904 for(i = hud_num_fields-1; i > 0; --i)
906 if(hud_field[i] == SP_SEPARATOR)
909 pos.x -= hud_size[i];
911 if (scoreboard_highlight)
915 if (i == hud_num_fields-1)
916 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
918 column_dim.x = hud_size[i] + hud_fontsize.x;
919 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
923 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
924 tmp.x = (hud_size[i] - tmp.x);
925 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
926 pos.x -= hud_fontsize.x;
932 pos.y += 1.25 * hud_fontsize.y; // skip the header
933 pos.y += autocvar_scoreboard_border_thickness;
937 tmp.y = hud_fontsize.y * 1.25;
939 // fill the table and draw the rows
942 for(pl = players.sort_next; pl; pl = pl.sort_next)
944 if(pl.team != tm.team)
946 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
947 pos.y += 1.25 * hud_fontsize.y;
951 for(pl = players.sort_next; pl; pl = pl.sort_next)
953 if(pl.team == NUM_SPECTATOR)
955 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
956 pos.y += 1.25 * hud_fontsize.y;
961 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
962 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
967 float HUD_WouldDrawScoreboard() {
968 if (autocvar__hud_configure)
970 else if (scoreboard_showscores)
972 else if (intermission == 1)
974 else if (intermission == 2)
976 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
978 else if (scoreboard_showscores_force)
983 float average_accuracy;
984 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
987 int weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
989 if(autocvar_scoreboard_accuracy_doublerows)
994 float fontsize = height * 1/3;
995 float weapon_height = height * 2/3;
996 float weapon_width = sbwidth / weapon_cnt;
997 float g_instagib = 0;
999 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1000 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1001 vector tmp = '0 0 0';
1003 tmp.y = height * rows;
1006 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1008 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1009 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1011 // column highlighting
1012 for(i = 0; i < weapon_cnt/rows; ++i)
1015 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);
1019 for(i = 0; i < rows; ++i)
1021 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);
1024 average_accuracy = 0;
1025 float weapons_with_stats;
1026 weapons_with_stats = 0;
1028 pos.x += weapon_width / 2;
1030 if(switchweapon == WEP_VAPORIZER)
1031 g_instagib = 1; // TODO: real detection for instagib?
1034 if(autocvar_scoreboard_accuracy_nocolors)
1037 Accuracy_LoadColors();
1039 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1041 self = get_weaponinfo(i);
1044 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
1046 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1049 if(weapon_stats >= 0)
1050 weapon_alpha = scoreboard_alpha_fg;
1052 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1055 drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1057 if(weapon_stats >= 0) {
1058 weapons_with_stats += 1;
1059 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1062 s = sprintf("%d%%", weapon_stats*100);
1065 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1067 if(!autocvar_scoreboard_accuracy_nocolors)
1068 rgb = Accuracy_GetColor(weapon_stats);
1070 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1072 pos.x += weapon_width * rows;
1073 if(rows == 2 && i == 6) {
1079 if(weapons_with_stats)
1080 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1083 pos.x -= weapon_width / 2;
1087 pos.y += 1.25 * hud_fontsize.y;
1091 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1093 pos.x += hud_fontsize.x * 0.25;
1094 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1095 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1096 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1098 pos.y+= hud_fontsize.y;
1103 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1104 float stat_secrets_found, stat_secrets_total;
1105 float stat_monsters_killed, stat_monsters_total;
1109 // get monster stats
1110 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1111 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1113 // get secrets stats
1114 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1115 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1117 // get number of rows
1118 if(stat_secrets_total)
1120 if(stat_monsters_total)
1123 // if no rows, return
1127 // draw table header
1128 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1129 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1132 vector tmp = '0 0 0';
1134 tmp.y = hud_fontsize.y * rows;
1137 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1139 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1140 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1143 if(stat_monsters_total)
1145 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1146 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1150 if(stat_secrets_total)
1152 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1153 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1157 pos.y += 1.25 * hud_fontsize.y;
1162 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1165 RANKINGS_RECEIVED_CNT = 0;
1166 for (i=RANKINGS_CNT-1; i>=0; --i)
1168 ++RANKINGS_RECEIVED_CNT;
1170 if (RANKINGS_RECEIVED_CNT == 0)
1174 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1176 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1177 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1178 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1180 pos.y += hud_fontsize.y;
1181 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1182 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1183 vector tmp = '0 0 0';
1185 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1188 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1190 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1191 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1194 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1201 n = grecordholder[i];
1202 p = count_ordinal(i+1);
1203 if(grecordholder[i] == GetPlayerName(player_localnum))
1204 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1205 else if(!(i % 2) && scoreboard_highlight)
1206 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1207 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1208 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);
1209 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1210 pos.y += 1.25 * hud_fontsize.y;
1212 pos.y += autocvar_scoreboard_border_thickness;
1217 float hud_woulddrawscoreboard_prev;
1218 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1219 void HUD_DrawScoreboard()
1221 float hud_woulddrawscoreboard;
1222 hud_woulddrawscoreboard = scoreboard_active;
1223 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1224 hud_woulddrawscoreboard_change = time;
1225 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1228 if(hud_woulddrawscoreboard) {
1229 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1230 if (scoreboard_fadeinspeed)
1231 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1233 scoreboard_fade_alpha = 1;
1236 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1237 if (scoreboard_fadeoutspeed)
1238 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1240 scoreboard_fade_alpha = 0;
1243 if (!scoreboard_fade_alpha)
1246 HUD_UpdatePlayerTeams();
1248 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1249 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1250 scoreboard_highlight = autocvar_scoreboard_highlight;
1251 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1252 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1253 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1254 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1256 vector rgb, pos, tmp;
1260 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1261 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1263 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1264 ymax = (vid_conheight - ymin);
1266 sbwidth = xmax - xmin;
1268 // Initializes position
1274 vector sb_heading_fontsize;
1275 sb_heading_fontsize = hud_fontsize * 2;
1276 draw_beginBoldFont();
1277 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1280 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1282 // Draw the scoreboard
1283 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1287 vector team_score_baseoffset;
1288 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1289 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1291 if(tm.team == NUM_SPECTATOR)
1294 draw_beginBoldFont();
1295 rgb = Team_ColorRGB(tm.team);
1296 str = ftos(tm.(teamscores[ts_primary]));
1297 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1299 if(ts_primary != ts_secondary)
1301 str = ftos(tm.(teamscores[ts_secondary]));
1302 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);
1306 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1308 rgb.x = autocvar_scoreboard_color_bg_r;
1309 rgb.y = autocvar_scoreboard_color_bg_g;
1310 rgb.z = autocvar_scoreboard_color_bg_b;
1314 rgb.x = autocvar_scoreboard_color_bg_r;
1315 rgb.y = autocvar_scoreboard_color_bg_g;
1316 rgb.z = autocvar_scoreboard_color_bg_b;
1318 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1320 if(tm.team == NUM_SPECTATOR)
1323 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1327 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1328 if(race_speedaward) {
1329 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1330 pos.y += 1.25 * hud_fontsize.y;
1332 if(race_speedaward_alltimebest) {
1333 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1334 pos.y += 1.25 * hud_fontsize.y;
1336 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1338 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1340 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1342 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1347 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1349 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1356 item_size.x = sbwidth;
1357 item_size.y = hud_fontsize.y * 1.25;
1359 for(pl = players.sort_next; pl; pl = pl.sort_next)
1361 if(pl.team != NUM_SPECTATOR)
1363 pos.y += 1.25 * hud_fontsize.y;
1364 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1370 draw_beginBoldFont();
1371 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1373 pos.y += 1.25 * hud_fontsize.y;
1376 // Print info string
1378 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1379 tl = getstatf(STAT_TIMELIMIT);
1380 fl = getstatf(STAT_FRAGLIMIT);
1381 ll = getstatf(STAT_LEADLIMIT);
1382 if(gametype == MAPINFO_TYPE_LMS)
1385 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1390 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1394 str = strcat(str, _(" or"));
1397 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1398 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1399 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1400 TranslateScoresLabel(teamscores_label[ts_primary])));
1404 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1405 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1406 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1407 TranslateScoresLabel(scores_label[ps_primary])));
1412 if(tl > 0 || fl > 0)
1413 str = strcat(str, _(" or"));
1416 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1417 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1418 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1419 TranslateScoresLabel(teamscores_label[ts_primary])));
1423 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1424 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1425 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1426 TranslateScoresLabel(scores_label[ps_primary])));
1431 pos.y += 1.2 * hud_fontsize.y;
1432 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1434 // print information about respawn status
1435 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1439 if(respawn_time < 0)
1441 // a negative number means we are awaiting respawn, time value is still the same
1442 respawn_time *= -1; // remove mark now that we checked it
1443 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1445 str = sprintf(_("^1Respawning in ^3%s^1..."),
1446 (autocvar_scoreboard_respawntime_decimals ?
1447 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1449 count_seconds(respawn_time - time)
1453 else if(time < respawn_time)
1455 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1456 (autocvar_scoreboard_respawntime_decimals ?
1457 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1459 count_seconds(respawn_time - time)
1463 else if(time >= respawn_time)
1464 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1466 pos.y += 1.2 * hud_fontsize.y;
1467 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1470 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;