1 float scoreboard_alpha_bg;
2 float scoreboard_alpha_fg;
3 float scoreboard_highlight;
4 float scoreboard_highlight_alpha;
5 float scoreboard_highlight_alpha_self;
6 float scoreboard_alpha_name;
7 float scoreboard_alpha_name_self;
9 void drawstringright(vector, string, vector, vector, float, float);
10 void drawstringcenter(vector, string, vector, vector, float, float);
12 const float SCOREBOARD_OFFSET = 50;
14 // wrapper to put all possible scores titles through gettext
15 string TranslateScoresLabel(string l)
19 case "bckills": return CTX(_("SCO^bckills"));
20 case "bctime": return CTX(_("SCO^bctime"));
21 case "caps": return CTX(_("SCO^caps"));
22 case "captime": return CTX(_("SCO^captime"));
23 case "deaths": return CTX(_("SCO^deaths"));
24 case "destroyed": return CTX(_("SCO^destroyed"));
25 case "drops": return CTX(_("SCO^drops"));
26 case "faults": return CTX(_("SCO^faults"));
27 case "fckills": return CTX(_("SCO^fckills"));
28 case "goals": return CTX(_("SCO^goals"));
29 case "kckills": return CTX(_("SCO^kckills"));
30 case "kdratio": return CTX(_("SCO^kdratio"));
31 case "k/d": return CTX(_("SCO^k/d"));
32 case "kd": return CTX(_("SCO^kd"));
33 case "kdr": return CTX(_("SCO^kdr"));
34 case "kills": return CTX(_("SCO^kills"));
35 case "laps": return CTX(_("SCO^laps"));
36 case "lives": return CTX(_("SCO^lives"));
37 case "losses": return CTX(_("SCO^losses"));
38 case "name": return CTX(_("SCO^name"));
39 case "sum": return CTX(_("SCO^sum"));
40 case "nick": return CTX(_("SCO^nick"));
41 case "objectives": return CTX(_("SCO^objectives"));
42 case "pickups": return CTX(_("SCO^pickups"));
43 case "ping": return CTX(_("SCO^ping"));
44 case "pl": return CTX(_("SCO^pl"));
45 case "pushes": return CTX(_("SCO^pushes"));
46 case "rank": return CTX(_("SCO^rank"));
47 case "returns": return CTX(_("SCO^returns"));
48 case "revivals": return CTX(_("SCO^revivals"));
49 case "score": return CTX(_("SCO^score"));
50 case "suicides": return CTX(_("SCO^suicides"));
51 case "takes": return CTX(_("SCO^takes"));
52 case "ticks": return CTX(_("SCO^ticks"));
57 void Cmd_HUD_SetFields(float argc);
62 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
63 for(i = 0; i < MAX_SCORE; ++i)
65 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
66 if(f == SFL_SORT_PRIO_PRIMARY)
68 if(f == SFL_SORT_PRIO_SECONDARY)
71 if(ps_secondary == -1)
72 ps_secondary = ps_primary;
74 for(i = 0; i < MAX_TEAMSCORE; ++i)
76 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
77 if(f == SFL_SORT_PRIO_PRIMARY)
79 if(f == SFL_SORT_PRIO_SECONDARY)
82 if(ts_secondary == -1)
83 ts_secondary = ts_primary;
88 void HUD_UpdatePlayerPos(entity pl);
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]);
168 for(i = 0; i < MAX_SCORE; ++i)
170 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
175 if (left.sv_entnum < right.sv_entnum)
181 void HUD_UpdatePlayerPos(entity player)
183 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
185 SORT_SWAP(player, other);
187 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
189 SORT_SWAP(other, player);
193 float HUD_CompareTeamScores(entity left, entity right)
197 if(left.team == NUM_SPECTATOR)
199 if(right.team == NUM_SPECTATOR)
202 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
206 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
210 for(i = 0; i < MAX_SCORE; ++i)
212 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
217 if (left.team < right.team)
223 void HUD_UpdateTeamPos(entity Team)
225 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
227 SORT_SWAP(Team, other);
229 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
231 SORT_SWAP(other, Team);
237 print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
238 print(_("^3|---------------------------------------------------------------|\n"));
239 print(_("Usage:\n"));
240 print(_("^2scoreboard_columns_set default\n"));
241 print(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
242 print(_("The following field names are recognized (case insensitive):\n"));
243 print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
245 print(_("^3name^7 or ^3nick^7 Name of a player\n"));
246 print(_("^3ping^7 Ping time\n"));
247 print(_("^3pl^7 Packet loss\n"));
248 print(_("^3kills^7 Number of kills\n"));
249 print(_("^3deaths^7 Number of deaths\n"));
250 print(_("^3suicides^7 Number of suicides\n"));
251 print(_("^3frags^7 kills - suicides\n"));
252 print(_("^3kd^7 The kill-death ratio\n"));
253 print(_("^3sum^7 frags - deaths\n"));
254 print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
255 print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
256 print(_("^3captime^7 Time of fastest cap (CTF)\n"));
257 print(_("^3fckills^7 Number of flag carrier kills\n"));
258 print(_("^3returns^7 Number of flag returns\n"));
259 print(_("^3drops^7 Number of flag drops\n"));
260 print(_("^3lives^7 Number of lives (LMS)\n"));
261 print(_("^3rank^7 Player rank\n"));
262 print(_("^3pushes^7 Number of players pushed into void\n"));
263 print(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
264 print(_("^3kckills^7 Number of keys carrier kills\n"));
265 print(_("^3losses^7 Number of times a key was lost\n"));
266 print(_("^3laps^7 Number of laps finished (race/cts)\n"));
267 print(_("^3time^7 Total time raced (race/cts)\n"));
268 print(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
269 print(_("^3ticks^7 Number of ticks (DOM)\n"));
270 print(_("^3takes^7 Number of domination points taken (DOM)\n"));
271 print(_("^3bckills^7 Number of ball carrier kills\n"));
272 print(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
273 print(_("^3score^7 Total score\n\n"));
275 print(_("Before a field you can put a + or - sign, then a comma separated list\n"
276 "of game types, then a slash, to make the field show up only in these\n"
277 "or in all but these game types. You can also specify 'all' as a\n"
278 "field to show all fields available for the current game mode.\n\n"));
280 print(_("The special game type names 'teams' and 'noteams' can be used to\n"
281 "include/exclude ALL teams/noteams game modes.\n\n"));
283 print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
284 print(_("will display name, ping and pl aligned to the left, and the fields\n"
285 "right of the vertical bar aligned to the right.\n"));
286 print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
287 "other gamemodes except DM.\n"));
290 #define HUD_DefaultColumnLayout() \
292 "-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" */ \
293 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \
294 "+lms/lives +lms/rank " \
295 "+kh/caps +kh/pushes +kh/destroyed " \
296 "?+race/laps ?+race/time ?+race/fastest " \
297 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
300 void Cmd_HUD_SetFields(float argc)
304 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
309 // set up a temporary scoreboard layout
310 // no layout can be properly set up until score_info data haven't been received
311 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
313 scores_label[ps_primary] = strzone("score");
314 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
317 // TODO: re enable with gametype dependant cvars?
318 if(argc < 3) // no arguments provided
319 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
322 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
326 if(argv(2) == "default")
327 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
328 else if(argv(2) == "all")
331 s = "ping pl name |";
332 for(i = 0; i < MAX_SCORE; ++i)
335 if(i != ps_secondary)
336 if(scores_label[i] != "")
337 s = strcat(s, " ", scores_label[i]);
339 if(ps_secondary != ps_primary)
340 s = strcat(s, " ", scores_label[ps_secondary]);
341 s = strcat(s, " ", scores_label[ps_primary]);
342 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
349 hud_fontsize = HUD_GetFontsize("hud_fontsize");
351 draw_beginBoldFont();
352 for(i = 1; i < argc - 1; ++i)
358 if(substring(str, 0, 1) == "?")
361 str = substring(str, 1, strlen(str) - 1);
364 slash = strstrofs(str, "/", 0);
367 pattern = substring(str, 0, slash);
368 str = substring(str, slash + 1, strlen(str) - (slash + 1));
370 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
374 strunzone(hud_title[hud_num_fields]);
375 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
376 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
377 str = strtolower(str);
380 hud_field[hud_num_fields] = SP_PING;
381 } else if(str == "pl") {
382 hud_field[hud_num_fields] = SP_PL;
383 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
384 hud_field[hud_num_fields] = SP_KDRATIO;
385 } else if(str == "sum" || str == "diff" || str == "k-d") {
386 hud_field[hud_num_fields] = SP_SUM;
387 } else if(str == "name" || str == "nick") {
388 hud_field[hud_num_fields] = SP_NAME;
390 } else if(str == "|") {
391 hud_field[hud_num_fields] = SP_SEPARATOR;
394 for(j = 0; j < MAX_SCORE; ++j)
395 if(str == strtolower(scores_label[j]))
396 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
405 printf("^1Error:^7 Unknown score field: '%s'\n", str);
409 hud_field[hud_num_fields] = j;
412 if(j == ps_secondary)
416 if(hud_num_fields >= MAX_HUD_FIELDS)
420 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
422 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
424 if(ps_primary == ps_secondary)
426 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
428 if(hud_num_fields+missing < MAX_HUD_FIELDS)
432 strunzone(hud_title[hud_num_fields]);
433 for(i = hud_num_fields; i > 0; --i)
435 hud_title[i] = hud_title[i-1];
436 hud_size[i] = hud_size[i-1];
437 hud_field[i] = hud_field[i-1];
439 hud_title[0] = strzone(TranslateScoresLabel("name"));
440 hud_field[0] = SP_NAME;
442 print("fixed missing field 'name'\n");
446 strunzone(hud_title[hud_num_fields]);
447 for(i = hud_num_fields; i > 1; --i)
449 hud_title[i] = hud_title[i-1];
450 hud_size[i] = hud_size[i-1];
451 hud_field[i] = hud_field[i-1];
453 hud_title[1] = strzone("|");
454 hud_field[1] = SP_SEPARATOR;
455 hud_size[1] = stringwidth("|", false, hud_fontsize);
457 print("fixed missing field '|'\n");
460 else if(!have_separator)
462 strunzone(hud_title[hud_num_fields]);
463 hud_title[hud_num_fields] = strzone("|");
464 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
465 hud_field[hud_num_fields] = SP_SEPARATOR;
467 print("fixed missing field '|'\n");
471 strunzone(hud_title[hud_num_fields]);
472 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
473 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
474 hud_field[hud_num_fields] = ps_secondary;
476 printf("fixed missing field '%s'\n", scores_label[ps_secondary]);
480 strunzone(hud_title[hud_num_fields]);
481 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
482 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
483 hud_field[hud_num_fields] = ps_primary;
485 printf("fixed missing field '%s'\n", scores_label[ps_primary]);
489 hud_field[hud_num_fields] = SP_END;
494 vector hud_field_rgb;
495 string hud_field_icon0;
496 string hud_field_icon1;
497 string hud_field_icon2;
498 vector hud_field_icon0_rgb;
499 vector hud_field_icon1_rgb;
500 vector hud_field_icon2_rgb;
501 float hud_field_icon0_alpha;
502 float hud_field_icon1_alpha;
503 float hud_field_icon2_alpha;
504 string HUD_GetField(entity pl, float field)
506 float tmp, num, denom, f;
508 hud_field_rgb = '1 1 1';
509 hud_field_icon0 = "";
510 hud_field_icon1 = "";
511 hud_field_icon2 = "";
512 hud_field_icon0_rgb = '1 1 1';
513 hud_field_icon1_rgb = '1 1 1';
514 hud_field_icon2_rgb = '1 1 1';
515 hud_field_icon0_alpha = 1;
516 hud_field_icon1_alpha = 1;
517 hud_field_icon2_alpha = 1;
522 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
523 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
527 tmp = max(0, min(220, f-80)) / 220;
528 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
534 f = pl.ping_packetloss;
535 tmp = pl.ping_movementloss;
536 if(f == 0 && tmp == 0)
538 str = ftos(ceil(f * 100));
540 str = strcat(str, "~", ftos(ceil(tmp * 100)));
541 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
542 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
546 if(ready_waiting && pl.ready)
548 hud_field_icon0 = "gfx/scoreboard/player_ready";
552 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
554 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
555 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
556 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
557 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
558 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
561 return GetPlayerName(pl.sv_entnum);
564 f = pl.(scores[SP_KILLS]);
565 f -= pl.(scores[SP_SUICIDES]);
569 num = pl.(scores[SP_KILLS]);
570 denom = pl.(scores[SP_DEATHS]);
573 hud_field_rgb = '0 1 0';
574 str = sprintf("%d", num);
575 } else if(num <= 0) {
576 hud_field_rgb = '1 0 0';
577 str = sprintf("%.1f", num/denom);
579 str = sprintf("%.1f", num/denom);
583 f = pl.(scores[SP_KILLS]);
584 f -= pl.(scores[SP_DEATHS]);
587 hud_field_rgb = '0 1 0';
589 hud_field_rgb = '1 1 1';
591 hud_field_rgb = '1 0 0';
596 tmp = pl.(scores[field]);
597 f = scores_flags[field];
598 if(field == ps_primary)
599 hud_field_rgb = '1 1 0';
600 else if(field == ps_secondary)
601 hud_field_rgb = '0 1 1';
603 hud_field_rgb = '1 1 1';
604 return ScoreString(f, tmp);
609 float xmin, xmax, ymin, ymax, sbwidth;
610 float hud_fixscoreboardcolumnwidth_len;
611 float hud_fixscoreboardcolumnwidth_iconlen;
612 float hud_fixscoreboardcolumnwidth_marginlen;
614 string HUD_FixScoreboardColumnWidth(int i, string str)
618 field = hud_field[i];
620 hud_fixscoreboardcolumnwidth_iconlen = 0;
622 if(hud_field_icon0 != "")
624 sz = draw_getimagesize(hud_field_icon0);
626 if(hud_fixscoreboardcolumnwidth_iconlen < f)
627 hud_fixscoreboardcolumnwidth_iconlen = f;
630 if(hud_field_icon1 != "")
632 sz = draw_getimagesize(hud_field_icon1);
634 if(hud_fixscoreboardcolumnwidth_iconlen < f)
635 hud_fixscoreboardcolumnwidth_iconlen = f;
638 if(hud_field_icon2 != "")
640 sz = draw_getimagesize(hud_field_icon2);
642 if(hud_fixscoreboardcolumnwidth_iconlen < f)
643 hud_fixscoreboardcolumnwidth_iconlen = f;
646 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
648 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
649 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
651 hud_fixscoreboardcolumnwidth_marginlen = 0;
653 if(field == SP_NAME) // name gets all remaining space
657 namesize = sbwidth;// / hud_fontsize_x;
658 for(j = 0; j < hud_num_fields; ++j)
660 if (hud_field[i] != SP_SEPARATOR)
661 namesize -= hud_size[j] + hud_fontsize.x;
662 namesize += hud_fontsize.x;
663 hud_size[i] = namesize;
665 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
666 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
667 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
668 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
671 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
673 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
680 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
683 rgb = Team_ColorRGB(pl.team);
687 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
689 if((rgb == '1 1 1') && (!is_spec)) {
690 rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
691 rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
692 rgb_z = autocvar_scoreboard_color_bg_b + 0.5; }
694 vector h_pos = pos - '1 1 0';
695 vector h_size = item_size + '2 0 0';
696 // alternated rows highlighting
698 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
699 else if((scoreboard_highlight) && (!(pl_number % 2)))
700 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
706 for(i = 0; i < hud_num_fields; ++i)
708 field = hud_field[i];
709 if(field == SP_SEPARATOR)
712 if(is_spec && field != SP_NAME && field != SP_PING) {
713 pos.x += hud_size[i] + hud_fontsize.x;
716 str = HUD_GetField(pl, field);
717 str = HUD_FixScoreboardColumnWidth(i, str);
719 pos.x += hud_size[i] + hud_fontsize.x;
721 if(field == SP_NAME) {
722 tmp_x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
724 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
726 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
728 tmp_x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
730 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
732 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
735 tmp_x = hud_size[i] + hud_fontsize.x;
736 if(hud_field_icon0 != "")
738 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);
740 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);
741 if(hud_field_icon1 != "")
743 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);
745 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);
746 if(hud_field_icon2 != "")
748 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);
750 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);
753 if(hud_field[i] == SP_SEPARATOR)
756 for(i = hud_num_fields-1; i > 0; --i)
758 field = hud_field[i];
759 if(field == SP_SEPARATOR)
762 if(is_spec && field != SP_NAME && field != SP_PING) {
763 pos.x -= hud_size[i] + hud_fontsize.x;
767 str = HUD_GetField(pl, field);
768 str = HUD_FixScoreboardColumnWidth(i, str);
770 if(field == SP_NAME) {
771 tmp_x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
773 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
775 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
777 tmp_x = hud_fixscoreboardcolumnwidth_len;
779 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
781 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
785 if(hud_field_icon0 != "")
787 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);
789 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);
790 if(hud_field_icon1 != "")
792 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);
794 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);
795 if(hud_field_icon2 != "")
797 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);
799 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);
800 pos.x -= hud_size[i] + hud_fontsize.x;
805 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
809 * HUD_Scoreboard_MakeTable
811 * Makes a table for a team (for all playing players in DM) and fills it
814 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
816 float body_table_height, i;
817 vector tmp = '0 0 0', column_dim = '0 0 0';
820 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
822 pos.y += autocvar_scoreboard_border_thickness;
826 tmp_y = 1.25 * hud_fontsize.y;
830 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
832 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
835 tmp.y += autocvar_scoreboard_border_thickness;
836 tmp.y += body_table_height;
837 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
839 // separator header/table
840 pos.y += 1.25 * hud_fontsize.y;
841 tmp_y = autocvar_scoreboard_border_thickness;
842 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
844 pos.y += autocvar_scoreboard_border_thickness;
847 tmp_y = body_table_height;
849 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
851 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
853 // anyway, apply some color
854 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
856 // go back to the top to make alternated columns highlighting and to print the strings
857 pos.y -= 1.25 * hud_fontsize.y;
858 pos.y -= autocvar_scoreboard_border_thickness;
862 if (scoreboard_highlight)
864 column_dim_y = 1.25 * hud_fontsize.y; // header
865 column_dim.y += autocvar_scoreboard_border_thickness;
866 column_dim.y += body_table_height;
869 // print the strings of the columns headers and draw the columns
870 draw_beginBoldFont();
871 for(i = 0; i < hud_num_fields; ++i)
873 if(hud_field[i] == SP_SEPARATOR)
875 column_dim_x = hud_size[i] + hud_fontsize.x;
876 if (scoreboard_highlight)
879 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
881 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
882 pos.x += column_dim.x;
884 if(hud_field[i] == SP_SEPARATOR)
888 for(i = hud_num_fields-1; i > 0; --i)
890 if(hud_field[i] == SP_SEPARATOR)
893 pos.x -= hud_size[i];
895 if (scoreboard_highlight)
899 if (i == hud_num_fields-1)
900 column_dim_x = hud_size[i] + hud_fontsize.x / 2 + 1;
902 column_dim_x = hud_size[i] + hud_fontsize.x;
903 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
907 tmp_x = stringwidth(hud_title[i], false, hud_fontsize);
908 tmp_x = (hud_size[i] - tmp.x);
909 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
910 pos.x -= hud_fontsize.x;
916 pos.y += 1.25 * hud_fontsize.y; // skip the header
917 pos.y += autocvar_scoreboard_border_thickness;
921 tmp_y = hud_fontsize.y * 1.25;
923 // fill the table and draw the rows
926 for(pl = players.sort_next; pl; pl = pl.sort_next)
928 if(pl.team != tm.team)
930 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
931 pos.y += 1.25 * hud_fontsize.y;
935 for(pl = players.sort_next; pl; pl = pl.sort_next)
937 if(pl.team == NUM_SPECTATOR)
939 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
940 pos.y += 1.25 * hud_fontsize.y;
945 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
946 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
951 float HUD_WouldDrawScoreboard() {
952 if (autocvar__hud_configure)
954 else if (scoreboard_showscores)
956 else if (intermission == 1)
958 else if (intermission == 2)
960 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
962 else if (scoreboard_showscores_force)
967 float average_accuracy;
968 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
971 float weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
973 if(autocvar_scoreboard_accuracy_doublerows)
978 float fontsize = height * 1/3;
979 float weapon_height = height * 2/3;
980 float weapon_width = sbwidth / weapon_cnt;
981 float g_instagib = 0;
983 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
984 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
985 vector tmp = '0 0 0';
987 tmp_y = height * rows;
990 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
992 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
993 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
995 // column highlighting
996 for(i = 0; i < weapon_cnt/rows; ++i)
999 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);
1003 for(i = 0; i < rows; ++i)
1005 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);
1008 average_accuracy = 0;
1009 float weapons_with_stats;
1010 weapons_with_stats = 0;
1012 pos.x += weapon_width / 2;
1014 if(switchweapon == WEP_VAPORIZER)
1015 g_instagib = 1; // TODO: real detection for instagib?
1018 if(autocvar_scoreboard_accuracy_nocolors)
1021 Accuracy_LoadColors();
1023 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1025 self = get_weaponinfo(i);
1028 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
1030 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1033 if(weapon_stats >= 0)
1034 weapon_alpha = scoreboard_alpha_fg;
1036 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1039 drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1041 if(weapon_stats >= 0) {
1042 weapons_with_stats += 1;
1043 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1046 s = sprintf("%d%%", weapon_stats*100);
1049 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1051 if(!autocvar_scoreboard_accuracy_nocolors)
1052 rgb = Accuracy_GetColor(weapon_stats);
1054 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1056 pos.x += weapon_width * rows;
1057 if(rows == 2 && i == 6) {
1063 if(weapons_with_stats)
1064 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1067 pos.x -= weapon_width / 2;
1071 pos.y += 1.25 * hud_fontsize.y;
1075 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1077 pos.x += hud_fontsize.x * 0.25;
1078 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1079 pos_x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1080 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1082 pos.y+= hud_fontsize.y;
1087 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1088 float stat_secrets_found, stat_secrets_total;
1089 float stat_monsters_killed, stat_monsters_total;
1093 // get monster stats
1094 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1095 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1097 // get secrets stats
1098 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1099 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1101 // get number of rows
1102 if(stat_secrets_total)
1104 if(stat_monsters_total)
1107 // if no rows, return
1111 // draw table header
1112 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1113 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1116 vector tmp = '0 0 0';
1118 tmp_y = hud_fontsize.y * rows;
1121 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1123 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1124 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1127 if(stat_monsters_total)
1129 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1130 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1134 if(stat_secrets_total)
1136 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1137 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1141 pos.y += 1.25 * hud_fontsize.y;
1146 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1149 RANKINGS_RECEIVED_CNT = 0;
1150 for (i=RANKINGS_CNT-1; i>=0; --i)
1152 ++RANKINGS_RECEIVED_CNT;
1154 if (RANKINGS_RECEIVED_CNT == 0)
1158 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1160 hl_rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
1161 hl_rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
1162 hl_rgb_z = autocvar_scoreboard_color_bg_b + 0.5;
1164 pos.y += hud_fontsize.y;
1165 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1166 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1167 vector tmp = '0 0 0';
1169 tmp_y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1172 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1174 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1175 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1178 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1185 n = grecordholder[i];
1186 p = count_ordinal(i+1);
1187 if(grecordholder[i] == GetPlayerName(player_localnum))
1188 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1189 else if(!(i % 2) && scoreboard_highlight)
1190 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1191 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1192 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);
1193 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1194 pos.y += 1.25 * hud_fontsize.y;
1196 pos.y += autocvar_scoreboard_border_thickness;
1201 float hud_woulddrawscoreboard_prev;
1202 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1203 void HUD_DrawScoreboard()
1205 float hud_woulddrawscoreboard;
1206 hud_woulddrawscoreboard = scoreboard_active;
1207 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1208 hud_woulddrawscoreboard_change = time;
1209 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1212 if(hud_woulddrawscoreboard) {
1213 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1214 if (scoreboard_fadeinspeed)
1215 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1217 scoreboard_fade_alpha = 1;
1220 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1221 if (scoreboard_fadeoutspeed)
1222 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1224 scoreboard_fade_alpha = 0;
1227 if (!scoreboard_fade_alpha)
1230 HUD_UpdatePlayerTeams();
1232 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1233 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1234 scoreboard_highlight = autocvar_scoreboard_highlight;
1235 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1236 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1237 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1238 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1240 vector rgb, pos, tmp;
1244 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1245 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1247 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1248 ymax = (vid_conheight - ymin);
1250 sbwidth = xmax - xmin;
1252 // Initializes position
1258 vector sb_heading_fontsize;
1259 sb_heading_fontsize = hud_fontsize * 2;
1260 draw_beginBoldFont();
1261 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1264 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1266 // Draw the scoreboard
1267 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1271 vector team_score_baseoffset;
1272 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1273 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1275 if(tm.team == NUM_SPECTATOR)
1278 draw_beginBoldFont();
1279 rgb = Team_ColorRGB(tm.team);
1280 str = ftos(tm.(teamscores[ts_primary]));
1281 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1283 if(ts_primary != ts_secondary)
1285 str = ftos(tm.(teamscores[ts_secondary]));
1286 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);
1290 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1292 rgb_x = autocvar_scoreboard_color_bg_r;
1293 rgb_y = autocvar_scoreboard_color_bg_g;
1294 rgb_z = autocvar_scoreboard_color_bg_b;
1298 rgb_x = autocvar_scoreboard_color_bg_r;
1299 rgb_y = autocvar_scoreboard_color_bg_g;
1300 rgb_z = autocvar_scoreboard_color_bg_b;
1302 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1304 if(tm.team == NUM_SPECTATOR)
1307 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1311 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1312 if(race_speedaward) {
1313 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1314 pos.y += 1.25 * hud_fontsize.y;
1316 if(race_speedaward_alltimebest) {
1317 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1318 pos.y += 1.25 * hud_fontsize.y;
1320 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1322 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1324 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1326 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1331 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1333 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1340 item_size_x = sbwidth;
1341 item_size_y = hud_fontsize.y * 1.25;
1343 for(pl = players.sort_next; pl; pl = pl.sort_next)
1345 if(pl.team != NUM_SPECTATOR)
1347 pos.y += 1.25 * hud_fontsize.y;
1348 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1354 draw_beginBoldFont();
1355 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1357 pos.y += 1.25 * hud_fontsize.y;
1360 // Print info string
1362 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1363 tl = getstatf(STAT_TIMELIMIT);
1364 fl = getstatf(STAT_FRAGLIMIT);
1365 ll = getstatf(STAT_LEADLIMIT);
1366 if(gametype == MAPINFO_TYPE_LMS)
1369 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1374 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1378 str = strcat(str, _(" or"));
1381 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1382 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1383 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1384 TranslateScoresLabel(teamscores_label[ts_primary])));
1388 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1389 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1390 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1391 TranslateScoresLabel(scores_label[ps_primary])));
1396 if(tl > 0 || fl > 0)
1397 str = strcat(str, _(" or"));
1400 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1401 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1402 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1403 TranslateScoresLabel(teamscores_label[ts_primary])));
1407 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1408 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1409 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1410 TranslateScoresLabel(scores_label[ps_primary])));
1415 pos.y += 1.2 * hud_fontsize.y;
1416 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1418 // print information about respawn status
1419 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1423 if(respawn_time < 0)
1425 // a negative number means we are awaiting respawn, time value is still the same
1426 respawn_time *= -1; // remove mark now that we checked it
1427 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1429 str = sprintf(_("^1Respawning in ^3%s^1..."),
1430 (autocvar_scoreboard_respawntime_decimals ?
1431 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1433 count_seconds(respawn_time - time)
1437 else if(time < respawn_time)
1439 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1440 (autocvar_scoreboard_respawntime_decimals ?
1441 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1443 count_seconds(respawn_time - time)
1447 else if(time >= respawn_time)
1448 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1450 pos.y += 1.2 * hud_fontsize.y;
1451 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1454 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;