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 (HUD_Radar_Clickable())
961 else if (scoreboard_showscores)
963 else if (intermission == 1)
965 else if (intermission == 2)
967 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
969 else if (scoreboard_showscores_force)
974 float average_accuracy;
975 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
977 WepSet weapons_stat = WepSet_GetFromStat();
978 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
979 float initial_posx = pos.x;
983 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
985 self = get_weaponinfo(i);
989 weapon_stats = weapon_accuracy[i-WEP_FIRST];
991 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
995 int weapon_cnt = WEP_COUNT - disownedcnt;
1001 if(autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor(WEP_COUNT * 0.5))
1005 int columnns = ceil(weapon_cnt / rows);
1008 float fontsize = height * 1/3;
1009 float weapon_height = height * 2/3;
1010 float weapon_width = sbwidth / columnns / rows;
1012 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1013 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1014 vector tmp = '0 0 0';
1016 tmp.y = height * rows;
1019 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1021 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1022 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1024 // column highlighting
1025 for(i = 0; i < columnns; ++i)
1028 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);
1032 for(i = 0; i < rows; ++i)
1034 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);
1037 average_accuracy = 0;
1038 int weapons_with_stats = 0;
1040 pos.x += weapon_width / 2;
1042 if(autocvar_scoreboard_accuracy_nocolors)
1045 Accuracy_LoadColors();
1047 float oldposx = pos.x;
1051 for(i = WEP_FIRST, column = 0; i <= WEP_LAST; ++i)
1053 self = get_weaponinfo(i);
1056 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1058 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1062 if(weapon_stats >= 0)
1063 weapon_alpha = scoreboard_alpha_fg;
1065 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1068 drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1070 if(weapon_stats >= 0) {
1071 weapons_with_stats += 1;
1072 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1075 s = sprintf("%d%%", weapon_stats*100);
1078 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1080 if(!autocvar_scoreboard_accuracy_nocolors)
1081 rgb = Accuracy_GetColor(weapon_stats);
1083 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1085 tmpos.x += weapon_width * rows;
1086 pos.x += weapon_width * rows;
1087 if(rows == 2 && column == columnns - 1) {
1095 if(weapons_with_stats)
1096 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1099 pos.y += 1.25 * hud_fontsize.y;
1100 pos.x = initial_posx;
1104 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1106 pos.x += hud_fontsize.x * 0.25;
1107 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1108 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1109 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1111 pos.y+= hud_fontsize.y;
1116 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1117 float stat_secrets_found, stat_secrets_total;
1118 float stat_monsters_killed, stat_monsters_total;
1122 // get monster stats
1123 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1124 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1126 // get secrets stats
1127 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1128 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1130 // get number of rows
1131 if(stat_secrets_total)
1133 if(stat_monsters_total)
1136 // if no rows, return
1140 // draw table header
1141 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1142 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1145 vector tmp = '0 0 0';
1147 tmp.y = hud_fontsize.y * rows;
1150 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1152 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1153 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1156 if(stat_monsters_total)
1158 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1159 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1163 if(stat_secrets_total)
1165 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1166 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1170 pos.y += 1.25 * hud_fontsize.y;
1175 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1178 RANKINGS_RECEIVED_CNT = 0;
1179 for (i=RANKINGS_CNT-1; i>=0; --i)
1181 ++RANKINGS_RECEIVED_CNT;
1183 if (RANKINGS_RECEIVED_CNT == 0)
1187 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1189 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1190 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1191 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1193 pos.y += hud_fontsize.y;
1194 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1195 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1196 vector tmp = '0 0 0';
1198 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1201 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1203 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1204 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1207 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1214 n = grecordholder[i];
1215 p = count_ordinal(i+1);
1216 if(grecordholder[i] == GetPlayerName(player_localnum))
1217 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1218 else if(!(i % 2) && scoreboard_highlight)
1219 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1220 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1221 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);
1222 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1223 pos.y += 1.25 * hud_fontsize.y;
1225 pos.y += autocvar_scoreboard_border_thickness;
1230 float hud_woulddrawscoreboard_prev;
1231 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1232 void HUD_DrawScoreboard()
1234 float hud_woulddrawscoreboard;
1235 hud_woulddrawscoreboard = scoreboard_active;
1236 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1237 hud_woulddrawscoreboard_change = time;
1238 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1241 if(hud_woulddrawscoreboard) {
1242 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1243 if (scoreboard_fadeinspeed)
1244 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1246 scoreboard_fade_alpha = 1;
1249 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1250 if (scoreboard_fadeoutspeed)
1251 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1253 scoreboard_fade_alpha = 0;
1256 if (!scoreboard_fade_alpha)
1259 HUD_UpdatePlayerTeams();
1261 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1262 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1263 scoreboard_highlight = autocvar_scoreboard_highlight;
1264 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1265 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1266 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1267 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1269 vector rgb, pos, tmp;
1273 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1274 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1276 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1277 ymax = (vid_conheight - ymin);
1279 sbwidth = xmax - xmin;
1281 // Initializes position
1287 vector sb_heading_fontsize;
1288 sb_heading_fontsize = hud_fontsize * 2;
1289 draw_beginBoldFont();
1290 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1293 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1295 // Draw the scoreboard
1296 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1300 vector team_score_baseoffset;
1301 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1302 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1304 if(tm.team == NUM_SPECTATOR)
1307 draw_beginBoldFont();
1308 rgb = Team_ColorRGB(tm.team);
1309 str = ftos(tm.(teamscores[ts_primary]));
1310 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1312 if(ts_primary != ts_secondary)
1314 str = ftos(tm.(teamscores[ts_secondary]));
1315 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);
1319 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1321 rgb.x = autocvar_scoreboard_color_bg_r;
1322 rgb.y = autocvar_scoreboard_color_bg_g;
1323 rgb.z = autocvar_scoreboard_color_bg_b;
1327 rgb.x = autocvar_scoreboard_color_bg_r;
1328 rgb.y = autocvar_scoreboard_color_bg_g;
1329 rgb.z = autocvar_scoreboard_color_bg_b;
1331 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1333 if(tm.team == NUM_SPECTATOR)
1336 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1340 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1341 if(race_speedaward) {
1342 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1343 pos.y += 1.25 * hud_fontsize.y;
1345 if(race_speedaward_alltimebest) {
1346 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1347 pos.y += 1.25 * hud_fontsize.y;
1349 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1351 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1353 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1355 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1360 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1362 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1369 item_size.x = sbwidth;
1370 item_size.y = hud_fontsize.y * 1.25;
1372 for(pl = players.sort_next; pl; pl = pl.sort_next)
1374 if(pl.team != NUM_SPECTATOR)
1376 pos.y += 1.25 * hud_fontsize.y;
1377 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1383 draw_beginBoldFont();
1384 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1386 pos.y += 1.25 * hud_fontsize.y;
1389 // Print info string
1391 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1392 tl = getstatf(STAT_TIMELIMIT);
1393 fl = getstatf(STAT_FRAGLIMIT);
1394 ll = getstatf(STAT_LEADLIMIT);
1395 if(gametype == MAPINFO_TYPE_LMS)
1398 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1403 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1407 str = strcat(str, _(" or"));
1410 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1411 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1412 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1413 TranslateScoresLabel(teamscores_label[ts_primary])));
1417 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1418 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1419 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1420 TranslateScoresLabel(scores_label[ps_primary])));
1425 if(tl > 0 || fl > 0)
1426 str = strcat(str, _(" or"));
1429 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1430 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1431 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1432 TranslateScoresLabel(teamscores_label[ts_primary])));
1436 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1437 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1438 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1439 TranslateScoresLabel(scores_label[ps_primary])));
1444 pos.y += 1.2 * hud_fontsize.y;
1445 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1447 // print information about respawn status
1448 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1452 if(respawn_time < 0)
1454 // a negative number means we are awaiting respawn, time value is still the same
1455 respawn_time *= -1; // remove mark now that we checked it
1456 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1458 str = sprintf(_("^1Respawning in ^3%s^1..."),
1459 (autocvar_scoreboard_respawntime_decimals ?
1460 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1462 count_seconds(respawn_time - time)
1466 else if(time < respawn_time)
1468 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1469 (autocvar_scoreboard_respawntime_decimals ?
1470 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1472 count_seconds(respawn_time - time)
1476 else if(time >= respawn_time)
1477 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1479 pos.y += 1.2 * hud_fontsize.y;
1480 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1483 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;