1 #include "scoreboard.qh"
7 #include "../common/constants.qh"
8 #include "../common/counting.qh"
9 #include "../common/mapinfo.qh"
10 #include "../common/stats.qh"
11 #include "../common/teams.qh"
12 #include "../common/util.qh"
14 float scoreboard_alpha_bg;
15 float scoreboard_alpha_fg;
16 float scoreboard_highlight;
17 float scoreboard_highlight_alpha;
18 float scoreboard_highlight_alpha_self;
19 float scoreboard_alpha_name;
20 float scoreboard_alpha_name_self;
22 void drawstringright(vector, string, vector, vector, float, float);
23 void drawstringcenter(vector, string, vector, vector, float, float);
25 const float SCOREBOARD_OFFSET = 50;
27 // wrapper to put all possible scores titles through gettext
28 string TranslateScoresLabel(string l)
32 case "bckills": return CTX(_("SCO^bckills"));
33 case "bctime": return CTX(_("SCO^bctime"));
34 case "caps": return CTX(_("SCO^caps"));
35 case "captime": return CTX(_("SCO^captime"));
36 case "deaths": return CTX(_("SCO^deaths"));
37 case "destroyed": return CTX(_("SCO^destroyed"));
38 case "drops": return CTX(_("SCO^drops"));
39 case "faults": return CTX(_("SCO^faults"));
40 case "fckills": return CTX(_("SCO^fckills"));
41 case "goals": return CTX(_("SCO^goals"));
42 case "kckills": return CTX(_("SCO^kckills"));
43 case "kdratio": return CTX(_("SCO^kdratio"));
44 case "k/d": return CTX(_("SCO^k/d"));
45 case "kd": return CTX(_("SCO^kd"));
46 case "kdr": return CTX(_("SCO^kdr"));
47 case "kills": return CTX(_("SCO^kills"));
48 case "laps": return CTX(_("SCO^laps"));
49 case "lives": return CTX(_("SCO^lives"));
50 case "losses": return CTX(_("SCO^losses"));
51 case "name": return CTX(_("SCO^name"));
52 case "sum": return CTX(_("SCO^sum"));
53 case "nick": return CTX(_("SCO^nick"));
54 case "objectives": return CTX(_("SCO^objectives"));
55 case "pickups": return CTX(_("SCO^pickups"));
56 case "ping": return CTX(_("SCO^ping"));
57 case "pl": return CTX(_("SCO^pl"));
58 case "pushes": return CTX(_("SCO^pushes"));
59 case "rank": return CTX(_("SCO^rank"));
60 case "returns": return CTX(_("SCO^returns"));
61 case "revivals": return CTX(_("SCO^revivals"));
62 case "score": return CTX(_("SCO^score"));
63 case "suicides": return CTX(_("SCO^suicides"));
64 case "takes": return CTX(_("SCO^takes"));
65 case "ticks": return CTX(_("SCO^ticks"));
74 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
75 for(i = 0; i < MAX_SCORE; ++i)
77 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
78 if(f == SFL_SORT_PRIO_PRIMARY)
80 if(f == SFL_SORT_PRIO_SECONDARY)
83 if(ps_secondary == -1)
84 ps_secondary = ps_primary;
86 for(i = 0; i < MAX_TEAMSCORE; ++i)
88 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
89 if(f == SFL_SORT_PRIO_PRIMARY)
91 if(f == SFL_SORT_PRIO_SECONDARY)
94 if(ts_secondary == -1)
95 ts_secondary = ts_primary;
100 float SetTeam(entity pl, float Team);
102 void HUD_UpdatePlayerTeams()
109 for(pl = players.sort_next; pl; pl = pl.sort_next)
112 Team = GetPlayerColor(pl.sv_entnum);
113 if(SetTeam(pl, Team))
116 HUD_UpdatePlayerPos(pl);
120 pl = players.sort_next;
125 print(strcat("PNUM: ", ftos(num), "\n"));
130 int HUD_CompareScore(float vl, float vr, int f)
132 if(f & SFL_ZERO_IS_WORST)
134 if(vl == 0 && vr != 0)
136 if(vl != 0 && vr == 0)
140 return IS_INCREASING(f);
142 return IS_DECREASING(f);
146 float HUD_ComparePlayerScores(entity left, entity right)
149 vl = GetPlayerColor(left.sv_entnum);
150 vr = GetPlayerColor(right.sv_entnum);
162 if(vl == NUM_SPECTATOR)
164 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
166 if(!left.gotscores && right.gotscores)
171 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
175 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
180 for(i = 0; i < MAX_SCORE; ++i)
182 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
187 if (left.sv_entnum < right.sv_entnum)
193 void HUD_UpdatePlayerPos(entity player)
195 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
197 SORT_SWAP(player, other);
199 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
201 SORT_SWAP(other, player);
205 float HUD_CompareTeamScores(entity left, entity right)
209 if(left.team == NUM_SPECTATOR)
211 if(right.team == NUM_SPECTATOR)
214 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
218 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
222 for(i = 0; i < MAX_SCORE; ++i)
224 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
229 if (left.team < right.team)
235 void HUD_UpdateTeamPos(entity Team)
237 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
239 SORT_SWAP(Team, other);
241 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
243 SORT_SWAP(other, Team);
249 print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
250 print(_("^3|---------------------------------------------------------------|\n"));
251 print(_("Usage:\n"));
252 print(_("^2scoreboard_columns_set default\n"));
253 print(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
254 print(_("The following field names are recognized (case insensitive):\n"));
255 print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
257 print(_("^3name^7 or ^3nick^7 Name of a player\n"));
258 print(_("^3ping^7 Ping time\n"));
259 print(_("^3pl^7 Packet loss\n"));
260 print(_("^3kills^7 Number of kills\n"));
261 print(_("^3deaths^7 Number of deaths\n"));
262 print(_("^3suicides^7 Number of suicides\n"));
263 print(_("^3frags^7 kills - suicides\n"));
264 print(_("^3kd^7 The kill-death ratio\n"));
265 print(_("^3sum^7 frags - deaths\n"));
266 print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
267 print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
268 print(_("^3captime^7 Time of fastest cap (CTF)\n"));
269 print(_("^3fckills^7 Number of flag carrier kills\n"));
270 print(_("^3returns^7 Number of flag returns\n"));
271 print(_("^3drops^7 Number of flag drops\n"));
272 print(_("^3lives^7 Number of lives (LMS)\n"));
273 print(_("^3rank^7 Player rank\n"));
274 print(_("^3pushes^7 Number of players pushed into void\n"));
275 print(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
276 print(_("^3kckills^7 Number of keys carrier kills\n"));
277 print(_("^3losses^7 Number of times a key was lost\n"));
278 print(_("^3laps^7 Number of laps finished (race/cts)\n"));
279 print(_("^3time^7 Total time raced (race/cts)\n"));
280 print(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
281 print(_("^3ticks^7 Number of ticks (DOM)\n"));
282 print(_("^3takes^7 Number of domination points taken (DOM)\n"));
283 print(_("^3bckills^7 Number of ball carrier kills\n"));
284 print(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
285 print(_("^3score^7 Total score\n\n"));
287 print(_("Before a field you can put a + or - sign, then a comma separated list\n"
288 "of game types, then a slash, to make the field show up only in these\n"
289 "or in all but these game types. You can also specify 'all' as a\n"
290 "field to show all fields available for the current game mode.\n\n"));
292 print(_("The special game type names 'teams' and 'noteams' can be used to\n"
293 "include/exclude ALL teams/noteams game modes.\n\n"));
295 print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
296 print(_("will display name, ping and pl aligned to the left, and the fields\n"
297 "right of the vertical bar aligned to the right.\n"));
298 print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
299 "other gamemodes except DM.\n"));
302 #define HUD_DefaultColumnLayout() \
304 "-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" */ \
305 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \
306 "+lms/lives +lms/rank " \
307 "+kh/caps +kh/pushes +kh/destroyed " \
308 "?+race/laps ?+race/time ?+race/fastest " \
309 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
312 void Cmd_HUD_SetFields(float argc)
316 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
321 // set up a temporary scoreboard layout
322 // no layout can be properly set up until score_info data haven't been received
323 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
325 scores_label[ps_primary] = strzone("score");
326 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
329 // TODO: re enable with gametype dependant cvars?
330 if(argc < 3) // no arguments provided
331 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
334 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
338 if(argv(2) == "default")
339 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
340 else if(argv(2) == "all")
343 s = "ping pl name |";
344 for(i = 0; i < MAX_SCORE; ++i)
347 if(i != ps_secondary)
348 if(scores_label[i] != "")
349 s = strcat(s, " ", scores_label[i]);
351 if(ps_secondary != ps_primary)
352 s = strcat(s, " ", scores_label[ps_secondary]);
353 s = strcat(s, " ", scores_label[ps_primary]);
354 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
361 hud_fontsize = HUD_GetFontsize("hud_fontsize");
363 draw_beginBoldFont();
364 for(i = 1; i < argc - 1; ++i)
370 if(substring(str, 0, 1) == "?")
373 str = substring(str, 1, strlen(str) - 1);
376 slash = strstrofs(str, "/", 0);
379 pattern = substring(str, 0, slash);
380 str = substring(str, slash + 1, strlen(str) - (slash + 1));
382 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
386 strunzone(hud_title[hud_num_fields]);
387 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
388 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
389 str = strtolower(str);
392 hud_field[hud_num_fields] = SP_PING;
393 } else if(str == "pl") {
394 hud_field[hud_num_fields] = SP_PL;
395 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
396 hud_field[hud_num_fields] = SP_KDRATIO;
397 } else if(str == "sum" || str == "diff" || str == "k-d") {
398 hud_field[hud_num_fields] = SP_SUM;
399 } else if(str == "name" || str == "nick") {
400 hud_field[hud_num_fields] = SP_NAME;
402 } else if(str == "|") {
403 hud_field[hud_num_fields] = SP_SEPARATOR;
406 for(j = 0; j < MAX_SCORE; ++j)
407 if(str == strtolower(scores_label[j]))
408 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
417 printf("^1Error:^7 Unknown score field: '%s'\n", str);
421 hud_field[hud_num_fields] = j;
424 if(j == ps_secondary)
428 if(hud_num_fields >= MAX_HUD_FIELDS)
432 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
434 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
436 if(ps_primary == ps_secondary)
438 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
440 if(hud_num_fields+missing < MAX_HUD_FIELDS)
444 strunzone(hud_title[hud_num_fields]);
445 for(i = hud_num_fields; i > 0; --i)
447 hud_title[i] = hud_title[i-1];
448 hud_size[i] = hud_size[i-1];
449 hud_field[i] = hud_field[i-1];
451 hud_title[0] = strzone(TranslateScoresLabel("name"));
452 hud_field[0] = SP_NAME;
454 print("fixed missing field 'name'\n");
458 strunzone(hud_title[hud_num_fields]);
459 for(i = hud_num_fields; i > 1; --i)
461 hud_title[i] = hud_title[i-1];
462 hud_size[i] = hud_size[i-1];
463 hud_field[i] = hud_field[i-1];
465 hud_title[1] = strzone("|");
466 hud_field[1] = SP_SEPARATOR;
467 hud_size[1] = stringwidth("|", false, hud_fontsize);
469 print("fixed missing field '|'\n");
472 else if(!have_separator)
474 strunzone(hud_title[hud_num_fields]);
475 hud_title[hud_num_fields] = strzone("|");
476 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
477 hud_field[hud_num_fields] = SP_SEPARATOR;
479 print("fixed missing field '|'\n");
483 strunzone(hud_title[hud_num_fields]);
484 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
485 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
486 hud_field[hud_num_fields] = ps_secondary;
488 printf("fixed missing field '%s'\n", scores_label[ps_secondary]);
492 strunzone(hud_title[hud_num_fields]);
493 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
494 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
495 hud_field[hud_num_fields] = ps_primary;
497 printf("fixed missing field '%s'\n", scores_label[ps_primary]);
501 hud_field[hud_num_fields] = SP_END;
506 vector hud_field_rgb;
507 string hud_field_icon0;
508 string hud_field_icon1;
509 string hud_field_icon2;
510 vector hud_field_icon0_rgb;
511 vector hud_field_icon1_rgb;
512 vector hud_field_icon2_rgb;
513 float hud_field_icon0_alpha;
514 float hud_field_icon1_alpha;
515 float hud_field_icon2_alpha;
516 string HUD_GetField(entity pl, int field)
518 float tmp, num, denom;
521 hud_field_rgb = '1 1 1';
522 hud_field_icon0 = "";
523 hud_field_icon1 = "";
524 hud_field_icon2 = "";
525 hud_field_icon0_rgb = '1 1 1';
526 hud_field_icon1_rgb = '1 1 1';
527 hud_field_icon2_rgb = '1 1 1';
528 hud_field_icon0_alpha = 1;
529 hud_field_icon1_alpha = 1;
530 hud_field_icon2_alpha = 1;
535 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
536 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
540 tmp = max(0, min(220, f-80)) / 220;
541 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
547 f = pl.ping_packetloss;
548 tmp = pl.ping_movementloss;
549 if(f == 0 && tmp == 0)
551 str = ftos(ceil(f * 100));
553 str = strcat(str, "~", ftos(ceil(tmp * 100)));
554 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
555 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
559 if(ready_waiting && pl.ready)
561 hud_field_icon0 = "gfx/scoreboard/player_ready";
565 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
567 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
568 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
569 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
570 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
571 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
574 return GetPlayerName(pl.sv_entnum);
577 f = pl.(scores[SP_KILLS]);
578 f -= pl.(scores[SP_SUICIDES]);
582 num = pl.(scores[SP_KILLS]);
583 denom = pl.(scores[SP_DEATHS]);
586 hud_field_rgb = '0 1 0';
587 str = sprintf("%d", num);
588 } else if(num <= 0) {
589 hud_field_rgb = '1 0 0';
590 str = sprintf("%.1f", num/denom);
592 str = sprintf("%.1f", num/denom);
596 f = pl.(scores[SP_KILLS]);
597 f -= pl.(scores[SP_DEATHS]);
600 hud_field_rgb = '0 1 0';
602 hud_field_rgb = '1 1 1';
604 hud_field_rgb = '1 0 0';
609 tmp = pl.(scores[field]);
610 f = scores_flags[field];
611 if(field == ps_primary)
612 hud_field_rgb = '1 1 0';
613 else if(field == ps_secondary)
614 hud_field_rgb = '0 1 1';
616 hud_field_rgb = '1 1 1';
617 return ScoreString(f, tmp);
622 float hud_fixscoreboardcolumnwidth_len;
623 float hud_fixscoreboardcolumnwidth_iconlen;
624 float hud_fixscoreboardcolumnwidth_marginlen;
626 string HUD_FixScoreboardColumnWidth(int i, string str)
630 field = hud_field[i];
632 hud_fixscoreboardcolumnwidth_iconlen = 0;
634 if(hud_field_icon0 != "")
636 sz = draw_getimagesize(hud_field_icon0);
638 if(hud_fixscoreboardcolumnwidth_iconlen < f)
639 hud_fixscoreboardcolumnwidth_iconlen = f;
642 if(hud_field_icon1 != "")
644 sz = draw_getimagesize(hud_field_icon1);
646 if(hud_fixscoreboardcolumnwidth_iconlen < f)
647 hud_fixscoreboardcolumnwidth_iconlen = f;
650 if(hud_field_icon2 != "")
652 sz = draw_getimagesize(hud_field_icon2);
654 if(hud_fixscoreboardcolumnwidth_iconlen < f)
655 hud_fixscoreboardcolumnwidth_iconlen = f;
658 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
660 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
661 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
663 hud_fixscoreboardcolumnwidth_marginlen = 0;
665 if(field == SP_NAME) // name gets all remaining space
669 namesize = sbwidth;// / hud_fontsize_x;
670 for(j = 0; j < hud_num_fields; ++j)
672 if (hud_field[i] != SP_SEPARATOR)
673 namesize -= hud_size[j] + hud_fontsize.x;
674 namesize += hud_fontsize.x;
675 hud_size[i] = namesize;
677 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
678 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
679 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
680 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
683 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
685 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
692 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
695 rgb = Team_ColorRGB(pl.team);
699 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
701 if((rgb == '1 1 1') && (!is_spec)) {
702 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
703 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
704 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
706 vector h_pos = pos - '1 1 0';
707 vector h_size = item_size + '2 0 0';
708 // alternated rows highlighting
710 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
711 else if((scoreboard_highlight) && (!(pl_number % 2)))
712 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
718 for(i = 0; i < hud_num_fields; ++i)
720 field = hud_field[i];
721 if(field == SP_SEPARATOR)
724 if(is_spec && field != SP_NAME && field != SP_PING) {
725 pos.x += hud_size[i] + hud_fontsize.x;
728 str = HUD_GetField(pl, field);
729 str = HUD_FixScoreboardColumnWidth(i, str);
731 pos.x += hud_size[i] + hud_fontsize.x;
733 if(field == SP_NAME) {
734 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
736 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
738 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
740 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
742 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
744 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
747 tmp.x = hud_size[i] + hud_fontsize.x;
748 if(hud_field_icon0 != "")
750 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);
752 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);
753 if(hud_field_icon1 != "")
755 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);
757 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);
758 if(hud_field_icon2 != "")
760 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);
762 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);
765 if(hud_field[i] == SP_SEPARATOR)
768 for(i = hud_num_fields-1; i > 0; --i)
770 field = hud_field[i];
771 if(field == SP_SEPARATOR)
774 if(is_spec && field != SP_NAME && field != SP_PING) {
775 pos.x -= hud_size[i] + hud_fontsize.x;
779 str = HUD_GetField(pl, field);
780 str = HUD_FixScoreboardColumnWidth(i, str);
782 if(field == SP_NAME) {
783 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
785 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
787 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
789 tmp.x = hud_fixscoreboardcolumnwidth_len;
791 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
793 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
797 if(hud_field_icon0 != "")
799 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);
801 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);
802 if(hud_field_icon1 != "")
804 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);
806 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);
807 if(hud_field_icon2 != "")
809 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);
811 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);
812 pos.x -= hud_size[i] + hud_fontsize.x;
817 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
821 * HUD_Scoreboard_MakeTable
823 * Makes a table for a team (for all playing players in DM) and fills it
826 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
828 float body_table_height;
829 vector tmp = '0 0 0', column_dim = '0 0 0';
832 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
834 pos.y += autocvar_scoreboard_border_thickness;
838 tmp.y = 1.25 * hud_fontsize.y;
842 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
844 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
847 tmp.y += autocvar_scoreboard_border_thickness;
848 tmp.y += body_table_height;
849 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
851 // separator header/table
852 pos.y += 1.25 * hud_fontsize.y;
853 tmp.y = autocvar_scoreboard_border_thickness;
854 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
856 pos.y += autocvar_scoreboard_border_thickness;
859 tmp.y = body_table_height;
861 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
863 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
865 // anyway, apply some color
866 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
868 // go back to the top to make alternated columns highlighting and to print the strings
869 pos.y -= 1.25 * hud_fontsize.y;
870 pos.y -= autocvar_scoreboard_border_thickness;
874 if (scoreboard_highlight)
876 column_dim.y = 1.25 * hud_fontsize.y; // header
877 column_dim.y += autocvar_scoreboard_border_thickness;
878 column_dim.y += body_table_height;
881 // print the strings of the columns headers and draw the columns
882 draw_beginBoldFont();
884 for(i = 0; i < hud_num_fields; ++i)
886 if(hud_field[i] == SP_SEPARATOR)
888 column_dim.x = hud_size[i] + hud_fontsize.x;
889 if (scoreboard_highlight)
892 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
894 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
895 pos.x += column_dim.x;
897 if(hud_field[i] == SP_SEPARATOR)
901 for(i = hud_num_fields-1; i > 0; --i)
903 if(hud_field[i] == SP_SEPARATOR)
906 pos.x -= hud_size[i];
908 if (scoreboard_highlight)
912 if (i == hud_num_fields-1)
913 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
915 column_dim.x = hud_size[i] + hud_fontsize.x;
916 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
920 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
921 tmp.x = (hud_size[i] - tmp.x);
922 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
923 pos.x -= hud_fontsize.x;
929 pos.y += 1.25 * hud_fontsize.y; // skip the header
930 pos.y += autocvar_scoreboard_border_thickness;
934 tmp.y = hud_fontsize.y * 1.25;
936 // fill the table and draw the rows
939 for(pl = players.sort_next; pl; pl = pl.sort_next)
941 if(pl.team != tm.team)
943 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
944 pos.y += 1.25 * hud_fontsize.y;
948 for(pl = players.sort_next; pl; pl = pl.sort_next)
950 if(pl.team == NUM_SPECTATOR)
952 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
953 pos.y += 1.25 * hud_fontsize.y;
958 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
959 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
964 float HUD_WouldDrawScoreboard() {
965 if (autocvar__hud_configure)
967 else if (scoreboard_showscores)
969 else if (intermission == 1)
971 else if (intermission == 2)
973 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
975 else if (scoreboard_showscores_force)
980 float average_accuracy;
981 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
984 int weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
986 if(autocvar_scoreboard_accuracy_doublerows)
991 float fontsize = height * 1/3;
992 float weapon_height = height * 2/3;
993 float weapon_width = sbwidth / weapon_cnt;
994 float g_instagib = 0;
996 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
997 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
998 vector tmp = '0 0 0';
1000 tmp.y = height * rows;
1003 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1005 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1006 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1008 // column highlighting
1009 for(i = 0; i < weapon_cnt/rows; ++i)
1012 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);
1016 for(i = 0; i < rows; ++i)
1018 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);
1021 average_accuracy = 0;
1022 float weapons_with_stats;
1023 weapons_with_stats = 0;
1025 pos.x += weapon_width / 2;
1027 if(switchweapon == WEP_VAPORIZER)
1028 g_instagib = 1; // TODO: real detection for instagib?
1031 if(autocvar_scoreboard_accuracy_nocolors)
1034 Accuracy_LoadColors();
1036 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1038 self = get_weaponinfo(i);
1041 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
1043 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1046 if(weapon_stats >= 0)
1047 weapon_alpha = scoreboard_alpha_fg;
1049 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1052 drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1054 if(weapon_stats >= 0) {
1055 weapons_with_stats += 1;
1056 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1059 s = sprintf("%d%%", weapon_stats*100);
1062 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1064 if(!autocvar_scoreboard_accuracy_nocolors)
1065 rgb = Accuracy_GetColor(weapon_stats);
1067 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1069 pos.x += weapon_width * rows;
1070 if(rows == 2 && i == 6) {
1076 if(weapons_with_stats)
1077 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1080 pos.x -= weapon_width / 2;
1084 pos.y += 1.25 * hud_fontsize.y;
1088 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1090 pos.x += hud_fontsize.x * 0.25;
1091 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1092 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1093 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1095 pos.y+= hud_fontsize.y;
1100 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1101 float stat_secrets_found, stat_secrets_total;
1102 float stat_monsters_killed, stat_monsters_total;
1106 // get monster stats
1107 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1108 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1110 // get secrets stats
1111 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1112 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1114 // get number of rows
1115 if(stat_secrets_total)
1117 if(stat_monsters_total)
1120 // if no rows, return
1124 // draw table header
1125 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1126 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1129 vector tmp = '0 0 0';
1131 tmp.y = hud_fontsize.y * rows;
1134 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1136 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1137 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1140 if(stat_monsters_total)
1142 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1143 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1147 if(stat_secrets_total)
1149 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1150 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1154 pos.y += 1.25 * hud_fontsize.y;
1159 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1162 RANKINGS_RECEIVED_CNT = 0;
1163 for (i=RANKINGS_CNT-1; i>=0; --i)
1165 ++RANKINGS_RECEIVED_CNT;
1167 if (RANKINGS_RECEIVED_CNT == 0)
1171 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1173 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1174 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1175 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1177 pos.y += hud_fontsize.y;
1178 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1179 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1180 vector tmp = '0 0 0';
1182 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1185 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1187 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1188 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1191 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1198 n = grecordholder[i];
1199 p = count_ordinal(i+1);
1200 if(grecordholder[i] == GetPlayerName(player_localnum))
1201 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1202 else if(!(i % 2) && scoreboard_highlight)
1203 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1204 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1205 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);
1206 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1207 pos.y += 1.25 * hud_fontsize.y;
1209 pos.y += autocvar_scoreboard_border_thickness;
1214 float hud_woulddrawscoreboard_prev;
1215 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1216 void HUD_DrawScoreboard()
1218 float hud_woulddrawscoreboard;
1219 hud_woulddrawscoreboard = scoreboard_active;
1220 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1221 hud_woulddrawscoreboard_change = time;
1222 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1225 if(hud_woulddrawscoreboard) {
1226 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1227 if (scoreboard_fadeinspeed)
1228 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1230 scoreboard_fade_alpha = 1;
1233 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1234 if (scoreboard_fadeoutspeed)
1235 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1237 scoreboard_fade_alpha = 0;
1240 if (!scoreboard_fade_alpha)
1243 HUD_UpdatePlayerTeams();
1245 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1246 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1247 scoreboard_highlight = autocvar_scoreboard_highlight;
1248 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1249 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1250 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1251 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1253 vector rgb, pos, tmp;
1257 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1258 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1260 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1261 ymax = (vid_conheight - ymin);
1263 sbwidth = xmax - xmin;
1265 // Initializes position
1271 vector sb_heading_fontsize;
1272 sb_heading_fontsize = hud_fontsize * 2;
1273 draw_beginBoldFont();
1274 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1277 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1279 // Draw the scoreboard
1280 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1284 vector team_score_baseoffset;
1285 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1286 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1288 if(tm.team == NUM_SPECTATOR)
1291 draw_beginBoldFont();
1292 rgb = Team_ColorRGB(tm.team);
1293 str = ftos(tm.(teamscores[ts_primary]));
1294 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1296 if(ts_primary != ts_secondary)
1298 str = ftos(tm.(teamscores[ts_secondary]));
1299 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);
1303 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1305 rgb.x = autocvar_scoreboard_color_bg_r;
1306 rgb.y = autocvar_scoreboard_color_bg_g;
1307 rgb.z = autocvar_scoreboard_color_bg_b;
1311 rgb.x = autocvar_scoreboard_color_bg_r;
1312 rgb.y = autocvar_scoreboard_color_bg_g;
1313 rgb.z = autocvar_scoreboard_color_bg_b;
1315 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1317 if(tm.team == NUM_SPECTATOR)
1320 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1324 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1325 if(race_speedaward) {
1326 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1327 pos.y += 1.25 * hud_fontsize.y;
1329 if(race_speedaward_alltimebest) {
1330 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1331 pos.y += 1.25 * hud_fontsize.y;
1333 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1335 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1337 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1339 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1344 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1346 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1353 item_size.x = sbwidth;
1354 item_size.y = hud_fontsize.y * 1.25;
1356 for(pl = players.sort_next; pl; pl = pl.sort_next)
1358 if(pl.team != NUM_SPECTATOR)
1360 pos.y += 1.25 * hud_fontsize.y;
1361 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1367 draw_beginBoldFont();
1368 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1370 pos.y += 1.25 * hud_fontsize.y;
1373 // Print info string
1375 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1376 tl = getstatf(STAT_TIMELIMIT);
1377 fl = getstatf(STAT_FRAGLIMIT);
1378 ll = getstatf(STAT_LEADLIMIT);
1379 if(gametype == MAPINFO_TYPE_LMS)
1382 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1387 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1391 str = strcat(str, _(" or"));
1394 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1395 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1396 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1397 TranslateScoresLabel(teamscores_label[ts_primary])));
1401 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1402 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1403 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1404 TranslateScoresLabel(scores_label[ps_primary])));
1409 if(tl > 0 || fl > 0)
1410 str = strcat(str, _(" or"));
1413 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1414 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1415 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1416 TranslateScoresLabel(teamscores_label[ts_primary])));
1420 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1421 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1422 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1423 TranslateScoresLabel(scores_label[ps_primary])));
1428 pos.y += 1.2 * hud_fontsize.y;
1429 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1431 // print information about respawn status
1432 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1436 if(respawn_time < 0)
1438 // a negative number means we are awaiting respawn, time value is still the same
1439 respawn_time *= -1; // remove mark now that we checked it
1440 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1442 str = sprintf(_("^1Respawning in ^3%s^1..."),
1443 (autocvar_scoreboard_respawntime_decimals ?
1444 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1446 count_seconds(respawn_time - time)
1450 else if(time < respawn_time)
1452 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1453 (autocvar_scoreboard_respawntime_decimals ?
1454 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1456 count_seconds(respawn_time - time)
1460 else if(time >= respawn_time)
1461 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1463 pos.y += 1.2 * hud_fontsize.y;
1464 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1467 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;