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(float vl, float vr, int f)
133 if(f & SFL_ZERO_IS_WORST)
135 if(vl == 0 && vr != 0)
137 if(vl != 0 && vr == 0)
141 return IS_INCREASING(f);
143 return IS_DECREASING(f);
147 float HUD_ComparePlayerScores(entity left, entity right)
150 vl = entcs_GetTeam(left.sv_entnum);
151 vr = entcs_GetTeam(right.sv_entnum);
163 if(vl == NUM_SPECTATOR)
165 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
167 if(!left.gotscores && right.gotscores)
172 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
176 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
181 for(i = 0; i < MAX_SCORE; ++i)
183 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
188 if (left.sv_entnum < right.sv_entnum)
194 void HUD_UpdatePlayerPos(entity player)
196 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
198 SORT_SWAP(player, other);
200 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
202 SORT_SWAP(other, player);
206 float HUD_CompareTeamScores(entity left, entity right)
210 if(left.team == NUM_SPECTATOR)
212 if(right.team == NUM_SPECTATOR)
215 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
219 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
223 for(i = 0; i < MAX_SCORE; ++i)
225 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
230 if (left.team < right.team)
236 void HUD_UpdateTeamPos(entity Team)
238 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
240 SORT_SWAP(Team, other);
242 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
244 SORT_SWAP(other, Team);
250 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
251 LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
252 LOG_INFO(_("Usage:\n"));
253 LOG_INFO(_("^2scoreboard_columns_set default\n"));
254 LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
255 LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
256 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
258 LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n"));
259 LOG_INFO(_("^3ping^7 Ping time\n"));
260 LOG_INFO(_("^3pl^7 Packet loss\n"));
261 LOG_INFO(_("^3kills^7 Number of kills\n"));
262 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
263 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
264 LOG_INFO(_("^3frags^7 kills - suicides\n"));
265 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
266 LOG_INFO(_("^3dmg^7 The total damage done\n"));
267 LOG_INFO(_("^3dmgtaken^7 The total damage taken\n"));
268 LOG_INFO(_("^3sum^7 frags - deaths\n"));
269 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
270 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
271 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
272 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
273 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
274 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
275 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
276 LOG_INFO(_("^3rank^7 Player rank\n"));
277 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
278 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
279 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
280 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
281 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
282 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
283 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
284 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
285 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
286 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
287 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
288 LOG_INFO(_("^3score^7 Total score\n\n"));
290 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
291 "of game types, then a slash, to make the field show up only in these\n"
292 "or in all but these game types. You can also specify 'all' as a\n"
293 "field to show all fields available for the current game mode.\n\n"));
295 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
296 "include/exclude ALL teams/noteams game modes.\n\n"));
298 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
299 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
300 "right of the vertical bar aligned to the right.\n"));
301 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
302 "other gamemodes except DM.\n"));
305 #define HUD_DefaultColumnLayout() \
307 "-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" */ \
308 "-rc,cts,nb/dmg -rc,cts,nb/dmgtaken " \
309 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \
310 "+lms/lives +lms/rank " \
311 "+kh/caps +kh/pushes +kh/destroyed " \
312 "?+rc/laps ?+rc/time ?+rc/fastest " \
313 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
316 void Cmd_HUD_SetFields(float argc)
320 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
325 // set up a temporary scoreboard layout
326 // no layout can be properly set up until score_info data haven't been received
327 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
329 scores_label[ps_primary] = strzone("score");
330 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
333 // TODO: re enable with gametype dependant cvars?
334 if(argc < 3) // no arguments provided
335 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
338 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
342 if(argv(2) == "default")
343 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
344 else if(argv(2) == "all")
347 s = "ping pl name |";
348 for(i = 0; i < MAX_SCORE; ++i)
351 if(i != ps_secondary)
352 if(scores_label[i] != "")
353 s = strcat(s, " ", scores_label[i]);
355 if(ps_secondary != ps_primary)
356 s = strcat(s, " ", scores_label[ps_secondary]);
357 s = strcat(s, " ", scores_label[ps_primary]);
358 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
365 hud_fontsize = HUD_GetFontsize("hud_fontsize");
367 for(i = 1; i < argc - 1; ++i)
373 if(substring(str, 0, 1) == "?")
376 str = substring(str, 1, strlen(str) - 1);
379 slash = strstrofs(str, "/", 0);
382 pattern = substring(str, 0, slash);
383 str = substring(str, slash + 1, strlen(str) - (slash + 1));
385 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
389 strunzone(hud_title[hud_num_fields]);
390 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
391 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
392 str = strtolower(str);
396 case "ping": hud_field[hud_num_fields] = SP_PING; break;
397 case "pl": hud_field[hud_num_fields] = SP_PL; break;
398 case "pl": hud_field[hud_num_fields] = SP_PL; break;
399 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
400 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
401 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
402 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
403 case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
404 case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
407 for(j = 0; j < MAX_SCORE; ++j)
408 if(str == strtolower(scores_label[j]))
409 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
417 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
421 hud_field[hud_num_fields] = j;
424 if(j == ps_secondary)
430 if(hud_num_fields >= MAX_HUD_FIELDS)
434 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
436 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
438 if(ps_primary == ps_secondary)
440 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
442 if(hud_num_fields+missing < MAX_HUD_FIELDS)
446 strunzone(hud_title[hud_num_fields]);
447 for(i = hud_num_fields; i > 0; --i)
449 hud_title[i] = hud_title[i-1];
450 hud_size[i] = hud_size[i-1];
451 hud_field[i] = hud_field[i-1];
453 hud_title[0] = strzone(TranslateScoresLabel("name"));
454 hud_field[0] = SP_NAME;
456 LOG_INFO("fixed missing field 'name'\n");
460 strunzone(hud_title[hud_num_fields]);
461 for(i = hud_num_fields; i > 1; --i)
463 hud_title[i] = hud_title[i-1];
464 hud_size[i] = hud_size[i-1];
465 hud_field[i] = hud_field[i-1];
467 hud_title[1] = strzone("|");
468 hud_field[1] = SP_SEPARATOR;
469 hud_size[1] = stringwidth("|", false, hud_fontsize);
471 LOG_INFO("fixed missing field '|'\n");
474 else if(!have_separator)
476 strunzone(hud_title[hud_num_fields]);
477 hud_title[hud_num_fields] = strzone("|");
478 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
479 hud_field[hud_num_fields] = SP_SEPARATOR;
481 LOG_INFO("fixed missing field '|'\n");
485 strunzone(hud_title[hud_num_fields]);
486 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
487 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
488 hud_field[hud_num_fields] = ps_secondary;
490 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_secondary]);
494 strunzone(hud_title[hud_num_fields]);
495 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
496 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
497 hud_field[hud_num_fields] = ps_primary;
499 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_primary]);
503 hud_field[hud_num_fields] = SP_END;
507 vector hud_field_rgb;
508 string hud_field_icon0;
509 string hud_field_icon1;
510 string hud_field_icon2;
511 vector hud_field_icon0_rgb;
512 vector hud_field_icon1_rgb;
513 vector hud_field_icon2_rgb;
514 float hud_field_icon0_alpha;
515 float hud_field_icon1_alpha;
516 float hud_field_icon2_alpha;
517 string HUD_GetField(entity pl, int field)
519 float tmp, num, denom;
522 hud_field_rgb = '1 1 1';
523 hud_field_icon0 = "";
524 hud_field_icon1 = "";
525 hud_field_icon2 = "";
526 hud_field_icon0_rgb = '1 1 1';
527 hud_field_icon1_rgb = '1 1 1';
528 hud_field_icon2_rgb = '1 1 1';
529 hud_field_icon0_alpha = 1;
530 hud_field_icon1_alpha = 1;
531 hud_field_icon2_alpha = 1;
536 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
537 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
541 tmp = max(0, min(220, f-80)) / 220;
542 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
548 f = pl.ping_packetloss;
549 tmp = pl.ping_movementloss;
550 if(f == 0 && tmp == 0)
552 str = ftos(ceil(f * 100));
554 str = strcat(str, "~", ftos(ceil(tmp * 100)));
555 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
556 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
560 if(ready_waiting && pl.ready)
562 hud_field_icon0 = "gfx/scoreboard/player_ready";
566 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
568 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
569 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
570 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
571 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
572 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
575 return entcs_GetName(pl.sv_entnum);
578 f = pl.(scores[SP_KILLS]);
579 f -= pl.(scores[SP_SUICIDES]);
583 num = pl.(scores[SP_KILLS]);
584 denom = pl.(scores[SP_DEATHS]);
587 hud_field_rgb = '0 1 0';
588 str = sprintf("%d", num);
589 } else if(num <= 0) {
590 hud_field_rgb = '1 0 0';
591 str = sprintf("%.1f", num/denom);
593 str = sprintf("%.1f", num/denom);
597 f = pl.(scores[SP_KILLS]);
598 f -= pl.(scores[SP_DEATHS]);
601 hud_field_rgb = '0 1 0';
603 hud_field_rgb = '1 1 1';
605 hud_field_rgb = '1 0 0';
610 num = pl.(scores[SP_DMG]);
613 str = sprintf("%.1f k", num/denom);
617 num = pl.(scores[SP_DMGTAKEN]);
620 str = sprintf("%.1f k", num/denom);
624 tmp = pl.(scores[field]);
625 f = scores_flags[field];
626 if(field == ps_primary)
627 hud_field_rgb = '1 1 0';
628 else if(field == ps_secondary)
629 hud_field_rgb = '0 1 1';
631 hud_field_rgb = '1 1 1';
632 return ScoreString(f, tmp);
637 float hud_fixscoreboardcolumnwidth_len;
638 float hud_fixscoreboardcolumnwidth_iconlen;
639 float hud_fixscoreboardcolumnwidth_marginlen;
641 string HUD_FixScoreboardColumnWidth(int i, string str)
645 field = hud_field[i];
647 hud_fixscoreboardcolumnwidth_iconlen = 0;
649 if(hud_field_icon0 != "")
651 sz = draw_getimagesize(hud_field_icon0);
653 if(hud_fixscoreboardcolumnwidth_iconlen < f)
654 hud_fixscoreboardcolumnwidth_iconlen = f;
657 if(hud_field_icon1 != "")
659 sz = draw_getimagesize(hud_field_icon1);
661 if(hud_fixscoreboardcolumnwidth_iconlen < f)
662 hud_fixscoreboardcolumnwidth_iconlen = f;
665 if(hud_field_icon2 != "")
667 sz = draw_getimagesize(hud_field_icon2);
669 if(hud_fixscoreboardcolumnwidth_iconlen < f)
670 hud_fixscoreboardcolumnwidth_iconlen = f;
673 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
675 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
676 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
678 hud_fixscoreboardcolumnwidth_marginlen = 0;
680 if(field == SP_NAME) // name gets all remaining space
684 namesize = sbwidth;// / hud_fontsize_x;
685 for(j = 0; j < hud_num_fields; ++j)
687 if (hud_field[i] != SP_SEPARATOR)
688 namesize -= hud_size[j] + hud_fontsize.x;
689 namesize += hud_fontsize.x;
690 hud_size[i] = namesize;
692 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
693 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
694 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
695 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
698 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
700 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
707 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
710 rgb = Team_ColorRGB(pl.team);
714 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
716 if((rgb == '1 1 1') && (!is_spec)) {
717 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
718 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
719 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
721 vector h_pos = pos - '1 1 0';
722 vector h_size = item_size + '2 0 0';
723 // alternated rows highlighting
725 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
726 else if((scoreboard_highlight) && (!(pl_number % 2)))
727 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
733 for(i = 0; i < hud_num_fields; ++i)
735 field = hud_field[i];
736 if(field == SP_SEPARATOR)
739 if(is_spec && field != SP_NAME && field != SP_PING) {
740 pos.x += hud_size[i] + hud_fontsize.x;
743 str = HUD_GetField(pl, field);
744 str = HUD_FixScoreboardColumnWidth(i, str);
746 pos.x += hud_size[i] + hud_fontsize.x;
748 if(field == SP_NAME) {
749 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
751 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
753 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
755 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
757 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
759 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
762 tmp.x = hud_size[i] + hud_fontsize.x;
763 if(hud_field_icon0 != "")
765 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);
767 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);
768 if(hud_field_icon1 != "")
770 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);
772 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);
773 if(hud_field_icon2 != "")
775 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);
777 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);
780 if(hud_field[i] == SP_SEPARATOR)
783 for(i = hud_num_fields-1; i > 0; --i)
785 field = hud_field[i];
786 if(field == SP_SEPARATOR)
789 if(is_spec && field != SP_NAME && field != SP_PING) {
790 pos.x -= hud_size[i] + hud_fontsize.x;
794 str = HUD_GetField(pl, field);
795 str = HUD_FixScoreboardColumnWidth(i, str);
797 if(field == SP_NAME) {
798 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
800 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
802 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
804 tmp.x = hud_fixscoreboardcolumnwidth_len;
806 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
808 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
812 if(hud_field_icon0 != "")
814 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);
816 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);
817 if(hud_field_icon1 != "")
819 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);
821 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);
822 if(hud_field_icon2 != "")
824 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);
826 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);
827 pos.x -= hud_size[i] + hud_fontsize.x;
832 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
836 * HUD_Scoreboard_MakeTable
838 * Makes a table for a team (for all playing players in DM) and fills it
841 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
843 float body_table_height;
844 vector tmp = '0 0 0', column_dim = '0 0 0';
847 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
849 pos.y += autocvar_scoreboard_border_thickness;
853 tmp.y = 1.25 * hud_fontsize.y;
857 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
859 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
862 tmp.y += autocvar_scoreboard_border_thickness;
863 tmp.y += body_table_height;
864 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
866 // separator header/table
867 pos.y += 1.25 * hud_fontsize.y;
868 tmp.y = autocvar_scoreboard_border_thickness;
869 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
871 pos.y += autocvar_scoreboard_border_thickness;
874 tmp.y = body_table_height;
876 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
878 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
880 // anyway, apply some color
881 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
883 // go back to the top to make alternated columns highlighting and to print the strings
884 pos.y -= 1.25 * hud_fontsize.y;
885 pos.y -= autocvar_scoreboard_border_thickness;
889 if (scoreboard_highlight)
891 column_dim.y = 1.25 * hud_fontsize.y; // header
892 column_dim.y += autocvar_scoreboard_border_thickness;
893 column_dim.y += body_table_height;
896 // print the strings of the columns headers and draw the columns
898 for(i = 0; i < hud_num_fields; ++i)
900 if(hud_field[i] == SP_SEPARATOR)
902 column_dim.x = hud_size[i] + hud_fontsize.x;
903 if (scoreboard_highlight)
906 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
908 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
909 pos.x += column_dim.x;
911 if(hud_field[i] == SP_SEPARATOR)
915 for(i = hud_num_fields-1; i > 0; --i)
917 if(hud_field[i] == SP_SEPARATOR)
920 pos.x -= hud_size[i];
922 if (scoreboard_highlight)
926 if (i == hud_num_fields-1)
927 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
929 column_dim.x = hud_size[i] + hud_fontsize.x;
930 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
934 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
935 tmp.x = (hud_size[i] - tmp.x);
936 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
937 pos.x -= hud_fontsize.x;
942 pos.y += 1.25 * hud_fontsize.y; // skip the header
943 pos.y += autocvar_scoreboard_border_thickness;
947 tmp.y = hud_fontsize.y * 1.25;
949 // fill the table and draw the rows
952 for(pl = players.sort_next; pl; pl = pl.sort_next)
954 if(pl.team != tm.team)
956 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
957 pos.y += 1.25 * hud_fontsize.y;
961 for(pl = players.sort_next; pl; pl = pl.sort_next)
963 if(pl.team == NUM_SPECTATOR)
965 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
966 pos.y += 1.25 * hud_fontsize.y;
971 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
972 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
977 float HUD_WouldDrawScoreboard() {
978 if (autocvar__hud_configure)
980 else if (QuickMenu_IsOpened())
982 else if (HUD_Radar_Clickable())
984 else if (scoreboard_showscores)
986 else if (intermission == 1)
988 else if (intermission == 2)
990 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
992 else if (scoreboard_showscores_force)
997 float average_accuracy;
998 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
1001 WepSet weapons_stat = WepSet_GetFromStat();
1002 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1003 float initial_posx = pos.x;
1004 int disownedcnt = 0;
1005 FOREACH(Weapons, it != WEP_Null, {
1006 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1008 WepSet set = it.m_wepset;
1009 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1013 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1014 if (weapon_cnt <= 0) return pos;
1017 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1019 int columnns = ceil(weapon_cnt / rows);
1022 float fontsize = height * 1/3;
1023 float weapon_height = height * 2/3;
1024 float weapon_width = sbwidth / columnns / rows;
1026 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1027 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1028 vector tmp = '0 0 0';
1030 tmp.y = height * rows;
1033 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1035 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1036 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1038 // column highlighting
1039 for (int i = 0; i < columnns; ++i)
1042 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);
1046 for (int i = 0; i < rows; ++i)
1048 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);
1051 average_accuracy = 0;
1052 int weapons_with_stats = 0;
1054 pos.x += weapon_width / 2;
1056 if (autocvar_scoreboard_accuracy_nocolors)
1059 Accuracy_LoadColors();
1061 float oldposx = pos.x;
1065 FOREACH(Weapons, it != WEP_Null, {
1066 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1068 WepSet set = it.m_wepset;
1069 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1073 if (weapon_stats >= 0)
1074 weapon_alpha = scoreboard_alpha_fg;
1076 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1079 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1081 if (weapon_stats >= 0) {
1082 weapons_with_stats += 1;
1083 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1086 s = sprintf("%d%%", weapon_stats*100);
1089 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1091 if(!autocvar_scoreboard_accuracy_nocolors)
1092 rgb = Accuracy_GetColor(weapon_stats);
1094 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1096 tmpos.x += weapon_width * rows;
1097 pos.x += weapon_width * rows;
1098 if (rows == 2 && column == columnns - 1) {
1106 if (weapons_with_stats)
1107 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1110 pos.y += 1.25 * hud_fontsize.y;
1111 pos.x = initial_posx;
1115 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1117 pos.x += hud_fontsize.x * 0.25;
1118 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1119 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1120 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1122 pos.y+= hud_fontsize.y;
1127 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1128 float stat_secrets_found, stat_secrets_total;
1129 float stat_monsters_killed, stat_monsters_total;
1133 // get monster stats
1134 stat_monsters_killed = STAT(MONSTERS_KILLED);
1135 stat_monsters_total = STAT(MONSTERS_TOTAL);
1137 // get secrets stats
1138 stat_secrets_found = STAT(SECRETS_FOUND);
1139 stat_secrets_total = STAT(SECRETS_TOTAL);
1141 // get number of rows
1142 if(stat_secrets_total)
1144 if(stat_monsters_total)
1147 // if no rows, return
1151 // draw table header
1152 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1153 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1156 vector tmp = '0 0 0';
1158 tmp.y = hud_fontsize.y * rows;
1161 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1163 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1164 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1167 if(stat_monsters_total)
1169 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1170 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1174 if(stat_secrets_total)
1176 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1177 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1181 pos.y += 1.25 * hud_fontsize.y;
1186 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1189 RANKINGS_RECEIVED_CNT = 0;
1190 for (i=RANKINGS_CNT-1; i>=0; --i)
1192 ++RANKINGS_RECEIVED_CNT;
1194 if (RANKINGS_RECEIVED_CNT == 0)
1198 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
1200 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1201 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1202 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1204 pos.y += hud_fontsize.y;
1205 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1206 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1207 vector tmp = '0 0 0';
1209 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1212 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1214 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1215 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1218 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1225 n = grecordholder[i];
1226 p = count_ordinal(i+1);
1227 if(grecordholder[i] == entcs_GetName(player_localnum))
1228 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1229 else if(!(i % 2) && scoreboard_highlight)
1230 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1231 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1232 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);
1233 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1234 pos.y += 1.25 * hud_fontsize.y;
1236 pos.y += autocvar_scoreboard_border_thickness;
1241 float hud_woulddrawscoreboard_prev;
1242 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1243 void HUD_DrawScoreboard()
1245 float hud_woulddrawscoreboard;
1246 hud_woulddrawscoreboard = scoreboard_active;
1247 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1248 hud_woulddrawscoreboard_change = time;
1249 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1252 if(hud_woulddrawscoreboard) {
1253 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1254 if (scoreboard_fadeinspeed)
1255 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1257 scoreboard_fade_alpha = 1;
1260 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1261 if (scoreboard_fadeoutspeed)
1262 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1264 scoreboard_fade_alpha = 0;
1267 if (!scoreboard_fade_alpha)
1270 HUD_UpdatePlayerTeams();
1272 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1273 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1274 scoreboard_highlight = autocvar_scoreboard_highlight;
1275 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1276 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1277 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1278 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1280 vector rgb, pos, tmp;
1284 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1285 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1287 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1288 ymax = (vid_conheight - ymin);
1290 sbwidth = xmax - xmin;
1292 // Initializes position
1298 vector sb_heading_fontsize;
1299 sb_heading_fontsize = hud_fontsize * 2;
1300 draw_beginBoldFont();
1301 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1304 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1306 // Draw the scoreboard
1307 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1311 vector team_score_baseoffset;
1312 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1313 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1315 if(tm.team == NUM_SPECTATOR)
1317 if(!tm.team && teamplay)
1320 draw_beginBoldFont();
1321 rgb = Team_ColorRGB(tm.team);
1322 str = ftos(tm.(teamscores[ts_primary]));
1323 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1325 if(ts_primary != ts_secondary)
1327 str = ftos(tm.(teamscores[ts_secondary]));
1328 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize) + eY * hud_fontsize.y * 1.5, str, hud_fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1332 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1334 rgb.x = autocvar_scoreboard_color_bg_r;
1335 rgb.y = autocvar_scoreboard_color_bg_g;
1336 rgb.z = autocvar_scoreboard_color_bg_b;
1340 rgb.x = autocvar_scoreboard_color_bg_r;
1341 rgb.y = autocvar_scoreboard_color_bg_g;
1342 rgb.z = autocvar_scoreboard_color_bg_b;
1344 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1346 if(tm.team == NUM_SPECTATOR)
1348 if(!tm.team && teamplay)
1351 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1355 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1356 if(race_speedaward) {
1357 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1358 pos.y += 1.25 * hud_fontsize.y;
1360 if(race_speedaward_alltimebest) {
1361 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1362 pos.y += 1.25 * hud_fontsize.y;
1364 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1366 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1368 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1370 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1375 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1377 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1384 item_size.x = sbwidth;
1385 item_size.y = hud_fontsize.y * 1.25;
1387 for(pl = players.sort_next; pl; pl = pl.sort_next)
1389 if(pl.team != NUM_SPECTATOR)
1391 pos.y += 1.25 * hud_fontsize.y;
1392 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1398 draw_beginBoldFont();
1399 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1401 pos.y += 1.25 * hud_fontsize.y;
1404 // Print info string
1406 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1407 tl = STAT(TIMELIMIT);
1408 fl = STAT(FRAGLIMIT);
1409 ll = STAT(LEADLIMIT);
1410 if(gametype == MAPINFO_TYPE_LMS)
1413 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1418 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1422 str = strcat(str, _(" or"));
1425 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1426 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1427 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1428 TranslateScoresLabel(teamscores_label[ts_primary])));
1432 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1433 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1434 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1435 TranslateScoresLabel(scores_label[ps_primary])));
1440 if(tl > 0 || fl > 0)
1441 str = strcat(str, _(" or"));
1444 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1445 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1446 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1447 TranslateScoresLabel(teamscores_label[ts_primary])));
1451 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1452 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1453 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1454 TranslateScoresLabel(scores_label[ps_primary])));
1459 pos.y += 1.2 * hud_fontsize.y;
1460 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1462 // print information about respawn status
1463 float respawn_time = STAT(RESPAWN_TIME);
1467 if(respawn_time < 0)
1469 // a negative number means we are awaiting respawn, time value is still the same
1470 respawn_time *= -1; // remove mark now that we checked it
1471 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1473 str = sprintf(_("^1Respawning in ^3%s^1..."),
1474 (autocvar_scoreboard_respawntime_decimals ?
1475 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1477 count_seconds(respawn_time - time)
1481 else if(time < respawn_time)
1483 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1484 (autocvar_scoreboard_respawntime_decimals ?
1485 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1487 count_seconds(respawn_time - time)
1491 else if(time >= respawn_time)
1492 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1494 pos.y += 1.2 * hud_fontsize.y;
1495 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1498 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;