1 #include "scoreboard.qh"
3 #include "hud/panel/quickmenu.qh"
6 #include <common/ent_cs.qh>
7 #include <common/constants.qh>
8 #include <common/mapinfo.qh>
9 #include <common/minigames/cl_minigames.qh>
10 #include <common/stats.qh>
11 #include <common/teams.qh>
13 float scoreboard_alpha_bg;
14 float scoreboard_alpha_fg;
15 float scoreboard_highlight;
16 float scoreboard_highlight_alpha;
17 float scoreboard_highlight_alpha_self;
18 float scoreboard_alpha_name;
19 float scoreboard_alpha_name_self;
21 void drawstringright(vector, string, vector, vector, float, float);
22 void drawstringcenter(vector, string, vector, vector, float, float);
24 const float SCOREBOARD_OFFSET = 50;
26 // wrapper to put all possible scores titles through gettext
27 string TranslateScoresLabel(string l)
31 case "bckills": return CTX(_("SCO^bckills"));
32 case "bctime": return CTX(_("SCO^bctime"));
33 case "caps": return CTX(_("SCO^caps"));
34 case "captime": return CTX(_("SCO^captime"));
35 case "deaths": return CTX(_("SCO^deaths"));
36 case "destroyed": return CTX(_("SCO^destroyed"));
37 case "dmg": return CTX(_("SCO^dmg"));
38 case "dmgtaken": return CTX(_("SCO^dmgtaken"));
39 case "drops": return CTX(_("SCO^drops"));
40 case "faults": return CTX(_("SCO^faults"));
41 case "fckills": return CTX(_("SCO^fckills"));
42 case "goals": return CTX(_("SCO^goals"));
43 case "kckills": return CTX(_("SCO^kckills"));
44 case "kdratio": return CTX(_("SCO^kdratio"));
45 case "k/d": return CTX(_("SCO^k/d"));
46 case "kd": return CTX(_("SCO^kd"));
47 case "kdr": return CTX(_("SCO^kdr"));
48 case "kills": return CTX(_("SCO^kills"));
49 case "laps": return CTX(_("SCO^laps"));
50 case "lives": return CTX(_("SCO^lives"));
51 case "losses": return CTX(_("SCO^losses"));
52 case "name": return CTX(_("SCO^name"));
53 case "sum": return CTX(_("SCO^sum"));
54 case "nick": return CTX(_("SCO^nick"));
55 case "objectives": return CTX(_("SCO^objectives"));
56 case "pickups": return CTX(_("SCO^pickups"));
57 case "ping": return CTX(_("SCO^ping"));
58 case "pl": return CTX(_("SCO^pl"));
59 case "pushes": return CTX(_("SCO^pushes"));
60 case "rank": return CTX(_("SCO^rank"));
61 case "returns": return CTX(_("SCO^returns"));
62 case "revivals": return CTX(_("SCO^revivals"));
63 case "score": return CTX(_("SCO^score"));
64 case "suicides": return CTX(_("SCO^suicides"));
65 case "takes": return CTX(_("SCO^takes"));
66 case "ticks": return CTX(_("SCO^ticks"));
75 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
76 for(i = 0; i < MAX_SCORE; ++i)
78 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
79 if(f == SFL_SORT_PRIO_PRIMARY)
81 if(f == SFL_SORT_PRIO_SECONDARY)
84 if(ps_secondary == -1)
85 ps_secondary = ps_primary;
87 for(i = 0; i < MAX_TEAMSCORE; ++i)
89 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
90 if(f == SFL_SORT_PRIO_PRIMARY)
92 if(f == SFL_SORT_PRIO_SECONDARY)
95 if(ts_secondary == -1)
96 ts_secondary = ts_primary;
101 float SetTeam(entity pl, float Team);
103 void HUD_UpdatePlayerTeams()
110 for(pl = players.sort_next; pl; pl = pl.sort_next)
113 Team = entcs_GetScoreTeam(pl.sv_entnum);
114 if(SetTeam(pl, Team))
117 HUD_UpdatePlayerPos(pl);
121 pl = players.sort_next;
126 print(strcat("PNUM: ", ftos(num), "\n"));
131 int HUD_CompareScore(int vl, int vr, int f)
133 TC(int, vl); TC(int, vr); TC(int, f);
134 if(f & SFL_ZERO_IS_WORST)
136 if(vl == 0 && vr != 0)
138 if(vl != 0 && vr == 0)
142 return IS_INCREASING(f);
144 return IS_DECREASING(f);
148 float HUD_ComparePlayerScores(entity left, entity right)
151 vl = entcs_GetTeam(left.sv_entnum);
152 vr = entcs_GetTeam(right.sv_entnum);
164 if(vl == NUM_SPECTATOR)
166 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
168 if(!left.gotscores && right.gotscores)
173 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
177 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
182 for(i = 0; i < MAX_SCORE; ++i)
184 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
189 if (left.sv_entnum < right.sv_entnum)
195 void HUD_UpdatePlayerPos(entity player)
197 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
199 SORT_SWAP(player, other);
201 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
203 SORT_SWAP(other, player);
207 float HUD_CompareTeamScores(entity left, entity right)
211 if(left.team == NUM_SPECTATOR)
213 if(right.team == NUM_SPECTATOR)
216 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
220 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
224 for(i = 0; i < MAX_SCORE; ++i)
226 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
231 if (left.team < right.team)
237 void HUD_UpdateTeamPos(entity Team)
239 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
241 SORT_SWAP(Team, other);
243 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
245 SORT_SWAP(other, Team);
251 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
252 LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
253 LOG_INFO(_("Usage:\n"));
254 LOG_INFO(_("^2scoreboard_columns_set default\n"));
255 LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
256 LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
257 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
259 LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n"));
260 LOG_INFO(_("^3ping^7 Ping time\n"));
261 LOG_INFO(_("^3pl^7 Packet loss\n"));
262 LOG_INFO(_("^3kills^7 Number of kills\n"));
263 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
264 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
265 LOG_INFO(_("^3frags^7 kills - suicides\n"));
266 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
267 LOG_INFO(_("^3dmg^7 The total damage done\n"));
268 LOG_INFO(_("^3dmgtaken^7 The total damage taken\n"));
269 LOG_INFO(_("^3sum^7 frags - deaths\n"));
270 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
271 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
272 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
273 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
274 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
275 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
276 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
277 LOG_INFO(_("^3rank^7 Player rank\n"));
278 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
279 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
280 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
281 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
282 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
283 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
284 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
285 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
286 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
287 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
288 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
289 LOG_INFO(_("^3score^7 Total score\n\n"));
291 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
292 "of game types, then a slash, to make the field show up only in these\n"
293 "or in all but these game types. You can also specify 'all' as a\n"
294 "field to show all fields available for the current game mode.\n\n"));
296 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
297 "include/exclude ALL teams/noteams game modes.\n\n"));
299 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
300 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
301 "right of the vertical bar aligned to the right.\n"));
302 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
303 "other gamemodes except DM.\n"));
306 // NOTE: adding a gametype with ? to not warn for an optional field
307 // make sure it's excluded in a previous exclusive rule, if any
308 // otherwise the previous exclusive rule warns anyway
309 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
310 #define SCOREBOARD_DEFAULT_COLUMNS \
312 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
313 " -teams,lms/deaths +ft,tdm/deaths" \
314 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
315 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
316 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
317 " +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
318 " +lms/lives +lms/rank" \
319 " +kh/caps +kh/pushes +kh/destroyed" \
320 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
321 " +as/objectives +nb/faults +nb/goals" \
322 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
323 " -lms,rc,cts,inv,nb/score"
325 void Cmd_HUD_SetFields(int argc)
330 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
335 // set up a temporary scoreboard layout
336 // no layout can be properly set up until score_info data haven't been received
337 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
339 scores_label[ps_primary] = strzone("score");
340 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
343 // TODO: re enable with gametype dependant cvars?
344 if(argc < 3) // no arguments provided
345 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
348 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
352 if(argv(2) == "default")
353 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
354 else if(argv(2) == "all")
357 s = "ping pl name |";
358 for(i = 0; i < MAX_SCORE; ++i)
361 if(i != ps_secondary)
362 if(scores_label[i] != "")
363 s = strcat(s, " ", scores_label[i]);
365 if(ps_secondary != ps_primary)
366 s = strcat(s, " ", scores_label[ps_secondary]);
367 s = strcat(s, " ", scores_label[ps_primary]);
368 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
375 hud_fontsize = HUD_GetFontsize("hud_fontsize");
377 for(i = 1; i < argc - 1; ++i)
383 if(substring(str, 0, 1) == "?")
386 str = substring(str, 1, strlen(str) - 1);
389 slash = strstrofs(str, "/", 0);
392 pattern = substring(str, 0, slash);
393 str = substring(str, slash + 1, strlen(str) - (slash + 1));
395 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
399 strunzone(hud_title[hud_num_fields]);
400 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
401 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
402 str = strtolower(str);
406 case "ping": hud_field[hud_num_fields] = SP_PING; break;
407 case "pl": hud_field[hud_num_fields] = SP_PL; break;
408 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
409 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
410 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
411 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
412 case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
413 case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
416 for(j = 0; j < MAX_SCORE; ++j)
417 if(str == strtolower(scores_label[j]))
418 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
426 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
430 hud_field[hud_num_fields] = j;
433 if(j == ps_secondary)
439 if(hud_num_fields >= MAX_HUD_FIELDS)
443 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
445 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
447 if(ps_primary == ps_secondary)
449 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
451 if(hud_num_fields+missing < MAX_HUD_FIELDS)
455 strunzone(hud_title[hud_num_fields]);
456 for(i = hud_num_fields; i > 0; --i)
458 hud_title[i] = hud_title[i-1];
459 hud_size[i] = hud_size[i-1];
460 hud_field[i] = hud_field[i-1];
462 hud_title[0] = strzone(TranslateScoresLabel("name"));
463 hud_field[0] = SP_NAME;
465 LOG_INFO("fixed missing field 'name'\n");
469 strunzone(hud_title[hud_num_fields]);
470 for(i = hud_num_fields; i > 1; --i)
472 hud_title[i] = hud_title[i-1];
473 hud_size[i] = hud_size[i-1];
474 hud_field[i] = hud_field[i-1];
476 hud_title[1] = strzone("|");
477 hud_field[1] = SP_SEPARATOR;
478 hud_size[1] = stringwidth("|", false, hud_fontsize);
480 LOG_INFO("fixed missing field '|'\n");
483 else if(!have_separator)
485 strunzone(hud_title[hud_num_fields]);
486 hud_title[hud_num_fields] = strzone("|");
487 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
488 hud_field[hud_num_fields] = SP_SEPARATOR;
490 LOG_INFO("fixed missing field '|'\n");
494 strunzone(hud_title[hud_num_fields]);
495 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
496 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
497 hud_field[hud_num_fields] = ps_secondary;
499 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_secondary]);
503 strunzone(hud_title[hud_num_fields]);
504 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
505 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
506 hud_field[hud_num_fields] = ps_primary;
508 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_primary]);
512 hud_field[hud_num_fields] = SP_END;
516 vector hud_field_rgb;
517 string hud_field_icon0;
518 string hud_field_icon1;
519 string hud_field_icon2;
520 vector hud_field_icon0_rgb;
521 vector hud_field_icon1_rgb;
522 vector hud_field_icon2_rgb;
523 float hud_field_icon0_alpha;
524 float hud_field_icon1_alpha;
525 float hud_field_icon2_alpha;
526 string HUD_GetField(entity pl, int field)
529 float tmp, num, denom;
532 hud_field_rgb = '1 1 1';
533 hud_field_icon0 = "";
534 hud_field_icon1 = "";
535 hud_field_icon2 = "";
536 hud_field_icon0_rgb = '1 1 1';
537 hud_field_icon1_rgb = '1 1 1';
538 hud_field_icon2_rgb = '1 1 1';
539 hud_field_icon0_alpha = 1;
540 hud_field_icon1_alpha = 1;
541 hud_field_icon2_alpha = 1;
546 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
547 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
551 tmp = max(0, min(220, f-80)) / 220;
552 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
558 f = pl.ping_packetloss;
559 tmp = pl.ping_movementloss;
560 if(f == 0 && tmp == 0)
562 str = ftos(ceil(f * 100));
564 str = strcat(str, "~", ftos(ceil(tmp * 100)));
565 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
566 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
570 if(ready_waiting && pl.ready)
572 hud_field_icon0 = "gfx/scoreboard/player_ready";
576 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
578 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
579 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
580 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
581 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
582 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
585 return entcs_GetName(pl.sv_entnum);
588 f = pl.(scores[SP_KILLS]);
589 f -= pl.(scores[SP_SUICIDES]);
593 num = pl.(scores[SP_KILLS]);
594 denom = pl.(scores[SP_DEATHS]);
597 hud_field_rgb = '0 1 0';
598 str = sprintf("%d", num);
599 } else if(num <= 0) {
600 hud_field_rgb = '1 0 0';
601 str = sprintf("%.1f", num/denom);
603 str = sprintf("%.1f", num/denom);
607 f = pl.(scores[SP_KILLS]);
608 f -= pl.(scores[SP_DEATHS]);
611 hud_field_rgb = '0 1 0';
613 hud_field_rgb = '1 1 1';
615 hud_field_rgb = '1 0 0';
620 num = pl.(scores[SP_DMG]);
623 str = sprintf("%.1f k", num/denom);
627 num = pl.(scores[SP_DMGTAKEN]);
630 str = sprintf("%.1f k", num/denom);
634 tmp = pl.(scores[field]);
635 f = scores_flags[field];
636 if(field == ps_primary)
637 hud_field_rgb = '1 1 0';
638 else if(field == ps_secondary)
639 hud_field_rgb = '0 1 1';
641 hud_field_rgb = '1 1 1';
642 return ScoreString(f, tmp);
647 float hud_fixscoreboardcolumnwidth_len;
648 float hud_fixscoreboardcolumnwidth_iconlen;
649 float hud_fixscoreboardcolumnwidth_marginlen;
651 string HUD_FixScoreboardColumnWidth(int i, string str)
656 field = hud_field[i];
658 hud_fixscoreboardcolumnwidth_iconlen = 0;
660 if(hud_field_icon0 != "")
662 sz = draw_getimagesize(hud_field_icon0);
664 if(hud_fixscoreboardcolumnwidth_iconlen < f)
665 hud_fixscoreboardcolumnwidth_iconlen = f;
668 if(hud_field_icon1 != "")
670 sz = draw_getimagesize(hud_field_icon1);
672 if(hud_fixscoreboardcolumnwidth_iconlen < f)
673 hud_fixscoreboardcolumnwidth_iconlen = f;
676 if(hud_field_icon2 != "")
678 sz = draw_getimagesize(hud_field_icon2);
680 if(hud_fixscoreboardcolumnwidth_iconlen < f)
681 hud_fixscoreboardcolumnwidth_iconlen = f;
684 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
686 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
687 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
689 hud_fixscoreboardcolumnwidth_marginlen = 0;
691 if(field == SP_NAME) // name gets all remaining space
695 namesize = sbwidth;// / hud_fontsize_x;
696 for(j = 0; j < hud_num_fields; ++j)
698 if (hud_field[i] != SP_SEPARATOR)
699 namesize -= hud_size[j] + hud_fontsize.x;
700 namesize += hud_fontsize.x;
701 hud_size[i] = namesize;
703 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
704 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
705 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
706 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
709 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
711 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
718 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, bool is_self, int pl_number)
720 TC(bool, is_self); TC(int, pl_number);
722 rgb = Team_ColorRGB(pl.team);
726 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
728 if((rgb == '1 1 1') && (!is_spec)) {
729 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
730 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
731 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
733 vector h_pos = pos - '1 1 0';
734 vector h_size = item_size + '2 0 0';
735 // alternated rows highlighting
737 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
738 else if((scoreboard_highlight) && (!(pl_number % 2)))
739 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
745 for(i = 0; i < hud_num_fields; ++i)
747 field = hud_field[i];
748 if(field == SP_SEPARATOR)
751 if(is_spec && field != SP_NAME && field != SP_PING) {
752 pos.x += hud_size[i] + hud_fontsize.x;
755 str = HUD_GetField(pl, field);
756 str = HUD_FixScoreboardColumnWidth(i, str);
758 pos.x += hud_size[i] + hud_fontsize.x;
760 if(field == SP_NAME) {
761 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
763 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
765 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
767 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
769 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
771 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
774 tmp.x = hud_size[i] + hud_fontsize.x;
775 if(hud_field_icon0 != "")
777 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);
779 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);
780 if(hud_field_icon1 != "")
782 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);
784 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);
785 if(hud_field_icon2 != "")
787 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);
789 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);
792 if(hud_field[i] == SP_SEPARATOR)
795 for(i = hud_num_fields-1; i > 0; --i)
797 field = hud_field[i];
798 if(field == SP_SEPARATOR)
801 if(is_spec && field != SP_NAME && field != SP_PING) {
802 pos.x -= hud_size[i] + hud_fontsize.x;
806 str = HUD_GetField(pl, field);
807 str = HUD_FixScoreboardColumnWidth(i, str);
809 if(field == SP_NAME) {
810 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
812 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
814 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
816 tmp.x = hud_fixscoreboardcolumnwidth_len;
818 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
820 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
824 if(hud_field_icon0 != "")
826 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);
828 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);
829 if(hud_field_icon1 != "")
831 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);
833 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);
834 if(hud_field_icon2 != "")
836 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);
838 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);
839 pos.x -= hud_size[i] + hud_fontsize.x;
844 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
848 * HUD_Scoreboard_MakeTable
850 * Makes a table for a team (for all playing players in DM) and fills it
853 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
855 float body_table_height;
856 vector tmp = '0 0 0', column_dim = '0 0 0';
859 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
861 pos.y += autocvar_scoreboard_border_thickness;
865 tmp.y = 1.25 * hud_fontsize.y;
869 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
871 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
874 tmp.y += autocvar_scoreboard_border_thickness;
875 tmp.y += body_table_height;
876 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
878 // separator header/table
879 pos.y += 1.25 * hud_fontsize.y;
880 tmp.y = autocvar_scoreboard_border_thickness;
881 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
883 pos.y += autocvar_scoreboard_border_thickness;
886 tmp.y = body_table_height;
888 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
890 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
892 // anyway, apply some color
893 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
895 // go back to the top to make alternated columns highlighting and to print the strings
896 pos.y -= 1.25 * hud_fontsize.y;
897 pos.y -= autocvar_scoreboard_border_thickness;
901 if (scoreboard_highlight)
903 column_dim.y = 1.25 * hud_fontsize.y; // header
904 column_dim.y += autocvar_scoreboard_border_thickness;
905 column_dim.y += body_table_height;
908 // print the strings of the columns headers and draw the columns
910 for(i = 0; i < hud_num_fields; ++i)
912 if(hud_field[i] == SP_SEPARATOR)
914 column_dim.x = hud_size[i] + hud_fontsize.x;
915 if (scoreboard_highlight)
918 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
920 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
921 pos.x += column_dim.x;
923 if(hud_field[i] == SP_SEPARATOR)
927 for(i = hud_num_fields-1; i > 0; --i)
929 if(hud_field[i] == SP_SEPARATOR)
932 pos.x -= hud_size[i];
934 if (scoreboard_highlight)
938 if (i == hud_num_fields-1)
939 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
941 column_dim.x = hud_size[i] + hud_fontsize.x;
942 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
946 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
947 tmp.x = (hud_size[i] - tmp.x);
948 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
949 pos.x -= hud_fontsize.x;
954 pos.y += 1.25 * hud_fontsize.y; // skip the header
955 pos.y += autocvar_scoreboard_border_thickness;
959 tmp.y = hud_fontsize.y * 1.25;
961 // fill the table and draw the rows
964 for(pl = players.sort_next; pl; pl = pl.sort_next)
966 if(pl.team != tm.team)
968 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
969 pos.y += 1.25 * hud_fontsize.y;
973 for(pl = players.sort_next; pl; pl = pl.sort_next)
975 if(pl.team == NUM_SPECTATOR)
977 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
978 pos.y += 1.25 * hud_fontsize.y;
983 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
984 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
989 float HUD_WouldDrawScoreboard() {
990 if (autocvar__hud_configure)
992 else if (QuickMenu_IsOpened())
994 else if (HUD_Radar_Clickable())
996 else if (scoreboard_showscores)
998 else if (intermission == 1)
1000 else if (intermission == 2)
1002 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
1004 else if (scoreboard_showscores_force)
1009 float average_accuracy;
1010 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
1012 WepSet weapons_stat = WepSet_GetFromStat();
1013 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1014 float initial_posx = pos.x;
1015 int disownedcnt = 0;
1016 FOREACH(Weapons, it != WEP_Null, {
1017 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1019 WepSet set = it.m_wepset;
1020 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1024 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1025 if (weapon_cnt <= 0) return pos;
1028 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1030 int columnns = ceil(weapon_cnt / rows);
1033 float fontsize = height * 1/3;
1034 float weapon_height = height * 2/3;
1035 float weapon_width = sbwidth / columnns / rows;
1037 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1038 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1039 vector tmp = '0 0 0';
1041 tmp.y = height * rows;
1044 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1046 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1047 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1049 // column highlighting
1050 for (int i = 0; i < columnns; ++i)
1053 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);
1057 for (int i = 0; i < rows; ++i)
1059 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);
1062 average_accuracy = 0;
1063 int weapons_with_stats = 0;
1065 pos.x += weapon_width / 2;
1067 if (autocvar_scoreboard_accuracy_nocolors)
1070 Accuracy_LoadColors();
1072 float oldposx = pos.x;
1076 FOREACH(Weapons, it != WEP_Null, {
1077 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1079 WepSet set = it.m_wepset;
1080 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1084 if (weapon_stats >= 0)
1085 weapon_alpha = scoreboard_alpha_fg;
1087 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1090 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1092 if (weapon_stats >= 0) {
1093 weapons_with_stats += 1;
1094 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1097 s = sprintf("%d%%", weapon_stats*100);
1100 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1102 if(!autocvar_scoreboard_accuracy_nocolors)
1103 rgb = Accuracy_GetColor(weapon_stats);
1105 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1107 tmpos.x += weapon_width * rows;
1108 pos.x += weapon_width * rows;
1109 if (rows == 2 && column == columnns - 1) {
1117 if (weapons_with_stats)
1118 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1121 pos.y += 1.25 * hud_fontsize.y;
1122 pos.x = initial_posx;
1126 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1128 pos.x += hud_fontsize.x * 0.25;
1129 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1130 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1131 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1133 pos.y+= hud_fontsize.y;
1138 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1139 float stat_secrets_found, stat_secrets_total;
1140 float stat_monsters_killed, stat_monsters_total;
1144 // get monster stats
1145 stat_monsters_killed = STAT(MONSTERS_KILLED);
1146 stat_monsters_total = STAT(MONSTERS_TOTAL);
1148 // get secrets stats
1149 stat_secrets_found = STAT(SECRETS_FOUND);
1150 stat_secrets_total = STAT(SECRETS_TOTAL);
1152 // get number of rows
1153 if(stat_secrets_total)
1155 if(stat_monsters_total)
1158 // if no rows, return
1162 // draw table header
1163 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1164 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1167 vector tmp = '0 0 0';
1169 tmp.y = hud_fontsize.y * rows;
1172 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1174 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1175 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1178 if(stat_monsters_total)
1180 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1181 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1185 if(stat_secrets_total)
1187 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1188 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1192 pos.y += 1.25 * hud_fontsize.y;
1197 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1200 RANKINGS_RECEIVED_CNT = 0;
1201 for (i=RANKINGS_CNT-1; i>=0; --i)
1203 ++RANKINGS_RECEIVED_CNT;
1205 if (RANKINGS_RECEIVED_CNT == 0)
1209 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
1211 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1212 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1213 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1215 pos.y += hud_fontsize.y;
1216 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1217 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1218 vector tmp = '0 0 0';
1220 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1223 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1225 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1226 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1229 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1236 n = grecordholder[i];
1237 p = count_ordinal(i+1);
1238 if(grecordholder[i] == entcs_GetName(player_localnum))
1239 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1240 else if(!(i % 2) && scoreboard_highlight)
1241 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1242 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1243 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);
1244 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1245 pos.y += 1.25 * hud_fontsize.y;
1247 pos.y += autocvar_scoreboard_border_thickness;
1252 float hud_woulddrawscoreboard_prev;
1253 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1254 void HUD_DrawScoreboard()
1256 float hud_woulddrawscoreboard;
1257 hud_woulddrawscoreboard = scoreboard_active;
1258 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1259 hud_woulddrawscoreboard_change = time;
1260 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1263 if(hud_woulddrawscoreboard) {
1264 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1265 if (scoreboard_fadeinspeed)
1266 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1268 scoreboard_fade_alpha = 1;
1271 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1272 if (scoreboard_fadeoutspeed)
1273 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1275 scoreboard_fade_alpha = 0;
1278 if (!scoreboard_fade_alpha)
1281 if (autocvar_scoreboard_dynamichud)
1284 HUD_Scale_Disable();
1286 HUD_UpdatePlayerTeams();
1288 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1289 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1290 scoreboard_highlight = autocvar_scoreboard_highlight;
1291 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1292 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1293 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1294 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1296 vector rgb, pos, tmp;
1300 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1301 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1303 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1304 ymax = (vid_conheight - ymin);
1306 sbwidth = xmax - xmin;
1308 // Initializes position
1314 vector sb_heading_fontsize;
1315 sb_heading_fontsize = hud_fontsize * 2;
1316 draw_beginBoldFont();
1317 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1320 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1322 // Draw the scoreboard
1323 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * ((autocvar_scoreboard_bg_scale > 0) ? autocvar_scoreboard_bg_scale : 0.25);
1327 vector team_score_baseoffset;
1328 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1329 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1331 if(tm.team == NUM_SPECTATOR)
1333 if(!tm.team && teamplay)
1336 draw_beginBoldFont();
1337 rgb = Team_ColorRGB(tm.team);
1338 str = ftos(tm.(teamscores[ts_primary]));
1339 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1341 if(ts_primary != ts_secondary)
1343 str = ftos(tm.(teamscores[ts_secondary]));
1344 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);
1348 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1350 rgb.x = autocvar_scoreboard_color_bg_r;
1351 rgb.y = autocvar_scoreboard_color_bg_g;
1352 rgb.z = autocvar_scoreboard_color_bg_b;
1356 rgb.x = autocvar_scoreboard_color_bg_r;
1357 rgb.y = autocvar_scoreboard_color_bg_g;
1358 rgb.z = autocvar_scoreboard_color_bg_b;
1360 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1362 if(tm.team == NUM_SPECTATOR)
1364 if(!tm.team && teamplay)
1367 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1371 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1372 if(race_speedaward) {
1373 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1374 pos.y += 1.25 * hud_fontsize.y;
1376 if(race_speedaward_alltimebest) {
1377 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1378 pos.y += 1.25 * hud_fontsize.y;
1380 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1382 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1384 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1386 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1391 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1393 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1400 item_size.x = sbwidth;
1401 item_size.y = hud_fontsize.y * 1.25;
1403 for(pl = players.sort_next; pl; pl = pl.sort_next)
1405 if(pl.team != NUM_SPECTATOR)
1407 pos.y += 1.25 * hud_fontsize.y;
1408 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1414 draw_beginBoldFont();
1415 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1417 pos.y += 1.25 * hud_fontsize.y;
1420 // Print info string
1422 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1423 tl = STAT(TIMELIMIT);
1424 fl = STAT(FRAGLIMIT);
1425 ll = STAT(LEADLIMIT);
1426 if(gametype == MAPINFO_TYPE_LMS)
1429 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1434 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1438 str = strcat(str, _(" or"));
1441 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1442 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1443 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1444 TranslateScoresLabel(teamscores_label[ts_primary])));
1448 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1449 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1450 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1451 TranslateScoresLabel(scores_label[ps_primary])));
1456 if(tl > 0 || fl > 0)
1457 str = strcat(str, _(" or"));
1460 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1461 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1462 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1463 TranslateScoresLabel(teamscores_label[ts_primary])));
1467 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1468 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1469 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1470 TranslateScoresLabel(scores_label[ps_primary])));
1475 pos.y += 1.2 * hud_fontsize.y;
1476 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1478 // print information about respawn status
1479 float respawn_time = STAT(RESPAWN_TIME);
1483 if(respawn_time < 0)
1485 // a negative number means we are awaiting respawn, time value is still the same
1486 respawn_time *= -1; // remove mark now that we checked it
1487 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1489 str = sprintf(_("^1Respawning in ^3%s^1..."),
1490 (autocvar_scoreboard_respawntime_decimals ?
1491 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1493 count_seconds(respawn_time - time)
1497 else if(time < respawn_time)
1499 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1500 (autocvar_scoreboard_respawntime_decimals ?
1501 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1503 count_seconds(respawn_time - time)
1507 else if(time >= respawn_time)
1508 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1510 pos.y += 1.2 * hud_fontsize.y;
1511 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1514 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;