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 "dmg": hud_field[hud_num_fields] = SP_DMG; break;
415 case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
418 FOREACH(Scores, true, {
419 if (str == strtolower(scores_label(it))) {
421 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
431 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
435 hud_field[hud_num_fields] = j;
438 if(j == ps_secondary)
444 if(hud_num_fields >= MAX_HUD_FIELDS)
448 if(scores_flags(ps_primary) & SFL_ALLOW_HIDE)
450 if(scores_flags(ps_secondary) & SFL_ALLOW_HIDE)
452 if(ps_primary == ps_secondary)
454 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
456 if(hud_num_fields+missing < MAX_HUD_FIELDS)
460 strunzone(hud_title[hud_num_fields]);
461 for(i = hud_num_fields; i > 0; --i)
463 hud_title[i] = hud_title[i-1];
464 hud_size[i] = hud_size[i-1];
465 hud_field[i] = hud_field[i-1];
467 hud_title[0] = strzone(TranslateScoresLabel("name"));
468 hud_field[0] = SP_NAME;
470 LOG_INFO("fixed missing field 'name'\n");
474 strunzone(hud_title[hud_num_fields]);
475 for(i = hud_num_fields; i > 1; --i)
477 hud_title[i] = hud_title[i-1];
478 hud_size[i] = hud_size[i-1];
479 hud_field[i] = hud_field[i-1];
481 hud_title[1] = strzone("|");
482 hud_field[1] = SP_SEPARATOR;
483 hud_size[1] = stringwidth("|", false, hud_fontsize);
485 LOG_INFO("fixed missing field '|'\n");
488 else if(!have_separator)
490 strunzone(hud_title[hud_num_fields]);
491 hud_title[hud_num_fields] = strzone("|");
492 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
493 hud_field[hud_num_fields] = SP_SEPARATOR;
495 LOG_INFO("fixed missing field '|'\n");
499 strunzone(hud_title[hud_num_fields]);
500 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_secondary)));
501 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
502 hud_field[hud_num_fields] = ps_secondary;
504 LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_secondary));
508 strunzone(hud_title[hud_num_fields]);
509 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_primary)));
510 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
511 hud_field[hud_num_fields] = ps_primary;
513 LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_primary));
517 hud_field[hud_num_fields] = SP_END;
521 vector hud_field_rgb;
522 string hud_field_icon0;
523 string hud_field_icon1;
524 string hud_field_icon2;
525 vector hud_field_icon0_rgb;
526 vector hud_field_icon1_rgb;
527 vector hud_field_icon2_rgb;
528 float hud_field_icon0_alpha;
529 float hud_field_icon1_alpha;
530 float hud_field_icon2_alpha;
531 string HUD_GetField(entity pl, PlayerScoreField field)
533 float tmp, num, denom;
536 hud_field_rgb = '1 1 1';
537 hud_field_icon0 = "";
538 hud_field_icon1 = "";
539 hud_field_icon2 = "";
540 hud_field_icon0_rgb = '1 1 1';
541 hud_field_icon1_rgb = '1 1 1';
542 hud_field_icon2_rgb = '1 1 1';
543 hud_field_icon0_alpha = 1;
544 hud_field_icon1_alpha = 1;
545 hud_field_icon2_alpha = 1;
550 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
551 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
555 tmp = max(0, min(220, f-80)) / 220;
556 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
562 f = pl.ping_packetloss;
563 tmp = pl.ping_movementloss;
564 if(f == 0 && tmp == 0)
566 str = ftos(ceil(f * 100));
568 str = strcat(str, "~", ftos(ceil(tmp * 100)));
569 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
570 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
574 if(ready_waiting && pl.ready)
576 hud_field_icon0 = "gfx/scoreboard/player_ready";
580 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
582 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
583 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
584 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
585 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
586 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
589 return entcs_GetName(pl.sv_entnum);
592 f = pl.(scores(SP_KILLS));
593 f -= pl.(scores(SP_SUICIDES));
597 num = pl.(scores(SP_KILLS));
598 denom = pl.(scores(SP_DEATHS));
601 hud_field_rgb = '0 1 0';
602 str = sprintf("%d", num);
603 } else if(num <= 0) {
604 hud_field_rgb = '1 0 0';
605 str = sprintf("%.1f", num/denom);
607 str = sprintf("%.1f", num/denom);
611 f = pl.(scores(SP_KILLS));
612 f -= pl.(scores(SP_DEATHS));
615 hud_field_rgb = '0 1 0';
617 hud_field_rgb = '1 1 1';
619 hud_field_rgb = '1 0 0';
624 num = pl.(scores(SP_DMG));
627 str = sprintf("%.1f k", num/denom);
631 num = pl.(scores(SP_DMGTAKEN));
634 str = sprintf("%.1f k", num/denom);
638 tmp = pl.(scores(field));
639 f = scores_flags(field);
640 if(field == ps_primary)
641 hud_field_rgb = '1 1 0';
642 else if(field == ps_secondary)
643 hud_field_rgb = '0 1 1';
645 hud_field_rgb = '1 1 1';
646 return ScoreString(f, tmp);
651 float hud_fixscoreboardcolumnwidth_len;
652 float hud_fixscoreboardcolumnwidth_iconlen;
653 float hud_fixscoreboardcolumnwidth_marginlen;
655 string HUD_FixScoreboardColumnWidth(int i, string str)
660 PlayerScoreField field = hud_field[i];
662 hud_fixscoreboardcolumnwidth_iconlen = 0;
664 if(hud_field_icon0 != "")
666 sz = draw_getimagesize(hud_field_icon0);
668 if(hud_fixscoreboardcolumnwidth_iconlen < f)
669 hud_fixscoreboardcolumnwidth_iconlen = f;
672 if(hud_field_icon1 != "")
674 sz = draw_getimagesize(hud_field_icon1);
676 if(hud_fixscoreboardcolumnwidth_iconlen < f)
677 hud_fixscoreboardcolumnwidth_iconlen = f;
680 if(hud_field_icon2 != "")
682 sz = draw_getimagesize(hud_field_icon2);
684 if(hud_fixscoreboardcolumnwidth_iconlen < f)
685 hud_fixscoreboardcolumnwidth_iconlen = f;
688 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
690 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
691 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
693 hud_fixscoreboardcolumnwidth_marginlen = 0;
695 if(field == SP_NAME) // name gets all remaining space
699 namesize = sbwidth;// / hud_fontsize_x;
700 for(j = 0; j < hud_num_fields; ++j)
702 if (hud_field[i] != SP_SEPARATOR)
703 namesize -= hud_size[j] + hud_fontsize.x;
704 namesize += hud_fontsize.x;
705 hud_size[i] = namesize;
707 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
708 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
709 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
710 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
713 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
715 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
722 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, bool is_self, int pl_number)
724 TC(bool, is_self); TC(int, pl_number);
726 vector rgb = Team_ColorRGB(pl.team);
728 bool is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
730 if((rgb == '1 1 1') && (!is_spec)) {
731 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
732 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
733 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
735 vector h_pos = pos - '1 1 0';
736 vector h_size = item_size + '2 0 0';
737 // alternated rows highlighting
739 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
740 else if((scoreboard_highlight) && (!(pl_number % 2)))
741 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
747 PlayerScoreField field;
748 for(i = 0; i < hud_num_fields; ++i)
750 field = hud_field[i];
751 if(field == SP_SEPARATOR)
754 if(is_spec && field != SP_NAME && field != SP_PING) {
755 pos.x += hud_size[i] + hud_fontsize.x;
758 str = HUD_GetField(pl, field);
759 str = HUD_FixScoreboardColumnWidth(i, str);
761 pos.x += hud_size[i] + hud_fontsize.x;
763 if(field == SP_NAME) {
764 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
766 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
768 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
770 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
772 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
774 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
777 tmp.x = hud_size[i] + hud_fontsize.x;
778 if(hud_field_icon0 != "")
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_self, DRAWFLAG_NORMAL);
782 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);
783 if(hud_field_icon1 != "")
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_self, DRAWFLAG_NORMAL);
787 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);
788 if(hud_field_icon2 != "")
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_self, DRAWFLAG_NORMAL);
792 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);
795 if(hud_field[i] == SP_SEPARATOR)
798 for(i = hud_num_fields-1; i > 0; --i)
800 field = hud_field[i];
801 if(field == SP_SEPARATOR)
804 if(is_spec && field != SP_NAME && field != SP_PING) {
805 pos.x -= hud_size[i] + hud_fontsize.x;
809 str = HUD_GetField(pl, field);
810 str = HUD_FixScoreboardColumnWidth(i, str);
812 if(field == SP_NAME) {
813 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
815 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
817 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
819 tmp.x = hud_fixscoreboardcolumnwidth_len;
821 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
823 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
827 if(hud_field_icon0 != "")
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_self, DRAWFLAG_NORMAL);
831 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);
832 if(hud_field_icon1 != "")
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_self, DRAWFLAG_NORMAL);
836 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);
837 if(hud_field_icon2 != "")
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_self, DRAWFLAG_NORMAL);
841 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);
842 pos.x -= hud_size[i] + hud_fontsize.x;
847 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
851 * HUD_Scoreboard_MakeTable
853 * Makes a table for a team (for all playing players in DM) and fills it
856 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
858 float body_table_height;
859 vector tmp = '0 0 0', column_dim = '0 0 0';
862 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
864 pos.y += autocvar_scoreboard_border_thickness;
868 tmp.y = 1.25 * hud_fontsize.y;
872 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
874 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
877 tmp.y += autocvar_scoreboard_border_thickness;
878 tmp.y += body_table_height;
879 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
881 // separator header/table
882 pos.y += 1.25 * hud_fontsize.y;
883 tmp.y = autocvar_scoreboard_border_thickness;
884 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
886 pos.y += autocvar_scoreboard_border_thickness;
889 tmp.y = body_table_height;
891 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
893 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
895 // anyway, apply some color
896 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
898 // go back to the top to make alternated columns highlighting and to print the strings
899 pos.y -= 1.25 * hud_fontsize.y;
900 pos.y -= autocvar_scoreboard_border_thickness;
904 if (scoreboard_highlight)
906 column_dim.y = 1.25 * hud_fontsize.y; // header
907 column_dim.y += autocvar_scoreboard_border_thickness;
908 column_dim.y += body_table_height;
911 // print the strings of the columns headers and draw the columns
913 for(i = 0; i < hud_num_fields; ++i)
915 if(hud_field[i] == SP_SEPARATOR)
917 column_dim.x = hud_size[i] + hud_fontsize.x;
918 if (scoreboard_highlight)
921 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
923 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
924 pos.x += column_dim.x;
926 if(hud_field[i] == SP_SEPARATOR)
930 for(i = hud_num_fields-1; i > 0; --i)
932 if(hud_field[i] == SP_SEPARATOR)
935 pos.x -= hud_size[i];
937 if (scoreboard_highlight)
941 if (i == hud_num_fields-1)
942 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
944 column_dim.x = hud_size[i] + hud_fontsize.x;
945 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
949 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
950 tmp.x = (hud_size[i] - tmp.x);
951 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
952 pos.x -= hud_fontsize.x;
957 pos.y += 1.25 * hud_fontsize.y; // skip the header
958 pos.y += autocvar_scoreboard_border_thickness;
962 tmp.y = hud_fontsize.y * 1.25;
964 // fill the table and draw the rows
967 for(pl = players.sort_next; pl; pl = pl.sort_next)
969 if(pl.team != tm.team)
971 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
972 pos.y += 1.25 * hud_fontsize.y;
976 for(pl = players.sort_next; pl; pl = pl.sort_next)
978 if(pl.team == NUM_SPECTATOR)
980 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
981 pos.y += 1.25 * hud_fontsize.y;
986 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
987 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
992 float HUD_WouldDrawScoreboard() {
993 if (autocvar__hud_configure)
995 else if (QuickMenu_IsOpened())
997 else if (HUD_Radar_Clickable())
999 else if (scoreboard_showscores)
1001 else if (intermission == 1)
1003 else if (intermission == 2)
1005 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
1007 else if (scoreboard_showscores_force)
1012 float average_accuracy;
1013 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
1015 WepSet weapons_stat = WepSet_GetFromStat();
1016 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1017 float initial_posx = pos.x;
1018 int disownedcnt = 0;
1019 FOREACH(Weapons, it != WEP_Null, {
1020 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1022 WepSet set = it.m_wepset;
1023 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1027 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1028 if (weapon_cnt <= 0) return pos;
1031 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1033 int columnns = ceil(weapon_cnt / rows);
1036 float fontsize = height * 1/3;
1037 float weapon_height = height * 2/3;
1038 float weapon_width = sbwidth / columnns / rows;
1040 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1041 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1042 vector tmp = '0 0 0';
1044 tmp.y = height * rows;
1047 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1049 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1050 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1052 // column highlighting
1053 for (int i = 0; i < columnns; ++i)
1056 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);
1060 for (int i = 0; i < rows; ++i)
1062 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);
1065 average_accuracy = 0;
1066 int weapons_with_stats = 0;
1068 pos.x += weapon_width / 2;
1070 if (autocvar_scoreboard_accuracy_nocolors)
1073 Accuracy_LoadColors();
1075 float oldposx = pos.x;
1079 FOREACH(Weapons, it != WEP_Null, {
1080 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1082 WepSet set = it.m_wepset;
1083 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1087 if (weapon_stats >= 0)
1088 weapon_alpha = scoreboard_alpha_fg;
1090 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1093 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1095 if (weapon_stats >= 0) {
1096 weapons_with_stats += 1;
1097 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1100 s = sprintf("%d%%", weapon_stats*100);
1103 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1105 if(!autocvar_scoreboard_accuracy_nocolors)
1106 rgb = Accuracy_GetColor(weapon_stats);
1108 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1110 tmpos.x += weapon_width * rows;
1111 pos.x += weapon_width * rows;
1112 if (rows == 2 && column == columnns - 1) {
1120 if (weapons_with_stats)
1121 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1124 pos.y += 1.25 * hud_fontsize.y;
1125 pos.x = initial_posx;
1129 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1131 pos.x += hud_fontsize.x * 0.25;
1132 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1133 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1134 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1136 pos.y+= hud_fontsize.y;
1141 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1142 float stat_secrets_found, stat_secrets_total;
1143 float stat_monsters_killed, stat_monsters_total;
1147 // get monster stats
1148 stat_monsters_killed = STAT(MONSTERS_KILLED);
1149 stat_monsters_total = STAT(MONSTERS_TOTAL);
1151 // get secrets stats
1152 stat_secrets_found = STAT(SECRETS_FOUND);
1153 stat_secrets_total = STAT(SECRETS_TOTAL);
1155 // get number of rows
1156 if(stat_secrets_total)
1158 if(stat_monsters_total)
1161 // if no rows, return
1165 // draw table header
1166 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1167 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1170 vector tmp = '0 0 0';
1172 tmp.y = hud_fontsize.y * rows;
1175 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1177 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1178 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1181 if(stat_monsters_total)
1183 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1184 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1188 if(stat_secrets_total)
1190 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1191 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1195 pos.y += 1.25 * hud_fontsize.y;
1200 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1203 RANKINGS_RECEIVED_CNT = 0;
1204 for (i=RANKINGS_CNT-1; i>=0; --i)
1206 ++RANKINGS_RECEIVED_CNT;
1208 if (RANKINGS_RECEIVED_CNT == 0)
1212 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
1214 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1215 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1216 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1218 pos.y += hud_fontsize.y;
1219 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1220 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1221 vector tmp = '0 0 0';
1223 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1226 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1228 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1229 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1232 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1239 n = grecordholder[i];
1240 p = count_ordinal(i+1);
1241 if(grecordholder[i] == entcs_GetName(player_localnum))
1242 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1243 else if(!(i % 2) && scoreboard_highlight)
1244 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1245 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1246 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);
1247 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1248 pos.y += 1.25 * hud_fontsize.y;
1250 pos.y += autocvar_scoreboard_border_thickness;
1255 float hud_woulddrawscoreboard_prev;
1256 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1257 void HUD_DrawScoreboard()
1259 float hud_woulddrawscoreboard;
1260 hud_woulddrawscoreboard = scoreboard_active;
1261 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1262 hud_woulddrawscoreboard_change = time;
1263 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1266 if(hud_woulddrawscoreboard) {
1267 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1268 if (scoreboard_fadeinspeed)
1269 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1271 scoreboard_fade_alpha = 1;
1274 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1275 if (scoreboard_fadeoutspeed)
1276 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1278 scoreboard_fade_alpha = 0;
1281 if (!scoreboard_fade_alpha)
1284 if (autocvar_scoreboard_dynamichud)
1287 HUD_Scale_Disable();
1289 HUD_UpdatePlayerTeams();
1291 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1292 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1293 scoreboard_highlight = autocvar_scoreboard_highlight;
1294 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1295 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1296 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1297 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1299 vector rgb, pos, tmp;
1303 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1304 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1306 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1307 ymax = (vid_conheight - ymin);
1309 sbwidth = xmax - xmin;
1311 // Initializes position
1317 vector sb_heading_fontsize;
1318 sb_heading_fontsize = hud_fontsize * 2;
1319 draw_beginBoldFont();
1320 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1323 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1325 // Draw the scoreboard
1326 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * ((autocvar_scoreboard_bg_scale > 0) ? autocvar_scoreboard_bg_scale : 0.25);
1330 vector team_score_baseoffset;
1331 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1332 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1334 if(tm.team == NUM_SPECTATOR)
1336 if(!tm.team && teamplay)
1339 draw_beginBoldFont();
1340 rgb = Team_ColorRGB(tm.team);
1341 str = ftos(tm.(teamscores(ts_primary)));
1342 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1344 if(ts_primary != ts_secondary)
1346 str = ftos(tm.(teamscores(ts_secondary)));
1347 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);
1351 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1353 rgb.x = autocvar_scoreboard_color_bg_r;
1354 rgb.y = autocvar_scoreboard_color_bg_g;
1355 rgb.z = autocvar_scoreboard_color_bg_b;
1359 rgb.x = autocvar_scoreboard_color_bg_r;
1360 rgb.y = autocvar_scoreboard_color_bg_g;
1361 rgb.z = autocvar_scoreboard_color_bg_b;
1363 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1365 if(tm.team == NUM_SPECTATOR)
1367 if(!tm.team && teamplay)
1370 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1374 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1375 if(race_speedaward) {
1376 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);
1377 pos.y += 1.25 * hud_fontsize.y;
1379 if(race_speedaward_alltimebest) {
1380 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);
1381 pos.y += 1.25 * hud_fontsize.y;
1383 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1385 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1387 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1389 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1394 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1396 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1403 item_size.x = sbwidth;
1404 item_size.y = hud_fontsize.y * 1.25;
1406 for(pl = players.sort_next; pl; pl = pl.sort_next)
1408 if(pl.team != NUM_SPECTATOR)
1410 pos.y += 1.25 * hud_fontsize.y;
1411 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1417 draw_beginBoldFont();
1418 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1420 pos.y += 1.25 * hud_fontsize.y;
1423 // Print info string
1425 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1426 tl = STAT(TIMELIMIT);
1427 fl = STAT(FRAGLIMIT);
1428 ll = STAT(LEADLIMIT);
1429 if(gametype == MAPINFO_TYPE_LMS)
1432 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1437 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1441 str = strcat(str, _(" or"));
1444 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), fl),
1445 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
1446 (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1447 TranslateScoresLabel(teamscores_label(ts_primary))));
1451 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags(ps_primary), fl),
1452 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
1453 (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1454 TranslateScoresLabel(scores_label(ps_primary))));
1459 if(tl > 0 || fl > 0)
1460 str = strcat(str, _(" or"));
1463 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), ll),
1464 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
1465 (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1466 TranslateScoresLabel(teamscores_label(ts_primary))));
1470 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags(ps_primary), ll),
1471 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
1472 (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1473 TranslateScoresLabel(scores_label(ps_primary))));
1478 pos.y += 1.2 * hud_fontsize.y;
1479 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1481 // print information about respawn status
1482 float respawn_time = STAT(RESPAWN_TIME);
1486 if(respawn_time < 0)
1488 // a negative number means we are awaiting respawn, time value is still the same
1489 respawn_time *= -1; // remove mark now that we checked it
1490 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1492 str = sprintf(_("^1Respawning in ^3%s^1..."),
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)
1502 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1503 (autocvar_scoreboard_respawntime_decimals ?
1504 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1506 count_seconds(respawn_time - time)
1510 else if(time >= respawn_time)
1511 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey(_("jump"), "+jump"));
1513 pos.y += 1.2 * hud_fontsize.y;
1514 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1517 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;