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 (HUD_QuickMenu_IsOpened())
958 else if (scoreboard_showscores)
960 else if (intermission == 1)
962 else if (intermission == 2)
964 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
966 else if (scoreboard_showscores_force)
971 float average_accuracy;
972 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
975 int weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
977 if(autocvar_scoreboard_accuracy_doublerows)
982 float fontsize = height * 1/3;
983 float weapon_height = height * 2/3;
984 float weapon_width = sbwidth / weapon_cnt;
985 float g_instagib = 0;
987 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
988 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
989 vector tmp = '0 0 0';
991 tmp.y = height * rows;
994 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
996 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
997 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
999 // column highlighting
1000 for(i = 0; i < weapon_cnt/rows; ++i)
1003 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);
1007 for(i = 0; i < rows; ++i)
1009 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);
1012 average_accuracy = 0;
1013 float weapons_with_stats;
1014 weapons_with_stats = 0;
1016 pos.x += weapon_width / 2;
1018 if(switchweapon == WEP_VAPORIZER)
1019 g_instagib = 1; // TODO: real detection for instagib?
1022 if(autocvar_scoreboard_accuracy_nocolors)
1025 Accuracy_LoadColors();
1027 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1029 self = get_weaponinfo(i);
1032 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
1034 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1037 if(weapon_stats >= 0)
1038 weapon_alpha = scoreboard_alpha_fg;
1040 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1043 drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1045 if(weapon_stats >= 0) {
1046 weapons_with_stats += 1;
1047 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1050 s = sprintf("%d%%", weapon_stats*100);
1053 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1055 if(!autocvar_scoreboard_accuracy_nocolors)
1056 rgb = Accuracy_GetColor(weapon_stats);
1058 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1060 pos.x += weapon_width * rows;
1061 if(rows == 2 && i == 6) {
1067 if(weapons_with_stats)
1068 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1071 pos.x -= weapon_width / 2;
1075 pos.y += 1.25 * hud_fontsize.y;
1079 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1081 pos.x += hud_fontsize.x * 0.25;
1082 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1083 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1084 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1086 pos.y+= hud_fontsize.y;
1091 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1092 float stat_secrets_found, stat_secrets_total;
1093 float stat_monsters_killed, stat_monsters_total;
1097 // get monster stats
1098 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1099 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1101 // get secrets stats
1102 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1103 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1105 // get number of rows
1106 if(stat_secrets_total)
1108 if(stat_monsters_total)
1111 // if no rows, return
1115 // draw table header
1116 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1117 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1120 vector tmp = '0 0 0';
1122 tmp.y = hud_fontsize.y * rows;
1125 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1127 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1128 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1131 if(stat_monsters_total)
1133 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1134 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1138 if(stat_secrets_total)
1140 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1141 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1145 pos.y += 1.25 * hud_fontsize.y;
1150 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1153 RANKINGS_RECEIVED_CNT = 0;
1154 for (i=RANKINGS_CNT-1; i>=0; --i)
1156 ++RANKINGS_RECEIVED_CNT;
1158 if (RANKINGS_RECEIVED_CNT == 0)
1162 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1164 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1165 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1166 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1168 pos.y += hud_fontsize.y;
1169 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1170 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1171 vector tmp = '0 0 0';
1173 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1176 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1178 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1179 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1182 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1189 n = grecordholder[i];
1190 p = count_ordinal(i+1);
1191 if(grecordholder[i] == GetPlayerName(player_localnum))
1192 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1193 else if(!(i % 2) && scoreboard_highlight)
1194 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1195 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1196 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);
1197 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1198 pos.y += 1.25 * hud_fontsize.y;
1200 pos.y += autocvar_scoreboard_border_thickness;
1205 float hud_woulddrawscoreboard_prev;
1206 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1207 void HUD_DrawScoreboard()
1209 float hud_woulddrawscoreboard;
1210 hud_woulddrawscoreboard = scoreboard_active;
1211 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1212 hud_woulddrawscoreboard_change = time;
1213 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1216 if(hud_woulddrawscoreboard) {
1217 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1218 if (scoreboard_fadeinspeed)
1219 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1221 scoreboard_fade_alpha = 1;
1224 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1225 if (scoreboard_fadeoutspeed)
1226 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1228 scoreboard_fade_alpha = 0;
1231 if (!scoreboard_fade_alpha)
1234 HUD_UpdatePlayerTeams();
1236 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1237 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1238 scoreboard_highlight = autocvar_scoreboard_highlight;
1239 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1240 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1241 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1242 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1244 vector rgb, pos, tmp;
1248 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1249 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1251 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1252 ymax = (vid_conheight - ymin);
1254 sbwidth = xmax - xmin;
1256 // Initializes position
1262 vector sb_heading_fontsize;
1263 sb_heading_fontsize = hud_fontsize * 2;
1264 draw_beginBoldFont();
1265 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1268 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1270 // Draw the scoreboard
1271 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1275 vector team_score_baseoffset;
1276 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1277 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1279 if(tm.team == NUM_SPECTATOR)
1282 draw_beginBoldFont();
1283 rgb = Team_ColorRGB(tm.team);
1284 str = ftos(tm.(teamscores[ts_primary]));
1285 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1287 if(ts_primary != ts_secondary)
1289 str = ftos(tm.(teamscores[ts_secondary]));
1290 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);
1294 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1296 rgb.x = autocvar_scoreboard_color_bg_r;
1297 rgb.y = autocvar_scoreboard_color_bg_g;
1298 rgb.z = autocvar_scoreboard_color_bg_b;
1302 rgb.x = autocvar_scoreboard_color_bg_r;
1303 rgb.y = autocvar_scoreboard_color_bg_g;
1304 rgb.z = autocvar_scoreboard_color_bg_b;
1306 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1308 if(tm.team == NUM_SPECTATOR)
1311 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1315 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1316 if(race_speedaward) {
1317 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1318 pos.y += 1.25 * hud_fontsize.y;
1320 if(race_speedaward_alltimebest) {
1321 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1322 pos.y += 1.25 * hud_fontsize.y;
1324 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1326 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1328 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1330 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1335 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1337 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1344 item_size.x = sbwidth;
1345 item_size.y = hud_fontsize.y * 1.25;
1347 for(pl = players.sort_next; pl; pl = pl.sort_next)
1349 if(pl.team != NUM_SPECTATOR)
1351 pos.y += 1.25 * hud_fontsize.y;
1352 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1358 draw_beginBoldFont();
1359 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1361 pos.y += 1.25 * hud_fontsize.y;
1364 // Print info string
1366 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1367 tl = getstatf(STAT_TIMELIMIT);
1368 fl = getstatf(STAT_FRAGLIMIT);
1369 ll = getstatf(STAT_LEADLIMIT);
1370 if(gametype == MAPINFO_TYPE_LMS)
1373 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1378 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1382 str = strcat(str, _(" or"));
1385 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1386 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1387 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1388 TranslateScoresLabel(teamscores_label[ts_primary])));
1392 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1393 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1394 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1395 TranslateScoresLabel(scores_label[ps_primary])));
1400 if(tl > 0 || fl > 0)
1401 str = strcat(str, _(" or"));
1404 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1405 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1406 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1407 TranslateScoresLabel(teamscores_label[ts_primary])));
1411 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1412 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1413 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1414 TranslateScoresLabel(scores_label[ps_primary])));
1419 pos.y += 1.2 * hud_fontsize.y;
1420 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1422 // print information about respawn status
1423 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1427 if(respawn_time < 0)
1429 // a negative number means we are awaiting respawn, time value is still the same
1430 respawn_time *= -1; // remove mark now that we checked it
1431 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1433 str = sprintf(_("^1Respawning in ^3%s^1..."),
1434 (autocvar_scoreboard_respawntime_decimals ?
1435 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1437 count_seconds(respawn_time - time)
1441 else if(time < respawn_time)
1443 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1444 (autocvar_scoreboard_respawntime_decimals ?
1445 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1447 count_seconds(respawn_time - time)
1451 else if(time >= respawn_time)
1452 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1454 pos.y += 1.2 * hud_fontsize.y;
1455 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1458 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;