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 = ts_primary = ts_secondary = -1;
76 for(i = 0; i < MAX_SCORE; ++i)
78 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
79 if(f == SFL_SORT_PRIO_PRIMARY)
81 if(f == SFL_SORT_PRIO_SECONDARY)
84 if(ps_secondary == -1)
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]);
182 for(i = 0; i < MAX_SCORE; ++i)
184 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
189 if (left.sv_entnum < right.sv_entnum)
195 void HUD_UpdatePlayerPos(entity player)
197 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
199 SORT_SWAP(player, other);
201 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
203 SORT_SWAP(other, player);
207 float HUD_CompareTeamScores(entity left, entity right)
211 if(left.team == NUM_SPECTATOR)
213 if(right.team == NUM_SPECTATOR)
216 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
220 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
224 for(i = 0; i < MAX_SCORE; ++i)
226 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
231 if (left.team < right.team)
237 void HUD_UpdateTeamPos(entity Team)
239 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
241 SORT_SWAP(Team, other);
243 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
245 SORT_SWAP(other, Team);
251 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
252 LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
253 LOG_INFO(_("Usage:\n"));
254 LOG_INFO(_("^2scoreboard_columns_set default\n"));
255 LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
256 LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
257 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n\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(_("^3kills^7 Number of kills\n"));
263 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
264 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
265 LOG_INFO(_("^3frags^7 kills - suicides\n"));
266 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
267 LOG_INFO(_("^3dmg^7 The total damage done\n"));
268 LOG_INFO(_("^3dmgtaken^7 The total damage taken\n"));
269 LOG_INFO(_("^3sum^7 frags - deaths\n"));
270 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
271 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
272 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
273 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
274 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
275 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
276 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
277 LOG_INFO(_("^3rank^7 Player rank\n"));
278 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
279 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
280 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
281 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
282 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
283 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
284 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
285 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
286 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
287 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
288 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
289 LOG_INFO(_("^3score^7 Total score\n\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 HUD_DefaultColumnLayout() \
312 "-teams,rc,cts,inv,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides -cts,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
313 "?+rc,inv/kills ?+rc,inv/suicides " \
314 "-rc,cts,nb/dmg -rc,cts,nb/dmgtaken " \
315 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \
316 "+lms/lives +lms/rank " \
317 "+kh/caps +kh/pushes +kh/destroyed " \
318 "?+rc/laps ?+rc/time +rc,cts/fastest " \
319 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
320 "-lms,rc,cts,inv,nb/score"
322 void Cmd_HUD_SetFields(int argc)
327 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
332 // set up a temporary scoreboard layout
333 // no layout can be properly set up until score_info data haven't been received
334 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
336 scores_label[ps_primary] = strzone("score");
337 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
340 // TODO: re enable with gametype dependant cvars?
341 if(argc < 3) // no arguments provided
342 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
345 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
349 if(argv(2) == "default")
350 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
351 else if(argv(2) == "all")
354 s = "ping pl name |";
355 for(i = 0; i < MAX_SCORE; ++i)
358 if(i != ps_secondary)
359 if(scores_label[i] != "")
360 s = strcat(s, " ", scores_label[i]);
362 if(ps_secondary != ps_primary)
363 s = strcat(s, " ", scores_label[ps_secondary]);
364 s = strcat(s, " ", scores_label[ps_primary]);
365 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
372 hud_fontsize = HUD_GetFontsize("hud_fontsize");
374 for(i = 1; i < argc - 1; ++i)
380 if(substring(str, 0, 1) == "?")
383 str = substring(str, 1, strlen(str) - 1);
386 slash = strstrofs(str, "/", 0);
389 pattern = substring(str, 0, slash);
390 str = substring(str, slash + 1, strlen(str) - (slash + 1));
392 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
396 strunzone(hud_title[hud_num_fields]);
397 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
398 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
399 str = strtolower(str);
403 case "ping": hud_field[hud_num_fields] = SP_PING; break;
404 case "pl": hud_field[hud_num_fields] = SP_PL; break;
405 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
406 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
407 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
408 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
409 case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
410 case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
413 for(j = 0; j < MAX_SCORE; ++j)
414 if(str == strtolower(scores_label[j]))
415 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
423 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
427 hud_field[hud_num_fields] = j;
430 if(j == ps_secondary)
436 if(hud_num_fields >= MAX_HUD_FIELDS)
440 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
442 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
444 if(ps_primary == ps_secondary)
446 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
448 if(hud_num_fields+missing < MAX_HUD_FIELDS)
452 strunzone(hud_title[hud_num_fields]);
453 for(i = hud_num_fields; i > 0; --i)
455 hud_title[i] = hud_title[i-1];
456 hud_size[i] = hud_size[i-1];
457 hud_field[i] = hud_field[i-1];
459 hud_title[0] = strzone(TranslateScoresLabel("name"));
460 hud_field[0] = SP_NAME;
462 LOG_INFO("fixed missing field 'name'\n");
466 strunzone(hud_title[hud_num_fields]);
467 for(i = hud_num_fields; i > 1; --i)
469 hud_title[i] = hud_title[i-1];
470 hud_size[i] = hud_size[i-1];
471 hud_field[i] = hud_field[i-1];
473 hud_title[1] = strzone("|");
474 hud_field[1] = SP_SEPARATOR;
475 hud_size[1] = stringwidth("|", false, hud_fontsize);
477 LOG_INFO("fixed missing field '|'\n");
480 else if(!have_separator)
482 strunzone(hud_title[hud_num_fields]);
483 hud_title[hud_num_fields] = strzone("|");
484 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
485 hud_field[hud_num_fields] = SP_SEPARATOR;
487 LOG_INFO("fixed missing field '|'\n");
491 strunzone(hud_title[hud_num_fields]);
492 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
493 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
494 hud_field[hud_num_fields] = ps_secondary;
496 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_secondary]);
500 strunzone(hud_title[hud_num_fields]);
501 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
502 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
503 hud_field[hud_num_fields] = ps_primary;
505 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_primary]);
509 hud_field[hud_num_fields] = SP_END;
513 vector hud_field_rgb;
514 string hud_field_icon0;
515 string hud_field_icon1;
516 string hud_field_icon2;
517 vector hud_field_icon0_rgb;
518 vector hud_field_icon1_rgb;
519 vector hud_field_icon2_rgb;
520 float hud_field_icon0_alpha;
521 float hud_field_icon1_alpha;
522 float hud_field_icon2_alpha;
523 string HUD_GetField(entity pl, int field)
526 float tmp, num, denom;
529 hud_field_rgb = '1 1 1';
530 hud_field_icon0 = "";
531 hud_field_icon1 = "";
532 hud_field_icon2 = "";
533 hud_field_icon0_rgb = '1 1 1';
534 hud_field_icon1_rgb = '1 1 1';
535 hud_field_icon2_rgb = '1 1 1';
536 hud_field_icon0_alpha = 1;
537 hud_field_icon1_alpha = 1;
538 hud_field_icon2_alpha = 1;
543 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
544 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
548 tmp = max(0, min(220, f-80)) / 220;
549 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
555 f = pl.ping_packetloss;
556 tmp = pl.ping_movementloss;
557 if(f == 0 && tmp == 0)
559 str = ftos(ceil(f * 100));
561 str = strcat(str, "~", ftos(ceil(tmp * 100)));
562 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
563 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
567 if(ready_waiting && pl.ready)
569 hud_field_icon0 = "gfx/scoreboard/player_ready";
573 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
575 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
576 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
577 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
578 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
579 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
582 return entcs_GetName(pl.sv_entnum);
585 f = pl.(scores[SP_KILLS]);
586 f -= pl.(scores[SP_SUICIDES]);
590 num = pl.(scores[SP_KILLS]);
591 denom = pl.(scores[SP_DEATHS]);
594 hud_field_rgb = '0 1 0';
595 str = sprintf("%d", num);
596 } else if(num <= 0) {
597 hud_field_rgb = '1 0 0';
598 str = sprintf("%.1f", num/denom);
600 str = sprintf("%.1f", num/denom);
604 f = pl.(scores[SP_KILLS]);
605 f -= pl.(scores[SP_DEATHS]);
608 hud_field_rgb = '0 1 0';
610 hud_field_rgb = '1 1 1';
612 hud_field_rgb = '1 0 0';
617 num = pl.(scores[SP_DMG]);
620 str = sprintf("%.1f k", num/denom);
624 num = pl.(scores[SP_DMGTAKEN]);
627 str = sprintf("%.1f k", num/denom);
631 tmp = pl.(scores[field]);
632 f = scores_flags[field];
633 if(field == ps_primary)
634 hud_field_rgb = '1 1 0';
635 else if(field == ps_secondary)
636 hud_field_rgb = '0 1 1';
638 hud_field_rgb = '1 1 1';
639 return ScoreString(f, tmp);
644 float hud_fixscoreboardcolumnwidth_len;
645 float hud_fixscoreboardcolumnwidth_iconlen;
646 float hud_fixscoreboardcolumnwidth_marginlen;
648 string HUD_FixScoreboardColumnWidth(int i, string str)
653 field = hud_field[i];
655 hud_fixscoreboardcolumnwidth_iconlen = 0;
657 if(hud_field_icon0 != "")
659 sz = draw_getimagesize(hud_field_icon0);
661 if(hud_fixscoreboardcolumnwidth_iconlen < f)
662 hud_fixscoreboardcolumnwidth_iconlen = f;
665 if(hud_field_icon1 != "")
667 sz = draw_getimagesize(hud_field_icon1);
669 if(hud_fixscoreboardcolumnwidth_iconlen < f)
670 hud_fixscoreboardcolumnwidth_iconlen = f;
673 if(hud_field_icon2 != "")
675 sz = draw_getimagesize(hud_field_icon2);
677 if(hud_fixscoreboardcolumnwidth_iconlen < f)
678 hud_fixscoreboardcolumnwidth_iconlen = f;
681 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
683 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
684 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
686 hud_fixscoreboardcolumnwidth_marginlen = 0;
688 if(field == SP_NAME) // name gets all remaining space
692 namesize = sbwidth;// / hud_fontsize_x;
693 for(j = 0; j < hud_num_fields; ++j)
695 if (hud_field[i] != SP_SEPARATOR)
696 namesize -= hud_size[j] + hud_fontsize.x;
697 namesize += hud_fontsize.x;
698 hud_size[i] = namesize;
700 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
701 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
702 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
703 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
706 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
708 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
715 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, bool is_self, int pl_number)
717 TC(bool, is_self); TC(int, pl_number);
719 rgb = Team_ColorRGB(pl.team);
723 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
725 if((rgb == '1 1 1') && (!is_spec)) {
726 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
727 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
728 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
730 vector h_pos = pos - '1 1 0';
731 vector h_size = item_size + '2 0 0';
732 // alternated rows highlighting
734 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
735 else if((scoreboard_highlight) && (!(pl_number % 2)))
736 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
742 for(i = 0; i < hud_num_fields; ++i)
744 field = hud_field[i];
745 if(field == SP_SEPARATOR)
748 if(is_spec && field != SP_NAME && field != SP_PING) {
749 pos.x += hud_size[i] + hud_fontsize.x;
752 str = HUD_GetField(pl, field);
753 str = HUD_FixScoreboardColumnWidth(i, str);
755 pos.x += hud_size[i] + hud_fontsize.x;
757 if(field == SP_NAME) {
758 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
760 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
762 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
764 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
766 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
768 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
771 tmp.x = hud_size[i] + hud_fontsize.x;
772 if(hud_field_icon0 != "")
774 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);
776 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);
777 if(hud_field_icon1 != "")
779 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);
781 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);
782 if(hud_field_icon2 != "")
784 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);
786 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);
789 if(hud_field[i] == SP_SEPARATOR)
792 for(i = hud_num_fields-1; i > 0; --i)
794 field = hud_field[i];
795 if(field == SP_SEPARATOR)
798 if(is_spec && field != SP_NAME && field != SP_PING) {
799 pos.x -= hud_size[i] + hud_fontsize.x;
803 str = HUD_GetField(pl, field);
804 str = HUD_FixScoreboardColumnWidth(i, str);
806 if(field == SP_NAME) {
807 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
809 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
811 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
813 tmp.x = hud_fixscoreboardcolumnwidth_len;
815 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
817 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
821 if(hud_field_icon0 != "")
823 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);
825 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);
826 if(hud_field_icon1 != "")
828 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);
830 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);
831 if(hud_field_icon2 != "")
833 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);
835 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);
836 pos.x -= hud_size[i] + hud_fontsize.x;
841 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
845 * HUD_Scoreboard_MakeTable
847 * Makes a table for a team (for all playing players in DM) and fills it
850 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
852 float body_table_height;
853 vector tmp = '0 0 0', column_dim = '0 0 0';
856 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
858 pos.y += autocvar_scoreboard_border_thickness;
862 tmp.y = 1.25 * hud_fontsize.y;
866 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
868 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
871 tmp.y += autocvar_scoreboard_border_thickness;
872 tmp.y += body_table_height;
873 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
875 // separator header/table
876 pos.y += 1.25 * hud_fontsize.y;
877 tmp.y = autocvar_scoreboard_border_thickness;
878 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
880 pos.y += autocvar_scoreboard_border_thickness;
883 tmp.y = body_table_height;
885 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
887 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
889 // anyway, apply some color
890 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
892 // go back to the top to make alternated columns highlighting and to print the strings
893 pos.y -= 1.25 * hud_fontsize.y;
894 pos.y -= autocvar_scoreboard_border_thickness;
898 if (scoreboard_highlight)
900 column_dim.y = 1.25 * hud_fontsize.y; // header
901 column_dim.y += autocvar_scoreboard_border_thickness;
902 column_dim.y += body_table_height;
905 // print the strings of the columns headers and draw the columns
907 for(i = 0; i < hud_num_fields; ++i)
909 if(hud_field[i] == SP_SEPARATOR)
911 column_dim.x = hud_size[i] + hud_fontsize.x;
912 if (scoreboard_highlight)
915 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
917 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
918 pos.x += column_dim.x;
920 if(hud_field[i] == SP_SEPARATOR)
924 for(i = hud_num_fields-1; i > 0; --i)
926 if(hud_field[i] == SP_SEPARATOR)
929 pos.x -= hud_size[i];
931 if (scoreboard_highlight)
935 if (i == hud_num_fields-1)
936 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
938 column_dim.x = hud_size[i] + hud_fontsize.x;
939 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
943 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
944 tmp.x = (hud_size[i] - tmp.x);
945 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
946 pos.x -= hud_fontsize.x;
951 pos.y += 1.25 * hud_fontsize.y; // skip the header
952 pos.y += autocvar_scoreboard_border_thickness;
956 tmp.y = hud_fontsize.y * 1.25;
958 // fill the table and draw the rows
961 for(pl = players.sort_next; pl; pl = pl.sort_next)
963 if(pl.team != tm.team)
965 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
966 pos.y += 1.25 * hud_fontsize.y;
970 for(pl = players.sort_next; pl; pl = pl.sort_next)
972 if(pl.team == NUM_SPECTATOR)
974 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
975 pos.y += 1.25 * hud_fontsize.y;
980 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
981 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
986 float HUD_WouldDrawScoreboard() {
987 if (autocvar__hud_configure)
989 else if (QuickMenu_IsOpened())
991 else if (HUD_Radar_Clickable())
993 else if (scoreboard_showscores)
995 else if (intermission == 1)
997 else if (intermission == 2)
999 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
1001 else if (scoreboard_showscores_force)
1006 float average_accuracy;
1007 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
1009 WepSet weapons_stat = WepSet_GetFromStat();
1010 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1011 float initial_posx = pos.x;
1012 int disownedcnt = 0;
1013 FOREACH(Weapons, it != WEP_Null, {
1014 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1016 WepSet set = it.m_wepset;
1017 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1021 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1022 if (weapon_cnt <= 0) return pos;
1025 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1027 int columnns = ceil(weapon_cnt / rows);
1030 float fontsize = height * 1/3;
1031 float weapon_height = height * 2/3;
1032 float weapon_width = sbwidth / columnns / rows;
1034 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1035 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1036 vector tmp = '0 0 0';
1038 tmp.y = height * rows;
1041 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1043 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1044 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1046 // column highlighting
1047 for (int i = 0; i < columnns; ++i)
1050 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);
1054 for (int i = 0; i < rows; ++i)
1056 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);
1059 average_accuracy = 0;
1060 int weapons_with_stats = 0;
1062 pos.x += weapon_width / 2;
1064 if (autocvar_scoreboard_accuracy_nocolors)
1067 Accuracy_LoadColors();
1069 float oldposx = pos.x;
1073 FOREACH(Weapons, it != WEP_Null, {
1074 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1076 WepSet set = it.m_wepset;
1077 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1081 if (weapon_stats >= 0)
1082 weapon_alpha = scoreboard_alpha_fg;
1084 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1087 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1089 if (weapon_stats >= 0) {
1090 weapons_with_stats += 1;
1091 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1094 s = sprintf("%d%%", weapon_stats*100);
1097 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1099 if(!autocvar_scoreboard_accuracy_nocolors)
1100 rgb = Accuracy_GetColor(weapon_stats);
1102 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1104 tmpos.x += weapon_width * rows;
1105 pos.x += weapon_width * rows;
1106 if (rows == 2 && column == columnns - 1) {
1114 if (weapons_with_stats)
1115 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1118 pos.y += 1.25 * hud_fontsize.y;
1119 pos.x = initial_posx;
1123 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1125 pos.x += hud_fontsize.x * 0.25;
1126 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1127 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1128 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1130 pos.y+= hud_fontsize.y;
1135 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1136 float stat_secrets_found, stat_secrets_total;
1137 float stat_monsters_killed, stat_monsters_total;
1141 // get monster stats
1142 stat_monsters_killed = STAT(MONSTERS_KILLED);
1143 stat_monsters_total = STAT(MONSTERS_TOTAL);
1145 // get secrets stats
1146 stat_secrets_found = STAT(SECRETS_FOUND);
1147 stat_secrets_total = STAT(SECRETS_TOTAL);
1149 // get number of rows
1150 if(stat_secrets_total)
1152 if(stat_monsters_total)
1155 // if no rows, return
1159 // draw table header
1160 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1161 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1164 vector tmp = '0 0 0';
1166 tmp.y = hud_fontsize.y * rows;
1169 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1171 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1172 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1175 if(stat_monsters_total)
1177 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1178 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1182 if(stat_secrets_total)
1184 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1185 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1189 pos.y += 1.25 * hud_fontsize.y;
1194 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1197 RANKINGS_RECEIVED_CNT = 0;
1198 for (i=RANKINGS_CNT-1; i>=0; --i)
1200 ++RANKINGS_RECEIVED_CNT;
1202 if (RANKINGS_RECEIVED_CNT == 0)
1206 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
1208 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1209 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1210 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1212 pos.y += hud_fontsize.y;
1213 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1214 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1215 vector tmp = '0 0 0';
1217 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1220 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1222 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1223 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1226 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1233 n = grecordholder[i];
1234 p = count_ordinal(i+1);
1235 if(grecordholder[i] == entcs_GetName(player_localnum))
1236 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1237 else if(!(i % 2) && scoreboard_highlight)
1238 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1239 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1240 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);
1241 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1242 pos.y += 1.25 * hud_fontsize.y;
1244 pos.y += autocvar_scoreboard_border_thickness;
1249 float hud_woulddrawscoreboard_prev;
1250 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1251 void HUD_DrawScoreboard()
1253 float hud_woulddrawscoreboard;
1254 hud_woulddrawscoreboard = scoreboard_active;
1255 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1256 hud_woulddrawscoreboard_change = time;
1257 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1260 if(hud_woulddrawscoreboard) {
1261 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1262 if (scoreboard_fadeinspeed)
1263 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1265 scoreboard_fade_alpha = 1;
1268 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1269 if (scoreboard_fadeoutspeed)
1270 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1272 scoreboard_fade_alpha = 0;
1275 if (!scoreboard_fade_alpha)
1278 HUD_UpdatePlayerTeams();
1280 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1281 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1282 scoreboard_highlight = autocvar_scoreboard_highlight;
1283 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1284 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1285 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1286 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1288 vector rgb, pos, tmp;
1292 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1293 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1295 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1296 ymax = (vid_conheight - ymin);
1298 sbwidth = xmax - xmin;
1300 // Initializes position
1306 vector sb_heading_fontsize;
1307 sb_heading_fontsize = hud_fontsize * 2;
1308 draw_beginBoldFont();
1309 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1312 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1314 // Draw the scoreboard
1315 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * ((autocvar_scoreboard_bg_scale > 0) ? autocvar_scoreboard_bg_scale : 0.25);
1319 vector team_score_baseoffset;
1320 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1321 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1323 if(tm.team == NUM_SPECTATOR)
1325 if(!tm.team && teamplay)
1328 draw_beginBoldFont();
1329 rgb = Team_ColorRGB(tm.team);
1330 str = ftos(tm.(teamscores[ts_primary]));
1331 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1333 if(ts_primary != ts_secondary)
1335 str = ftos(tm.(teamscores[ts_secondary]));
1336 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);
1340 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1342 rgb.x = autocvar_scoreboard_color_bg_r;
1343 rgb.y = autocvar_scoreboard_color_bg_g;
1344 rgb.z = autocvar_scoreboard_color_bg_b;
1348 rgb.x = autocvar_scoreboard_color_bg_r;
1349 rgb.y = autocvar_scoreboard_color_bg_g;
1350 rgb.z = autocvar_scoreboard_color_bg_b;
1352 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1354 if(tm.team == NUM_SPECTATOR)
1356 if(!tm.team && teamplay)
1359 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1363 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1364 if(race_speedaward) {
1365 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1366 pos.y += 1.25 * hud_fontsize.y;
1368 if(race_speedaward_alltimebest) {
1369 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1370 pos.y += 1.25 * hud_fontsize.y;
1372 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1374 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1376 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1378 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1383 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1385 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1392 item_size.x = sbwidth;
1393 item_size.y = hud_fontsize.y * 1.25;
1395 for(pl = players.sort_next; pl; pl = pl.sort_next)
1397 if(pl.team != NUM_SPECTATOR)
1399 pos.y += 1.25 * hud_fontsize.y;
1400 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1406 draw_beginBoldFont();
1407 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1409 pos.y += 1.25 * hud_fontsize.y;
1412 // Print info string
1414 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1415 tl = STAT(TIMELIMIT);
1416 fl = STAT(FRAGLIMIT);
1417 ll = STAT(LEADLIMIT);
1418 if(gametype == MAPINFO_TYPE_LMS)
1421 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1426 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1430 str = strcat(str, _(" or"));
1433 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1434 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1435 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1436 TranslateScoresLabel(teamscores_label[ts_primary])));
1440 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1441 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1442 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1443 TranslateScoresLabel(scores_label[ps_primary])));
1448 if(tl > 0 || fl > 0)
1449 str = strcat(str, _(" or"));
1452 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1453 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1454 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1455 TranslateScoresLabel(teamscores_label[ts_primary])));
1459 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1460 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1461 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1462 TranslateScoresLabel(scores_label[ps_primary])));
1467 pos.y += 1.2 * hud_fontsize.y;
1468 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1470 // print information about respawn status
1471 float respawn_time = STAT(RESPAWN_TIME);
1475 if(respawn_time < 0)
1477 // a negative number means we are awaiting respawn, time value is still the same
1478 respawn_time *= -1; // remove mark now that we checked it
1479 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1481 str = sprintf(_("^1Respawning in ^3%s^1..."),
1482 (autocvar_scoreboard_respawntime_decimals ?
1483 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1485 count_seconds(respawn_time - time)
1489 else if(time < respawn_time)
1491 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1492 (autocvar_scoreboard_respawntime_decimals ?
1493 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1495 count_seconds(respawn_time - time)
1499 else if(time >= respawn_time)
1500 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1502 pos.y += 1.2 * hud_fontsize.y;
1503 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1506 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;