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 "drops": return CTX(_("SCO^drops"));
37 case "faults": return CTX(_("SCO^faults"));
38 case "fckills": return CTX(_("SCO^fckills"));
39 case "goals": return CTX(_("SCO^goals"));
40 case "kckills": return CTX(_("SCO^kckills"));
41 case "kdratio": return CTX(_("SCO^kdratio"));
42 case "k/d": return CTX(_("SCO^k/d"));
43 case "kd": return CTX(_("SCO^kd"));
44 case "kdr": return CTX(_("SCO^kdr"));
45 case "kills": return CTX(_("SCO^kills"));
46 case "laps": return CTX(_("SCO^laps"));
47 case "lives": return CTX(_("SCO^lives"));
48 case "losses": return CTX(_("SCO^losses"));
49 case "name": return CTX(_("SCO^name"));
50 case "sum": return CTX(_("SCO^sum"));
51 case "nick": return CTX(_("SCO^nick"));
52 case "objectives": return CTX(_("SCO^objectives"));
53 case "pickups": return CTX(_("SCO^pickups"));
54 case "ping": return CTX(_("SCO^ping"));
55 case "pl": return CTX(_("SCO^pl"));
56 case "pushes": return CTX(_("SCO^pushes"));
57 case "rank": return CTX(_("SCO^rank"));
58 case "returns": return CTX(_("SCO^returns"));
59 case "revivals": return CTX(_("SCO^revivals"));
60 case "score": return CTX(_("SCO^score"));
61 case "suicides": return CTX(_("SCO^suicides"));
62 case "takes": return CTX(_("SCO^takes"));
63 case "ticks": return CTX(_("SCO^ticks"));
72 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
73 for(i = 0; i < MAX_SCORE; ++i)
75 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
76 if(f == SFL_SORT_PRIO_PRIMARY)
78 if(f == SFL_SORT_PRIO_SECONDARY)
81 if(ps_secondary == -1)
82 ps_secondary = ps_primary;
84 for(i = 0; i < MAX_TEAMSCORE; ++i)
86 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
87 if(f == SFL_SORT_PRIO_PRIMARY)
89 if(f == SFL_SORT_PRIO_SECONDARY)
92 if(ts_secondary == -1)
93 ts_secondary = ts_primary;
98 float SetTeam(entity pl, float Team);
100 void HUD_UpdatePlayerTeams()
107 for(pl = players.sort_next; pl; pl = pl.sort_next)
110 Team = GetPlayerColor(pl.sv_entnum);
111 if(SetTeam(pl, Team))
114 HUD_UpdatePlayerPos(pl);
118 pl = players.sort_next;
123 print(strcat("PNUM: ", ftos(num), "\n"));
128 int HUD_CompareScore(float vl, float vr, int f)
130 if(f & SFL_ZERO_IS_WORST)
132 if(vl == 0 && vr != 0)
134 if(vl != 0 && vr == 0)
138 return IS_INCREASING(f);
140 return IS_DECREASING(f);
144 float HUD_ComparePlayerScores(entity left, entity right)
147 vl = GetPlayerColor(left.sv_entnum);
148 vr = GetPlayerColor(right.sv_entnum);
160 if(vl == NUM_SPECTATOR)
162 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
164 if(!left.gotscores && right.gotscores)
169 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
173 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
178 for(i = 0; i < MAX_SCORE; ++i)
180 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
185 if (left.sv_entnum < right.sv_entnum)
191 void HUD_UpdatePlayerPos(entity player)
193 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
195 SORT_SWAP(player, other);
197 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
199 SORT_SWAP(other, player);
203 float HUD_CompareTeamScores(entity left, entity right)
207 if(left.team == NUM_SPECTATOR)
209 if(right.team == NUM_SPECTATOR)
212 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
216 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
220 for(i = 0; i < MAX_SCORE; ++i)
222 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
227 if (left.team < right.team)
233 void HUD_UpdateTeamPos(entity Team)
235 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
237 SORT_SWAP(Team, other);
239 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
241 SORT_SWAP(other, Team);
247 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
248 LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
249 LOG_INFO(_("Usage:\n"));
250 LOG_INFO(_("^2scoreboard_columns_set default\n"));
251 LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
252 LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
253 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
255 LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n"));
256 LOG_INFO(_("^3ping^7 Ping time\n"));
257 LOG_INFO(_("^3pl^7 Packet loss\n"));
258 LOG_INFO(_("^3kills^7 Number of kills\n"));
259 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
260 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
261 LOG_INFO(_("^3frags^7 kills - suicides\n"));
262 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
263 LOG_INFO(_("^3sum^7 frags - deaths\n"));
264 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
265 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
266 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
267 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
268 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
269 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
270 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
271 LOG_INFO(_("^3rank^7 Player rank\n"));
272 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
273 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
274 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
275 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
276 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
277 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
278 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
279 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
280 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
281 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
282 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
283 LOG_INFO(_("^3score^7 Total score\n\n"));
285 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
286 "of game types, then a slash, to make the field show up only in these\n"
287 "or in all but these game types. You can also specify 'all' as a\n"
288 "field to show all fields available for the current game mode.\n\n"));
290 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
291 "include/exclude ALL teams/noteams game modes.\n\n"));
293 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
294 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
295 "right of the vertical bar aligned to the right.\n"));
296 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
297 "other gamemodes except DM.\n"));
300 #define HUD_DefaultColumnLayout() \
302 "-teams,rc,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,rc,ka/suicides +ft,tdm/suicides -rc,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
303 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \
304 "+lms/lives +lms/rank " \
305 "+kh/caps +kh/pushes +kh/destroyed " \
306 "?+rc/laps ?+rc/time ?+rc/fastest " \
307 "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
310 void Cmd_HUD_SetFields(float argc)
314 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
319 // set up a temporary scoreboard layout
320 // no layout can be properly set up until score_info data haven't been received
321 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
323 scores_label[ps_primary] = strzone("score");
324 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
327 // TODO: re enable with gametype dependant cvars?
328 if(argc < 3) // no arguments provided
329 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
332 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
336 if(argv(2) == "default")
337 argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
338 else if(argv(2) == "all")
341 s = "ping pl name |";
342 for(i = 0; i < MAX_SCORE; ++i)
345 if(i != ps_secondary)
346 if(scores_label[i] != "")
347 s = strcat(s, " ", scores_label[i]);
349 if(ps_secondary != ps_primary)
350 s = strcat(s, " ", scores_label[ps_secondary]);
351 s = strcat(s, " ", scores_label[ps_primary]);
352 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
359 hud_fontsize = HUD_GetFontsize("hud_fontsize");
361 for(i = 1; i < argc - 1; ++i)
367 if(substring(str, 0, 1) == "?")
370 str = substring(str, 1, strlen(str) - 1);
373 slash = strstrofs(str, "/", 0);
376 pattern = substring(str, 0, slash);
377 str = substring(str, slash + 1, strlen(str) - (slash + 1));
379 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
383 strunzone(hud_title[hud_num_fields]);
384 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
385 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
386 str = strtolower(str);
390 case "ping": hud_field[hud_num_fields] = SP_PING; break;
391 case "pl": hud_field[hud_num_fields] = SP_PL; break;
392 case "pl": hud_field[hud_num_fields] = SP_PL; break;
393 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
394 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
395 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
396 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
399 for(j = 0; j < MAX_SCORE; ++j)
400 if(str == strtolower(scores_label[j]))
401 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
409 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
413 hud_field[hud_num_fields] = j;
416 if(j == ps_secondary)
422 if(hud_num_fields >= MAX_HUD_FIELDS)
426 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
428 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
430 if(ps_primary == ps_secondary)
432 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
434 if(hud_num_fields+missing < MAX_HUD_FIELDS)
438 strunzone(hud_title[hud_num_fields]);
439 for(i = hud_num_fields; i > 0; --i)
441 hud_title[i] = hud_title[i-1];
442 hud_size[i] = hud_size[i-1];
443 hud_field[i] = hud_field[i-1];
445 hud_title[0] = strzone(TranslateScoresLabel("name"));
446 hud_field[0] = SP_NAME;
448 LOG_INFO("fixed missing field 'name'\n");
452 strunzone(hud_title[hud_num_fields]);
453 for(i = hud_num_fields; i > 1; --i)
455 hud_title[i] = hud_title[i-1];
456 hud_size[i] = hud_size[i-1];
457 hud_field[i] = hud_field[i-1];
459 hud_title[1] = strzone("|");
460 hud_field[1] = SP_SEPARATOR;
461 hud_size[1] = stringwidth("|", false, hud_fontsize);
463 LOG_INFO("fixed missing field '|'\n");
466 else if(!have_separator)
468 strunzone(hud_title[hud_num_fields]);
469 hud_title[hud_num_fields] = strzone("|");
470 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
471 hud_field[hud_num_fields] = SP_SEPARATOR;
473 LOG_INFO("fixed missing field '|'\n");
477 strunzone(hud_title[hud_num_fields]);
478 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
479 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
480 hud_field[hud_num_fields] = ps_secondary;
482 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_secondary]);
486 strunzone(hud_title[hud_num_fields]);
487 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
488 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
489 hud_field[hud_num_fields] = ps_primary;
491 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_primary]);
495 hud_field[hud_num_fields] = SP_END;
499 vector hud_field_rgb;
500 string hud_field_icon0;
501 string hud_field_icon1;
502 string hud_field_icon2;
503 vector hud_field_icon0_rgb;
504 vector hud_field_icon1_rgb;
505 vector hud_field_icon2_rgb;
506 float hud_field_icon0_alpha;
507 float hud_field_icon1_alpha;
508 float hud_field_icon2_alpha;
509 string HUD_GetField(entity pl, int field)
511 float tmp, num, denom;
514 hud_field_rgb = '1 1 1';
515 hud_field_icon0 = "";
516 hud_field_icon1 = "";
517 hud_field_icon2 = "";
518 hud_field_icon0_rgb = '1 1 1';
519 hud_field_icon1_rgb = '1 1 1';
520 hud_field_icon2_rgb = '1 1 1';
521 hud_field_icon0_alpha = 1;
522 hud_field_icon1_alpha = 1;
523 hud_field_icon2_alpha = 1;
528 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
529 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
533 tmp = max(0, min(220, f-80)) / 220;
534 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
540 f = pl.ping_packetloss;
541 tmp = pl.ping_movementloss;
542 if(f == 0 && tmp == 0)
544 str = ftos(ceil(f * 100));
546 str = strcat(str, "~", ftos(ceil(tmp * 100)));
547 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
548 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
552 if(ready_waiting && pl.ready)
554 hud_field_icon0 = "gfx/scoreboard/player_ready";
558 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
560 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
561 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
562 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
563 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
564 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
567 return GetPlayerName(pl.sv_entnum);
570 f = pl.(scores[SP_KILLS]);
571 f -= pl.(scores[SP_SUICIDES]);
575 num = pl.(scores[SP_KILLS]);
576 denom = pl.(scores[SP_DEATHS]);
579 hud_field_rgb = '0 1 0';
580 str = sprintf("%d", num);
581 } else if(num <= 0) {
582 hud_field_rgb = '1 0 0';
583 str = sprintf("%.1f", num/denom);
585 str = sprintf("%.1f", num/denom);
589 f = pl.(scores[SP_KILLS]);
590 f -= pl.(scores[SP_DEATHS]);
593 hud_field_rgb = '0 1 0';
595 hud_field_rgb = '1 1 1';
597 hud_field_rgb = '1 0 0';
602 tmp = pl.(scores[field]);
603 f = scores_flags[field];
604 if(field == ps_primary)
605 hud_field_rgb = '1 1 0';
606 else if(field == ps_secondary)
607 hud_field_rgb = '0 1 1';
609 hud_field_rgb = '1 1 1';
610 return ScoreString(f, tmp);
615 float hud_fixscoreboardcolumnwidth_len;
616 float hud_fixscoreboardcolumnwidth_iconlen;
617 float hud_fixscoreboardcolumnwidth_marginlen;
619 string HUD_FixScoreboardColumnWidth(int i, string str)
623 field = hud_field[i];
625 hud_fixscoreboardcolumnwidth_iconlen = 0;
627 if(hud_field_icon0 != "")
629 sz = draw_getimagesize(hud_field_icon0);
631 if(hud_fixscoreboardcolumnwidth_iconlen < f)
632 hud_fixscoreboardcolumnwidth_iconlen = f;
635 if(hud_field_icon1 != "")
637 sz = draw_getimagesize(hud_field_icon1);
639 if(hud_fixscoreboardcolumnwidth_iconlen < f)
640 hud_fixscoreboardcolumnwidth_iconlen = f;
643 if(hud_field_icon2 != "")
645 sz = draw_getimagesize(hud_field_icon2);
647 if(hud_fixscoreboardcolumnwidth_iconlen < f)
648 hud_fixscoreboardcolumnwidth_iconlen = f;
651 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
653 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
654 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
656 hud_fixscoreboardcolumnwidth_marginlen = 0;
658 if(field == SP_NAME) // name gets all remaining space
662 namesize = sbwidth;// / hud_fontsize_x;
663 for(j = 0; j < hud_num_fields; ++j)
665 if (hud_field[i] != SP_SEPARATOR)
666 namesize -= hud_size[j] + hud_fontsize.x;
667 namesize += hud_fontsize.x;
668 hud_size[i] = namesize;
670 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
671 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
672 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
673 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
676 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
678 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
685 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
688 rgb = Team_ColorRGB(pl.team);
692 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
694 if((rgb == '1 1 1') && (!is_spec)) {
695 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
696 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
697 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
699 vector h_pos = pos - '1 1 0';
700 vector h_size = item_size + '2 0 0';
701 // alternated rows highlighting
703 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
704 else if((scoreboard_highlight) && (!(pl_number % 2)))
705 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
711 for(i = 0; i < hud_num_fields; ++i)
713 field = hud_field[i];
714 if(field == SP_SEPARATOR)
717 if(is_spec && field != SP_NAME && field != SP_PING) {
718 pos.x += hud_size[i] + hud_fontsize.x;
721 str = HUD_GetField(pl, field);
722 str = HUD_FixScoreboardColumnWidth(i, str);
724 pos.x += hud_size[i] + hud_fontsize.x;
726 if(field == SP_NAME) {
727 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
729 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
731 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
733 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
735 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
737 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
740 tmp.x = hud_size[i] + hud_fontsize.x;
741 if(hud_field_icon0 != "")
743 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);
745 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);
746 if(hud_field_icon1 != "")
748 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);
750 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);
751 if(hud_field_icon2 != "")
753 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);
755 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);
758 if(hud_field[i] == SP_SEPARATOR)
761 for(i = hud_num_fields-1; i > 0; --i)
763 field = hud_field[i];
764 if(field == SP_SEPARATOR)
767 if(is_spec && field != SP_NAME && field != SP_PING) {
768 pos.x -= hud_size[i] + hud_fontsize.x;
772 str = HUD_GetField(pl, field);
773 str = HUD_FixScoreboardColumnWidth(i, str);
775 if(field == SP_NAME) {
776 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
778 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
780 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
782 tmp.x = hud_fixscoreboardcolumnwidth_len;
784 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
786 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
790 if(hud_field_icon0 != "")
792 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);
794 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);
795 if(hud_field_icon1 != "")
797 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);
799 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);
800 if(hud_field_icon2 != "")
802 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);
804 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);
805 pos.x -= hud_size[i] + hud_fontsize.x;
810 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
814 * HUD_Scoreboard_MakeTable
816 * Makes a table for a team (for all playing players in DM) and fills it
819 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
821 float body_table_height;
822 vector tmp = '0 0 0', column_dim = '0 0 0';
825 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
827 pos.y += autocvar_scoreboard_border_thickness;
831 tmp.y = 1.25 * hud_fontsize.y;
835 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
837 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
840 tmp.y += autocvar_scoreboard_border_thickness;
841 tmp.y += body_table_height;
842 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
844 // separator header/table
845 pos.y += 1.25 * hud_fontsize.y;
846 tmp.y = autocvar_scoreboard_border_thickness;
847 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
849 pos.y += autocvar_scoreboard_border_thickness;
852 tmp.y = body_table_height;
854 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
856 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
858 // anyway, apply some color
859 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
861 // go back to the top to make alternated columns highlighting and to print the strings
862 pos.y -= 1.25 * hud_fontsize.y;
863 pos.y -= autocvar_scoreboard_border_thickness;
867 if (scoreboard_highlight)
869 column_dim.y = 1.25 * hud_fontsize.y; // header
870 column_dim.y += autocvar_scoreboard_border_thickness;
871 column_dim.y += body_table_height;
874 // print the strings of the columns headers and draw the columns
876 for(i = 0; i < hud_num_fields; ++i)
878 if(hud_field[i] == SP_SEPARATOR)
880 column_dim.x = hud_size[i] + hud_fontsize.x;
881 if (scoreboard_highlight)
884 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
886 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
887 pos.x += column_dim.x;
889 if(hud_field[i] == SP_SEPARATOR)
893 for(i = hud_num_fields-1; i > 0; --i)
895 if(hud_field[i] == SP_SEPARATOR)
898 pos.x -= hud_size[i];
900 if (scoreboard_highlight)
904 if (i == hud_num_fields-1)
905 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
907 column_dim.x = hud_size[i] + hud_fontsize.x;
908 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
912 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
913 tmp.x = (hud_size[i] - tmp.x);
914 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
915 pos.x -= hud_fontsize.x;
920 pos.y += 1.25 * hud_fontsize.y; // skip the header
921 pos.y += autocvar_scoreboard_border_thickness;
925 tmp.y = hud_fontsize.y * 1.25;
927 // fill the table and draw the rows
930 for(pl = players.sort_next; pl; pl = pl.sort_next)
932 if(pl.team != tm.team)
934 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
935 pos.y += 1.25 * hud_fontsize.y;
939 for(pl = players.sort_next; pl; pl = pl.sort_next)
941 if(pl.team == NUM_SPECTATOR)
943 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
944 pos.y += 1.25 * hud_fontsize.y;
949 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
950 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
955 float HUD_WouldDrawScoreboard() {
956 if (autocvar__hud_configure)
958 else if (QuickMenu_IsOpened())
960 else if (HUD_Radar_Clickable())
962 else if (scoreboard_showscores)
964 else if (intermission == 1)
966 else if (intermission == 2)
968 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
970 else if (scoreboard_showscores_force)
975 float average_accuracy;
976 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
978 WepSet weapons_stat = WepSet_GetFromStat();
979 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
980 float initial_posx = pos.x;
984 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
986 setself(get_weaponinfo(i));
990 weapon_stats = weapon_accuracy[i-WEP_FIRST];
992 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
996 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1002 if(autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1006 int columnns = ceil(weapon_cnt / rows);
1009 float fontsize = height * 1/3;
1010 float weapon_height = height * 2/3;
1011 float weapon_width = sbwidth / columnns / rows;
1013 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1014 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1015 vector tmp = '0 0 0';
1017 tmp.y = height * rows;
1020 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1022 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1023 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1025 // column highlighting
1026 for(i = 0; i < columnns; ++i)
1029 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);
1033 for(i = 0; i < rows; ++i)
1035 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);
1038 average_accuracy = 0;
1039 int weapons_with_stats = 0;
1041 pos.x += weapon_width / 2;
1043 if(autocvar_scoreboard_accuracy_nocolors)
1046 Accuracy_LoadColors();
1048 float oldposx = pos.x;
1052 for(i = WEP_FIRST, column = 0; i <= WEP_LAST; ++i)
1054 setself(get_weaponinfo(i));
1057 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1059 if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
1063 if(weapon_stats >= 0)
1064 weapon_alpha = scoreboard_alpha_fg;
1066 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1069 drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1071 if(weapon_stats >= 0) {
1072 weapons_with_stats += 1;
1073 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1076 s = sprintf("%d%%", weapon_stats*100);
1079 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1081 if(!autocvar_scoreboard_accuracy_nocolors)
1082 rgb = Accuracy_GetColor(weapon_stats);
1084 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1086 tmpos.x += weapon_width * rows;
1087 pos.x += weapon_width * rows;
1088 if(rows == 2 && column == columnns - 1) {
1096 if(weapons_with_stats)
1097 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1100 pos.y += 1.25 * hud_fontsize.y;
1101 pos.x = initial_posx;
1105 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1107 pos.x += hud_fontsize.x * 0.25;
1108 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1109 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1110 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1112 pos.y+= hud_fontsize.y;
1117 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1118 float stat_secrets_found, stat_secrets_total;
1119 float stat_monsters_killed, stat_monsters_total;
1123 // get monster stats
1124 stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
1125 stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
1127 // get secrets stats
1128 stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1129 stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1131 // get number of rows
1132 if(stat_secrets_total)
1134 if(stat_monsters_total)
1137 // if no rows, return
1141 // draw table header
1142 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1143 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1146 vector tmp = '0 0 0';
1148 tmp.y = hud_fontsize.y * rows;
1151 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1153 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1154 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1157 if(stat_monsters_total)
1159 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1160 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1164 if(stat_secrets_total)
1166 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1167 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1171 pos.y += 1.25 * hud_fontsize.y;
1176 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1179 RANKINGS_RECEIVED_CNT = 0;
1180 for (i=RANKINGS_CNT-1; i>=0; --i)
1182 ++RANKINGS_RECEIVED_CNT;
1184 if (RANKINGS_RECEIVED_CNT == 0)
1188 is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1190 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1191 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1192 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1194 pos.y += hud_fontsize.y;
1195 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1196 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1197 vector tmp = '0 0 0';
1199 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1202 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1204 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1205 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1208 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1215 n = grecordholder[i];
1216 p = count_ordinal(i+1);
1217 if(grecordholder[i] == GetPlayerName(player_localnum))
1218 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1219 else if(!(i % 2) && scoreboard_highlight)
1220 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1221 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1222 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);
1223 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1224 pos.y += 1.25 * hud_fontsize.y;
1226 pos.y += autocvar_scoreboard_border_thickness;
1231 float hud_woulddrawscoreboard_prev;
1232 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1233 void HUD_DrawScoreboard()
1235 float hud_woulddrawscoreboard;
1236 hud_woulddrawscoreboard = scoreboard_active;
1237 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1238 hud_woulddrawscoreboard_change = time;
1239 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1242 if(hud_woulddrawscoreboard) {
1243 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1244 if (scoreboard_fadeinspeed)
1245 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1247 scoreboard_fade_alpha = 1;
1250 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1251 if (scoreboard_fadeoutspeed)
1252 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1254 scoreboard_fade_alpha = 0;
1257 if (!scoreboard_fade_alpha)
1260 HUD_UpdatePlayerTeams();
1262 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1263 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1264 scoreboard_highlight = autocvar_scoreboard_highlight;
1265 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1266 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1267 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1268 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1270 vector rgb, pos, tmp;
1274 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1275 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1277 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1278 ymax = (vid_conheight - ymin);
1280 sbwidth = xmax - xmin;
1282 // Initializes position
1288 vector sb_heading_fontsize;
1289 sb_heading_fontsize = hud_fontsize * 2;
1290 draw_beginBoldFont();
1291 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1294 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1296 // Draw the scoreboard
1297 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1301 vector team_score_baseoffset;
1302 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1303 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1305 if(tm.team == NUM_SPECTATOR)
1308 draw_beginBoldFont();
1309 rgb = Team_ColorRGB(tm.team);
1310 str = ftos(tm.(teamscores[ts_primary]));
1311 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1313 if(ts_primary != ts_secondary)
1315 str = ftos(tm.(teamscores[ts_secondary]));
1316 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);
1320 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1322 rgb.x = autocvar_scoreboard_color_bg_r;
1323 rgb.y = autocvar_scoreboard_color_bg_g;
1324 rgb.z = autocvar_scoreboard_color_bg_b;
1328 rgb.x = autocvar_scoreboard_color_bg_r;
1329 rgb.y = autocvar_scoreboard_color_bg_g;
1330 rgb.z = autocvar_scoreboard_color_bg_b;
1332 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1334 if(tm.team == NUM_SPECTATOR)
1337 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1341 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1342 if(race_speedaward) {
1343 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1344 pos.y += 1.25 * hud_fontsize.y;
1346 if(race_speedaward_alltimebest) {
1347 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1348 pos.y += 1.25 * hud_fontsize.y;
1350 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1352 else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1354 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1356 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1361 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1363 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1370 item_size.x = sbwidth;
1371 item_size.y = hud_fontsize.y * 1.25;
1373 for(pl = players.sort_next; pl; pl = pl.sort_next)
1375 if(pl.team != NUM_SPECTATOR)
1377 pos.y += 1.25 * hud_fontsize.y;
1378 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1384 draw_beginBoldFont();
1385 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1387 pos.y += 1.25 * hud_fontsize.y;
1390 // Print info string
1392 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1393 tl = getstatf(STAT_TIMELIMIT);
1394 fl = getstatf(STAT_FRAGLIMIT);
1395 ll = getstatf(STAT_LEADLIMIT);
1396 if(gametype == MAPINFO_TYPE_LMS)
1399 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1404 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1408 str = strcat(str, _(" or"));
1411 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1412 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1413 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1414 TranslateScoresLabel(teamscores_label[ts_primary])));
1418 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1419 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1420 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1421 TranslateScoresLabel(scores_label[ps_primary])));
1426 if(tl > 0 || fl > 0)
1427 str = strcat(str, _(" or"));
1430 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
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 a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
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 pos.y += 1.2 * hud_fontsize.y;
1446 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1448 // print information about respawn status
1449 float respawn_time = getstatf(STAT_RESPAWN_TIME);
1453 if(respawn_time < 0)
1455 // a negative number means we are awaiting respawn, time value is still the same
1456 respawn_time *= -1; // remove mark now that we checked it
1457 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1459 str = sprintf(_("^1Respawning in ^3%s^1..."),
1460 (autocvar_scoreboard_respawntime_decimals ?
1461 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1463 count_seconds(respawn_time - time)
1467 else if(time < respawn_time)
1469 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1470 (autocvar_scoreboard_respawntime_decimals ?
1471 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1473 count_seconds(respawn_time - time)
1477 else if(time >= respawn_time)
1478 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1480 pos.y += 1.2 * hud_fontsize.y;
1481 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1484 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;