1 #include "scoreboard.qh"
6 #include "../common/constants.qh"
7 #include "../common/mapinfo.qh"
8 #include "../common/minigames/cl_minigames.qh"
9 #include "../common/stats.qh"
10 #include "../common/teams.qh"
11 #include "../common/util.qh"
13 float scoreboard_alpha_bg;
14 float scoreboard_alpha_fg;
15 float scoreboard_highlight;
16 float scoreboard_highlight_alpha;
17 float scoreboard_highlight_alpha_self;
18 float scoreboard_alpha_name;
19 float scoreboard_alpha_name_self;
21 void drawstringright(vector, string, vector, vector, float, float);
22 void drawstringcenter(vector, string, vector, vector, float, float);
24 const float SCOREBOARD_OFFSET = 50;
26 // wrapper to put all possible scores titles through gettext
27 string TranslateScoresLabel(string l)
31 case "bckills": return CTX(_("SCO^bckills"));
32 case "bctime": return CTX(_("SCO^bctime"));
33 case "caps": return CTX(_("SCO^caps"));
34 case "captime": return CTX(_("SCO^captime"));
35 case "deaths": return CTX(_("SCO^deaths"));
36 case "destroyed": return CTX(_("SCO^destroyed"));
37 case "drops": return CTX(_("SCO^drops"));
38 case "faults": return CTX(_("SCO^faults"));
39 case "fckills": return CTX(_("SCO^fckills"));
40 case "goals": return CTX(_("SCO^goals"));
41 case "kckills": return CTX(_("SCO^kckills"));
42 case "kdratio": return CTX(_("SCO^kdratio"));
43 case "k/d": return CTX(_("SCO^k/d"));
44 case "kd": return CTX(_("SCO^kd"));
45 case "kdr": return CTX(_("SCO^kdr"));
46 case "kills": return CTX(_("SCO^kills"));
47 case "laps": return CTX(_("SCO^laps"));
48 case "lives": return CTX(_("SCO^lives"));
49 case "losses": return CTX(_("SCO^losses"));
50 case "name": return CTX(_("SCO^name"));
51 case "sum": return CTX(_("SCO^sum"));
52 case "nick": return CTX(_("SCO^nick"));
53 case "objectives": return CTX(_("SCO^objectives"));
54 case "pickups": return CTX(_("SCO^pickups"));
55 case "ping": return CTX(_("SCO^ping"));
56 case "pl": return CTX(_("SCO^pl"));
57 case "pushes": return CTX(_("SCO^pushes"));
58 case "rank": return CTX(_("SCO^rank"));
59 case "returns": return CTX(_("SCO^returns"));
60 case "revivals": return CTX(_("SCO^revivals"));
61 case "score": return CTX(_("SCO^score"));
62 case "suicides": return CTX(_("SCO^suicides"));
63 case "takes": return CTX(_("SCO^takes"));
64 case "ticks": return CTX(_("SCO^ticks"));
73 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
74 for(i = 0; i < MAX_SCORE; ++i)
76 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
77 if(f == SFL_SORT_PRIO_PRIMARY)
79 if(f == SFL_SORT_PRIO_SECONDARY)
82 if(ps_secondary == -1)
83 ps_secondary = ps_primary;
85 for(i = 0; i < MAX_TEAMSCORE; ++i)
87 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
88 if(f == SFL_SORT_PRIO_PRIMARY)
90 if(f == SFL_SORT_PRIO_SECONDARY)
93 if(ts_secondary == -1)
94 ts_secondary = ts_primary;
99 float SetTeam(entity pl, float Team);
101 void HUD_UpdatePlayerTeams()
108 for(pl = players.sort_next; pl; pl = pl.sort_next)
111 Team = GetPlayerColor(pl.sv_entnum);
112 if(SetTeam(pl, Team))
115 HUD_UpdatePlayerPos(pl);
119 pl = players.sort_next;
124 print(strcat("PNUM: ", ftos(num), "\n"));
129 int HUD_CompareScore(float vl, float vr, int f)
131 if(f & SFL_ZERO_IS_WORST)
133 if(vl == 0 && vr != 0)
135 if(vl != 0 && vr == 0)
139 return IS_INCREASING(f);
141 return IS_DECREASING(f);
145 float HUD_ComparePlayerScores(entity left, entity right)
148 vl = GetPlayerColor(left.sv_entnum);
149 vr = GetPlayerColor(right.sv_entnum);
161 if(vl == NUM_SPECTATOR)
163 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
165 if(!left.gotscores && right.gotscores)
170 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
174 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
179 for(i = 0; i < MAX_SCORE; ++i)
181 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
186 if (left.sv_entnum < right.sv_entnum)
192 void HUD_UpdatePlayerPos(entity player)
194 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
196 SORT_SWAP(player, other);
198 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
200 SORT_SWAP(other, player);
204 float HUD_CompareTeamScores(entity left, entity right)
208 if(left.team == NUM_SPECTATOR)
210 if(right.team == NUM_SPECTATOR)
213 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
217 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
221 for(i = 0; i < MAX_SCORE; ++i)
223 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
228 if (left.team < right.team)
234 void HUD_UpdateTeamPos(entity Team)
236 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
238 SORT_SWAP(Team, other);
240 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
242 SORT_SWAP(other, Team);
248 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
249 LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
250 LOG_INFO(_("Usage:\n"));
251 LOG_INFO(_("^2scoreboard_columns_set default\n"));
252 LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
253 LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
254 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
256 LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n"));
257 LOG_INFO(_("^3ping^7 Ping time\n"));
258 LOG_INFO(_("^3pl^7 Packet loss\n"));
259 LOG_INFO(_("^3kills^7 Number of kills\n"));
260 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
261 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
262 LOG_INFO(_("^3frags^7 kills - suicides\n"));
263 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
264 LOG_INFO(_("^3sum^7 frags - deaths\n"));
265 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
266 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
267 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
268 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
269 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
270 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
271 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
272 LOG_INFO(_("^3rank^7 Player rank\n"));
273 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
274 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
275 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
276 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
277 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
278 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
279 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
280 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
281 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
282 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
283 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
284 LOG_INFO(_("^3score^7 Total score\n\n"));
286 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
287 "of game types, then a slash, to make the field show up only in these\n"
288 "or in all but these game types. You can also specify 'all' as a\n"
289 "field to show all fields available for the current game mode.\n\n"));
291 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
292 "include/exclude ALL teams/noteams game modes.\n\n"));
294 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
295 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
296 "right of the vertical bar aligned to the right.\n"));
297 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
298 "other gamemodes except DM.\n"));
301 #define HUD_DefaultColumnLayout() \
303 "-teams,rc,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,rc,ka/suicides +ft,tdm/suicides -rc,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
304 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \
305 "+lms/lives +lms/rank " \
306 "+kh/caps +kh/pushes +kh/destroyed " \
307 "?+rc/laps ?+rc/time ?+rc/fastest " \
308 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
311 void Cmd_HUD_SetFields(float argc)
315 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
320 // set up a temporary scoreboard layout
321 // no layout can be properly set up until score_info data haven't been received
322 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
324 scores_label[ps_primary] = strzone("score");
325 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
328 // TODO: re enable with gametype dependant cvars?
329 if(argc < 3) // no arguments provided
330 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
333 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
337 if(argv(2) == "default")
338 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
339 else if(argv(2) == "all")
342 s = "ping pl name |";
343 for(i = 0; i < MAX_SCORE; ++i)
346 if(i != ps_secondary)
347 if(scores_label[i] != "")
348 s = strcat(s, " ", scores_label[i]);
350 if(ps_secondary != ps_primary)
351 s = strcat(s, " ", scores_label[ps_secondary]);
352 s = strcat(s, " ", scores_label[ps_primary]);
353 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
360 hud_fontsize = HUD_GetFontsize("hud_fontsize");
362 for(i = 1; i < argc - 1; ++i)
368 if(substring(str, 0, 1) == "?")
371 str = substring(str, 1, strlen(str) - 1);
374 slash = strstrofs(str, "/", 0);
377 pattern = substring(str, 0, slash);
378 str = substring(str, slash + 1, strlen(str) - (slash + 1));
380 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
384 strunzone(hud_title[hud_num_fields]);
385 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
386 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
387 str = strtolower(str);
391 case "ping": hud_field[hud_num_fields] = SP_PING; break;
392 case "pl": hud_field[hud_num_fields] = SP_PL; break;
393 case "pl": hud_field[hud_num_fields] = SP_PL; break;
394 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
395 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
396 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
397 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
400 for(j = 0; j < MAX_SCORE; ++j)
401 if(str == strtolower(scores_label[j]))
402 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
410 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
414 hud_field[hud_num_fields] = j;
417 if(j == ps_secondary)
423 if(hud_num_fields >= MAX_HUD_FIELDS)
427 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
429 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
431 if(ps_primary == ps_secondary)
433 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
435 if(hud_num_fields+missing < MAX_HUD_FIELDS)
439 strunzone(hud_title[hud_num_fields]);
440 for(i = hud_num_fields; i > 0; --i)
442 hud_title[i] = hud_title[i-1];
443 hud_size[i] = hud_size[i-1];
444 hud_field[i] = hud_field[i-1];
446 hud_title[0] = strzone(TranslateScoresLabel("name"));
447 hud_field[0] = SP_NAME;
449 LOG_INFO("fixed missing field 'name'\n");
453 strunzone(hud_title[hud_num_fields]);
454 for(i = hud_num_fields; i > 1; --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[1] = strzone("|");
461 hud_field[1] = SP_SEPARATOR;
462 hud_size[1] = stringwidth("|", false, hud_fontsize);
464 LOG_INFO("fixed missing field '|'\n");
467 else if(!have_separator)
469 strunzone(hud_title[hud_num_fields]);
470 hud_title[hud_num_fields] = strzone("|");
471 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
472 hud_field[hud_num_fields] = SP_SEPARATOR;
474 LOG_INFO("fixed missing field '|'\n");
478 strunzone(hud_title[hud_num_fields]);
479 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
480 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
481 hud_field[hud_num_fields] = ps_secondary;
483 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_secondary]);
487 strunzone(hud_title[hud_num_fields]);
488 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
489 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
490 hud_field[hud_num_fields] = ps_primary;
492 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_primary]);
496 hud_field[hud_num_fields] = SP_END;
500 vector hud_field_rgb;
501 string hud_field_icon0;
502 string hud_field_icon1;
503 string hud_field_icon2;
504 vector hud_field_icon0_rgb;
505 vector hud_field_icon1_rgb;
506 vector hud_field_icon2_rgb;
507 float hud_field_icon0_alpha;
508 float hud_field_icon1_alpha;
509 float hud_field_icon2_alpha;
510 string HUD_GetField(entity pl, int field)
512 float tmp, num, denom;
515 hud_field_rgb = '1 1 1';
516 hud_field_icon0 = "";
517 hud_field_icon1 = "";
518 hud_field_icon2 = "";
519 hud_field_icon0_rgb = '1 1 1';
520 hud_field_icon1_rgb = '1 1 1';
521 hud_field_icon2_rgb = '1 1 1';
522 hud_field_icon0_alpha = 1;
523 hud_field_icon1_alpha = 1;
524 hud_field_icon2_alpha = 1;
529 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
530 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
534 tmp = max(0, min(220, f-80)) / 220;
535 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
541 f = pl.ping_packetloss;
542 tmp = pl.ping_movementloss;
543 if(f == 0 && tmp == 0)
545 str = ftos(ceil(f * 100));
547 str = strcat(str, "~", ftos(ceil(tmp * 100)));
548 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
549 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
553 if(ready_waiting && pl.ready)
555 hud_field_icon0 = "gfx/scoreboard/player_ready";
559 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
561 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
562 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
563 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
564 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
565 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
568 return GetPlayerName(pl.sv_entnum);
571 f = pl.(scores[SP_KILLS]);
572 f -= pl.(scores[SP_SUICIDES]);
576 num = pl.(scores[SP_KILLS]);
577 denom = pl.(scores[SP_DEATHS]);
580 hud_field_rgb = '0 1 0';
581 str = sprintf("%d", num);
582 } else if(num <= 0) {
583 hud_field_rgb = '1 0 0';
584 str = sprintf("%.1f", num/denom);
586 str = sprintf("%.1f", num/denom);
590 f = pl.(scores[SP_KILLS]);
591 f -= pl.(scores[SP_DEATHS]);
594 hud_field_rgb = '0 1 0';
596 hud_field_rgb = '1 1 1';
598 hud_field_rgb = '1 0 0';
603 tmp = pl.(scores[field]);
604 f = scores_flags[field];
605 if(field == ps_primary)
606 hud_field_rgb = '1 1 0';
607 else if(field == ps_secondary)
608 hud_field_rgb = '0 1 1';
610 hud_field_rgb = '1 1 1';
611 return ScoreString(f, tmp);
616 float hud_fixscoreboardcolumnwidth_len;
617 float hud_fixscoreboardcolumnwidth_iconlen;
618 float hud_fixscoreboardcolumnwidth_marginlen;
620 string HUD_FixScoreboardColumnWidth(int i, string str)
624 field = hud_field[i];
626 hud_fixscoreboardcolumnwidth_iconlen = 0;
628 if(hud_field_icon0 != "")
630 sz = draw_getimagesize(hud_field_icon0);
632 if(hud_fixscoreboardcolumnwidth_iconlen < f)
633 hud_fixscoreboardcolumnwidth_iconlen = f;
636 if(hud_field_icon1 != "")
638 sz = draw_getimagesize(hud_field_icon1);
640 if(hud_fixscoreboardcolumnwidth_iconlen < f)
641 hud_fixscoreboardcolumnwidth_iconlen = f;
644 if(hud_field_icon2 != "")
646 sz = draw_getimagesize(hud_field_icon2);
648 if(hud_fixscoreboardcolumnwidth_iconlen < f)
649 hud_fixscoreboardcolumnwidth_iconlen = f;
652 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
654 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
655 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
657 hud_fixscoreboardcolumnwidth_marginlen = 0;
659 if(field == SP_NAME) // name gets all remaining space
663 namesize = sbwidth;// / hud_fontsize_x;
664 for(j = 0; j < hud_num_fields; ++j)
666 if (hud_field[i] != SP_SEPARATOR)
667 namesize -= hud_size[j] + hud_fontsize.x;
668 namesize += hud_fontsize.x;
669 hud_size[i] = namesize;
671 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
672 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
673 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
674 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
677 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
679 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
686 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
689 rgb = Team_ColorRGB(pl.team);
693 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
695 if((rgb == '1 1 1') && (!is_spec)) {
696 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
697 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
698 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
700 vector h_pos = pos - '1 1 0';
701 vector h_size = item_size + '2 0 0';
702 // alternated rows highlighting
704 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
705 else if((scoreboard_highlight) && (!(pl_number % 2)))
706 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
712 for(i = 0; i < hud_num_fields; ++i)
714 field = hud_field[i];
715 if(field == SP_SEPARATOR)
718 if(is_spec && field != SP_NAME && field != SP_PING) {
719 pos.x += hud_size[i] + hud_fontsize.x;
722 str = HUD_GetField(pl, field);
723 str = HUD_FixScoreboardColumnWidth(i, str);
725 pos.x += hud_size[i] + hud_fontsize.x;
727 if(field == SP_NAME) {
728 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
730 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
732 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
734 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
736 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
738 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
741 tmp.x = hud_size[i] + hud_fontsize.x;
742 if(hud_field_icon0 != "")
744 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);
746 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);
747 if(hud_field_icon1 != "")
749 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);
751 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);
752 if(hud_field_icon2 != "")
754 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);
756 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);
759 if(hud_field[i] == SP_SEPARATOR)
762 for(i = hud_num_fields-1; i > 0; --i)
764 field = hud_field[i];
765 if(field == SP_SEPARATOR)
768 if(is_spec && field != SP_NAME && field != SP_PING) {
769 pos.x -= hud_size[i] + hud_fontsize.x;
773 str = HUD_GetField(pl, field);
774 str = HUD_FixScoreboardColumnWidth(i, str);
776 if(field == SP_NAME) {
777 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
779 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
781 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
783 tmp.x = hud_fixscoreboardcolumnwidth_len;
785 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
787 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
791 if(hud_field_icon0 != "")
793 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);
795 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);
796 if(hud_field_icon1 != "")
798 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);
800 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);
801 if(hud_field_icon2 != "")
803 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);
805 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);
806 pos.x -= hud_size[i] + hud_fontsize.x;
811 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
815 * HUD_Scoreboard_MakeTable
817 * Makes a table for a team (for all playing players in DM) and fills it
820 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
822 float body_table_height;
823 vector tmp = '0 0 0', column_dim = '0 0 0';
826 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
828 pos.y += autocvar_scoreboard_border_thickness;
832 tmp.y = 1.25 * hud_fontsize.y;
836 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
838 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
841 tmp.y += autocvar_scoreboard_border_thickness;
842 tmp.y += body_table_height;
843 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
845 // separator header/table
846 pos.y += 1.25 * hud_fontsize.y;
847 tmp.y = autocvar_scoreboard_border_thickness;
848 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
850 pos.y += autocvar_scoreboard_border_thickness;
853 tmp.y = body_table_height;
855 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
857 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
859 // anyway, apply some color
860 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
862 // go back to the top to make alternated columns highlighting and to print the strings
863 pos.y -= 1.25 * hud_fontsize.y;
864 pos.y -= autocvar_scoreboard_border_thickness;
868 if (scoreboard_highlight)
870 column_dim.y = 1.25 * hud_fontsize.y; // header
871 column_dim.y += autocvar_scoreboard_border_thickness;
872 column_dim.y += body_table_height;
875 // print the strings of the columns headers and draw the columns
877 for(i = 0; i < hud_num_fields; ++i)
879 if(hud_field[i] == SP_SEPARATOR)
881 column_dim.x = hud_size[i] + hud_fontsize.x;
882 if (scoreboard_highlight)
885 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
887 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
888 pos.x += column_dim.x;
890 if(hud_field[i] == SP_SEPARATOR)
894 for(i = hud_num_fields-1; i > 0; --i)
896 if(hud_field[i] == SP_SEPARATOR)
899 pos.x -= hud_size[i];
901 if (scoreboard_highlight)
905 if (i == hud_num_fields-1)
906 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
908 column_dim.x = hud_size[i] + hud_fontsize.x;
909 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
913 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
914 tmp.x = (hud_size[i] - tmp.x);
915 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
916 pos.x -= hud_fontsize.x;
921 pos.y += 1.25 * hud_fontsize.y; // skip the header
922 pos.y += autocvar_scoreboard_border_thickness;
926 tmp.y = hud_fontsize.y * 1.25;
928 // fill the table and draw the rows
931 for(pl = players.sort_next; pl; pl = pl.sort_next)
933 if(pl.team != tm.team)
935 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
936 pos.y += 1.25 * hud_fontsize.y;
940 for(pl = players.sort_next; pl; pl = pl.sort_next)
942 if(pl.team == NUM_SPECTATOR)
944 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
945 pos.y += 1.25 * hud_fontsize.y;
950 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
951 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
956 float HUD_WouldDrawScoreboard() {
957 if (autocvar__hud_configure)
959 else if (QuickMenu_IsOpened())
961 else if (HUD_Radar_Clickable())
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 && !active_minigame)
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 setself(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;
1014 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1015 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1016 vector tmp = '0 0 0';
1018 tmp.y = height * rows;
1021 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1023 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1024 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1026 // column highlighting
1027 for(i = 0; i < columnns; ++i)
1030 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);
1034 for(i = 0; i < rows; ++i)
1036 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);
1039 average_accuracy = 0;
1040 int weapons_with_stats = 0;
1042 pos.x += weapon_width / 2;
1044 if(autocvar_scoreboard_accuracy_nocolors)
1047 Accuracy_LoadColors();
1049 float oldposx = pos.x;
1053 for(i = WEP_FIRST, column = 0; i <= WEP_LAST; ++i)
1055 setself(get_weaponinfo(i));
1058 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1060 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1064 if(weapon_stats >= 0)
1065 weapon_alpha = scoreboard_alpha_fg;
1067 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1070 drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1072 if(weapon_stats >= 0) {
1073 weapons_with_stats += 1;
1074 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1077 s = sprintf("%d%%", weapon_stats*100);
1080 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1082 if(!autocvar_scoreboard_accuracy_nocolors)
1083 rgb = Accuracy_GetColor(weapon_stats);
1085 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1087 tmpos.x += weapon_width * rows;
1088 pos.x += weapon_width * rows;
1089 if(rows == 2 && column == columnns - 1) {
1097 if(weapons_with_stats)
1098 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1101 pos.y += 1.25 * hud_fontsize.y;
1102 pos.x = initial_posx;
1106 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1108 pos.x += hud_fontsize.x * 0.25;
1109 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1110 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1111 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1113 pos.y+= hud_fontsize.y;
1118 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1119 float stat_secrets_found, stat_secrets_total;
1120 float stat_monsters_killed, stat_monsters_total;
1124 // get monster stats
1125 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1126 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1128 // get secrets stats
1129 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1130 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1132 // get number of rows
1133 if(stat_secrets_total)
1135 if(stat_monsters_total)
1138 // if no rows, return
1142 // draw table header
1143 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1144 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1147 vector tmp = '0 0 0';
1149 tmp.y = hud_fontsize.y * rows;
1152 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1154 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1155 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1158 if(stat_monsters_total)
1160 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1161 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1165 if(stat_secrets_total)
1167 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1168 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1172 pos.y += 1.25 * hud_fontsize.y;
1177 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1180 RANKINGS_RECEIVED_CNT = 0;
1181 for (i=RANKINGS_CNT-1; i>=0; --i)
1183 ++RANKINGS_RECEIVED_CNT;
1185 if (RANKINGS_RECEIVED_CNT == 0)
1189 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1191 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1192 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1193 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1195 pos.y += hud_fontsize.y;
1196 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1197 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1198 vector tmp = '0 0 0';
1200 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1203 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1205 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1206 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1209 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1216 n = grecordholder[i];
1217 p = count_ordinal(i+1);
1218 if(grecordholder[i] == GetPlayerName(player_localnum))
1219 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1220 else if(!(i % 2) && scoreboard_highlight)
1221 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1222 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1223 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);
1224 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1225 pos.y += 1.25 * hud_fontsize.y;
1227 pos.y += autocvar_scoreboard_border_thickness;
1232 float hud_woulddrawscoreboard_prev;
1233 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1234 void HUD_DrawScoreboard()
1236 float hud_woulddrawscoreboard;
1237 hud_woulddrawscoreboard = scoreboard_active;
1238 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1239 hud_woulddrawscoreboard_change = time;
1240 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1243 if(hud_woulddrawscoreboard) {
1244 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1245 if (scoreboard_fadeinspeed)
1246 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1248 scoreboard_fade_alpha = 1;
1251 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1252 if (scoreboard_fadeoutspeed)
1253 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1255 scoreboard_fade_alpha = 0;
1258 if (!scoreboard_fade_alpha)
1261 HUD_UpdatePlayerTeams();
1263 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1264 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1265 scoreboard_highlight = autocvar_scoreboard_highlight;
1266 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1267 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1268 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1269 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1271 vector rgb, pos, tmp;
1275 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1276 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1278 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1279 ymax = (vid_conheight - ymin);
1281 sbwidth = xmax - xmin;
1283 // Initializes position
1289 vector sb_heading_fontsize;
1290 sb_heading_fontsize = hud_fontsize * 2;
1291 draw_beginBoldFont();
1292 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1295 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1297 // Draw the scoreboard
1298 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1302 vector team_score_baseoffset;
1303 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1304 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1306 if(tm.team == NUM_SPECTATOR)
1309 draw_beginBoldFont();
1310 rgb = Team_ColorRGB(tm.team);
1311 str = ftos(tm.(teamscores[ts_primary]));
1312 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1314 if(ts_primary != ts_secondary)
1316 str = ftos(tm.(teamscores[ts_secondary]));
1317 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);
1321 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1323 rgb.x = autocvar_scoreboard_color_bg_r;
1324 rgb.y = autocvar_scoreboard_color_bg_g;
1325 rgb.z = autocvar_scoreboard_color_bg_b;
1329 rgb.x = autocvar_scoreboard_color_bg_r;
1330 rgb.y = autocvar_scoreboard_color_bg_g;
1331 rgb.z = autocvar_scoreboard_color_bg_b;
1333 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1335 if(tm.team == NUM_SPECTATOR)
1338 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1342 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1343 if(race_speedaward) {
1344 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1345 pos.y += 1.25 * hud_fontsize.y;
1347 if(race_speedaward_alltimebest) {
1348 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1349 pos.y += 1.25 * hud_fontsize.y;
1351 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1353 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1355 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1357 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1362 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1364 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1371 item_size.x = sbwidth;
1372 item_size.y = hud_fontsize.y * 1.25;
1374 for(pl = players.sort_next; pl; pl = pl.sort_next)
1376 if(pl.team != NUM_SPECTATOR)
1378 pos.y += 1.25 * hud_fontsize.y;
1379 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1385 draw_beginBoldFont();
1386 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1388 pos.y += 1.25 * hud_fontsize.y;
1391 // Print info string
1393 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1394 tl = getstatf(STAT_TIMELIMIT);
1395 fl = getstatf(STAT_FRAGLIMIT);
1396 ll = getstatf(STAT_LEADLIMIT);
1397 if(gametype == MAPINFO_TYPE_LMS)
1400 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1405 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1409 str = strcat(str, _(" or"));
1412 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1413 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1414 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1415 TranslateScoresLabel(teamscores_label[ts_primary])));
1419 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1420 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1421 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1422 TranslateScoresLabel(scores_label[ps_primary])));
1427 if(tl > 0 || fl > 0)
1428 str = strcat(str, _(" or"));
1431 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1432 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1433 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1434 TranslateScoresLabel(teamscores_label[ts_primary])));
1438 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1439 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1440 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1441 TranslateScoresLabel(scores_label[ps_primary])));
1446 pos.y += 1.2 * hud_fontsize.y;
1447 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1449 // print information about respawn status
1450 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1454 if(respawn_time < 0)
1456 // a negative number means we are awaiting respawn, time value is still the same
1457 respawn_time *= -1; // remove mark now that we checked it
1458 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1460 str = sprintf(_("^1Respawning in ^3%s^1..."),
1461 (autocvar_scoreboard_respawntime_decimals ?
1462 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1464 count_seconds(respawn_time - time)
1468 else if(time < respawn_time)
1470 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1471 (autocvar_scoreboard_respawntime_decimals ?
1472 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1474 count_seconds(respawn_time - time)
1478 else if(time >= respawn_time)
1479 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1481 pos.y += 1.2 * hud_fontsize.y;
1482 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1485 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;