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 for(i = 1; i < argc - 1; ++i)
369 if(substring(str, 0, 1) == "?")
372 str = substring(str, 1, strlen(str) - 1);
375 slash = strstrofs(str, "/", 0);
378 pattern = substring(str, 0, slash);
379 str = substring(str, slash + 1, strlen(str) - (slash + 1));
381 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
385 strunzone(hud_title[hud_num_fields]);
386 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
387 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
388 str = strtolower(str);
391 hud_field[hud_num_fields] = SP_PING;
392 } else if(str == "pl") {
393 hud_field[hud_num_fields] = SP_PL;
394 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
395 hud_field[hud_num_fields] = SP_KDRATIO;
396 } else if(str == "sum" || str == "diff" || str == "k-d") {
397 hud_field[hud_num_fields] = SP_SUM;
398 } else if(str == "name" || str == "nick") {
399 hud_field[hud_num_fields] = SP_NAME;
401 } else if(str == "|") {
402 hud_field[hud_num_fields] = SP_SEPARATOR;
405 for(j = 0; j < MAX_SCORE; ++j)
406 if(str == strtolower(scores_label[j]))
407 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
416 printf("^1Error:^7 Unknown score field: '%s'\n", str);
420 hud_field[hud_num_fields] = j;
423 if(j == ps_secondary)
427 if(hud_num_fields >= MAX_HUD_FIELDS)
431 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
433 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
435 if(ps_primary == ps_secondary)
437 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
439 if(hud_num_fields+missing < MAX_HUD_FIELDS)
443 strunzone(hud_title[hud_num_fields]);
444 for(i = hud_num_fields; i > 0; --i)
446 hud_title[i] = hud_title[i-1];
447 hud_size[i] = hud_size[i-1];
448 hud_field[i] = hud_field[i-1];
450 hud_title[0] = strzone(TranslateScoresLabel("name"));
451 hud_field[0] = SP_NAME;
453 print("fixed missing field 'name'\n");
457 strunzone(hud_title[hud_num_fields]);
458 for(i = hud_num_fields; i > 1; --i)
460 hud_title[i] = hud_title[i-1];
461 hud_size[i] = hud_size[i-1];
462 hud_field[i] = hud_field[i-1];
464 hud_title[1] = strzone("|");
465 hud_field[1] = SP_SEPARATOR;
466 hud_size[1] = stringwidth("|", false, hud_fontsize);
468 print("fixed missing field '|'\n");
471 else if(!have_separator)
473 strunzone(hud_title[hud_num_fields]);
474 hud_title[hud_num_fields] = strzone("|");
475 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
476 hud_field[hud_num_fields] = SP_SEPARATOR;
478 print("fixed missing field '|'\n");
482 strunzone(hud_title[hud_num_fields]);
483 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
484 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
485 hud_field[hud_num_fields] = ps_secondary;
487 printf("fixed missing field '%s'\n", scores_label[ps_secondary]);
491 strunzone(hud_title[hud_num_fields]);
492 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
493 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
494 hud_field[hud_num_fields] = ps_primary;
496 printf("fixed missing field '%s'\n", scores_label[ps_primary]);
500 hud_field[hud_num_fields] = SP_END;
504 vector hud_field_rgb;
505 string hud_field_icon0;
506 string hud_field_icon1;
507 string hud_field_icon2;
508 vector hud_field_icon0_rgb;
509 vector hud_field_icon1_rgb;
510 vector hud_field_icon2_rgb;
511 float hud_field_icon0_alpha;
512 float hud_field_icon1_alpha;
513 float hud_field_icon2_alpha;
514 string HUD_GetField(entity pl, int field)
516 float tmp, num, denom;
519 hud_field_rgb = '1 1 1';
520 hud_field_icon0 = "";
521 hud_field_icon1 = "";
522 hud_field_icon2 = "";
523 hud_field_icon0_rgb = '1 1 1';
524 hud_field_icon1_rgb = '1 1 1';
525 hud_field_icon2_rgb = '1 1 1';
526 hud_field_icon0_alpha = 1;
527 hud_field_icon1_alpha = 1;
528 hud_field_icon2_alpha = 1;
533 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
534 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
538 tmp = max(0, min(220, f-80)) / 220;
539 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
545 f = pl.ping_packetloss;
546 tmp = pl.ping_movementloss;
547 if(f == 0 && tmp == 0)
549 str = ftos(ceil(f * 100));
551 str = strcat(str, "~", ftos(ceil(tmp * 100)));
552 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
553 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
557 if(ready_waiting && pl.ready)
559 hud_field_icon0 = "gfx/scoreboard/player_ready";
563 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
565 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
566 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
567 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
568 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
569 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
572 return GetPlayerName(pl.sv_entnum);
575 f = pl.(scores[SP_KILLS]);
576 f -= pl.(scores[SP_SUICIDES]);
580 num = pl.(scores[SP_KILLS]);
581 denom = pl.(scores[SP_DEATHS]);
584 hud_field_rgb = '0 1 0';
585 str = sprintf("%d", num);
586 } else if(num <= 0) {
587 hud_field_rgb = '1 0 0';
588 str = sprintf("%.1f", num/denom);
590 str = sprintf("%.1f", num/denom);
594 f = pl.(scores[SP_KILLS]);
595 f -= pl.(scores[SP_DEATHS]);
598 hud_field_rgb = '0 1 0';
600 hud_field_rgb = '1 1 1';
602 hud_field_rgb = '1 0 0';
607 tmp = pl.(scores[field]);
608 f = scores_flags[field];
609 if(field == ps_primary)
610 hud_field_rgb = '1 1 0';
611 else if(field == ps_secondary)
612 hud_field_rgb = '0 1 1';
614 hud_field_rgb = '1 1 1';
615 return ScoreString(f, tmp);
620 float hud_fixscoreboardcolumnwidth_len;
621 float hud_fixscoreboardcolumnwidth_iconlen;
622 float hud_fixscoreboardcolumnwidth_marginlen;
624 string HUD_FixScoreboardColumnWidth(int i, string str)
628 field = hud_field[i];
630 hud_fixscoreboardcolumnwidth_iconlen = 0;
632 if(hud_field_icon0 != "")
634 sz = draw_getimagesize(hud_field_icon0);
636 if(hud_fixscoreboardcolumnwidth_iconlen < f)
637 hud_fixscoreboardcolumnwidth_iconlen = f;
640 if(hud_field_icon1 != "")
642 sz = draw_getimagesize(hud_field_icon1);
644 if(hud_fixscoreboardcolumnwidth_iconlen < f)
645 hud_fixscoreboardcolumnwidth_iconlen = f;
648 if(hud_field_icon2 != "")
650 sz = draw_getimagesize(hud_field_icon2);
652 if(hud_fixscoreboardcolumnwidth_iconlen < f)
653 hud_fixscoreboardcolumnwidth_iconlen = f;
656 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
658 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
659 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
661 hud_fixscoreboardcolumnwidth_marginlen = 0;
663 if(field == SP_NAME) // name gets all remaining space
667 namesize = sbwidth;// / hud_fontsize_x;
668 for(j = 0; j < hud_num_fields; ++j)
670 if (hud_field[i] != SP_SEPARATOR)
671 namesize -= hud_size[j] + hud_fontsize.x;
672 namesize += hud_fontsize.x;
673 hud_size[i] = namesize;
675 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
676 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
677 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
678 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
681 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
683 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
690 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
693 rgb = Team_ColorRGB(pl.team);
697 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
699 if((rgb == '1 1 1') && (!is_spec)) {
700 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
701 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
702 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
704 vector h_pos = pos - '1 1 0';
705 vector h_size = item_size + '2 0 0';
706 // alternated rows highlighting
708 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
709 else if((scoreboard_highlight) && (!(pl_number % 2)))
710 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
716 for(i = 0; i < hud_num_fields; ++i)
718 field = hud_field[i];
719 if(field == SP_SEPARATOR)
722 if(is_spec && field != SP_NAME && field != SP_PING) {
723 pos.x += hud_size[i] + hud_fontsize.x;
726 str = HUD_GetField(pl, field);
727 str = HUD_FixScoreboardColumnWidth(i, str);
729 pos.x += hud_size[i] + hud_fontsize.x;
731 if(field == SP_NAME) {
732 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
734 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
736 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
738 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
740 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
742 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
745 tmp.x = hud_size[i] + hud_fontsize.x;
746 if(hud_field_icon0 != "")
748 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);
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, DRAWFLAG_NORMAL);
751 if(hud_field_icon1 != "")
753 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);
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, DRAWFLAG_NORMAL);
756 if(hud_field_icon2 != "")
758 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);
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, DRAWFLAG_NORMAL);
763 if(hud_field[i] == SP_SEPARATOR)
766 for(i = hud_num_fields-1; i > 0; --i)
768 field = hud_field[i];
769 if(field == SP_SEPARATOR)
772 if(is_spec && field != SP_NAME && field != SP_PING) {
773 pos.x -= hud_size[i] + hud_fontsize.x;
777 str = HUD_GetField(pl, field);
778 str = HUD_FixScoreboardColumnWidth(i, str);
780 if(field == SP_NAME) {
781 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
783 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
785 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
787 tmp.x = hud_fixscoreboardcolumnwidth_len;
789 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
791 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
795 if(hud_field_icon0 != "")
797 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);
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, DRAWFLAG_NORMAL);
800 if(hud_field_icon1 != "")
802 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);
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, DRAWFLAG_NORMAL);
805 if(hud_field_icon2 != "")
807 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);
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, DRAWFLAG_NORMAL);
810 pos.x -= hud_size[i] + hud_fontsize.x;
815 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
819 * HUD_Scoreboard_MakeTable
821 * Makes a table for a team (for all playing players in DM) and fills it
824 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
826 float body_table_height;
827 vector tmp = '0 0 0', column_dim = '0 0 0';
830 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
832 pos.y += autocvar_scoreboard_border_thickness;
836 tmp.y = 1.25 * hud_fontsize.y;
840 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
842 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
845 tmp.y += autocvar_scoreboard_border_thickness;
846 tmp.y += body_table_height;
847 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
849 // separator header/table
850 pos.y += 1.25 * hud_fontsize.y;
851 tmp.y = autocvar_scoreboard_border_thickness;
852 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
854 pos.y += autocvar_scoreboard_border_thickness;
857 tmp.y = body_table_height;
859 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
861 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
863 // anyway, apply some color
864 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
866 // go back to the top to make alternated columns highlighting and to print the strings
867 pos.y -= 1.25 * hud_fontsize.y;
868 pos.y -= autocvar_scoreboard_border_thickness;
872 if (scoreboard_highlight)
874 column_dim.y = 1.25 * hud_fontsize.y; // header
875 column_dim.y += autocvar_scoreboard_border_thickness;
876 column_dim.y += body_table_height;
879 // print the strings of the columns headers and draw the columns
881 for(i = 0; i < hud_num_fields; ++i)
883 if(hud_field[i] == SP_SEPARATOR)
885 column_dim.x = hud_size[i] + hud_fontsize.x;
886 if (scoreboard_highlight)
889 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
891 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
892 pos.x += column_dim.x;
894 if(hud_field[i] == SP_SEPARATOR)
898 for(i = hud_num_fields-1; i > 0; --i)
900 if(hud_field[i] == SP_SEPARATOR)
903 pos.x -= hud_size[i];
905 if (scoreboard_highlight)
909 if (i == hud_num_fields-1)
910 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
912 column_dim.x = hud_size[i] + hud_fontsize.x;
913 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
917 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
918 tmp.x = (hud_size[i] - tmp.x);
919 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
920 pos.x -= hud_fontsize.x;
925 pos.y += 1.25 * hud_fontsize.y; // skip the header
926 pos.y += autocvar_scoreboard_border_thickness;
930 tmp.y = hud_fontsize.y * 1.25;
932 // fill the table and draw the rows
935 for(pl = players.sort_next; pl; pl = pl.sort_next)
937 if(pl.team != tm.team)
939 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
940 pos.y += 1.25 * hud_fontsize.y;
944 for(pl = players.sort_next; pl; pl = pl.sort_next)
946 if(pl.team == NUM_SPECTATOR)
948 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
949 pos.y += 1.25 * hud_fontsize.y;
954 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
955 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
960 float HUD_WouldDrawScoreboard() {
961 if (autocvar__hud_configure)
963 else if (scoreboard_showscores)
965 else if (intermission == 1)
967 else if (intermission == 2)
969 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
971 else if (scoreboard_showscores_force)
976 float average_accuracy;
977 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
979 WepSet weapons_stat = WepSet_GetFromStat();
980 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
981 float initial_posx = pos.x;
985 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
987 self = get_weaponinfo(i);
991 weapon_stats = weapon_accuracy[i-WEP_FIRST];
993 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
997 int weapon_cnt = WEP_COUNT - disownedcnt;
1003 if(autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor(WEP_COUNT * 0.5))
1007 int columnns = ceil(weapon_cnt / rows);
1010 float fontsize = height * 1/3;
1011 float weapon_height = height * 2/3;
1012 float weapon_width = sbwidth / columnns / rows;
1013 bool g_instagib = false;
1015 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1016 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1017 vector tmp = '0 0 0';
1019 tmp.y = height * rows;
1022 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1024 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1025 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1027 // column highlighting
1028 for(i = 0; i < columnns; ++i)
1031 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);
1035 for(i = 0; i < rows; ++i)
1037 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);
1040 average_accuracy = 0;
1041 int weapons_with_stats = 0;
1043 pos.x += weapon_width / 2;
1045 if(switchweapon == WEP_VAPORIZER)
1046 g_instagib = true; // TODO: real detection for instagib?
1048 if(autocvar_scoreboard_accuracy_nocolors)
1051 Accuracy_LoadColors();
1053 float oldposx = pos.x;
1057 for(i = WEP_FIRST, column = 0; i <= WEP_LAST; ++i)
1059 self = get_weaponinfo(i);
1062 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1064 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1068 if(weapon_stats >= 0)
1069 weapon_alpha = scoreboard_alpha_fg;
1071 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1074 drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1076 if(weapon_stats >= 0) {
1077 weapons_with_stats += 1;
1078 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1081 s = sprintf("%d%%", weapon_stats*100);
1084 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1086 if(!autocvar_scoreboard_accuracy_nocolors)
1087 rgb = Accuracy_GetColor(weapon_stats);
1089 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1091 tmpos.x += weapon_width * rows;
1092 pos.x += weapon_width * rows;
1093 if(rows == 2 && column == columnns - 1) {
1101 if(weapons_with_stats)
1102 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1105 pos.y += 1.25 * hud_fontsize.y;
1106 pos.x = initial_posx;
1110 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1112 pos.x += hud_fontsize.x * 0.25;
1113 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1114 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1115 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1117 pos.y+= hud_fontsize.y;
1122 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1123 float stat_secrets_found, stat_secrets_total;
1124 float stat_monsters_killed, stat_monsters_total;
1128 // get monster stats
1129 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1130 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1132 // get secrets stats
1133 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1134 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1136 // get number of rows
1137 if(stat_secrets_total)
1139 if(stat_monsters_total)
1142 // if no rows, return
1146 // draw table header
1147 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1148 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1151 vector tmp = '0 0 0';
1153 tmp.y = hud_fontsize.y * rows;
1156 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1158 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1159 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1162 if(stat_monsters_total)
1164 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1165 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1169 if(stat_secrets_total)
1171 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1172 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1176 pos.y += 1.25 * hud_fontsize.y;
1181 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1184 RANKINGS_RECEIVED_CNT = 0;
1185 for (i=RANKINGS_CNT-1; i>=0; --i)
1187 ++RANKINGS_RECEIVED_CNT;
1189 if (RANKINGS_RECEIVED_CNT == 0)
1193 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1195 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1196 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1197 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1199 pos.y += hud_fontsize.y;
1200 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1201 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1202 vector tmp = '0 0 0';
1204 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1207 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1209 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1210 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1213 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1220 n = grecordholder[i];
1221 p = count_ordinal(i+1);
1222 if(grecordholder[i] == GetPlayerName(player_localnum))
1223 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1224 else if(!(i % 2) && scoreboard_highlight)
1225 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1226 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1227 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);
1228 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1229 pos.y += 1.25 * hud_fontsize.y;
1231 pos.y += autocvar_scoreboard_border_thickness;
1236 float hud_woulddrawscoreboard_prev;
1237 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1238 void HUD_DrawScoreboard()
1240 float hud_woulddrawscoreboard;
1241 hud_woulddrawscoreboard = scoreboard_active;
1242 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1243 hud_woulddrawscoreboard_change = time;
1244 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1247 if(hud_woulddrawscoreboard) {
1248 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1249 if (scoreboard_fadeinspeed)
1250 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1252 scoreboard_fade_alpha = 1;
1255 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1256 if (scoreboard_fadeoutspeed)
1257 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1259 scoreboard_fade_alpha = 0;
1262 if (!scoreboard_fade_alpha)
1265 HUD_UpdatePlayerTeams();
1267 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1268 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1269 scoreboard_highlight = autocvar_scoreboard_highlight;
1270 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1271 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1272 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1273 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1275 vector rgb, pos, tmp;
1279 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1280 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1282 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1283 ymax = (vid_conheight - ymin);
1285 sbwidth = xmax - xmin;
1287 // Initializes position
1293 vector sb_heading_fontsize;
1294 sb_heading_fontsize = hud_fontsize * 2;
1295 draw_beginBoldFont();
1296 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1299 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1301 // Draw the scoreboard
1302 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1306 vector team_score_baseoffset;
1307 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1308 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1310 if(tm.team == NUM_SPECTATOR)
1313 draw_beginBoldFont();
1314 rgb = Team_ColorRGB(tm.team);
1315 str = ftos(tm.(teamscores[ts_primary]));
1316 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1318 if(ts_primary != ts_secondary)
1320 str = ftos(tm.(teamscores[ts_secondary]));
1321 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);
1325 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1327 rgb.x = autocvar_scoreboard_color_bg_r;
1328 rgb.y = autocvar_scoreboard_color_bg_g;
1329 rgb.z = autocvar_scoreboard_color_bg_b;
1333 rgb.x = autocvar_scoreboard_color_bg_r;
1334 rgb.y = autocvar_scoreboard_color_bg_g;
1335 rgb.z = autocvar_scoreboard_color_bg_b;
1337 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1339 if(tm.team == NUM_SPECTATOR)
1342 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1346 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1347 if(race_speedaward) {
1348 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1349 pos.y += 1.25 * hud_fontsize.y;
1351 if(race_speedaward_alltimebest) {
1352 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1353 pos.y += 1.25 * hud_fontsize.y;
1355 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1357 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1359 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1361 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1366 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1368 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1375 item_size.x = sbwidth;
1376 item_size.y = hud_fontsize.y * 1.25;
1378 for(pl = players.sort_next; pl; pl = pl.sort_next)
1380 if(pl.team != NUM_SPECTATOR)
1382 pos.y += 1.25 * hud_fontsize.y;
1383 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1389 draw_beginBoldFont();
1390 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1392 pos.y += 1.25 * hud_fontsize.y;
1395 // Print info string
1397 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1398 tl = getstatf(STAT_TIMELIMIT);
1399 fl = getstatf(STAT_FRAGLIMIT);
1400 ll = getstatf(STAT_LEADLIMIT);
1401 if(gametype == MAPINFO_TYPE_LMS)
1404 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1409 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1413 str = strcat(str, _(" or"));
1416 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1417 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1418 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1419 TranslateScoresLabel(teamscores_label[ts_primary])));
1423 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1424 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1425 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1426 TranslateScoresLabel(scores_label[ps_primary])));
1431 if(tl > 0 || fl > 0)
1432 str = strcat(str, _(" or"));
1435 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1436 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1437 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1438 TranslateScoresLabel(teamscores_label[ts_primary])));
1442 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1443 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1444 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1445 TranslateScoresLabel(scores_label[ps_primary])));
1450 pos.y += 1.2 * hud_fontsize.y;
1451 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1453 // print information about respawn status
1454 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1458 if(respawn_time < 0)
1460 // a negative number means we are awaiting respawn, time value is still the same
1461 respawn_time *= -1; // remove mark now that we checked it
1462 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1464 str = sprintf(_("^1Respawning in ^3%s^1..."),
1465 (autocvar_scoreboard_respawntime_decimals ?
1466 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1468 count_seconds(respawn_time - time)
1472 else if(time < respawn_time)
1474 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1475 (autocvar_scoreboard_respawntime_decimals ?
1476 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1478 count_seconds(respawn_time - time)
1482 else if(time >= respawn_time)
1483 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1485 pos.y += 1.2 * hud_fontsize.y;
1486 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1489 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;