1 #include "scoreboard.qh"
3 #include "quickmenu.qh"
6 #include "../common/constants.qh"
7 #include "../common/mapinfo.qh"
8 #include "../common/minigames/cl_minigames.qh"
9 #include "../common/stats.qh"
10 #include "../common/teams.qh"
12 float scoreboard_alpha_bg;
13 float scoreboard_alpha_fg;
14 float scoreboard_highlight;
15 float scoreboard_highlight_alpha;
16 float scoreboard_highlight_alpha_self;
17 float scoreboard_alpha_name;
18 float scoreboard_alpha_name_self;
20 void drawstringright(vector, string, vector, vector, float, float);
21 void drawstringcenter(vector, string, vector, vector, float, float);
23 const float SCOREBOARD_OFFSET = 50;
25 // wrapper to put all possible scores titles through gettext
26 string TranslateScoresLabel(string l)
30 case "bckills": return CTX(_("SCO^bckills"));
31 case "bctime": return CTX(_("SCO^bctime"));
32 case "caps": return CTX(_("SCO^caps"));
33 case "captime": return CTX(_("SCO^captime"));
34 case "deaths": return CTX(_("SCO^deaths"));
35 case "destroyed": return CTX(_("SCO^destroyed"));
36 case "dmg": return CTX(_("SCO^dmg"));
37 case "dmgtaken": return CTX(_("SCO^dmgtaken"));
38 case "drops": return CTX(_("SCO^drops"));
39 case "faults": return CTX(_("SCO^faults"));
40 case "fckills": return CTX(_("SCO^fckills"));
41 case "goals": return CTX(_("SCO^goals"));
42 case "kckills": return CTX(_("SCO^kckills"));
43 case "kdratio": return CTX(_("SCO^kdratio"));
44 case "k/d": return CTX(_("SCO^k/d"));
45 case "kd": return CTX(_("SCO^kd"));
46 case "kdr": return CTX(_("SCO^kdr"));
47 case "kills": return CTX(_("SCO^kills"));
48 case "laps": return CTX(_("SCO^laps"));
49 case "lives": return CTX(_("SCO^lives"));
50 case "losses": return CTX(_("SCO^losses"));
51 case "name": return CTX(_("SCO^name"));
52 case "sum": return CTX(_("SCO^sum"));
53 case "nick": return CTX(_("SCO^nick"));
54 case "objectives": return CTX(_("SCO^objectives"));
55 case "pickups": return CTX(_("SCO^pickups"));
56 case "ping": return CTX(_("SCO^ping"));
57 case "pl": return CTX(_("SCO^pl"));
58 case "pushes": return CTX(_("SCO^pushes"));
59 case "rank": return CTX(_("SCO^rank"));
60 case "returns": return CTX(_("SCO^returns"));
61 case "revivals": return CTX(_("SCO^revivals"));
62 case "score": return CTX(_("SCO^score"));
63 case "suicides": return CTX(_("SCO^suicides"));
64 case "takes": return CTX(_("SCO^takes"));
65 case "ticks": return CTX(_("SCO^ticks"));
74 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
75 for(i = 0; i < MAX_SCORE; ++i)
77 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
78 if(f == SFL_SORT_PRIO_PRIMARY)
80 if(f == SFL_SORT_PRIO_SECONDARY)
83 if(ps_secondary == -1)
84 ps_secondary = ps_primary;
86 for(i = 0; i < MAX_TEAMSCORE; ++i)
88 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
89 if(f == SFL_SORT_PRIO_PRIMARY)
91 if(f == SFL_SORT_PRIO_SECONDARY)
94 if(ts_secondary == -1)
95 ts_secondary = ts_primary;
100 float SetTeam(entity pl, float Team);
102 void HUD_UpdatePlayerTeams()
109 for(pl = players.sort_next; pl; pl = pl.sort_next)
112 Team = GetPlayerColor(pl.sv_entnum);
113 if(SetTeam(pl, Team))
116 HUD_UpdatePlayerPos(pl);
120 pl = players.sort_next;
125 print(strcat("PNUM: ", ftos(num), "\n"));
130 int HUD_CompareScore(float vl, float vr, int f)
132 if(f & SFL_ZERO_IS_WORST)
134 if(vl == 0 && vr != 0)
136 if(vl != 0 && vr == 0)
140 return IS_INCREASING(f);
142 return IS_DECREASING(f);
146 float HUD_ComparePlayerScores(entity left, entity right)
149 vl = GetPlayerColor(left.sv_entnum);
150 vr = GetPlayerColor(right.sv_entnum);
162 if(vl == NUM_SPECTATOR)
164 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
166 if(!left.gotscores && right.gotscores)
171 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
175 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
180 for(i = 0; i < MAX_SCORE; ++i)
182 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
187 if (left.sv_entnum < right.sv_entnum)
193 void HUD_UpdatePlayerPos(entity player)
195 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
197 SORT_SWAP(player, other);
199 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
201 SORT_SWAP(other, 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_SCORE; ++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)
237 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
239 SORT_SWAP(Team, other);
241 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
243 SORT_SWAP(other, Team);
249 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
250 LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
251 LOG_INFO(_("Usage:\n"));
252 LOG_INFO(_("^2scoreboard_columns_set default\n"));
253 LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
254 LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
255 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n\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(_("^3kills^7 Number of kills\n"));
261 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
262 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
263 LOG_INFO(_("^3frags^7 kills - suicides\n"));
264 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
265 LOG_INFO(_("^3dmg^7 The total damage done\n"));
266 LOG_INFO(_("^3dmgtaken^7 The total damage taken\n"));
267 LOG_INFO(_("^3sum^7 frags - deaths\n"));
268 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
269 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
270 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
271 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
272 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
273 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
274 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
275 LOG_INFO(_("^3rank^7 Player rank\n"));
276 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
277 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
278 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
279 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
280 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
281 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
282 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
283 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
284 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
285 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
286 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
287 LOG_INFO(_("^3score^7 Total score\n\n"));
289 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
290 "of game types, then a slash, to make the field show up only in these\n"
291 "or in all but these game types. You can also specify 'all' as a\n"
292 "field to show all fields available for the current game mode.\n\n"));
294 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
295 "include/exclude ALL teams/noteams game modes.\n\n"));
297 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
298 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
299 "right of the vertical bar aligned to the right.\n"));
300 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
301 "other gamemodes except DM.\n"));
304 #define HUD_DefaultColumnLayout() \
306 "-teams,rc,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,rc,ka/suicides +ft,tdm/suicides -rc,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
308 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \
309 "+lms/lives +lms/rank " \
310 "+kh/caps +kh/pushes +kh/destroyed " \
311 "?+rc/laps ?+rc/time ?+rc/fastest " \
312 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
315 void Cmd_HUD_SetFields(float argc)
319 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
324 // set up a temporary scoreboard layout
325 // no layout can be properly set up until score_info data haven't been received
326 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
328 scores_label[ps_primary] = strzone("score");
329 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
332 // TODO: re enable with gametype dependant cvars?
333 if(argc < 3) // no arguments provided
334 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
337 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
341 if(argv(2) == "default")
342 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
343 else if(argv(2) == "all")
346 s = "ping pl name |";
347 for(i = 0; i < MAX_SCORE; ++i)
350 if(i != ps_secondary)
351 if(scores_label[i] != "")
352 s = strcat(s, " ", scores_label[i]);
354 if(ps_secondary != ps_primary)
355 s = strcat(s, " ", scores_label[ps_secondary]);
356 s = strcat(s, " ", scores_label[ps_primary]);
357 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
364 hud_fontsize = HUD_GetFontsize("hud_fontsize");
366 for(i = 1; i < argc - 1; ++i)
372 if(substring(str, 0, 1) == "?")
375 str = substring(str, 1, strlen(str) - 1);
378 slash = strstrofs(str, "/", 0);
381 pattern = substring(str, 0, slash);
382 str = substring(str, slash + 1, strlen(str) - (slash + 1));
384 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
388 strunzone(hud_title[hud_num_fields]);
389 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
390 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
391 str = strtolower(str);
395 case "ping": hud_field[hud_num_fields] = SP_PING; break;
396 case "pl": hud_field[hud_num_fields] = SP_PL; break;
397 case "pl": hud_field[hud_num_fields] = SP_PL; break;
398 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
399 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
400 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
401 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
402 case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
403 case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
406 for(j = 0; j < MAX_SCORE; ++j)
407 if(str == strtolower(scores_label[j]))
408 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
416 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
420 hud_field[hud_num_fields] = j;
423 if(j == ps_secondary)
429 if(hud_num_fields >= MAX_HUD_FIELDS)
433 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
435 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
437 if(ps_primary == ps_secondary)
439 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
441 if(hud_num_fields+missing < MAX_HUD_FIELDS)
445 strunzone(hud_title[hud_num_fields]);
446 for(i = hud_num_fields; i > 0; --i)
448 hud_title[i] = hud_title[i-1];
449 hud_size[i] = hud_size[i-1];
450 hud_field[i] = hud_field[i-1];
452 hud_title[0] = strzone(TranslateScoresLabel("name"));
453 hud_field[0] = SP_NAME;
455 LOG_INFO("fixed missing field 'name'\n");
459 strunzone(hud_title[hud_num_fields]);
460 for(i = hud_num_fields; i > 1; --i)
462 hud_title[i] = hud_title[i-1];
463 hud_size[i] = hud_size[i-1];
464 hud_field[i] = hud_field[i-1];
466 hud_title[1] = strzone("|");
467 hud_field[1] = SP_SEPARATOR;
468 hud_size[1] = stringwidth("|", false, hud_fontsize);
470 LOG_INFO("fixed missing field '|'\n");
473 else if(!have_separator)
475 strunzone(hud_title[hud_num_fields]);
476 hud_title[hud_num_fields] = strzone("|");
477 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
478 hud_field[hud_num_fields] = SP_SEPARATOR;
480 LOG_INFO("fixed missing field '|'\n");
484 strunzone(hud_title[hud_num_fields]);
485 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
486 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
487 hud_field[hud_num_fields] = ps_secondary;
489 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_secondary]);
493 strunzone(hud_title[hud_num_fields]);
494 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
495 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
496 hud_field[hud_num_fields] = ps_primary;
498 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_primary]);
502 hud_field[hud_num_fields] = SP_END;
506 vector hud_field_rgb;
507 string hud_field_icon0;
508 string hud_field_icon1;
509 string hud_field_icon2;
510 vector hud_field_icon0_rgb;
511 vector hud_field_icon1_rgb;
512 vector hud_field_icon2_rgb;
513 float hud_field_icon0_alpha;
514 float hud_field_icon1_alpha;
515 float hud_field_icon2_alpha;
516 string HUD_GetField(entity pl, int field)
518 float tmp, num, denom;
521 hud_field_rgb = '1 1 1';
522 hud_field_icon0 = "";
523 hud_field_icon1 = "";
524 hud_field_icon2 = "";
525 hud_field_icon0_rgb = '1 1 1';
526 hud_field_icon1_rgb = '1 1 1';
527 hud_field_icon2_rgb = '1 1 1';
528 hud_field_icon0_alpha = 1;
529 hud_field_icon1_alpha = 1;
530 hud_field_icon2_alpha = 1;
535 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
536 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
540 tmp = max(0, min(220, f-80)) / 220;
541 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
547 f = pl.ping_packetloss;
548 tmp = pl.ping_movementloss;
549 if(f == 0 && tmp == 0)
551 str = ftos(ceil(f * 100));
553 str = strcat(str, "~", ftos(ceil(tmp * 100)));
554 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
555 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
559 if(ready_waiting && pl.ready)
561 hud_field_icon0 = "gfx/scoreboard/player_ready";
565 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
567 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
568 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
569 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
570 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
571 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
574 return GetPlayerName(pl.sv_entnum);
577 f = pl.(scores[SP_KILLS]);
578 f -= pl.(scores[SP_SUICIDES]);
582 num = pl.(scores[SP_KILLS]);
583 denom = pl.(scores[SP_DEATHS]);
586 hud_field_rgb = '0 1 0';
587 str = sprintf("%d", num);
588 } else if(num <= 0) {
589 hud_field_rgb = '1 0 0';
590 str = sprintf("%.1f", num/denom);
592 str = sprintf("%.1f", num/denom);
596 f = pl.(scores[SP_KILLS]);
597 f -= pl.(scores[SP_DEATHS]);
600 hud_field_rgb = '0 1 0';
602 hud_field_rgb = '1 1 1';
604 hud_field_rgb = '1 0 0';
609 num = pl.(scores[SP_DMG]);
612 str = sprintf("%.1f k", num/denom);
616 num = pl.(scores[SP_DMGTAKEN]);
619 str = sprintf("%.1f k", num/denom);
623 tmp = pl.(scores[field]);
624 f = scores_flags[field];
625 if(field == ps_primary)
626 hud_field_rgb = '1 1 0';
627 else if(field == ps_secondary)
628 hud_field_rgb = '0 1 1';
630 hud_field_rgb = '1 1 1';
631 return ScoreString(f, tmp);
636 float hud_fixscoreboardcolumnwidth_len;
637 float hud_fixscoreboardcolumnwidth_iconlen;
638 float hud_fixscoreboardcolumnwidth_marginlen;
640 string HUD_FixScoreboardColumnWidth(int i, string str)
644 field = hud_field[i];
646 hud_fixscoreboardcolumnwidth_iconlen = 0;
648 if(hud_field_icon0 != "")
650 sz = draw_getimagesize(hud_field_icon0);
652 if(hud_fixscoreboardcolumnwidth_iconlen < f)
653 hud_fixscoreboardcolumnwidth_iconlen = f;
656 if(hud_field_icon1 != "")
658 sz = draw_getimagesize(hud_field_icon1);
660 if(hud_fixscoreboardcolumnwidth_iconlen < f)
661 hud_fixscoreboardcolumnwidth_iconlen = f;
664 if(hud_field_icon2 != "")
666 sz = draw_getimagesize(hud_field_icon2);
668 if(hud_fixscoreboardcolumnwidth_iconlen < f)
669 hud_fixscoreboardcolumnwidth_iconlen = f;
672 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
674 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
675 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
677 hud_fixscoreboardcolumnwidth_marginlen = 0;
679 if(field == SP_NAME) // name gets all remaining space
683 namesize = sbwidth;// / hud_fontsize_x;
684 for(j = 0; j < hud_num_fields; ++j)
686 if (hud_field[i] != SP_SEPARATOR)
687 namesize -= hud_size[j] + hud_fontsize.x;
688 namesize += hud_fontsize.x;
689 hud_size[i] = namesize;
691 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
692 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
693 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
694 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
697 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
699 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
706 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
709 rgb = Team_ColorRGB(pl.team);
713 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
715 if((rgb == '1 1 1') && (!is_spec)) {
716 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
717 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
718 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
720 vector h_pos = pos - '1 1 0';
721 vector h_size = item_size + '2 0 0';
722 // alternated rows highlighting
724 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
725 else if((scoreboard_highlight) && (!(pl_number % 2)))
726 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
732 for(i = 0; i < hud_num_fields; ++i)
734 field = hud_field[i];
735 if(field == SP_SEPARATOR)
738 if(is_spec && field != SP_NAME && field != SP_PING) {
739 pos.x += hud_size[i] + hud_fontsize.x;
742 str = HUD_GetField(pl, field);
743 str = HUD_FixScoreboardColumnWidth(i, str);
745 pos.x += hud_size[i] + hud_fontsize.x;
747 if(field == SP_NAME) {
748 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
750 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
752 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
754 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
756 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
758 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
761 tmp.x = hud_size[i] + hud_fontsize.x;
762 if(hud_field_icon0 != "")
764 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);
766 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);
767 if(hud_field_icon1 != "")
769 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);
771 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);
772 if(hud_field_icon2 != "")
774 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);
776 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);
779 if(hud_field[i] == SP_SEPARATOR)
782 for(i = hud_num_fields-1; i > 0; --i)
784 field = hud_field[i];
785 if(field == SP_SEPARATOR)
788 if(is_spec && field != SP_NAME && field != SP_PING) {
789 pos.x -= hud_size[i] + hud_fontsize.x;
793 str = HUD_GetField(pl, field);
794 str = HUD_FixScoreboardColumnWidth(i, str);
796 if(field == SP_NAME) {
797 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
799 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
801 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
803 tmp.x = hud_fixscoreboardcolumnwidth_len;
805 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
807 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
811 if(hud_field_icon0 != "")
813 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);
815 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);
816 if(hud_field_icon1 != "")
818 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);
820 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);
821 if(hud_field_icon2 != "")
823 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);
825 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);
826 pos.x -= hud_size[i] + hud_fontsize.x;
831 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
835 * HUD_Scoreboard_MakeTable
837 * Makes a table for a team (for all playing players in DM) and fills it
840 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
842 float body_table_height;
843 vector tmp = '0 0 0', column_dim = '0 0 0';
846 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
848 pos.y += autocvar_scoreboard_border_thickness;
852 tmp.y = 1.25 * hud_fontsize.y;
856 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
858 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
861 tmp.y += autocvar_scoreboard_border_thickness;
862 tmp.y += body_table_height;
863 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
865 // separator header/table
866 pos.y += 1.25 * hud_fontsize.y;
867 tmp.y = autocvar_scoreboard_border_thickness;
868 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
870 pos.y += autocvar_scoreboard_border_thickness;
873 tmp.y = body_table_height;
875 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
877 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
879 // anyway, apply some color
880 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
882 // go back to the top to make alternated columns highlighting and to print the strings
883 pos.y -= 1.25 * hud_fontsize.y;
884 pos.y -= autocvar_scoreboard_border_thickness;
888 if (scoreboard_highlight)
890 column_dim.y = 1.25 * hud_fontsize.y; // header
891 column_dim.y += autocvar_scoreboard_border_thickness;
892 column_dim.y += body_table_height;
895 // print the strings of the columns headers and draw the columns
897 for(i = 0; i < hud_num_fields; ++i)
899 if(hud_field[i] == SP_SEPARATOR)
901 column_dim.x = hud_size[i] + hud_fontsize.x;
902 if (scoreboard_highlight)
905 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
907 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
908 pos.x += column_dim.x;
910 if(hud_field[i] == SP_SEPARATOR)
914 for(i = hud_num_fields-1; i > 0; --i)
916 if(hud_field[i] == SP_SEPARATOR)
919 pos.x -= hud_size[i];
921 if (scoreboard_highlight)
925 if (i == hud_num_fields-1)
926 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
928 column_dim.x = hud_size[i] + hud_fontsize.x;
929 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
933 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
934 tmp.x = (hud_size[i] - tmp.x);
935 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
936 pos.x -= hud_fontsize.x;
941 pos.y += 1.25 * hud_fontsize.y; // skip the header
942 pos.y += autocvar_scoreboard_border_thickness;
946 tmp.y = hud_fontsize.y * 1.25;
948 // fill the table and draw the rows
951 for(pl = players.sort_next; pl; pl = pl.sort_next)
953 if(pl.team != tm.team)
955 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
956 pos.y += 1.25 * hud_fontsize.y;
960 for(pl = players.sort_next; pl; pl = pl.sort_next)
962 if(pl.team == NUM_SPECTATOR)
964 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
965 pos.y += 1.25 * hud_fontsize.y;
970 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
971 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
976 float HUD_WouldDrawScoreboard() {
977 if (autocvar__hud_configure)
979 else if (QuickMenu_IsOpened())
981 else if (HUD_Radar_Clickable())
983 else if (scoreboard_showscores)
985 else if (intermission == 1)
987 else if (intermission == 2)
989 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
991 else if (scoreboard_showscores_force)
996 float average_accuracy;
997 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
1000 WepSet weapons_stat = WepSet_GetFromStat();
1001 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1002 float initial_posx = pos.x;
1003 int disownedcnt = 0;
1004 for (int i = WEP_FIRST; i <= WEP_LAST; ++i) {
1005 setself(get_weaponinfo(i));
1006 if (!self.weapon) continue;
1008 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1010 if (weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1014 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1015 if (weapon_cnt <= 0) return pos;
1018 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1020 int columnns = ceil(weapon_cnt / rows);
1023 float fontsize = height * 1/3;
1024 float weapon_height = height * 2/3;
1025 float weapon_width = sbwidth / columnns / rows;
1027 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1028 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1029 vector tmp = '0 0 0';
1031 tmp.y = height * rows;
1034 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1036 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1037 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1039 // column highlighting
1040 for (int i = 0; i < columnns; ++i)
1043 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);
1047 for (int i = 0; i < rows; ++i)
1049 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);
1052 average_accuracy = 0;
1053 int weapons_with_stats = 0;
1055 pos.x += weapon_width / 2;
1057 if (autocvar_scoreboard_accuracy_nocolors)
1060 Accuracy_LoadColors();
1062 float oldposx = pos.x;
1066 for (int i = WEP_FIRST; i <= WEP_LAST; ++i) {
1067 setself(get_weaponinfo(i));
1068 if (!self.weapon) continue;
1069 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1071 if (weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1075 if (weapon_stats >= 0)
1076 weapon_alpha = scoreboard_alpha_fg;
1078 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1081 drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1083 if (weapon_stats >= 0) {
1084 weapons_with_stats += 1;
1085 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1088 s = sprintf("%d%%", weapon_stats*100);
1091 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1093 if(!autocvar_scoreboard_accuracy_nocolors)
1094 rgb = Accuracy_GetColor(weapon_stats);
1096 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1098 tmpos.x += weapon_width * rows;
1099 pos.x += weapon_width * rows;
1100 if (rows == 2 && column == columnns - 1) {
1108 if (weapons_with_stats)
1109 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1112 pos.y += 1.25 * hud_fontsize.y;
1113 pos.x = initial_posx;
1117 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1119 pos.x += hud_fontsize.x * 0.25;
1120 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1121 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1122 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1124 pos.y+= hud_fontsize.y;
1129 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1130 float stat_secrets_found, stat_secrets_total;
1131 float stat_monsters_killed, stat_monsters_total;
1135 // get monster stats
1136 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1137 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1139 // get secrets stats
1140 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1141 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1143 // get number of rows
1144 if(stat_secrets_total)
1146 if(stat_monsters_total)
1149 // if no rows, return
1153 // draw table header
1154 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1155 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1158 vector tmp = '0 0 0';
1160 tmp.y = hud_fontsize.y * rows;
1163 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1165 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1166 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1169 if(stat_monsters_total)
1171 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1172 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1176 if(stat_secrets_total)
1178 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1179 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1183 pos.y += 1.25 * hud_fontsize.y;
1188 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1191 RANKINGS_RECEIVED_CNT = 0;
1192 for (i=RANKINGS_CNT-1; i>=0; --i)
1194 ++RANKINGS_RECEIVED_CNT;
1196 if (RANKINGS_RECEIVED_CNT == 0)
1200 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1202 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1203 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1204 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1206 pos.y += hud_fontsize.y;
1207 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1208 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1209 vector tmp = '0 0 0';
1211 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1214 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1216 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1217 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1220 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1227 n = grecordholder[i];
1228 p = count_ordinal(i+1);
1229 if(grecordholder[i] == GetPlayerName(player_localnum))
1230 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1231 else if(!(i % 2) && scoreboard_highlight)
1232 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1233 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1234 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);
1235 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1236 pos.y += 1.25 * hud_fontsize.y;
1238 pos.y += autocvar_scoreboard_border_thickness;
1243 float hud_woulddrawscoreboard_prev;
1244 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1245 void HUD_DrawScoreboard()
1247 float hud_woulddrawscoreboard;
1248 hud_woulddrawscoreboard = scoreboard_active;
1249 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1250 hud_woulddrawscoreboard_change = time;
1251 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1254 if(hud_woulddrawscoreboard) {
1255 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1256 if (scoreboard_fadeinspeed)
1257 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1259 scoreboard_fade_alpha = 1;
1262 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1263 if (scoreboard_fadeoutspeed)
1264 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1266 scoreboard_fade_alpha = 0;
1269 if (!scoreboard_fade_alpha)
1272 HUD_UpdatePlayerTeams();
1274 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1275 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1276 scoreboard_highlight = autocvar_scoreboard_highlight;
1277 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1278 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1279 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1280 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1282 vector rgb, pos, tmp;
1286 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1287 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1289 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1290 ymax = (vid_conheight - ymin);
1292 sbwidth = xmax - xmin;
1294 // Initializes position
1300 vector sb_heading_fontsize;
1301 sb_heading_fontsize = hud_fontsize * 2;
1302 draw_beginBoldFont();
1303 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1306 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1308 // Draw the scoreboard
1309 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1313 vector team_score_baseoffset;
1314 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1315 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1317 if(tm.team == NUM_SPECTATOR)
1320 draw_beginBoldFont();
1321 rgb = Team_ColorRGB(tm.team);
1322 str = ftos(tm.(teamscores[ts_primary]));
1323 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1325 if(ts_primary != ts_secondary)
1327 str = ftos(tm.(teamscores[ts_secondary]));
1328 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);
1332 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1334 rgb.x = autocvar_scoreboard_color_bg_r;
1335 rgb.y = autocvar_scoreboard_color_bg_g;
1336 rgb.z = autocvar_scoreboard_color_bg_b;
1340 rgb.x = autocvar_scoreboard_color_bg_r;
1341 rgb.y = autocvar_scoreboard_color_bg_g;
1342 rgb.z = autocvar_scoreboard_color_bg_b;
1344 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1346 if(tm.team == NUM_SPECTATOR)
1349 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1353 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1354 if(race_speedaward) {
1355 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1356 pos.y += 1.25 * hud_fontsize.y;
1358 if(race_speedaward_alltimebest) {
1359 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1360 pos.y += 1.25 * hud_fontsize.y;
1362 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1364 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1366 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1368 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1373 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1375 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1382 item_size.x = sbwidth;
1383 item_size.y = hud_fontsize.y * 1.25;
1385 for(pl = players.sort_next; pl; pl = pl.sort_next)
1387 if(pl.team != NUM_SPECTATOR)
1389 pos.y += 1.25 * hud_fontsize.y;
1390 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1396 draw_beginBoldFont();
1397 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1399 pos.y += 1.25 * hud_fontsize.y;
1402 // Print info string
1404 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1405 tl = getstatf(STAT_TIMELIMIT);
1406 fl = getstatf(STAT_FRAGLIMIT);
1407 ll = STAT(LEADLIMIT);
1408 if(gametype == MAPINFO_TYPE_LMS)
1411 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1416 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1420 str = strcat(str, _(" or"));
1423 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1424 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1425 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1426 TranslateScoresLabel(teamscores_label[ts_primary])));
1430 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1431 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1432 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1433 TranslateScoresLabel(scores_label[ps_primary])));
1438 if(tl > 0 || fl > 0)
1439 str = strcat(str, _(" or"));
1442 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
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 a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
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 pos.y += 1.2 * hud_fontsize.y;
1458 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1460 // print information about respawn status
1461 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1465 if(respawn_time < 0)
1467 // a negative number means we are awaiting respawn, time value is still the same
1468 respawn_time *= -1; // remove mark now that we checked it
1469 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1471 str = sprintf(_("^1Respawning in ^3%s^1..."),
1472 (autocvar_scoreboard_respawntime_decimals ?
1473 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1475 count_seconds(respawn_time - time)
1479 else if(time < respawn_time)
1481 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
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)
1490 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1492 pos.y += 1.2 * hud_fontsize.y;
1493 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1496 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;