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;
998 if(autocvar_scoreboard_accuracy_doublerows)
1003 float fontsize = height * 1/3;
1004 float weapon_height = height * 2/3;
1005 float weapon_width = sbwidth / weapon_cnt;
1006 float g_instagib = 0;
1008 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1009 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1010 vector tmp = '0 0 0';
1012 tmp.y = height * rows;
1015 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1017 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1018 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1020 // column highlighting
1021 for(i = 0; i < weapon_cnt/rows; ++i)
1024 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);
1028 for(i = 0; i < rows; ++i)
1030 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);
1033 average_accuracy = 0;
1034 float weapons_with_stats;
1035 weapons_with_stats = 0;
1037 pos.x += weapon_width / 2;
1039 if(switchweapon == WEP_VAPORIZER)
1040 g_instagib = 1; // TODO: real detection for instagib?
1042 if(autocvar_scoreboard_accuracy_nocolors)
1045 Accuracy_LoadColors();
1047 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1049 self = get_weaponinfo(i);
1052 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1054 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1058 if(weapon_stats >= 0)
1059 weapon_alpha = scoreboard_alpha_fg;
1061 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1064 drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1066 if(weapon_stats >= 0) {
1067 weapons_with_stats += 1;
1068 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1071 s = sprintf("%d%%", weapon_stats*100);
1074 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1076 if(!autocvar_scoreboard_accuracy_nocolors)
1077 rgb = Accuracy_GetColor(weapon_stats);
1079 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1081 pos.x += weapon_width * rows;
1082 if(rows == 2 && i == 6) {
1088 if(weapons_with_stats)
1089 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1092 pos.x -= weapon_width / 2;
1096 pos.y += 1.25 * hud_fontsize.y;
1100 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1102 pos.x += hud_fontsize.x * 0.25;
1103 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1104 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1105 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1107 pos.y+= hud_fontsize.y;
1112 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1113 float stat_secrets_found, stat_secrets_total;
1114 float stat_monsters_killed, stat_monsters_total;
1118 // get monster stats
1119 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1120 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1122 // get secrets stats
1123 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1124 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1126 // get number of rows
1127 if(stat_secrets_total)
1129 if(stat_monsters_total)
1132 // if no rows, return
1136 // draw table header
1137 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1138 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1141 vector tmp = '0 0 0';
1143 tmp.y = hud_fontsize.y * rows;
1146 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1148 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1149 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1152 if(stat_monsters_total)
1154 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1155 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1159 if(stat_secrets_total)
1161 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1162 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1166 pos.y += 1.25 * hud_fontsize.y;
1171 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1174 RANKINGS_RECEIVED_CNT = 0;
1175 for (i=RANKINGS_CNT-1; i>=0; --i)
1177 ++RANKINGS_RECEIVED_CNT;
1179 if (RANKINGS_RECEIVED_CNT == 0)
1183 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1185 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1186 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1187 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1189 pos.y += hud_fontsize.y;
1190 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1191 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1192 vector tmp = '0 0 0';
1194 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1197 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1199 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1200 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1203 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1210 n = grecordholder[i];
1211 p = count_ordinal(i+1);
1212 if(grecordholder[i] == GetPlayerName(player_localnum))
1213 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1214 else if(!(i % 2) && scoreboard_highlight)
1215 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1216 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1217 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);
1218 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1219 pos.y += 1.25 * hud_fontsize.y;
1221 pos.y += autocvar_scoreboard_border_thickness;
1226 float hud_woulddrawscoreboard_prev;
1227 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1228 void HUD_DrawScoreboard()
1230 float hud_woulddrawscoreboard;
1231 hud_woulddrawscoreboard = scoreboard_active;
1232 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1233 hud_woulddrawscoreboard_change = time;
1234 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1237 if(hud_woulddrawscoreboard) {
1238 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1239 if (scoreboard_fadeinspeed)
1240 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1242 scoreboard_fade_alpha = 1;
1245 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1246 if (scoreboard_fadeoutspeed)
1247 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1249 scoreboard_fade_alpha = 0;
1252 if (!scoreboard_fade_alpha)
1255 HUD_UpdatePlayerTeams();
1257 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1258 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1259 scoreboard_highlight = autocvar_scoreboard_highlight;
1260 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1261 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1262 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1263 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1265 vector rgb, pos, tmp;
1269 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1270 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1272 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1273 ymax = (vid_conheight - ymin);
1275 sbwidth = xmax - xmin;
1277 // Initializes position
1283 vector sb_heading_fontsize;
1284 sb_heading_fontsize = hud_fontsize * 2;
1285 draw_beginBoldFont();
1286 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1289 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1291 // Draw the scoreboard
1292 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1296 vector team_score_baseoffset;
1297 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1298 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1300 if(tm.team == NUM_SPECTATOR)
1303 draw_beginBoldFont();
1304 rgb = Team_ColorRGB(tm.team);
1305 str = ftos(tm.(teamscores[ts_primary]));
1306 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1308 if(ts_primary != ts_secondary)
1310 str = ftos(tm.(teamscores[ts_secondary]));
1311 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);
1315 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1317 rgb.x = autocvar_scoreboard_color_bg_r;
1318 rgb.y = autocvar_scoreboard_color_bg_g;
1319 rgb.z = autocvar_scoreboard_color_bg_b;
1323 rgb.x = autocvar_scoreboard_color_bg_r;
1324 rgb.y = autocvar_scoreboard_color_bg_g;
1325 rgb.z = autocvar_scoreboard_color_bg_b;
1327 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1329 if(tm.team == NUM_SPECTATOR)
1332 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1336 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1337 if(race_speedaward) {
1338 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1339 pos.y += 1.25 * hud_fontsize.y;
1341 if(race_speedaward_alltimebest) {
1342 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1343 pos.y += 1.25 * hud_fontsize.y;
1345 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1347 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1349 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1351 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1356 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1358 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1365 item_size.x = sbwidth;
1366 item_size.y = hud_fontsize.y * 1.25;
1368 for(pl = players.sort_next; pl; pl = pl.sort_next)
1370 if(pl.team != NUM_SPECTATOR)
1372 pos.y += 1.25 * hud_fontsize.y;
1373 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1379 draw_beginBoldFont();
1380 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1382 pos.y += 1.25 * hud_fontsize.y;
1385 // Print info string
1387 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1388 tl = getstatf(STAT_TIMELIMIT);
1389 fl = getstatf(STAT_FRAGLIMIT);
1390 ll = getstatf(STAT_LEADLIMIT);
1391 if(gametype == MAPINFO_TYPE_LMS)
1394 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1399 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1403 str = strcat(str, _(" or"));
1406 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1407 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1408 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1409 TranslateScoresLabel(teamscores_label[ts_primary])));
1413 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1414 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1415 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1416 TranslateScoresLabel(scores_label[ps_primary])));
1421 if(tl > 0 || fl > 0)
1422 str = strcat(str, _(" or"));
1425 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1426 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1427 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1428 TranslateScoresLabel(teamscores_label[ts_primary])));
1432 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1433 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1434 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1435 TranslateScoresLabel(scores_label[ps_primary])));
1440 pos.y += 1.2 * hud_fontsize.y;
1441 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1443 // print information about respawn status
1444 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1448 if(respawn_time < 0)
1450 // a negative number means we are awaiting respawn, time value is still the same
1451 respawn_time *= -1; // remove mark now that we checked it
1452 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1454 str = sprintf(_("^1Respawning in ^3%s^1..."),
1455 (autocvar_scoreboard_respawntime_decimals ?
1456 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1458 count_seconds(respawn_time - time)
1462 else if(time < respawn_time)
1464 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1465 (autocvar_scoreboard_respawntime_decimals ?
1466 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1468 count_seconds(respawn_time - time)
1472 else if(time >= respawn_time)
1473 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1475 pos.y += 1.2 * hud_fontsize.y;
1476 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1479 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;