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 +ons/caps +ons/takes " \
306 "+lms/lives +lms/rank " \
307 "+kh/caps +kh/pushes +kh/destroyed " \
308 "?+race/laps ?+race/time ?+race/fastest " \
309 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
312 void Cmd_HUD_SetFields(float argc)
316 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
321 // set up a temporary scoreboard layout
322 // no layout can be properly set up until score_info data haven't been received
323 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
325 scores_label[ps_primary] = strzone("score");
326 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
329 // TODO: re enable with gametype dependant cvars?
330 if(argc < 3) // no arguments provided
331 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
334 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
338 if(argv(2) == "default")
339 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
340 else if(argv(2) == "all")
343 s = "ping pl name |";
344 for(i = 0; i < MAX_SCORE; ++i)
347 if(i != ps_secondary)
348 if(scores_label[i] != "")
349 s = strcat(s, " ", scores_label[i]);
351 if(ps_secondary != ps_primary)
352 s = strcat(s, " ", scores_label[ps_secondary]);
353 s = strcat(s, " ", scores_label[ps_primary]);
354 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
361 hud_fontsize = HUD_GetFontsize("hud_fontsize");
363 for(i = 1; i < argc - 1; ++i)
369 if(substring(str, 0, 1) == "?")
372 str = substring(str, 1, strlen(str) - 1);
375 slash = strstrofs(str, "/", 0);
378 pattern = substring(str, 0, slash);
379 str = substring(str, slash + 1, strlen(str) - (slash + 1));
381 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
385 strunzone(hud_title[hud_num_fields]);
386 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
387 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
388 str = strtolower(str);
391 hud_field[hud_num_fields] = SP_PING;
392 } else if(str == "pl") {
393 hud_field[hud_num_fields] = SP_PL;
394 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
395 hud_field[hud_num_fields] = SP_KDRATIO;
396 } else if(str == "sum" || str == "diff" || str == "k-d") {
397 hud_field[hud_num_fields] = SP_SUM;
398 } else if(str == "name" || str == "nick") {
399 hud_field[hud_num_fields] = SP_NAME;
401 } else if(str == "|") {
402 hud_field[hud_num_fields] = SP_SEPARATOR;
405 for(j = 0; j < MAX_SCORE; ++j)
406 if(str == strtolower(scores_label[j]))
407 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
416 printf("^1Error:^7 Unknown score field: '%s'\n", str);
420 hud_field[hud_num_fields] = j;
423 if(j == ps_secondary)
427 if(hud_num_fields >= MAX_HUD_FIELDS)
431 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
433 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
435 if(ps_primary == ps_secondary)
437 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
439 if(hud_num_fields+missing < MAX_HUD_FIELDS)
443 strunzone(hud_title[hud_num_fields]);
444 for(i = hud_num_fields; i > 0; --i)
446 hud_title[i] = hud_title[i-1];
447 hud_size[i] = hud_size[i-1];
448 hud_field[i] = hud_field[i-1];
450 hud_title[0] = strzone(TranslateScoresLabel("name"));
451 hud_field[0] = SP_NAME;
453 print("fixed missing field 'name'\n");
457 strunzone(hud_title[hud_num_fields]);
458 for(i = hud_num_fields; i > 1; --i)
460 hud_title[i] = hud_title[i-1];
461 hud_size[i] = hud_size[i-1];
462 hud_field[i] = hud_field[i-1];
464 hud_title[1] = strzone("|");
465 hud_field[1] = SP_SEPARATOR;
466 hud_size[1] = stringwidth("|", false, hud_fontsize);
468 print("fixed missing field '|'\n");
471 else if(!have_separator)
473 strunzone(hud_title[hud_num_fields]);
474 hud_title[hud_num_fields] = strzone("|");
475 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
476 hud_field[hud_num_fields] = SP_SEPARATOR;
478 print("fixed missing field '|'\n");
482 strunzone(hud_title[hud_num_fields]);
483 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
484 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
485 hud_field[hud_num_fields] = ps_secondary;
487 printf("fixed missing field '%s'\n", scores_label[ps_secondary]);
491 strunzone(hud_title[hud_num_fields]);
492 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
493 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
494 hud_field[hud_num_fields] = ps_primary;
496 printf("fixed missing field '%s'\n", scores_label[ps_primary]);
500 hud_field[hud_num_fields] = SP_END;
504 vector hud_field_rgb;
505 string hud_field_icon0;
506 string hud_field_icon1;
507 string hud_field_icon2;
508 vector hud_field_icon0_rgb;
509 vector hud_field_icon1_rgb;
510 vector hud_field_icon2_rgb;
511 float hud_field_icon0_alpha;
512 float hud_field_icon1_alpha;
513 float hud_field_icon2_alpha;
514 string HUD_GetField(entity pl, int field)
516 float tmp, num, denom;
519 hud_field_rgb = '1 1 1';
520 hud_field_icon0 = "";
521 hud_field_icon1 = "";
522 hud_field_icon2 = "";
523 hud_field_icon0_rgb = '1 1 1';
524 hud_field_icon1_rgb = '1 1 1';
525 hud_field_icon2_rgb = '1 1 1';
526 hud_field_icon0_alpha = 1;
527 hud_field_icon1_alpha = 1;
528 hud_field_icon2_alpha = 1;
533 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
534 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
538 tmp = max(0, min(220, f-80)) / 220;
539 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
545 f = pl.ping_packetloss;
546 tmp = pl.ping_movementloss;
547 if(f == 0 && tmp == 0)
549 str = ftos(ceil(f * 100));
551 str = strcat(str, "~", ftos(ceil(tmp * 100)));
552 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
553 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
557 if(ready_waiting && pl.ready)
559 hud_field_icon0 = "gfx/scoreboard/player_ready";
563 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
565 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
566 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
567 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
568 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
569 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
572 return GetPlayerName(pl.sv_entnum);
575 f = pl.(scores[SP_KILLS]);
576 f -= pl.(scores[SP_SUICIDES]);
580 num = pl.(scores[SP_KILLS]);
581 denom = pl.(scores[SP_DEATHS]);
584 hud_field_rgb = '0 1 0';
585 str = sprintf("%d", num);
586 } else if(num <= 0) {
587 hud_field_rgb = '1 0 0';
588 str = sprintf("%.1f", num/denom);
590 str = sprintf("%.1f", num/denom);
594 f = pl.(scores[SP_KILLS]);
595 f -= pl.(scores[SP_DEATHS]);
598 hud_field_rgb = '0 1 0';
600 hud_field_rgb = '1 1 1';
602 hud_field_rgb = '1 0 0';
607 tmp = pl.(scores[field]);
608 f = scores_flags[field];
609 if(field == ps_primary)
610 hud_field_rgb = '1 1 0';
611 else if(field == ps_secondary)
612 hud_field_rgb = '0 1 1';
614 hud_field_rgb = '1 1 1';
615 return ScoreString(f, tmp);
620 float hud_fixscoreboardcolumnwidth_len;
621 float hud_fixscoreboardcolumnwidth_iconlen;
622 float hud_fixscoreboardcolumnwidth_marginlen;
624 string HUD_FixScoreboardColumnWidth(int i, string str)
628 field = hud_field[i];
630 hud_fixscoreboardcolumnwidth_iconlen = 0;
632 if(hud_field_icon0 != "")
634 sz = draw_getimagesize(hud_field_icon0);
636 if(hud_fixscoreboardcolumnwidth_iconlen < f)
637 hud_fixscoreboardcolumnwidth_iconlen = f;
640 if(hud_field_icon1 != "")
642 sz = draw_getimagesize(hud_field_icon1);
644 if(hud_fixscoreboardcolumnwidth_iconlen < f)
645 hud_fixscoreboardcolumnwidth_iconlen = f;
648 if(hud_field_icon2 != "")
650 sz = draw_getimagesize(hud_field_icon2);
652 if(hud_fixscoreboardcolumnwidth_iconlen < f)
653 hud_fixscoreboardcolumnwidth_iconlen = f;
656 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
658 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
659 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
661 hud_fixscoreboardcolumnwidth_marginlen = 0;
663 if(field == SP_NAME) // name gets all remaining space
667 namesize = sbwidth;// / hud_fontsize_x;
668 for(j = 0; j < hud_num_fields; ++j)
670 if (hud_field[i] != SP_SEPARATOR)
671 namesize -= hud_size[j] + hud_fontsize.x;
672 namesize += hud_fontsize.x;
673 hud_size[i] = namesize;
675 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
676 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
677 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
678 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
681 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
683 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
690 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
693 rgb = Team_ColorRGB(pl.team);
697 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
699 if((rgb == '1 1 1') && (!is_spec)) {
700 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
701 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
702 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
704 vector h_pos = pos - '1 1 0';
705 vector h_size = item_size + '2 0 0';
706 // alternated rows highlighting
708 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
709 else if((scoreboard_highlight) && (!(pl_number % 2)))
710 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
716 for(i = 0; i < hud_num_fields; ++i)
718 field = hud_field[i];
719 if(field == SP_SEPARATOR)
722 if(is_spec && field != SP_NAME && field != SP_PING) {
723 pos.x += hud_size[i] + hud_fontsize.x;
726 str = HUD_GetField(pl, field);
727 str = HUD_FixScoreboardColumnWidth(i, str);
729 pos.x += hud_size[i] + hud_fontsize.x;
731 if(field == SP_NAME) {
732 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
734 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
736 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
738 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
740 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
742 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
745 tmp.x = hud_size[i] + hud_fontsize.x;
746 if(hud_field_icon0 != "")
748 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
750 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
751 if(hud_field_icon1 != "")
753 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
755 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
756 if(hud_field_icon2 != "")
758 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
760 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
763 if(hud_field[i] == SP_SEPARATOR)
766 for(i = hud_num_fields-1; i > 0; --i)
768 field = hud_field[i];
769 if(field == SP_SEPARATOR)
772 if(is_spec && field != SP_NAME && field != SP_PING) {
773 pos.x -= hud_size[i] + hud_fontsize.x;
777 str = HUD_GetField(pl, field);
778 str = HUD_FixScoreboardColumnWidth(i, str);
780 if(field == SP_NAME) {
781 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
783 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
785 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
787 tmp.x = hud_fixscoreboardcolumnwidth_len;
789 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
791 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
795 if(hud_field_icon0 != "")
797 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
799 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
800 if(hud_field_icon1 != "")
802 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
804 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
805 if(hud_field_icon2 != "")
807 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
809 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
810 pos.x -= hud_size[i] + hud_fontsize.x;
815 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
819 * HUD_Scoreboard_MakeTable
821 * Makes a table for a team (for all playing players in DM) and fills it
824 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
826 float body_table_height;
827 vector tmp = '0 0 0', column_dim = '0 0 0';
830 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
832 pos.y += autocvar_scoreboard_border_thickness;
836 tmp.y = 1.25 * hud_fontsize.y;
840 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
842 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
845 tmp.y += autocvar_scoreboard_border_thickness;
846 tmp.y += body_table_height;
847 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
849 // separator header/table
850 pos.y += 1.25 * hud_fontsize.y;
851 tmp.y = autocvar_scoreboard_border_thickness;
852 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
854 pos.y += autocvar_scoreboard_border_thickness;
857 tmp.y = body_table_height;
859 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
861 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
863 // anyway, apply some color
864 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
866 // go back to the top to make alternated columns highlighting and to print the strings
867 pos.y -= 1.25 * hud_fontsize.y;
868 pos.y -= autocvar_scoreboard_border_thickness;
872 if (scoreboard_highlight)
874 column_dim.y = 1.25 * hud_fontsize.y; // header
875 column_dim.y += autocvar_scoreboard_border_thickness;
876 column_dim.y += body_table_height;
879 // print the strings of the columns headers and draw the columns
881 for(i = 0; i < hud_num_fields; ++i)
883 if(hud_field[i] == SP_SEPARATOR)
885 column_dim.x = hud_size[i] + hud_fontsize.x;
886 if (scoreboard_highlight)
889 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
891 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
892 pos.x += column_dim.x;
894 if(hud_field[i] == SP_SEPARATOR)
898 for(i = hud_num_fields-1; i > 0; --i)
900 if(hud_field[i] == SP_SEPARATOR)
903 pos.x -= hud_size[i];
905 if (scoreboard_highlight)
909 if (i == hud_num_fields-1)
910 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
912 column_dim.x = hud_size[i] + hud_fontsize.x;
913 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
917 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
918 tmp.x = (hud_size[i] - tmp.x);
919 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
920 pos.x -= hud_fontsize.x;
925 pos.y += 1.25 * hud_fontsize.y; // skip the header
926 pos.y += autocvar_scoreboard_border_thickness;
930 tmp.y = hud_fontsize.y * 1.25;
932 // fill the table and draw the rows
935 for(pl = players.sort_next; pl; pl = pl.sort_next)
937 if(pl.team != tm.team)
939 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
940 pos.y += 1.25 * hud_fontsize.y;
944 for(pl = players.sort_next; pl; pl = pl.sort_next)
946 if(pl.team == NUM_SPECTATOR)
948 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
949 pos.y += 1.25 * hud_fontsize.y;
954 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
955 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
960 float HUD_WouldDrawScoreboard() {
961 if (autocvar__hud_configure)
963 else if (HUD_Radar_Clickable())
965 else if (scoreboard_showscores)
967 else if (intermission == 1)
969 else if (intermission == 2)
971 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
973 else if (scoreboard_showscores_force)
978 float average_accuracy;
979 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
982 int weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
984 if(autocvar_scoreboard_accuracy_doublerows)
989 float fontsize = height * 1/3;
990 float weapon_height = height * 2/3;
991 float weapon_width = sbwidth / weapon_cnt;
992 float g_instagib = 0;
994 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
995 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
996 vector tmp = '0 0 0';
998 tmp.y = height * rows;
1001 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1003 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1004 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1006 // column highlighting
1007 for(i = 0; i < weapon_cnt/rows; ++i)
1010 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);
1014 for(i = 0; i < rows; ++i)
1016 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);
1019 average_accuracy = 0;
1020 float weapons_with_stats;
1021 weapons_with_stats = 0;
1023 pos.x += weapon_width / 2;
1025 if(switchweapon == WEP_VAPORIZER)
1026 g_instagib = 1; // TODO: real detection for instagib?
1029 if(autocvar_scoreboard_accuracy_nocolors)
1032 Accuracy_LoadColors();
1034 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1036 self = get_weaponinfo(i);
1039 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
1041 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1044 if(weapon_stats >= 0)
1045 weapon_alpha = scoreboard_alpha_fg;
1047 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1050 drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1052 if(weapon_stats >= 0) {
1053 weapons_with_stats += 1;
1054 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1057 s = sprintf("%d%%", weapon_stats*100);
1060 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1062 if(!autocvar_scoreboard_accuracy_nocolors)
1063 rgb = Accuracy_GetColor(weapon_stats);
1065 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1067 pos.x += weapon_width * rows;
1068 if(rows == 2 && i == 6) {
1074 if(weapons_with_stats)
1075 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1078 pos.x -= weapon_width / 2;
1082 pos.y += 1.25 * hud_fontsize.y;
1086 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1088 pos.x += hud_fontsize.x * 0.25;
1089 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1090 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1091 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1093 pos.y+= hud_fontsize.y;
1098 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1099 float stat_secrets_found, stat_secrets_total;
1100 float stat_monsters_killed, stat_monsters_total;
1104 // get monster stats
1105 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1106 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1108 // get secrets stats
1109 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1110 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1112 // get number of rows
1113 if(stat_secrets_total)
1115 if(stat_monsters_total)
1118 // if no rows, return
1122 // draw table header
1123 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1124 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1127 vector tmp = '0 0 0';
1129 tmp.y = hud_fontsize.y * rows;
1132 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1134 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1135 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1138 if(stat_monsters_total)
1140 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1141 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1145 if(stat_secrets_total)
1147 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1148 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1152 pos.y += 1.25 * hud_fontsize.y;
1157 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1160 RANKINGS_RECEIVED_CNT = 0;
1161 for (i=RANKINGS_CNT-1; i>=0; --i)
1163 ++RANKINGS_RECEIVED_CNT;
1165 if (RANKINGS_RECEIVED_CNT == 0)
1169 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1171 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1172 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1173 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1175 pos.y += hud_fontsize.y;
1176 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1177 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1178 vector tmp = '0 0 0';
1180 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1183 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1185 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1186 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1189 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1196 n = grecordholder[i];
1197 p = count_ordinal(i+1);
1198 if(grecordholder[i] == GetPlayerName(player_localnum))
1199 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1200 else if(!(i % 2) && scoreboard_highlight)
1201 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1202 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1203 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);
1204 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1205 pos.y += 1.25 * hud_fontsize.y;
1207 pos.y += autocvar_scoreboard_border_thickness;
1212 float hud_woulddrawscoreboard_prev;
1213 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1214 void HUD_DrawScoreboard()
1216 float hud_woulddrawscoreboard;
1217 hud_woulddrawscoreboard = scoreboard_active;
1218 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1219 hud_woulddrawscoreboard_change = time;
1220 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1223 if(hud_woulddrawscoreboard) {
1224 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1225 if (scoreboard_fadeinspeed)
1226 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1228 scoreboard_fade_alpha = 1;
1231 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1232 if (scoreboard_fadeoutspeed)
1233 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1235 scoreboard_fade_alpha = 0;
1238 if (!scoreboard_fade_alpha)
1241 HUD_UpdatePlayerTeams();
1243 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1244 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1245 scoreboard_highlight = autocvar_scoreboard_highlight;
1246 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1247 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1248 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1249 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1251 vector rgb, pos, tmp;
1255 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1256 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1258 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1259 ymax = (vid_conheight - ymin);
1261 sbwidth = xmax - xmin;
1263 // Initializes position
1269 vector sb_heading_fontsize;
1270 sb_heading_fontsize = hud_fontsize * 2;
1271 draw_beginBoldFont();
1272 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1275 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1277 // Draw the scoreboard
1278 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1282 vector team_score_baseoffset;
1283 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1284 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1286 if(tm.team == NUM_SPECTATOR)
1289 draw_beginBoldFont();
1290 rgb = Team_ColorRGB(tm.team);
1291 str = ftos(tm.(teamscores[ts_primary]));
1292 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1294 if(ts_primary != ts_secondary)
1296 str = ftos(tm.(teamscores[ts_secondary]));
1297 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);
1301 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1303 rgb.x = autocvar_scoreboard_color_bg_r;
1304 rgb.y = autocvar_scoreboard_color_bg_g;
1305 rgb.z = autocvar_scoreboard_color_bg_b;
1309 rgb.x = autocvar_scoreboard_color_bg_r;
1310 rgb.y = autocvar_scoreboard_color_bg_g;
1311 rgb.z = autocvar_scoreboard_color_bg_b;
1313 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1315 if(tm.team == NUM_SPECTATOR)
1318 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1322 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1323 if(race_speedaward) {
1324 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1325 pos.y += 1.25 * hud_fontsize.y;
1327 if(race_speedaward_alltimebest) {
1328 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1329 pos.y += 1.25 * hud_fontsize.y;
1331 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1333 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1335 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1337 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1342 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1344 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1351 item_size.x = sbwidth;
1352 item_size.y = hud_fontsize.y * 1.25;
1354 for(pl = players.sort_next; pl; pl = pl.sort_next)
1356 if(pl.team != NUM_SPECTATOR)
1358 pos.y += 1.25 * hud_fontsize.y;
1359 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1365 draw_beginBoldFont();
1366 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1368 pos.y += 1.25 * hud_fontsize.y;
1371 // Print info string
1373 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1374 tl = getstatf(STAT_TIMELIMIT);
1375 fl = getstatf(STAT_FRAGLIMIT);
1376 ll = getstatf(STAT_LEADLIMIT);
1377 if(gametype == MAPINFO_TYPE_LMS)
1380 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1385 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1389 str = strcat(str, _(" or"));
1392 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1393 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1394 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1395 TranslateScoresLabel(teamscores_label[ts_primary])));
1399 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1400 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1401 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1402 TranslateScoresLabel(scores_label[ps_primary])));
1407 if(tl > 0 || fl > 0)
1408 str = strcat(str, _(" or"));
1411 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1412 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1413 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1414 TranslateScoresLabel(teamscores_label[ts_primary])));
1418 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1419 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1420 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1421 TranslateScoresLabel(scores_label[ps_primary])));
1426 pos.y += 1.2 * hud_fontsize.y;
1427 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1429 // print information about respawn status
1430 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1434 if(respawn_time < 0)
1436 // a negative number means we are awaiting respawn, time value is still the same
1437 respawn_time *= -1; // remove mark now that we checked it
1438 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1440 str = sprintf(_("^1Respawning in ^3%s^1..."),
1441 (autocvar_scoreboard_respawntime_decimals ?
1442 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1444 count_seconds(respawn_time - time)
1448 else if(time < respawn_time)
1450 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1451 (autocvar_scoreboard_respawntime_decimals ?
1452 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1454 count_seconds(respawn_time - time)
1458 else if(time >= respawn_time)
1459 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1461 pos.y += 1.2 * hud_fontsize.y;
1462 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1465 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;