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 = entcs_GetScoreTeam(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 = entcs_GetTeam(left.sv_entnum);
150 vr = entcs_GetTeam(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 entcs_GetName(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 = (entcs_GetTeam(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 && 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 FOREACH(Weapons, it != WEP_Null, LAMBDA(
1005 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1007 WepSet set = it.m_wepset;
1008 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1012 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1013 if (weapon_cnt <= 0) return pos;
1016 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1018 int columnns = ceil(weapon_cnt / rows);
1021 float fontsize = height * 1/3;
1022 float weapon_height = height * 2/3;
1023 float weapon_width = sbwidth / columnns / rows;
1025 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1026 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1027 vector tmp = '0 0 0';
1029 tmp.y = height * rows;
1032 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1034 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1035 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1037 // column highlighting
1038 for (int i = 0; i < columnns; ++i)
1041 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);
1045 for (int i = 0; i < rows; ++i)
1047 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);
1050 average_accuracy = 0;
1051 int weapons_with_stats = 0;
1053 pos.x += weapon_width / 2;
1055 if (autocvar_scoreboard_accuracy_nocolors)
1058 Accuracy_LoadColors();
1060 float oldposx = pos.x;
1064 FOREACH(Weapons, it != WEP_Null, LAMBDA(
1065 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1067 WepSet set = it.m_wepset;
1068 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1072 if (weapon_stats >= 0)
1073 weapon_alpha = scoreboard_alpha_fg;
1075 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1078 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1080 if (weapon_stats >= 0) {
1081 weapons_with_stats += 1;
1082 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1085 s = sprintf("%d%%", weapon_stats*100);
1088 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1090 if(!autocvar_scoreboard_accuracy_nocolors)
1091 rgb = Accuracy_GetColor(weapon_stats);
1093 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1095 tmpos.x += weapon_width * rows;
1096 pos.x += weapon_width * rows;
1097 if (rows == 2 && column == columnns - 1) {
1105 if (weapons_with_stats)
1106 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1109 pos.y += 1.25 * hud_fontsize.y;
1110 pos.x = initial_posx;
1114 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1116 pos.x += hud_fontsize.x * 0.25;
1117 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1118 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1119 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1121 pos.y+= hud_fontsize.y;
1126 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1127 float stat_secrets_found, stat_secrets_total;
1128 float stat_monsters_killed, stat_monsters_total;
1132 // get monster stats
1133 stat_monsters_killed = STAT(MONSTERS_KILLED);
1134 stat_monsters_total = STAT(MONSTERS_TOTAL);
1136 // get secrets stats
1137 stat_secrets_found = STAT(SECRETS_FOUND);
1138 stat_secrets_total = STAT(SECRETS_TOTAL);
1140 // get number of rows
1141 if(stat_secrets_total)
1143 if(stat_monsters_total)
1146 // if no rows, return
1150 // draw table header
1151 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1152 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1155 vector tmp = '0 0 0';
1157 tmp.y = hud_fontsize.y * rows;
1160 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1162 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1163 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1166 if(stat_monsters_total)
1168 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1169 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1173 if(stat_secrets_total)
1175 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1176 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1180 pos.y += 1.25 * hud_fontsize.y;
1185 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1188 RANKINGS_RECEIVED_CNT = 0;
1189 for (i=RANKINGS_CNT-1; i>=0; --i)
1191 ++RANKINGS_RECEIVED_CNT;
1193 if (RANKINGS_RECEIVED_CNT == 0)
1197 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
1199 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1200 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1201 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1203 pos.y += hud_fontsize.y;
1204 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1205 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1206 vector tmp = '0 0 0';
1208 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1211 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1213 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1214 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1217 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1224 n = grecordholder[i];
1225 p = count_ordinal(i+1);
1226 if(grecordholder[i] == entcs_GetName(player_localnum))
1227 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1228 else if(!(i % 2) && scoreboard_highlight)
1229 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1230 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1231 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);
1232 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1233 pos.y += 1.25 * hud_fontsize.y;
1235 pos.y += autocvar_scoreboard_border_thickness;
1240 float hud_woulddrawscoreboard_prev;
1241 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1242 void HUD_DrawScoreboard()
1244 float hud_woulddrawscoreboard;
1245 hud_woulddrawscoreboard = scoreboard_active;
1246 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1247 hud_woulddrawscoreboard_change = time;
1248 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1251 if(hud_woulddrawscoreboard) {
1252 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1253 if (scoreboard_fadeinspeed)
1254 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1256 scoreboard_fade_alpha = 1;
1259 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1260 if (scoreboard_fadeoutspeed)
1261 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1263 scoreboard_fade_alpha = 0;
1266 if (!scoreboard_fade_alpha)
1269 HUD_UpdatePlayerTeams();
1271 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1272 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1273 scoreboard_highlight = autocvar_scoreboard_highlight;
1274 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1275 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1276 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1277 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1279 vector rgb, pos, tmp;
1283 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1284 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1286 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1287 ymax = (vid_conheight - ymin);
1289 sbwidth = xmax - xmin;
1291 // Initializes position
1297 vector sb_heading_fontsize;
1298 sb_heading_fontsize = hud_fontsize * 2;
1299 draw_beginBoldFont();
1300 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1303 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1305 // Draw the scoreboard
1306 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1310 vector team_score_baseoffset;
1311 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1312 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1314 if(tm.team == NUM_SPECTATOR)
1317 draw_beginBoldFont();
1318 rgb = Team_ColorRGB(tm.team);
1319 str = ftos(tm.(teamscores[ts_primary]));
1320 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1322 if(ts_primary != ts_secondary)
1324 str = ftos(tm.(teamscores[ts_secondary]));
1325 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);
1329 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1331 rgb.x = autocvar_scoreboard_color_bg_r;
1332 rgb.y = autocvar_scoreboard_color_bg_g;
1333 rgb.z = autocvar_scoreboard_color_bg_b;
1337 rgb.x = autocvar_scoreboard_color_bg_r;
1338 rgb.y = autocvar_scoreboard_color_bg_g;
1339 rgb.z = autocvar_scoreboard_color_bg_b;
1341 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1343 if(tm.team == NUM_SPECTATOR)
1346 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1350 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1351 if(race_speedaward) {
1352 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1353 pos.y += 1.25 * hud_fontsize.y;
1355 if(race_speedaward_alltimebest) {
1356 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1357 pos.y += 1.25 * hud_fontsize.y;
1359 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1361 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1363 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1365 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1370 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1372 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1379 item_size.x = sbwidth;
1380 item_size.y = hud_fontsize.y * 1.25;
1382 for(pl = players.sort_next; pl; pl = pl.sort_next)
1384 if(pl.team != NUM_SPECTATOR)
1386 pos.y += 1.25 * hud_fontsize.y;
1387 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1393 draw_beginBoldFont();
1394 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1396 pos.y += 1.25 * hud_fontsize.y;
1399 // Print info string
1401 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1402 tl = STAT(TIMELIMIT);
1403 fl = STAT(FRAGLIMIT);
1404 ll = STAT(LEADLIMIT);
1405 if(gametype == MAPINFO_TYPE_LMS)
1408 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1413 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1417 str = strcat(str, _(" or"));
1420 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1421 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1422 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1423 TranslateScoresLabel(teamscores_label[ts_primary])));
1427 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1428 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1429 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1430 TranslateScoresLabel(scores_label[ps_primary])));
1435 if(tl > 0 || fl > 0)
1436 str = strcat(str, _(" or"));
1439 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1440 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1441 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1442 TranslateScoresLabel(teamscores_label[ts_primary])));
1446 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1447 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1448 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1449 TranslateScoresLabel(scores_label[ps_primary])));
1454 pos.y += 1.2 * hud_fontsize.y;
1455 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1457 // print information about respawn status
1458 float respawn_time = STAT(RESPAWN_TIME);
1462 if(respawn_time < 0)
1464 // a negative number means we are awaiting respawn, time value is still the same
1465 respawn_time *= -1; // remove mark now that we checked it
1466 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1468 str = sprintf(_("^1Respawning in ^3%s^1..."),
1469 (autocvar_scoreboard_respawntime_decimals ?
1470 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1472 count_seconds(respawn_time - time)
1476 else if(time < respawn_time)
1478 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
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)
1487 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1489 pos.y += 1.2 * hud_fontsize.y;
1490 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1493 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;