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"));
257 LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n"));
258 LOG_INFO(_("^3ping^7 Ping time\n"));
259 LOG_INFO(_("^3pl^7 Packet loss\n"));
260 LOG_INFO(_("^3elo^7 Player ELO\n"));
261 LOG_INFO(_("^3kills^7 Number of kills\n"));
262 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
263 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
264 LOG_INFO(_("^3frags^7 kills - suicides\n"));
265 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
266 LOG_INFO(_("^3dmg^7 The total damage done\n"));
267 LOG_INFO(_("^3dmgtaken^7 The total damage taken\n"));
268 LOG_INFO(_("^3sum^7 frags - deaths\n"));
269 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
270 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
271 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
272 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
273 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
274 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
275 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
276 LOG_INFO(_("^3rank^7 Player rank\n"));
277 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
278 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
279 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
280 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
281 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
282 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
283 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
284 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
285 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
286 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
287 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
288 LOG_INFO(_("^3score^7 Total score\n"));
291 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
292 "of game types, then a slash, to make the field show up only in these\n"
293 "or in all but these game types. You can also specify 'all' as a\n"
294 "field to show all fields available for the current game mode.\n\n"));
296 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
297 "include/exclude ALL teams/noteams game modes.\n\n"));
299 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
300 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
301 "right of the vertical bar aligned to the right.\n"));
302 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
303 "other gamemodes except DM.\n"));
306 // NOTE: adding a gametype with ? to not warn for an optional field
307 // make sure it's excluded in a previous exclusive rule, if any
308 // otherwise the previous exclusive rule warns anyway
309 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
310 #define SCOREBOARD_DEFAULT_COLUMNS \
312 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
313 " -teams,lms/deaths +ft,tdm/deaths" \
314 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
315 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
316 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
317 " +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
318 " +lms/lives +lms/rank" \
319 " +kh/caps +kh/pushes +kh/destroyed" \
320 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
321 " +as/objectives +nb/faults +nb/goals" \
322 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
323 " -lms,rc,cts,inv,nb/score"
325 void Cmd_HUD_SetFields(int argc)
330 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
335 // set up a temporary scoreboard layout
336 // no layout can be properly set up until score_info data haven't been received
337 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
338 ps_primary = SP_SCORE;
339 scores_label(ps_primary) = strzone("score");
340 scores_flags(ps_primary) = SFL_ALLOW_HIDE;
343 // TODO: re enable with gametype dependant cvars?
344 if(argc < 3) // no arguments provided
345 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
348 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
352 if(argv(2) == "default")
353 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
354 else if(argv(2) == "all")
357 s = "ping pl name |";
358 FOREACH(Scores, true, {
360 if(it != ps_secondary)
361 if(scores_label(it) != "")
362 s = strcat(s, " ", scores_label(it));
364 if(ps_secondary != ps_primary)
365 s = strcat(s, " ", scores_label(ps_secondary));
366 s = strcat(s, " ", scores_label(ps_primary));
367 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
374 hud_fontsize = HUD_GetFontsize("hud_fontsize");
376 for(i = 1; i < argc - 1; ++i)
382 if(substring(str, 0, 1) == "?")
385 str = substring(str, 1, strlen(str) - 1);
388 slash = strstrofs(str, "/", 0);
391 pattern = substring(str, 0, slash);
392 str = substring(str, slash + 1, strlen(str) - (slash + 1));
394 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
398 strunzone(hud_title[hud_num_fields]);
399 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
400 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
401 str = strtolower(str);
406 case "ping": hud_field[hud_num_fields] = SP_PING; break;
407 case "pl": hud_field[hud_num_fields] = SP_PL; break;
408 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
409 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
410 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
411 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
412 case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
413 case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
416 FOREACH(Scores, true, {
417 if (str == strtolower(scores_label(it))) {
419 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
429 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
433 hud_field[hud_num_fields] = j;
436 if(j == ps_secondary)
442 if(hud_num_fields >= MAX_HUD_FIELDS)
446 if(scores_flags(ps_primary) & SFL_ALLOW_HIDE)
448 if(scores_flags(ps_secondary) & SFL_ALLOW_HIDE)
450 if(ps_primary == ps_secondary)
452 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
454 if(hud_num_fields+missing < MAX_HUD_FIELDS)
458 strunzone(hud_title[hud_num_fields]);
459 for(i = hud_num_fields; i > 0; --i)
461 hud_title[i] = hud_title[i-1];
462 hud_size[i] = hud_size[i-1];
463 hud_field[i] = hud_field[i-1];
465 hud_title[0] = strzone(TranslateScoresLabel("name"));
466 hud_field[0] = SP_NAME;
468 LOG_INFO("fixed missing field 'name'\n");
472 strunzone(hud_title[hud_num_fields]);
473 for(i = hud_num_fields; i > 1; --i)
475 hud_title[i] = hud_title[i-1];
476 hud_size[i] = hud_size[i-1];
477 hud_field[i] = hud_field[i-1];
479 hud_title[1] = strzone("|");
480 hud_field[1] = SP_SEPARATOR;
481 hud_size[1] = stringwidth("|", false, hud_fontsize);
483 LOG_INFO("fixed missing field '|'\n");
486 else if(!have_separator)
488 strunzone(hud_title[hud_num_fields]);
489 hud_title[hud_num_fields] = strzone("|");
490 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
491 hud_field[hud_num_fields] = SP_SEPARATOR;
493 LOG_INFO("fixed missing field '|'\n");
497 strunzone(hud_title[hud_num_fields]);
498 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_secondary)));
499 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
500 hud_field[hud_num_fields] = ps_secondary;
502 LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_secondary));
506 strunzone(hud_title[hud_num_fields]);
507 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_primary)));
508 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
509 hud_field[hud_num_fields] = ps_primary;
511 LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_primary));
515 hud_field[hud_num_fields] = SP_END;
519 vector hud_field_rgb;
520 string hud_field_icon0;
521 string hud_field_icon1;
522 string hud_field_icon2;
523 vector hud_field_icon0_rgb;
524 vector hud_field_icon1_rgb;
525 vector hud_field_icon2_rgb;
526 float hud_field_icon0_alpha;
527 float hud_field_icon1_alpha;
528 float hud_field_icon2_alpha;
529 string HUD_GetField(entity pl, PlayerScoreField field)
531 float tmp, num, denom;
534 hud_field_rgb = '1 1 1';
535 hud_field_icon0 = "";
536 hud_field_icon1 = "";
537 hud_field_icon2 = "";
538 hud_field_icon0_rgb = '1 1 1';
539 hud_field_icon1_rgb = '1 1 1';
540 hud_field_icon2_rgb = '1 1 1';
541 hud_field_icon0_alpha = 1;
542 hud_field_icon1_alpha = 1;
543 hud_field_icon2_alpha = 1;
548 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
549 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
553 tmp = max(0, min(220, f-80)) / 220;
554 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
560 f = pl.ping_packetloss;
561 tmp = pl.ping_movementloss;
562 if(f == 0 && tmp == 0)
564 str = ftos(ceil(f * 100));
566 str = strcat(str, "~", ftos(ceil(tmp * 100)));
567 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
568 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
572 if(ready_waiting && pl.ready)
574 hud_field_icon0 = "gfx/scoreboard/player_ready";
578 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
580 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
581 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
582 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
583 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
584 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
587 return entcs_GetName(pl.sv_entnum);
590 f = pl.(scores(SP_KILLS));
591 f -= pl.(scores(SP_SUICIDES));
595 num = pl.(scores(SP_KILLS));
596 denom = pl.(scores(SP_DEATHS));
599 hud_field_rgb = '0 1 0';
600 str = sprintf("%d", num);
601 } else if(num <= 0) {
602 hud_field_rgb = '1 0 0';
603 str = sprintf("%.1f", num/denom);
605 str = sprintf("%.1f", num/denom);
609 f = pl.(scores(SP_KILLS));
610 f -= pl.(scores(SP_DEATHS));
613 hud_field_rgb = '0 1 0';
615 hud_field_rgb = '1 1 1';
617 hud_field_rgb = '1 0 0';
622 num = pl.(scores(SP_DMG));
625 str = sprintf("%.1f k", num/denom);
629 num = pl.(scores(SP_DMGTAKEN));
632 str = sprintf("%.1f k", num/denom);
636 tmp = pl.(scores(field));
637 f = scores_flags(field);
638 if(field == ps_primary)
639 hud_field_rgb = '1 1 0';
640 else if(field == ps_secondary)
641 hud_field_rgb = '0 1 1';
643 hud_field_rgb = '1 1 1';
644 return ScoreString(f, tmp);
649 float hud_fixscoreboardcolumnwidth_len;
650 float hud_fixscoreboardcolumnwidth_iconlen;
651 float hud_fixscoreboardcolumnwidth_marginlen;
653 string HUD_FixScoreboardColumnWidth(int i, string str)
658 PlayerScoreField field = hud_field[i];
660 hud_fixscoreboardcolumnwidth_iconlen = 0;
662 if(hud_field_icon0 != "")
664 sz = draw_getimagesize(hud_field_icon0);
666 if(hud_fixscoreboardcolumnwidth_iconlen < f)
667 hud_fixscoreboardcolumnwidth_iconlen = f;
670 if(hud_field_icon1 != "")
672 sz = draw_getimagesize(hud_field_icon1);
674 if(hud_fixscoreboardcolumnwidth_iconlen < f)
675 hud_fixscoreboardcolumnwidth_iconlen = f;
678 if(hud_field_icon2 != "")
680 sz = draw_getimagesize(hud_field_icon2);
682 if(hud_fixscoreboardcolumnwidth_iconlen < f)
683 hud_fixscoreboardcolumnwidth_iconlen = f;
686 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
688 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
689 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
691 hud_fixscoreboardcolumnwidth_marginlen = 0;
693 if(field == SP_NAME) // name gets all remaining space
697 namesize = sbwidth;// / hud_fontsize_x;
698 for(j = 0; j < hud_num_fields; ++j)
700 if (hud_field[i] != SP_SEPARATOR)
701 namesize -= hud_size[j] + hud_fontsize.x;
702 namesize += hud_fontsize.x;
703 hud_size[i] = namesize;
705 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
706 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
707 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
708 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
711 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
713 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
720 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, bool is_self, int pl_number)
722 TC(bool, is_self); TC(int, pl_number);
724 vector rgb = Team_ColorRGB(pl.team);
726 bool is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
728 if((rgb == '1 1 1') && (!is_spec)) {
729 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
730 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
731 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
733 vector h_pos = pos - '1 1 0';
734 vector h_size = item_size + '2 0 0';
735 // alternated rows highlighting
737 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
738 else if((scoreboard_highlight) && (!(pl_number % 2)))
739 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
745 PlayerScoreField field;
746 for(i = 0; i < hud_num_fields; ++i)
748 field = hud_field[i];
749 if(field == SP_SEPARATOR)
752 if(is_spec && field != SP_NAME && field != SP_PING) {
753 pos.x += hud_size[i] + hud_fontsize.x;
756 str = HUD_GetField(pl, field);
757 str = HUD_FixScoreboardColumnWidth(i, str);
759 pos.x += hud_size[i] + hud_fontsize.x;
761 if(field == SP_NAME) {
762 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
764 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
766 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
768 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
770 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
772 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
775 tmp.x = hud_size[i] + hud_fontsize.x;
776 if(hud_field_icon0 != "")
778 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);
780 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);
781 if(hud_field_icon1 != "")
783 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);
785 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);
786 if(hud_field_icon2 != "")
788 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);
790 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);
793 if(hud_field[i] == SP_SEPARATOR)
796 for(i = hud_num_fields-1; i > 0; --i)
798 field = hud_field[i];
799 if(field == SP_SEPARATOR)
802 if(is_spec && field != SP_NAME && field != SP_PING) {
803 pos.x -= hud_size[i] + hud_fontsize.x;
807 str = HUD_GetField(pl, field);
808 str = HUD_FixScoreboardColumnWidth(i, str);
810 if(field == SP_NAME) {
811 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
813 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
815 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
817 tmp.x = hud_fixscoreboardcolumnwidth_len;
819 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
821 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
825 if(hud_field_icon0 != "")
827 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);
829 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);
830 if(hud_field_icon1 != "")
832 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);
834 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);
835 if(hud_field_icon2 != "")
837 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);
839 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);
840 pos.x -= hud_size[i] + hud_fontsize.x;
845 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
849 * HUD_Scoreboard_MakeTable
851 * Makes a table for a team (for all playing players in DM) and fills it
854 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
856 float body_table_height;
857 vector tmp = '0 0 0', column_dim = '0 0 0';
860 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
862 pos.y += autocvar_scoreboard_border_thickness;
866 tmp.y = 1.25 * hud_fontsize.y;
870 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
872 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
875 tmp.y += autocvar_scoreboard_border_thickness;
876 tmp.y += body_table_height;
877 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
879 // separator header/table
880 pos.y += 1.25 * hud_fontsize.y;
881 tmp.y = autocvar_scoreboard_border_thickness;
882 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
884 pos.y += autocvar_scoreboard_border_thickness;
887 tmp.y = body_table_height;
889 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
891 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
893 // anyway, apply some color
894 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
896 // go back to the top to make alternated columns highlighting and to print the strings
897 pos.y -= 1.25 * hud_fontsize.y;
898 pos.y -= autocvar_scoreboard_border_thickness;
902 if (scoreboard_highlight)
904 column_dim.y = 1.25 * hud_fontsize.y; // header
905 column_dim.y += autocvar_scoreboard_border_thickness;
906 column_dim.y += body_table_height;
909 // print the strings of the columns headers and draw the columns
911 for(i = 0; i < hud_num_fields; ++i)
913 if(hud_field[i] == SP_SEPARATOR)
915 column_dim.x = hud_size[i] + hud_fontsize.x;
916 if (scoreboard_highlight)
919 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
921 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
922 pos.x += column_dim.x;
924 if(hud_field[i] == SP_SEPARATOR)
928 for(i = hud_num_fields-1; i > 0; --i)
930 if(hud_field[i] == SP_SEPARATOR)
933 pos.x -= hud_size[i];
935 if (scoreboard_highlight)
939 if (i == hud_num_fields-1)
940 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
942 column_dim.x = hud_size[i] + hud_fontsize.x;
943 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
947 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
948 tmp.x = (hud_size[i] - tmp.x);
949 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
950 pos.x -= hud_fontsize.x;
955 pos.y += 1.25 * hud_fontsize.y; // skip the header
956 pos.y += autocvar_scoreboard_border_thickness;
960 tmp.y = hud_fontsize.y * 1.25;
962 // fill the table and draw the rows
965 for(pl = players.sort_next; pl; pl = pl.sort_next)
967 if(pl.team != tm.team)
969 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
970 pos.y += 1.25 * hud_fontsize.y;
974 for(pl = players.sort_next; pl; pl = pl.sort_next)
976 if(pl.team == NUM_SPECTATOR)
978 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
979 pos.y += 1.25 * hud_fontsize.y;
984 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
985 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
990 float HUD_WouldDrawScoreboard() {
991 if (autocvar__hud_configure)
993 else if (QuickMenu_IsOpened())
995 else if (HUD_Radar_Clickable())
997 else if (scoreboard_showscores)
999 else if (intermission == 1)
1001 else if (intermission == 2)
1003 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
1005 else if (scoreboard_showscores_force)
1010 float average_accuracy;
1011 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
1013 WepSet weapons_stat = WepSet_GetFromStat();
1014 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1015 float initial_posx = pos.x;
1016 int disownedcnt = 0;
1017 FOREACH(Weapons, it != WEP_Null, {
1018 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1020 WepSet set = it.m_wepset;
1021 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1025 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1026 if (weapon_cnt <= 0) return pos;
1029 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1031 int columnns = ceil(weapon_cnt / rows);
1034 float fontsize = height * 1/3;
1035 float weapon_height = height * 2/3;
1036 float weapon_width = sbwidth / columnns / rows;
1038 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1039 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1040 vector tmp = '0 0 0';
1042 tmp.y = height * rows;
1045 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1047 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1048 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1050 // column highlighting
1051 for (int i = 0; i < columnns; ++i)
1054 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);
1058 for (int i = 0; i < rows; ++i)
1060 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);
1063 average_accuracy = 0;
1064 int weapons_with_stats = 0;
1066 pos.x += weapon_width / 2;
1068 if (autocvar_scoreboard_accuracy_nocolors)
1071 Accuracy_LoadColors();
1073 float oldposx = pos.x;
1077 FOREACH(Weapons, it != WEP_Null, {
1078 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1080 WepSet set = it.m_wepset;
1081 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1085 if (weapon_stats >= 0)
1086 weapon_alpha = scoreboard_alpha_fg;
1088 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1091 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1093 if (weapon_stats >= 0) {
1094 weapons_with_stats += 1;
1095 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1098 s = sprintf("%d%%", weapon_stats*100);
1101 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1103 if(!autocvar_scoreboard_accuracy_nocolors)
1104 rgb = Accuracy_GetColor(weapon_stats);
1106 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1108 tmpos.x += weapon_width * rows;
1109 pos.x += weapon_width * rows;
1110 if (rows == 2 && column == columnns - 1) {
1118 if (weapons_with_stats)
1119 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1122 pos.y += 1.25 * hud_fontsize.y;
1123 pos.x = initial_posx;
1127 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1129 pos.x += hud_fontsize.x * 0.25;
1130 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1131 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1132 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1134 pos.y+= hud_fontsize.y;
1139 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1140 float stat_secrets_found, stat_secrets_total;
1141 float stat_monsters_killed, stat_monsters_total;
1145 // get monster stats
1146 stat_monsters_killed = STAT(MONSTERS_KILLED);
1147 stat_monsters_total = STAT(MONSTERS_TOTAL);
1149 // get secrets stats
1150 stat_secrets_found = STAT(SECRETS_FOUND);
1151 stat_secrets_total = STAT(SECRETS_TOTAL);
1153 // get number of rows
1154 if(stat_secrets_total)
1156 if(stat_monsters_total)
1159 // if no rows, return
1163 // draw table header
1164 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1165 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1168 vector tmp = '0 0 0';
1170 tmp.y = hud_fontsize.y * rows;
1173 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1175 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1176 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1179 if(stat_monsters_total)
1181 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1182 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1186 if(stat_secrets_total)
1188 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1189 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1193 pos.y += 1.25 * hud_fontsize.y;
1198 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1201 RANKINGS_RECEIVED_CNT = 0;
1202 for (i=RANKINGS_CNT-1; i>=0; --i)
1204 ++RANKINGS_RECEIVED_CNT;
1206 if (RANKINGS_RECEIVED_CNT == 0)
1210 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
1212 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1213 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1214 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1216 pos.y += hud_fontsize.y;
1217 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1218 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1219 vector tmp = '0 0 0';
1221 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1224 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1226 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1227 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1230 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1237 n = grecordholder[i];
1238 p = count_ordinal(i+1);
1239 if(grecordholder[i] == entcs_GetName(player_localnum))
1240 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1241 else if(!(i % 2) && scoreboard_highlight)
1242 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1243 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1244 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);
1245 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1246 pos.y += 1.25 * hud_fontsize.y;
1248 pos.y += autocvar_scoreboard_border_thickness;
1253 float hud_woulddrawscoreboard_prev;
1254 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1255 void HUD_DrawScoreboard()
1257 float hud_woulddrawscoreboard;
1258 hud_woulddrawscoreboard = scoreboard_active;
1259 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1260 hud_woulddrawscoreboard_change = time;
1261 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1264 if(hud_woulddrawscoreboard) {
1265 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1266 if (scoreboard_fadeinspeed)
1267 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1269 scoreboard_fade_alpha = 1;
1272 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1273 if (scoreboard_fadeoutspeed)
1274 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1276 scoreboard_fade_alpha = 0;
1279 if (!scoreboard_fade_alpha)
1282 if (autocvar_scoreboard_dynamichud)
1285 HUD_Scale_Disable();
1287 HUD_UpdatePlayerTeams();
1289 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1290 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1291 scoreboard_highlight = autocvar_scoreboard_highlight;
1292 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1293 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1294 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1295 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1297 vector rgb, pos, tmp;
1301 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1302 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1304 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1305 ymax = (vid_conheight - ymin);
1307 sbwidth = xmax - xmin;
1309 // Initializes position
1315 vector sb_heading_fontsize;
1316 sb_heading_fontsize = hud_fontsize * 2;
1317 draw_beginBoldFont();
1318 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1321 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1323 // Draw the scoreboard
1324 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * ((autocvar_scoreboard_bg_scale > 0) ? autocvar_scoreboard_bg_scale : 0.25);
1328 vector team_score_baseoffset;
1329 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1330 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1332 if(tm.team == NUM_SPECTATOR)
1334 if(!tm.team && teamplay)
1337 draw_beginBoldFont();
1338 rgb = Team_ColorRGB(tm.team);
1339 str = ftos(tm.(teamscores(ts_primary)));
1340 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1342 if(ts_primary != ts_secondary)
1344 str = ftos(tm.(teamscores(ts_secondary)));
1345 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);
1349 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1351 rgb.x = autocvar_scoreboard_color_bg_r;
1352 rgb.y = autocvar_scoreboard_color_bg_g;
1353 rgb.z = autocvar_scoreboard_color_bg_b;
1357 rgb.x = autocvar_scoreboard_color_bg_r;
1358 rgb.y = autocvar_scoreboard_color_bg_g;
1359 rgb.z = autocvar_scoreboard_color_bg_b;
1361 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1363 if(tm.team == NUM_SPECTATOR)
1365 if(!tm.team && teamplay)
1368 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1372 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1373 if(race_speedaward) {
1374 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1375 pos.y += 1.25 * hud_fontsize.y;
1377 if(race_speedaward_alltimebest) {
1378 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1379 pos.y += 1.25 * hud_fontsize.y;
1381 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1383 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1385 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1387 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1392 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1394 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1401 item_size.x = sbwidth;
1402 item_size.y = hud_fontsize.y * 1.25;
1404 for(pl = players.sort_next; pl; pl = pl.sort_next)
1406 if(pl.team != NUM_SPECTATOR)
1408 pos.y += 1.25 * hud_fontsize.y;
1409 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1415 draw_beginBoldFont();
1416 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1418 pos.y += 1.25 * hud_fontsize.y;
1421 // Print info string
1423 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1424 tl = STAT(TIMELIMIT);
1425 fl = STAT(FRAGLIMIT);
1426 ll = STAT(LEADLIMIT);
1427 if(gametype == MAPINFO_TYPE_LMS)
1430 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1435 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1439 str = strcat(str, _(" or"));
1442 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), fl),
1443 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
1444 (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1445 TranslateScoresLabel(teamscores_label(ts_primary))));
1449 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags(ps_primary), fl),
1450 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
1451 (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1452 TranslateScoresLabel(scores_label(ps_primary))));
1457 if(tl > 0 || fl > 0)
1458 str = strcat(str, _(" or"));
1461 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), ll),
1462 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
1463 (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1464 TranslateScoresLabel(teamscores_label(ts_primary))));
1468 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags(ps_primary), ll),
1469 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
1470 (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1471 TranslateScoresLabel(scores_label(ps_primary))));
1476 pos.y += 1.2 * hud_fontsize.y;
1477 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1479 // print information about respawn status
1480 float respawn_time = STAT(RESPAWN_TIME);
1484 if(respawn_time < 0)
1486 // a negative number means we are awaiting respawn, time value is still the same
1487 respawn_time *= -1; // remove mark now that we checked it
1488 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1490 str = sprintf(_("^1Respawning in ^3%s^1..."),
1491 (autocvar_scoreboard_respawntime_decimals ?
1492 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1494 count_seconds(respawn_time - time)
1498 else if(time < respawn_time)
1500 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1501 (autocvar_scoreboard_respawntime_decimals ?
1502 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1504 count_seconds(respawn_time - time)
1508 else if(time >= respawn_time)
1509 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1511 pos.y += 1.2 * hud_fontsize.y;
1512 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1515 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;