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 (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 if(!autocvar__hud_configure)
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 if(menu_enabled == 1)
1267 scoreboard_fade_alpha = 1;
1268 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1269 if (scoreboard_fadeinspeed)
1270 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1272 scoreboard_fade_alpha = 1;
1275 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1276 if (scoreboard_fadeoutspeed)
1277 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1279 scoreboard_fade_alpha = 0;
1282 if (!scoreboard_fade_alpha)
1286 scoreboard_fade_alpha = 0;
1288 if (autocvar_scoreboard_dynamichud)
1291 HUD_Scale_Disable();
1293 float hud_fade_alpha_save = hud_fade_alpha;
1294 if(menu_enabled == 1)
1297 hud_fade_alpha = scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1298 HUD_Panel_UpdateCvars();
1300 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * panel_fg_alpha;
1301 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * panel_fg_alpha;
1302 scoreboard_highlight = autocvar_scoreboard_highlight;
1303 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * panel_fg_alpha;
1304 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * panel_fg_alpha;
1305 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * panel_fg_alpha;
1306 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * panel_fg_alpha;
1308 hud_fade_alpha = hud_fade_alpha_save;
1310 // don't overlap with con_notify
1311 if(!autocvar__hud_configure)
1312 panel_pos_y = max((autocvar_con_notify * autocvar_con_notifysize), panel_pos_y);
1314 HUD_Panel_DrawBg(scoreboard_fade_alpha);
1316 if(panel_bg_padding)
1318 panel_pos += '1 1 0' * panel_bg_padding;
1319 panel_size -= '2 2 0' * panel_bg_padding;
1322 HUD_UpdatePlayerTeams();
1324 vector rgb, pos, tmp;
1331 xmax = panel_pos_x + panel_size_x;
1332 ymax = panel_pos_y + panel_size_y;
1334 sbwidth = panel_size_x;
1336 // Initializes position
1342 vector sb_heading_fontsize;
1343 sb_heading_fontsize = hud_fontsize * 2;
1344 draw_beginBoldFont();
1345 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1348 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1350 // Draw the scoreboard
1351 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * ((autocvar_scoreboard_bg_scale > 0) ? autocvar_scoreboard_bg_scale : 0.25);
1355 vector team_score_baseoffset;
1356 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1357 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1359 if(tm.team == NUM_SPECTATOR)
1361 if(!tm.team && teamplay)
1364 draw_beginBoldFont();
1365 rgb = Team_ColorRGB(tm.team);
1366 str = ftos(tm.(teamscores[ts_primary]));
1367 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1369 if(ts_primary != ts_secondary)
1371 str = ftos(tm.(teamscores[ts_secondary]));
1372 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);
1376 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1378 rgb.x = autocvar_scoreboard_color_bg_r;
1379 rgb.y = autocvar_scoreboard_color_bg_g;
1380 rgb.z = autocvar_scoreboard_color_bg_b;
1384 rgb.x = autocvar_scoreboard_color_bg_r;
1385 rgb.y = autocvar_scoreboard_color_bg_g;
1386 rgb.z = autocvar_scoreboard_color_bg_b;
1388 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1390 if(tm.team == NUM_SPECTATOR)
1392 if(!tm.team && teamplay)
1395 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1399 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1400 if(race_speedaward) {
1401 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);
1402 pos.y += 1.25 * hud_fontsize.y;
1404 if(race_speedaward_alltimebest) {
1405 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);
1406 pos.y += 1.25 * hud_fontsize.y;
1408 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1410 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1412 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1414 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1419 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1421 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1428 item_size.x = sbwidth;
1429 item_size.y = hud_fontsize.y * 1.25;
1431 for(pl = players.sort_next; pl; pl = pl.sort_next)
1433 if(pl.team != NUM_SPECTATOR)
1435 pos.y += 1.25 * hud_fontsize.y;
1436 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1442 draw_beginBoldFont();
1443 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1445 pos.y += 1.25 * hud_fontsize.y;
1448 // Print info string
1450 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1451 tl = STAT(TIMELIMIT);
1452 fl = STAT(FRAGLIMIT);
1453 ll = STAT(LEADLIMIT);
1454 if(gametype == MAPINFO_TYPE_LMS)
1457 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1462 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1466 str = strcat(str, _(" or"));
1469 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1470 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1471 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1472 TranslateScoresLabel(teamscores_label[ts_primary])));
1476 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1477 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1478 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1479 TranslateScoresLabel(scores_label[ps_primary])));
1484 if(tl > 0 || fl > 0)
1485 str = strcat(str, _(" or"));
1488 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1489 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1490 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1491 TranslateScoresLabel(teamscores_label[ts_primary])));
1495 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1496 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1497 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1498 TranslateScoresLabel(scores_label[ps_primary])));
1503 pos.y += 1.2 * hud_fontsize.y;
1504 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1506 // print information about respawn status
1507 float respawn_time = STAT(RESPAWN_TIME);
1511 if(respawn_time < 0)
1513 // a negative number means we are awaiting respawn, time value is still the same
1514 respawn_time *= -1; // remove mark now that we checked it
1515 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1517 str = sprintf(_("^1Respawning in ^3%s^1..."),
1518 (autocvar_scoreboard_respawntime_decimals ?
1519 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1521 count_seconds(respawn_time - time)
1525 else if(time < respawn_time)
1527 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1528 (autocvar_scoreboard_respawntime_decimals ?
1529 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1531 count_seconds(respawn_time - time)
1535 else if(time >= respawn_time)
1536 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1538 pos.y += 1.2 * hud_fontsize.y;
1539 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1542 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;