1 #include "scoreboard.qh"
3 #include "hud/panel/quickmenu.qh"
6 #include <common/ent_cs.qh>
7 #include <common/constants.qh>
8 #include <common/mapinfo.qh>
9 #include <common/minigames/cl_minigames.qh>
10 #include <common/stats.qh>
11 #include <common/teams.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 "dmg": return CTX(_("SCO^dmg"));
38 case "dmgtaken": return CTX(_("SCO^dmgtaken"));
39 case "drops": return CTX(_("SCO^drops"));
40 case "faults": return CTX(_("SCO^faults"));
41 case "fckills": return CTX(_("SCO^fckills"));
42 case "goals": return CTX(_("SCO^goals"));
43 case "kckills": return CTX(_("SCO^kckills"));
44 case "kdratio": return CTX(_("SCO^kdratio"));
45 case "k/d": return CTX(_("SCO^k/d"));
46 case "kd": return CTX(_("SCO^kd"));
47 case "kdr": return CTX(_("SCO^kdr"));
48 case "kills": return CTX(_("SCO^kills"));
49 case "laps": return CTX(_("SCO^laps"));
50 case "lives": return CTX(_("SCO^lives"));
51 case "losses": return CTX(_("SCO^losses"));
52 case "name": return CTX(_("SCO^name"));
53 case "sum": return CTX(_("SCO^sum"));
54 case "nick": return CTX(_("SCO^nick"));
55 case "objectives": return CTX(_("SCO^objectives"));
56 case "pickups": return CTX(_("SCO^pickups"));
57 case "ping": return CTX(_("SCO^ping"));
58 case "pl": return CTX(_("SCO^pl"));
59 case "pushes": return CTX(_("SCO^pushes"));
60 case "rank": return CTX(_("SCO^rank"));
61 case "returns": return CTX(_("SCO^returns"));
62 case "revivals": return CTX(_("SCO^revivals"));
63 case "score": return CTX(_("SCO^score"));
64 case "suicides": return CTX(_("SCO^suicides"));
65 case "takes": return CTX(_("SCO^takes"));
66 case "ticks": return CTX(_("SCO^ticks"));
75 ps_primary = ps_secondary = NULL;
76 ts_primary = ts_secondary = -1;
77 FOREACH(Scores, true, {
78 f = (scores_flags(it) & SFL_SORT_PRIO_MASK);
79 if(f == SFL_SORT_PRIO_PRIMARY)
81 if(f == SFL_SORT_PRIO_SECONDARY)
84 if(ps_secondary == NULL)
85 ps_secondary = ps_primary;
87 for(i = 0; i < MAX_TEAMSCORE; ++i)
89 f = (teamscores_flags(i) & SFL_SORT_PRIO_MASK);
90 if(f == SFL_SORT_PRIO_PRIMARY)
92 if(f == SFL_SORT_PRIO_SECONDARY)
95 if(ts_secondary == -1)
96 ts_secondary = ts_primary;
101 float SetTeam(entity pl, float Team);
103 void HUD_UpdatePlayerTeams()
110 for(pl = players.sort_next; pl; pl = pl.sort_next)
113 Team = entcs_GetScoreTeam(pl.sv_entnum);
114 if(SetTeam(pl, Team))
117 HUD_UpdatePlayerPos(pl);
121 pl = players.sort_next;
126 print(strcat("PNUM: ", ftos(num), "\n"));
131 int HUD_CompareScore(int vl, int vr, int f)
133 TC(int, vl); TC(int, vr); TC(int, f);
134 if(f & SFL_ZERO_IS_WORST)
136 if(vl == 0 && vr != 0)
138 if(vl != 0 && vr == 0)
142 return IS_INCREASING(f);
144 return IS_DECREASING(f);
148 float HUD_ComparePlayerScores(entity left, entity right)
151 vl = entcs_GetTeam(left.sv_entnum);
152 vr = entcs_GetTeam(right.sv_entnum);
164 if(vl == NUM_SPECTATOR)
166 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
168 if(!left.gotscores && right.gotscores)
173 r = HUD_CompareScore(left.scores(ps_primary), right.scores(ps_primary), scores_flags(ps_primary));
177 r = HUD_CompareScore(left.scores(ps_secondary), right.scores(ps_secondary), scores_flags(ps_secondary));
181 FOREACH(Scores, true, {
182 r = HUD_CompareScore(left.scores(it), right.scores(it), scores_flags(it));
183 if (r >= 0) return r;
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_TEAMSCORE; ++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(_("^3dmg^7 The total damage done\n"));
265 LOG_INFO(_("^3dmgtaken^7 The total damage taken\n"));
266 LOG_INFO(_("^3sum^7 frags - deaths\n"));
267 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
268 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
269 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
270 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
271 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
272 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
273 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
274 LOG_INFO(_("^3rank^7 Player rank\n"));
275 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
276 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
277 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
278 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
279 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
280 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
281 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
282 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
283 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
284 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
285 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
286 LOG_INFO(_("^3score^7 Total score\n\n"));
288 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
289 "of game types, then a slash, to make the field show up only in these\n"
290 "or in all but these game types. You can also specify 'all' as a\n"
291 "field to show all fields available for the current game mode.\n\n"));
293 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
294 "include/exclude ALL teams/noteams game modes.\n\n"));
296 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
297 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
298 "right of the vertical bar aligned to the right.\n"));
299 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
300 "other gamemodes except DM.\n"));
303 // NOTE: adding a gametype with ? to not warn for an optional field
304 // make sure it's excluded in a previous exclusive rule, if any
305 // otherwise the previous exclusive rule warns anyway
306 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
307 #define SCOREBOARD_DEFAULT_COLUMNS \
309 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
310 " -teams,lms/deaths +ft,tdm/deaths" \
311 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
312 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
313 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
314 " +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
315 " +lms/lives +lms/rank" \
316 " +kh/caps +kh/pushes +kh/destroyed" \
317 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
318 " +as/objectives +nb/faults +nb/goals" \
319 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
320 " -lms,rc,cts,inv,nb/score"
322 void Cmd_HUD_SetFields(int argc)
327 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
332 // set up a temporary scoreboard layout
333 // no layout can be properly set up until score_info data haven't been received
334 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
335 ps_primary = SP_SCORE;
336 scores_label(ps_primary) = strzone("score");
337 scores_flags(ps_primary) = SFL_ALLOW_HIDE;
340 // TODO: re enable with gametype dependant cvars?
341 if(argc < 3) // no arguments provided
342 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
345 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
349 if(argv(2) == "default")
350 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
351 else if(argv(2) == "all")
354 s = "ping pl name |";
355 FOREACH(Scores, true, {
357 if(it != ps_secondary)
358 if(scores_label(it) != "")
359 s = strcat(s, " ", scores_label(it));
361 if(ps_secondary != ps_primary)
362 s = strcat(s, " ", scores_label(ps_secondary));
363 s = strcat(s, " ", scores_label(ps_primary));
364 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
371 hud_fontsize = HUD_GetFontsize("hud_fontsize");
373 for(i = 1; i < argc - 1; ++i)
379 if(substring(str, 0, 1) == "?")
382 str = substring(str, 1, strlen(str) - 1);
385 slash = strstrofs(str, "/", 0);
388 pattern = substring(str, 0, slash);
389 str = substring(str, slash + 1, strlen(str) - (slash + 1));
391 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
395 strunzone(hud_title[hud_num_fields]);
396 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
397 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
398 str = strtolower(str);
403 case "ping": hud_field[hud_num_fields] = SP_PING; break;
404 case "pl": hud_field[hud_num_fields] = SP_PL; break;
405 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
406 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
407 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
408 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
409 case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
410 case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
413 FOREACH(Scores, true, {
414 if (str == strtolower(scores_label(it))) {
416 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
426 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
430 hud_field[hud_num_fields] = j;
433 if(j == ps_secondary)
439 if(hud_num_fields >= MAX_HUD_FIELDS)
443 if(scores_flags(ps_primary) & SFL_ALLOW_HIDE)
445 if(scores_flags(ps_secondary) & SFL_ALLOW_HIDE)
447 if(ps_primary == ps_secondary)
449 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
451 if(hud_num_fields+missing < MAX_HUD_FIELDS)
455 strunzone(hud_title[hud_num_fields]);
456 for(i = hud_num_fields; i > 0; --i)
458 hud_title[i] = hud_title[i-1];
459 hud_size[i] = hud_size[i-1];
460 hud_field[i] = hud_field[i-1];
462 hud_title[0] = strzone(TranslateScoresLabel("name"));
463 hud_field[0] = SP_NAME;
465 LOG_INFO("fixed missing field 'name'\n");
469 strunzone(hud_title[hud_num_fields]);
470 for(i = hud_num_fields; i > 1; --i)
472 hud_title[i] = hud_title[i-1];
473 hud_size[i] = hud_size[i-1];
474 hud_field[i] = hud_field[i-1];
476 hud_title[1] = strzone("|");
477 hud_field[1] = SP_SEPARATOR;
478 hud_size[1] = stringwidth("|", false, hud_fontsize);
480 LOG_INFO("fixed missing field '|'\n");
483 else if(!have_separator)
485 strunzone(hud_title[hud_num_fields]);
486 hud_title[hud_num_fields] = strzone("|");
487 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
488 hud_field[hud_num_fields] = SP_SEPARATOR;
490 LOG_INFO("fixed missing field '|'\n");
494 strunzone(hud_title[hud_num_fields]);
495 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_secondary)));
496 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
497 hud_field[hud_num_fields] = ps_secondary;
499 LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_secondary));
503 strunzone(hud_title[hud_num_fields]);
504 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_primary)));
505 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
506 hud_field[hud_num_fields] = ps_primary;
508 LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_primary));
512 hud_field[hud_num_fields] = SP_END;
516 vector hud_field_rgb;
517 string hud_field_icon0;
518 string hud_field_icon1;
519 string hud_field_icon2;
520 vector hud_field_icon0_rgb;
521 vector hud_field_icon1_rgb;
522 vector hud_field_icon2_rgb;
523 float hud_field_icon0_alpha;
524 float hud_field_icon1_alpha;
525 float hud_field_icon2_alpha;
526 string HUD_GetField(entity pl, PlayerScoreField field)
528 float tmp, num, denom;
531 hud_field_rgb = '1 1 1';
532 hud_field_icon0 = "";
533 hud_field_icon1 = "";
534 hud_field_icon2 = "";
535 hud_field_icon0_rgb = '1 1 1';
536 hud_field_icon1_rgb = '1 1 1';
537 hud_field_icon2_rgb = '1 1 1';
538 hud_field_icon0_alpha = 1;
539 hud_field_icon1_alpha = 1;
540 hud_field_icon2_alpha = 1;
545 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
546 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
550 tmp = max(0, min(220, f-80)) / 220;
551 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
557 f = pl.ping_packetloss;
558 tmp = pl.ping_movementloss;
559 if(f == 0 && tmp == 0)
561 str = ftos(ceil(f * 100));
563 str = strcat(str, "~", ftos(ceil(tmp * 100)));
564 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
565 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
569 if(ready_waiting && pl.ready)
571 hud_field_icon0 = "gfx/scoreboard/player_ready";
575 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
577 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
578 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
579 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
580 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
581 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
584 return entcs_GetName(pl.sv_entnum);
587 f = pl.(scores(SP_KILLS));
588 f -= pl.(scores(SP_SUICIDES));
592 num = pl.(scores(SP_KILLS));
593 denom = pl.(scores(SP_DEATHS));
596 hud_field_rgb = '0 1 0';
597 str = sprintf("%d", num);
598 } else if(num <= 0) {
599 hud_field_rgb = '1 0 0';
600 str = sprintf("%.1f", num/denom);
602 str = sprintf("%.1f", num/denom);
606 f = pl.(scores(SP_KILLS));
607 f -= pl.(scores(SP_DEATHS));
610 hud_field_rgb = '0 1 0';
612 hud_field_rgb = '1 1 1';
614 hud_field_rgb = '1 0 0';
619 num = pl.(scores(SP_DMG));
622 str = sprintf("%.1f k", num/denom);
626 num = pl.(scores(SP_DMGTAKEN));
629 str = sprintf("%.1f k", num/denom);
633 tmp = pl.(scores(field));
634 f = scores_flags(field);
635 if(field == ps_primary)
636 hud_field_rgb = '1 1 0';
637 else if(field == ps_secondary)
638 hud_field_rgb = '0 1 1';
640 hud_field_rgb = '1 1 1';
641 return ScoreString(f, tmp);
646 float hud_fixscoreboardcolumnwidth_len;
647 float hud_fixscoreboardcolumnwidth_iconlen;
648 float hud_fixscoreboardcolumnwidth_marginlen;
650 string HUD_FixScoreboardColumnWidth(int i, string str)
655 PlayerScoreField field = hud_field[i];
657 hud_fixscoreboardcolumnwidth_iconlen = 0;
659 if(hud_field_icon0 != "")
661 sz = draw_getimagesize(hud_field_icon0);
663 if(hud_fixscoreboardcolumnwidth_iconlen < f)
664 hud_fixscoreboardcolumnwidth_iconlen = f;
667 if(hud_field_icon1 != "")
669 sz = draw_getimagesize(hud_field_icon1);
671 if(hud_fixscoreboardcolumnwidth_iconlen < f)
672 hud_fixscoreboardcolumnwidth_iconlen = f;
675 if(hud_field_icon2 != "")
677 sz = draw_getimagesize(hud_field_icon2);
679 if(hud_fixscoreboardcolumnwidth_iconlen < f)
680 hud_fixscoreboardcolumnwidth_iconlen = f;
683 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
685 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
686 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
688 hud_fixscoreboardcolumnwidth_marginlen = 0;
690 if(field == SP_NAME) // name gets all remaining space
694 namesize = sbwidth;// / hud_fontsize_x;
695 for(j = 0; j < hud_num_fields; ++j)
697 if (hud_field[i] != SP_SEPARATOR)
698 namesize -= hud_size[j] + hud_fontsize.x;
699 namesize += hud_fontsize.x;
700 hud_size[i] = namesize;
702 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
703 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
704 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
705 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
708 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
710 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
717 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, bool is_self, int pl_number)
719 TC(bool, is_self); TC(int, pl_number);
721 vector rgb = Team_ColorRGB(pl.team);
723 bool is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
725 if((rgb == '1 1 1') && (!is_spec)) {
726 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
727 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
728 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
730 vector h_pos = pos - '1 1 0';
731 vector h_size = item_size + '2 0 0';
732 // alternated rows highlighting
734 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
735 else if((scoreboard_highlight) && (!(pl_number % 2)))
736 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
742 PlayerScoreField field;
743 for(i = 0; i < hud_num_fields; ++i)
745 field = hud_field[i];
746 if(field == SP_SEPARATOR)
749 if(is_spec && field != SP_NAME && field != SP_PING) {
750 pos.x += hud_size[i] + hud_fontsize.x;
753 str = HUD_GetField(pl, field);
754 str = HUD_FixScoreboardColumnWidth(i, str);
756 pos.x += hud_size[i] + hud_fontsize.x;
758 if(field == SP_NAME) {
759 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
761 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
763 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
765 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
767 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
769 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
772 tmp.x = hud_size[i] + hud_fontsize.x;
773 if(hud_field_icon0 != "")
775 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);
777 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);
778 if(hud_field_icon1 != "")
780 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);
782 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);
783 if(hud_field_icon2 != "")
785 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);
787 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);
790 if(hud_field[i] == SP_SEPARATOR)
793 for(i = hud_num_fields-1; i > 0; --i)
795 field = hud_field[i];
796 if(field == SP_SEPARATOR)
799 if(is_spec && field != SP_NAME && field != SP_PING) {
800 pos.x -= hud_size[i] + hud_fontsize.x;
804 str = HUD_GetField(pl, field);
805 str = HUD_FixScoreboardColumnWidth(i, str);
807 if(field == SP_NAME) {
808 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
810 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
812 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
814 tmp.x = hud_fixscoreboardcolumnwidth_len;
816 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
818 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
822 if(hud_field_icon0 != "")
824 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);
826 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);
827 if(hud_field_icon1 != "")
829 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);
831 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);
832 if(hud_field_icon2 != "")
834 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);
836 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);
837 pos.x -= hud_size[i] + hud_fontsize.x;
842 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
846 * HUD_Scoreboard_MakeTable
848 * Makes a table for a team (for all playing players in DM) and fills it
851 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
853 float body_table_height;
854 vector tmp = '0 0 0', column_dim = '0 0 0';
857 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
859 pos.y += autocvar_scoreboard_border_thickness;
863 tmp.y = 1.25 * hud_fontsize.y;
867 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
869 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
872 tmp.y += autocvar_scoreboard_border_thickness;
873 tmp.y += body_table_height;
874 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
876 // separator header/table
877 pos.y += 1.25 * hud_fontsize.y;
878 tmp.y = autocvar_scoreboard_border_thickness;
879 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
881 pos.y += autocvar_scoreboard_border_thickness;
884 tmp.y = body_table_height;
886 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
888 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
890 // anyway, apply some color
891 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
893 // go back to the top to make alternated columns highlighting and to print the strings
894 pos.y -= 1.25 * hud_fontsize.y;
895 pos.y -= autocvar_scoreboard_border_thickness;
899 if (scoreboard_highlight)
901 column_dim.y = 1.25 * hud_fontsize.y; // header
902 column_dim.y += autocvar_scoreboard_border_thickness;
903 column_dim.y += body_table_height;
906 // print the strings of the columns headers and draw the columns
908 for(i = 0; i < hud_num_fields; ++i)
910 if(hud_field[i] == SP_SEPARATOR)
912 column_dim.x = hud_size[i] + hud_fontsize.x;
913 if (scoreboard_highlight)
916 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
918 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
919 pos.x += column_dim.x;
921 if(hud_field[i] == SP_SEPARATOR)
925 for(i = hud_num_fields-1; i > 0; --i)
927 if(hud_field[i] == SP_SEPARATOR)
930 pos.x -= hud_size[i];
932 if (scoreboard_highlight)
936 if (i == hud_num_fields-1)
937 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
939 column_dim.x = hud_size[i] + hud_fontsize.x;
940 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
944 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
945 tmp.x = (hud_size[i] - tmp.x);
946 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
947 pos.x -= hud_fontsize.x;
952 pos.y += 1.25 * hud_fontsize.y; // skip the header
953 pos.y += autocvar_scoreboard_border_thickness;
957 tmp.y = hud_fontsize.y * 1.25;
959 // fill the table and draw the rows
962 for(pl = players.sort_next; pl; pl = pl.sort_next)
964 if(pl.team != tm.team)
966 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
967 pos.y += 1.25 * hud_fontsize.y;
971 for(pl = players.sort_next; pl; pl = pl.sort_next)
973 if(pl.team == NUM_SPECTATOR)
975 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
976 pos.y += 1.25 * hud_fontsize.y;
981 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
982 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
987 float HUD_WouldDrawScoreboard() {
988 if (autocvar__hud_configure)
990 else if (QuickMenu_IsOpened())
992 else if (HUD_Radar_Clickable())
994 else if (scoreboard_showscores)
996 else if (intermission == 1)
998 else if (intermission == 2)
1000 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
1002 else if (scoreboard_showscores_force)
1007 float average_accuracy;
1008 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
1010 WepSet weapons_stat = WepSet_GetFromStat();
1011 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1012 float initial_posx = pos.x;
1013 int disownedcnt = 0;
1014 FOREACH(Weapons, it != WEP_Null, {
1015 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1017 WepSet set = it.m_wepset;
1018 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1022 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1023 if (weapon_cnt <= 0) return pos;
1026 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1028 int columnns = ceil(weapon_cnt / rows);
1031 float fontsize = height * 1/3;
1032 float weapon_height = height * 2/3;
1033 float weapon_width = sbwidth / columnns / rows;
1035 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1036 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1037 vector tmp = '0 0 0';
1039 tmp.y = height * rows;
1042 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1044 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1045 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1047 // column highlighting
1048 for (int i = 0; i < columnns; ++i)
1051 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);
1055 for (int i = 0; i < rows; ++i)
1057 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);
1060 average_accuracy = 0;
1061 int weapons_with_stats = 0;
1063 pos.x += weapon_width / 2;
1065 if (autocvar_scoreboard_accuracy_nocolors)
1068 Accuracy_LoadColors();
1070 float oldposx = pos.x;
1074 FOREACH(Weapons, it != WEP_Null, {
1075 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1077 WepSet set = it.m_wepset;
1078 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1082 if (weapon_stats >= 0)
1083 weapon_alpha = scoreboard_alpha_fg;
1085 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1088 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1090 if (weapon_stats >= 0) {
1091 weapons_with_stats += 1;
1092 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1095 s = sprintf("%d%%", weapon_stats*100);
1098 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1100 if(!autocvar_scoreboard_accuracy_nocolors)
1101 rgb = Accuracy_GetColor(weapon_stats);
1103 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1105 tmpos.x += weapon_width * rows;
1106 pos.x += weapon_width * rows;
1107 if (rows == 2 && column == columnns - 1) {
1115 if (weapons_with_stats)
1116 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1119 pos.y += 1.25 * hud_fontsize.y;
1120 pos.x = initial_posx;
1124 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1126 pos.x += hud_fontsize.x * 0.25;
1127 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1128 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1129 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1131 pos.y+= hud_fontsize.y;
1136 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1137 float stat_secrets_found, stat_secrets_total;
1138 float stat_monsters_killed, stat_monsters_total;
1142 // get monster stats
1143 stat_monsters_killed = STAT(MONSTERS_KILLED);
1144 stat_monsters_total = STAT(MONSTERS_TOTAL);
1146 // get secrets stats
1147 stat_secrets_found = STAT(SECRETS_FOUND);
1148 stat_secrets_total = STAT(SECRETS_TOTAL);
1150 // get number of rows
1151 if(stat_secrets_total)
1153 if(stat_monsters_total)
1156 // if no rows, return
1160 // draw table header
1161 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1162 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1165 vector tmp = '0 0 0';
1167 tmp.y = hud_fontsize.y * rows;
1170 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1172 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1173 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1176 if(stat_monsters_total)
1178 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1179 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1183 if(stat_secrets_total)
1185 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1186 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1190 pos.y += 1.25 * hud_fontsize.y;
1195 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1198 RANKINGS_RECEIVED_CNT = 0;
1199 for (i=RANKINGS_CNT-1; i>=0; --i)
1201 ++RANKINGS_RECEIVED_CNT;
1203 if (RANKINGS_RECEIVED_CNT == 0)
1207 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
1209 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1210 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1211 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1213 pos.y += hud_fontsize.y;
1214 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1215 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1216 vector tmp = '0 0 0';
1218 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1221 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1223 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1224 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1227 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1234 n = grecordholder[i];
1235 p = count_ordinal(i+1);
1236 if(grecordholder[i] == entcs_GetName(player_localnum))
1237 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1238 else if(!(i % 2) && scoreboard_highlight)
1239 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1240 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1241 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);
1242 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1243 pos.y += 1.25 * hud_fontsize.y;
1245 pos.y += autocvar_scoreboard_border_thickness;
1250 float hud_woulddrawscoreboard_prev;
1251 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1252 void HUD_DrawScoreboard()
1254 float hud_woulddrawscoreboard;
1255 hud_woulddrawscoreboard = scoreboard_active;
1256 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1257 hud_woulddrawscoreboard_change = time;
1258 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1261 if(hud_woulddrawscoreboard) {
1262 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1263 if (scoreboard_fadeinspeed)
1264 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1266 scoreboard_fade_alpha = 1;
1269 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1270 if (scoreboard_fadeoutspeed)
1271 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1273 scoreboard_fade_alpha = 0;
1276 if (!scoreboard_fade_alpha)
1279 HUD_UpdatePlayerTeams();
1281 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1282 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1283 scoreboard_highlight = autocvar_scoreboard_highlight;
1284 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1285 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1286 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1287 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1289 vector rgb, pos, tmp;
1293 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1294 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1296 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1297 ymax = (vid_conheight - ymin);
1299 sbwidth = xmax - xmin;
1301 // Initializes position
1307 vector sb_heading_fontsize;
1308 sb_heading_fontsize = hud_fontsize * 2;
1309 draw_beginBoldFont();
1310 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1313 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1315 // Draw the scoreboard
1316 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * ((autocvar_scoreboard_bg_scale > 0) ? autocvar_scoreboard_bg_scale : 0.25);
1320 vector team_score_baseoffset;
1321 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1322 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1324 if(tm.team == NUM_SPECTATOR)
1326 if(!tm.team && teamplay)
1329 draw_beginBoldFont();
1330 rgb = Team_ColorRGB(tm.team);
1331 str = ftos(tm.(teamscores(ts_primary)));
1332 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1334 if(ts_primary != ts_secondary)
1336 str = ftos(tm.(teamscores(ts_secondary)));
1337 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);
1341 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1343 rgb.x = autocvar_scoreboard_color_bg_r;
1344 rgb.y = autocvar_scoreboard_color_bg_g;
1345 rgb.z = autocvar_scoreboard_color_bg_b;
1349 rgb.x = autocvar_scoreboard_color_bg_r;
1350 rgb.y = autocvar_scoreboard_color_bg_g;
1351 rgb.z = autocvar_scoreboard_color_bg_b;
1353 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1355 if(tm.team == NUM_SPECTATOR)
1357 if(!tm.team && teamplay)
1360 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1364 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1365 if(race_speedaward) {
1366 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1367 pos.y += 1.25 * hud_fontsize.y;
1369 if(race_speedaward_alltimebest) {
1370 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1371 pos.y += 1.25 * hud_fontsize.y;
1373 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1375 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1377 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1379 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1384 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1386 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1393 item_size.x = sbwidth;
1394 item_size.y = hud_fontsize.y * 1.25;
1396 for(pl = players.sort_next; pl; pl = pl.sort_next)
1398 if(pl.team != NUM_SPECTATOR)
1400 pos.y += 1.25 * hud_fontsize.y;
1401 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1407 draw_beginBoldFont();
1408 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1410 pos.y += 1.25 * hud_fontsize.y;
1413 // Print info string
1415 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1416 tl = STAT(TIMELIMIT);
1417 fl = STAT(FRAGLIMIT);
1418 ll = STAT(LEADLIMIT);
1419 if(gametype == MAPINFO_TYPE_LMS)
1422 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1427 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1431 str = strcat(str, _(" or"));
1434 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), fl),
1435 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
1436 (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1437 TranslateScoresLabel(teamscores_label(ts_primary))));
1441 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags(ps_primary), fl),
1442 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
1443 (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1444 TranslateScoresLabel(scores_label(ps_primary))));
1449 if(tl > 0 || fl > 0)
1450 str = strcat(str, _(" or"));
1453 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), ll),
1454 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
1455 (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1456 TranslateScoresLabel(teamscores_label(ts_primary))));
1460 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags(ps_primary), ll),
1461 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
1462 (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1463 TranslateScoresLabel(scores_label(ps_primary))));
1468 pos.y += 1.2 * hud_fontsize.y;
1469 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1471 // print information about respawn status
1472 float respawn_time = STAT(RESPAWN_TIME);
1476 if(respawn_time < 0)
1478 // a negative number means we are awaiting respawn, time value is still the same
1479 respawn_time *= -1; // remove mark now that we checked it
1480 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1482 str = sprintf(_("^1Respawning in ^3%s^1..."),
1483 (autocvar_scoreboard_respawntime_decimals ?
1484 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1486 count_seconds(respawn_time - time)
1490 else if(time < respawn_time)
1492 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1493 (autocvar_scoreboard_respawntime_decimals ?
1494 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1496 count_seconds(respawn_time - time)
1500 else if(time >= respawn_time)
1501 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1503 pos.y += 1.2 * hud_fontsize.y;
1504 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1507 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;