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)
1006 WepSet weapons_stat = WepSet_GetFromStat();
1007 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1008 float initial_posx = pos.x;
1009 int disownedcnt = 0;
1010 FOREACH(Weapons, it != WEP_Null, {
1011 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1013 WepSet set = it.m_wepset;
1014 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1018 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1019 if (weapon_cnt <= 0) return pos;
1022 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1024 int columnns = ceil(weapon_cnt / rows);
1027 float fontsize = height * 1/3;
1028 float weapon_height = height * 2/3;
1029 float weapon_width = sbwidth / columnns / rows;
1031 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1032 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1033 vector tmp = '0 0 0';
1035 tmp.y = height * rows;
1038 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1040 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1041 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1043 // column highlighting
1044 for (int i = 0; i < columnns; ++i)
1047 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);
1051 for (int i = 0; i < rows; ++i)
1053 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);
1056 average_accuracy = 0;
1057 int weapons_with_stats = 0;
1059 pos.x += weapon_width / 2;
1061 if (autocvar_scoreboard_accuracy_nocolors)
1064 Accuracy_LoadColors();
1066 float oldposx = pos.x;
1070 FOREACH(Weapons, it != WEP_Null, {
1071 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1073 WepSet set = it.m_wepset;
1074 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1078 if (weapon_stats >= 0)
1079 weapon_alpha = scoreboard_alpha_fg;
1081 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1084 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1086 if (weapon_stats >= 0) {
1087 weapons_with_stats += 1;
1088 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1091 s = sprintf("%d%%", weapon_stats*100);
1094 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1096 if(!autocvar_scoreboard_accuracy_nocolors)
1097 rgb = Accuracy_GetColor(weapon_stats);
1099 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1101 tmpos.x += weapon_width * rows;
1102 pos.x += weapon_width * rows;
1103 if (rows == 2 && column == columnns - 1) {
1111 if (weapons_with_stats)
1112 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1115 pos.y += 1.25 * hud_fontsize.y;
1116 pos.x = initial_posx;
1120 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1122 pos.x += hud_fontsize.x * 0.25;
1123 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1124 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1125 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1127 pos.y+= hud_fontsize.y;
1132 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1133 float stat_secrets_found, stat_secrets_total;
1134 float stat_monsters_killed, stat_monsters_total;
1138 // get monster stats
1139 stat_monsters_killed = STAT(MONSTERS_KILLED);
1140 stat_monsters_total = STAT(MONSTERS_TOTAL);
1142 // get secrets stats
1143 stat_secrets_found = STAT(SECRETS_FOUND);
1144 stat_secrets_total = STAT(SECRETS_TOTAL);
1146 // get number of rows
1147 if(stat_secrets_total)
1149 if(stat_monsters_total)
1152 // if no rows, return
1156 // draw table header
1157 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1158 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1161 vector tmp = '0 0 0';
1163 tmp.y = hud_fontsize.y * rows;
1166 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1168 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1169 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1172 if(stat_monsters_total)
1174 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1175 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1179 if(stat_secrets_total)
1181 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1182 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1186 pos.y += 1.25 * hud_fontsize.y;
1191 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1194 RANKINGS_RECEIVED_CNT = 0;
1195 for (i=RANKINGS_CNT-1; i>=0; --i)
1197 ++RANKINGS_RECEIVED_CNT;
1199 if (RANKINGS_RECEIVED_CNT == 0)
1203 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
1205 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1206 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1207 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1209 pos.y += hud_fontsize.y;
1210 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1211 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1212 vector tmp = '0 0 0';
1214 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1217 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1219 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1220 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1223 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1230 n = grecordholder[i];
1231 p = count_ordinal(i+1);
1232 if(grecordholder[i] == entcs_GetName(player_localnum))
1233 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1234 else if(!(i % 2) && scoreboard_highlight)
1235 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1236 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1237 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);
1238 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1239 pos.y += 1.25 * hud_fontsize.y;
1241 pos.y += autocvar_scoreboard_border_thickness;
1246 float hud_woulddrawscoreboard_prev;
1247 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1248 void HUD_DrawScoreboard()
1250 float hud_woulddrawscoreboard;
1251 hud_woulddrawscoreboard = scoreboard_active;
1252 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1253 hud_woulddrawscoreboard_change = time;
1254 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1257 if(hud_woulddrawscoreboard) {
1258 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1259 if (scoreboard_fadeinspeed)
1260 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1262 scoreboard_fade_alpha = 1;
1265 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1266 if (scoreboard_fadeoutspeed)
1267 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1269 scoreboard_fade_alpha = 0;
1272 if (!scoreboard_fade_alpha)
1275 HUD_UpdatePlayerTeams();
1277 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1278 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1279 scoreboard_highlight = autocvar_scoreboard_highlight;
1280 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1281 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1282 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1283 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1285 vector rgb, pos, tmp;
1289 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1290 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1292 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1293 ymax = (vid_conheight - ymin);
1295 sbwidth = xmax - xmin;
1297 // Initializes position
1303 vector sb_heading_fontsize;
1304 sb_heading_fontsize = hud_fontsize * 2;
1305 draw_beginBoldFont();
1306 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1309 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1311 // Draw the scoreboard
1312 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1316 vector team_score_baseoffset;
1317 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1318 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1320 if(tm.team == NUM_SPECTATOR)
1322 if(!tm.team && teamplay)
1325 draw_beginBoldFont();
1326 rgb = Team_ColorRGB(tm.team);
1327 str = ftos(tm.(teamscores[ts_primary]));
1328 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1330 if(ts_primary != ts_secondary)
1332 str = ftos(tm.(teamscores[ts_secondary]));
1333 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);
1337 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1339 rgb.x = autocvar_scoreboard_color_bg_r;
1340 rgb.y = autocvar_scoreboard_color_bg_g;
1341 rgb.z = autocvar_scoreboard_color_bg_b;
1345 rgb.x = autocvar_scoreboard_color_bg_r;
1346 rgb.y = autocvar_scoreboard_color_bg_g;
1347 rgb.z = autocvar_scoreboard_color_bg_b;
1349 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1351 if(tm.team == NUM_SPECTATOR)
1353 if(!tm.team && teamplay)
1356 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1360 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1361 if(race_speedaward) {
1362 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1363 pos.y += 1.25 * hud_fontsize.y;
1365 if(race_speedaward_alltimebest) {
1366 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1367 pos.y += 1.25 * hud_fontsize.y;
1369 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1371 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1373 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1375 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1380 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1382 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1389 item_size.x = sbwidth;
1390 item_size.y = hud_fontsize.y * 1.25;
1392 for(pl = players.sort_next; pl; pl = pl.sort_next)
1394 if(pl.team != NUM_SPECTATOR)
1396 pos.y += 1.25 * hud_fontsize.y;
1397 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1403 draw_beginBoldFont();
1404 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1406 pos.y += 1.25 * hud_fontsize.y;
1409 // Print info string
1411 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1412 tl = STAT(TIMELIMIT);
1413 fl = STAT(FRAGLIMIT);
1414 ll = STAT(LEADLIMIT);
1415 if(gametype == MAPINFO_TYPE_LMS)
1418 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1423 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1427 str = strcat(str, _(" or"));
1430 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1431 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1432 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1433 TranslateScoresLabel(teamscores_label[ts_primary])));
1437 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1438 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1439 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1440 TranslateScoresLabel(scores_label[ps_primary])));
1445 if(tl > 0 || fl > 0)
1446 str = strcat(str, _(" or"));
1449 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1450 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1451 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1452 TranslateScoresLabel(teamscores_label[ts_primary])));
1456 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1457 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1458 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1459 TranslateScoresLabel(scores_label[ps_primary])));
1464 pos.y += 1.2 * hud_fontsize.y;
1465 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1467 // print information about respawn status
1468 float respawn_time = STAT(RESPAWN_TIME);
1472 if(respawn_time < 0)
1474 // a negative number means we are awaiting respawn, time value is still the same
1475 respawn_time *= -1; // remove mark now that we checked it
1476 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1478 str = sprintf(_("^1Respawning in ^3%s^1..."),
1479 (autocvar_scoreboard_respawntime_decimals ?
1480 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1482 count_seconds(respawn_time - time)
1486 else if(time < respawn_time)
1488 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1489 (autocvar_scoreboard_respawntime_decimals ?
1490 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1492 count_seconds(respawn_time - time)
1496 else if(time >= respawn_time)
1497 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1499 pos.y += 1.2 * hud_fontsize.y;
1500 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1503 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;