1 #include "scoreboard.qh"
3 #include "quickmenu.qh"
4 #include <common/ent_cs.qh>
5 #include <common/constants.qh>
6 #include <common/mapinfo.qh>
7 #include <common/minigames/cl_minigames.qh>
8 #include <common/stats.qh>
9 #include <common/teams.qh>
13 string autocvar_hud_fontsize;
14 string hud_fontsize_str;
19 float sbt_fg_alpha_self;
21 float sbt_highlight_alpha;
22 float sbt_highlight_alpha_self;
24 // provide basic panel cvars to old clients
25 // TODO remove them after a future release (0.8.2+)
26 string autocvar_hud_panel_scoreboard_pos = "0.150000 0.150000";
27 string autocvar_hud_panel_scoreboard_size = "0.700000 0.700000";
28 string autocvar_hud_panel_scoreboard_bg = "border_default";
29 string autocvar_hud_panel_scoreboard_bg_color = "0 0.3 0.5";
30 string autocvar_hud_panel_scoreboard_bg_color_team = "";
31 string autocvar_hud_panel_scoreboard_bg_alpha = "0.7";
32 string autocvar_hud_panel_scoreboard_bg_border = "";
33 string autocvar_hud_panel_scoreboard_bg_padding = "";
35 float autocvar_hud_panel_scoreboard_fadeinspeed = 10;
36 float autocvar_hud_panel_scoreboard_fadeoutspeed = 5;
37 float autocvar_hud_panel_scoreboard_respawntime_decimals = 1;
38 float autocvar_hud_panel_scoreboard_table_bg_alpha = 0;
39 float autocvar_hud_panel_scoreboard_table_bg_scale = 0.25;
40 float autocvar_hud_panel_scoreboard_table_fg_alpha = 0.9;
41 float autocvar_hud_panel_scoreboard_table_fg_alpha_self = 1;
42 bool autocvar_hud_panel_scoreboard_table_highlight = true;
43 float autocvar_hud_panel_scoreboard_table_highlight_alpha = 0.2;
44 float autocvar_hud_panel_scoreboard_table_highlight_alpha_self = 0.4;
45 float autocvar_hud_panel_scoreboard_bg_teams_color_team = 0;
46 float autocvar_hud_panel_scoreboard_namesize = 15;
48 bool autocvar_hud_panel_scoreboard_accuracy = true;
49 bool autocvar_hud_panel_scoreboard_accuracy_doublerows = false;
50 bool autocvar_hud_panel_scoreboard_accuracy_nocolors = false;
52 bool autocvar_hud_panel_scoreboard_dynamichud = false;
55 void drawstringright(vector, string, vector, vector, float, float);
56 void drawstringcenter(vector, string, vector, vector, float, float);
58 // wrapper to put all possible scores titles through gettext
59 string TranslateScoresLabel(string l)
63 case "bckills": return CTX(_("SCO^bckills"));
64 case "bctime": return CTX(_("SCO^bctime"));
65 case "caps": return CTX(_("SCO^caps"));
66 case "captime": return CTX(_("SCO^captime"));
67 case "deaths": return CTX(_("SCO^deaths"));
68 case "destroyed": return CTX(_("SCO^destroyed"));
69 case "dmg": return CTX(_("SCO^damage"));
70 case "dmgtaken": return CTX(_("SCO^dmgtaken"));
71 case "drops": return CTX(_("SCO^drops"));
72 case "faults": return CTX(_("SCO^faults"));
73 case "fckills": return CTX(_("SCO^fckills"));
74 case "goals": return CTX(_("SCO^goals"));
75 case "kckills": return CTX(_("SCO^kckills"));
76 case "kdratio": return CTX(_("SCO^kdratio"));
77 case "kd": return CTX(_("SCO^k/d"));
78 case "kdr": return CTX(_("SCO^kdr"));
79 case "kills": return CTX(_("SCO^kills"));
80 case "laps": return CTX(_("SCO^laps"));
81 case "lives": return CTX(_("SCO^lives"));
82 case "losses": return CTX(_("SCO^losses"));
83 case "name": return CTX(_("SCO^name"));
84 case "sum": return CTX(_("SCO^sum"));
85 case "nick": return CTX(_("SCO^nick"));
86 case "objectives": return CTX(_("SCO^objectives"));
87 case "pickups": return CTX(_("SCO^pickups"));
88 case "ping": return CTX(_("SCO^ping"));
89 case "pl": return CTX(_("SCO^pl"));
90 case "pushes": return CTX(_("SCO^pushes"));
91 case "rank": return CTX(_("SCO^rank"));
92 case "returns": return CTX(_("SCO^returns"));
93 case "revivals": return CTX(_("SCO^revivals"));
94 case "rounds": return CTX(_("SCO^rounds won"));
95 case "score": return CTX(_("SCO^score"));
96 case "suicides": return CTX(_("SCO^suicides"));
97 case "takes": return CTX(_("SCO^takes"));
98 case "ticks": return CTX(_("SCO^ticks"));
103 void Scoreboard_InitScores()
107 ps_primary = ps_secondary = NULL;
108 ts_primary = ts_secondary = -1;
109 FOREACH(Scores, true, {
110 f = (scores_flags(it) & SFL_SORT_PRIO_MASK);
111 if(f == SFL_SORT_PRIO_PRIMARY)
113 if(f == SFL_SORT_PRIO_SECONDARY)
116 if(ps_secondary == NULL)
117 ps_secondary = ps_primary;
119 for(i = 0; i < MAX_TEAMSCORE; ++i)
121 f = (teamscores_flags(i) & SFL_SORT_PRIO_MASK);
122 if(f == SFL_SORT_PRIO_PRIMARY)
124 if(f == SFL_SORT_PRIO_SECONDARY)
127 if(ts_secondary == -1)
128 ts_secondary = ts_primary;
130 Cmd_Scoreboard_SetFields(0);
133 float SetTeam(entity pl, float Team);
135 void Scoreboard_UpdatePlayerTeams()
142 for(pl = players.sort_next; pl; pl = pl.sort_next)
145 Team = entcs_GetScoreTeam(pl.sv_entnum);
146 if(SetTeam(pl, Team))
149 Scoreboard_UpdatePlayerPos(pl);
153 pl = players.sort_next;
158 print(strcat("PNUM: ", ftos(num), "\n"));
163 int Scoreboard_CompareScore(int vl, int vr, int f)
165 TC(int, vl); TC(int, vr); TC(int, f);
166 if(f & SFL_ZERO_IS_WORST)
168 if(vl == 0 && vr != 0)
170 if(vl != 0 && vr == 0)
174 return IS_INCREASING(f);
176 return IS_DECREASING(f);
180 float Scoreboard_ComparePlayerScores(entity left, entity right)
183 vl = entcs_GetTeam(left.sv_entnum);
184 vr = entcs_GetTeam(right.sv_entnum);
196 if(vl == NUM_SPECTATOR)
198 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
200 if(!left.gotscores && right.gotscores)
205 r = Scoreboard_CompareScore(left.scores(ps_primary), right.scores(ps_primary), scores_flags(ps_primary));
209 r = Scoreboard_CompareScore(left.scores(ps_secondary), right.scores(ps_secondary), scores_flags(ps_secondary));
213 FOREACH(Scores, true, {
214 r = Scoreboard_CompareScore(left.scores(it), right.scores(it), scores_flags(it));
215 if (r >= 0) return r;
218 if (left.sv_entnum < right.sv_entnum)
224 void Scoreboard_UpdatePlayerPos(entity player)
227 for(ent = player.sort_next; ent && Scoreboard_ComparePlayerScores(player, ent); ent = player.sort_next)
229 SORT_SWAP(player, ent);
231 for(ent = player.sort_prev; ent != players && Scoreboard_ComparePlayerScores(ent, player); ent = player.sort_prev)
233 SORT_SWAP(ent, player);
237 float Scoreboard_CompareTeamScores(entity left, entity right)
241 if(left.team == NUM_SPECTATOR)
243 if(right.team == NUM_SPECTATOR)
246 r = Scoreboard_CompareScore(left.teamscores(ts_primary), right.teamscores(ts_primary), teamscores_flags(ts_primary));
250 r = Scoreboard_CompareScore(left.teamscores(ts_secondary), right.teamscores(ts_secondary), teamscores_flags(ts_secondary));
254 for(i = 0; i < MAX_TEAMSCORE; ++i)
256 r = Scoreboard_CompareScore(left.teamscores(i), right.teamscores(i), teamscores_flags(i));
261 if (left.team < right.team)
267 void Scoreboard_UpdateTeamPos(entity Team)
270 for(ent = Team.sort_next; ent && Scoreboard_CompareTeamScores(Team, ent); ent = Team.sort_next)
272 SORT_SWAP(Team, ent);
274 for(ent = Team.sort_prev; ent != teams && Scoreboard_CompareTeamScores(ent, Team); ent = Team.sort_prev)
276 SORT_SWAP(ent, Team);
280 void Cmd_Scoreboard_Help()
282 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
283 LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
284 LOG_INFO(_("Usage:\n"));
285 LOG_INFO(_("^2scoreboard_columns_set default\n"));
286 LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
287 LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
288 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n"));
291 LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n"));
292 LOG_INFO(_("^3ping^7 Ping time\n"));
293 LOG_INFO(_("^3pl^7 Packet loss\n"));
294 LOG_INFO(_("^3elo^7 Player ELO\n"));
295 LOG_INFO(_("^3kills^7 Number of kills\n"));
296 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
297 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
298 LOG_INFO(_("^3frags^7 kills - suicides\n"));
299 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
300 LOG_INFO(_("^3dmg^7 The total damage done\n"));
301 LOG_INFO(_("^3dmgtaken^7 The total damage taken\n"));
302 LOG_INFO(_("^3sum^7 frags - deaths\n"));
303 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
304 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
305 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
306 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
307 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
308 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
309 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
310 LOG_INFO(_("^3rank^7 Player rank\n"));
311 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
312 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
313 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
314 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
315 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
316 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
317 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
318 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
319 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
320 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
321 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
322 LOG_INFO(_("^3score^7 Total score\n"));
325 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
326 "of game types, then a slash, to make the field show up only in these\n"
327 "or in all but these game types. You can also specify 'all' as a\n"
328 "field to show all fields available for the current game mode.\n\n"));
330 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
331 "include/exclude ALL teams/noteams game modes.\n\n"));
333 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
334 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
335 "right of the vertical bar aligned to the right.\n"));
336 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
337 "other gamemodes except DM.\n"));
340 // NOTE: adding a gametype with ? to not warn for an optional field
341 // make sure it's excluded in a previous exclusive rule, if any
342 // otherwise the previous exclusive rule warns anyway
343 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
344 #define SCOREBOARD_DEFAULT_COLUMNS \
346 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
347 " -teams,lms/deaths +ft,tdm/deaths" \
348 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
349 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
350 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
351 " +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
352 " +lms/lives +lms/rank" \
353 " +kh/caps +kh/pushes +kh/destroyed" \
354 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
355 " +as/objectives +nb/faults +nb/goals" \
356 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
357 " -lms,rc,cts,inv,nb/score"
359 void Cmd_Scoreboard_SetFields(int argc)
364 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
368 return; // do nothing, we don't know gametype and scores yet
370 // sbt_fields uses strunzone on the titles!
371 if(!sbt_field_title[0])
372 for(i = 0; i < MAX_SBT_FIELDS; ++i)
373 sbt_field_title[i] = strzone("(null)");
375 // TODO: re enable with gametype dependant cvars?
376 if(argc < 3) // no arguments provided
377 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
380 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
384 if(argv(2) == "default")
385 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
386 else if(argv(2) == "all")
389 s = "ping pl name |";
390 FOREACH(Scores, true, {
392 if(it != ps_secondary)
393 if(scores_label(it) != "")
394 s = strcat(s, " ", scores_label(it));
396 if(ps_secondary != ps_primary)
397 s = strcat(s, " ", scores_label(ps_secondary));
398 s = strcat(s, " ", scores_label(ps_primary));
399 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
406 hud_fontsize = HUD_GetFontsize("hud_fontsize");
408 for(i = 1; i < argc - 1; ++i)
414 if(substring(str, 0, 1) == "?")
417 str = substring(str, 1, strlen(str) - 1);
420 slash = strstrofs(str, "/", 0);
423 pattern = substring(str, 0, slash);
424 str = substring(str, slash + 1, strlen(str) - (slash + 1));
426 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
430 strunzone(sbt_field_title[sbt_num_fields]);
431 sbt_field_title[sbt_num_fields] = strzone(TranslateScoresLabel(str));
432 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
433 str = strtolower(str);
438 case "ping": sbt_field[sbt_num_fields] = SP_PING; break;
439 case "pl": sbt_field[sbt_num_fields] = SP_PL; break;
440 case "kd": case "kdr": case "kdratio": sbt_field[sbt_num_fields] = SP_KDRATIO; break;
441 case "sum": case "diff": case "k-d": sbt_field[sbt_num_fields] = SP_SUM; break;
442 case "name": case "nick": sbt_field[sbt_num_fields] = SP_NAME; have_name = true; break;
443 case "|": sbt_field[sbt_num_fields] = SP_SEPARATOR; have_separator = true; break;
444 case "elo": sbt_field[sbt_num_fields] = SP_ELO; break;
445 case "dmg": case "damage": sbt_field[sbt_num_fields] = SP_DMG; break;
446 case "dmgtaken": case "damagetaken": sbt_field[sbt_num_fields] = SP_DMGTAKEN; break;
449 FOREACH(Scores, true, {
450 if (str == strtolower(scores_label(it))) {
452 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
462 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
466 sbt_field[sbt_num_fields] = j;
469 if(j == ps_secondary)
475 if(sbt_num_fields >= MAX_SBT_FIELDS)
479 if(scores_flags(ps_primary) & SFL_ALLOW_HIDE)
481 if(scores_flags(ps_secondary) & SFL_ALLOW_HIDE)
483 if(ps_primary == ps_secondary)
485 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
487 if(sbt_num_fields + missing < MAX_SBT_FIELDS)
491 strunzone(sbt_field_title[sbt_num_fields]);
492 for(i = sbt_num_fields; i > 0; --i)
494 sbt_field_title[i] = sbt_field_title[i-1];
495 sbt_field_size[i] = sbt_field_size[i-1];
496 sbt_field[i] = sbt_field[i-1];
498 sbt_field_title[0] = strzone(TranslateScoresLabel("name"));
499 sbt_field[0] = SP_NAME;
501 LOG_INFO("fixed missing field 'name'\n");
505 strunzone(sbt_field_title[sbt_num_fields]);
506 for(i = sbt_num_fields; i > 1; --i)
508 sbt_field_title[i] = sbt_field_title[i-1];
509 sbt_field_size[i] = sbt_field_size[i-1];
510 sbt_field[i] = sbt_field[i-1];
512 sbt_field_title[1] = strzone("|");
513 sbt_field[1] = SP_SEPARATOR;
514 sbt_field_size[1] = stringwidth("|", false, hud_fontsize);
516 LOG_INFO("fixed missing field '|'\n");
519 else if(!have_separator)
521 strunzone(sbt_field_title[sbt_num_fields]);
522 sbt_field_title[sbt_num_fields] = strzone("|");
523 sbt_field_size[sbt_num_fields] = stringwidth("|", false, hud_fontsize);
524 sbt_field[sbt_num_fields] = SP_SEPARATOR;
526 LOG_INFO("fixed missing field '|'\n");
530 strunzone(sbt_field_title[sbt_num_fields]);
531 sbt_field_title[sbt_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_secondary)));
532 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
533 sbt_field[sbt_num_fields] = ps_secondary;
535 LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_secondary));
539 strunzone(sbt_field_title[sbt_num_fields]);
540 sbt_field_title[sbt_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_primary)));
541 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
542 sbt_field[sbt_num_fields] = ps_primary;
544 LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_primary));
548 sbt_field[sbt_num_fields] = SP_END;
552 vector sbt_field_rgb;
553 string sbt_field_icon0;
554 string sbt_field_icon1;
555 string sbt_field_icon2;
556 vector sbt_field_icon0_rgb;
557 vector sbt_field_icon1_rgb;
558 vector sbt_field_icon2_rgb;
559 float sbt_field_icon0_alpha;
560 float sbt_field_icon1_alpha;
561 float sbt_field_icon2_alpha;
562 string Scoreboard_GetField(entity pl, PlayerScoreField field)
564 float tmp, num, denom;
567 sbt_field_rgb = '1 1 1';
568 sbt_field_icon0 = "";
569 sbt_field_icon1 = "";
570 sbt_field_icon2 = "";
571 sbt_field_icon0_rgb = '1 1 1';
572 sbt_field_icon1_rgb = '1 1 1';
573 sbt_field_icon2_rgb = '1 1 1';
574 sbt_field_icon0_alpha = 1;
575 sbt_field_icon1_alpha = 1;
576 sbt_field_icon2_alpha = 1;
581 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
582 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
586 tmp = max(0, min(220, f-80)) / 220;
587 sbt_field_rgb = '1 1 1' - '0 1 1' * tmp;
593 f = pl.ping_packetloss;
594 tmp = pl.ping_movementloss;
595 if(f == 0 && tmp == 0)
597 str = ftos(ceil(f * 100));
599 str = strcat(str, "~", ftos(ceil(tmp * 100)));
600 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
601 sbt_field_rgb = '1 0.5 0.5' - '0 0.5 0.5' * tmp;
605 if(ready_waiting && pl.ready)
607 sbt_field_icon0 = "gfx/scoreboard/player_ready";
611 f = entcs_GetClientColors(pl.sv_entnum);
613 sbt_field_icon0 = "gfx/scoreboard/playercolor_base";
614 sbt_field_icon1 = "gfx/scoreboard/playercolor_shirt";
615 sbt_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
616 sbt_field_icon2 = "gfx/scoreboard/playercolor_pants";
617 sbt_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
620 return entcs_GetName(pl.sv_entnum);
623 f = pl.(scores(SP_KILLS));
624 f -= pl.(scores(SP_SUICIDES));
628 num = pl.(scores(SP_KILLS));
629 denom = pl.(scores(SP_DEATHS));
632 sbt_field_rgb = '0 1 0';
633 str = sprintf("%d", num);
634 } else if(num <= 0) {
635 sbt_field_rgb = '1 0 0';
636 str = sprintf("%.1f", num/denom);
638 str = sprintf("%.1f", num/denom);
642 f = pl.(scores(SP_KILLS));
643 f -= pl.(scores(SP_DEATHS));
646 sbt_field_rgb = '0 1 0';
648 sbt_field_rgb = '1 1 1';
650 sbt_field_rgb = '1 0 0';
656 float elo = pl.(scores(SP_ELO));
658 case -1: return "...";
659 case -2: return _("N/A");
660 default: return ftos(elo);
664 case SP_DMG: case SP_DMGTAKEN:
665 return sprintf("%.1f k", pl.(scores(field)) / 1000);
668 tmp = pl.(scores(field));
669 f = scores_flags(field);
670 if(field == ps_primary)
671 sbt_field_rgb = '1 1 0';
672 else if(field == ps_secondary)
673 sbt_field_rgb = '0 1 1';
675 sbt_field_rgb = '1 1 1';
676 return ScoreString(f, tmp);
681 float sbt_fixcolumnwidth_len;
682 float sbt_fixcolumnwidth_iconlen;
683 float sbt_fixcolumnwidth_marginlen;
685 string Scoreboard_FixColumnWidth(int i, string str)
691 sbt_fixcolumnwidth_iconlen = 0;
693 if(sbt_field_icon0 != "")
695 sz = draw_getimagesize(sbt_field_icon0);
697 if(sbt_fixcolumnwidth_iconlen < f)
698 sbt_fixcolumnwidth_iconlen = f;
701 if(sbt_field_icon1 != "")
703 sz = draw_getimagesize(sbt_field_icon1);
705 if(sbt_fixcolumnwidth_iconlen < f)
706 sbt_fixcolumnwidth_iconlen = f;
709 if(sbt_field_icon2 != "")
711 sz = draw_getimagesize(sbt_field_icon2);
713 if(sbt_fixcolumnwidth_iconlen < f)
714 sbt_fixcolumnwidth_iconlen = f;
717 sbt_fixcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
719 if(sbt_fixcolumnwidth_iconlen != 0)
720 sbt_fixcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
722 sbt_fixcolumnwidth_marginlen = 0;
724 if(sbt_field[i] == SP_NAME) // name gets all remaining space
727 float remaining_space = 0;
728 for(j = 0; j < sbt_num_fields; ++j)
730 if (sbt_field[i] != SP_SEPARATOR)
731 remaining_space += sbt_field_size[j] + hud_fontsize.x;
732 sbt_field_size[i] = panel_size.x - remaining_space;
734 if (sbt_fixcolumnwidth_iconlen != 0)
735 remaining_space += sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
736 float namesize = panel_size.x - remaining_space;
737 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
738 sbt_fixcolumnwidth_len = stringwidth(str, true, hud_fontsize);
740 max_namesize = vid_conwidth - remaining_space;
743 sbt_fixcolumnwidth_len = stringwidth(str, false, hud_fontsize);
745 f = sbt_fixcolumnwidth_len + sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
746 if(sbt_field_size[i] < f)
747 sbt_field_size[i] = f;
752 void Scoreboard_initFieldSizes()
754 for(int i = 0; i < sbt_num_fields; ++i)
755 sbt_field_size[i] = stringwidth(sbt_field_title[i], false, hud_fontsize);
758 vector Scoreboard_DrawHeader(vector pos, vector rgb)
761 vector column_dim = eY * panel_size.y;
762 vector text_offset = eY * (1.25 - 1) / 2 * hud_fontsize.y;
763 pos.x += hud_fontsize.x * 0.5;
764 for(i = 0; i < sbt_num_fields; ++i)
766 if(sbt_field[i] == SP_SEPARATOR)
768 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
771 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
772 drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
773 pos.x += column_dim.x;
775 if(sbt_field[i] == SP_SEPARATOR)
777 pos.x = panel_pos.x + panel_size.x - hud_fontsize.x * 0.5;
778 for(i = sbt_num_fields - 1; i > 0; --i)
780 if(sbt_field[i] == SP_SEPARATOR)
783 pos.x -= sbt_field_size[i];
788 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
789 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
792 text_offset.x = sbt_field_size[i] - stringwidth(sbt_field_title[i], false, hud_fontsize);
793 drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
794 pos.x -= hud_fontsize.x;
799 pos.y += 1.25 * hud_fontsize.y;
803 void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number)
805 TC(bool, is_self); TC(int, pl_number);
807 bool is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
809 vector h_pos = item_pos;
810 vector h_size = eX * panel_size.x + eY * hud_fontsize.y * 1.25;
811 // alternated rows highlighting
813 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
814 else if((sbt_highlight) && (!(pl_number % 2)))
815 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
817 vector pos = item_pos;
818 pos.x += hud_fontsize.x * 0.5;
819 pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
820 vector tmp = '0 0 0';
822 PlayerScoreField field;
823 for(i = 0; i < sbt_num_fields; ++i)
825 field = sbt_field[i];
826 if(field == SP_SEPARATOR)
829 if(is_spec && field != SP_NAME && field != SP_PING) {
830 pos.x += sbt_field_size[i] + hud_fontsize.x;
833 str = Scoreboard_GetField(pl, field);
834 str = Scoreboard_FixColumnWidth(i, str);
836 pos.x += sbt_field_size[i] + hud_fontsize.x;
838 if(field == SP_NAME) {
839 tmp.x = sbt_field_size[i] - hud_fontsize.x * sbt_fixcolumnwidth_iconlen - sbt_fixcolumnwidth_marginlen + hud_fontsize.x;
841 drawcolorcodedstring(pos - tmp, str, hud_fontsize, sbt_fg_alpha_self, DRAWFLAG_NORMAL);
843 drawcolorcodedstring(pos - tmp, str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
845 tmp.x = sbt_fixcolumnwidth_len + hud_fontsize.x;
847 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, sbt_fg_alpha_self, DRAWFLAG_NORMAL);
849 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
852 tmp.x = sbt_field_size[i] + hud_fontsize.x;
853 if(sbt_field_icon0 != "")
855 drawpic(pos - tmp, sbt_field_icon0, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, sbt_field_icon0_alpha * sbt_fg_alpha_self, DRAWFLAG_NORMAL);
857 drawpic(pos - tmp, sbt_field_icon0, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, sbt_field_icon0_alpha * sbt_fg_alpha, DRAWFLAG_NORMAL);
858 if(sbt_field_icon1 != "")
860 drawpic(pos - tmp, sbt_field_icon1, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, sbt_field_icon1_alpha * sbt_fg_alpha_self, DRAWFLAG_NORMAL);
862 drawpic(pos - tmp, sbt_field_icon1, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, sbt_field_icon1_alpha * sbt_fg_alpha, DRAWFLAG_NORMAL);
863 if(sbt_field_icon2 != "")
865 drawpic(pos - tmp, sbt_field_icon2, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon2_rgb, sbt_field_icon2_alpha * sbt_fg_alpha_self, DRAWFLAG_NORMAL);
867 drawpic(pos - tmp, sbt_field_icon2, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon2_rgb, sbt_field_icon2_alpha * sbt_fg_alpha, DRAWFLAG_NORMAL);
870 if(sbt_field[i] == SP_SEPARATOR)
872 pos.x = item_pos.x + panel_size.x - hud_fontsize.x * 0.5;
873 for(i = sbt_num_fields-1; i > 0; --i)
875 field = sbt_field[i];
876 if(field == SP_SEPARATOR)
879 if(is_spec && field != SP_NAME && field != SP_PING) {
880 pos.x -= sbt_field_size[i] + hud_fontsize.x;
884 str = Scoreboard_GetField(pl, field);
885 str = Scoreboard_FixColumnWidth(i, str);
887 if(field == SP_NAME) {
888 tmp.x = sbt_fixcolumnwidth_len; // left or right aligned? let's put it right...
890 drawcolorcodedstring(pos - tmp, str, hud_fontsize, sbt_fg_alpha_self, DRAWFLAG_NORMAL);
892 drawcolorcodedstring(pos - tmp, str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
894 tmp.x = sbt_fixcolumnwidth_len;
896 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, sbt_fg_alpha_self, DRAWFLAG_NORMAL);
898 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
901 tmp.x = sbt_field_size[i];
902 if(sbt_field_icon0 != "")
904 drawpic(pos - tmp, sbt_field_icon0, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, sbt_field_icon0_alpha * sbt_fg_alpha_self, DRAWFLAG_NORMAL);
906 drawpic(pos - tmp, sbt_field_icon0, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, sbt_field_icon0_alpha * sbt_fg_alpha, DRAWFLAG_NORMAL);
907 if(sbt_field_icon1 != "")
909 drawpic(pos - tmp, sbt_field_icon1, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, sbt_field_icon1_alpha * sbt_fg_alpha_self, DRAWFLAG_NORMAL);
911 drawpic(pos - tmp, sbt_field_icon1, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, sbt_field_icon1_alpha * sbt_fg_alpha, DRAWFLAG_NORMAL);
912 if(sbt_field_icon2 != "")
914 drawpic(pos - tmp, sbt_field_icon2, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon2_rgb, sbt_field_icon2_alpha * sbt_fg_alpha_self, DRAWFLAG_NORMAL);
916 drawpic(pos - tmp, sbt_field_icon2, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon2_rgb, sbt_field_icon2_alpha * sbt_fg_alpha, DRAWFLAG_NORMAL);
917 pos.x -= sbt_field_size[i] + hud_fontsize.x;
922 drawfill(h_pos, h_size, '0 0 0', 0.5 * panel_fg_alpha, DRAWFLAG_NORMAL);
925 vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
930 panel_size.y = 1.25 * hud_fontsize.y * (1 + max(1, tm.team_size));
931 panel_size.y += panel_bg_padding * 2;
934 vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
935 if(panel.current_panel_bg != "0")
936 end_pos.y += panel_bg_border * 2;
940 panel_pos += '1 1 0' * panel_bg_padding;
941 panel_size -= '2 2 0' * panel_bg_padding;
945 vector tmp = eX * panel_size.x + eY * 1.25 * hud_fontsize.y;
949 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', sbt_bg_alpha, DRAWFLAG_NORMAL);
951 pos.y += 1.25 * hud_fontsize.y;
954 tmp.y = panel_size.y - 1.25 * hud_fontsize.y;
956 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
959 // print header row and highlight columns
960 pos = Scoreboard_DrawHeader(panel_pos, rgb);
962 // fill the table and draw the rows
965 for(pl = players.sort_next; pl; pl = pl.sort_next)
967 if(pl.team != tm.team)
969 Scoreboard_DrawItem(pos, rgb, pl, (pl.sv_entnum == player_localnum), i);
970 pos.y += 1.25 * hud_fontsize.y;
974 for(pl = players.sort_next; pl; pl = pl.sort_next)
976 if(pl.team == NUM_SPECTATOR)
978 Scoreboard_DrawItem(pos, rgb, pl, (pl.sv_entnum == player_localnum), i);
979 pos.y += 1.25 * hud_fontsize.y;
983 panel_size.x += panel_bg_padding * 2; // restore initial width
987 float Scoreboard_WouldDraw() {
988 if (QuickMenu_IsOpened())
990 else if (HUD_Radar_Clickable())
992 else if (scoreboard_showscores)
994 else if (intermission == 1)
996 else if (intermission == 2)
998 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
1000 else if (scoreboard_showscores_force)
1005 float average_accuracy;
1006 vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size)
1008 WepSet weapons_stat = WepSet_GetFromStat();
1009 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1010 int disownedcnt = 0;
1012 FOREACH(Weapons, it != WEP_Null, {
1013 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1015 WepSet set = it.m_wepset;
1016 if (weapon_stats < 0)
1018 if (!(weapons_stat & set) && (it.spawnflags & WEP_FLAG_HIDDEN || it.spawnflags & WEP_FLAG_MUTATORBLOCKED))
1020 else if (!(weapons_stat & set || weapons_inmap & set))
1025 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt - nHidden;
1026 if (weapon_cnt <= 0) return pos;
1029 if (autocvar_hud_panel_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - nHidden - 1) * 0.5))
1031 int columnns = ceil(weapon_cnt / rows);
1033 float weapon_height = 29;
1034 float height = hud_fontsize.y + weapon_height;
1036 drawstring(pos + eX * panel_bg_padding, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1037 pos.y += 1.25 * hud_fontsize.y;
1038 if(panel.current_panel_bg != "0")
1039 pos.y += panel_bg_border;
1042 panel_size.y = height * rows;
1043 panel_size.y += panel_bg_padding * 2;
1046 vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
1047 if(panel.current_panel_bg != "0")
1048 end_pos.y += panel_bg_border * 2;
1050 if(panel_bg_padding)
1052 panel_pos += '1 1 0' * panel_bg_padding;
1053 panel_size -= '2 2 0' * panel_bg_padding;
1057 vector tmp = panel_size;
1059 float weapon_width = tmp.x / columnns / rows;
1062 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1066 // column highlighting
1067 for (int i = 0; i < columnns; ++i)
1069 drawfill(pos + eX * weapon_width * rows * i, eY * height * rows + eX * weapon_width * rows, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1072 for (int i = 0; i < rows; ++i)
1073 drawfill(pos + eY * weapon_height + eY * height * i, eX * tmp.x + eY * hud_fontsize.y, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1076 average_accuracy = 0;
1077 int weapons_with_stats = 0;
1079 pos.x += weapon_width / 2;
1081 if (autocvar_hud_panel_scoreboard_accuracy_nocolors)
1084 Accuracy_LoadColors();
1086 float oldposx = pos.x;
1090 FOREACH(Weapons, it != WEP_Null, {
1091 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1093 WepSet set = it.m_wepset;
1094 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1098 if (weapon_stats >= 0)
1099 weapon_alpha = sbt_fg_alpha;
1101 weapon_alpha = 0.2 * sbt_fg_alpha;
1104 drawpic_aspect_skin(tmpos, it.model2, eX * weapon_width + eY * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1106 if (weapon_stats >= 0) {
1107 weapons_with_stats += 1;
1108 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1111 s = sprintf("%d%%", weapon_stats * 100);
1114 padding = (weapon_width - stringwidth(s, false, hud_fontsize)) / 2; // center the accuracy value
1116 if(!autocvar_hud_panel_scoreboard_accuracy_nocolors)
1117 rgb = Accuracy_GetColor(weapon_stats);
1119 drawstring(tmpos + eX * padding + eY * weapon_height, s, hud_fontsize, rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
1121 tmpos.x += weapon_width * rows;
1122 pos.x += weapon_width * rows;
1123 if (rows == 2 && column == columnns - 1) {
1131 if (weapons_with_stats)
1132 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1134 panel_size.x += panel_bg_padding * 2; // restore initial width
1138 vector MapStats_DrawKeyValue(vector pos, string key, string value) {
1140 pos.x += hud_fontsize.x * 0.25;
1141 drawstring(pos, key, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1142 pos.x = panel_pos.x + panel_size.x - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1143 drawstring(pos, value, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1145 pos.y += hud_fontsize.y;
1150 vector Scoreboard_MapStats_Draw(vector pos, vector rgb, vector bg_size) {
1151 float stat_secrets_found, stat_secrets_total;
1152 float stat_monsters_killed, stat_monsters_total;
1156 // get monster stats
1157 stat_monsters_killed = STAT(MONSTERS_KILLED);
1158 stat_monsters_total = STAT(MONSTERS_TOTAL);
1160 // get secrets stats
1161 stat_secrets_found = STAT(SECRETS_FOUND);
1162 stat_secrets_total = STAT(SECRETS_TOTAL);
1164 // get number of rows
1165 if(stat_secrets_total)
1167 if(stat_monsters_total)
1170 // if no rows, return
1174 // draw table header
1175 drawstring(pos + eX * panel_bg_padding, _("Map stats:"), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1176 pos.y += 1.25 * hud_fontsize.y;
1177 if(panel.current_panel_bg != "0")
1178 pos.y += panel_bg_border;
1181 panel_size.y = hud_fontsize.y * rows;
1182 panel_size.y += panel_bg_padding * 2;
1185 vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
1186 if(panel.current_panel_bg != "0")
1187 end_pos.y += panel_bg_border * 2;
1189 if(panel_bg_padding)
1191 panel_pos += '1 1 0' * panel_bg_padding;
1192 panel_size -= '2 2 0' * panel_bg_padding;
1196 vector tmp = panel_size;
1199 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1202 if(stat_monsters_total)
1204 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1205 pos = MapStats_DrawKeyValue(pos, _("Monsters killed:"), val);
1209 if(stat_secrets_total)
1211 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1212 pos = MapStats_DrawKeyValue(pos, _("Secrets found:"), val);
1215 panel_size.x += panel_bg_padding * 2; // restore initial width
1220 vector Scoreboard_Rankings_Draw(vector pos, entity pl, vector rgb, vector bg_size)
1223 RANKINGS_RECEIVED_CNT = 0;
1224 for (i=RANKINGS_CNT-1; i>=0; --i)
1226 ++RANKINGS_RECEIVED_CNT;
1228 if (RANKINGS_RECEIVED_CNT == 0)
1231 vector hl_rgb = rgb + '0.5 0.5 0.5';
1233 pos.y += hud_fontsize.y;
1234 drawstring(pos + eX * panel_bg_padding, _("Rankings"), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1235 pos.y += 1.25 * hud_fontsize.y;
1236 if(panel.current_panel_bg != "0")
1237 pos.y += panel_bg_border;
1240 panel_size.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1241 panel_size.y += panel_bg_padding * 2;
1244 vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
1245 if(panel.current_panel_bg != "0")
1246 end_pos.y += panel_bg_border * 2;
1248 if(panel_bg_padding)
1250 panel_pos += '1 1 0' * panel_bg_padding;
1251 panel_size -= '2 2 0' * panel_bg_padding;
1255 vector tmp = panel_size;
1258 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1261 for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
1268 n = grecordholder[i];
1269 p = count_ordinal(i+1);
1270 if(grecordholder[i] == entcs_GetName(player_localnum))
1271 drawfill(pos, eX * panel_size.x + '0 1.25 0' * hud_fontsize.y, hl_rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
1272 else if(!(i % 2) && sbt_highlight)
1273 drawfill(pos, eX * panel_size.x + '0 1.25 0' * hud_fontsize.y, hl_rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1274 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1275 drawstring(pos + '3 0 0' * hud_fontsize.y, TIME_ENCODED_TOSTRING(t), '1 1 0' * hud_fontsize.y, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1276 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, sbt_fg_alpha, DRAWFLAG_NORMAL);
1277 pos.y += 1.25 * hud_fontsize.y;
1280 panel_size.x += panel_bg_padding * 2; // restore initial width
1284 void Scoreboard_Draw()
1286 if(!autocvar__hud_configure)
1288 if(!hud_draw_maximized) return;
1290 // frametime checks allow to toggle the scoreboard even when the game is paused
1291 if(scoreboard_active) {
1292 if(hud_configure_menu_open == 1)
1293 scoreboard_fade_alpha = 1;
1294 float scoreboard_fadeinspeed = autocvar_hud_panel_scoreboard_fadeinspeed;
1295 if (scoreboard_fadeinspeed && frametime)
1296 scoreboard_fade_alpha = min(1, scoreboard_fade_alpha + frametime * scoreboard_fadeinspeed);
1298 scoreboard_fade_alpha = 1;
1299 if(hud_fontsize_str != autocvar_hud_fontsize)
1301 hud_fontsize = HUD_GetFontsize("hud_fontsize");
1302 Scoreboard_initFieldSizes();
1303 if(hud_fontsize_str)
1304 strunzone(hud_fontsize_str);
1305 hud_fontsize_str = strzone(autocvar_hud_fontsize);
1309 float scoreboard_fadeoutspeed = autocvar_hud_panel_scoreboard_fadeoutspeed;
1310 if (scoreboard_fadeoutspeed && frametime)
1311 scoreboard_fade_alpha = max(0, scoreboard_fade_alpha - frametime * scoreboard_fadeoutspeed);
1313 scoreboard_fade_alpha = 0;
1316 if (!scoreboard_fade_alpha)
1320 scoreboard_fade_alpha = 0;
1322 if (autocvar_hud_panel_scoreboard_dynamichud)
1325 HUD_Scale_Disable();
1327 if(scoreboard_fade_alpha <= 0)
1329 panel_fade_alpha *= scoreboard_fade_alpha;
1330 HUD_Panel_LoadCvars();
1332 sbt_bg_alpha = autocvar_hud_panel_scoreboard_table_bg_alpha * panel_fg_alpha;
1333 sbt_highlight = autocvar_hud_panel_scoreboard_table_highlight;
1334 sbt_highlight_alpha = autocvar_hud_panel_scoreboard_table_highlight_alpha * panel_fg_alpha;
1335 sbt_highlight_alpha_self = autocvar_hud_panel_scoreboard_table_highlight_alpha_self * panel_fg_alpha;
1336 sbt_fg_alpha = autocvar_hud_panel_scoreboard_table_fg_alpha * panel_fg_alpha;
1337 sbt_fg_alpha_self = autocvar_hud_panel_scoreboard_table_fg_alpha_self * panel_fg_alpha;
1339 // don't overlap with con_notify
1340 if(!autocvar__hud_configure)
1341 panel_pos.y = max((autocvar_con_notify * autocvar_con_notifysize), panel_pos.y);
1343 float excess = max(0, max_namesize - autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x);
1344 float fixed_scoreboard_width = bound(vid_conwidth * 0.4, vid_conwidth - excess, vid_conwidth * 0.93);
1345 panel_pos.x = 0.5 * (vid_conwidth - fixed_scoreboard_width);
1346 panel_size.x = fixed_scoreboard_width;
1348 Scoreboard_UpdatePlayerTeams();
1354 // Initializes position
1358 vector sb_heading_fontsize;
1359 sb_heading_fontsize = hud_fontsize * 2;
1360 draw_beginBoldFont();
1361 drawstring(pos + eX * panel_bg_padding, _("Scoreboard"), sb_heading_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1364 pos.y += sb_heading_fontsize.y;
1365 if(panel.current_panel_bg != "0")
1366 pos.y += panel_bg_border;
1368 // Draw the scoreboard
1369 float scale = autocvar_hud_panel_scoreboard_table_bg_scale;
1372 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * scale;
1376 vector panel_bg_color_save = panel_bg_color;
1377 vector team_score_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 0.5;
1378 if(panel.current_panel_bg != "0")
1379 team_score_baseoffset.x -= panel_bg_border;
1380 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1382 if(tm.team == NUM_SPECTATOR)
1387 draw_beginBoldFont();
1388 vector rgb = Team_ColorRGB(tm.team);
1389 str = ftos(tm.(teamscores(ts_primary)));
1390 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1392 if(ts_primary != ts_secondary)
1394 str = ftos(tm.(teamscores(ts_secondary)));
1395 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize) + eY * hud_fontsize.y * 1.5, str, hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1398 if(autocvar_hud_panel_scoreboard_bg_teams_color_team > 0)
1399 panel_bg_color = rgb * autocvar_hud_panel_scoreboard_bg_teams_color_team;
1400 else if(panel_bg_color_team > 0)
1401 panel_bg_color = rgb * panel_bg_color_team;
1403 panel_bg_color = rgb;
1404 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
1406 panel_bg_color = panel_bg_color_save;
1410 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1412 if(tm.team == NUM_SPECTATOR)
1415 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
1419 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1420 if(race_speedaward) {
1421 drawcolorcodedstring(pos, sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward, race_speedaward_unit, race_speedaward_holder), hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1422 pos.y += 1.25 * hud_fontsize.y;
1424 if(race_speedaward_alltimebest) {
1425 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d%s ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_unit, race_speedaward_alltimebest_holder), hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1426 pos.y += 1.25 * hud_fontsize.y;
1428 pos = Scoreboard_Rankings_Draw(pos, playerslots[player_localnum], panel_bg_color, bg_size);
1430 else if (autocvar_hud_panel_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL)
1431 pos = Scoreboard_AccuracyStats_Draw(pos, panel_bg_color, bg_size);
1433 pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size);
1438 for(pl = players.sort_next; pl; pl = pl.sort_next)
1440 if(pl.team != NUM_SPECTATOR)
1442 pos.y += 1.25 * hud_fontsize.y;
1443 Scoreboard_DrawItem(pos, '0 0 0', pl, (pl.sv_entnum == player_localnum), specs);
1449 draw_beginBoldFont();
1450 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1452 pos.y += 1.25 * hud_fontsize.y;
1455 // Print info string
1457 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1458 tl = STAT(TIMELIMIT);
1459 fl = STAT(FRAGLIMIT);
1460 ll = STAT(LEADLIMIT);
1461 if(gametype == MAPINFO_TYPE_LMS)
1464 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1469 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1473 str = strcat(str, _(" or"));
1476 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), fl),
1477 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
1478 (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1479 TranslateScoresLabel(teamscores_label(ts_primary))));
1483 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags(ps_primary), fl),
1484 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
1485 (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1486 TranslateScoresLabel(scores_label(ps_primary))));
1491 if(tl > 0 || fl > 0)
1492 str = strcat(str, _(" or"));
1495 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), ll),
1496 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
1497 (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1498 TranslateScoresLabel(teamscores_label(ts_primary))));
1502 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags(ps_primary), ll),
1503 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
1504 (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1505 TranslateScoresLabel(scores_label(ps_primary))));
1510 pos.y += 1.2 * hud_fontsize.y;
1511 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1513 // print information about respawn status
1514 float respawn_time = STAT(RESPAWN_TIME);
1518 if(respawn_time < 0)
1520 // a negative number means we are awaiting respawn, time value is still the same
1521 respawn_time *= -1; // remove mark now that we checked it
1523 if(respawn_time < time) // it happens for a few frames when server is respawning the player
1524 str = ""; // draw an empty string to not change suddenly scoreboard_bottom
1526 str = sprintf(_("^1Respawning in ^3%s^1..."),
1527 (autocvar_hud_panel_scoreboard_respawntime_decimals ?
1528 count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
1530 count_seconds(ceil(respawn_time - time))
1534 else if(time < respawn_time)
1536 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1537 (autocvar_hud_panel_scoreboard_respawntime_decimals ?
1538 count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
1540 count_seconds(ceil(respawn_time - time))
1544 else if(time >= respawn_time)
1545 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1547 pos.y += 1.2 * hud_fontsize.y;
1548 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1551 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;