1 #include "scoreboard.qh"
3 float scoreboard_alpha_bg;
4 float scoreboard_alpha_fg;
5 float scoreboard_highlight;
6 float scoreboard_highlight_alpha;
7 float scoreboard_highlight_alpha_self;
8 float scoreboard_alpha_name;
9 float scoreboard_alpha_name_self;
11 void drawstringright(vector, string, vector, vector, float, float);
12 void drawstringcenter(vector, string, vector, vector, float, float);
14 const float SCOREBOARD_OFFSET = 50;
16 // wrapper to put all possible scores titles through gettext
17 string TranslateScoresLabel(string l)
21 case "bckills": return CTX(_("SCO^bckills"));
22 case "bctime": return CTX(_("SCO^bctime"));
23 case "caps": return CTX(_("SCO^caps"));
24 case "captime": return CTX(_("SCO^captime"));
25 case "deaths": return CTX(_("SCO^deaths"));
26 case "destroyed": return CTX(_("SCO^destroyed"));
27 case "drops": return CTX(_("SCO^drops"));
28 case "faults": return CTX(_("SCO^faults"));
29 case "fckills": return CTX(_("SCO^fckills"));
30 case "goals": return CTX(_("SCO^goals"));
31 case "kckills": return CTX(_("SCO^kckills"));
32 case "kdratio": return CTX(_("SCO^kdratio"));
33 case "k/d": return CTX(_("SCO^k/d"));
34 case "kd": return CTX(_("SCO^kd"));
35 case "kdr": return CTX(_("SCO^kdr"));
36 case "kills": return CTX(_("SCO^kills"));
37 case "laps": return CTX(_("SCO^laps"));
38 case "lives": return CTX(_("SCO^lives"));
39 case "losses": return CTX(_("SCO^losses"));
40 case "name": return CTX(_("SCO^name"));
41 case "sum": return CTX(_("SCO^sum"));
42 case "nick": return CTX(_("SCO^nick"));
43 case "objectives": return CTX(_("SCO^objectives"));
44 case "pickups": return CTX(_("SCO^pickups"));
45 case "ping": return CTX(_("SCO^ping"));
46 case "pl": return CTX(_("SCO^pl"));
47 case "pushes": return CTX(_("SCO^pushes"));
48 case "rank": return CTX(_("SCO^rank"));
49 case "returns": return CTX(_("SCO^returns"));
50 case "revivals": return CTX(_("SCO^revivals"));
51 case "score": return CTX(_("SCO^score"));
52 case "suicides": return CTX(_("SCO^suicides"));
53 case "takes": return CTX(_("SCO^takes"));
54 case "ticks": return CTX(_("SCO^ticks"));
63 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
64 for(i = 0; i < MAX_SCORE; ++i)
66 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
67 if(f == SFL_SORT_PRIO_PRIMARY)
69 if(f == SFL_SORT_PRIO_SECONDARY)
72 if(ps_secondary == -1)
73 ps_secondary = ps_primary;
75 for(i = 0; i < MAX_TEAMSCORE; ++i)
77 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
78 if(f == SFL_SORT_PRIO_PRIMARY)
80 if(f == SFL_SORT_PRIO_SECONDARY)
83 if(ts_secondary == -1)
84 ts_secondary = ts_primary;
89 float SetTeam(entity pl, float Team);
91 void HUD_UpdatePlayerTeams()
98 for(pl = players.sort_next; pl; pl = pl.sort_next)
101 Team = GetPlayerColor(pl.sv_entnum);
102 if(SetTeam(pl, Team))
105 HUD_UpdatePlayerPos(pl);
109 pl = players.sort_next;
114 print(strcat("PNUM: ", ftos(num), "\n"));
119 int HUD_CompareScore(float vl, float vr, int f)
121 if(f & SFL_ZERO_IS_WORST)
123 if(vl == 0 && vr != 0)
125 if(vl != 0 && vr == 0)
129 return IS_INCREASING(f);
131 return IS_DECREASING(f);
135 float HUD_ComparePlayerScores(entity left, entity right)
138 vl = GetPlayerColor(left.sv_entnum);
139 vr = GetPlayerColor(right.sv_entnum);
151 if(vl == NUM_SPECTATOR)
153 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
155 if(!left.gotscores && right.gotscores)
160 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
164 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
169 for(i = 0; i < MAX_SCORE; ++i)
171 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
176 if (left.sv_entnum < right.sv_entnum)
182 void HUD_UpdatePlayerPos(entity player)
184 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
186 SORT_SWAP(player, other);
188 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
190 SORT_SWAP(other, player);
194 float HUD_CompareTeamScores(entity left, entity right)
198 if(left.team == NUM_SPECTATOR)
200 if(right.team == NUM_SPECTATOR)
203 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
207 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
211 for(i = 0; i < MAX_SCORE; ++i)
213 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
218 if (left.team < right.team)
224 void HUD_UpdateTeamPos(entity Team)
226 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
228 SORT_SWAP(Team, other);
230 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
232 SORT_SWAP(other, Team);
238 print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
239 print(_("^3|---------------------------------------------------------------|\n"));
240 print(_("Usage:\n"));
241 print(_("^2scoreboard_columns_set default\n"));
242 print(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
243 print(_("The following field names are recognized (case insensitive):\n"));
244 print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
246 print(_("^3name^7 or ^3nick^7 Name of a player\n"));
247 print(_("^3ping^7 Ping time\n"));
248 print(_("^3pl^7 Packet loss\n"));
249 print(_("^3kills^7 Number of kills\n"));
250 print(_("^3deaths^7 Number of deaths\n"));
251 print(_("^3suicides^7 Number of suicides\n"));
252 print(_("^3frags^7 kills - suicides\n"));
253 print(_("^3kd^7 The kill-death ratio\n"));
254 print(_("^3sum^7 frags - deaths\n"));
255 print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
256 print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
257 print(_("^3captime^7 Time of fastest cap (CTF)\n"));
258 print(_("^3fckills^7 Number of flag carrier kills\n"));
259 print(_("^3returns^7 Number of flag returns\n"));
260 print(_("^3drops^7 Number of flag drops\n"));
261 print(_("^3lives^7 Number of lives (LMS)\n"));
262 print(_("^3rank^7 Player rank\n"));
263 print(_("^3pushes^7 Number of players pushed into void\n"));
264 print(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
265 print(_("^3kckills^7 Number of keys carrier kills\n"));
266 print(_("^3losses^7 Number of times a key was lost\n"));
267 print(_("^3laps^7 Number of laps finished (race/cts)\n"));
268 print(_("^3time^7 Total time raced (race/cts)\n"));
269 print(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
270 print(_("^3ticks^7 Number of ticks (DOM)\n"));
271 print(_("^3takes^7 Number of domination points taken (DOM)\n"));
272 print(_("^3bckills^7 Number of ball carrier kills\n"));
273 print(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
274 print(_("^3score^7 Total score\n\n"));
276 print(_("Before a field you can put a + or - sign, then a comma separated list\n"
277 "of game types, then a slash, to make the field show up only in these\n"
278 "or in all but these game types. You can also specify 'all' as a\n"
279 "field to show all fields available for the current game mode.\n\n"));
281 print(_("The special game type names 'teams' and 'noteams' can be used to\n"
282 "include/exclude ALL teams/noteams game modes.\n\n"));
284 print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
285 print(_("will display name, ping and pl aligned to the left, and the fields\n"
286 "right of the vertical bar aligned to the right.\n"));
287 print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
288 "other gamemodes except DM.\n"));
291 #define HUD_DefaultColumnLayout() \
293 "-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" */ \
294 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \
295 "+lms/lives +lms/rank " \
296 "+kh/caps +kh/pushes +kh/destroyed " \
297 "?+race/laps ?+race/time ?+race/fastest " \
298 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
301 void Cmd_HUD_SetFields(float argc)
305 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
310 // set up a temporary scoreboard layout
311 // no layout can be properly set up until score_info data haven't been received
312 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
314 scores_label[ps_primary] = strzone("score");
315 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
318 // TODO: re enable with gametype dependant cvars?
319 if(argc < 3) // no arguments provided
320 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
323 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
327 if(argv(2) == "default")
328 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
329 else if(argv(2) == "all")
332 s = "ping pl name |";
333 for(i = 0; i < MAX_SCORE; ++i)
336 if(i != ps_secondary)
337 if(scores_label[i] != "")
338 s = strcat(s, " ", scores_label[i]);
340 if(ps_secondary != ps_primary)
341 s = strcat(s, " ", scores_label[ps_secondary]);
342 s = strcat(s, " ", scores_label[ps_primary]);
343 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
350 hud_fontsize = HUD_GetFontsize("hud_fontsize");
352 draw_beginBoldFont();
353 for(i = 1; i < argc - 1; ++i)
359 if(substring(str, 0, 1) == "?")
362 str = substring(str, 1, strlen(str) - 1);
365 slash = strstrofs(str, "/", 0);
368 pattern = substring(str, 0, slash);
369 str = substring(str, slash + 1, strlen(str) - (slash + 1));
371 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
375 strunzone(hud_title[hud_num_fields]);
376 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
377 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
378 str = strtolower(str);
381 hud_field[hud_num_fields] = SP_PING;
382 } else if(str == "pl") {
383 hud_field[hud_num_fields] = SP_PL;
384 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
385 hud_field[hud_num_fields] = SP_KDRATIO;
386 } else if(str == "sum" || str == "diff" || str == "k-d") {
387 hud_field[hud_num_fields] = SP_SUM;
388 } else if(str == "name" || str == "nick") {
389 hud_field[hud_num_fields] = SP_NAME;
391 } else if(str == "|") {
392 hud_field[hud_num_fields] = SP_SEPARATOR;
395 for(j = 0; j < MAX_SCORE; ++j)
396 if(str == strtolower(scores_label[j]))
397 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
406 printf("^1Error:^7 Unknown score field: '%s'\n", str);
410 hud_field[hud_num_fields] = j;
413 if(j == ps_secondary)
417 if(hud_num_fields >= MAX_HUD_FIELDS)
421 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
423 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
425 if(ps_primary == ps_secondary)
427 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
429 if(hud_num_fields+missing < MAX_HUD_FIELDS)
433 strunzone(hud_title[hud_num_fields]);
434 for(i = hud_num_fields; i > 0; --i)
436 hud_title[i] = hud_title[i-1];
437 hud_size[i] = hud_size[i-1];
438 hud_field[i] = hud_field[i-1];
440 hud_title[0] = strzone(TranslateScoresLabel("name"));
441 hud_field[0] = SP_NAME;
443 print("fixed missing field 'name'\n");
447 strunzone(hud_title[hud_num_fields]);
448 for(i = hud_num_fields; i > 1; --i)
450 hud_title[i] = hud_title[i-1];
451 hud_size[i] = hud_size[i-1];
452 hud_field[i] = hud_field[i-1];
454 hud_title[1] = strzone("|");
455 hud_field[1] = SP_SEPARATOR;
456 hud_size[1] = stringwidth("|", false, hud_fontsize);
458 print("fixed missing field '|'\n");
461 else if(!have_separator)
463 strunzone(hud_title[hud_num_fields]);
464 hud_title[hud_num_fields] = strzone("|");
465 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
466 hud_field[hud_num_fields] = SP_SEPARATOR;
468 print("fixed missing field '|'\n");
472 strunzone(hud_title[hud_num_fields]);
473 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
474 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
475 hud_field[hud_num_fields] = ps_secondary;
477 printf("fixed missing field '%s'\n", scores_label[ps_secondary]);
481 strunzone(hud_title[hud_num_fields]);
482 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
483 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
484 hud_field[hud_num_fields] = ps_primary;
486 printf("fixed missing field '%s'\n", scores_label[ps_primary]);
490 hud_field[hud_num_fields] = SP_END;
495 vector hud_field_rgb;
496 string hud_field_icon0;
497 string hud_field_icon1;
498 string hud_field_icon2;
499 vector hud_field_icon0_rgb;
500 vector hud_field_icon1_rgb;
501 vector hud_field_icon2_rgb;
502 float hud_field_icon0_alpha;
503 float hud_field_icon1_alpha;
504 float hud_field_icon2_alpha;
505 string HUD_GetField(entity pl, int field)
507 float tmp, num, denom;
510 hud_field_rgb = '1 1 1';
511 hud_field_icon0 = "";
512 hud_field_icon1 = "";
513 hud_field_icon2 = "";
514 hud_field_icon0_rgb = '1 1 1';
515 hud_field_icon1_rgb = '1 1 1';
516 hud_field_icon2_rgb = '1 1 1';
517 hud_field_icon0_alpha = 1;
518 hud_field_icon1_alpha = 1;
519 hud_field_icon2_alpha = 1;
524 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
525 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
529 tmp = max(0, min(220, f-80)) / 220;
530 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
536 f = pl.ping_packetloss;
537 tmp = pl.ping_movementloss;
538 if(f == 0 && tmp == 0)
540 str = ftos(ceil(f * 100));
542 str = strcat(str, "~", ftos(ceil(tmp * 100)));
543 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
544 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
548 if(ready_waiting && pl.ready)
550 hud_field_icon0 = "gfx/scoreboard/player_ready";
554 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
556 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
557 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
558 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
559 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
560 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
563 return GetPlayerName(pl.sv_entnum);
566 f = pl.(scores[SP_KILLS]);
567 f -= pl.(scores[SP_SUICIDES]);
571 num = pl.(scores[SP_KILLS]);
572 denom = pl.(scores[SP_DEATHS]);
575 hud_field_rgb = '0 1 0';
576 str = sprintf("%d", num);
577 } else if(num <= 0) {
578 hud_field_rgb = '1 0 0';
579 str = sprintf("%.1f", num/denom);
581 str = sprintf("%.1f", num/denom);
585 f = pl.(scores[SP_KILLS]);
586 f -= pl.(scores[SP_DEATHS]);
589 hud_field_rgb = '0 1 0';
591 hud_field_rgb = '1 1 1';
593 hud_field_rgb = '1 0 0';
598 tmp = pl.(scores[field]);
599 f = scores_flags[field];
600 if(field == ps_primary)
601 hud_field_rgb = '1 1 0';
602 else if(field == ps_secondary)
603 hud_field_rgb = '0 1 1';
605 hud_field_rgb = '1 1 1';
606 return ScoreString(f, tmp);
611 float hud_fixscoreboardcolumnwidth_len;
612 float hud_fixscoreboardcolumnwidth_iconlen;
613 float hud_fixscoreboardcolumnwidth_marginlen;
615 string HUD_FixScoreboardColumnWidth(int i, string str)
619 field = hud_field[i];
621 hud_fixscoreboardcolumnwidth_iconlen = 0;
623 if(hud_field_icon0 != "")
625 sz = draw_getimagesize(hud_field_icon0);
627 if(hud_fixscoreboardcolumnwidth_iconlen < f)
628 hud_fixscoreboardcolumnwidth_iconlen = f;
631 if(hud_field_icon1 != "")
633 sz = draw_getimagesize(hud_field_icon1);
635 if(hud_fixscoreboardcolumnwidth_iconlen < f)
636 hud_fixscoreboardcolumnwidth_iconlen = f;
639 if(hud_field_icon2 != "")
641 sz = draw_getimagesize(hud_field_icon2);
643 if(hud_fixscoreboardcolumnwidth_iconlen < f)
644 hud_fixscoreboardcolumnwidth_iconlen = f;
647 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
649 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
650 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
652 hud_fixscoreboardcolumnwidth_marginlen = 0;
654 if(field == SP_NAME) // name gets all remaining space
658 namesize = sbwidth;// / hud_fontsize_x;
659 for(j = 0; j < hud_num_fields; ++j)
661 if (hud_field[i] != SP_SEPARATOR)
662 namesize -= hud_size[j] + hud_fontsize.x;
663 namesize += hud_fontsize.x;
664 hud_size[i] = namesize;
666 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
667 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
668 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
669 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
672 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
674 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
681 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
684 rgb = Team_ColorRGB(pl.team);
688 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
690 if((rgb == '1 1 1') && (!is_spec)) {
691 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
692 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
693 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
695 vector h_pos = pos - '1 1 0';
696 vector h_size = item_size + '2 0 0';
697 // alternated rows highlighting
699 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
700 else if((scoreboard_highlight) && (!(pl_number % 2)))
701 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
707 for(i = 0; i < hud_num_fields; ++i)
709 field = hud_field[i];
710 if(field == SP_SEPARATOR)
713 if(is_spec && field != SP_NAME && field != SP_PING) {
714 pos.x += hud_size[i] + hud_fontsize.x;
717 str = HUD_GetField(pl, field);
718 str = HUD_FixScoreboardColumnWidth(i, str);
720 pos.x += hud_size[i] + hud_fontsize.x;
722 if(field == SP_NAME) {
723 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
725 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
727 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
729 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
731 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
733 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
736 tmp.x = hud_size[i] + hud_fontsize.x;
737 if(hud_field_icon0 != "")
739 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);
741 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);
742 if(hud_field_icon1 != "")
744 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);
746 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);
747 if(hud_field_icon2 != "")
749 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);
751 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);
754 if(hud_field[i] == SP_SEPARATOR)
757 for(i = hud_num_fields-1; i > 0; --i)
759 field = hud_field[i];
760 if(field == SP_SEPARATOR)
763 if(is_spec && field != SP_NAME && field != SP_PING) {
764 pos.x -= hud_size[i] + hud_fontsize.x;
768 str = HUD_GetField(pl, field);
769 str = HUD_FixScoreboardColumnWidth(i, str);
771 if(field == SP_NAME) {
772 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
774 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
776 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
778 tmp.x = hud_fixscoreboardcolumnwidth_len;
780 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
782 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
786 if(hud_field_icon0 != "")
788 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);
790 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);
791 if(hud_field_icon1 != "")
793 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);
795 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);
796 if(hud_field_icon2 != "")
798 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);
800 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);
801 pos.x -= hud_size[i] + hud_fontsize.x;
806 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
810 * HUD_Scoreboard_MakeTable
812 * Makes a table for a team (for all playing players in DM) and fills it
815 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
817 float body_table_height;
818 vector tmp = '0 0 0', column_dim = '0 0 0';
821 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
823 pos.y += autocvar_scoreboard_border_thickness;
827 tmp.y = 1.25 * hud_fontsize.y;
831 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
833 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
836 tmp.y += autocvar_scoreboard_border_thickness;
837 tmp.y += body_table_height;
838 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
840 // separator header/table
841 pos.y += 1.25 * hud_fontsize.y;
842 tmp.y = autocvar_scoreboard_border_thickness;
843 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
845 pos.y += autocvar_scoreboard_border_thickness;
848 tmp.y = body_table_height;
850 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
852 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
854 // anyway, apply some color
855 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
857 // go back to the top to make alternated columns highlighting and to print the strings
858 pos.y -= 1.25 * hud_fontsize.y;
859 pos.y -= autocvar_scoreboard_border_thickness;
863 if (scoreboard_highlight)
865 column_dim.y = 1.25 * hud_fontsize.y; // header
866 column_dim.y += autocvar_scoreboard_border_thickness;
867 column_dim.y += body_table_height;
870 // print the strings of the columns headers and draw the columns
871 draw_beginBoldFont();
873 for(i = 0; i < hud_num_fields; ++i)
875 if(hud_field[i] == SP_SEPARATOR)
877 column_dim.x = hud_size[i] + hud_fontsize.x;
878 if (scoreboard_highlight)
881 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
883 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
884 pos.x += column_dim.x;
886 if(hud_field[i] == SP_SEPARATOR)
890 for(i = hud_num_fields-1; i > 0; --i)
892 if(hud_field[i] == SP_SEPARATOR)
895 pos.x -= hud_size[i];
897 if (scoreboard_highlight)
901 if (i == hud_num_fields-1)
902 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
904 column_dim.x = hud_size[i] + hud_fontsize.x;
905 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
909 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
910 tmp.x = (hud_size[i] - tmp.x);
911 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
912 pos.x -= hud_fontsize.x;
918 pos.y += 1.25 * hud_fontsize.y; // skip the header
919 pos.y += autocvar_scoreboard_border_thickness;
923 tmp.y = hud_fontsize.y * 1.25;
925 // fill the table and draw the rows
928 for(pl = players.sort_next; pl; pl = pl.sort_next)
930 if(pl.team != tm.team)
932 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
933 pos.y += 1.25 * hud_fontsize.y;
937 for(pl = players.sort_next; pl; pl = pl.sort_next)
939 if(pl.team == NUM_SPECTATOR)
941 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
942 pos.y += 1.25 * hud_fontsize.y;
947 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
948 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
953 float HUD_WouldDrawScoreboard() {
954 if (autocvar__hud_configure)
956 else if (scoreboard_showscores)
958 else if (intermission == 1)
960 else if (intermission == 2)
962 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
964 else if (scoreboard_showscores_force)
969 float average_accuracy;
970 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
973 int weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
975 if(autocvar_scoreboard_accuracy_doublerows)
980 float fontsize = height * 1/3;
981 float weapon_height = height * 2/3;
982 float weapon_width = sbwidth / weapon_cnt;
983 float g_instagib = 0;
985 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
986 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
987 vector tmp = '0 0 0';
989 tmp.y = height * rows;
992 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
994 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
995 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
997 // column highlighting
998 for(i = 0; i < weapon_cnt/rows; ++i)
1001 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);
1005 for(i = 0; i < rows; ++i)
1007 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);
1010 average_accuracy = 0;
1011 float weapons_with_stats;
1012 weapons_with_stats = 0;
1014 pos.x += weapon_width / 2;
1016 if(switchweapon == WEP_VAPORIZER)
1017 g_instagib = 1; // TODO: real detection for instagib?
1020 if(autocvar_scoreboard_accuracy_nocolors)
1023 Accuracy_LoadColors();
1025 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1027 self = get_weaponinfo(i);
1030 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
1032 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1035 if(weapon_stats >= 0)
1036 weapon_alpha = scoreboard_alpha_fg;
1038 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1041 drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1043 if(weapon_stats >= 0) {
1044 weapons_with_stats += 1;
1045 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1048 s = sprintf("%d%%", weapon_stats*100);
1051 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1053 if(!autocvar_scoreboard_accuracy_nocolors)
1054 rgb = Accuracy_GetColor(weapon_stats);
1056 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1058 pos.x += weapon_width * rows;
1059 if(rows == 2 && i == 6) {
1065 if(weapons_with_stats)
1066 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1069 pos.x -= weapon_width / 2;
1073 pos.y += 1.25 * hud_fontsize.y;
1077 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1079 pos.x += hud_fontsize.x * 0.25;
1080 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1081 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1082 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1084 pos.y+= hud_fontsize.y;
1089 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1090 float stat_secrets_found, stat_secrets_total;
1091 float stat_monsters_killed, stat_monsters_total;
1095 // get monster stats
1096 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1097 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1099 // get secrets stats
1100 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1101 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1103 // get number of rows
1104 if(stat_secrets_total)
1106 if(stat_monsters_total)
1109 // if no rows, return
1113 // draw table header
1114 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1115 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1118 vector tmp = '0 0 0';
1120 tmp.y = hud_fontsize.y * rows;
1123 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1125 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1126 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1129 if(stat_monsters_total)
1131 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1132 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1136 if(stat_secrets_total)
1138 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1139 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1143 pos.y += 1.25 * hud_fontsize.y;
1148 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1151 RANKINGS_RECEIVED_CNT = 0;
1152 for (i=RANKINGS_CNT-1; i>=0; --i)
1154 ++RANKINGS_RECEIVED_CNT;
1156 if (RANKINGS_RECEIVED_CNT == 0)
1160 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1162 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1163 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1164 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1166 pos.y += hud_fontsize.y;
1167 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1168 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1169 vector tmp = '0 0 0';
1171 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1174 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1176 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1177 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1180 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1187 n = grecordholder[i];
1188 p = count_ordinal(i+1);
1189 if(grecordholder[i] == GetPlayerName(player_localnum))
1190 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1191 else if(!(i % 2) && scoreboard_highlight)
1192 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1193 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1194 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);
1195 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1196 pos.y += 1.25 * hud_fontsize.y;
1198 pos.y += autocvar_scoreboard_border_thickness;
1203 float hud_woulddrawscoreboard_prev;
1204 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1205 void HUD_DrawScoreboard()
1207 float hud_woulddrawscoreboard;
1208 hud_woulddrawscoreboard = scoreboard_active;
1209 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1210 hud_woulddrawscoreboard_change = time;
1211 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1214 if(hud_woulddrawscoreboard) {
1215 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1216 if (scoreboard_fadeinspeed)
1217 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1219 scoreboard_fade_alpha = 1;
1222 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1223 if (scoreboard_fadeoutspeed)
1224 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1226 scoreboard_fade_alpha = 0;
1229 if (!scoreboard_fade_alpha)
1232 HUD_UpdatePlayerTeams();
1234 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1235 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1236 scoreboard_highlight = autocvar_scoreboard_highlight;
1237 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1238 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1239 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1240 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1242 vector rgb, pos, tmp;
1246 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1247 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1249 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1250 ymax = (vid_conheight - ymin);
1252 sbwidth = xmax - xmin;
1254 // Initializes position
1260 vector sb_heading_fontsize;
1261 sb_heading_fontsize = hud_fontsize * 2;
1262 draw_beginBoldFont();
1263 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1266 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1268 // Draw the scoreboard
1269 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1273 vector team_score_baseoffset;
1274 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1275 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1277 if(tm.team == NUM_SPECTATOR)
1280 draw_beginBoldFont();
1281 rgb = Team_ColorRGB(tm.team);
1282 str = ftos(tm.(teamscores[ts_primary]));
1283 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1285 if(ts_primary != ts_secondary)
1287 str = ftos(tm.(teamscores[ts_secondary]));
1288 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);
1292 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1294 rgb.x = autocvar_scoreboard_color_bg_r;
1295 rgb.y = autocvar_scoreboard_color_bg_g;
1296 rgb.z = autocvar_scoreboard_color_bg_b;
1300 rgb.x = autocvar_scoreboard_color_bg_r;
1301 rgb.y = autocvar_scoreboard_color_bg_g;
1302 rgb.z = autocvar_scoreboard_color_bg_b;
1304 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1306 if(tm.team == NUM_SPECTATOR)
1309 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1313 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1314 if(race_speedaward) {
1315 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1316 pos.y += 1.25 * hud_fontsize.y;
1318 if(race_speedaward_alltimebest) {
1319 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1320 pos.y += 1.25 * hud_fontsize.y;
1322 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1324 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1326 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1328 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1333 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1335 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1342 item_size.x = sbwidth;
1343 item_size.y = hud_fontsize.y * 1.25;
1345 for(pl = players.sort_next; pl; pl = pl.sort_next)
1347 if(pl.team != NUM_SPECTATOR)
1349 pos.y += 1.25 * hud_fontsize.y;
1350 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1356 draw_beginBoldFont();
1357 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1359 pos.y += 1.25 * hud_fontsize.y;
1362 // Print info string
1364 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1365 tl = getstatf(STAT_TIMELIMIT);
1366 fl = getstatf(STAT_FRAGLIMIT);
1367 ll = getstatf(STAT_LEADLIMIT);
1368 if(gametype == MAPINFO_TYPE_LMS)
1371 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1376 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1380 str = strcat(str, _(" or"));
1383 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1384 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1385 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1386 TranslateScoresLabel(teamscores_label[ts_primary])));
1390 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1391 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1392 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1393 TranslateScoresLabel(scores_label[ps_primary])));
1398 if(tl > 0 || fl > 0)
1399 str = strcat(str, _(" or"));
1402 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1403 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1404 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1405 TranslateScoresLabel(teamscores_label[ts_primary])));
1409 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1410 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1411 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1412 TranslateScoresLabel(scores_label[ps_primary])));
1417 pos.y += 1.2 * hud_fontsize.y;
1418 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1420 // print information about respawn status
1421 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1425 if(respawn_time < 0)
1427 // a negative number means we are awaiting respawn, time value is still the same
1428 respawn_time *= -1; // remove mark now that we checked it
1429 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1431 str = sprintf(_("^1Respawning in ^3%s^1..."),
1432 (autocvar_scoreboard_respawntime_decimals ?
1433 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1435 count_seconds(respawn_time - time)
1439 else if(time < respawn_time)
1441 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1442 (autocvar_scoreboard_respawntime_decimals ?
1443 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1445 count_seconds(respawn_time - time)
1449 else if(time >= respawn_time)
1450 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1452 pos.y += 1.2 * hud_fontsize.y;
1453 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1456 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;