1 #include "scoreboard.qh"
7 #include "../common/constants.qh"
8 #include "../common/counting.qh"
9 #include "../common/mapinfo.qh"
10 #include "../common/minigames/cl_minigames.qh"
11 #include "../common/stats.qh"
12 #include "../common/teams.qh"
13 #include "../common/util.qh"
15 float scoreboard_alpha_bg;
16 float scoreboard_alpha_fg;
17 float scoreboard_highlight;
18 float scoreboard_highlight_alpha;
19 float scoreboard_highlight_alpha_self;
20 float scoreboard_alpha_name;
21 float scoreboard_alpha_name_self;
23 void drawstringright(vector, string, vector, vector, float, float);
24 void drawstringcenter(vector, string, vector, vector, float, float);
26 const float SCOREBOARD_OFFSET = 50;
28 // wrapper to put all possible scores titles through gettext
29 string TranslateScoresLabel(string l)
33 case "bckills": return CTX(_("SCO^bckills"));
34 case "bctime": return CTX(_("SCO^bctime"));
35 case "caps": return CTX(_("SCO^caps"));
36 case "captime": return CTX(_("SCO^captime"));
37 case "deaths": return CTX(_("SCO^deaths"));
38 case "destroyed": return CTX(_("SCO^destroyed"));
39 case "drops": return CTX(_("SCO^drops"));
40 case "faults": return CTX(_("SCO^faults"));
41 case "fckills": return CTX(_("SCO^fckills"));
42 case "goals": return CTX(_("SCO^goals"));
43 case "kckills": return CTX(_("SCO^kckills"));
44 case "kdratio": return CTX(_("SCO^kdratio"));
45 case "k/d": return CTX(_("SCO^k/d"));
46 case "kd": return CTX(_("SCO^kd"));
47 case "kdr": return CTX(_("SCO^kdr"));
48 case "kills": return CTX(_("SCO^kills"));
49 case "laps": return CTX(_("SCO^laps"));
50 case "lives": return CTX(_("SCO^lives"));
51 case "losses": return CTX(_("SCO^losses"));
52 case "name": return CTX(_("SCO^name"));
53 case "sum": return CTX(_("SCO^sum"));
54 case "nick": return CTX(_("SCO^nick"));
55 case "objectives": return CTX(_("SCO^objectives"));
56 case "pickups": return CTX(_("SCO^pickups"));
57 case "ping": return CTX(_("SCO^ping"));
58 case "pl": return CTX(_("SCO^pl"));
59 case "pushes": return CTX(_("SCO^pushes"));
60 case "rank": return CTX(_("SCO^rank"));
61 case "returns": return CTX(_("SCO^returns"));
62 case "revivals": return CTX(_("SCO^revivals"));
63 case "score": return CTX(_("SCO^score"));
64 case "suicides": return CTX(_("SCO^suicides"));
65 case "takes": return CTX(_("SCO^takes"));
66 case "ticks": return CTX(_("SCO^ticks"));
75 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
76 for(i = 0; i < MAX_SCORE; ++i)
78 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
79 if(f == SFL_SORT_PRIO_PRIMARY)
81 if(f == SFL_SORT_PRIO_SECONDARY)
84 if(ps_secondary == -1)
85 ps_secondary = ps_primary;
87 for(i = 0; i < MAX_TEAMSCORE; ++i)
89 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
90 if(f == SFL_SORT_PRIO_PRIMARY)
92 if(f == SFL_SORT_PRIO_SECONDARY)
95 if(ts_secondary == -1)
96 ts_secondary = ts_primary;
101 float SetTeam(entity pl, float Team);
103 void HUD_UpdatePlayerTeams()
110 for(pl = players.sort_next; pl; pl = pl.sort_next)
113 Team = GetPlayerColor(pl.sv_entnum);
114 if(SetTeam(pl, Team))
117 HUD_UpdatePlayerPos(pl);
121 pl = players.sort_next;
126 print(strcat("PNUM: ", ftos(num), "\n"));
131 int HUD_CompareScore(float vl, float vr, int f)
133 if(f & SFL_ZERO_IS_WORST)
135 if(vl == 0 && vr != 0)
137 if(vl != 0 && vr == 0)
141 return IS_INCREASING(f);
143 return IS_DECREASING(f);
147 float HUD_ComparePlayerScores(entity left, entity right)
150 vl = GetPlayerColor(left.sv_entnum);
151 vr = GetPlayerColor(right.sv_entnum);
163 if(vl == NUM_SPECTATOR)
165 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
167 if(!left.gotscores && right.gotscores)
172 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
176 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
181 for(i = 0; i < MAX_SCORE; ++i)
183 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
188 if (left.sv_entnum < right.sv_entnum)
194 void HUD_UpdatePlayerPos(entity player)
196 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
198 SORT_SWAP(player, other);
200 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
202 SORT_SWAP(other, player);
206 float HUD_CompareTeamScores(entity left, entity right)
210 if(left.team == NUM_SPECTATOR)
212 if(right.team == NUM_SPECTATOR)
215 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
219 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
223 for(i = 0; i < MAX_SCORE; ++i)
225 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
230 if (left.team < right.team)
236 void HUD_UpdateTeamPos(entity Team)
238 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
240 SORT_SWAP(Team, other);
242 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
244 SORT_SWAP(other, Team);
250 print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
251 print(_("^3|---------------------------------------------------------------|\n"));
252 print(_("Usage:\n"));
253 print(_("^2scoreboard_columns_set default\n"));
254 print(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
255 print(_("The following field names are recognized (case insensitive):\n"));
256 print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
258 print(_("^3name^7 or ^3nick^7 Name of a player\n"));
259 print(_("^3ping^7 Ping time\n"));
260 print(_("^3pl^7 Packet loss\n"));
261 print(_("^3kills^7 Number of kills\n"));
262 print(_("^3deaths^7 Number of deaths\n"));
263 print(_("^3suicides^7 Number of suicides\n"));
264 print(_("^3frags^7 kills - suicides\n"));
265 print(_("^3kd^7 The kill-death ratio\n"));
266 print(_("^3sum^7 frags - deaths\n"));
267 print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
268 print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
269 print(_("^3captime^7 Time of fastest cap (CTF)\n"));
270 print(_("^3fckills^7 Number of flag carrier kills\n"));
271 print(_("^3returns^7 Number of flag returns\n"));
272 print(_("^3drops^7 Number of flag drops\n"));
273 print(_("^3lives^7 Number of lives (LMS)\n"));
274 print(_("^3rank^7 Player rank\n"));
275 print(_("^3pushes^7 Number of players pushed into void\n"));
276 print(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
277 print(_("^3kckills^7 Number of keys carrier kills\n"));
278 print(_("^3losses^7 Number of times a key was lost\n"));
279 print(_("^3laps^7 Number of laps finished (race/cts)\n"));
280 print(_("^3time^7 Total time raced (race/cts)\n"));
281 print(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
282 print(_("^3ticks^7 Number of ticks (DOM)\n"));
283 print(_("^3takes^7 Number of domination points taken (DOM)\n"));
284 print(_("^3bckills^7 Number of ball carrier kills\n"));
285 print(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
286 print(_("^3score^7 Total score\n\n"));
288 print(_("Before a field you can put a + or - sign, then a comma separated list\n"
289 "of game types, then a slash, to make the field show up only in these\n"
290 "or in all but these game types. You can also specify 'all' as a\n"
291 "field to show all fields available for the current game mode.\n\n"));
293 print(_("The special game type names 'teams' and 'noteams' can be used to\n"
294 "include/exclude ALL teams/noteams game modes.\n\n"));
296 print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
297 print(_("will display name, ping and pl aligned to the left, and the fields\n"
298 "right of the vertical bar aligned to the right.\n"));
299 print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
300 "other gamemodes except DM.\n"));
303 #define HUD_DefaultColumnLayout() \
305 "-teams,race,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,race,ka/suicides +ft,tdm/suicides -race,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
306 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \
307 "+lms/lives +lms/rank " \
308 "+kh/caps +kh/pushes +kh/destroyed " \
309 "?+race/laps ?+race/time ?+race/fastest " \
310 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
313 void Cmd_HUD_SetFields(float argc)
317 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
322 // set up a temporary scoreboard layout
323 // no layout can be properly set up until score_info data haven't been received
324 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
326 scores_label[ps_primary] = strzone("score");
327 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
330 // TODO: re enable with gametype dependant cvars?
331 if(argc < 3) // no arguments provided
332 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
335 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
339 if(argv(2) == "default")
340 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
341 else if(argv(2) == "all")
344 s = "ping pl name |";
345 for(i = 0; i < MAX_SCORE; ++i)
348 if(i != ps_secondary)
349 if(scores_label[i] != "")
350 s = strcat(s, " ", scores_label[i]);
352 if(ps_secondary != ps_primary)
353 s = strcat(s, " ", scores_label[ps_secondary]);
354 s = strcat(s, " ", scores_label[ps_primary]);
355 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
362 hud_fontsize = HUD_GetFontsize("hud_fontsize");
364 draw_beginBoldFont();
365 for(i = 1; i < argc - 1; ++i)
371 if(substring(str, 0, 1) == "?")
374 str = substring(str, 1, strlen(str) - 1);
377 slash = strstrofs(str, "/", 0);
380 pattern = substring(str, 0, slash);
381 str = substring(str, slash + 1, strlen(str) - (slash + 1));
383 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
387 strunzone(hud_title[hud_num_fields]);
388 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
389 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
390 str = strtolower(str);
393 hud_field[hud_num_fields] = SP_PING;
394 } else if(str == "pl") {
395 hud_field[hud_num_fields] = SP_PL;
396 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
397 hud_field[hud_num_fields] = SP_KDRATIO;
398 } else if(str == "sum" || str == "diff" || str == "k-d") {
399 hud_field[hud_num_fields] = SP_SUM;
400 } else if(str == "name" || str == "nick") {
401 hud_field[hud_num_fields] = SP_NAME;
403 } else if(str == "|") {
404 hud_field[hud_num_fields] = SP_SEPARATOR;
407 for(j = 0; j < MAX_SCORE; ++j)
408 if(str == strtolower(scores_label[j]))
409 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
418 printf("^1Error:^7 Unknown score field: '%s'\n", str);
422 hud_field[hud_num_fields] = j;
425 if(j == ps_secondary)
429 if(hud_num_fields >= MAX_HUD_FIELDS)
433 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
435 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
437 if(ps_primary == ps_secondary)
439 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
441 if(hud_num_fields+missing < MAX_HUD_FIELDS)
445 strunzone(hud_title[hud_num_fields]);
446 for(i = hud_num_fields; i > 0; --i)
448 hud_title[i] = hud_title[i-1];
449 hud_size[i] = hud_size[i-1];
450 hud_field[i] = hud_field[i-1];
452 hud_title[0] = strzone(TranslateScoresLabel("name"));
453 hud_field[0] = SP_NAME;
455 print("fixed missing field 'name'\n");
459 strunzone(hud_title[hud_num_fields]);
460 for(i = hud_num_fields; i > 1; --i)
462 hud_title[i] = hud_title[i-1];
463 hud_size[i] = hud_size[i-1];
464 hud_field[i] = hud_field[i-1];
466 hud_title[1] = strzone("|");
467 hud_field[1] = SP_SEPARATOR;
468 hud_size[1] = stringwidth("|", false, hud_fontsize);
470 print("fixed missing field '|'\n");
473 else if(!have_separator)
475 strunzone(hud_title[hud_num_fields]);
476 hud_title[hud_num_fields] = strzone("|");
477 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
478 hud_field[hud_num_fields] = SP_SEPARATOR;
480 print("fixed missing field '|'\n");
484 strunzone(hud_title[hud_num_fields]);
485 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
486 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
487 hud_field[hud_num_fields] = ps_secondary;
489 printf("fixed missing field '%s'\n", scores_label[ps_secondary]);
493 strunzone(hud_title[hud_num_fields]);
494 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
495 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
496 hud_field[hud_num_fields] = ps_primary;
498 printf("fixed missing field '%s'\n", scores_label[ps_primary]);
502 hud_field[hud_num_fields] = SP_END;
507 vector hud_field_rgb;
508 string hud_field_icon0;
509 string hud_field_icon1;
510 string hud_field_icon2;
511 vector hud_field_icon0_rgb;
512 vector hud_field_icon1_rgb;
513 vector hud_field_icon2_rgb;
514 float hud_field_icon0_alpha;
515 float hud_field_icon1_alpha;
516 float hud_field_icon2_alpha;
517 string HUD_GetField(entity pl, int field)
519 float tmp, num, denom;
522 hud_field_rgb = '1 1 1';
523 hud_field_icon0 = "";
524 hud_field_icon1 = "";
525 hud_field_icon2 = "";
526 hud_field_icon0_rgb = '1 1 1';
527 hud_field_icon1_rgb = '1 1 1';
528 hud_field_icon2_rgb = '1 1 1';
529 hud_field_icon0_alpha = 1;
530 hud_field_icon1_alpha = 1;
531 hud_field_icon2_alpha = 1;
536 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
537 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
541 tmp = max(0, min(220, f-80)) / 220;
542 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
548 f = pl.ping_packetloss;
549 tmp = pl.ping_movementloss;
550 if(f == 0 && tmp == 0)
552 str = ftos(ceil(f * 100));
554 str = strcat(str, "~", ftos(ceil(tmp * 100)));
555 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
556 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
560 if(ready_waiting && pl.ready)
562 hud_field_icon0 = "gfx/scoreboard/player_ready";
566 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
568 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
569 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
570 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
571 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
572 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
575 return GetPlayerName(pl.sv_entnum);
578 f = pl.(scores[SP_KILLS]);
579 f -= pl.(scores[SP_SUICIDES]);
583 num = pl.(scores[SP_KILLS]);
584 denom = pl.(scores[SP_DEATHS]);
587 hud_field_rgb = '0 1 0';
588 str = sprintf("%d", num);
589 } else if(num <= 0) {
590 hud_field_rgb = '1 0 0';
591 str = sprintf("%.1f", num/denom);
593 str = sprintf("%.1f", num/denom);
597 f = pl.(scores[SP_KILLS]);
598 f -= pl.(scores[SP_DEATHS]);
601 hud_field_rgb = '0 1 0';
603 hud_field_rgb = '1 1 1';
605 hud_field_rgb = '1 0 0';
610 tmp = pl.(scores[field]);
611 f = scores_flags[field];
612 if(field == ps_primary)
613 hud_field_rgb = '1 1 0';
614 else if(field == ps_secondary)
615 hud_field_rgb = '0 1 1';
617 hud_field_rgb = '1 1 1';
618 return ScoreString(f, tmp);
623 float hud_fixscoreboardcolumnwidth_len;
624 float hud_fixscoreboardcolumnwidth_iconlen;
625 float hud_fixscoreboardcolumnwidth_marginlen;
627 string HUD_FixScoreboardColumnWidth(int i, string str)
631 field = hud_field[i];
633 hud_fixscoreboardcolumnwidth_iconlen = 0;
635 if(hud_field_icon0 != "")
637 sz = draw_getimagesize(hud_field_icon0);
639 if(hud_fixscoreboardcolumnwidth_iconlen < f)
640 hud_fixscoreboardcolumnwidth_iconlen = f;
643 if(hud_field_icon1 != "")
645 sz = draw_getimagesize(hud_field_icon1);
647 if(hud_fixscoreboardcolumnwidth_iconlen < f)
648 hud_fixscoreboardcolumnwidth_iconlen = f;
651 if(hud_field_icon2 != "")
653 sz = draw_getimagesize(hud_field_icon2);
655 if(hud_fixscoreboardcolumnwidth_iconlen < f)
656 hud_fixscoreboardcolumnwidth_iconlen = f;
659 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
661 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
662 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
664 hud_fixscoreboardcolumnwidth_marginlen = 0;
666 if(field == SP_NAME) // name gets all remaining space
670 namesize = sbwidth;// / hud_fontsize_x;
671 for(j = 0; j < hud_num_fields; ++j)
673 if (hud_field[i] != SP_SEPARATOR)
674 namesize -= hud_size[j] + hud_fontsize.x;
675 namesize += hud_fontsize.x;
676 hud_size[i] = namesize;
678 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
679 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
680 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
681 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
684 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
686 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
693 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
696 rgb = Team_ColorRGB(pl.team);
700 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
702 if((rgb == '1 1 1') && (!is_spec)) {
703 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
704 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
705 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
707 vector h_pos = pos - '1 1 0';
708 vector h_size = item_size + '2 0 0';
709 // alternated rows highlighting
711 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
712 else if((scoreboard_highlight) && (!(pl_number % 2)))
713 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
719 for(i = 0; i < hud_num_fields; ++i)
721 field = hud_field[i];
722 if(field == SP_SEPARATOR)
725 if(is_spec && field != SP_NAME && field != SP_PING) {
726 pos.x += hud_size[i] + hud_fontsize.x;
729 str = HUD_GetField(pl, field);
730 str = HUD_FixScoreboardColumnWidth(i, str);
732 pos.x += hud_size[i] + hud_fontsize.x;
734 if(field == SP_NAME) {
735 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
737 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
739 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
741 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
743 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
745 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
748 tmp.x = hud_size[i] + hud_fontsize.x;
749 if(hud_field_icon0 != "")
751 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
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, DRAWFLAG_NORMAL);
754 if(hud_field_icon1 != "")
756 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
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, DRAWFLAG_NORMAL);
759 if(hud_field_icon2 != "")
761 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
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, DRAWFLAG_NORMAL);
766 if(hud_field[i] == SP_SEPARATOR)
769 for(i = hud_num_fields-1; i > 0; --i)
771 field = hud_field[i];
772 if(field == SP_SEPARATOR)
775 if(is_spec && field != SP_NAME && field != SP_PING) {
776 pos.x -= hud_size[i] + hud_fontsize.x;
780 str = HUD_GetField(pl, field);
781 str = HUD_FixScoreboardColumnWidth(i, str);
783 if(field == SP_NAME) {
784 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
786 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
788 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
790 tmp.x = hud_fixscoreboardcolumnwidth_len;
792 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
794 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
798 if(hud_field_icon0 != "")
800 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
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, DRAWFLAG_NORMAL);
803 if(hud_field_icon1 != "")
805 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
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, DRAWFLAG_NORMAL);
808 if(hud_field_icon2 != "")
810 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
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, DRAWFLAG_NORMAL);
813 pos.x -= hud_size[i] + hud_fontsize.x;
818 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
822 * HUD_Scoreboard_MakeTable
824 * Makes a table for a team (for all playing players in DM) and fills it
827 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
829 float body_table_height;
830 vector tmp = '0 0 0', column_dim = '0 0 0';
833 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
835 pos.y += autocvar_scoreboard_border_thickness;
839 tmp.y = 1.25 * hud_fontsize.y;
843 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
845 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
848 tmp.y += autocvar_scoreboard_border_thickness;
849 tmp.y += body_table_height;
850 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
852 // separator header/table
853 pos.y += 1.25 * hud_fontsize.y;
854 tmp.y = autocvar_scoreboard_border_thickness;
855 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
857 pos.y += autocvar_scoreboard_border_thickness;
860 tmp.y = body_table_height;
862 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
864 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
866 // anyway, apply some color
867 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
869 // go back to the top to make alternated columns highlighting and to print the strings
870 pos.y -= 1.25 * hud_fontsize.y;
871 pos.y -= autocvar_scoreboard_border_thickness;
875 if (scoreboard_highlight)
877 column_dim.y = 1.25 * hud_fontsize.y; // header
878 column_dim.y += autocvar_scoreboard_border_thickness;
879 column_dim.y += body_table_height;
882 // print the strings of the columns headers and draw the columns
883 draw_beginBoldFont();
885 for(i = 0; i < hud_num_fields; ++i)
887 if(hud_field[i] == SP_SEPARATOR)
889 column_dim.x = hud_size[i] + hud_fontsize.x;
890 if (scoreboard_highlight)
893 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
895 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
896 pos.x += column_dim.x;
898 if(hud_field[i] == SP_SEPARATOR)
902 for(i = hud_num_fields-1; i > 0; --i)
904 if(hud_field[i] == SP_SEPARATOR)
907 pos.x -= hud_size[i];
909 if (scoreboard_highlight)
913 if (i == hud_num_fields-1)
914 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
916 column_dim.x = hud_size[i] + hud_fontsize.x;
917 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
921 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
922 tmp.x = (hud_size[i] - tmp.x);
923 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
924 pos.x -= hud_fontsize.x;
930 pos.y += 1.25 * hud_fontsize.y; // skip the header
931 pos.y += autocvar_scoreboard_border_thickness;
935 tmp.y = hud_fontsize.y * 1.25;
937 // fill the table and draw the rows
940 for(pl = players.sort_next; pl; pl = pl.sort_next)
942 if(pl.team != tm.team)
944 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
945 pos.y += 1.25 * hud_fontsize.y;
949 for(pl = players.sort_next; pl; pl = pl.sort_next)
951 if(pl.team == NUM_SPECTATOR)
953 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
954 pos.y += 1.25 * hud_fontsize.y;
959 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
960 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
965 float HUD_WouldDrawScoreboard() {
966 if (autocvar__hud_configure)
968 else if (scoreboard_showscores)
970 else if (intermission == 1)
972 else if (intermission == 2)
974 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
976 else if (scoreboard_showscores_force)
981 float average_accuracy;
982 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
985 int weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
987 if(autocvar_scoreboard_accuracy_doublerows)
992 float fontsize = height * 1/3;
993 float weapon_height = height * 2/3;
994 float weapon_width = sbwidth / weapon_cnt;
995 float g_instagib = 0;
997 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
998 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
999 vector tmp = '0 0 0';
1001 tmp.y = height * rows;
1004 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1006 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1007 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1009 // column highlighting
1010 for(i = 0; i < weapon_cnt/rows; ++i)
1013 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);
1017 for(i = 0; i < rows; ++i)
1019 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);
1022 average_accuracy = 0;
1023 float weapons_with_stats;
1024 weapons_with_stats = 0;
1026 pos.x += weapon_width / 2;
1028 if(switchweapon == WEP_VAPORIZER)
1029 g_instagib = 1; // TODO: real detection for instagib?
1032 if(autocvar_scoreboard_accuracy_nocolors)
1035 Accuracy_LoadColors();
1037 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1039 self = get_weaponinfo(i);
1042 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
1044 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1047 if(weapon_stats >= 0)
1048 weapon_alpha = scoreboard_alpha_fg;
1050 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1053 drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1055 if(weapon_stats >= 0) {
1056 weapons_with_stats += 1;
1057 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1060 s = sprintf("%d%%", weapon_stats*100);
1063 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1065 if(!autocvar_scoreboard_accuracy_nocolors)
1066 rgb = Accuracy_GetColor(weapon_stats);
1068 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1070 pos.x += weapon_width * rows;
1071 if(rows == 2 && i == 6) {
1077 if(weapons_with_stats)
1078 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1081 pos.x -= weapon_width / 2;
1085 pos.y += 1.25 * hud_fontsize.y;
1089 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1091 pos.x += hud_fontsize.x * 0.25;
1092 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1093 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1094 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1096 pos.y+= hud_fontsize.y;
1101 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1102 float stat_secrets_found, stat_secrets_total;
1103 float stat_monsters_killed, stat_monsters_total;
1107 // get monster stats
1108 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1109 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1111 // get secrets stats
1112 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1113 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1115 // get number of rows
1116 if(stat_secrets_total)
1118 if(stat_monsters_total)
1121 // if no rows, return
1125 // draw table header
1126 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1127 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1130 vector tmp = '0 0 0';
1132 tmp.y = hud_fontsize.y * rows;
1135 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1137 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1138 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1141 if(stat_monsters_total)
1143 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1144 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1148 if(stat_secrets_total)
1150 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1151 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1155 pos.y += 1.25 * hud_fontsize.y;
1160 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1163 RANKINGS_RECEIVED_CNT = 0;
1164 for (i=RANKINGS_CNT-1; i>=0; --i)
1166 ++RANKINGS_RECEIVED_CNT;
1168 if (RANKINGS_RECEIVED_CNT == 0)
1172 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1174 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1175 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1176 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1178 pos.y += hud_fontsize.y;
1179 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1180 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1181 vector tmp = '0 0 0';
1183 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1186 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1188 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1189 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1192 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1199 n = grecordholder[i];
1200 p = count_ordinal(i+1);
1201 if(grecordholder[i] == GetPlayerName(player_localnum))
1202 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1203 else if(!(i % 2) && scoreboard_highlight)
1204 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1205 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1206 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);
1207 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1208 pos.y += 1.25 * hud_fontsize.y;
1210 pos.y += autocvar_scoreboard_border_thickness;
1215 float hud_woulddrawscoreboard_prev;
1216 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1217 void HUD_DrawScoreboard()
1219 float hud_woulddrawscoreboard;
1220 hud_woulddrawscoreboard = scoreboard_active;
1221 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1222 hud_woulddrawscoreboard_change = time;
1223 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1226 if(hud_woulddrawscoreboard) {
1227 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1228 if (scoreboard_fadeinspeed)
1229 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1231 scoreboard_fade_alpha = 1;
1234 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1235 if (scoreboard_fadeoutspeed)
1236 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1238 scoreboard_fade_alpha = 0;
1241 if (!scoreboard_fade_alpha)
1244 HUD_UpdatePlayerTeams();
1246 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1247 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1248 scoreboard_highlight = autocvar_scoreboard_highlight;
1249 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1250 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1251 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1252 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1254 vector rgb, pos, tmp;
1258 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1259 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1261 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1262 ymax = (vid_conheight - ymin);
1264 sbwidth = xmax - xmin;
1266 // Initializes position
1272 vector sb_heading_fontsize;
1273 sb_heading_fontsize = hud_fontsize * 2;
1274 draw_beginBoldFont();
1275 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1278 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1280 // Draw the scoreboard
1281 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1285 vector team_score_baseoffset;
1286 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1287 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1289 if(tm.team == NUM_SPECTATOR)
1292 draw_beginBoldFont();
1293 rgb = Team_ColorRGB(tm.team);
1294 str = ftos(tm.(teamscores[ts_primary]));
1295 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1297 if(ts_primary != ts_secondary)
1299 str = ftos(tm.(teamscores[ts_secondary]));
1300 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);
1304 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1306 rgb.x = autocvar_scoreboard_color_bg_r;
1307 rgb.y = autocvar_scoreboard_color_bg_g;
1308 rgb.z = autocvar_scoreboard_color_bg_b;
1312 rgb.x = autocvar_scoreboard_color_bg_r;
1313 rgb.y = autocvar_scoreboard_color_bg_g;
1314 rgb.z = autocvar_scoreboard_color_bg_b;
1316 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1318 if(tm.team == NUM_SPECTATOR)
1321 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1325 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1326 if(race_speedaward) {
1327 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1328 pos.y += 1.25 * hud_fontsize.y;
1330 if(race_speedaward_alltimebest) {
1331 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1332 pos.y += 1.25 * hud_fontsize.y;
1334 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1336 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1338 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1340 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1345 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1347 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1354 item_size.x = sbwidth;
1355 item_size.y = hud_fontsize.y * 1.25;
1357 for(pl = players.sort_next; pl; pl = pl.sort_next)
1359 if(pl.team != NUM_SPECTATOR)
1361 pos.y += 1.25 * hud_fontsize.y;
1362 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1368 draw_beginBoldFont();
1369 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1371 pos.y += 1.25 * hud_fontsize.y;
1374 // Print info string
1376 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1377 tl = getstatf(STAT_TIMELIMIT);
1378 fl = getstatf(STAT_FRAGLIMIT);
1379 ll = getstatf(STAT_LEADLIMIT);
1380 if(gametype == MAPINFO_TYPE_LMS)
1383 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1388 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1392 str = strcat(str, _(" or"));
1395 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1396 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1397 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1398 TranslateScoresLabel(teamscores_label[ts_primary])));
1402 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1403 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1404 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1405 TranslateScoresLabel(scores_label[ps_primary])));
1410 if(tl > 0 || fl > 0)
1411 str = strcat(str, _(" or"));
1414 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
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 a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
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 pos.y += 1.2 * hud_fontsize.y;
1430 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1432 // print information about respawn status
1433 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1437 if(respawn_time < 0)
1439 // a negative number means we are awaiting respawn, time value is still the same
1440 respawn_time *= -1; // remove mark now that we checked it
1441 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1443 str = sprintf(_("^1Respawning in ^3%s^1..."),
1444 (autocvar_scoreboard_respawntime_decimals ?
1445 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1447 count_seconds(respawn_time - time)
1451 else if(time < respawn_time)
1453 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1454 (autocvar_scoreboard_respawntime_decimals ?
1455 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1457 count_seconds(respawn_time - time)
1461 else if(time >= respawn_time)
1462 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1464 pos.y += 1.2 * hud_fontsize.y;
1465 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1468 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;