2 #include "../dpdefs/csprogsdefs.qh"
4 #include "../common/constants.qh"
5 #include "../common/stats.qh"
6 #include "../common/teams.qh"
7 #include "../common/util.qh"
8 #include "../common/buffs.qh"
9 #include "../common/counting.qh"
10 #include "../common/weapons/weapons.qh"
11 #include "../common/mapinfo.qh"
12 #include "command/cl_cmd.qh"
13 #include "autocvars.qh"
15 #include "scoreboard.qh"
21 float scoreboard_alpha_bg;
22 float scoreboard_alpha_fg;
23 float scoreboard_highlight;
24 float scoreboard_highlight_alpha;
25 float scoreboard_highlight_alpha_self;
26 float scoreboard_alpha_name;
27 float scoreboard_alpha_name_self;
29 void drawstringright(vector, string, vector, vector, float, float);
30 void drawstringcenter(vector, string, vector, vector, float, float);
32 const float SCOREBOARD_OFFSET = 50;
34 // wrapper to put all possible scores titles through gettext
35 string TranslateScoresLabel(string l)
39 case "bckills": return CTX(_("SCO^bckills"));
40 case "bctime": return CTX(_("SCO^bctime"));
41 case "caps": return CTX(_("SCO^caps"));
42 case "captime": return CTX(_("SCO^captime"));
43 case "deaths": return CTX(_("SCO^deaths"));
44 case "destroyed": return CTX(_("SCO^destroyed"));
45 case "drops": return CTX(_("SCO^drops"));
46 case "faults": return CTX(_("SCO^faults"));
47 case "fckills": return CTX(_("SCO^fckills"));
48 case "goals": return CTX(_("SCO^goals"));
49 case "kckills": return CTX(_("SCO^kckills"));
50 case "kdratio": return CTX(_("SCO^kdratio"));
51 case "k/d": return CTX(_("SCO^k/d"));
52 case "kd": return CTX(_("SCO^kd"));
53 case "kdr": return CTX(_("SCO^kdr"));
54 case "kills": return CTX(_("SCO^kills"));
55 case "laps": return CTX(_("SCO^laps"));
56 case "lives": return CTX(_("SCO^lives"));
57 case "losses": return CTX(_("SCO^losses"));
58 case "name": return CTX(_("SCO^name"));
59 case "sum": return CTX(_("SCO^sum"));
60 case "nick": return CTX(_("SCO^nick"));
61 case "objectives": return CTX(_("SCO^objectives"));
62 case "pickups": return CTX(_("SCO^pickups"));
63 case "ping": return CTX(_("SCO^ping"));
64 case "pl": return CTX(_("SCO^pl"));
65 case "pushes": return CTX(_("SCO^pushes"));
66 case "rank": return CTX(_("SCO^rank"));
67 case "returns": return CTX(_("SCO^returns"));
68 case "revivals": return CTX(_("SCO^revivals"));
69 case "score": return CTX(_("SCO^score"));
70 case "suicides": return CTX(_("SCO^suicides"));
71 case "takes": return CTX(_("SCO^takes"));
72 case "ticks": return CTX(_("SCO^ticks"));
77 void Cmd_HUD_SetFields(float argc);
82 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
83 for(i = 0; i < MAX_SCORE; ++i)
85 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
86 if(f == SFL_SORT_PRIO_PRIMARY)
88 if(f == SFL_SORT_PRIO_SECONDARY)
91 if(ps_secondary == -1)
92 ps_secondary = ps_primary;
94 for(i = 0; i < MAX_TEAMSCORE; ++i)
96 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
97 if(f == SFL_SORT_PRIO_PRIMARY)
99 if(f == SFL_SORT_PRIO_SECONDARY)
102 if(ts_secondary == -1)
103 ts_secondary = ts_primary;
105 Cmd_HUD_SetFields(0);
108 void HUD_UpdatePlayerPos(entity pl);
109 float SetTeam(entity pl, float Team);
111 void HUD_UpdatePlayerTeams()
118 for(pl = players.sort_next; pl; pl = pl.sort_next)
121 Team = GetPlayerColor(pl.sv_entnum);
122 if(SetTeam(pl, Team))
125 HUD_UpdatePlayerPos(pl);
129 pl = players.sort_next;
134 print(strcat("PNUM: ", ftos(num), "\n"));
139 int HUD_CompareScore(float vl, float vr, int f)
141 if(f & SFL_ZERO_IS_WORST)
143 if(vl == 0 && vr != 0)
145 if(vl != 0 && vr == 0)
149 return IS_INCREASING(f);
151 return IS_DECREASING(f);
155 float HUD_ComparePlayerScores(entity left, entity right)
158 vl = GetPlayerColor(left.sv_entnum);
159 vr = GetPlayerColor(right.sv_entnum);
171 if(vl == NUM_SPECTATOR)
173 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
175 if(!left.gotscores && right.gotscores)
180 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
184 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
189 for(i = 0; i < MAX_SCORE; ++i)
191 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
196 if (left.sv_entnum < right.sv_entnum)
202 void HUD_UpdatePlayerPos(entity player)
204 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
206 SORT_SWAP(player, other);
208 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
210 SORT_SWAP(other, player);
214 float HUD_CompareTeamScores(entity left, entity right)
218 if(left.team == NUM_SPECTATOR)
220 if(right.team == NUM_SPECTATOR)
223 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
227 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
231 for(i = 0; i < MAX_SCORE; ++i)
233 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
238 if (left.team < right.team)
244 void HUD_UpdateTeamPos(entity Team)
246 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
248 SORT_SWAP(Team, other);
250 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
252 SORT_SWAP(other, Team);
258 print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
259 print(_("^3|---------------------------------------------------------------|\n"));
260 print(_("Usage:\n"));
261 print(_("^2scoreboard_columns_set default\n"));
262 print(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
263 print(_("The following field names are recognized (case insensitive):\n"));
264 print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
266 print(_("^3name^7 or ^3nick^7 Name of a player\n"));
267 print(_("^3ping^7 Ping time\n"));
268 print(_("^3pl^7 Packet loss\n"));
269 print(_("^3kills^7 Number of kills\n"));
270 print(_("^3deaths^7 Number of deaths\n"));
271 print(_("^3suicides^7 Number of suicides\n"));
272 print(_("^3frags^7 kills - suicides\n"));
273 print(_("^3kd^7 The kill-death ratio\n"));
274 print(_("^3sum^7 frags - deaths\n"));
275 print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
276 print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
277 print(_("^3captime^7 Time of fastest cap (CTF)\n"));
278 print(_("^3fckills^7 Number of flag carrier kills\n"));
279 print(_("^3returns^7 Number of flag returns\n"));
280 print(_("^3drops^7 Number of flag drops\n"));
281 print(_("^3lives^7 Number of lives (LMS)\n"));
282 print(_("^3rank^7 Player rank\n"));
283 print(_("^3pushes^7 Number of players pushed into void\n"));
284 print(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
285 print(_("^3kckills^7 Number of keys carrier kills\n"));
286 print(_("^3losses^7 Number of times a key was lost\n"));
287 print(_("^3laps^7 Number of laps finished (race/cts)\n"));
288 print(_("^3time^7 Total time raced (race/cts)\n"));
289 print(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
290 print(_("^3ticks^7 Number of ticks (DOM)\n"));
291 print(_("^3takes^7 Number of domination points taken (DOM)\n"));
292 print(_("^3bckills^7 Number of ball carrier kills\n"));
293 print(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
294 print(_("^3score^7 Total score\n\n"));
296 print(_("Before a field you can put a + or - sign, then a comma separated list\n"
297 "of game types, then a slash, to make the field show up only in these\n"
298 "or in all but these game types. You can also specify 'all' as a\n"
299 "field to show all fields available for the current game mode.\n\n"));
301 print(_("The special game type names 'teams' and 'noteams' can be used to\n"
302 "include/exclude ALL teams/noteams game modes.\n\n"));
304 print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
305 print(_("will display name, ping and pl aligned to the left, and the fields\n"
306 "right of the vertical bar aligned to the right.\n"));
307 print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
308 "other gamemodes except DM.\n"));
311 #define HUD_DefaultColumnLayout() \
313 "-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" */ \
314 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \
315 "+lms/lives +lms/rank " \
316 "+kh/caps +kh/pushes +kh/destroyed " \
317 "?+race/laps ?+race/time ?+race/fastest " \
318 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
321 void Cmd_HUD_SetFields(float argc)
325 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
330 // set up a temporary scoreboard layout
331 // no layout can be properly set up until score_info data haven't been received
332 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
334 scores_label[ps_primary] = strzone("score");
335 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
338 // TODO: re enable with gametype dependant cvars?
339 if(argc < 3) // no arguments provided
340 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
343 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
347 if(argv(2) == "default")
348 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
349 else if(argv(2) == "all")
352 s = "ping pl name |";
353 for(i = 0; i < MAX_SCORE; ++i)
356 if(i != ps_secondary)
357 if(scores_label[i] != "")
358 s = strcat(s, " ", scores_label[i]);
360 if(ps_secondary != ps_primary)
361 s = strcat(s, " ", scores_label[ps_secondary]);
362 s = strcat(s, " ", scores_label[ps_primary]);
363 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
370 hud_fontsize = HUD_GetFontsize("hud_fontsize");
372 draw_beginBoldFont();
373 for(i = 1; i < argc - 1; ++i)
379 if(substring(str, 0, 1) == "?")
382 str = substring(str, 1, strlen(str) - 1);
385 slash = strstrofs(str, "/", 0);
388 pattern = substring(str, 0, slash);
389 str = substring(str, slash + 1, strlen(str) - (slash + 1));
391 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
395 strunzone(hud_title[hud_num_fields]);
396 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
397 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
398 str = strtolower(str);
401 hud_field[hud_num_fields] = SP_PING;
402 } else if(str == "pl") {
403 hud_field[hud_num_fields] = SP_PL;
404 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
405 hud_field[hud_num_fields] = SP_KDRATIO;
406 } else if(str == "sum" || str == "diff" || str == "k-d") {
407 hud_field[hud_num_fields] = SP_SUM;
408 } else if(str == "name" || str == "nick") {
409 hud_field[hud_num_fields] = SP_NAME;
411 } else if(str == "|") {
412 hud_field[hud_num_fields] = SP_SEPARATOR;
415 for(j = 0; j < MAX_SCORE; ++j)
416 if(str == strtolower(scores_label[j]))
417 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
426 printf("^1Error:^7 Unknown score field: '%s'\n", str);
430 hud_field[hud_num_fields] = j;
433 if(j == ps_secondary)
437 if(hud_num_fields >= MAX_HUD_FIELDS)
441 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
443 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
445 if(ps_primary == ps_secondary)
447 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
449 if(hud_num_fields+missing < MAX_HUD_FIELDS)
453 strunzone(hud_title[hud_num_fields]);
454 for(i = hud_num_fields; i > 0; --i)
456 hud_title[i] = hud_title[i-1];
457 hud_size[i] = hud_size[i-1];
458 hud_field[i] = hud_field[i-1];
460 hud_title[0] = strzone(TranslateScoresLabel("name"));
461 hud_field[0] = SP_NAME;
463 print("fixed missing field 'name'\n");
467 strunzone(hud_title[hud_num_fields]);
468 for(i = hud_num_fields; i > 1; --i)
470 hud_title[i] = hud_title[i-1];
471 hud_size[i] = hud_size[i-1];
472 hud_field[i] = hud_field[i-1];
474 hud_title[1] = strzone("|");
475 hud_field[1] = SP_SEPARATOR;
476 hud_size[1] = stringwidth("|", false, hud_fontsize);
478 print("fixed missing field '|'\n");
481 else if(!have_separator)
483 strunzone(hud_title[hud_num_fields]);
484 hud_title[hud_num_fields] = strzone("|");
485 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
486 hud_field[hud_num_fields] = SP_SEPARATOR;
488 print("fixed missing field '|'\n");
492 strunzone(hud_title[hud_num_fields]);
493 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
494 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
495 hud_field[hud_num_fields] = ps_secondary;
497 printf("fixed missing field '%s'\n", scores_label[ps_secondary]);
501 strunzone(hud_title[hud_num_fields]);
502 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
503 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
504 hud_field[hud_num_fields] = ps_primary;
506 printf("fixed missing field '%s'\n", scores_label[ps_primary]);
510 hud_field[hud_num_fields] = SP_END;
515 vector hud_field_rgb;
516 string hud_field_icon0;
517 string hud_field_icon1;
518 string hud_field_icon2;
519 vector hud_field_icon0_rgb;
520 vector hud_field_icon1_rgb;
521 vector hud_field_icon2_rgb;
522 float hud_field_icon0_alpha;
523 float hud_field_icon1_alpha;
524 float hud_field_icon2_alpha;
525 string HUD_GetField(entity pl, int field)
527 float tmp, num, denom, f;
529 hud_field_rgb = '1 1 1';
530 hud_field_icon0 = "";
531 hud_field_icon1 = "";
532 hud_field_icon2 = "";
533 hud_field_icon0_rgb = '1 1 1';
534 hud_field_icon1_rgb = '1 1 1';
535 hud_field_icon2_rgb = '1 1 1';
536 hud_field_icon0_alpha = 1;
537 hud_field_icon1_alpha = 1;
538 hud_field_icon2_alpha = 1;
543 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
544 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
548 tmp = max(0, min(220, f-80)) / 220;
549 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
555 f = pl.ping_packetloss;
556 tmp = pl.ping_movementloss;
557 if(f == 0 && tmp == 0)
559 str = ftos(ceil(f * 100));
561 str = strcat(str, "~", ftos(ceil(tmp * 100)));
562 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
563 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
567 if(ready_waiting && pl.ready)
569 hud_field_icon0 = "gfx/scoreboard/player_ready";
573 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
575 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
576 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
577 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
578 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
579 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
582 return GetPlayerName(pl.sv_entnum);
585 f = pl.(scores[SP_KILLS]);
586 f -= pl.(scores[SP_SUICIDES]);
590 num = pl.(scores[SP_KILLS]);
591 denom = pl.(scores[SP_DEATHS]);
594 hud_field_rgb = '0 1 0';
595 str = sprintf("%d", num);
596 } else if(num <= 0) {
597 hud_field_rgb = '1 0 0';
598 str = sprintf("%.1f", num/denom);
600 str = sprintf("%.1f", num/denom);
604 f = pl.(scores[SP_KILLS]);
605 f -= pl.(scores[SP_DEATHS]);
608 hud_field_rgb = '0 1 0';
610 hud_field_rgb = '1 1 1';
612 hud_field_rgb = '1 0 0';
617 tmp = pl.(scores[field]);
618 f = scores_flags[field];
619 if(field == ps_primary)
620 hud_field_rgb = '1 1 0';
621 else if(field == ps_secondary)
622 hud_field_rgb = '0 1 1';
624 hud_field_rgb = '1 1 1';
625 return ScoreString(f, tmp);
630 float xmin, xmax, ymin, ymax, sbwidth;
631 float hud_fixscoreboardcolumnwidth_len;
632 float hud_fixscoreboardcolumnwidth_iconlen;
633 float hud_fixscoreboardcolumnwidth_marginlen;
635 string HUD_FixScoreboardColumnWidth(int i, string str)
639 field = hud_field[i];
641 hud_fixscoreboardcolumnwidth_iconlen = 0;
643 if(hud_field_icon0 != "")
645 sz = draw_getimagesize(hud_field_icon0);
647 if(hud_fixscoreboardcolumnwidth_iconlen < f)
648 hud_fixscoreboardcolumnwidth_iconlen = f;
651 if(hud_field_icon1 != "")
653 sz = draw_getimagesize(hud_field_icon1);
655 if(hud_fixscoreboardcolumnwidth_iconlen < f)
656 hud_fixscoreboardcolumnwidth_iconlen = f;
659 if(hud_field_icon2 != "")
661 sz = draw_getimagesize(hud_field_icon2);
663 if(hud_fixscoreboardcolumnwidth_iconlen < f)
664 hud_fixscoreboardcolumnwidth_iconlen = f;
667 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
669 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
670 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
672 hud_fixscoreboardcolumnwidth_marginlen = 0;
674 if(field == SP_NAME) // name gets all remaining space
678 namesize = sbwidth;// / hud_fontsize_x;
679 for(j = 0; j < hud_num_fields; ++j)
681 if (hud_field[i] != SP_SEPARATOR)
682 namesize -= hud_size[j] + hud_fontsize.x;
683 namesize += hud_fontsize.x;
684 hud_size[i] = namesize;
686 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
687 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
688 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
689 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
692 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
694 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
701 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
704 rgb = Team_ColorRGB(pl.team);
708 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
710 if((rgb == '1 1 1') && (!is_spec)) {
711 rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
712 rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
713 rgb_z = autocvar_scoreboard_color_bg_b + 0.5; }
715 vector h_pos = pos - '1 1 0';
716 vector h_size = item_size + '2 0 0';
717 // alternated rows highlighting
719 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
720 else if((scoreboard_highlight) && (!(pl_number % 2)))
721 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
727 for(i = 0; i < hud_num_fields; ++i)
729 field = hud_field[i];
730 if(field == SP_SEPARATOR)
733 if(is_spec && field != SP_NAME && field != SP_PING) {
734 pos.x += hud_size[i] + hud_fontsize.x;
737 str = HUD_GetField(pl, field);
738 str = HUD_FixScoreboardColumnWidth(i, str);
740 pos.x += hud_size[i] + hud_fontsize.x;
742 if(field == SP_NAME) {
743 tmp_x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
745 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
747 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
749 tmp_x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
751 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
753 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
756 tmp_x = hud_size[i] + hud_fontsize.x;
757 if(hud_field_icon0 != "")
759 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);
761 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);
762 if(hud_field_icon1 != "")
764 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);
766 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);
767 if(hud_field_icon2 != "")
769 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);
771 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);
774 if(hud_field[i] == SP_SEPARATOR)
777 for(i = hud_num_fields-1; i > 0; --i)
779 field = hud_field[i];
780 if(field == SP_SEPARATOR)
783 if(is_spec && field != SP_NAME && field != SP_PING) {
784 pos.x -= hud_size[i] + hud_fontsize.x;
788 str = HUD_GetField(pl, field);
789 str = HUD_FixScoreboardColumnWidth(i, str);
791 if(field == SP_NAME) {
792 tmp_x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
794 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
796 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
798 tmp_x = hud_fixscoreboardcolumnwidth_len;
800 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
802 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
806 if(hud_field_icon0 != "")
808 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);
810 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);
811 if(hud_field_icon1 != "")
813 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);
815 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);
816 if(hud_field_icon2 != "")
818 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);
820 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);
821 pos.x -= hud_size[i] + hud_fontsize.x;
826 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
830 * HUD_Scoreboard_MakeTable
832 * Makes a table for a team (for all playing players in DM) and fills it
835 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
837 float body_table_height;
838 vector tmp = '0 0 0', column_dim = '0 0 0';
841 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
843 pos.y += autocvar_scoreboard_border_thickness;
847 tmp_y = 1.25 * hud_fontsize.y;
851 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
853 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
856 tmp.y += autocvar_scoreboard_border_thickness;
857 tmp.y += body_table_height;
858 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
860 // separator header/table
861 pos.y += 1.25 * hud_fontsize.y;
862 tmp_y = autocvar_scoreboard_border_thickness;
863 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
865 pos.y += autocvar_scoreboard_border_thickness;
868 tmp_y = body_table_height;
870 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
872 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
874 // anyway, apply some color
875 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
877 // go back to the top to make alternated columns highlighting and to print the strings
878 pos.y -= 1.25 * hud_fontsize.y;
879 pos.y -= autocvar_scoreboard_border_thickness;
883 if (scoreboard_highlight)
885 column_dim_y = 1.25 * hud_fontsize.y; // header
886 column_dim.y += autocvar_scoreboard_border_thickness;
887 column_dim.y += body_table_height;
890 // print the strings of the columns headers and draw the columns
891 draw_beginBoldFont();
893 for(i = 0; i < hud_num_fields; ++i)
895 if(hud_field[i] == SP_SEPARATOR)
897 column_dim_x = hud_size[i] + hud_fontsize.x;
898 if (scoreboard_highlight)
901 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
903 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
904 pos.x += column_dim.x;
906 if(hud_field[i] == SP_SEPARATOR)
910 for(i = hud_num_fields-1; i > 0; --i)
912 if(hud_field[i] == SP_SEPARATOR)
915 pos.x -= hud_size[i];
917 if (scoreboard_highlight)
921 if (i == hud_num_fields-1)
922 column_dim_x = hud_size[i] + hud_fontsize.x / 2 + 1;
924 column_dim_x = hud_size[i] + hud_fontsize.x;
925 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
929 tmp_x = stringwidth(hud_title[i], false, hud_fontsize);
930 tmp_x = (hud_size[i] - tmp.x);
931 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
932 pos.x -= hud_fontsize.x;
938 pos.y += 1.25 * hud_fontsize.y; // skip the header
939 pos.y += autocvar_scoreboard_border_thickness;
943 tmp_y = hud_fontsize.y * 1.25;
945 // fill the table and draw the rows
948 for(pl = players.sort_next; pl; pl = pl.sort_next)
950 if(pl.team != tm.team)
952 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
953 pos.y += 1.25 * hud_fontsize.y;
957 for(pl = players.sort_next; pl; pl = pl.sort_next)
959 if(pl.team == NUM_SPECTATOR)
961 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
962 pos.y += 1.25 * hud_fontsize.y;
967 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
968 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
973 float HUD_WouldDrawScoreboard() {
974 if (autocvar__hud_configure)
976 else if (scoreboard_showscores)
978 else if (intermission == 1)
980 else if (intermission == 2)
982 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
984 else if (scoreboard_showscores_force)
989 float average_accuracy;
990 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
993 float weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
995 if(autocvar_scoreboard_accuracy_doublerows)
1000 float fontsize = height * 1/3;
1001 float weapon_height = height * 2/3;
1002 float weapon_width = sbwidth / weapon_cnt;
1003 float g_instagib = 0;
1005 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1006 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1007 vector tmp = '0 0 0';
1009 tmp_y = height * rows;
1012 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1014 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1015 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1017 // column highlighting
1018 for(i = 0; i < weapon_cnt/rows; ++i)
1021 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);
1025 for(i = 0; i < rows; ++i)
1027 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);
1030 average_accuracy = 0;
1031 float weapons_with_stats;
1032 weapons_with_stats = 0;
1034 pos.x += weapon_width / 2;
1036 if(switchweapon == WEP_VAPORIZER)
1037 g_instagib = 1; // TODO: real detection for instagib?
1040 if(autocvar_scoreboard_accuracy_nocolors)
1043 Accuracy_LoadColors();
1045 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1047 self = get_weaponinfo(i);
1050 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
1052 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1055 if(weapon_stats >= 0)
1056 weapon_alpha = scoreboard_alpha_fg;
1058 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1061 drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1063 if(weapon_stats >= 0) {
1064 weapons_with_stats += 1;
1065 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1068 s = sprintf("%d%%", weapon_stats*100);
1071 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1073 if(!autocvar_scoreboard_accuracy_nocolors)
1074 rgb = Accuracy_GetColor(weapon_stats);
1076 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1078 pos.x += weapon_width * rows;
1079 if(rows == 2 && i == 6) {
1085 if(weapons_with_stats)
1086 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1089 pos.x -= weapon_width / 2;
1093 pos.y += 1.25 * hud_fontsize.y;
1097 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1099 pos.x += hud_fontsize.x * 0.25;
1100 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1101 pos_x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1102 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1104 pos.y+= hud_fontsize.y;
1109 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1110 float stat_secrets_found, stat_secrets_total;
1111 float stat_monsters_killed, stat_monsters_total;
1115 // get monster stats
1116 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1117 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1119 // get secrets stats
1120 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1121 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1123 // get number of rows
1124 if(stat_secrets_total)
1126 if(stat_monsters_total)
1129 // if no rows, return
1133 // draw table header
1134 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1135 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1138 vector tmp = '0 0 0';
1140 tmp_y = hud_fontsize.y * rows;
1143 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1145 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1146 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1149 if(stat_monsters_total)
1151 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1152 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1156 if(stat_secrets_total)
1158 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1159 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1163 pos.y += 1.25 * hud_fontsize.y;
1168 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1171 RANKINGS_RECEIVED_CNT = 0;
1172 for (i=RANKINGS_CNT-1; i>=0; --i)
1174 ++RANKINGS_RECEIVED_CNT;
1176 if (RANKINGS_RECEIVED_CNT == 0)
1180 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1182 hl_rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
1183 hl_rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
1184 hl_rgb_z = autocvar_scoreboard_color_bg_b + 0.5;
1186 pos.y += hud_fontsize.y;
1187 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1188 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1189 vector tmp = '0 0 0';
1191 tmp_y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1194 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1196 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1197 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1200 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1207 n = grecordholder[i];
1208 p = count_ordinal(i+1);
1209 if(grecordholder[i] == GetPlayerName(player_localnum))
1210 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1211 else if(!(i % 2) && scoreboard_highlight)
1212 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1213 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1214 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);
1215 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1216 pos.y += 1.25 * hud_fontsize.y;
1218 pos.y += autocvar_scoreboard_border_thickness;
1223 float hud_woulddrawscoreboard_prev;
1224 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1225 void HUD_DrawScoreboard()
1227 float hud_woulddrawscoreboard;
1228 hud_woulddrawscoreboard = scoreboard_active;
1229 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1230 hud_woulddrawscoreboard_change = time;
1231 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1234 if(hud_woulddrawscoreboard) {
1235 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1236 if (scoreboard_fadeinspeed)
1237 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1239 scoreboard_fade_alpha = 1;
1242 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1243 if (scoreboard_fadeoutspeed)
1244 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1246 scoreboard_fade_alpha = 0;
1249 if (!scoreboard_fade_alpha)
1252 HUD_UpdatePlayerTeams();
1254 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1255 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1256 scoreboard_highlight = autocvar_scoreboard_highlight;
1257 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1258 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1259 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1260 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1262 vector rgb, pos, tmp;
1266 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1267 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1269 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1270 ymax = (vid_conheight - ymin);
1272 sbwidth = xmax - xmin;
1274 // Initializes position
1280 vector sb_heading_fontsize;
1281 sb_heading_fontsize = hud_fontsize * 2;
1282 draw_beginBoldFont();
1283 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1286 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1288 // Draw the scoreboard
1289 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1293 vector team_score_baseoffset;
1294 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1295 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1297 if(tm.team == NUM_SPECTATOR)
1300 draw_beginBoldFont();
1301 rgb = Team_ColorRGB(tm.team);
1302 str = ftos(tm.(teamscores[ts_primary]));
1303 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1305 if(ts_primary != ts_secondary)
1307 str = ftos(tm.(teamscores[ts_secondary]));
1308 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);
1312 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1314 rgb_x = autocvar_scoreboard_color_bg_r;
1315 rgb_y = autocvar_scoreboard_color_bg_g;
1316 rgb_z = autocvar_scoreboard_color_bg_b;
1320 rgb_x = autocvar_scoreboard_color_bg_r;
1321 rgb_y = autocvar_scoreboard_color_bg_g;
1322 rgb_z = autocvar_scoreboard_color_bg_b;
1324 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1326 if(tm.team == NUM_SPECTATOR)
1329 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1333 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1334 if(race_speedaward) {
1335 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1336 pos.y += 1.25 * hud_fontsize.y;
1338 if(race_speedaward_alltimebest) {
1339 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1340 pos.y += 1.25 * hud_fontsize.y;
1342 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1344 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1346 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1348 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1353 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1355 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1362 item_size_x = sbwidth;
1363 item_size_y = hud_fontsize.y * 1.25;
1365 for(pl = players.sort_next; pl; pl = pl.sort_next)
1367 if(pl.team != NUM_SPECTATOR)
1369 pos.y += 1.25 * hud_fontsize.y;
1370 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1376 draw_beginBoldFont();
1377 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1379 pos.y += 1.25 * hud_fontsize.y;
1382 // Print info string
1384 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1385 tl = getstatf(STAT_TIMELIMIT);
1386 fl = getstatf(STAT_FRAGLIMIT);
1387 ll = getstatf(STAT_LEADLIMIT);
1388 if(gametype == MAPINFO_TYPE_LMS)
1391 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1396 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1400 str = strcat(str, _(" or"));
1403 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1404 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1405 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1406 TranslateScoresLabel(teamscores_label[ts_primary])));
1410 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1411 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1412 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1413 TranslateScoresLabel(scores_label[ps_primary])));
1418 if(tl > 0 || fl > 0)
1419 str = strcat(str, _(" or"));
1422 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1423 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1424 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1425 TranslateScoresLabel(teamscores_label[ts_primary])));
1429 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1430 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1431 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1432 TranslateScoresLabel(scores_label[ps_primary])));
1437 pos.y += 1.2 * hud_fontsize.y;
1438 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1440 // print information about respawn status
1441 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1445 if(respawn_time < 0)
1447 // a negative number means we are awaiting respawn, time value is still the same
1448 respawn_time *= -1; // remove mark now that we checked it
1449 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1451 str = sprintf(_("^1Respawning in ^3%s^1..."),
1452 (autocvar_scoreboard_respawntime_decimals ?
1453 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1455 count_seconds(respawn_time - time)
1459 else if(time < respawn_time)
1461 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1462 (autocvar_scoreboard_respawntime_decimals ?
1463 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1465 count_seconds(respawn_time - time)
1469 else if(time >= respawn_time)
1470 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1472 pos.y += 1.2 * hud_fontsize.y;
1473 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1476 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;