1 #include "scoreboard.qh"
7 #include "../common/constants.qh"
8 #include "../common/counting.qh"
9 #include "../common/mapinfo.qh"
10 #include "../common/stats.qh"
11 #include "../common/teams.qh"
12 #include "../common/util.qh"
14 float scoreboard_alpha_bg;
15 float scoreboard_alpha_fg;
16 float scoreboard_highlight;
17 float scoreboard_highlight_alpha;
18 float scoreboard_highlight_alpha_self;
19 float scoreboard_alpha_name;
20 float scoreboard_alpha_name_self;
22 void drawstringright(vector, string, vector, vector, float, float);
23 void drawstringcenter(vector, string, vector, vector, float, float);
25 const float SCOREBOARD_OFFSET = 50;
27 // wrapper to put all possible scores titles through gettext
28 string TranslateScoresLabel(string l)
32 case "bckills": return CTX(_("SCO^bckills"));
33 case "bctime": return CTX(_("SCO^bctime"));
34 case "caps": return CTX(_("SCO^caps"));
35 case "captime": return CTX(_("SCO^captime"));
36 case "deaths": return CTX(_("SCO^deaths"));
37 case "destroyed": return CTX(_("SCO^destroyed"));
38 case "drops": return CTX(_("SCO^drops"));
39 case "faults": return CTX(_("SCO^faults"));
40 case "fckills": return CTX(_("SCO^fckills"));
41 case "goals": return CTX(_("SCO^goals"));
42 case "kckills": return CTX(_("SCO^kckills"));
43 case "kdratio": return CTX(_("SCO^kdratio"));
44 case "k/d": return CTX(_("SCO^k/d"));
45 case "kd": return CTX(_("SCO^kd"));
46 case "kdr": return CTX(_("SCO^kdr"));
47 case "kills": return CTX(_("SCO^kills"));
48 case "laps": return CTX(_("SCO^laps"));
49 case "lives": return CTX(_("SCO^lives"));
50 case "losses": return CTX(_("SCO^losses"));
51 case "name": return CTX(_("SCO^name"));
52 case "sum": return CTX(_("SCO^sum"));
53 case "nick": return CTX(_("SCO^nick"));
54 case "objectives": return CTX(_("SCO^objectives"));
55 case "pickups": return CTX(_("SCO^pickups"));
56 case "ping": return CTX(_("SCO^ping"));
57 case "pl": return CTX(_("SCO^pl"));
58 case "pushes": return CTX(_("SCO^pushes"));
59 case "rank": return CTX(_("SCO^rank"));
60 case "returns": return CTX(_("SCO^returns"));
61 case "revivals": return CTX(_("SCO^revivals"));
62 case "score": return CTX(_("SCO^score"));
63 case "suicides": return CTX(_("SCO^suicides"));
64 case "takes": return CTX(_("SCO^takes"));
65 case "ticks": return CTX(_("SCO^ticks"));
74 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
75 for(i = 0; i < MAX_SCORE; ++i)
77 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
78 if(f == SFL_SORT_PRIO_PRIMARY)
80 if(f == SFL_SORT_PRIO_SECONDARY)
83 if(ps_secondary == -1)
84 ps_secondary = ps_primary;
86 for(i = 0; i < MAX_TEAMSCORE; ++i)
88 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
89 if(f == SFL_SORT_PRIO_PRIMARY)
91 if(f == SFL_SORT_PRIO_SECONDARY)
94 if(ts_secondary == -1)
95 ts_secondary = ts_primary;
100 float SetTeam(entity pl, float Team);
102 void HUD_UpdatePlayerTeams()
109 for(pl = players.sort_next; pl; pl = pl.sort_next)
112 Team = GetPlayerColor(pl.sv_entnum);
113 if(SetTeam(pl, Team))
116 HUD_UpdatePlayerPos(pl);
120 pl = players.sort_next;
125 print(strcat("PNUM: ", ftos(num), "\n"));
130 int HUD_CompareScore(float vl, float vr, int f)
132 if(f & SFL_ZERO_IS_WORST)
134 if(vl == 0 && vr != 0)
136 if(vl != 0 && vr == 0)
140 return IS_INCREASING(f);
142 return IS_DECREASING(f);
146 float HUD_ComparePlayerScores(entity left, entity right)
149 vl = GetPlayerColor(left.sv_entnum);
150 vr = GetPlayerColor(right.sv_entnum);
162 if(vl == NUM_SPECTATOR)
164 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
166 if(!left.gotscores && right.gotscores)
171 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
175 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
180 for(i = 0; i < MAX_SCORE; ++i)
182 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
187 if (left.sv_entnum < right.sv_entnum)
193 void HUD_UpdatePlayerPos(entity player)
195 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
197 SORT_SWAP(player, other);
199 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
201 SORT_SWAP(other, player);
205 float HUD_CompareTeamScores(entity left, entity right)
209 if(left.team == NUM_SPECTATOR)
211 if(right.team == NUM_SPECTATOR)
214 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
218 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
222 for(i = 0; i < MAX_SCORE; ++i)
224 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
229 if (left.team < right.team)
235 void HUD_UpdateTeamPos(entity Team)
237 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
239 SORT_SWAP(Team, other);
241 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
243 SORT_SWAP(other, Team);
249 print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
250 print(_("^3|---------------------------------------------------------------|\n"));
251 print(_("Usage:\n"));
252 print(_("^2scoreboard_columns_set default\n"));
253 print(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
254 print(_("The following field names are recognized (case insensitive):\n"));
255 print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
257 print(_("^3name^7 or ^3nick^7 Name of a player\n"));
258 print(_("^3ping^7 Ping time\n"));
259 print(_("^3pl^7 Packet loss\n"));
260 print(_("^3kills^7 Number of kills\n"));
261 print(_("^3deaths^7 Number of deaths\n"));
262 print(_("^3suicides^7 Number of suicides\n"));
263 print(_("^3frags^7 kills - suicides\n"));
264 print(_("^3kd^7 The kill-death ratio\n"));
265 print(_("^3sum^7 frags - deaths\n"));
266 print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
267 print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
268 print(_("^3captime^7 Time of fastest cap (CTF)\n"));
269 print(_("^3fckills^7 Number of flag carrier kills\n"));
270 print(_("^3returns^7 Number of flag returns\n"));
271 print(_("^3drops^7 Number of flag drops\n"));
272 print(_("^3lives^7 Number of lives (LMS)\n"));
273 print(_("^3rank^7 Player rank\n"));
274 print(_("^3pushes^7 Number of players pushed into void\n"));
275 print(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
276 print(_("^3kckills^7 Number of keys carrier kills\n"));
277 print(_("^3losses^7 Number of times a key was lost\n"));
278 print(_("^3laps^7 Number of laps finished (race/cts)\n"));
279 print(_("^3time^7 Total time raced (race/cts)\n"));
280 print(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
281 print(_("^3ticks^7 Number of ticks (DOM)\n"));
282 print(_("^3takes^7 Number of domination points taken (DOM)\n"));
283 print(_("^3bckills^7 Number of ball carrier kills\n"));
284 print(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
285 print(_("^3score^7 Total score\n\n"));
287 print(_("Before a field you can put a + or - sign, then a comma separated list\n"
288 "of game types, then a slash, to make the field show up only in these\n"
289 "or in all but these game types. You can also specify 'all' as a\n"
290 "field to show all fields available for the current game mode.\n\n"));
292 print(_("The special game type names 'teams' and 'noteams' can be used to\n"
293 "include/exclude ALL teams/noteams game modes.\n\n"));
295 print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
296 print(_("will display name, ping and pl aligned to the left, and the fields\n"
297 "right of the vertical bar aligned to the right.\n"));
298 print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
299 "other gamemodes except DM.\n"));
302 #define HUD_DefaultColumnLayout() \
304 "-teams,race,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,race,ka/suicides +ft,tdm/suicides -race,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
305 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \
306 "+lms/lives +lms/rank " \
307 "+kh/caps +kh/pushes +kh/destroyed " \
308 "?+race/laps ?+race/time ?+race/fastest " \
309 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
312 void Cmd_HUD_SetFields(float argc)
316 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
321 // set up a temporary scoreboard layout
322 // no layout can be properly set up until score_info data haven't been received
323 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
325 scores_label[ps_primary] = strzone("score");
326 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
329 // TODO: re enable with gametype dependant cvars?
330 if(argc < 3) // no arguments provided
331 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
334 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
338 if(argv(2) == "default")
339 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
340 else if(argv(2) == "all")
343 s = "ping pl name |";
344 for(i = 0; i < MAX_SCORE; ++i)
347 if(i != ps_secondary)
348 if(scores_label[i] != "")
349 s = strcat(s, " ", scores_label[i]);
351 if(ps_secondary != ps_primary)
352 s = strcat(s, " ", scores_label[ps_secondary]);
353 s = strcat(s, " ", scores_label[ps_primary]);
354 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
361 hud_fontsize = HUD_GetFontsize("hud_fontsize");
363 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 (scoreboard_showscores)
965 else if (intermission == 1)
967 else if (intermission == 2)
969 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
971 else if (scoreboard_showscores_force)
976 float average_accuracy;
977 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
979 WepSet weapons_stat = WepSet_GetFromStat();
980 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
984 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
986 self = get_weaponinfo(i);
990 weapon_stats = weapon_accuracy[i-WEP_FIRST];
992 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
996 int weapon_cnt = WEP_COUNT - disownedcnt;
1002 if(autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor(WEP_COUNT * 0.5))
1006 int count_perrow = rint(weapon_cnt / rows);
1008 float fontsize = height * 1/3;
1009 float weapon_height = height * 2/3;
1010 float weapon_width = sbwidth / weapon_cnt / rows;
1011 bool g_instagib = false;
1013 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1014 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1015 vector tmp = '0 0 0';
1017 tmp.y = height * rows;
1020 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1022 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1023 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1025 // column highlighting
1026 for(i = 0; i < count_perrow; ++i)
1029 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);
1033 for(i = 0; i < rows; ++i)
1035 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);
1038 average_accuracy = 0;
1039 int weapons_with_stats = 0;
1041 pos.x += weapon_width / 2;
1043 if(switchweapon == WEP_VAPORIZER)
1044 g_instagib = true; // TODO: real detection for instagib?
1046 if(autocvar_scoreboard_accuracy_nocolors)
1049 Accuracy_LoadColors();
1051 float oldposx = pos.x;
1054 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1056 self = get_weaponinfo(i);
1059 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1061 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1065 if(weapon_stats >= 0)
1066 weapon_alpha = scoreboard_alpha_fg;
1068 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1071 drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1073 if(weapon_stats >= 0) {
1074 weapons_with_stats += 1;
1075 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1078 s = sprintf("%d%%", weapon_stats*100);
1081 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1083 if(!autocvar_scoreboard_accuracy_nocolors)
1084 rgb = Accuracy_GetColor(weapon_stats);
1086 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1088 tmpos.x += weapon_width * rows;
1089 pos.x += weapon_width * rows;
1090 if(rows == 2 && i == count_perrow) {
1097 if(weapons_with_stats)
1098 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1101 pos.x -= weapon_width / 2;
1105 pos.y += 1.25 * hud_fontsize.y;
1109 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1111 pos.x += hud_fontsize.x * 0.25;
1112 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1113 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1114 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1116 pos.y+= hud_fontsize.y;
1121 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1122 float stat_secrets_found, stat_secrets_total;
1123 float stat_monsters_killed, stat_monsters_total;
1127 // get monster stats
1128 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1129 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1131 // get secrets stats
1132 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1133 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1135 // get number of rows
1136 if(stat_secrets_total)
1138 if(stat_monsters_total)
1141 // if no rows, return
1145 // draw table header
1146 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1147 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1150 vector tmp = '0 0 0';
1152 tmp.y = hud_fontsize.y * rows;
1155 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1157 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1158 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1161 if(stat_monsters_total)
1163 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1164 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1168 if(stat_secrets_total)
1170 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1171 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1175 pos.y += 1.25 * hud_fontsize.y;
1180 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1183 RANKINGS_RECEIVED_CNT = 0;
1184 for (i=RANKINGS_CNT-1; i>=0; --i)
1186 ++RANKINGS_RECEIVED_CNT;
1188 if (RANKINGS_RECEIVED_CNT == 0)
1192 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1194 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1195 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1196 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1198 pos.y += hud_fontsize.y;
1199 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1200 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1201 vector tmp = '0 0 0';
1203 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1206 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1208 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1209 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1212 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1219 n = grecordholder[i];
1220 p = count_ordinal(i+1);
1221 if(grecordholder[i] == GetPlayerName(player_localnum))
1222 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1223 else if(!(i % 2) && scoreboard_highlight)
1224 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1225 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1226 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);
1227 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1228 pos.y += 1.25 * hud_fontsize.y;
1230 pos.y += autocvar_scoreboard_border_thickness;
1235 float hud_woulddrawscoreboard_prev;
1236 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1237 void HUD_DrawScoreboard()
1239 float hud_woulddrawscoreboard;
1240 hud_woulddrawscoreboard = scoreboard_active;
1241 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1242 hud_woulddrawscoreboard_change = time;
1243 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1246 if(hud_woulddrawscoreboard) {
1247 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1248 if (scoreboard_fadeinspeed)
1249 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1251 scoreboard_fade_alpha = 1;
1254 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1255 if (scoreboard_fadeoutspeed)
1256 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1258 scoreboard_fade_alpha = 0;
1261 if (!scoreboard_fade_alpha)
1264 HUD_UpdatePlayerTeams();
1266 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1267 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1268 scoreboard_highlight = autocvar_scoreboard_highlight;
1269 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1270 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1271 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1272 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1274 vector rgb, pos, tmp;
1278 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1279 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1281 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1282 ymax = (vid_conheight - ymin);
1284 sbwidth = xmax - xmin;
1286 // Initializes position
1292 vector sb_heading_fontsize;
1293 sb_heading_fontsize = hud_fontsize * 2;
1294 draw_beginBoldFont();
1295 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1298 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1300 // Draw the scoreboard
1301 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1305 vector team_score_baseoffset;
1306 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1307 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1309 if(tm.team == NUM_SPECTATOR)
1312 draw_beginBoldFont();
1313 rgb = Team_ColorRGB(tm.team);
1314 str = ftos(tm.(teamscores[ts_primary]));
1315 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1317 if(ts_primary != ts_secondary)
1319 str = ftos(tm.(teamscores[ts_secondary]));
1320 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);
1324 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1326 rgb.x = autocvar_scoreboard_color_bg_r;
1327 rgb.y = autocvar_scoreboard_color_bg_g;
1328 rgb.z = autocvar_scoreboard_color_bg_b;
1332 rgb.x = autocvar_scoreboard_color_bg_r;
1333 rgb.y = autocvar_scoreboard_color_bg_g;
1334 rgb.z = autocvar_scoreboard_color_bg_b;
1336 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1338 if(tm.team == NUM_SPECTATOR)
1341 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1345 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1346 if(race_speedaward) {
1347 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1348 pos.y += 1.25 * hud_fontsize.y;
1350 if(race_speedaward_alltimebest) {
1351 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1352 pos.y += 1.25 * hud_fontsize.y;
1354 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1356 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1358 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1360 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1365 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1367 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1374 item_size.x = sbwidth;
1375 item_size.y = hud_fontsize.y * 1.25;
1377 for(pl = players.sort_next; pl; pl = pl.sort_next)
1379 if(pl.team != NUM_SPECTATOR)
1381 pos.y += 1.25 * hud_fontsize.y;
1382 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1388 draw_beginBoldFont();
1389 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1391 pos.y += 1.25 * hud_fontsize.y;
1394 // Print info string
1396 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1397 tl = getstatf(STAT_TIMELIMIT);
1398 fl = getstatf(STAT_FRAGLIMIT);
1399 ll = getstatf(STAT_LEADLIMIT);
1400 if(gametype == MAPINFO_TYPE_LMS)
1403 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1408 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1412 str = strcat(str, _(" or"));
1415 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1416 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1417 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1418 TranslateScoresLabel(teamscores_label[ts_primary])));
1422 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1423 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1424 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1425 TranslateScoresLabel(scores_label[ps_primary])));
1430 if(tl > 0 || fl > 0)
1431 str = strcat(str, _(" or"));
1434 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1435 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1436 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1437 TranslateScoresLabel(teamscores_label[ts_primary])));
1441 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1442 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1443 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1444 TranslateScoresLabel(scores_label[ps_primary])));
1449 pos.y += 1.2 * hud_fontsize.y;
1450 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1452 // print information about respawn status
1453 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1457 if(respawn_time < 0)
1459 // a negative number means we are awaiting respawn, time value is still the same
1460 respawn_time *= -1; // remove mark now that we checked it
1461 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1463 str = sprintf(_("^1Respawning in ^3%s^1..."),
1464 (autocvar_scoreboard_respawntime_decimals ?
1465 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1467 count_seconds(respawn_time - time)
1471 else if(time < respawn_time)
1473 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1474 (autocvar_scoreboard_respawntime_decimals ?
1475 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1477 count_seconds(respawn_time - time)
1481 else if(time >= respawn_time)
1482 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1484 pos.y += 1.2 * hud_fontsize.y;
1485 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1488 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;