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)
195 for(ent = player.sort_next; ent && HUD_ComparePlayerScores(player, ent); ent = player.sort_next)
197 SORT_SWAP(player, ent);
199 for(ent = player.sort_prev; ent != players && HUD_ComparePlayerScores(ent, player); ent = player.sort_prev)
201 SORT_SWAP(ent, player);
205 float HUD_CompareTeamScores(entity left, entity right)
209 if(left.team == NUM_SPECTATOR)
211 if(right.team == NUM_SPECTATOR)
214 r = HUD_CompareScore(left.teamscores(ts_primary), right.teamscores(ts_primary), teamscores_flags(ts_primary));
218 r = HUD_CompareScore(left.teamscores(ts_secondary), right.teamscores(ts_secondary), teamscores_flags(ts_secondary));
222 for(i = 0; i < MAX_TEAMSCORE; ++i)
224 r = HUD_CompareScore(left.teamscores(i), right.teamscores(i), teamscores_flags(i));
229 if (left.team < right.team)
235 void HUD_UpdateTeamPos(entity Team)
238 for(ent = Team.sort_next; ent && HUD_CompareTeamScores(Team, ent); ent = Team.sort_next)
240 SORT_SWAP(Team, ent);
242 for(ent = Team.sort_prev; ent != teams && HUD_CompareTeamScores(ent, Team); ent = Team.sort_prev)
244 SORT_SWAP(ent, Team);
250 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
251 LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
252 LOG_INFO(_("Usage:\n"));
253 LOG_INFO(_("^2scoreboard_columns_set default\n"));
254 LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
255 LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
256 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n"));
259 LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n"));
260 LOG_INFO(_("^3ping^7 Ping time\n"));
261 LOG_INFO(_("^3pl^7 Packet loss\n"));
262 LOG_INFO(_("^3elo^7 Player ELO\n"));
263 LOG_INFO(_("^3kills^7 Number of kills\n"));
264 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
265 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
266 LOG_INFO(_("^3frags^7 kills - suicides\n"));
267 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
268 LOG_INFO(_("^3dmg^7 The total damage done\n"));
269 LOG_INFO(_("^3dmgtaken^7 The total damage taken\n"));
270 LOG_INFO(_("^3sum^7 frags - deaths\n"));
271 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
272 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
273 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
274 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
275 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
276 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
277 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
278 LOG_INFO(_("^3rank^7 Player rank\n"));
279 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
280 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
281 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
282 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
283 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
284 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
285 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
286 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
287 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
288 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
289 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
290 LOG_INFO(_("^3score^7 Total score\n"));
293 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
294 "of game types, then a slash, to make the field show up only in these\n"
295 "or in all but these game types. You can also specify 'all' as a\n"
296 "field to show all fields available for the current game mode.\n\n"));
298 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
299 "include/exclude ALL teams/noteams game modes.\n\n"));
301 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
302 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
303 "right of the vertical bar aligned to the right.\n"));
304 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
305 "other gamemodes except DM.\n"));
308 // NOTE: adding a gametype with ? to not warn for an optional field
309 // make sure it's excluded in a previous exclusive rule, if any
310 // otherwise the previous exclusive rule warns anyway
311 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
312 #define SCOREBOARD_DEFAULT_COLUMNS \
314 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
315 " -teams,lms/deaths +ft,tdm/deaths" \
316 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
317 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
318 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
319 " +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
320 " +lms/lives +lms/rank" \
321 " +kh/caps +kh/pushes +kh/destroyed" \
322 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
323 " +as/objectives +nb/faults +nb/goals" \
324 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
325 " -lms,rc,cts,inv,nb/score"
327 void Cmd_HUD_SetFields(int argc)
332 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
337 // set up a temporary scoreboard layout
338 // no layout can be properly set up until score_info data haven't been received
339 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
340 ps_primary = SP_SCORE;
341 scores_label(ps_primary) = strzone("score");
342 scores_flags(ps_primary) = SFL_ALLOW_HIDE;
345 // TODO: re enable with gametype dependant cvars?
346 if(argc < 3) // no arguments provided
347 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
350 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
354 if(argv(2) == "default")
355 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
356 else if(argv(2) == "all")
359 s = "ping pl name |";
360 FOREACH(Scores, true, {
362 if(it != ps_secondary)
363 if(scores_label(it) != "")
364 s = strcat(s, " ", scores_label(it));
366 if(ps_secondary != ps_primary)
367 s = strcat(s, " ", scores_label(ps_secondary));
368 s = strcat(s, " ", scores_label(ps_primary));
369 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
376 hud_fontsize = HUD_GetFontsize("hud_fontsize");
378 for(i = 1; i < argc - 1; ++i)
384 if(substring(str, 0, 1) == "?")
387 str = substring(str, 1, strlen(str) - 1);
390 slash = strstrofs(str, "/", 0);
393 pattern = substring(str, 0, slash);
394 str = substring(str, slash + 1, strlen(str) - (slash + 1));
396 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
400 strunzone(hud_title[hud_num_fields]);
401 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
402 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
403 str = strtolower(str);
408 case "ping": hud_field[hud_num_fields] = SP_PING; break;
409 case "pl": hud_field[hud_num_fields] = SP_PL; break;
410 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
411 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
412 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
413 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
414 case "elo": hud_field[hud_num_fields] = SP_ELO; break;
415 case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
416 case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
419 FOREACH(Scores, true, {
420 if (str == strtolower(scores_label(it))) {
422 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
432 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
436 hud_field[hud_num_fields] = j;
439 if(j == ps_secondary)
445 if(hud_num_fields >= MAX_HUD_FIELDS)
449 if(scores_flags(ps_primary) & SFL_ALLOW_HIDE)
451 if(scores_flags(ps_secondary) & SFL_ALLOW_HIDE)
453 if(ps_primary == ps_secondary)
455 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
457 if(hud_num_fields+missing < MAX_HUD_FIELDS)
461 strunzone(hud_title[hud_num_fields]);
462 for(i = hud_num_fields; i > 0; --i)
464 hud_title[i] = hud_title[i-1];
465 hud_size[i] = hud_size[i-1];
466 hud_field[i] = hud_field[i-1];
468 hud_title[0] = strzone(TranslateScoresLabel("name"));
469 hud_field[0] = SP_NAME;
471 LOG_INFO("fixed missing field 'name'\n");
475 strunzone(hud_title[hud_num_fields]);
476 for(i = hud_num_fields; i > 1; --i)
478 hud_title[i] = hud_title[i-1];
479 hud_size[i] = hud_size[i-1];
480 hud_field[i] = hud_field[i-1];
482 hud_title[1] = strzone("|");
483 hud_field[1] = SP_SEPARATOR;
484 hud_size[1] = stringwidth("|", false, hud_fontsize);
486 LOG_INFO("fixed missing field '|'\n");
489 else if(!have_separator)
491 strunzone(hud_title[hud_num_fields]);
492 hud_title[hud_num_fields] = strzone("|");
493 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
494 hud_field[hud_num_fields] = SP_SEPARATOR;
496 LOG_INFO("fixed missing field '|'\n");
500 strunzone(hud_title[hud_num_fields]);
501 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_secondary)));
502 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
503 hud_field[hud_num_fields] = ps_secondary;
505 LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_secondary));
509 strunzone(hud_title[hud_num_fields]);
510 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_primary)));
511 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
512 hud_field[hud_num_fields] = ps_primary;
514 LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_primary));
518 hud_field[hud_num_fields] = SP_END;
522 vector hud_field_rgb;
523 string hud_field_icon0;
524 string hud_field_icon1;
525 string hud_field_icon2;
526 vector hud_field_icon0_rgb;
527 vector hud_field_icon1_rgb;
528 vector hud_field_icon2_rgb;
529 float hud_field_icon0_alpha;
530 float hud_field_icon1_alpha;
531 float hud_field_icon2_alpha;
532 string HUD_GetField(entity pl, PlayerScoreField field)
534 float tmp, num, denom;
537 hud_field_rgb = '1 1 1';
538 hud_field_icon0 = "";
539 hud_field_icon1 = "";
540 hud_field_icon2 = "";
541 hud_field_icon0_rgb = '1 1 1';
542 hud_field_icon1_rgb = '1 1 1';
543 hud_field_icon2_rgb = '1 1 1';
544 hud_field_icon0_alpha = 1;
545 hud_field_icon1_alpha = 1;
546 hud_field_icon2_alpha = 1;
551 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
552 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
556 tmp = max(0, min(220, f-80)) / 220;
557 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
563 f = pl.ping_packetloss;
564 tmp = pl.ping_movementloss;
565 if(f == 0 && tmp == 0)
567 str = ftos(ceil(f * 100));
569 str = strcat(str, "~", ftos(ceil(tmp * 100)));
570 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
571 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
575 if(ready_waiting && pl.ready)
577 hud_field_icon0 = "gfx/scoreboard/player_ready";
581 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
583 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
584 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
585 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
586 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
587 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
590 return entcs_GetName(pl.sv_entnum);
593 f = pl.(scores(SP_KILLS));
594 f -= pl.(scores(SP_SUICIDES));
598 num = pl.(scores(SP_KILLS));
599 denom = pl.(scores(SP_DEATHS));
602 hud_field_rgb = '0 1 0';
603 str = sprintf("%d", num);
604 } else if(num <= 0) {
605 hud_field_rgb = '1 0 0';
606 str = sprintf("%.1f", num/denom);
608 str = sprintf("%.1f", num/denom);
612 f = pl.(scores(SP_KILLS));
613 f -= pl.(scores(SP_DEATHS));
616 hud_field_rgb = '0 1 0';
618 hud_field_rgb = '1 1 1';
620 hud_field_rgb = '1 0 0';
626 float elo = pl.(scores(SP_ELO));
628 case -1: return "...";
629 case -2: return _("N/A");
630 default: return ftos(elo);
635 num = pl.(scores(SP_DMG));
638 str = sprintf("%.1f k", num/denom);
642 num = pl.(scores(SP_DMGTAKEN));
645 str = sprintf("%.1f k", num/denom);
649 tmp = pl.(scores(field));
650 f = scores_flags(field);
651 if(field == ps_primary)
652 hud_field_rgb = '1 1 0';
653 else if(field == ps_secondary)
654 hud_field_rgb = '0 1 1';
656 hud_field_rgb = '1 1 1';
657 return ScoreString(f, tmp);
662 float hud_fixscoreboardcolumnwidth_len;
663 float hud_fixscoreboardcolumnwidth_iconlen;
664 float hud_fixscoreboardcolumnwidth_marginlen;
666 string HUD_FixScoreboardColumnWidth(int i, string str)
671 PlayerScoreField field = hud_field[i];
673 hud_fixscoreboardcolumnwidth_iconlen = 0;
675 if(hud_field_icon0 != "")
677 sz = draw_getimagesize(hud_field_icon0);
679 if(hud_fixscoreboardcolumnwidth_iconlen < f)
680 hud_fixscoreboardcolumnwidth_iconlen = f;
683 if(hud_field_icon1 != "")
685 sz = draw_getimagesize(hud_field_icon1);
687 if(hud_fixscoreboardcolumnwidth_iconlen < f)
688 hud_fixscoreboardcolumnwidth_iconlen = f;
691 if(hud_field_icon2 != "")
693 sz = draw_getimagesize(hud_field_icon2);
695 if(hud_fixscoreboardcolumnwidth_iconlen < f)
696 hud_fixscoreboardcolumnwidth_iconlen = f;
699 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
701 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
702 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
704 hud_fixscoreboardcolumnwidth_marginlen = 0;
706 if(field == SP_NAME) // name gets all remaining space
710 namesize = sbwidth;// / hud_fontsize_x;
711 for(j = 0; j < hud_num_fields; ++j)
713 if (hud_field[i] != SP_SEPARATOR)
714 namesize -= hud_size[j] + hud_fontsize.x;
715 namesize += hud_fontsize.x;
716 hud_size[i] = namesize;
718 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
719 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
720 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
721 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
724 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
726 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
733 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, bool is_self, int pl_number)
735 TC(bool, is_self); TC(int, pl_number);
737 vector rgb = Team_ColorRGB(pl.team);
739 bool is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
741 if((rgb == '1 1 1') && (!is_spec)) {
742 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
743 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
744 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
746 vector h_pos = pos - '1 1 0';
747 vector h_size = item_size + '2 0 0';
748 // alternated rows highlighting
750 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
751 else if((scoreboard_highlight) && (!(pl_number % 2)))
752 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
758 PlayerScoreField field;
759 for(i = 0; i < hud_num_fields; ++i)
761 field = hud_field[i];
762 if(field == SP_SEPARATOR)
765 if(is_spec && field != SP_NAME && field != SP_PING) {
766 pos.x += hud_size[i] + hud_fontsize.x;
769 str = HUD_GetField(pl, field);
770 str = HUD_FixScoreboardColumnWidth(i, str);
772 pos.x += hud_size[i] + hud_fontsize.x;
774 if(field == SP_NAME) {
775 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
777 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
779 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
781 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
783 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
785 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
788 tmp.x = hud_size[i] + hud_fontsize.x;
789 if(hud_field_icon0 != "")
791 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);
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, DRAWFLAG_NORMAL);
794 if(hud_field_icon1 != "")
796 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);
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, DRAWFLAG_NORMAL);
799 if(hud_field_icon2 != "")
801 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);
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, DRAWFLAG_NORMAL);
806 if(hud_field[i] == SP_SEPARATOR)
809 for(i = hud_num_fields-1; i > 0; --i)
811 field = hud_field[i];
812 if(field == SP_SEPARATOR)
815 if(is_spec && field != SP_NAME && field != SP_PING) {
816 pos.x -= hud_size[i] + hud_fontsize.x;
820 str = HUD_GetField(pl, field);
821 str = HUD_FixScoreboardColumnWidth(i, str);
823 if(field == SP_NAME) {
824 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
826 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
828 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
830 tmp.x = hud_fixscoreboardcolumnwidth_len;
832 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
834 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
838 if(hud_field_icon0 != "")
840 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);
842 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);
843 if(hud_field_icon1 != "")
845 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);
847 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);
848 if(hud_field_icon2 != "")
850 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);
852 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);
853 pos.x -= hud_size[i] + hud_fontsize.x;
858 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
862 * HUD_Scoreboard_MakeTable
864 * Makes a table for a team (for all playing players in DM) and fills it
867 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
869 float body_table_height;
870 vector tmp = '0 0 0', column_dim = '0 0 0';
873 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
875 pos.y += autocvar_scoreboard_border_thickness;
879 tmp.y = 1.25 * hud_fontsize.y;
883 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
885 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
888 tmp.y += autocvar_scoreboard_border_thickness;
889 tmp.y += body_table_height;
890 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
892 // separator header/table
893 pos.y += 1.25 * hud_fontsize.y;
894 tmp.y = autocvar_scoreboard_border_thickness;
895 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
897 pos.y += autocvar_scoreboard_border_thickness;
900 tmp.y = body_table_height;
902 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
904 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
906 // anyway, apply some color
907 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
909 // go back to the top to make alternated columns highlighting and to print the strings
910 pos.y -= 1.25 * hud_fontsize.y;
911 pos.y -= autocvar_scoreboard_border_thickness;
915 if (scoreboard_highlight)
917 column_dim.y = 1.25 * hud_fontsize.y; // header
918 column_dim.y += autocvar_scoreboard_border_thickness;
919 column_dim.y += body_table_height;
922 // print the strings of the columns headers and draw the columns
924 for(i = 0; i < hud_num_fields; ++i)
926 if(hud_field[i] == SP_SEPARATOR)
928 column_dim.x = hud_size[i] + hud_fontsize.x;
929 if (scoreboard_highlight)
932 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
934 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
935 pos.x += column_dim.x;
937 if(hud_field[i] == SP_SEPARATOR)
941 for(i = hud_num_fields-1; i > 0; --i)
943 if(hud_field[i] == SP_SEPARATOR)
946 pos.x -= hud_size[i];
948 if (scoreboard_highlight)
952 if (i == hud_num_fields-1)
953 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
955 column_dim.x = hud_size[i] + hud_fontsize.x;
956 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
960 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
961 tmp.x = (hud_size[i] - tmp.x);
962 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
963 pos.x -= hud_fontsize.x;
968 pos.y += 1.25 * hud_fontsize.y; // skip the header
969 pos.y += autocvar_scoreboard_border_thickness;
973 tmp.y = hud_fontsize.y * 1.25;
975 // fill the table and draw the rows
978 for(pl = players.sort_next; pl; pl = pl.sort_next)
980 if(pl.team != tm.team)
982 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
983 pos.y += 1.25 * hud_fontsize.y;
987 for(pl = players.sort_next; pl; pl = pl.sort_next)
989 if(pl.team == NUM_SPECTATOR)
991 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
992 pos.y += 1.25 * hud_fontsize.y;
997 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
998 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
1003 float HUD_WouldDrawScoreboard() {
1004 if (autocvar__hud_configure)
1006 else if (QuickMenu_IsOpened())
1008 else if (HUD_Radar_Clickable())
1010 else if (scoreboard_showscores)
1012 else if (intermission == 1)
1014 else if (intermission == 2)
1016 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
1018 else if (scoreboard_showscores_force)
1023 float average_accuracy;
1024 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
1026 WepSet weapons_stat = WepSet_GetFromStat();
1027 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1028 float initial_posx = pos.x;
1029 int disownedcnt = 0;
1030 FOREACH(Weapons, it != WEP_Null, {
1031 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1033 WepSet set = it.m_wepset;
1034 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1038 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1039 if (weapon_cnt <= 0) return pos;
1042 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1044 int columnns = ceil(weapon_cnt / rows);
1047 float fontsize = height * 1/3;
1048 float weapon_height = height * 2/3;
1049 float weapon_width = sbwidth / columnns / rows;
1051 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1052 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1053 vector tmp = '0 0 0';
1055 tmp.y = height * rows;
1058 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1060 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1061 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1063 // column highlighting
1064 for (int i = 0; i < columnns; ++i)
1067 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);
1071 for (int i = 0; i < rows; ++i)
1073 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);
1076 average_accuracy = 0;
1077 int weapons_with_stats = 0;
1079 pos.x += weapon_width / 2;
1081 if (autocvar_scoreboard_accuracy_nocolors)
1084 Accuracy_LoadColors();
1086 float oldposx = pos.x;
1090 FOREACH(Weapons, it != WEP_Null, {
1091 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1093 WepSet set = it.m_wepset;
1094 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1098 if (weapon_stats >= 0)
1099 weapon_alpha = scoreboard_alpha_fg;
1101 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1104 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1106 if (weapon_stats >= 0) {
1107 weapons_with_stats += 1;
1108 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1111 s = sprintf("%d%%", weapon_stats*100);
1114 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1116 if(!autocvar_scoreboard_accuracy_nocolors)
1117 rgb = Accuracy_GetColor(weapon_stats);
1119 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1121 tmpos.x += weapon_width * rows;
1122 pos.x += weapon_width * rows;
1123 if (rows == 2 && column == columnns - 1) {
1131 if (weapons_with_stats)
1132 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1135 pos.y += 1.25 * hud_fontsize.y;
1136 pos.x = initial_posx;
1140 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1142 pos.x += hud_fontsize.x * 0.25;
1143 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1144 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1145 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1147 pos.y+= hud_fontsize.y;
1152 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1153 float stat_secrets_found, stat_secrets_total;
1154 float stat_monsters_killed, stat_monsters_total;
1158 // get monster stats
1159 stat_monsters_killed = STAT(MONSTERS_KILLED);
1160 stat_monsters_total = STAT(MONSTERS_TOTAL);
1162 // get secrets stats
1163 stat_secrets_found = STAT(SECRETS_FOUND);
1164 stat_secrets_total = STAT(SECRETS_TOTAL);
1166 // get number of rows
1167 if(stat_secrets_total)
1169 if(stat_monsters_total)
1172 // if no rows, return
1176 // draw table header
1177 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1178 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1181 vector tmp = '0 0 0';
1183 tmp.y = hud_fontsize.y * rows;
1186 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1188 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1189 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1192 if(stat_monsters_total)
1194 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1195 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1199 if(stat_secrets_total)
1201 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1202 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1206 pos.y += 1.25 * hud_fontsize.y;
1211 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1214 RANKINGS_RECEIVED_CNT = 0;
1215 for (i=RANKINGS_CNT-1; i>=0; --i)
1217 ++RANKINGS_RECEIVED_CNT;
1219 if (RANKINGS_RECEIVED_CNT == 0)
1223 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
1225 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1226 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1227 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1229 pos.y += hud_fontsize.y;
1230 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1231 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1232 vector tmp = '0 0 0';
1234 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1237 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1239 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1240 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1243 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1250 n = grecordholder[i];
1251 p = count_ordinal(i+1);
1252 if(grecordholder[i] == entcs_GetName(player_localnum))
1253 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1254 else if(!(i % 2) && scoreboard_highlight)
1255 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1256 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1257 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);
1258 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1259 pos.y += 1.25 * hud_fontsize.y;
1261 pos.y += autocvar_scoreboard_border_thickness;
1266 float hud_woulddrawscoreboard_prev;
1267 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1268 void HUD_DrawScoreboard()
1270 float hud_woulddrawscoreboard;
1271 hud_woulddrawscoreboard = scoreboard_active;
1272 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1273 hud_woulddrawscoreboard_change = time;
1274 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1277 if(hud_woulddrawscoreboard) {
1278 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1279 if (scoreboard_fadeinspeed)
1280 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1282 scoreboard_fade_alpha = 1;
1285 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1286 if (scoreboard_fadeoutspeed)
1287 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1289 scoreboard_fade_alpha = 0;
1292 if (!scoreboard_fade_alpha)
1295 if (autocvar_scoreboard_dynamichud)
1298 HUD_Scale_Disable();
1300 HUD_UpdatePlayerTeams();
1302 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1303 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1304 scoreboard_highlight = autocvar_scoreboard_highlight;
1305 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1306 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1307 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1308 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1310 vector rgb, pos, tmp;
1314 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1315 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1317 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1318 ymax = (vid_conheight - ymin);
1320 sbwidth = xmax - xmin;
1322 // Initializes position
1328 vector sb_heading_fontsize;
1329 sb_heading_fontsize = hud_fontsize * 2;
1330 draw_beginBoldFont();
1331 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1334 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1336 // Draw the scoreboard
1337 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * ((autocvar_scoreboard_bg_scale > 0) ? autocvar_scoreboard_bg_scale : 0.25);
1341 vector team_score_baseoffset;
1342 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1343 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1345 if(tm.team == NUM_SPECTATOR)
1347 if(!tm.team && teamplay)
1350 draw_beginBoldFont();
1351 rgb = Team_ColorRGB(tm.team);
1352 str = ftos(tm.(teamscores(ts_primary)));
1353 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1355 if(ts_primary != ts_secondary)
1357 str = ftos(tm.(teamscores(ts_secondary)));
1358 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);
1362 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1364 rgb.x = autocvar_scoreboard_color_bg_r;
1365 rgb.y = autocvar_scoreboard_color_bg_g;
1366 rgb.z = autocvar_scoreboard_color_bg_b;
1370 rgb.x = autocvar_scoreboard_color_bg_r;
1371 rgb.y = autocvar_scoreboard_color_bg_g;
1372 rgb.z = autocvar_scoreboard_color_bg_b;
1374 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1376 if(tm.team == NUM_SPECTATOR)
1378 if(!tm.team && teamplay)
1381 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1385 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1386 if(race_speedaward) {
1387 drawcolorcodedstring(pos, sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward, race_speedaward_unit, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1388 pos.y += 1.25 * hud_fontsize.y;
1390 if(race_speedaward_alltimebest) {
1391 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d%s ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_unit, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1392 pos.y += 1.25 * hud_fontsize.y;
1394 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1396 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1398 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1400 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1405 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1407 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1414 item_size.x = sbwidth;
1415 item_size.y = hud_fontsize.y * 1.25;
1417 for(pl = players.sort_next; pl; pl = pl.sort_next)
1419 if(pl.team != NUM_SPECTATOR)
1421 pos.y += 1.25 * hud_fontsize.y;
1422 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1428 draw_beginBoldFont();
1429 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1431 pos.y += 1.25 * hud_fontsize.y;
1434 // Print info string
1436 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1437 tl = STAT(TIMELIMIT);
1438 fl = STAT(FRAGLIMIT);
1439 ll = STAT(LEADLIMIT);
1440 if(gametype == MAPINFO_TYPE_LMS)
1443 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1448 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1452 str = strcat(str, _(" or"));
1455 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), fl),
1456 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
1457 (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1458 TranslateScoresLabel(teamscores_label(ts_primary))));
1462 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags(ps_primary), fl),
1463 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
1464 (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1465 TranslateScoresLabel(scores_label(ps_primary))));
1470 if(tl > 0 || fl > 0)
1471 str = strcat(str, _(" or"));
1474 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), ll),
1475 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
1476 (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1477 TranslateScoresLabel(teamscores_label(ts_primary))));
1481 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags(ps_primary), ll),
1482 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
1483 (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1484 TranslateScoresLabel(scores_label(ps_primary))));
1489 pos.y += 1.2 * hud_fontsize.y;
1490 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1492 // print information about respawn status
1493 float respawn_time = STAT(RESPAWN_TIME);
1497 if(respawn_time < 0)
1499 // a negative number means we are awaiting respawn, time value is still the same
1500 respawn_time *= -1; // remove mark now that we checked it
1501 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1503 str = sprintf(_("^1Respawning in ^3%s^1..."),
1504 (autocvar_scoreboard_respawntime_decimals ?
1505 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1507 count_seconds(respawn_time - time)
1511 else if(time < respawn_time)
1513 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1514 (autocvar_scoreboard_respawntime_decimals ?
1515 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1517 count_seconds(respawn_time - time)
1521 else if(time >= respawn_time)
1522 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey(_("jump"), "+jump"));
1524 pos.y += 1.2 * hud_fontsize.y;
1525 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1528 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;