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)
198 for(ent = player.sort_next; ent && HUD_ComparePlayerScores(player, ent); ent = player.sort_next)
200 SORT_SWAP(player, ent);
202 for(ent = player.sort_prev; ent != players && HUD_ComparePlayerScores(ent, player); ent = player.sort_prev)
204 SORT_SWAP(ent, player);
208 float HUD_CompareTeamScores(entity left, entity right)
212 if(left.team == NUM_SPECTATOR)
214 if(right.team == NUM_SPECTATOR)
217 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
221 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
225 for(i = 0; i < MAX_SCORE; ++i)
227 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
232 if (left.team < right.team)
238 void HUD_UpdateTeamPos(entity Team)
241 for(ent = Team.sort_next; ent && HUD_CompareTeamScores(Team, ent); ent = Team.sort_next)
243 SORT_SWAP(Team, ent);
245 for(ent = Team.sort_prev; ent != teams && HUD_CompareTeamScores(ent, Team); ent = Team.sort_prev)
247 SORT_SWAP(ent, Team);
253 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
254 LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
255 LOG_INFO(_("Usage:\n"));
256 LOG_INFO(_("^2scoreboard_columns_set default\n"));
257 LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
258 LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
259 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
261 LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n"));
262 LOG_INFO(_("^3ping^7 Ping time\n"));
263 LOG_INFO(_("^3pl^7 Packet loss\n"));
264 LOG_INFO(_("^3kills^7 Number of kills\n"));
265 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
266 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
267 LOG_INFO(_("^3frags^7 kills - suicides\n"));
268 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
269 LOG_INFO(_("^3dmg^7 The total damage done\n"));
270 LOG_INFO(_("^3dmgtaken^7 The total damage taken\n"));
271 LOG_INFO(_("^3sum^7 frags - deaths\n"));
272 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
273 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
274 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
275 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
276 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
277 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
278 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
279 LOG_INFO(_("^3rank^7 Player rank\n"));
280 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
281 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
282 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
283 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
284 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
285 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
286 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
287 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
288 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
289 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
290 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
291 LOG_INFO(_("^3score^7 Total score\n\n"));
293 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
294 "of game types, then a slash, to make the field show up only in these\n"
295 "or in all but these game types. You can also specify 'all' as a\n"
296 "field to show all fields available for the current game mode.\n\n"));
298 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
299 "include/exclude ALL teams/noteams game modes.\n\n"));
301 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
302 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
303 "right of the vertical bar aligned to the right.\n"));
304 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
305 "other gamemodes except DM.\n"));
308 // NOTE: adding a gametype with ? to not warn for an optional field
309 // make sure it's excluded in a previous exclusive rule, if any
310 // otherwise the previous exclusive rule warns anyway
311 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
312 #define SCOREBOARD_DEFAULT_COLUMNS \
314 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
315 " -teams,lms/deaths +ft,tdm/deaths" \
316 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
317 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
318 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
319 " +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
320 " +lms/lives +lms/rank" \
321 " +kh/caps +kh/pushes +kh/destroyed" \
322 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
323 " +as/objectives +nb/faults +nb/goals" \
324 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
325 " -lms,rc,cts,inv,nb/score"
327 void Cmd_HUD_SetFields(int argc)
332 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
337 // set up a temporary scoreboard layout
338 // no layout can be properly set up until score_info data haven't been received
339 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
341 scores_label[ps_primary] = strzone("score");
342 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
345 // TODO: re enable with gametype dependant cvars?
346 if(argc < 3) // no arguments provided
347 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
350 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
354 if(argv(2) == "default")
355 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
356 else if(argv(2) == "all")
359 s = "ping pl name |";
360 for(i = 0; i < MAX_SCORE; ++i)
363 if(i != ps_secondary)
364 if(scores_label[i] != "")
365 s = strcat(s, " ", scores_label[i]);
367 if(ps_secondary != ps_primary)
368 s = strcat(s, " ", scores_label[ps_secondary]);
369 s = strcat(s, " ", scores_label[ps_primary]);
370 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
377 hud_fontsize = HUD_GetFontsize("hud_fontsize");
379 for(i = 1; i < argc - 1; ++i)
385 if(substring(str, 0, 1) == "?")
388 str = substring(str, 1, strlen(str) - 1);
391 slash = strstrofs(str, "/", 0);
394 pattern = substring(str, 0, slash);
395 str = substring(str, slash + 1, strlen(str) - (slash + 1));
397 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
401 strunzone(hud_title[hud_num_fields]);
402 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
403 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
404 str = strtolower(str);
408 case "ping": hud_field[hud_num_fields] = SP_PING; break;
409 case "pl": hud_field[hud_num_fields] = SP_PL; break;
410 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
411 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
412 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
413 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
414 case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
415 case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
418 for(j = 0; j < MAX_SCORE; ++j)
419 if(str == strtolower(scores_label[j]))
420 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
428 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
432 hud_field[hud_num_fields] = j;
435 if(j == ps_secondary)
441 if(hud_num_fields >= MAX_HUD_FIELDS)
445 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
447 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
449 if(ps_primary == ps_secondary)
451 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
453 if(hud_num_fields+missing < MAX_HUD_FIELDS)
457 strunzone(hud_title[hud_num_fields]);
458 for(i = hud_num_fields; i > 0; --i)
460 hud_title[i] = hud_title[i-1];
461 hud_size[i] = hud_size[i-1];
462 hud_field[i] = hud_field[i-1];
464 hud_title[0] = strzone(TranslateScoresLabel("name"));
465 hud_field[0] = SP_NAME;
467 LOG_INFO("fixed missing field 'name'\n");
471 strunzone(hud_title[hud_num_fields]);
472 for(i = hud_num_fields; i > 1; --i)
474 hud_title[i] = hud_title[i-1];
475 hud_size[i] = hud_size[i-1];
476 hud_field[i] = hud_field[i-1];
478 hud_title[1] = strzone("|");
479 hud_field[1] = SP_SEPARATOR;
480 hud_size[1] = stringwidth("|", false, hud_fontsize);
482 LOG_INFO("fixed missing field '|'\n");
485 else if(!have_separator)
487 strunzone(hud_title[hud_num_fields]);
488 hud_title[hud_num_fields] = strzone("|");
489 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
490 hud_field[hud_num_fields] = SP_SEPARATOR;
492 LOG_INFO("fixed missing field '|'\n");
496 strunzone(hud_title[hud_num_fields]);
497 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
498 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
499 hud_field[hud_num_fields] = ps_secondary;
501 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_secondary]);
505 strunzone(hud_title[hud_num_fields]);
506 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
507 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
508 hud_field[hud_num_fields] = ps_primary;
510 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_primary]);
514 hud_field[hud_num_fields] = SP_END;
518 vector hud_field_rgb;
519 string hud_field_icon0;
520 string hud_field_icon1;
521 string hud_field_icon2;
522 vector hud_field_icon0_rgb;
523 vector hud_field_icon1_rgb;
524 vector hud_field_icon2_rgb;
525 float hud_field_icon0_alpha;
526 float hud_field_icon1_alpha;
527 float hud_field_icon2_alpha;
528 string HUD_GetField(entity pl, int field)
531 float tmp, num, denom;
534 hud_field_rgb = '1 1 1';
535 hud_field_icon0 = "";
536 hud_field_icon1 = "";
537 hud_field_icon2 = "";
538 hud_field_icon0_rgb = '1 1 1';
539 hud_field_icon1_rgb = '1 1 1';
540 hud_field_icon2_rgb = '1 1 1';
541 hud_field_icon0_alpha = 1;
542 hud_field_icon1_alpha = 1;
543 hud_field_icon2_alpha = 1;
548 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
549 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
553 tmp = max(0, min(220, f-80)) / 220;
554 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
560 f = pl.ping_packetloss;
561 tmp = pl.ping_movementloss;
562 if(f == 0 && tmp == 0)
564 str = ftos(ceil(f * 100));
566 str = strcat(str, "~", ftos(ceil(tmp * 100)));
567 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
568 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
572 if(ready_waiting && pl.ready)
574 hud_field_icon0 = "gfx/scoreboard/player_ready";
578 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
580 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
581 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
582 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
583 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
584 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
587 return entcs_GetName(pl.sv_entnum);
590 f = pl.(scores[SP_KILLS]);
591 f -= pl.(scores[SP_SUICIDES]);
595 num = pl.(scores[SP_KILLS]);
596 denom = pl.(scores[SP_DEATHS]);
599 hud_field_rgb = '0 1 0';
600 str = sprintf("%d", num);
601 } else if(num <= 0) {
602 hud_field_rgb = '1 0 0';
603 str = sprintf("%.1f", num/denom);
605 str = sprintf("%.1f", num/denom);
609 f = pl.(scores[SP_KILLS]);
610 f -= pl.(scores[SP_DEATHS]);
613 hud_field_rgb = '0 1 0';
615 hud_field_rgb = '1 1 1';
617 hud_field_rgb = '1 0 0';
622 num = pl.(scores[SP_DMG]);
625 str = sprintf("%.1f k", num/denom);
629 num = pl.(scores[SP_DMGTAKEN]);
632 str = sprintf("%.1f k", num/denom);
636 tmp = pl.(scores[field]);
637 f = scores_flags[field];
638 if(field == ps_primary)
639 hud_field_rgb = '1 1 0';
640 else if(field == ps_secondary)
641 hud_field_rgb = '0 1 1';
643 hud_field_rgb = '1 1 1';
644 return ScoreString(f, tmp);
649 float hud_fixscoreboardcolumnwidth_len;
650 float hud_fixscoreboardcolumnwidth_iconlen;
651 float hud_fixscoreboardcolumnwidth_marginlen;
653 string HUD_FixScoreboardColumnWidth(int i, string str)
658 field = hud_field[i];
660 hud_fixscoreboardcolumnwidth_iconlen = 0;
662 if(hud_field_icon0 != "")
664 sz = draw_getimagesize(hud_field_icon0);
666 if(hud_fixscoreboardcolumnwidth_iconlen < f)
667 hud_fixscoreboardcolumnwidth_iconlen = f;
670 if(hud_field_icon1 != "")
672 sz = draw_getimagesize(hud_field_icon1);
674 if(hud_fixscoreboardcolumnwidth_iconlen < f)
675 hud_fixscoreboardcolumnwidth_iconlen = f;
678 if(hud_field_icon2 != "")
680 sz = draw_getimagesize(hud_field_icon2);
682 if(hud_fixscoreboardcolumnwidth_iconlen < f)
683 hud_fixscoreboardcolumnwidth_iconlen = f;
686 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
688 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
689 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
691 hud_fixscoreboardcolumnwidth_marginlen = 0;
693 if(field == SP_NAME) // name gets all remaining space
697 namesize = sbwidth;// / hud_fontsize_x;
698 for(j = 0; j < hud_num_fields; ++j)
700 if (hud_field[i] != SP_SEPARATOR)
701 namesize -= hud_size[j] + hud_fontsize.x;
702 namesize += hud_fontsize.x;
703 hud_size[i] = namesize;
705 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
706 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
707 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
708 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
711 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
713 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
720 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, bool is_self, int pl_number)
722 TC(bool, is_self); TC(int, pl_number);
724 rgb = Team_ColorRGB(pl.team);
728 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
730 if((rgb == '1 1 1') && (!is_spec)) {
731 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
732 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
733 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
735 vector h_pos = pos - '1 1 0';
736 vector h_size = item_size + '2 0 0';
737 // alternated rows highlighting
739 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
740 else if((scoreboard_highlight) && (!(pl_number % 2)))
741 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
747 for(i = 0; i < hud_num_fields; ++i)
749 field = hud_field[i];
750 if(field == SP_SEPARATOR)
753 if(is_spec && field != SP_NAME && field != SP_PING) {
754 pos.x += hud_size[i] + hud_fontsize.x;
757 str = HUD_GetField(pl, field);
758 str = HUD_FixScoreboardColumnWidth(i, str);
760 pos.x += hud_size[i] + hud_fontsize.x;
762 if(field == SP_NAME) {
763 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
765 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
767 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
769 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
771 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
773 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
776 tmp.x = hud_size[i] + hud_fontsize.x;
777 if(hud_field_icon0 != "")
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_self, DRAWFLAG_NORMAL);
781 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);
782 if(hud_field_icon1 != "")
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_self, DRAWFLAG_NORMAL);
786 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);
787 if(hud_field_icon2 != "")
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_self, DRAWFLAG_NORMAL);
791 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);
794 if(hud_field[i] == SP_SEPARATOR)
797 for(i = hud_num_fields-1; i > 0; --i)
799 field = hud_field[i];
800 if(field == SP_SEPARATOR)
803 if(is_spec && field != SP_NAME && field != SP_PING) {
804 pos.x -= hud_size[i] + hud_fontsize.x;
808 str = HUD_GetField(pl, field);
809 str = HUD_FixScoreboardColumnWidth(i, str);
811 if(field == SP_NAME) {
812 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
814 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
816 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
818 tmp.x = hud_fixscoreboardcolumnwidth_len;
820 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
822 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
826 if(hud_field_icon0 != "")
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_self, DRAWFLAG_NORMAL);
830 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);
831 if(hud_field_icon1 != "")
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_self, DRAWFLAG_NORMAL);
835 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);
836 if(hud_field_icon2 != "")
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_self, DRAWFLAG_NORMAL);
840 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);
841 pos.x -= hud_size[i] + hud_fontsize.x;
846 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
850 * HUD_Scoreboard_MakeTable
852 * Makes a table for a team (for all playing players in DM) and fills it
855 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
857 float body_table_height;
858 vector tmp = '0 0 0', column_dim = '0 0 0';
861 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
863 pos.y += autocvar_scoreboard_border_thickness;
867 tmp.y = 1.25 * hud_fontsize.y;
871 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
873 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
876 tmp.y += autocvar_scoreboard_border_thickness;
877 tmp.y += body_table_height;
878 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
880 // separator header/table
881 pos.y += 1.25 * hud_fontsize.y;
882 tmp.y = autocvar_scoreboard_border_thickness;
883 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
885 pos.y += autocvar_scoreboard_border_thickness;
888 tmp.y = body_table_height;
890 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
892 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
894 // anyway, apply some color
895 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
897 // go back to the top to make alternated columns highlighting and to print the strings
898 pos.y -= 1.25 * hud_fontsize.y;
899 pos.y -= autocvar_scoreboard_border_thickness;
903 if (scoreboard_highlight)
905 column_dim.y = 1.25 * hud_fontsize.y; // header
906 column_dim.y += autocvar_scoreboard_border_thickness;
907 column_dim.y += body_table_height;
910 // print the strings of the columns headers and draw the columns
912 for(i = 0; i < hud_num_fields; ++i)
914 if(hud_field[i] == SP_SEPARATOR)
916 column_dim.x = hud_size[i] + hud_fontsize.x;
917 if (scoreboard_highlight)
920 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
922 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
923 pos.x += column_dim.x;
925 if(hud_field[i] == SP_SEPARATOR)
929 for(i = hud_num_fields-1; i > 0; --i)
931 if(hud_field[i] == SP_SEPARATOR)
934 pos.x -= hud_size[i];
936 if (scoreboard_highlight)
940 if (i == hud_num_fields-1)
941 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
943 column_dim.x = hud_size[i] + hud_fontsize.x;
944 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
948 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
949 tmp.x = (hud_size[i] - tmp.x);
950 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
951 pos.x -= hud_fontsize.x;
956 pos.y += 1.25 * hud_fontsize.y; // skip the header
957 pos.y += autocvar_scoreboard_border_thickness;
961 tmp.y = hud_fontsize.y * 1.25;
963 // fill the table and draw the rows
966 for(pl = players.sort_next; pl; pl = pl.sort_next)
968 if(pl.team != tm.team)
970 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
971 pos.y += 1.25 * hud_fontsize.y;
975 for(pl = players.sort_next; pl; pl = pl.sort_next)
977 if(pl.team == NUM_SPECTATOR)
979 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
980 pos.y += 1.25 * hud_fontsize.y;
985 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
986 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
991 float HUD_WouldDrawScoreboard() {
992 if (autocvar__hud_configure)
994 else if (QuickMenu_IsOpened())
996 else if (HUD_Radar_Clickable())
998 else if (scoreboard_showscores)
1000 else if (intermission == 1)
1002 else if (intermission == 2)
1004 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
1006 else if (scoreboard_showscores_force)
1011 float average_accuracy;
1012 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
1014 WepSet weapons_stat = WepSet_GetFromStat();
1015 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1016 float initial_posx = pos.x;
1017 int disownedcnt = 0;
1018 FOREACH(Weapons, it != WEP_Null, {
1019 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1021 WepSet set = it.m_wepset;
1022 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1026 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1027 if (weapon_cnt <= 0) return pos;
1030 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1032 int columnns = ceil(weapon_cnt / rows);
1035 float fontsize = height * 1/3;
1036 float weapon_height = height * 2/3;
1037 float weapon_width = sbwidth / columnns / rows;
1039 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1040 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1041 vector tmp = '0 0 0';
1043 tmp.y = height * rows;
1046 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1048 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1049 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1051 // column highlighting
1052 for (int i = 0; i < columnns; ++i)
1055 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);
1059 for (int i = 0; i < rows; ++i)
1061 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);
1064 average_accuracy = 0;
1065 int weapons_with_stats = 0;
1067 pos.x += weapon_width / 2;
1069 if (autocvar_scoreboard_accuracy_nocolors)
1072 Accuracy_LoadColors();
1074 float oldposx = pos.x;
1078 FOREACH(Weapons, it != WEP_Null, {
1079 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1081 WepSet set = it.m_wepset;
1082 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1086 if (weapon_stats >= 0)
1087 weapon_alpha = scoreboard_alpha_fg;
1089 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1092 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1094 if (weapon_stats >= 0) {
1095 weapons_with_stats += 1;
1096 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1099 s = sprintf("%d%%", weapon_stats*100);
1102 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1104 if(!autocvar_scoreboard_accuracy_nocolors)
1105 rgb = Accuracy_GetColor(weapon_stats);
1107 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1109 tmpos.x += weapon_width * rows;
1110 pos.x += weapon_width * rows;
1111 if (rows == 2 && column == columnns - 1) {
1119 if (weapons_with_stats)
1120 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1123 pos.y += 1.25 * hud_fontsize.y;
1124 pos.x = initial_posx;
1128 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1130 pos.x += hud_fontsize.x * 0.25;
1131 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1132 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1133 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1135 pos.y+= hud_fontsize.y;
1140 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1141 float stat_secrets_found, stat_secrets_total;
1142 float stat_monsters_killed, stat_monsters_total;
1146 // get monster stats
1147 stat_monsters_killed = STAT(MONSTERS_KILLED);
1148 stat_monsters_total = STAT(MONSTERS_TOTAL);
1150 // get secrets stats
1151 stat_secrets_found = STAT(SECRETS_FOUND);
1152 stat_secrets_total = STAT(SECRETS_TOTAL);
1154 // get number of rows
1155 if(stat_secrets_total)
1157 if(stat_monsters_total)
1160 // if no rows, return
1164 // draw table header
1165 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1166 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1169 vector tmp = '0 0 0';
1171 tmp.y = hud_fontsize.y * rows;
1174 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1176 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1177 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1180 if(stat_monsters_total)
1182 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1183 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1187 if(stat_secrets_total)
1189 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1190 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1194 pos.y += 1.25 * hud_fontsize.y;
1199 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1202 RANKINGS_RECEIVED_CNT = 0;
1203 for (i=RANKINGS_CNT-1; i>=0; --i)
1205 ++RANKINGS_RECEIVED_CNT;
1207 if (RANKINGS_RECEIVED_CNT == 0)
1211 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
1213 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1214 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1215 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1217 pos.y += hud_fontsize.y;
1218 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1219 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1220 vector tmp = '0 0 0';
1222 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1225 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1227 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1228 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1231 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1238 n = grecordholder[i];
1239 p = count_ordinal(i+1);
1240 if(grecordholder[i] == entcs_GetName(player_localnum))
1241 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1242 else if(!(i % 2) && scoreboard_highlight)
1243 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1244 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1245 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);
1246 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1247 pos.y += 1.25 * hud_fontsize.y;
1249 pos.y += autocvar_scoreboard_border_thickness;
1254 float hud_woulddrawscoreboard_prev;
1255 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1256 void HUD_DrawScoreboard()
1258 float hud_woulddrawscoreboard;
1259 hud_woulddrawscoreboard = scoreboard_active;
1260 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1261 hud_woulddrawscoreboard_change = time;
1262 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1265 if(hud_woulddrawscoreboard) {
1266 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1267 if (scoreboard_fadeinspeed)
1268 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1270 scoreboard_fade_alpha = 1;
1273 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1274 if (scoreboard_fadeoutspeed)
1275 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1277 scoreboard_fade_alpha = 0;
1280 if (!scoreboard_fade_alpha)
1283 if (autocvar_scoreboard_dynamichud)
1286 HUD_Scale_Disable();
1288 HUD_UpdatePlayerTeams();
1290 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1291 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1292 scoreboard_highlight = autocvar_scoreboard_highlight;
1293 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1294 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1295 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1296 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1298 vector rgb, pos, tmp;
1302 xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1303 ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1305 xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1306 ymax = (vid_conheight - ymin);
1308 sbwidth = xmax - xmin;
1310 // Initializes position
1316 vector sb_heading_fontsize;
1317 sb_heading_fontsize = hud_fontsize * 2;
1318 draw_beginBoldFont();
1319 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1322 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1324 // Draw the scoreboard
1325 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * ((autocvar_scoreboard_bg_scale > 0) ? autocvar_scoreboard_bg_scale : 0.25);
1329 vector team_score_baseoffset;
1330 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1331 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1333 if(tm.team == NUM_SPECTATOR)
1335 if(!tm.team && teamplay)
1338 draw_beginBoldFont();
1339 rgb = Team_ColorRGB(tm.team);
1340 str = ftos(tm.(teamscores[ts_primary]));
1341 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1343 if(ts_primary != ts_secondary)
1345 str = ftos(tm.(teamscores[ts_secondary]));
1346 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);
1350 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1352 rgb.x = autocvar_scoreboard_color_bg_r;
1353 rgb.y = autocvar_scoreboard_color_bg_g;
1354 rgb.z = autocvar_scoreboard_color_bg_b;
1358 rgb.x = autocvar_scoreboard_color_bg_r;
1359 rgb.y = autocvar_scoreboard_color_bg_g;
1360 rgb.z = autocvar_scoreboard_color_bg_b;
1362 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1364 if(tm.team == NUM_SPECTATOR)
1366 if(!tm.team && teamplay)
1369 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1373 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1374 if(race_speedaward) {
1375 drawcolorcodedstring(pos, sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward, race_speedaward_unit, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1376 pos.y += 1.25 * hud_fontsize.y;
1378 if(race_speedaward_alltimebest) {
1379 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d%s ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_unit, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1380 pos.y += 1.25 * hud_fontsize.y;
1382 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1384 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1386 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1388 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1393 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1395 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1402 item_size.x = sbwidth;
1403 item_size.y = hud_fontsize.y * 1.25;
1405 for(pl = players.sort_next; pl; pl = pl.sort_next)
1407 if(pl.team != NUM_SPECTATOR)
1409 pos.y += 1.25 * hud_fontsize.y;
1410 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1416 draw_beginBoldFont();
1417 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1419 pos.y += 1.25 * hud_fontsize.y;
1422 // Print info string
1424 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1425 tl = STAT(TIMELIMIT);
1426 fl = STAT(FRAGLIMIT);
1427 ll = STAT(LEADLIMIT);
1428 if(gametype == MAPINFO_TYPE_LMS)
1431 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1436 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1440 str = strcat(str, _(" or"));
1443 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1444 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1445 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1446 TranslateScoresLabel(teamscores_label[ts_primary])));
1450 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1451 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1452 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1453 TranslateScoresLabel(scores_label[ps_primary])));
1458 if(tl > 0 || fl > 0)
1459 str = strcat(str, _(" or"));
1462 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1463 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1464 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1465 TranslateScoresLabel(teamscores_label[ts_primary])));
1469 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1470 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1471 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1472 TranslateScoresLabel(scores_label[ps_primary])));
1477 pos.y += 1.2 * hud_fontsize.y;
1478 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1480 // print information about respawn status
1481 float respawn_time = STAT(RESPAWN_TIME);
1485 if(respawn_time < 0)
1487 // a negative number means we are awaiting respawn, time value is still the same
1488 respawn_time *= -1; // remove mark now that we checked it
1489 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1491 str = sprintf(_("^1Respawning in ^3%s^1..."),
1492 (autocvar_scoreboard_respawntime_decimals ?
1493 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1495 count_seconds(respawn_time - time)
1499 else if(time < respawn_time)
1501 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1502 (autocvar_scoreboard_respawntime_decimals ?
1503 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1505 count_seconds(respawn_time - time)
1509 else if(time >= respawn_time)
1510 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1512 pos.y += 1.2 * hud_fontsize.y;
1513 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1516 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;