1 #include "scoreboard.qh"
3 #include "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 #define HUD_DefaultColumnLayout() \
308 "-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" */ \
309 "-rc,cts,nb/dmg -rc,cts,nb/dmgtaken " \
310 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \
311 "+lms/lives +lms/rank " \
312 "+kh/caps +kh/pushes +kh/destroyed " \
313 "?+rc/laps ?+rc/time ?+rc/fastest " \
314 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
317 void Cmd_HUD_SetFields(int argc)
322 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
327 // set up a temporary scoreboard layout
328 // no layout can be properly set up until score_info data haven't been received
329 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
331 scores_label[ps_primary] = strzone("score");
332 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
335 // TODO: re enable with gametype dependant cvars?
336 if(argc < 3) // no arguments provided
337 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
340 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
344 if(argv(2) == "default")
345 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
346 else if(argv(2) == "all")
349 s = "ping pl name |";
350 for(i = 0; i < MAX_SCORE; ++i)
353 if(i != ps_secondary)
354 if(scores_label[i] != "")
355 s = strcat(s, " ", scores_label[i]);
357 if(ps_secondary != ps_primary)
358 s = strcat(s, " ", scores_label[ps_secondary]);
359 s = strcat(s, " ", scores_label[ps_primary]);
360 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
367 hud_fontsize = HUD_GetFontsize("hud_fontsize");
369 for(i = 1; i < argc - 1; ++i)
375 if(substring(str, 0, 1) == "?")
378 str = substring(str, 1, strlen(str) - 1);
381 slash = strstrofs(str, "/", 0);
384 pattern = substring(str, 0, slash);
385 str = substring(str, slash + 1, strlen(str) - (slash + 1));
387 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
391 strunzone(hud_title[hud_num_fields]);
392 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
393 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
394 str = strtolower(str);
398 case "ping": hud_field[hud_num_fields] = SP_PING; break;
399 case "pl": hud_field[hud_num_fields] = SP_PL; break;
400 case "pl": hud_field[hud_num_fields] = SP_PL; break;
401 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
402 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
403 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
404 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
405 case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
406 case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
409 for(j = 0; j < MAX_SCORE; ++j)
410 if(str == strtolower(scores_label[j]))
411 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
419 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
423 hud_field[hud_num_fields] = j;
426 if(j == ps_secondary)
432 if(hud_num_fields >= MAX_HUD_FIELDS)
436 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
438 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
440 if(ps_primary == ps_secondary)
442 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
444 if(hud_num_fields+missing < MAX_HUD_FIELDS)
448 strunzone(hud_title[hud_num_fields]);
449 for(i = hud_num_fields; i > 0; --i)
451 hud_title[i] = hud_title[i-1];
452 hud_size[i] = hud_size[i-1];
453 hud_field[i] = hud_field[i-1];
455 hud_title[0] = strzone(TranslateScoresLabel("name"));
456 hud_field[0] = SP_NAME;
458 LOG_INFO("fixed missing field 'name'\n");
462 strunzone(hud_title[hud_num_fields]);
463 for(i = hud_num_fields; i > 1; --i)
465 hud_title[i] = hud_title[i-1];
466 hud_size[i] = hud_size[i-1];
467 hud_field[i] = hud_field[i-1];
469 hud_title[1] = strzone("|");
470 hud_field[1] = SP_SEPARATOR;
471 hud_size[1] = stringwidth("|", false, hud_fontsize);
473 LOG_INFO("fixed missing field '|'\n");
476 else if(!have_separator)
478 strunzone(hud_title[hud_num_fields]);
479 hud_title[hud_num_fields] = strzone("|");
480 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
481 hud_field[hud_num_fields] = SP_SEPARATOR;
483 LOG_INFO("fixed missing field '|'\n");
487 strunzone(hud_title[hud_num_fields]);
488 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
489 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
490 hud_field[hud_num_fields] = ps_secondary;
492 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_secondary]);
496 strunzone(hud_title[hud_num_fields]);
497 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
498 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
499 hud_field[hud_num_fields] = ps_primary;
501 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_primary]);
505 hud_field[hud_num_fields] = SP_END;
509 vector hud_field_rgb;
510 string hud_field_icon0;
511 string hud_field_icon1;
512 string hud_field_icon2;
513 vector hud_field_icon0_rgb;
514 vector hud_field_icon1_rgb;
515 vector hud_field_icon2_rgb;
516 float hud_field_icon0_alpha;
517 float hud_field_icon1_alpha;
518 float hud_field_icon2_alpha;
519 string HUD_GetField(entity pl, int field)
522 float tmp, num, denom;
525 hud_field_rgb = '1 1 1';
526 hud_field_icon0 = "";
527 hud_field_icon1 = "";
528 hud_field_icon2 = "";
529 hud_field_icon0_rgb = '1 1 1';
530 hud_field_icon1_rgb = '1 1 1';
531 hud_field_icon2_rgb = '1 1 1';
532 hud_field_icon0_alpha = 1;
533 hud_field_icon1_alpha = 1;
534 hud_field_icon2_alpha = 1;
539 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
540 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
544 tmp = max(0, min(220, f-80)) / 220;
545 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
551 f = pl.ping_packetloss;
552 tmp = pl.ping_movementloss;
553 if(f == 0 && tmp == 0)
555 str = ftos(ceil(f * 100));
557 str = strcat(str, "~", ftos(ceil(tmp * 100)));
558 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
559 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
563 if(ready_waiting && pl.ready)
565 hud_field_icon0 = "gfx/scoreboard/player_ready";
569 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
571 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
572 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
573 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
574 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
575 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
578 return entcs_GetName(pl.sv_entnum);
581 f = pl.(scores[SP_KILLS]);
582 f -= pl.(scores[SP_SUICIDES]);
586 num = pl.(scores[SP_KILLS]);
587 denom = pl.(scores[SP_DEATHS]);
590 hud_field_rgb = '0 1 0';
591 str = sprintf("%d", num);
592 } else if(num <= 0) {
593 hud_field_rgb = '1 0 0';
594 str = sprintf("%.1f", num/denom);
596 str = sprintf("%.1f", num/denom);
600 f = pl.(scores[SP_KILLS]);
601 f -= pl.(scores[SP_DEATHS]);
604 hud_field_rgb = '0 1 0';
606 hud_field_rgb = '1 1 1';
608 hud_field_rgb = '1 0 0';
613 num = pl.(scores[SP_DMG]);
616 str = sprintf("%.1f k", num/denom);
620 num = pl.(scores[SP_DMGTAKEN]);
623 str = sprintf("%.1f k", num/denom);
627 tmp = pl.(scores[field]);
628 f = scores_flags[field];
629 if(field == ps_primary)
630 hud_field_rgb = '1 1 0';
631 else if(field == ps_secondary)
632 hud_field_rgb = '0 1 1';
634 hud_field_rgb = '1 1 1';
635 return ScoreString(f, tmp);
640 float hud_fixscoreboardcolumnwidth_len;
641 float hud_fixscoreboardcolumnwidth_iconlen;
642 float hud_fixscoreboardcolumnwidth_marginlen;
644 string HUD_FixScoreboardColumnWidth(int i, string str)
649 field = hud_field[i];
651 hud_fixscoreboardcolumnwidth_iconlen = 0;
653 if(hud_field_icon0 != "")
655 sz = draw_getimagesize(hud_field_icon0);
657 if(hud_fixscoreboardcolumnwidth_iconlen < f)
658 hud_fixscoreboardcolumnwidth_iconlen = f;
661 if(hud_field_icon1 != "")
663 sz = draw_getimagesize(hud_field_icon1);
665 if(hud_fixscoreboardcolumnwidth_iconlen < f)
666 hud_fixscoreboardcolumnwidth_iconlen = f;
669 if(hud_field_icon2 != "")
671 sz = draw_getimagesize(hud_field_icon2);
673 if(hud_fixscoreboardcolumnwidth_iconlen < f)
674 hud_fixscoreboardcolumnwidth_iconlen = f;
677 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
679 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
680 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
682 hud_fixscoreboardcolumnwidth_marginlen = 0;
684 if(field == SP_NAME) // name gets all remaining space
688 namesize = sbwidth;// / hud_fontsize_x;
689 for(j = 0; j < hud_num_fields; ++j)
691 if (hud_field[i] != SP_SEPARATOR)
692 namesize -= hud_size[j] + hud_fontsize.x;
693 namesize += hud_fontsize.x;
694 hud_size[i] = namesize;
696 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
697 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
698 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
699 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
702 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
704 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
711 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, bool is_self, int pl_number)
713 TC(bool, is_self); TC(int, pl_number);
715 rgb = Team_ColorRGB(pl.team);
719 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
721 if((rgb == '1 1 1') && (!is_spec)) {
722 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
723 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
724 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
726 vector h_pos = pos - '1 1 0';
727 vector h_size = item_size + '2 0 0';
728 // alternated rows highlighting
730 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
731 else if((scoreboard_highlight) && (!(pl_number % 2)))
732 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
738 for(i = 0; i < hud_num_fields; ++i)
740 field = hud_field[i];
741 if(field == SP_SEPARATOR)
744 if(is_spec && field != SP_NAME && field != SP_PING) {
745 pos.x += hud_size[i] + hud_fontsize.x;
748 str = HUD_GetField(pl, field);
749 str = HUD_FixScoreboardColumnWidth(i, str);
751 pos.x += hud_size[i] + hud_fontsize.x;
753 if(field == SP_NAME) {
754 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
756 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
758 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
760 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
762 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
764 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
767 tmp.x = hud_size[i] + hud_fontsize.x;
768 if(hud_field_icon0 != "")
770 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);
772 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);
773 if(hud_field_icon1 != "")
775 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);
777 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);
778 if(hud_field_icon2 != "")
780 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);
782 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);
785 if(hud_field[i] == SP_SEPARATOR)
788 for(i = hud_num_fields-1; i > 0; --i)
790 field = hud_field[i];
791 if(field == SP_SEPARATOR)
794 if(is_spec && field != SP_NAME && field != SP_PING) {
795 pos.x -= hud_size[i] + hud_fontsize.x;
799 str = HUD_GetField(pl, field);
800 str = HUD_FixScoreboardColumnWidth(i, str);
802 if(field == SP_NAME) {
803 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
805 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
807 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
809 tmp.x = hud_fixscoreboardcolumnwidth_len;
811 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
813 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
817 if(hud_field_icon0 != "")
819 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);
821 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);
822 if(hud_field_icon1 != "")
824 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);
826 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);
827 if(hud_field_icon2 != "")
829 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);
831 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);
832 pos.x -= hud_size[i] + hud_fontsize.x;
837 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
841 * HUD_Scoreboard_MakeTable
843 * Makes a table for a team (for all playing players in DM) and fills it
846 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
848 float body_table_height;
849 vector tmp = '0 0 0', column_dim = '0 0 0';
852 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
854 pos.y += autocvar_scoreboard_border_thickness;
858 tmp.y = 1.25 * hud_fontsize.y;
862 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
864 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
867 tmp.y += autocvar_scoreboard_border_thickness;
868 tmp.y += body_table_height;
869 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
871 // separator header/table
872 pos.y += 1.25 * hud_fontsize.y;
873 tmp.y = autocvar_scoreboard_border_thickness;
874 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
876 pos.y += autocvar_scoreboard_border_thickness;
879 tmp.y = body_table_height;
881 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
883 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
885 // anyway, apply some color
886 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
888 // go back to the top to make alternated columns highlighting and to print the strings
889 pos.y -= 1.25 * hud_fontsize.y;
890 pos.y -= autocvar_scoreboard_border_thickness;
894 if (scoreboard_highlight)
896 column_dim.y = 1.25 * hud_fontsize.y; // header
897 column_dim.y += autocvar_scoreboard_border_thickness;
898 column_dim.y += body_table_height;
901 // print the strings of the columns headers and draw the columns
903 for(i = 0; i < hud_num_fields; ++i)
905 if(hud_field[i] == SP_SEPARATOR)
907 column_dim.x = hud_size[i] + hud_fontsize.x;
908 if (scoreboard_highlight)
911 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
913 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
914 pos.x += column_dim.x;
916 if(hud_field[i] == SP_SEPARATOR)
920 for(i = hud_num_fields-1; i > 0; --i)
922 if(hud_field[i] == SP_SEPARATOR)
925 pos.x -= hud_size[i];
927 if (scoreboard_highlight)
931 if (i == hud_num_fields-1)
932 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
934 column_dim.x = hud_size[i] + hud_fontsize.x;
935 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
939 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
940 tmp.x = (hud_size[i] - tmp.x);
941 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
942 pos.x -= hud_fontsize.x;
947 pos.y += 1.25 * hud_fontsize.y; // skip the header
948 pos.y += autocvar_scoreboard_border_thickness;
952 tmp.y = hud_fontsize.y * 1.25;
954 // fill the table and draw the rows
957 for(pl = players.sort_next; pl; pl = pl.sort_next)
959 if(pl.team != tm.team)
961 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
962 pos.y += 1.25 * hud_fontsize.y;
966 for(pl = players.sort_next; pl; pl = pl.sort_next)
968 if(pl.team == NUM_SPECTATOR)
970 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
971 pos.y += 1.25 * hud_fontsize.y;
976 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
977 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
982 float HUD_WouldDrawScoreboard() {
983 if (autocvar__hud_configure)
985 else if (QuickMenu_IsOpened())
987 else if (HUD_Radar_Clickable())
989 else if (scoreboard_showscores)
991 else if (intermission == 1)
993 else if (intermission == 2)
995 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
997 else if (scoreboard_showscores_force)
1002 float average_accuracy;
1003 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
1005 WepSet weapons_stat = WepSet_GetFromStat();
1006 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1007 float initial_posx = pos.x;
1008 int disownedcnt = 0;
1009 FOREACH(Weapons, it != WEP_Null, {
1010 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1012 WepSet set = it.m_wepset;
1013 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1017 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1018 if (weapon_cnt <= 0) return pos;
1021 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1023 int columnns = ceil(weapon_cnt / rows);
1026 float fontsize = height * 1/3;
1027 float weapon_height = height * 2/3;
1028 float weapon_width = sbwidth / columnns / rows;
1030 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1031 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1032 vector tmp = '0 0 0';
1034 tmp.y = height * rows;
1037 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1039 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1040 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1042 // column highlighting
1043 for (int i = 0; i < columnns; ++i)
1046 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);
1050 for (int i = 0; i < rows; ++i)
1052 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);
1055 average_accuracy = 0;
1056 int weapons_with_stats = 0;
1058 pos.x += weapon_width / 2;
1060 if (autocvar_scoreboard_accuracy_nocolors)
1063 Accuracy_LoadColors();
1065 float oldposx = pos.x;
1069 FOREACH(Weapons, it != WEP_Null, {
1070 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1072 WepSet set = it.m_wepset;
1073 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1077 if (weapon_stats >= 0)
1078 weapon_alpha = scoreboard_alpha_fg;
1080 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1083 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1085 if (weapon_stats >= 0) {
1086 weapons_with_stats += 1;
1087 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1090 s = sprintf("%d%%", weapon_stats*100);
1093 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1095 if(!autocvar_scoreboard_accuracy_nocolors)
1096 rgb = Accuracy_GetColor(weapon_stats);
1098 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1100 tmpos.x += weapon_width * rows;
1101 pos.x += weapon_width * rows;
1102 if (rows == 2 && column == columnns - 1) {
1110 if (weapons_with_stats)
1111 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1114 pos.y += 1.25 * hud_fontsize.y;
1115 pos.x = initial_posx;
1119 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1121 pos.x += hud_fontsize.x * 0.25;
1122 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1123 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1124 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1126 pos.y+= hud_fontsize.y;
1131 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1132 float stat_secrets_found, stat_secrets_total;
1133 float stat_monsters_killed, stat_monsters_total;
1137 // get monster stats
1138 stat_monsters_killed = STAT(MONSTERS_KILLED);
1139 stat_monsters_total = STAT(MONSTERS_TOTAL);
1141 // get secrets stats
1142 stat_secrets_found = STAT(SECRETS_FOUND);
1143 stat_secrets_total = STAT(SECRETS_TOTAL);
1145 // get number of rows
1146 if(stat_secrets_total)
1148 if(stat_monsters_total)
1151 // if no rows, return
1155 // draw table header
1156 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1157 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1160 vector tmp = '0 0 0';
1162 tmp.y = hud_fontsize.y * rows;
1165 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1167 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1168 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1171 if(stat_monsters_total)
1173 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1174 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1178 if(stat_secrets_total)
1180 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1181 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1185 pos.y += 1.25 * hud_fontsize.y;
1190 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1193 RANKINGS_RECEIVED_CNT = 0;
1194 for (i=RANKINGS_CNT-1; i>=0; --i)
1196 ++RANKINGS_RECEIVED_CNT;
1198 if (RANKINGS_RECEIVED_CNT == 0)
1202 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
1204 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1205 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1206 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1208 pos.y += hud_fontsize.y;
1209 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1210 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1211 vector tmp = '0 0 0';
1213 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1216 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1218 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1219 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1222 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1229 n = grecordholder[i];
1230 p = count_ordinal(i+1);
1231 if(grecordholder[i] == entcs_GetName(player_localnum))
1232 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1233 else if(!(i % 2) && scoreboard_highlight)
1234 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1235 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1236 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);
1237 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1238 pos.y += 1.25 * hud_fontsize.y;
1240 pos.y += autocvar_scoreboard_border_thickness;
1245 float hud_woulddrawscoreboard_prev;
1246 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1247 void HUD_DrawScoreboard()
1249 float hud_woulddrawscoreboard;
1250 hud_woulddrawscoreboard = scoreboard_active;
1251 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1252 hud_woulddrawscoreboard_change = time;
1253 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1256 if(hud_woulddrawscoreboard) {
1257 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1258 if (scoreboard_fadeinspeed)
1259 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1261 scoreboard_fade_alpha = 1;
1264 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1265 if (scoreboard_fadeoutspeed)
1266 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1268 scoreboard_fade_alpha = 0;
1271 if (!scoreboard_fade_alpha)
1274 HUD_UpdatePlayerTeams();
1276 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1277 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1278 scoreboard_highlight = autocvar_scoreboard_highlight;
1279 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1280 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1281 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1282 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1284 vector rgb, pos, tmp;
1288 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1289 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1291 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1292 ymax = (vid_conheight - ymin);
1294 sbwidth = xmax - xmin;
1296 // Initializes position
1302 vector sb_heading_fontsize;
1303 sb_heading_fontsize = hud_fontsize * 2;
1304 draw_beginBoldFont();
1305 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1308 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1310 // Draw the scoreboard
1311 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1315 vector team_score_baseoffset;
1316 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1317 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1319 if(tm.team == NUM_SPECTATOR)
1321 if(!tm.team && teamplay)
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)
1352 if(!tm.team && teamplay)
1355 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1359 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1360 if(race_speedaward) {
1361 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1362 pos.y += 1.25 * hud_fontsize.y;
1364 if(race_speedaward_alltimebest) {
1365 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1366 pos.y += 1.25 * hud_fontsize.y;
1368 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1370 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1372 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1374 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1379 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1381 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1388 item_size.x = sbwidth;
1389 item_size.y = hud_fontsize.y * 1.25;
1391 for(pl = players.sort_next; pl; pl = pl.sort_next)
1393 if(pl.team != NUM_SPECTATOR)
1395 pos.y += 1.25 * hud_fontsize.y;
1396 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1402 draw_beginBoldFont();
1403 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1405 pos.y += 1.25 * hud_fontsize.y;
1408 // Print info string
1410 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1411 tl = STAT(TIMELIMIT);
1412 fl = STAT(FRAGLIMIT);
1413 ll = STAT(LEADLIMIT);
1414 if(gametype == MAPINFO_TYPE_LMS)
1417 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1422 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1426 str = strcat(str, _(" or"));
1429 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1430 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1431 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1432 TranslateScoresLabel(teamscores_label[ts_primary])));
1436 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1437 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1438 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1439 TranslateScoresLabel(scores_label[ps_primary])));
1444 if(tl > 0 || fl > 0)
1445 str = strcat(str, _(" or"));
1448 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1449 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1450 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1451 TranslateScoresLabel(teamscores_label[ts_primary])));
1455 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1456 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1457 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1458 TranslateScoresLabel(scores_label[ps_primary])));
1463 pos.y += 1.2 * hud_fontsize.y;
1464 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1466 // print information about respawn status
1467 float respawn_time = STAT(RESPAWN_TIME);
1471 if(respawn_time < 0)
1473 // a negative number means we are awaiting respawn, time value is still the same
1474 respawn_time *= -1; // remove mark now that we checked it
1475 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1477 str = sprintf(_("^1Respawning in ^3%s^1..."),
1478 (autocvar_scoreboard_respawntime_decimals ?
1479 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1481 count_seconds(respawn_time - time)
1485 else if(time < respawn_time)
1487 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1488 (autocvar_scoreboard_respawntime_decimals ?
1489 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1491 count_seconds(respawn_time - time)
1495 else if(time >= respawn_time)
1496 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1498 pos.y += 1.2 * hud_fontsize.y;
1499 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1502 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;