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, f;
509 hud_field_rgb = '1 1 1';
510 hud_field_icon0 = "";
511 hud_field_icon1 = "";
512 hud_field_icon2 = "";
513 hud_field_icon0_rgb = '1 1 1';
514 hud_field_icon1_rgb = '1 1 1';
515 hud_field_icon2_rgb = '1 1 1';
516 hud_field_icon0_alpha = 1;
517 hud_field_icon1_alpha = 1;
518 hud_field_icon2_alpha = 1;
523 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
524 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
528 tmp = max(0, min(220, f-80)) / 220;
529 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
535 f = pl.ping_packetloss;
536 tmp = pl.ping_movementloss;
537 if(f == 0 && tmp == 0)
539 str = ftos(ceil(f * 100));
541 str = strcat(str, "~", ftos(ceil(tmp * 100)));
542 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
543 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
547 if(ready_waiting && pl.ready)
549 hud_field_icon0 = "gfx/scoreboard/player_ready";
553 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
555 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
556 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
557 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
558 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
559 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
562 return GetPlayerName(pl.sv_entnum);
565 f = pl.(scores[SP_KILLS]);
566 f -= pl.(scores[SP_SUICIDES]);
570 num = pl.(scores[SP_KILLS]);
571 denom = pl.(scores[SP_DEATHS]);
574 hud_field_rgb = '0 1 0';
575 str = sprintf("%d", num);
576 } else if(num <= 0) {
577 hud_field_rgb = '1 0 0';
578 str = sprintf("%.1f", num/denom);
580 str = sprintf("%.1f", num/denom);
584 f = pl.(scores[SP_KILLS]);
585 f -= pl.(scores[SP_DEATHS]);
588 hud_field_rgb = '0 1 0';
590 hud_field_rgb = '1 1 1';
592 hud_field_rgb = '1 0 0';
597 tmp = pl.(scores[field]);
598 f = scores_flags[field];
599 if(field == ps_primary)
600 hud_field_rgb = '1 1 0';
601 else if(field == ps_secondary)
602 hud_field_rgb = '0 1 1';
604 hud_field_rgb = '1 1 1';
605 return ScoreString(f, tmp);
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;
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();
872 for(i = 0; i < hud_num_fields; ++i)
874 if(hud_field[i] == SP_SEPARATOR)
876 column_dim_x = hud_size[i] + hud_fontsize.x;
877 if (scoreboard_highlight)
880 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
882 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
883 pos.x += column_dim.x;
885 if(hud_field[i] == SP_SEPARATOR)
889 for(i = hud_num_fields-1; i > 0; --i)
891 if(hud_field[i] == SP_SEPARATOR)
894 pos.x -= hud_size[i];
896 if (scoreboard_highlight)
900 if (i == hud_num_fields-1)
901 column_dim_x = hud_size[i] + hud_fontsize.x / 2 + 1;
903 column_dim_x = hud_size[i] + hud_fontsize.x;
904 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
908 tmp_x = stringwidth(hud_title[i], false, hud_fontsize);
909 tmp_x = (hud_size[i] - tmp.x);
910 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
911 pos.x -= hud_fontsize.x;
917 pos.y += 1.25 * hud_fontsize.y; // skip the header
918 pos.y += autocvar_scoreboard_border_thickness;
922 tmp_y = hud_fontsize.y * 1.25;
924 // fill the table and draw the rows
927 for(pl = players.sort_next; pl; pl = pl.sort_next)
929 if(pl.team != tm.team)
931 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
932 pos.y += 1.25 * hud_fontsize.y;
936 for(pl = players.sort_next; pl; pl = pl.sort_next)
938 if(pl.team == NUM_SPECTATOR)
940 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
941 pos.y += 1.25 * hud_fontsize.y;
946 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
947 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
952 float HUD_WouldDrawScoreboard() {
953 if (autocvar__hud_configure)
955 else if (scoreboard_showscores)
957 else if (intermission == 1)
959 else if (intermission == 2)
961 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
963 else if (scoreboard_showscores_force)
968 float average_accuracy;
969 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
972 float weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
974 if(autocvar_scoreboard_accuracy_doublerows)
979 float fontsize = height * 1/3;
980 float weapon_height = height * 2/3;
981 float weapon_width = sbwidth / weapon_cnt;
982 float g_instagib = 0;
984 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
985 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
986 vector tmp = '0 0 0';
988 tmp_y = height * rows;
991 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
993 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
994 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
996 // column highlighting
997 for(i = 0; i < weapon_cnt/rows; ++i)
1000 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);
1004 for(i = 0; i < rows; ++i)
1006 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);
1009 average_accuracy = 0;
1010 float weapons_with_stats;
1011 weapons_with_stats = 0;
1013 pos.x += weapon_width / 2;
1015 if(switchweapon == WEP_VAPORIZER)
1016 g_instagib = 1; // TODO: real detection for instagib?
1019 if(autocvar_scoreboard_accuracy_nocolors)
1022 Accuracy_LoadColors();
1024 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1026 self = get_weaponinfo(i);
1029 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
1031 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1034 if(weapon_stats >= 0)
1035 weapon_alpha = scoreboard_alpha_fg;
1037 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1040 drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1042 if(weapon_stats >= 0) {
1043 weapons_with_stats += 1;
1044 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1047 s = sprintf("%d%%", weapon_stats*100);
1050 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1052 if(!autocvar_scoreboard_accuracy_nocolors)
1053 rgb = Accuracy_GetColor(weapon_stats);
1055 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1057 pos.x += weapon_width * rows;
1058 if(rows == 2 && i == 6) {
1064 if(weapons_with_stats)
1065 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1068 pos.x -= weapon_width / 2;
1072 pos.y += 1.25 * hud_fontsize.y;
1076 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1078 pos.x += hud_fontsize.x * 0.25;
1079 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1080 pos_x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1081 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1083 pos.y+= hud_fontsize.y;
1088 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1089 float stat_secrets_found, stat_secrets_total;
1090 float stat_monsters_killed, stat_monsters_total;
1094 // get monster stats
1095 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1096 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1098 // get secrets stats
1099 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1100 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1102 // get number of rows
1103 if(stat_secrets_total)
1105 if(stat_monsters_total)
1108 // if no rows, return
1112 // draw table header
1113 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1114 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1117 vector tmp = '0 0 0';
1119 tmp_y = hud_fontsize.y * rows;
1122 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1124 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1125 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1128 if(stat_monsters_total)
1130 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1131 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1135 if(stat_secrets_total)
1137 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1138 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1142 pos.y += 1.25 * hud_fontsize.y;
1147 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1150 RANKINGS_RECEIVED_CNT = 0;
1151 for (i=RANKINGS_CNT-1; i>=0; --i)
1153 ++RANKINGS_RECEIVED_CNT;
1155 if (RANKINGS_RECEIVED_CNT == 0)
1159 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1161 hl_rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
1162 hl_rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
1163 hl_rgb_z = autocvar_scoreboard_color_bg_b + 0.5;
1165 pos.y += hud_fontsize.y;
1166 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1167 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1168 vector tmp = '0 0 0';
1170 tmp_y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1173 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1175 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1176 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1179 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1186 n = grecordholder[i];
1187 p = count_ordinal(i+1);
1188 if(grecordholder[i] == GetPlayerName(player_localnum))
1189 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1190 else if(!(i % 2) && scoreboard_highlight)
1191 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1192 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1193 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);
1194 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1195 pos.y += 1.25 * hud_fontsize.y;
1197 pos.y += autocvar_scoreboard_border_thickness;
1202 float hud_woulddrawscoreboard_prev;
1203 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1204 void HUD_DrawScoreboard()
1206 float hud_woulddrawscoreboard;
1207 hud_woulddrawscoreboard = scoreboard_active;
1208 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1209 hud_woulddrawscoreboard_change = time;
1210 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1213 if(hud_woulddrawscoreboard) {
1214 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1215 if (scoreboard_fadeinspeed)
1216 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1218 scoreboard_fade_alpha = 1;
1221 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1222 if (scoreboard_fadeoutspeed)
1223 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1225 scoreboard_fade_alpha = 0;
1228 if (!scoreboard_fade_alpha)
1231 HUD_UpdatePlayerTeams();
1233 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1234 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1235 scoreboard_highlight = autocvar_scoreboard_highlight;
1236 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1237 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1238 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1239 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1241 vector rgb, pos, tmp;
1245 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1246 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1248 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1249 ymax = (vid_conheight - ymin);
1251 sbwidth = xmax - xmin;
1253 // Initializes position
1259 vector sb_heading_fontsize;
1260 sb_heading_fontsize = hud_fontsize * 2;
1261 draw_beginBoldFont();
1262 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1265 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1267 // Draw the scoreboard
1268 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1272 vector team_score_baseoffset;
1273 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1274 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1276 if(tm.team == NUM_SPECTATOR)
1279 draw_beginBoldFont();
1280 rgb = Team_ColorRGB(tm.team);
1281 str = ftos(tm.(teamscores[ts_primary]));
1282 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1284 if(ts_primary != ts_secondary)
1286 str = ftos(tm.(teamscores[ts_secondary]));
1287 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);
1291 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1293 rgb_x = autocvar_scoreboard_color_bg_r;
1294 rgb_y = autocvar_scoreboard_color_bg_g;
1295 rgb_z = autocvar_scoreboard_color_bg_b;
1299 rgb_x = autocvar_scoreboard_color_bg_r;
1300 rgb_y = autocvar_scoreboard_color_bg_g;
1301 rgb_z = autocvar_scoreboard_color_bg_b;
1303 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1305 if(tm.team == NUM_SPECTATOR)
1308 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1312 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1313 if(race_speedaward) {
1314 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1315 pos.y += 1.25 * hud_fontsize.y;
1317 if(race_speedaward_alltimebest) {
1318 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1319 pos.y += 1.25 * hud_fontsize.y;
1321 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1323 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1325 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1327 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1332 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1334 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1341 item_size_x = sbwidth;
1342 item_size_y = hud_fontsize.y * 1.25;
1344 for(pl = players.sort_next; pl; pl = pl.sort_next)
1346 if(pl.team != NUM_SPECTATOR)
1348 pos.y += 1.25 * hud_fontsize.y;
1349 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1355 draw_beginBoldFont();
1356 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1358 pos.y += 1.25 * hud_fontsize.y;
1361 // Print info string
1363 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1364 tl = getstatf(STAT_TIMELIMIT);
1365 fl = getstatf(STAT_FRAGLIMIT);
1366 ll = getstatf(STAT_LEADLIMIT);
1367 if(gametype == MAPINFO_TYPE_LMS)
1370 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1375 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1379 str = strcat(str, _(" or"));
1382 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1383 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1384 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1385 TranslateScoresLabel(teamscores_label[ts_primary])));
1389 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1390 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1391 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1392 TranslateScoresLabel(scores_label[ps_primary])));
1397 if(tl > 0 || fl > 0)
1398 str = strcat(str, _(" or"));
1401 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1402 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1403 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1404 TranslateScoresLabel(teamscores_label[ts_primary])));
1408 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1409 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1410 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1411 TranslateScoresLabel(scores_label[ps_primary])));
1416 pos.y += 1.2 * hud_fontsize.y;
1417 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1419 // print information about respawn status
1420 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1424 if(respawn_time < 0)
1426 // a negative number means we are awaiting respawn, time value is still the same
1427 respawn_time *= -1; // remove mark now that we checked it
1428 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1430 str = sprintf(_("^1Respawning in ^3%s^1..."),
1431 (autocvar_scoreboard_respawntime_decimals ?
1432 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1434 count_seconds(respawn_time - time)
1438 else if(time < respawn_time)
1440 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1441 (autocvar_scoreboard_respawntime_decimals ?
1442 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1444 count_seconds(respawn_time - time)
1448 else if(time >= respawn_time)
1449 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1451 pos.y += 1.2 * hud_fontsize.y;
1452 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1455 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;