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,cts,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,rc,ka/suicides +ft,tdm/suicides -cts,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
307 "-rc,cts,nb/dmg -rc,cts,nb/dmgtaken " \
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 Weapon e = get_weaponinfo(i);
1007 if (!self.weapon) continue;
1009 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1011 WepSet set = e.m_wepset;
1012 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1016 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1017 if (weapon_cnt <= 0) return pos;
1020 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1022 int columnns = ceil(weapon_cnt / rows);
1025 float fontsize = height * 1/3;
1026 float weapon_height = height * 2/3;
1027 float weapon_width = sbwidth / columnns / rows;
1029 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1030 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1031 vector tmp = '0 0 0';
1033 tmp.y = height * rows;
1036 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1038 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1039 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1041 // column highlighting
1042 for (int i = 0; i < columnns; ++i)
1045 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);
1049 for (int i = 0; i < rows; ++i)
1051 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);
1054 average_accuracy = 0;
1055 int weapons_with_stats = 0;
1057 pos.x += weapon_width / 2;
1059 if (autocvar_scoreboard_accuracy_nocolors)
1062 Accuracy_LoadColors();
1064 float oldposx = pos.x;
1068 for (int i = WEP_FIRST; i <= WEP_LAST; ++i) {
1069 Weapon e = get_weaponinfo(i);
1071 if (!self.weapon) continue;
1072 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1074 WepSet set = e.m_wepset;
1075 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1079 if (weapon_stats >= 0)
1080 weapon_alpha = scoreboard_alpha_fg;
1082 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1085 drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1087 if (weapon_stats >= 0) {
1088 weapons_with_stats += 1;
1089 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1092 s = sprintf("%d%%", weapon_stats*100);
1095 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1097 if(!autocvar_scoreboard_accuracy_nocolors)
1098 rgb = Accuracy_GetColor(weapon_stats);
1100 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1102 tmpos.x += weapon_width * rows;
1103 pos.x += weapon_width * rows;
1104 if (rows == 2 && column == columnns - 1) {
1112 if (weapons_with_stats)
1113 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1116 pos.y += 1.25 * hud_fontsize.y;
1117 pos.x = initial_posx;
1121 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1123 pos.x += hud_fontsize.x * 0.25;
1124 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1125 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1126 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1128 pos.y+= hud_fontsize.y;
1133 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1134 float stat_secrets_found, stat_secrets_total;
1135 float stat_monsters_killed, stat_monsters_total;
1139 // get monster stats
1140 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1141 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1143 // get secrets stats
1144 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1145 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1147 // get number of rows
1148 if(stat_secrets_total)
1150 if(stat_monsters_total)
1153 // if no rows, return
1157 // draw table header
1158 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1159 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1162 vector tmp = '0 0 0';
1164 tmp.y = hud_fontsize.y * rows;
1167 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1169 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1170 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1173 if(stat_monsters_total)
1175 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1176 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1180 if(stat_secrets_total)
1182 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1183 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1187 pos.y += 1.25 * hud_fontsize.y;
1192 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1195 RANKINGS_RECEIVED_CNT = 0;
1196 for (i=RANKINGS_CNT-1; i>=0; --i)
1198 ++RANKINGS_RECEIVED_CNT;
1200 if (RANKINGS_RECEIVED_CNT == 0)
1204 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1206 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1207 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1208 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1210 pos.y += hud_fontsize.y;
1211 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1212 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1213 vector tmp = '0 0 0';
1215 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1218 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1220 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1221 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1224 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1231 n = grecordholder[i];
1232 p = count_ordinal(i+1);
1233 if(grecordholder[i] == GetPlayerName(player_localnum))
1234 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1235 else if(!(i % 2) && scoreboard_highlight)
1236 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1237 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1238 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);
1239 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1240 pos.y += 1.25 * hud_fontsize.y;
1242 pos.y += autocvar_scoreboard_border_thickness;
1247 float hud_woulddrawscoreboard_prev;
1248 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1249 void HUD_DrawScoreboard()
1251 float hud_woulddrawscoreboard;
1252 hud_woulddrawscoreboard = scoreboard_active;
1253 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1254 hud_woulddrawscoreboard_change = time;
1255 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1258 if(hud_woulddrawscoreboard) {
1259 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1260 if (scoreboard_fadeinspeed)
1261 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1263 scoreboard_fade_alpha = 1;
1266 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1267 if (scoreboard_fadeoutspeed)
1268 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1270 scoreboard_fade_alpha = 0;
1273 if (!scoreboard_fade_alpha)
1276 HUD_UpdatePlayerTeams();
1278 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1279 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1280 scoreboard_highlight = autocvar_scoreboard_highlight;
1281 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1282 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1283 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1284 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1286 vector rgb, pos, tmp;
1290 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1291 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1293 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1294 ymax = (vid_conheight - ymin);
1296 sbwidth = xmax - xmin;
1298 // Initializes position
1304 vector sb_heading_fontsize;
1305 sb_heading_fontsize = hud_fontsize * 2;
1306 draw_beginBoldFont();
1307 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1310 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1312 // Draw the scoreboard
1313 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1317 vector team_score_baseoffset;
1318 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1319 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1321 if(tm.team == NUM_SPECTATOR)
1324 draw_beginBoldFont();
1325 rgb = Team_ColorRGB(tm.team);
1326 str = ftos(tm.(teamscores[ts_primary]));
1327 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1329 if(ts_primary != ts_secondary)
1331 str = ftos(tm.(teamscores[ts_secondary]));
1332 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);
1336 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1338 rgb.x = autocvar_scoreboard_color_bg_r;
1339 rgb.y = autocvar_scoreboard_color_bg_g;
1340 rgb.z = autocvar_scoreboard_color_bg_b;
1344 rgb.x = autocvar_scoreboard_color_bg_r;
1345 rgb.y = autocvar_scoreboard_color_bg_g;
1346 rgb.z = autocvar_scoreboard_color_bg_b;
1348 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1350 if(tm.team == NUM_SPECTATOR)
1353 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1357 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1358 if(race_speedaward) {
1359 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1360 pos.y += 1.25 * hud_fontsize.y;
1362 if(race_speedaward_alltimebest) {
1363 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1364 pos.y += 1.25 * hud_fontsize.y;
1366 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1368 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1370 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1372 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1377 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1379 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1386 item_size.x = sbwidth;
1387 item_size.y = hud_fontsize.y * 1.25;
1389 for(pl = players.sort_next; pl; pl = pl.sort_next)
1391 if(pl.team != NUM_SPECTATOR)
1393 pos.y += 1.25 * hud_fontsize.y;
1394 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1400 draw_beginBoldFont();
1401 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1403 pos.y += 1.25 * hud_fontsize.y;
1406 // Print info string
1408 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1409 tl = getstatf(STAT_TIMELIMIT);
1410 fl = getstatf(STAT_FRAGLIMIT);
1411 ll = getstatf(STAT_LEADLIMIT);
1412 if(gametype == MAPINFO_TYPE_LMS)
1415 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1420 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1424 str = strcat(str, _(" or"));
1427 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1428 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1429 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1430 TranslateScoresLabel(teamscores_label[ts_primary])));
1434 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1435 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1436 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1437 TranslateScoresLabel(scores_label[ps_primary])));
1442 if(tl > 0 || fl > 0)
1443 str = strcat(str, _(" or"));
1446 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1447 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1448 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1449 TranslateScoresLabel(teamscores_label[ts_primary])));
1453 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1454 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1455 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1456 TranslateScoresLabel(scores_label[ps_primary])));
1461 pos.y += 1.2 * hud_fontsize.y;
1462 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1464 // print information about respawn status
1465 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1469 if(respawn_time < 0)
1471 // a negative number means we are awaiting respawn, time value is still the same
1472 respawn_time *= -1; // remove mark now that we checked it
1473 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1475 str = sprintf(_("^1Respawning in ^3%s^1..."),
1476 (autocvar_scoreboard_respawntime_decimals ?
1477 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1479 count_seconds(respawn_time - time)
1483 else if(time < respawn_time)
1485 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1486 (autocvar_scoreboard_respawntime_decimals ?
1487 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1489 count_seconds(respawn_time - time)
1493 else if(time >= respawn_time)
1494 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1496 pos.y += 1.2 * hud_fontsize.y;
1497 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1500 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;